summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/map/atcommand.c22
-rw-r--r--src/map/clif.c23
-rw-r--r--src/map/clif.h1
-rw-r--r--src/map/mob.c132
-rw-r--r--src/map/mob.h7
-rw-r--r--src/map/skill.c1
-rw-r--r--src/map/skill.h6
7 files changed, 182 insertions, 10 deletions
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index ceb8ddb42..7424d1ab0 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -6343,20 +6343,31 @@ int atcommand_cleanmap(const int fd, struct map_session_data* sd, const char* co
/*==========================================
* make a NPC/PET talk
+ * @npctalkc [SnakeDrak]
*------------------------------------------*/
int atcommand_npctalk(const int fd, struct map_session_data* sd, const char* command, const char* message)
{
char name[NAME_LENGTH],mes[100],temp[100];
struct npc_data *nd;
+ bool ifcolor=(*(command + 8) != 'c' && *(command + 8) != 'C')?0:1;
+ unsigned long color=0;
if (sd->sc.count && //no "chatting" while muted.
(sd->sc.data[SC_BERSERK] ||
(sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT)))
return -1;
- if (!message || !*message || sscanf(message, "%23[^,], %99[^\n]", name, mes) < 2) {
- clif_displaymessage(fd, "Please, enter the correct info (usage: @npctalk <npc name>, <message>).");
- return -1;
+ if(!ifcolor) {
+ if (!message || !*message || sscanf(message, "%23[^,], %99[^\n]", name, mes) < 2) {
+ clif_displaymessage(fd, "Please, enter the correct info (usage: @npctalk <npc name>, <message>).");
+ return -1;
+ }
+ }
+ else {
+ if (!message || !*message || sscanf(message, "%lx %23[^,], %99[^\n]", &color, name, mes) < 3) {
+ clif_displaymessage(fd, "Please, enter the correct info (usage: @npctalkc <color> <npc name>, <message>).");
+ return -1;
+ }
}
if (!(nd = npc_name2id(name))) {
@@ -6366,7 +6377,9 @@ int atcommand_npctalk(const int fd, struct map_session_data* sd, const char* com
strtok(name, "#"); // discard extra name identifier if present
snprintf(temp, sizeof(temp), "%s : %s", name, mes);
- clif_message(&nd->bl, temp);
+
+ if(ifcolor) clif_messagecolor(&nd->bl,color,temp);
+ else clif_message(&nd->bl, temp);
return 0;
}
@@ -8733,6 +8746,7 @@ AtCommandInfo atcommand_info[] = {
{ "mobsearch", 10,10, atcommand_mobsearch },
{ "cleanmap", 40,40, atcommand_cleanmap },
{ "npctalk", 20,20, atcommand_npctalk },
+ { "npctalkc", 20,20, atcommand_npctalk },
{ "pettalk", 10,10, atcommand_pettalk },
{ "users", 40,40, atcommand_users },
{ "reset", 40,40, atcommand_reset },
diff --git a/src/map/clif.c b/src/map/clif.c
index 1d8c2e752..7d14ac446 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -7381,6 +7381,29 @@ void clif_specialeffect_single(struct block_list* bl, int type, int fd)
WFIFOSET(fd,10);
}
+/******************************************************
+ * W.<packet> W.<LENGTH> L.<ID> L.<COLOR> S.<TEXT>
+ * Mob/NPC Color Talk [SnakeDrak]
+ ******************************************************/
+int clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg)
+{
+ unsigned short msg_len = strlen(msg) + 1;
+ uint8 buf[256];
+ color = (color & 0x0000FF) << 16 | (color & 0x00FF00) | (color & 0xFF0000) >> 16; // RGB to BGR
+
+ nullpo_retr(0, bl);
+
+ WBUFW(buf,0) = 0x2C1;
+ WBUFW(buf,2) = msg_len + 12;
+ WBUFL(buf,4) = bl->id;
+ WBUFL(buf,8) = color;
+ memcpy(WBUFP(buf,12), msg, msg_len);
+
+ clif_send(buf, WBUFW(buf,2), bl, AREA_CHAT_WOC);
+
+ return 0;
+}
+
// messages (from mobs/npcs) [Valaris]
int clif_message(struct block_list* bl, const char* msg)
{
diff --git a/src/map/clif.h b/src/map/clif.h
index 2a7007e2b..605bbd762 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -371,6 +371,7 @@ int clif_mob_hp(struct mob_data *md);
void clif_weather(int m); // [Valaris]
int clif_specialeffect(struct block_list* bl, int type, enum send_target target); // special effects [Valaris]
void clif_specialeffect_single(struct block_list* bl, int type, int fd);
+int clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg); // Mob/Npc color talk [SnakeDrak]
int clif_message(struct block_list *bl, const char* msg); // messages (from mobs/npcs) [Valaris]
int clif_GM_kickack(struct map_session_data *sd,int id);
diff --git a/src/map/mob.c b/src/map/mob.c
index a0066bfa3..afe8adf5b 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -51,6 +51,7 @@
#define MOB_MAX_DELAY (24*3600*1000)
#define MAX_MINCHASE 30 //Max minimum chase value to use for mobs.
#define RUDE_ATTACKED_COUNT 2 //After how many rude-attacks should the skill be used?
+#define MAX_MOB_CHAT 250 //Max Skill's messages
//Dynamic mob database, allows saving of memory when there's big gaps in the mob_db [Skotlex]
struct mob_db *mob_db_data[MAX_MOB_DB+1];
@@ -58,6 +59,10 @@ struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant
struct mob_db *mob_db(int index) { if (index < 0 || index > MAX_MOB_DB || mob_db_data[index] == NULL) return mob_dummy; return mob_db_data[index]; }
+//Dynamic mob chat database
+struct mob_chat *mob_chat_db[MAX_MOB_CHAT+1];
+struct mob_chat *mob_chat(short id) { if(id<=0 || id>MAX_MOB_CHAT || mob_chat_db[id]==NULL) return (struct mob_chat*)NULL; return mob_chat_db[id]; }
+
static struct eri *item_drop_ers; //For loot drops delay structures.
static struct eri *item_drop_list_ers;
@@ -2978,6 +2983,13 @@ int mobskill_use(struct mob_data *md, unsigned int tick, int event)
if (!flag)
continue; //Skill requisite failed to be fulfilled.
+ if (ms[i].msg_id){ //Display color message [SnakeDrak]
+ struct mob_chat *mc = mob_chat(ms[i].msg_id);
+ char temp[CHAT_SIZE_MAX];
+ snprintf(temp, sizeof temp,"%s : %s", md->name, mc->msg);
+ clif_messagecolor(&md->bl, mc->color, temp);
+ }
+
//Execute skill
if (skill_get_casttype(ms[i].skill_id) == CAST_GROUND)
{ //Ground skill.
@@ -3869,6 +3881,103 @@ static int mob_read_randommonster(void)
}
/*==========================================
+ * processes one mob_chat_db entry [SnakeDrak]
+ * @param last_msg_id ensures that only one error message per mob id is printed
+ *------------------------------------------*/
+static bool mob_parse_row_chatdb(char** str, const char* source, int line, int* last_msg_id)
+{
+ struct mob_chat *ms;
+ int msg_id;
+
+ msg_id = atoi(str[0]);
+
+ if (msg_id <= 0 || msg_id > MAX_MOB_CHAT)
+ {
+ if (msg_id != *last_msg_id) {
+ ShowError("mob_chat: Invalid chat ID: %d at %s, line %d\n", msg_id, source, line);
+ *last_msg_id = msg_id;
+ }
+ return false;
+ }
+
+ if (mob_chat_db[msg_id] == NULL)
+ mob_chat_db[msg_id] = (struct mob_chat*)aCalloc(1, sizeof (struct mob_chat));
+
+ ms = mob_chat_db[msg_id];
+ //MSG ID
+ ms->msg_id=msg_id;
+ //Color
+ ms->color=strtoul(str[1],NULL,0);
+ //Message
+ if(strlen(str[2])>(CHAT_SIZE_MAX-1)){
+ if (msg_id != *last_msg_id) {
+ ShowError("mob_chat: readdb: Message too long! Line %d, id: %d\n", line, msg_id);
+ *last_msg_id = msg_id;
+ }
+ return false;
+ }
+ strncpy(ms->msg, str[2], CHAT_SIZE_MAX);
+
+ return true;
+}
+
+/*==========================================
+ * mob_chat_db.txt reading [SnakeDrak]
+ *-------------------------------------------------------------------------*/
+static void mob_readchatdb(void)
+{
+ char arc[]="mob_chat_db.txt";
+ uint32 lines=0, count=0;
+ char line[1024], path[256];
+ int i, tmp=0;
+ FILE *fp;
+ sprintf(path, "%s/%s", db_path, arc);
+ fp=fopen(path, "r");
+ if(fp == NULL)
+ {
+ ShowWarning("mob_readchatdb: File not found \"%s\", skipping.\n", path);
+ return;
+ }
+
+ while(fgets(line, sizeof(line), fp))
+ {
+ char *str[3], *p, *np;
+ int j=0;
+
+ lines++;
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+ memset(str, 0, sizeof(str));
+
+ p=line;
+ while(ISSPACE(*p))
+ ++p;
+ if(*p == '\0')
+ continue;// empty line
+ for(i = 0; i <= 2; i++)
+ {
+ str[i] = p;
+ if(i<2 && (np = strchr(p, ',')) != NULL) {
+ *np = '\0'; p = np + 1; j++;
+ }
+ }
+
+ if( j < 2 || str[2]==NULL)
+ {
+ ShowError("mob_readchatdb: Insufficient number of fields for skill at %s, line %d\n", arc, lines);
+ continue;
+ }
+
+ if( !mob_parse_row_chatdb(str, path, lines, &tmp) )
+ continue;
+
+ count++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", arc);
+}
+
+/*==========================================
* processes one mob_skill_db entry
* @param last_mob_id ensures that only one error message per mob id is printed
*------------------------------------------*/
@@ -4092,6 +4201,12 @@ static bool mob_parse_row_mobskilldb(char** str, const char* source, int line, i
ms->emotion=atoi(str[17]);
else
ms->emotion=-1;
+
+ if(str[18]!=NULL && mob_chat_db[atoi(str[18])]!=NULL)
+ ms->msg_id=atoi(str[18]);
+ else
+ ms->msg_id=0;
+
if (mob_id < 0)
{ //Set this skill to ALL mobs. [Skotlex]
mob_id *= -1;
@@ -4156,6 +4271,7 @@ static int mob_readskilldb(void)
while(fgets(line, sizeof(line), fp))
{
char *str[20], *p, *np;
+ int j=0;
lines++;
if(line[0] == '/' && line[1] == '/')
@@ -4167,15 +4283,15 @@ static int mob_readskilldb(void)
++p;
if( *p == '\0' )
continue;// empty line
- for(i = 0; i < 18; i++)
+ for(i = 0; i < 19; i++)
{
str[i] = p;
if((np = strchr(p, ',')) != NULL) {
- *np = '\0'; p = np + 1;
+ *np = '\0'; p = np + 1; j++;
}
}
- if( i < 18 )
+ if ( j < 18 || str[18]==NULL )
{
ShowError("mob_readskilldb: Insufficient number of fields for skill at %s, line %d\n", filename[fi], lines);
continue;
@@ -4262,6 +4378,7 @@ void mob_reload(void)
memset(&mob_db_data[i]->skill,0,sizeof(mob_db_data[i]->skill));
mob_db_data[i]->maxskill=0;
}
+ mob_readchatdb();
mob_readskilldb();
mob_readdb_race();
}
@@ -4294,6 +4411,7 @@ int do_init_mob(void)
mob_readdb_mobavail();
mob_read_randommonster();
+ mob_readchatdb();
mob_readskilldb();
mob_readdb_race();
@@ -4329,6 +4447,14 @@ int do_final_mob(void)
mob_db_data[i] = NULL;
}
}
+ for (i = 0; i <= MAX_MOB_CHAT; i++)
+ {
+ if (mob_chat_db[i] != NULL)
+ {
+ aFree(mob_chat_db[i]);
+ mob_chat_db[i] = NULL;
+ }
+ }
ers_destroy(item_drop_ers);
ers_destroy(item_drop_list_ers);
return 0;
diff --git a/src/map/mob.h b/src/map/mob.h
index b5127310f..b7f193b5b 100644
--- a/src/map/mob.h
+++ b/src/map/mob.h
@@ -62,6 +62,13 @@ struct mob_skill {
short target;
int val[5];
short emotion;
+ unsigned short msg_id;
+};
+
+struct mob_chat {
+ unsigned short msg_id;
+ unsigned long color;
+ char msg[CHAT_SIZE_MAX];
};
struct spawn_info {
diff --git a/src/map/skill.c b/src/map/skill.c
index dde93adbf..3c3ea3802 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -5701,6 +5701,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
skill_castend_nodamage_id);
}
break;
+ case NPC_TALK:
case ALL_WEWISH:
clif_skill_nodamage(src,bl,skillid,skilllv,1);
break;
diff --git a/src/map/skill.h b/src/map/skill.h
index 6c8ac178e..a74a0e644 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -965,9 +965,9 @@ enum e_skill {
NPC_VAMPIRE_GIFT,
NPC_WIDESOULDRAIN,
- ALL_INCCARRY = 681,
- //NPC_TALK = 682,
- NPC_HELLPOWER = 683,
+ ALL_INCCARRY,
+ NPC_TALK,
+ NPC_HELLPOWER,
NPC_WIDEHELLDIGNITY,
NPC_INVINCIBLE,
NPC_INVINCIBLEOFF,