/**
* This file is part of Hercules.
* http://herc.ws - http://github.com/HerculesWS/Hercules
*
* Copyright (C) 2012-2016 Hercules Dev Team
* Copyright (C) Athena Dev Teams
*
* Hercules is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#define HERCULES_CORE
#include "config/core.h" // AUTOLOOTITEM_SIZE, AUTOTRADE_PERSISTENCY, MAX_SUGGESTIONS, MOB_FLEE(), MOB_HIT(), RENEWAL, RENEWAL_DROP, RENEWAL_EXP
#include "atcommand.h"
#include "map/HPMmap.h"
#include "map/battle.h"
#include "map/channel.h"
#include "map/chat.h"
#include "map/chrif.h"
#include "map/clif.h"
#include "map/duel.h"
#include "map/elemental.h"
#include "map/guild.h"
#include "map/homunculus.h"
#include "map/intif.h"
#include "map/itemdb.h"
#include "map/log.h"
#include "map/mail.h"
#include "map/map.h"
#include "map/mapreg.h"
#include "map/mercenary.h"
#include "map/mob.h"
#include "map/npc.h"
#include "map/party.h"
#include "map/pc.h"
#include "map/pc_groups.h" // groupid2name
#include "map/pet.h"
#include "map/quest.h"
#include "map/script.h"
#include "map/searchstore.h"
#include "map/skill.h"
#include "map/status.h"
#include "map/storage.h"
#include "map/trade.h"
#include "map/unit.h"
#include "common/cbasetypes.h"
#include "common/conf.h"
#include "common/core.h"
#include "common/memmgr.h"
#include "common/mmo.h" // MAX_CARTS
#include "common/nullpo.h"
#include "common/random.h"
#include "common/showmsg.h"
#include "common/socket.h"
#include "common/strlib.h"
#include "common/sysinfo.h"
#include "common/timer.h"
#include "common/utils.h"
#include
#include
#include
#include
struct atcommand_interface atcommand_s;
struct atcommand_interface *atcommand;
static char atcmd_output[CHAT_SIZE_MAX];
static char atcmd_player_name[NAME_LENGTH];
// @commands (script-based)
struct atcmd_binding_data* get_atcommandbind_byname(const char* name) {
int i = 0;
nullpo_retr(NULL, name);
if( *name == atcommand->at_symbol || *name == atcommand->char_symbol )
name++; // for backwards compatibility
ARR_FIND( 0, atcommand->binding_count, i, strcmpi(atcommand->binding[i]->command, name) == 0 );
return ( i < atcommand->binding_count ) ? atcommand->binding[i] : NULL;
}
const char* atcommand_msgsd(struct map_session_data *sd, int msg_number) {
Assert_retr("??", msg_number >= 0 && msg_number < MAX_MSG && atcommand->msg_table[0][msg_number] != NULL);
if (!sd || sd->lang_id >= atcommand->max_message_table || !atcommand->msg_table[sd->lang_id][msg_number])
return atcommand->msg_table[0][msg_number];
return atcommand->msg_table[sd->lang_id][msg_number];
}
const char* atcommand_msgfd(int fd, int msg_number) {
struct map_session_data *sd = sockt->session_is_valid(fd) ? sockt->session[fd]->session_data : NULL;
Assert_retr("??", msg_number >= 0 && msg_number < MAX_MSG && atcommand->msg_table[0][msg_number] != NULL);
if (!sd || sd->lang_id >= atcommand->max_message_table || !atcommand->msg_table[sd->lang_id][msg_number])
return atcommand->msg_table[0][msg_number];
return atcommand->msg_table[sd->lang_id][msg_number];
}
//-----------------------------------------------------------
// Return the message string of the specified number by [Yor]
//-----------------------------------------------------------
const char* atcommand_msg(int msg_number) {
Assert_retr("??", msg_number >= 0 && msg_number < MAX_MSG);
if (atcommand->msg_table[map->default_lang_id][msg_number] != NULL && atcommand->msg_table[map->default_lang_id][msg_number][0] != '\0')
return atcommand->msg_table[map->default_lang_id][msg_number];
if(atcommand->msg_table[0][msg_number] != NULL && atcommand->msg_table[0][msg_number][0] != '\0')
return atcommand->msg_table[0][msg_number];
return "??";
}
/**
* Reads Message Data
*
* @param[in] cfg_name configuration filename to read.
* @param[in] allow_override whether to allow duplicate message IDs to override the original value.
* @return success state.
*/
bool msg_config_read(const char *cfg_name, bool allow_override) {
int msg_number;
char line[1024], w1[1024], w2[1024];
FILE *fp;
nullpo_retr(false, cfg_name);
if ((fp = fopen(cfg_name, "r")) == NULL) {
ShowError("Messages file not found: %s\n", cfg_name);
return false;
}
if( !atcommand->max_message_table )
atcommand->expand_message_table();
while(fgets(line, sizeof(line), fp)) {
if (line[0] == '/' && line[1] == '/')
continue;
if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
continue;
if (strcmpi(w1, "import") == 0) {
atcommand->msg_read(w2, true);
} else {
msg_number = atoi(w1);
if (msg_number >= 0 && msg_number < MAX_MSG) {
if (atcommand->msg_table[0][msg_number] != NULL) {
if (!allow_override) {
ShowError("Duplicate message: ID '%d' was already used for '%s'. Message '%s' will be ignored.\n",
msg_number, w2, atcommand->msg_table[0][msg_number]);
continue;
}
aFree(atcommand->msg_table[0][msg_number]);
}
/* this could easily become consecutive memory like get_str() and save the malloc overhead for over 1k calls [Ind] */
atcommand->msg_table[0][msg_number] = (char *)aMalloc((strlen(w2) + 1)*sizeof (char));
strcpy(atcommand->msg_table[0][msg_number],w2);
}
}
}
fclose(fp);
return true;
}
/*==========================================
* Cleanup Message Data
*------------------------------------------*/
void do_final_msg(void) {
int i, j;
for(i = 0; i < atcommand->max_message_table; i++) {
for (j = 0; j < MAX_MSG; j++) {
if( atcommand->msg_table[i][j] )
aFree(atcommand->msg_table[i][j]);
}
aFree(atcommand->msg_table[i]);
}
if( atcommand->msg_table )
aFree(atcommand->msg_table);
}
/**
* retrieves the help string associated with a given command.
*/
static inline const char* atcommand_help_string(AtCommandInfo *info) {
return info->help;
}
/*==========================================
* @send (used for testing packet sends from the client)
*------------------------------------------*/
ACMD(send)
{
int len=0,type;
long num;
// read message type as hex number (without the 0x)
if (!*message
|| !((sscanf(message, "len %x", (unsigned int*)&type)==1 && (len=1, true))
|| sscanf(message, "%x", (unsigned int*)&type)==1)
) {
clif->message(fd, msg_fd(fd,900)); // Usage:
clif->message(fd, msg_fd(fd,901)); // @send len
clif->message(fd, msg_fd(fd,902)); // @send {}*
clif->message(fd, msg_fd(fd,903)); // Value: or S""
return false;
}
#define PARSE_ERROR(error,p) do {\
clif->message(fd, (error));\
safesnprintf(atcmd_output, sizeof(atcmd_output), ">%s", (p));\
clif->message(fd, atcmd_output);\
} while(0) //define PARSE_ERROR
#define CHECK_EOS(p) do { \
if(*(p) == 0){ \
clif->message(fd, "Unexpected end of string");\
return false;\
} \
} while(0) //define CHECK_EOS
#define SKIP_VALUE(p) do { \
while(*(p) && !ISSPACE(*(p))) ++(p); /* non-space */\
while(*(p) && ISSPACE(*(p))) ++(p); /* space */\
} while(0) //define SKIP_VALUE
#define GET_VALUE(p,num) do { \
if(sscanf((p), "x%lx", (long unsigned int*)&(num)) < 1 && sscanf((p), "%ld ", &(num)) < 1){\
PARSE_ERROR("Invalid number in:",(p));\
return false;\
}\
} while(0) //define GET_VALUE
if (type >= MIN_PACKET_DB && type <= MAX_PACKET_DB) {
int off = 2;
if (clif->packet(type) == NULL) {
// unknown packet - ERROR
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,905), type); // Unknown packet: 0x%x
clif->message(fd, atcmd_output);
return false;
}
if (len) {
// show packet length
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,904), type, clif->packet(type)->len); // Packet 0x%x length: %d
clif->message(fd, atcmd_output);
return true;
}
len = clif->packet(type)->len;
if (len == -1) {
// dynamic packet
len = SHRT_MAX-4; // maximum length
off = 4;
}
WFIFOHEAD(sd->fd, len);
WFIFOW(sd->fd,0)=TOW(type);
// parse packet contents
SKIP_VALUE(message);
while(*message != 0 && off < len){
if(ISDIGIT(*message) || *message == '-' || *message == '+')
{// default (byte)
GET_VALUE(message,num);
WFIFOB(sd->fd,off)=TOB(num);
++off;
} else if(TOUPPER(*message) == 'B')
{// byte
++message;
GET_VALUE(message,num);
WFIFOB(sd->fd,off)=TOB(num);
++off;
} else if(TOUPPER(*message) == 'W')
{// word (2 bytes)
++message;
GET_VALUE(message,num);
WFIFOW(sd->fd,off)=TOW(num);
off+=2;
} else if(TOUPPER(*message) == 'L')
{// long word (4 bytes)
++message;
GET_VALUE(message,num);
WFIFOL(sd->fd,off)=TOL(num);
off+=4;
} else if(TOUPPER(*message) == 'S')
{// string - escapes are valid
// get string length - num <= 0 means not fixed length (default)
int end;
++message;
if(*message == '"'){
num=0;
} else {
GET_VALUE(message,num);
while(*message != '"')
{// find start of string
if(*message == 0 || ISSPACE(*message)){
PARSE_ERROR(msg_fd(fd,906),message); // Not a string:
return false;
}
++message;
}
}
// parse string
++message;
CHECK_EOS(message);
end=(num<=0? 0: min(off+((int)num),len));
for(; *message != '"' && (off < end || end == 0); ++off){
if(*message == '\\'){
++message;
CHECK_EOS(message);
switch(*message){
case 'a': num=0x07; break; // Bell
case 'b': num=0x08; break; // Backspace
case 't': num=0x09; break; // Horizontal tab
case 'n': num=0x0A; break; // Line feed
case 'v': num=0x0B; break; // Vertical tab
case 'f': num=0x0C; break; // Form feed
case 'r': num=0x0D; break; // Carriage return
case 'e': num=0x1B; break; // Escape
default: num=*message; break;
case 'x': // Hexadecimal
{
++message;
CHECK_EOS(message);
if(!ISXDIGIT(*message)){
PARSE_ERROR(msg_fd(fd,907),message); // Not a hexadecimal digit:
return false;
}
num=(ISDIGIT(*message)?*message-'0':TOLOWER(*message)-'a'+10);
if(ISXDIGIT(*message)){
++message;
CHECK_EOS(message);
num<<=8;
num+=(ISDIGIT(*message)?*message-'0':TOLOWER(*message)-'a'+10);
}
WFIFOB(sd->fd,off)=TOB(num);
++message;
CHECK_EOS(message);
continue;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': // Octal
{
num=*message-'0'; // 1st octal digit
++message;
CHECK_EOS(message);
if(ISDIGIT(*message) && *message < '8'){
num<<=3;
num+=*message-'0'; // 2nd octal digit
++message;
CHECK_EOS(message);
if(ISDIGIT(*message) && *message < '8'){
num<<=3;
num+=*message-'0'; // 3rd octal digit
++message;
CHECK_EOS(message);
}
}
WFIFOB(sd->fd,off)=TOB(num);
continue;
}
}
} else
num=*message;
WFIFOB(sd->fd,off)=TOB(num);
++message;
CHECK_EOS(message);
}//for
while(*message != '"')
{// ignore extra characters
++message;
CHECK_EOS(message);
}
// terminate the string
if(off < end)
{// fill the rest with 0's
memset(WFIFOP(sd->fd,off),0,end-off);
off=end;
}
} else
{// unknown
PARSE_ERROR(msg_fd(fd,908),message); // Unknown type of value in:
return false;
}
SKIP_VALUE(message);
}
if (clif->packet(type)->len == -1) { // send dynamic packet
WFIFOW(sd->fd,2)=TOW(off);
WFIFOSET(sd->fd,off);
} else {// send static packet
if(off < len)
memset(WFIFOP(sd->fd,off),0,len-off);
WFIFOSET(sd->fd,len);
}
} else {
clif->message(fd, msg_fd(fd,259)); // Invalid packet
return false;
}
sprintf (atcmd_output, msg_fd(fd,258), type, type); // Sent packet 0x%x (%d)
clif->message(fd, atcmd_output);
return true;
#undef PARSE_ERROR
#undef CHECK_EOS
#undef SKIP_VALUE
#undef GET_VALUE
}
/*==========================================
* @rura, @warp, @mapmove
*------------------------------------------*/
ACMD(mapmove) {
char map_name[MAP_NAME_LENGTH_EXT];
unsigned short map_index;
short x = 0, y = 0;
int16 m = -1;
memset(map_name, '\0', sizeof(map_name));
if (!*message ||
(sscanf(message, "%15s %5hd %5hd", map_name, &x, &y) < 3 &&
sscanf(message, "%15[^,],%5hd,%5hd", map_name, &x, &y) < 1)) {
clif->message(fd, msg_fd(fd,909)); // Please enter a map (usage: @warp/@rura/@mapmove ).
return false;
}
map_index = mapindex->name2id(map_name);
if (map_index)
m = map->mapindex2mapid(map_index);
if (!map_index || m < 0) { // m < 0 means on different server or that map is disabled! [Kevin]
clif->message(fd, msg_fd(fd,1)); // Map not found.
return false;
}
if( sd->bl.m == m && sd->bl.x == x && sd->bl.y == y ) {
clif->message(fd, msg_fd(fd,253)); // You already are at your destination!
return false;
}
if ((x || y) && map->getcell(m, &sd->bl, x, y, CELL_CHKNOPASS) && pc_get_group_level(sd) < battle_config.gm_ignore_warpable_area) {
//This is to prevent the pc->setpos call from printing an error.
clif->message(fd, msg_fd(fd,2));
if (!map->search_freecell(NULL, m, &x, &y, 10, 10, 1))
x = y = 0; //Invalid cell, use random spot.
}
if (map->list[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,247)); // You are not authorized to warp to this map.
return false;
}
if (sd->bl.m >= 0 && map->list[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,248)); // You are not authorized to warp from your current map.
return false;
}
if (pc->setpos(sd, map_index, x, y, CLR_TELEPORT) != 0) {
clif->message(fd, msg_fd(fd,1)); // Map not found.
return false;
}
clif->message(fd, msg_fd(fd,0)); // Warped.
return true;
}
/*==========================================
* Displays where a character is. Corrected version by Silent. [Skotlex]
*------------------------------------------*/
ACMD(where) {
struct map_session_data* pl_sd;
memset(atcmd_player_name, '\0', sizeof atcmd_player_name);
if (!*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
clif->message(fd, msg_fd(fd,910)); // Please enter a player name (usage: @where ).
return false;
}
pl_sd = map->nick2sd(atcmd_player_name);
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->message(fd, msg_fd(fd,3)); // Character not found.
return false;
}
snprintf(atcmd_output, sizeof atcmd_output, "%s %s %d %d", pl_sd->status.name, mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y);
clif->message(fd, atcmd_output);
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(jumpto) {
struct map_session_data *pl_sd = NULL;
if (!*message) {
clif->message(fd, msg_fd(fd,911)); // Please enter a player name (usage: @jumpto/@warpto/@goto ).
return false;
}
if (sd->bl.m >= 0 && map->list[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,248)); // You are not authorized to warp from your current map.
return false;
}
if( pc_isdead(sd) ) {
clif->message(fd, msg_fd(fd,864)); // "You cannot use this command when dead."
return false;
}
if ((pl_sd=map->nick2sd(message)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) {
clif->message(fd, msg_fd(fd,3)); // Character not found.
return false;
}
if (pl_sd->bl.m >= 0 && map->list[pl_sd->bl.m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,247)); // You are not authorized to warp to this map.
return false;
}
if( pl_sd->bl.m == sd->bl.m && pl_sd->bl.x == sd->bl.x && pl_sd->bl.y == sd->bl.y ) {
clif->message(fd, msg_fd(fd,253)); // You already are at your destination!
return false;
}
pc->setpos(sd, pl_sd->mapindex, pl_sd->bl.x, pl_sd->bl.y, CLR_TELEPORT);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,4), pl_sd->status.name); // Jumped to %s
clif->message(fd, atcmd_output);
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(jump)
{
short x = 0, y = 0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
sscanf(message, "%5hd %5hd", &x, &y);
if (map->list[sd->bl.m].flag.noteleport && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,248)); // You are not authorized to warp from your current map.
return false;
}
if( pc_isdead(sd) ) {
clif->message(fd, msg_fd(fd,864)); // "You cannot use this command when dead."
return false;
}
if ((x || y) && map->getcell(sd->bl.m, &sd->bl, x, y, CELL_CHKNOPASS)) {
//This is to prevent the pc->setpos call from printing an error.
clif->message(fd, msg_fd(fd,2));
if (!map->search_freecell(NULL, sd->bl.m, &x, &y, 10, 10, 1))
x = y = 0; //Invalid cell, use random spot.
}
if (x && y && sd->bl.x == x && sd->bl.y == y) {
clif->message(fd, msg_fd(fd,253)); // You already are at your destination!
return false;
}
pc->setpos(sd, sd->mapindex, x, y, CLR_TELEPORT);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,5), sd->bl.x, sd->bl.y); // Jumped to %d %d
clif->message(fd, atcmd_output);
return true;
}
/*==========================================
* Display list of online characters with
* various info.
*------------------------------------------*/
ACMD(who) {
const struct map_session_data *pl_sd = NULL;
struct s_mapiterator *iter = NULL;
char player_name[NAME_LENGTH] = "";
int count = 0;
int level = 0;
StringBuf buf;
/**
* 1 = @who : Player name, [Title], [Party name], [Guild name]
* 2 = @who2 : Player name, [Title], BLvl, JLvl, Job
* 3 = @who3 : [CID/AID] Player name [Title], Map, X, Y
*/
int display_type = 1;
int map_id = -1;
if (stristr(info->command, "map") != NULL) {
char map_name[MAP_NAME_LENGTH_EXT] = "";
if (sscanf(message, "%15s %23s", map_name, player_name) < 1 || (map_id = map->mapname2mapid(map_name)) < 0)
map_id = sd->bl.m;
} else {
sscanf(message, "%23s", player_name);
}
if (stristr(info->command, "2") != NULL)
display_type = 2;
else if (stristr(info->command, "3") != NULL)
display_type = 3;
level = pc_get_group_level(sd);
StrBuf->Init(&buf);
iter = mapit_getallusers();
for (pl_sd = BL_UCCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); pl_sd = BL_UCCAST(BL_PC, mapit->next(iter))) {
if (!((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || pc_isinvisible(pl_sd)) && pc_get_group_level(pl_sd) > level)) { // you can look only lower or same level
if (stristr(pl_sd->status.name, player_name) == NULL // search with no case sensitive
|| (map_id >= 0 && pl_sd->bl.m != map_id))
continue;
switch (display_type) {
case 2: {
StrBuf->Printf(&buf, msg_fd(fd,343), pl_sd->status.name); // "Name: %s "
if (pc_get_group_id(pl_sd) > 0) // Player title, if exists
StrBuf->Printf(&buf, msg_fd(fd,344), pcg->get_name(pl_sd->group)); // "(%s) "
StrBuf->Printf(&buf, msg_fd(fd,347), pl_sd->status.base_level, pl_sd->status.job_level,
pc->job_name(pl_sd->status.class)); // "| Lv:%d/%d | Job: %s"
break;
}
case 3: {
if (pc_has_permission(sd, PC_PERM_WHO_DISPLAY_AID))
StrBuf->Printf(&buf, msg_fd(fd,912), pl_sd->status.char_id, pl_sd->status.account_id); // "(CID:%d/AID:%d) "
StrBuf->Printf(&buf, msg_fd(fd,343), pl_sd->status.name); // "Name: %s "
if (pc_get_group_id(pl_sd) > 0) // Player title, if exists
StrBuf->Printf(&buf, msg_fd(fd,344), pcg->get_name(pl_sd->group)); // "(%s) "
StrBuf->Printf(&buf, msg_fd(fd,348), mapindex_id2name(pl_sd->mapindex), pl_sd->bl.x, pl_sd->bl.y); // "| Location: %s %d %d"
break;
}
default: {
struct party_data *p = party->search(pl_sd->status.party_id);
struct guild *g = pl_sd->guild;
StrBuf->Printf(&buf, msg_fd(fd,343), pl_sd->status.name); // "Name: %s "
if (pc_get_group_id(pl_sd) > 0) // Player title, if exists
StrBuf->Printf(&buf, msg_fd(fd,344), pcg->get_name(pl_sd->group)); // "(%s) "
if (p != NULL)
StrBuf->Printf(&buf, msg_fd(fd,345), p->party.name); // " | Party: '%s'"
if (g != NULL)
StrBuf->Printf(&buf, msg_fd(fd,346), g->name); // " | Guild: '%s'"
break;
}
}
clif->messagecolor_self(fd, COLOR_DEFAULT, StrBuf->Value(&buf));/** for whatever reason clif->message crashes with some patterns, see bugreport:8186 **/
StrBuf->Clear(&buf);
count++;
}
}
mapit->free(iter);
if (map_id < 0) {
if (count == 0)
StrBuf->AppendStr(&buf, msg_fd(fd,28)); // No player found.
else if (count == 1)
StrBuf->AppendStr(&buf, msg_fd(fd,29)); // 1 player found.
else
StrBuf->Printf(&buf, msg_fd(fd,30), count); // %d players found.
} else {
if (count == 0)
StrBuf->Printf(&buf, msg_fd(fd,54), map->list[map_id].name); // No player found in map '%s'.
else if (count == 1)
StrBuf->Printf(&buf, msg_fd(fd,55), map->list[map_id].name); // 1 player found in map '%s'.
else
StrBuf->Printf(&buf, msg_fd(fd,56), count, map->list[map_id].name); // %d players found in map '%s'.
}
clif->message(fd, StrBuf->Value(&buf));
StrBuf->Destroy(&buf);
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(whogm)
{
const struct map_session_data *pl_sd;
struct s_mapiterator* iter;
int j, count;
int level;
char match_text[CHAT_SIZE_MAX];
char player_name[NAME_LENGTH];
struct guild *g;
struct party_data *p;
memset(atcmd_output, '\0', sizeof(atcmd_output));
memset(match_text, '\0', sizeof(match_text));
memset(player_name, '\0', sizeof(player_name));
if (sscanf(message, "%199[^\n]", match_text) < 1)
strcpy(match_text, "");
for (j = 0; match_text[j]; j++)
match_text[j] = TOLOWER(match_text[j]);
count = 0;
level = pc_get_group_level(sd);
iter = mapit_getallusers();
for (pl_sd = BL_UCCAST(BL_PC, mapit->first(iter)); mapit->exists(iter); pl_sd = BL_UCCAST(BL_PC, mapit->next(iter))) {
int pl_level = pc_get_group_level(pl_sd);
if (!pl_level)
continue;
if (match_text[0]) {
memcpy(player_name, pl_sd->status.name, NAME_LENGTH);
for (j = 0; player_name[j]; j++)
player_name[j] = TOLOWER(player_name[j]);
// search with no case sensitive
if (strstr(player_name, match_text) == NULL)
continue;
}
if (pl_level > level) {
if (pc_isinvisible(pl_sd))
continue;
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,913), pl_sd->status.name); // Name: %s (GM)
clif->message(fd, atcmd_output);
count++;
continue;
}
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,914), // 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);
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,915), // BLvl: %d | Job: %s (Lvl: %d)
pl_sd->status.base_level,
pc->job_name(pl_sd->status.class), pl_sd->status.job_level);
clif->message(fd, atcmd_output);
p = party->search(pl_sd->status.party_id);
g = pl_sd->guild;
safesnprintf(atcmd_output, sizeof(atcmd_output),msg_fd(fd,916), // Party: '%s' | Guild: '%s'
p?p->party.name:msg_fd(fd,917), g?g->name:msg_fd(fd,917)); // None.
clif->message(fd, atcmd_output);
count++;
}
mapit->free(iter);
if (count == 0)
clif->message(fd, msg_fd(fd,150)); // No GM found.
else if (count == 1)
clif->message(fd, msg_fd(fd,151)); // 1 GM found.
else {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,152), count); // %d GMs found.
clif->message(fd, atcmd_output);
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(save)
{
pc->setsavepoint(sd, sd->mapindex, sd->bl.x, sd->bl.y);
if (sd->status.pet_id > 0 && sd->pd)
intif->save_petdata(sd->status.account_id, &sd->pd->pet);
chrif->save(sd,0);
clif->message(fd, msg_fd(fd,6)); // Your save point has been changed.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(load) {
int16 m;
m = map->mapindex2mapid(sd->status.save_point.map);
if (m >= 0 && map->list[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,249)); // You are not authorized to warp to your save map.
return false;
}
if (sd->bl.m >= 0 && map->list[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,248)); // You are not authorized to warp from your current map.
return false;
}
pc->setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, CLR_OUTSIGHT);
clif->message(fd, msg_fd(fd,7)); // Warping to save point..
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(speed)
{
int speed;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%12d", &speed) < 1) {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,918), MIN_WALK_SPEED, MAX_WALK_SPEED); // Please enter a speed value (usage: @speed <%d-%d>).
clif->message(fd, atcmd_output);
return false;
}
sd->state.permanent_speed = 0;
if (speed < 0)
sd->base_status.speed = DEFAULT_WALK_SPEED;
else
sd->base_status.speed = cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
if( sd->base_status.speed != DEFAULT_WALK_SPEED ) {
sd->state.permanent_speed = 1; // Set lock when set to non-default speed.
clif->message(fd, msg_fd(fd,8)); // Speed changed.
} else
clif->message(fd, msg_fd(fd,172)); //Speed returned to normal.
status_calc_bl(&sd->bl, SCB_SPEED);
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(storage)
{
if (sd->npc_id || sd->state.vending || sd->state.buyingstore || sd->state.trading || sd->state.storage_flag)
return false;
if (storage->open(sd) == 1) { //Already open.
clif->message(fd, msg_fd(fd,250)); // You have already opened your storage. Close it first.
return false;
}
clif->message(fd, msg_fd(fd,919)); // Storage opened.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(guildstorage)
{
if (!sd->status.guild_id) {
clif->message(fd, msg_fd(fd,252)); // You are not in a guild.
return false;
}
if (sd->npc_id || sd->state.vending || sd->state.buyingstore || sd->state.trading)
return false;
if (sd->state.storage_flag == STORAGE_FLAG_NORMAL) {
clif->message(fd, msg_fd(fd,250)); // You have already opened your storage. Close it first.
return false;
}
if (sd->state.storage_flag == STORAGE_FLAG_GUILD) {
clif->message(fd, msg_fd(fd,251)); // You have already opened your guild storage. Close it first.
return false;
}
if( gstorage->open(sd) ) {
clif->message(fd, msg_fd(fd,1201)); // Your guild's storage has already been opened by another member, try again later.
return false;
}
clif->message(fd, msg_fd(fd,920)); // Guild storage opened.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(option)
{
int param1 = 0, param2 = 0, param3 = 0;
if (!*message || sscanf(message, "%12d %12d %12d", ¶m1, ¶m2, ¶m3) < 1 || param1 < 0 || param2 < 0 || param3 < 0)
{// failed to match the parameters so inform the user of the options
const char* text;
// attempt to find the setting information for this command
text = atcommand_help_string( info );
// notify the user of the requirement to enter an option
clif->message(fd, msg_fd(fd,921)); // Please enter at least one option.
if( text ) {// send the help text associated with this command
clif->messageln( fd, text );
}
return false;
}
sd->sc.opt1 = param1;
sd->sc.opt2 = param2;
pc->setoption(sd, param3);
clif->message(fd, msg_fd(fd,9)); // Options changed.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(hide) {
if (pc_isinvisible(sd)) {
sd->sc.option &= ~OPTION_INVISIBLE;
if (sd->disguise != -1 )
status->set_viewdata(&sd->bl, sd->disguise);
else
status->set_viewdata(&sd->bl, sd->status.class);
clif->message(fd, msg_fd(fd,10)); // Invisible: Off
// increment the number of pvp players on the map
map->list[sd->bl.m].users_pvp++;
if( map->list[sd->bl.m].flag.pvp && !map->list[sd->bl.m].flag.pvp_nocalcrank ) {
// register the player for ranking calculations
sd->pvp_timer = timer->add( timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0 );
}
//bugreport:2266
map->foreachinmovearea(clif->insight, &sd->bl, AREA_SIZE, sd->bl.x, sd->bl.y, BL_ALL, &sd->bl);
} else {
sd->sc.option |= OPTION_INVISIBLE;
sd->vd.class_ = INVISIBLE_CLASS;
clif->message(fd, msg_fd(fd,11)); // Invisible: On
// decrement the number of pvp players on the map
map->list[sd->bl.m].users_pvp--;
if( map->list[sd->bl.m].flag.pvp && !map->list[sd->bl.m].flag.pvp_nocalcrank && sd->pvp_timer != INVALID_TIMER ) {
// unregister the player for ranking
timer->delete( sd->pvp_timer, pc->calc_pvprank_timer );
sd->pvp_timer = INVALID_TIMER;
}
}
clif->changeoption(&sd->bl);
return true;
}
/*==========================================
* Changes a character's class
*------------------------------------------*/
ACMD(jobchange)
{
int job = 0, upper = 0;
bool found = false;
if (*message == '\0') { // No message, just show the list
found = false;
} else if (sscanf(message, "%12d %12d", &job, &upper) >= 1) { // Numeric job ID
found = true;
} else { // Job name
int i;
// Normal Jobs
for (i = JOB_NOVICE; !found && i < JOB_MAX_BASIC; i++) {
if (strncmpi(message, pc->job_name(i), 16) == 0) {
job = i;
found = true;
break;
}
}
// High Jobs, Babies and Third
for (i = JOB_NOVICE_HIGH; !found && i < JOB_MAX; i++) {
if (strncmpi(message, pc->job_name(i), 16) == 0) {
job = i;
found = true;
break;
}
}
}
if (!found || !pc->db_checkid(job)) {
const char *text = atcommand_help_string(info);
if (text)
clif->messageln(fd, text);
return false;
}
// Deny direct transformation into dummy jobs
if (job == JOB_KNIGHT2 || job == JOB_CRUSADER2
|| job == JOB_WEDDING || job == JOB_XMAS || job == JOB_SUMMER
|| job == JOB_LORD_KNIGHT2 || job == JOB_PALADIN2
|| job == JOB_BABY_KNIGHT2 || job == JOB_BABY_CRUSADER2
|| job == JOB_STAR_GLADIATOR2
|| (job >= JOB_RUNE_KNIGHT2 && job <= JOB_MECHANIC_T2)
|| (job >= JOB_BABY_RUNE2 && job <= JOB_BABY_MECHANIC2)
) {
/* WHY DO WE LIST THEM THEN? */
clif->message(fd, msg_fd(fd,923)); //"You can not change to this job by command."
return true;
}
if (pc->jobchange(sd, job, upper) != 0) {
clif->message(fd, msg_fd(fd,155)); // You are unable to change your job.
return false;
}
clif->message(fd, msg_fd(fd,12)); // Your job has been changed.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(kill)
{
status_kill(&sd->bl);
clif->message(sd->fd, msg_fd(fd,13)); // A pity! You've died.
if (fd != sd->fd)
clif->message(fd, msg_fd(fd,14)); // Character killed.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(alive)
{
if (!status->revive(&sd->bl, 100, 100)) {
clif->message(fd, msg_fd(fd,867)); // "You're not dead."
return false;
}
clif->skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,4,1);
clif->message(fd, msg_fd(fd,16)); // You've been revived! It's a miracle!
return true;
}
/*==========================================
* +kamic [LuzZza]
*------------------------------------------*/
ACMD(kami)
{
unsigned int color=0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if(*(info->command + 4) != 'c' && *(info->command + 4) != 'C') {
if (!*message) {
clif->message(fd, msg_fd(fd,980)); // Please enter a message (usage: @kami ).
return false;
}
sscanf(message, "%199[^\n]", atcmd_output);
if (stristr(info->command, "l") != NULL)
clif->broadcast(&sd->bl, atcmd_output, (int)strlen(atcmd_output) + 1, BC_DEFAULT, ALL_SAMEMAP);
else
intif->broadcast(atcmd_output, (int)strlen(atcmd_output) + 1, (*(info->command + 4) == 'b' || *(info->command + 4) == 'B') ? BC_BLUE : BC_YELLOW);
} else {
if(!*message || (sscanf(message, "%10u %199[^\n]", &color, atcmd_output) < 2)) {
clif->message(fd, msg_fd(fd,981)); // Please enter color and message (usage: @kamic ).
return false;
}
if(color > 0xFFFFFF) {
clif->message(fd, msg_fd(fd,982)); // Invalid color.
return false;
}
intif->broadcast2(atcmd_output, (int)strlen(atcmd_output) + 1, color, 0x190, 12, 0, 0);
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(heal)
{
int hp = 0, sp = 0; // [Valaris] thanks to fov
sscanf(message, "%12d %12d", &hp, &sp);
// some overflow checks
if( hp == INT_MIN ) hp++;
if( sp == INT_MIN ) sp++;
if ( hp == 0 && sp == 0 ) {
if (!status_percent_heal(&sd->bl, 100, 100))
clif->message(fd, msg_fd(fd,157)); // HP and SP have already been recovered.
else
clif->message(fd, msg_fd(fd,17)); // HP, SP recovered.
return true;
}
if ( hp > 0 && sp >= 0 ) {
if(!status->heal(&sd->bl, hp, sp, 0))
clif->message(fd, msg_fd(fd,157)); // HP and SP are already with the good value.
else
clif->message(fd, msg_fd(fd,17)); // HP, SP recovered.
return true;
}
if ( hp < 0 && sp <= 0 ) {
status->damage(NULL, &sd->bl, -hp, -sp, 0, 0);
clif->damage(&sd->bl,&sd->bl, 0, 0, -hp, 0, BDT_ENDURE, 0);
clif->message(fd, msg_fd(fd,156)); // HP or/and SP modified.
return true;
}
//Opposing signs.
if ( hp ) {
if (hp > 0)
status->heal(&sd->bl, hp, 0, 0);
else {
status->damage(NULL, &sd->bl, -hp, 0, 0, 0);
clif->damage(&sd->bl,&sd->bl, 0, 0, -hp, 0, BDT_ENDURE, 0);
}
}
if ( sp ) {
if (sp > 0)
status->heal(&sd->bl, 0, sp, 0);
else
status->damage(NULL, &sd->bl, 0, -sp, 0, 0);
}
clif->message(fd, msg_fd(fd,156)); // HP or/and SP modified.
return true;
}
/*==========================================
* @item command (usage: @item ) (modified by [Yor] for pet_egg)
* @itembound command (usage: @itembound ) (revised by [Mhalicot])
*------------------------------------------*/
ACMD(item)
{
char item_name[100];
int number = 0, item_id, flag = 0, bound = 0;
struct item item_tmp;
struct item_data *item_data;
int get_count, i;
memset(item_name, '\0', sizeof(item_name));
if (!strcmpi(info->command,"itembound") && (!*message || (
sscanf(message, "\"%99[^\"]\" %12d %12d", item_name, &number, &bound) < 2 &&
sscanf(message, "%99s %12d %12d", item_name, &number, &bound) < 2
))) {
clif->message(fd, msg_fd(fd,295)); // Please enter an item name or ID (usage: @itembound - ).
return false;
} else if (!*message
|| ( sscanf(message, "\"%99[^\"]\" %12d", item_name, &number) < 1
&& sscanf(message, "%99s %12d", item_name, &number) < 1
)) {
clif->message(fd, msg_fd(fd,983)); // Please enter an item name or ID (usage: @item
- ).
return false;
}
if (number <= 0)
number = 1;
if ((item_data = itemdb->search_name(item_name)) == NULL &&
(item_data = itemdb->exists(atoi(item_name))) == NULL)
{
clif->message(fd, msg_fd(fd,19)); // Invalid item ID or name.
return false;
}
if(!strcmpi(info->command,"itembound") ) {
if( !(bound >= IBT_MIN && bound <= IBT_MAX) ) {
clif->message(fd, msg_fd(fd,298)); // Invalid bound type
return false;
}
switch( (enum e_item_bound_type)bound ) {
case IBT_CHARACTER:
case IBT_ACCOUNT:
break; /* no restrictions */
case IBT_PARTY:
if( !sd->status.party_id ) {
clif->message(fd, msg_fd(fd,1498)); //You can't add a party bound item to a character without party!
return false;
}
break;
case IBT_GUILD:
if( !sd->status.guild_id ) {
clif->message(fd, msg_fd(fd,1499)); //You can't add a guild bound item to a character without guild!
return false;
}
break;
}
}
item_id = item_data->nameid;
get_count = number;
//Check if it's stackable.
if (!itemdb->isstackable2(item_data)) {
if( bound && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) {
clif->message(fd, msg_fd(fd,498)); // Cannot create bounded pet eggs or pet armors.
return false;
}
get_count = 1;
}
for (i = 0; i < number; i += get_count) {
// if not pet egg
if (!pet->create_egg(sd, item_id)) {
memset(&item_tmp, 0, sizeof(item_tmp));
item_tmp.nameid = item_id;
item_tmp.identify = 1;
item_tmp.bound = (unsigned char)bound;
if ((flag = pc->additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
clif->additem(sd, 0, 0, flag);
}
}
if (flag == 0)
clif->message(fd, msg_fd(fd,18)); // Item created.
return true;
}
/*==========================================
* @item2 and @itembound2 command (revised by [Mhalicot])
*------------------------------------------*/
ACMD(item2)
{
struct item item_tmp;
struct item_data *item_data;
char item_name[100];
int item_id, number = 0, bound = 0;
int identify = 0, refine = 0, attr = 0;
int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
memset(item_name, '\0', sizeof(item_name));
if (!strcmpi(info->command,"itembound2") && (!*message || (
sscanf(message, "\"%99[^\"]\" %12d %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 &&
sscanf(message, "%99s %12d %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4, &bound) < 10 ))) {
clif->message(fd, msg_fd(fd,296)); // Please enter all parameters (usage: @itembound2
-
clif->message(fd, msg_fd(fd,297)); // ).
return false;
} else if (!*message
|| ( sscanf(message, "\"%99[^\"]\" %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9
&& sscanf(message, "%99s %12d %12d %12d %12d %12d %12d %12d %12d", item_name, &number, &identify, &refine, &attr, &c1, &c2, &c3, &c4) < 9
)) {
clif->message(fd, msg_fd(fd,984)); // Please enter all parameters (usage: @item2
-
clif->message(fd, msg_fd(fd,985)); // ).
return false;
}
if (number <= 0)
number = 1;
if( !strcmpi(info->command,"itembound2") && !(bound >= IBT_MIN && bound <= IBT_MAX) ) {
clif->message(fd, msg_fd(fd,298)); // Invalid bound type
return false;
}
item_id = 0;
if ((item_data = itemdb->search_name(item_name)) != NULL ||
(item_data = itemdb->exists(atoi(item_name))) != NULL)
item_id = item_data->nameid;
if (item_id > 500) {
int flag = 0;
int loop, get_count, i;
loop = 1;
get_count = number;
if( !strcmpi(info->command,"itembound2") )
bound = 1;
if( !itemdb->isstackable2(item_data) ) {
if( bound && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) {
clif->message(fd, msg_fd(fd,498)); // Cannot create bounded pet eggs or pet armors.
return false;
}
loop = number;
get_count = 1;
if (item_data->type == IT_PETEGG) {
identify = 1;
refine = 0;
}
if (item_data->type == IT_PETARMOR)
refine = 0;
} else {
identify = 1;
refine = attr = 0;
}
refine = cap_value(refine, 0, MAX_REFINE);
for (i = 0; i < loop; i++) {
memset(&item_tmp, 0, sizeof(item_tmp));
item_tmp.nameid = item_id;
item_tmp.identify = identify;
item_tmp.refine = refine;
item_tmp.attribute = attr;
item_tmp.bound = (unsigned char)bound;
item_tmp.card[0] = c1;
item_tmp.card[1] = c2;
item_tmp.card[2] = c3;
item_tmp.card[3] = c4;
if ((flag = pc->additem(sd, &item_tmp, get_count, LOG_TYPE_COMMAND)))
clif->additem(sd, 0, 0, flag);
}
if (flag == 0)
clif->message(fd, msg_fd(fd,18)); // Item created.
} else {
clif->message(fd, msg_fd(fd,19)); // Invalid item ID or name.
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(itemreset)
{
int i;
for (i = 0; i < MAX_INVENTORY; i++) {
if (sd->status.inventory[i].amount && sd->status.inventory[i].equip == 0) {
pc->delitem(sd, i, sd->status.inventory[i].amount, 0, DELITEM_NORMAL, LOG_TYPE_COMMAND);
}
}
clif->message(fd, msg_fd(fd,20)); // All of your items have been removed.
return true;
}
/*==========================================
* Atcommand @lvlup
*------------------------------------------*/
ACMD(baselevelup)
{
int level=0, i=0, status_point=0;
if (!*message || !(level = atoi(message))) {
clif->message(fd, msg_fd(fd,986)); // Please enter a level adjustment (usage: @lvup/@blevel/@baselvlup ).
return false;
}
if (level > 0) {
if (sd->status.base_level >= pc->maxbaselv(sd)) { // check for max level by Valaris
clif->message(fd, msg_fd(fd,47)); // Base level can't go any higher.
return false;
} // End Addition
if (level > pc->maxbaselv(sd) || level > pc->maxbaselv(sd) - sd->status.base_level) // fix positive overflow
level = pc->maxbaselv(sd) - sd->status.base_level;
for (i = 0; i < level; i++)
status_point += pc->gets_status_point(sd->status.base_level + i);
sd->status.status_point += status_point;
sd->status.base_level += level;
status_calc_pc(sd, SCO_FORCE);
status_percent_heal(&sd->bl, 100, 100);
clif->misceffect(&sd->bl, 0);
clif->message(fd, msg_fd(fd,21)); // Base level raised.
} else {
if (sd->status.base_level == 1) {
clif->message(fd, msg_fd(fd,158)); // Base level can't go any lower.
return false;
}
level*=-1;
if (level >= sd->status.base_level)
level = sd->status.base_level-1;
for (i = 0; i > -level; i--)
status_point += pc->gets_status_point(sd->status.base_level + i - 1);
if (sd->status.status_point < status_point)
pc->resetstate(sd);
if (sd->status.status_point < status_point)
sd->status.status_point = 0;
else
sd->status.status_point -= status_point;
sd->status.base_level -= level;
clif->message(fd, msg_fd(fd,22)); // Base level lowered.
status_calc_pc(sd, SCO_FORCE);
level *= -1;
}
sd->status.base_exp = 0;
clif->updatestatus(sd, SP_STATUSPOINT);
clif->updatestatus(sd, SP_BASELEVEL);
clif->updatestatus(sd, SP_BASEEXP);
clif->updatestatus(sd, SP_NEXTBASEEXP);
pc->baselevelchanged(sd);
if(sd->status.party_id)
party->send_levelup(sd);
if (level > 0 && battle_config.atcommand_levelup_events)
npc->script_event(sd, NPCE_BASELVUP); // Trigger OnPCBaseLvUpEvent
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(joblevelup)
{
int level=0;
if (!*message || !(level = atoi(message))) {
clif->message(fd, msg_fd(fd,987)); // Please enter a level adjustment (usage: @joblvup/@jlevel/@joblvlup ).
return false;
}
if (level > 0) {
if (sd->status.job_level >= pc->maxjoblv(sd)) {
clif->message(fd, msg_fd(fd,23)); // Job level can't go any higher.
return false;
}
if (level > pc->maxjoblv(sd) || level > pc->maxjoblv(sd) - sd->status.job_level) // fix positive overflow
level = pc->maxjoblv(sd) - sd->status.job_level;
sd->status.job_level += level;
sd->status.skill_point += level;
clif->misceffect(&sd->bl, 1);
clif->message(fd, msg_fd(fd,24)); // Job level raised.
} else {
if (sd->status.job_level == 1) {
clif->message(fd, msg_fd(fd,159)); // Job level can't go any lower.
return false;
}
level *=-1;
if (level >= sd->status.job_level) // fix negative overflow
level = sd->status.job_level-1;
sd->status.job_level -= level;
if (sd->status.skill_point < level)
pc->resetskill(sd, PCRESETSKILL_NONE); //Reset skills since we need to subtract more points.
if (sd->status.skill_point < level)
sd->status.skill_point = 0;
else
sd->status.skill_point -= level;
clif->message(fd, msg_fd(fd,25)); // Job level lowered.
level *= -1;
}
sd->status.job_exp = 0;
clif->updatestatus(sd, SP_JOBLEVEL);
clif->updatestatus(sd, SP_JOBEXP);
clif->updatestatus(sd, SP_NEXTJOBEXP);
clif->updatestatus(sd, SP_SKILLPOINT);
status_calc_pc(sd, SCO_FORCE);
if (level > 0 && battle_config.atcommand_levelup_events)
npc->script_event(sd, NPCE_JOBLVUP); // Trigger OnPCJobLvUpEvent
return true;
}
/*==========================================
* @help
*------------------------------------------*/
ACMD(help) {
const char *command_name = NULL;
char *default_command = "help";
AtCommandInfo *tinfo = NULL;
if (!*message) {
command_name = default_command; // If no command_name specified, display help for @help.
} else {
if (*message == atcommand->at_symbol || *message == atcommand->char_symbol)
++message;
command_name = atcommand->check_alias(message);
}
if (!atcommand->can_use2(sd, command_name, COMMAND_ATCOMMAND)) {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,153), message); // "%s is Unknown Command"
clif->message(fd, atcmd_output);
atcommand->get_suggestions(sd, command_name, true);
return false;
}
tinfo = atcommand->get_info_byname(atcommand->check_alias(command_name));
if ( !tinfo || tinfo->help == NULL ) {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,988), atcommand->at_symbol, command_name); // There is no help for %c%s.
clif->message(fd, atcmd_output);
atcommand->get_suggestions(sd, command_name, true);
return false;
}
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,989), atcommand->at_symbol, command_name); // Help for command %c%s:
clif->message(fd, atcmd_output);
{ // Display aliases
struct DBIterator *iter;
AtCommandInfo *command_info;
AliasInfo *alias_info = NULL;
StringBuf buf;
bool has_aliases = false;
StrBuf->Init(&buf);
StrBuf->AppendStr(&buf, msg_fd(fd,990)); // Available aliases:
command_info = atcommand->get_info_byname(command_name);
iter = db_iterator(atcommand->alias_db);
for (alias_info = dbi_first(iter); dbi_exists(iter); alias_info = dbi_next(iter)) {
if (alias_info->command == command_info) {
StrBuf->Printf(&buf, " %s", alias_info->alias);
has_aliases = true;
}
}
dbi_destroy(iter);
if (has_aliases)
clif->message(fd, StrBuf->Value(&buf));
StrBuf->Destroy(&buf);
}
// Display help contents
clif->messageln(fd, tinfo->help);
return true;
}
/**
* Helper function, used in foreach calls to stop auto-attack timers.
*
* @see map_foreachinmap
*
* Arglist parameters:
* - (int) id: If 0, stop any attacks. Otherwise, the target block list id to stop attacking.
*/
int atcommand_stopattack(struct block_list *bl,va_list ap)
{
struct unit_data *ud = NULL;
int id = 0;
nullpo_ret(bl);
ud = unit->bl2ud(bl);
id = va_arg(ap, int);
if (ud && ud->attacktimer != INVALID_TIMER && (!id || id == ud->target)) {
unit->stop_attack(bl);
return 1;
}
return 0;
}
/*==========================================
*
*------------------------------------------*/
int atcommand_pvpoff_sub(struct block_list *bl,va_list ap)
{
struct map_session_data *sd = NULL;
nullpo_ret(bl);
Assert_ret(bl->type == BL_PC);
sd = BL_UCAST(BL_PC, bl);
clif->pvpset(sd, 0, 0, 2);
if (sd->pvp_timer != INVALID_TIMER) {
timer->delete(sd->pvp_timer, pc->calc_pvprank_timer);
sd->pvp_timer = INVALID_TIMER;
}
return 0;
}
ACMD(pvpoff)
{
if (!map->list[sd->bl.m].flag.pvp) {
clif->message(fd, msg_fd(fd,160)); // PvP is already Off.
return false;
}
map->zone_change2(sd->bl.m,map->list[sd->bl.m].prev_zone);
map->list[sd->bl.m].flag.pvp = 0;
if (!battle_config.pk_mode) {
clif->map_property_mapall(sd->bl.m, MAPPROPERTY_NOTHING);
clif->maptypeproperty2(&sd->bl,ALL_SAMEMAP);
}
map->foreachinmap(atcommand->pvpoff_sub,sd->bl.m, BL_PC);
map->foreachinmap(atcommand->stopattack,sd->bl.m, BL_CHAR, 0);
clif->message(fd, msg_fd(fd,31)); // PvP: Off.
return true;
}
/*==========================================
*
*------------------------------------------*/
int atcommand_pvpon_sub(struct block_list *bl,va_list ap)
{
struct map_session_data *sd = NULL;
nullpo_ret(bl);
Assert_ret(bl->type == BL_PC);
sd = BL_UCAST(BL_PC, bl);
if (sd->pvp_timer == INVALID_TIMER) {
sd->pvp_timer = timer->add(timer->gettick() + 200, pc->calc_pvprank_timer, sd->bl.id, 0);
sd->pvp_rank = 0;
sd->pvp_lastusers = 0;
sd->pvp_point = 5;
sd->pvp_won = 0;
sd->pvp_lost = 0;
}
return 0;
}
ACMD(pvpon)
{
if (map->list[sd->bl.m].flag.pvp) {
clif->message(fd, msg_fd(fd,161)); // PvP is already On.
return false;
}
map->zone_change2(sd->bl.m,strdb_get(map->zone_db, MAP_ZONE_PVP_NAME));
map->list[sd->bl.m].flag.pvp = 1;
if (!battle_config.pk_mode) {// display pvp circle and rank
clif->map_property_mapall(sd->bl.m, MAPPROPERTY_FREEPVPZONE);
clif->maptypeproperty2(&sd->bl,ALL_SAMEMAP);
map->foreachinmap(atcommand->pvpon_sub,sd->bl.m, BL_PC);
}
clif->message(fd, msg_fd(fd,32)); // PvP: On.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(gvgoff) {
if (!map->list[sd->bl.m].flag.gvg) {
clif->message(fd, msg_fd(fd,162)); // GvG is already Off.
return false;
}
map->zone_change2(sd->bl.m,map->list[sd->bl.m].prev_zone);
map->list[sd->bl.m].flag.gvg = 0;
clif->map_property_mapall(sd->bl.m, MAPPROPERTY_NOTHING);
clif->maptypeproperty2(&sd->bl,ALL_SAMEMAP);
map->foreachinmap(atcommand->stopattack,sd->bl.m, BL_CHAR, 0);
clif->message(fd, msg_fd(fd,33)); // GvG: Off.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(gvgon)
{
if (map->list[sd->bl.m].flag.gvg) {
clif->message(fd, msg_fd(fd,163)); // GvG is already On.
return false;
}
map->zone_change2(sd->bl.m,strdb_get(map->zone_db, MAP_ZONE_GVG_NAME));
map->list[sd->bl.m].flag.gvg = 1;
clif->map_property_mapall(sd->bl.m, MAPPROPERTY_AGITZONE);
clif->maptypeproperty2(&sd->bl,ALL_SAMEMAP);
clif->message(fd, msg_fd(fd,34)); // GvG: On.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(model)
{
int hair_style = 0, hair_color = 0, cloth_color = 0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%12d %12d %12d", &hair_style, &hair_color, &cloth_color) < 1) {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,991), // Please enter at least one value (usage: @model ).
MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
clif->message(fd, atcmd_output);
return false;
}
if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE &&
hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR &&
cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
pc->changelook(sd, LOOK_HAIR, hair_style);
pc->changelook(sd, LOOK_HAIR_COLOR, hair_color);
pc->changelook(sd, LOOK_CLOTHES_COLOR, cloth_color);
clif->message(fd, msg_fd(fd,36)); // Appearance changed.
} else {
clif->message(fd, msg_fd(fd,37)); // An invalid number was specified.
return false;
}
return true;
}
/*==========================================
* @bodystyle [Rytech]
*------------------------------------------*/
ACMD(bodystyle)
{
int body_style = 0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%d", &body_style) < 1) {
sprintf(atcmd_output, "Please, enter a body style (usage: @bodystyle ).", MIN_BODY_STYLE, MAX_BODY_STYLE);
clif->message(fd, atcmd_output);
return false;
}
if (body_style >= MIN_BODY_STYLE && body_style <= MAX_BODY_STYLE) {
pc->changelook(sd, LOOK_BODY2, body_style);
clif->message(fd, msg_txt(36)); // Appearence changed.
} else {
clif->message(fd, msg_txt(37)); // An invalid number was specified.
return false;
}
return true;
}
/*==========================================
* @dye && @ccolor
*------------------------------------------*/
ACMD(dye)
{
int cloth_color = 0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%12d", &cloth_color) < 1) {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,992), MIN_CLOTH_COLOR, MAX_CLOTH_COLOR); // Please enter a clothes color (usage: @dye/@ccolor ).
clif->message(fd, atcmd_output);
return false;
}
if (cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
pc->changelook(sd, LOOK_CLOTHES_COLOR, cloth_color);
clif->message(fd, msg_fd(fd,36)); // Appearance changed.
} else {
clif->message(fd, msg_fd(fd,37)); // An invalid number was specified.
return false;
}
return true;
}
/*==========================================
* @hairstyle && @hstyle
*------------------------------------------*/
ACMD(hair_style)
{
int hair_style = 0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%12d", &hair_style) < 1) {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,993), MIN_HAIR_STYLE, MAX_HAIR_STYLE); // Please enter a hair style (usage: @hairstyle/@hstyle ).
clif->message(fd, atcmd_output);
return false;
}
if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE) {
pc->changelook(sd, LOOK_HAIR, hair_style);
clif->message(fd, msg_fd(fd,36)); // Appearance changed.
} else {
clif->message(fd, msg_fd(fd,37)); // An invalid number was specified.
return false;
}
return true;
}
/*==========================================
* @haircolor && @hcolor
*------------------------------------------*/
ACMD(hair_color)
{
int hair_color = 0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%12d", &hair_color) < 1) {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,994), MIN_HAIR_COLOR, MAX_HAIR_COLOR); // Please enter a hair color (usage: @haircolor/@hcolor ).
clif->message(fd, atcmd_output);
return false;
}
if (hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR) {
pc->changelook(sd, LOOK_HAIR_COLOR, hair_color);
clif->message(fd, msg_fd(fd,36)); // Appearance changed.
} else {
clif->message(fd, msg_fd(fd,37)); // An invalid number was specified.
return false;
}
return true;
}
/*==========================================
* @go [city_number or city_name] - Updated by Harbin
*------------------------------------------*/
ACMD(go) {
int town = INT_MAX; // Initialized to INT_MAX instead of -1 to avoid conflicts with those who map [-3:-1] to @memo locations.
char map_name[MAP_NAME_LENGTH];
const struct {
char map[MAP_NAME_LENGTH];
int x, y;
int min_match; ///< Minimum string length to match
} data[] = {
{ MAP_PRONTERA, 156, 191, 3 }, // 0 = Prontera
{ MAP_MORROC, 156, 93, 4 }, // 1 = Morroc
{ MAP_GEFFEN, 119, 59, 3 }, // 2 = Geffen
{ MAP_PAYON, 162, 233, 3 }, // 3 = Payon
{ MAP_ALBERTA, 192, 147, 3 }, // 4 = Alberta
#ifdef RENEWAL
{ MAP_IZLUDE, 128, 146, 3 }, // 5 = Izlude (Renewal)
#else
{ MAP_IZLUDE, 128, 114, 3 }, // 5 = Izlude
#endif
{ MAP_ALDEBARAN, 140, 131, 3 }, // 6 = Aldebaran
{ MAP_LUTIE, 147, 134, 3 }, // 7 = Lutie
{ MAP_COMODO, 209, 143, 3 }, // 8 = Comodo
{ MAP_YUNO, 157, 51, 3 }, // 9 = Juno
{ MAP_AMATSU, 198, 84, 3 }, // 10 = Amatsu
{ MAP_GONRYUN, 160, 120, 3 }, // 11 = Kunlun
{ MAP_UMBALA, 89, 157, 3 }, // 12 = Umbala
{ MAP_NIFLHEIM, 21, 153, 3 }, // 13 = Niflheim
{ MAP_LOUYANG, 217, 40, 3 }, // 14 = Luoyang
{ MAP_NOVICE, 53, 111, 3 }, // 15 = Training Grounds
{ MAP_JAIL, 23, 61, 3 }, // 16 = Prison
{ MAP_JAWAII, 249, 127, 3 }, // 17 = Jawaii
{ MAP_AYOTHAYA, 151, 117, 3 }, // 18 = Ayothaya
{ MAP_EINBROCH, 64, 200, 5 }, // 19 = Einbroch
{ MAP_LIGHTHALZEN, 158, 92, 3 }, // 20 = Lighthalzen
{ MAP_EINBECH, 70, 95, 5 }, // 21 = Einbech
{ MAP_HUGEL, 96, 145, 3 }, // 22 = Hugel
{ MAP_RACHEL, 130, 110, 3 }, // 23 = Rachel
{ MAP_VEINS, 216, 123, 3 }, // 24 = Veins
{ MAP_MOSCOVIA, 223, 184, 3 }, // 25 = Moscovia
{ MAP_MIDCAMP, 180, 240, 3 }, // 26 = Midgard Camp
{ MAP_MANUK, 282, 138, 3 }, // 27 = Manuk
{ MAP_SPLENDIDE, 197, 176, 3 }, // 28 = Splendide
{ MAP_BRASILIS, 182, 239, 3 }, // 29 = Brasilis
{ MAP_DICASTES, 198, 187, 3 }, // 30 = El Dicastes
{ MAP_MORA, 44, 151, 4 }, // 31 = Mora
{ MAP_DEWATA, 200, 180, 3 }, // 32 = Dewata
{ MAP_MALANGDO, 140, 114, 5 }, // 33 = Malangdo Island
{ MAP_MALAYA, 242, 211, 5 }, // 34 = Malaya Port
{ MAP_ECLAGE, 110, 39, 3 }, // 35 = Eclage
};
memset(map_name, '\0', sizeof(map_name));
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%11s", map_name) < 1) {
// no value matched so send the list of locations
const char* text;
// attempt to find the text help string
text = atcommand_help_string( info );
clif->message(fd, msg_fd(fd,38)); // Invalid location number, or name.
if (text) { // send the text to the client
clif->messageln( fd, text );
}
return false;
}
// Numeric entry
if (ISDIGIT(*message) || (message[0] == '-' && ISDIGIT(message[1]))) {
town = atoi(message);
}
if (town < 0 || town >= ARRAYLENGTH(data)) {
int i;
map_name[MAP_NAME_LENGTH-1] = '\0';
// Match maps on the list
for (i = 0; i < ARRAYLENGTH(data); i++) {
if (strncmpi(map_name, data[i].map, data[i].min_match) == 0) {
town = i;
break;
}
}
}
if (town < 0 || town >= ARRAYLENGTH(data)) {
// Alternate spellings
if (strncmpi(map_name, "morroc", 4) == 0) { // Correct town name for 'morocc'
town = 1;
} else if (strncmpi(map_name, "lutie", 3) == 0) { // Correct town name for 'xmas'
town = 7;
} else if (strncmpi(map_name, "juno", 3) == 0) { // Correct town name for 'yuno'
town = 9;
} else if (strncmpi(map_name, "kunlun", 3) == 0) { // Original town name for 'gonryun'
town = 11;
} else if (strncmpi(map_name, "luoyang", 3) == 0) { // Original town name for 'louyang'
town = 14;
} else if (strncmpi(map_name, "startpoint", 3) == 0 // Easy to remember alternatives to 'new_1-1'
|| strncmpi(map_name, "beginning", 3) == 0) {
town = 15;
} else if (strncmpi(map_name, "prison", 3) == 0 // Easy to remember alternatives to 'sec_pri'
|| strncmpi(map_name, "jail", 3) == 0) {
town = 16;
} else if (strncmpi(map_name, "rael", 3) == 0) { // Original town name for 'rachel'
town = 23;
}
}
if (town >= 0 && town < ARRAYLENGTH(data)) {
int16 m = map->mapname2mapid(data[town].map);
if (m >= 0 && map->list[m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,247));
return false;
}
if (sd->bl.m >= 0 && map->list[sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,248));
return false;
}
if (pc->setpos(sd, mapindex->name2id(data[town].map), data[town].x, data[town].y, CLR_TELEPORT) == 0) {
clif->message(fd, msg_fd(fd,0)); // Warped.
} else {
clif->message(fd, msg_fd(fd,1)); // Map not found.
return false;
}
} else {
clif->message(fd, msg_fd(fd,38)); // Invalid location number or name.
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(monster)
{
char name[NAME_LENGTH];
char monster[NAME_LENGTH];
char eventname[EVENT_NAME_LENGTH] = "";
int mob_id;
int number = 0;
int count;
int i, range;
short mx, my;
unsigned int size;
memset(name, '\0', sizeof(name));
memset(monster, '\0', sizeof(monster));
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message) {
clif->message(fd, msg_fd(fd,80)); // Please specify a display name or monster name/id.
return false;
}
if (sscanf(message, "\"%23[^\"]\" %23s %12d", name, monster, &number) > 1 ||
sscanf(message, "%23s \"%23[^\"]\" %12d", monster, name, &number) > 1) {
//All data can be left as it is.
} else if ((count=sscanf(message, "%23s %12d %23s", monster, &number, name)) > 1) {
//Here, it is possible name was not given and we are using monster for it.
if (count < 3) //Blank mob's name.
name[0] = '\0';
} else if (sscanf(message, "%23s %23s %12d", name, monster, &number) > 1) {
//All data can be left as it is.
} else if (sscanf(message, "%23s", monster) > 0) {
//As before, name may be already filled.
name[0] = '\0';
} else {
clif->message(fd, msg_fd(fd,80)); // Give a display name and monster name/id please.
return false;
}
if ((mob_id = mob->db_searchname(monster)) == 0) // check name first (to avoid possible name beginning by a number)
mob_id = mob->db_checkid(atoi(monster));
if (mob_id == 0) {
clif->message(fd, msg_fd(fd,40)); // Invalid monster ID or name.
return false;
}
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 && number > battle_config.atc_spawn_quantity_limit)
number = battle_config.atc_spawn_quantity_limit;
if (strcmpi(info->command, "monstersmall") == 0)
size = SZ_MEDIUM;
else if (strcmpi(info->command, "monsterbig") == 0)
size = SZ_BIG;
else
size = SZ_SMALL;
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);
count = 0;
range = (int)sqrt((float)number) +2; // calculation of an odd number (+ 4 area around)
for (i = 0; i < number; i++) {
int k;
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, eventname, size, AI_NONE|(mob_id == MOBID_EMPELIUM?0x200:0x0));
count += (k != 0) ? 1 : 0;
}
if (count != 0)
if (number == count)
clif->message(fd, msg_fd(fd,39)); // All monster summoned!
else {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,240), count); // %d monster(s) summoned!
clif->message(fd, atcmd_output);
}
else {
clif->message(fd, msg_fd(fd,40)); // Invalid monster ID or name.
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
int atkillmonster_sub(struct block_list *bl, va_list ap)
{
struct mob_data *md = NULL;
int flag = va_arg(ap, int);
nullpo_ret(bl);
Assert_ret(bl->type == BL_MOB);
md = BL_UCAST(BL_MOB, bl);
if (md->guardian_data)
return 0; //Do not touch WoE mobs!
if (flag)
status_zap(bl,md->status.hp, 0);
else
status_kill(bl);
return 1;
}
ACMD(killmonster) {
int map_id, drop_flag;
char map_name[MAP_NAME_LENGTH_EXT];
memset(map_name, '\0', sizeof(map_name));
if (!*message || sscanf(message, "%15s", map_name) < 1) {
map_id = sd->bl.m;
} else {
if ((map_id = map->mapname2mapid(map_name)) < 0)
map_id = sd->bl.m;
}
drop_flag = strcmpi(info->command, "killmonster2");
map->foreachinmap(atcommand->atkillmonster_sub, map_id, BL_MOB, -drop_flag);
clif->message(fd, msg_fd(fd,165)); // All monsters killed!
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(refine)
{
int j, position = 0, refine = 0, current_position, final_refine;
int count;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%12d %12d", &position, &refine) < 2) {
clif->message(fd, msg_fd(fd,996)); // Please enter a position and an amount (usage: @refine <+/- amount>).
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,997), EQP_HEAD_LOW); // %d: Lower Headgear
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,998), EQP_HAND_R); // %d: Right Hand
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,999), EQP_GARMENT); // %d: Garment
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1000), EQP_ACC_L); // %d: Left Accessory
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1001), EQP_ARMOR); // %d: Body Armor
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1002), EQP_HAND_L); // %d: Left Hand
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1003), EQP_SHOES); // %d: Shoes
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1004), EQP_ACC_R); // %d: Right Accessory
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1005), EQP_HEAD_TOP); // %d: Top Headgear
clif->message(fd, atcmd_output);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1006), EQP_HEAD_MID); // %d: Mid Headgear
clif->message(fd, atcmd_output);
return false;
}
refine = cap_value(refine, -MAX_REFINE, MAX_REFINE);
count = 0;
for (j = 0; j < EQI_MAX; j++) {
int idx = sd->equip_index[j];
if (idx < 0)
continue;
if(j == EQI_AMMO) continue; /* can't equip ammo */
if(j == EQI_HAND_R && sd->equip_index[EQI_HAND_L] == idx)
continue;
if(j == EQI_HEAD_MID && sd->equip_index[EQI_HEAD_LOW] == idx)
continue;
if(j == EQI_HEAD_TOP && (sd->equip_index[EQI_HEAD_MID] == idx || sd->equip_index[EQI_HEAD_LOW] == idx))
continue;
if(position && !(sd->status.inventory[idx].equip & position))
continue;
final_refine = cap_value(sd->status.inventory[idx].refine + refine, 0, MAX_REFINE);
if (sd->status.inventory[idx].refine != final_refine) {
sd->status.inventory[idx].refine = final_refine;
current_position = sd->status.inventory[idx].equip;
pc->unequipitem(sd, idx, PCUNEQUIPITEM_RECALC|PCUNEQUIPITEM_FORCE);
clif->refine(fd, 0, idx, sd->status.inventory[idx].refine);
clif->delitem(sd, idx, 1, DELITEM_MATERIALCHANGE);
clif->additem(sd, idx, 1, 0);
pc->equipitem(sd, idx, current_position);
clif->misceffect(&sd->bl, 3);
count++;
}
}
if (count == 0)
clif->message(fd, msg_fd(fd,166)); // No item has been refined.
else if (count == 1)
clif->message(fd, msg_fd(fd,167)); // 1 item has been refined.
else {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,168), count); // %d items have been refined.
clif->message(fd, atcmd_output);
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(produce)
{
char item_name[100];
int item_id, attribute = 0, star = 0;
struct item_data *item_data;
struct item tmp_item;
memset(atcmd_output, '\0', sizeof(atcmd_output));
memset(item_name, '\0', sizeof(item_name));
if (!*message || (
sscanf(message, "\"%99[^\"]\" %12d %12d", item_name, &attribute, &star) < 1 &&
sscanf(message, "%99s %12d %12d", item_name, &attribute, &star) < 1
)) {
clif->message(fd, msg_fd(fd,1007)); // Please enter at least one item name/ID (usage: @produce <# of very's>).
return false;
}
if ( (item_data = itemdb->search_name(item_name)) == NULL &&
(item_data = itemdb->exists(atoi(item_name))) == NULL ) {
clif->message(fd, msg_fd(fd,170)); //This item is not an equipment.
return false;
}
item_id = item_data->nameid;
if (itemdb->isequip2(item_data)) {
int flag = 0;
if (attribute < MIN_ATTRIBUTE || attribute > MAX_ATTRIBUTE)
attribute = ATTRIBUTE_NORMAL;
if (star < MIN_STAR || star > MAX_STAR)
star = 0;
memset(&tmp_item, 0, sizeof tmp_item);
tmp_item.nameid = item_id;
tmp_item.amount = 1;
tmp_item.identify = 1;
tmp_item.card[0] = CARD0_FORGE;
tmp_item.card[1] = item_data->type==IT_WEAPON?
((star*5) << 8) + attribute:0;
tmp_item.card[2] = GetWord(sd->status.char_id, 0);
tmp_item.card[3] = GetWord(sd->status.char_id, 1);
clif->produce_effect(sd, 0, item_id);
clif->misceffect(&sd->bl, 3);
if ((flag = pc->additem(sd, &tmp_item, 1, LOG_TYPE_COMMAND)))
clif->additem(sd, 0, 0, flag);
} else {
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,169), item_id, item_data->name); // The item (%d: '%s') is not equipable.
clif->message(fd, atcmd_output);
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(memo)
{
int position = 0;
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%d", &position) < 1)
{
int i;
clif->message(sd->fd, msg_fd(fd,868)); // "Your current memo positions are:"
for( i = 0; i < MAX_MEMOPOINTS; i++ )
{
if( sd->status.memo_point[i].map )
safesnprintf(atcmd_output, sizeof(atcmd_output), "%d - %s (%d,%d)", i, mapindex_id2name(sd->status.memo_point[i].map), sd->status.memo_point[i].x, sd->status.memo_point[i].y);
else
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,171), i); // %d - void
clif->message(sd->fd, atcmd_output);
}
return true;
}
if( position < 0 || position >= MAX_MEMOPOINTS )
{
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,1008), 0, MAX_MEMOPOINTS-1); // Please enter a valid position (usage: @memo ).
clif->message(fd, atcmd_output);
return false;
}
pc->memo(sd, position);
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(gat) {
int y;
memset(atcmd_output, '\0', sizeof(atcmd_output));
for (y = 2; y >= -2; y--) {
safesnprintf(atcmd_output, sizeof(atcmd_output), "%s (x= %d, y= %d) %02X %02X %02X %02X %02X",
map->list[sd->bl.m].name, sd->bl.x - 2, sd->bl.y + y,
(unsigned int)map->getcell(sd->bl.m, &sd->bl, sd->bl.x - 2, sd->bl.y + y, CELL_GETTYPE),
(unsigned int)map->getcell(sd->bl.m, &sd->bl, sd->bl.x - 1, sd->bl.y + y, CELL_GETTYPE),
(unsigned int)map->getcell(sd->bl.m, &sd->bl, sd->bl.x, sd->bl.y + y, CELL_GETTYPE),
(unsigned int)map->getcell(sd->bl.m, &sd->bl, sd->bl.x + 1, sd->bl.y + y, CELL_GETTYPE),
(unsigned int)map->getcell(sd->bl.m, &sd->bl, sd->bl.x + 2, sd->bl.y + y, CELL_GETTYPE));
clif->message(fd, atcmd_output);
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(displaystatus)
{
int i, type, flag, tick, val1 = 0, val2 = 0, val3 = 0;
if (!*message || (i = sscanf(message, "%d %d %d %d %d %d", &type, &flag, &tick, &val1, &val2, &val3)) < 1) {
clif->message(fd, msg_fd(fd,1009)); // Please enter a status type/flag (usage: @displaystatus { { {}}}).
return false;
}
if (i < 2) flag = 1;
if (i < 3) tick = 0;
if( flag == 0 )
clif->sc_end(&sd->bl,sd->bl.id,AREA,type);
else
clif->status_change(&sd->bl, type, flag, tick, val1, val2, val3);
return true;
}
/*==========================================
* @stpoint (Rewritten by [Yor])
*------------------------------------------*/
ACMD(statuspoint)
{
int point;
int new_status_point;
if (!*message || (point = atoi(message)) == 0) {
clif->message(fd, msg_fd(fd,1010)); // Please enter a number (usage: @stpoint ).
return false;
}
if (point < 0 && sd->status.status_point + point < 0) {
new_status_point = 0;
} else if (point > 0 && (int64)sd->status.status_point + point > INT_MAX) {
new_status_point = INT_MAX;
} else {
new_status_point = sd->status.status_point + point;
}
if (new_status_point != sd->status.status_point) {
sd->status.status_point = new_status_point;
clif->updatestatus(sd, SP_STATUSPOINT);
clif->message(fd, msg_fd(fd,174)); // Number of status points changed.
} else {
if (point < 0)
clif->message(fd, msg_fd(fd,41)); // Unable to decrease the number/value.
else
clif->message(fd, msg_fd(fd,149)); // Unable to increase the number/value.
return false;
}
return true;
}
/*==========================================
* @skpoint (Rewritten by [Yor])
*------------------------------------------*/
ACMD(skillpoint)
{
int point;
int new_skill_point;
if (!*message || (point = atoi(message)) == 0) {
clif->message(fd, msg_fd(fd,1011)); // Please enter a number (usage: @skpoint ).
return false;
}
if (point < 0 && sd->status.skill_point + point < 0) {
new_skill_point = 0;
} else if (point > 0 && (int64)sd->status.skill_point + point > INT_MAX) {
new_skill_point = INT_MAX;
} else {
new_skill_point = sd->status.skill_point + point;
}
if (new_skill_point != sd->status.skill_point) {
sd->status.skill_point = new_skill_point;
clif->updatestatus(sd, SP_SKILLPOINT);
clif->message(fd, msg_fd(fd,175)); // Number of skill points changed.
} else {
if (point < 0)
clif->message(fd, msg_fd(fd,41)); // Unable to decrease the number/value.
else
clif->message(fd, msg_fd(fd,149)); // Unable to increase the number/value.
return false;
}
return true;
}
/*==========================================
* @zeny
*------------------------------------------*/
ACMD(zeny)
{
int zeny=0, ret=-1;
if (!*message || (zeny = atoi(message)) == 0) {
clif->message(fd, msg_fd(fd,1012)); // Please enter an amount (usage: @zeny ).
return false;
}
if(zeny > 0) {
if((ret=pc->getzeny(sd,zeny,LOG_TYPE_COMMAND,NULL)) == 1)
clif->message(fd, msg_fd(fd,149)); // Unable to increase the number/value.
}
else {
if( sd->status.zeny < -zeny ) zeny = -sd->status.zeny;
if((ret=pc->payzeny(sd,-zeny,LOG_TYPE_COMMAND,NULL)) == 1)
clif->message(fd, msg_fd(fd,41)); // Unable to decrease the number/value.
}
if( ret ) //ret != 0 means cmd failure
return false;
clif->message(fd, msg_fd(fd,176));
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(param) {
int i, value = 0, new_value, max;
const char* param[] = { "str", "agi", "vit", "int", "dex", "luk" };
short* stats[6];
//we don't use direct initialization because it isn't part of the c standard.
memset(atcmd_output, '\0', sizeof(atcmd_output));
if (!*message || sscanf(message, "%d", &value) < 1 || value == 0) {
clif->message(fd, msg_fd(fd,1013)); // Please enter a valid value (usage: @str/@agi/@vit/@int/@dex/@luk <+/-adjustment>).
return false;
}
ARR_FIND( 0, ARRAYLENGTH(param), i, strcmpi(info->command, param[i]) == 0 );
if( i == ARRAYLENGTH(param) || i > MAX_STATUS_TYPE) { // normally impossible...
clif->message(fd, msg_fd(fd,1013)); // Please enter a valid value (usage: @str/@agi/@vit/@int/@dex/@luk <+/-adjustment>).
return false;
}
stats[0] = &sd->status.str;
stats[1] = &sd->status.agi;
stats[2] = &sd->status.vit;
stats[3] = &sd->status.int_;
stats[4] = &sd->status.dex;
stats[5] = &sd->status.luk;
if( battle_config.atcommand_max_stat_bypass )
max = SHRT_MAX;
else
max = pc_maxparameter(sd);
if(value < 0 && *stats[i] <= -value) {
new_value = 1;
} else if(max - *stats[i] < value) {
new_value = max;
} else {
new_value = *stats[i] + value;
}
if (new_value != *stats[i]) {
*stats[i] = new_value;
clif->updatestatus(sd, SP_STR + i);
clif->updatestatus(sd, SP_USTR + i);
status_calc_pc(sd, SCO_FORCE);
clif->message(fd, msg_fd(fd,42)); // Stat changed.
} else {
if (value < 0)
clif->message(fd, msg_fd(fd,41)); // Unable to decrease the number/value.
else
clif->message(fd, msg_fd(fd,149)); // Unable to increase the number/value.
return false;
}
return true;
}
/*==========================================
* Stat all by fritz (rewritten by [Yor])
*------------------------------------------*/
ACMD(stat_all) {
int index, count, value, max, new_value;
short* stats[6];
//we don't use direct initialization because it isn't part of the c standard.
stats[0] = &sd->status.str;
stats[1] = &sd->status.agi;
stats[2] = &sd->status.vit;
stats[3] = &sd->status.int_;
stats[4] = &sd->status.dex;
stats[5] = &sd->status.luk;
if (!*message || sscanf(message, "%d", &value) < 1 || value == 0) {
value = pc_maxparameter(sd);
max = pc_maxparameter(sd);
} else {
if( battle_config.atcommand_max_stat_bypass )
max = SHRT_MAX;
else
max = pc_maxparameter(sd);
}
count = 0;
for (index = 0; index < ARRAYLENGTH(stats); index++) {
if (value > 0 && *stats[index] > max - value)
new_value = max;
else if (value < 0 && *stats[index] <= -value)
new_value = 1;
else
new_value = *stats[index] +value;
if (new_value != (int)*stats[index]) {
*stats[index] = new_value;
clif->updatestatus(sd, SP_STR + index);
clif->updatestatus(sd, SP_USTR + index);
count++;
}
}
if (count > 0) { // if at least 1 stat modified
status_calc_pc(sd, SCO_FORCE);
clif->message(fd, msg_fd(fd,84)); // All stats changed!
} else {
if (value < 0)
clif->message(fd, msg_fd(fd,177)); // You cannot decrease that stat anymore.
else
clif->message(fd, msg_fd(fd,178)); // You cannot increase that stat anymore.
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(guildlevelup) {
int level = 0;
int16 added_level;
struct guild *guild_info;
if (!*message || sscanf(message, "%d", &level) < 1 || level == 0) {
clif->message(fd, msg_fd(fd,1014)); // Please enter a valid level (usage: @guildlvup/@guildlvlup <# of levels>).
return false;
}
if (sd->status.guild_id <= 0 || (guild_info = sd->guild) == NULL) {
clif->message(fd, msg_fd(fd,43)); // You're not in a guild.
return false;
}
#if 0 // By enabling this, only the guild leader can use this command
if (strcmp(sd->status.name, guild_info->master) != 0) {
clif->message(fd, msg_fd(fd,44)); // You're not the master of your guild.
return false;
}
#endif // 0
if (level > INT16_MAX || (level > 0 && level > MAX_GUILDLEVEL - guild_info->guild_lv)) // fix positive overflow
level = MAX_GUILDLEVEL - guild_info->guild_lv;
else if (level < INT16_MIN || (level < 0 && level < 1 - guild_info->guild_lv)) // fix negative overflow
level = 1 - guild_info->guild_lv;
added_level = (int16)level;
if (added_level != 0) {
intif->guild_change_basicinfo(guild_info->guild_id, GBI_GUILDLV, &added_level, sizeof(added_level));
clif->message(fd, msg_fd(fd,179)); // Guild level changed.
} else {
clif->message(fd, msg_fd(fd,45)); // Guild level change failed.
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(makeegg)
{
struct item_data *item_data;
int id, pet_id;
if (!*message) {
clif->message(fd, msg_fd(fd,1015)); // Please enter a monster/egg name/ID (usage: @makeegg ).
return false;
}
if ((item_data = itemdb->search_name(message)) != NULL) // for egg name
id = item_data->nameid;
else
if ((id = mob->db_searchname(message)) != 0) // for monster name
;
else
id = atoi(message);
pet_id = pet->search_petDB_index(id, PET_CLASS);
if (pet_id < 0)
pet_id = pet->search_petDB_index(id, PET_EGG);
if (pet_id >= 0) {
sd->catch_target_class = pet->db[pet_id].class_;
intif->create_pet(
sd->status.account_id, sd->status.char_id,
(short)pet->db[pet_id].class_, (short)mob->db(pet->db[pet_id].class_)->lv,
(short)pet->db[pet_id].EggID, 0, (short)pet->db[pet_id].intimate,
100, 0, 1, pet->db[pet_id].jname);
} else {
clif->message(fd, msg_fd(fd,180)); // The monster/egg name/id doesn't exist.
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(hatch)
{
if (sd->status.pet_id <= 0)
clif->sendegg(sd);
else {
clif->message(fd, msg_fd(fd,181)); // You already have a pet.
return false;
}
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(petfriendly)
{
int friendly;
struct pet_data *pd;
if (!*message || (friendly = atoi(message)) < 0) {
clif->message(fd, msg_fd(fd,1016)); // Please enter a valid value (usage: @petfriendly <0-1000>).
return false;
}
pd = sd->pd;
if (!pd) {
clif->message(fd, msg_fd(fd,184)); // Sorry, but you have no pet.
return false;
}
if (friendly < 0 || friendly > 1000)
{
clif->message(fd, msg_fd(fd,37)); // An invalid number was specified.
return false;
}
if (friendly == pd->pet.intimate) {
clif->message(fd, msg_fd(fd,183)); // Pet intimacy is already at maximum.
return false;
}
pet->set_intimate(pd, friendly);
clif->send_petstatus(sd);
clif->message(fd, msg_fd(fd,182)); // Pet intimacy changed.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(pethungry)
{
int hungry;
struct pet_data *pd;
if (!*message || (hungry = atoi(message)) < 0) {
clif->message(fd, msg_fd(fd,1017)); // Please enter a valid number (usage: @pethungry <0-100>).
return false;
}
pd = sd->pd;
if (!sd->status.pet_id || !pd) {
clif->message(fd, msg_fd(fd,184)); // Sorry, but you have no pet.
return false;
}
if (hungry < 0 || hungry > 100) {
clif->message(fd, msg_fd(fd,37)); // An invalid number was specified.
return false;
}
if (hungry == pd->pet.hungry) {
clif->message(fd, msg_fd(fd,186)); // Pet hunger is already at maximum.
return false;
}
pd->pet.hungry = hungry;
clif->send_petstatus(sd);
clif->message(fd, msg_fd(fd,185)); // Pet hunger changed.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(petrename)
{
struct pet_data *pd;
if (!sd->status.pet_id || !sd->pd) {
clif->message(fd, msg_fd(fd,184)); // Sorry, but you have no pet.
return false;
}
pd = sd->pd;
if (!pd->pet.rename_flag) {
clif->message(fd, msg_fd(fd,188)); // You can already rename your pet.
return false;
}
pd->pet.rename_flag = 0;
intif->save_petdata(sd->status.account_id, &pd->pet);
clif->send_petstatus(sd);
clif->message(fd, msg_fd(fd,187)); // You can now rename your pet.
return true;
}
/*==========================================
*
*------------------------------------------*/
ACMD(recall) {
struct map_session_data *pl_sd = NULL;
if (!*message) {
clif->message(fd, msg_fd(fd,1018)); // Please enter a player name (usage: @recall ).
return false;
}
if ((pl_sd=map->nick2sd(message)) == NULL && (pl_sd=map->charid2sd(atoi(message))) == NULL) {
clif->message(fd, msg_fd(fd,3)); // Character not found.
return false;
}
if ( pc_get_group_level(sd) < pc_get_group_level(pl_sd) )
{
clif->message(fd, msg_fd(fd,81)); // Your GM level doesn't authorize you to preform this action on the specified player.
return false;
}
if (sd->bl.m >= 0 && map->list[sd->bl.m].flag.nowarpto && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,1019)); // You are not authorized to warp someone to this map.
return false;
}
if (pl_sd->bl.m >= 0 && map->list[pl_sd->bl.m].flag.nowarp && !pc_has_permission(sd, PC_PERM_WARP_ANYWHERE)) {
clif->message(fd, msg_fd(fd,1020)); // You are not authorized to warp this player from their map.
return false;
}
if (pl_sd->bl.m == sd->bl.m && pl_sd->bl.x == sd->bl.x && pl_sd->bl.y == sd->bl.y) {
return false;
}
pc->setpos(pl_sd, sd->mapindex, sd->bl.x, sd->bl.y, CLR_RESPAWN);
safesnprintf(atcmd_output, sizeof(atcmd_output), msg_fd(fd,46), pl_sd->status.name); // %s recalled!
clif->message(fd, atcmd_output);
return true;
}
/*==========================================
* charblock command (usage: charblock )
* This command do a definitiv ban on a player
*------------------------------------------*/
ACMD(char_block)
{
memset(atcmd_player_name, '\0', sizeof(atcmd_player_name));
if (!*message || sscanf(message, "%23[^\n]", atcmd_player_name) < 1) {
clif->message(fd, msg_fd(fd,1021)); // Please enter a player name (usage: @block ).
return false;
}
chrif->char_ask_name(sd->status.account_id, atcmd_player_name, CHAR_ASK_NAME_BLOCK, 0, 0, 0, 0, 0, 0);
clif->message(fd, msg_fd(fd,88)); // Character name sent to char-server to ask it.
return true;
}
/*==========================================
* charban command (usage: charban