瀏覽代碼

> Implemented Channel System to replace @main. See tid:80999 for full details. (Hercules 7ec1e8f, 1e87c09)
* Adds @join and @channel commands to manipulate chat channels. (see doc/atcommands.txt)
* Speak in a #channel by sending a whisper to #channel or binding your global chat to the channel (@channel bindto <#channel_name>).
* Automatically join local map channels (#map) and guild ally channels (#ally), with mapflag 'nomapchannelautojoin' to disable the channel for a map (optional).
* Allow users to create private channels if 'allow_user_channel_creation' is true.
* Set default channels, text colors, and other settings in conf/channels.conf.
> Other changes:
* Improved overall guild processing/lookup by creating a cached guild state.
* Fixed a bug where equipping a garment would override costume garment.
* Removed clif_message and merged it with clif_disp_overhead, since both use the same packet.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@17228 54d463be-8e91-2dee-dedb-b68131a5f0ec

euphyy 12 年之前
父節點
當前提交
643861ba17

+ 1 - 0
conf/atcommand_athena.conf

@@ -56,6 +56,7 @@ aliases: {
 	guildstorage: ["gstorage"]
 	accinfo: ["accountinfo"]
 	itemreset: ["clearinventory"]
+	channel: ["main"]
 }
 
 /* Commands help file */

+ 44 - 0
conf/channels.conf

@@ -0,0 +1,44 @@
+/* Channel system configuration file */
+
+chsys: (
+{
+	/* Default channels (available to all players) */
+	default_channels: {
+		/* channel_name : channel_messages_color */
+	 	main: "Orange"
+		support: "Blue"
+		trade: "Red"
+		offtopic: "Cyan"
+		/* Add as many channels as you'd like. */
+	}
+
+	/* Colors available */
+	colors: {
+		Default: "0xffffff" /* Custom channels will use the first in the list unless a font is selected through @channel. */
+		Red: "0xff0000"
+		Blue: "0x83cfe9"
+		Orange: "0xe57c00"
+		Cyan: "0x00b89d"
+		Yellow: "0xffff90"
+		Green: "0x28bf00"
+		Normal: "0x00ff00"
+		/* Add as many channels as you'd like. */
+	}
+
+	/* Allow users to create their own (private) channels through @channels command? */
+	/* (must also allow players to use @channels in groups.conf) */
+	allow_user_channel_creation: true
+
+	/* "map_local_channel" is an instanced channel unique to each map. */
+	map_local_channel: true
+	map_local_channel_name: "map"
+	map_local_channel_color: "Yellow"
+	map_local_channel_autojoin: true /* Disable autojoin in specific maps through mapflag 'nomapchannelautojoin'. */
+
+	/* "ally_channel" is a channel shared by all your guild allies. */
+	ally_channel_enabled: true
+	ally_channel_name: "ally"
+	ally_channel_color: "Green"
+	ally_channel_autojoin: true
+}
+)

+ 2 - 1
conf/groups.conf

@@ -123,13 +123,13 @@ groups: (
 		refresh: true
 		noask: true
 		noks: true
-		main: true
 		autoloot: true
 		alootid: true
 		autotrade: true
 		request: true
 		go: true
 		breakguild: true
+		channel: true
 	}
 	permissions: {
 	}
@@ -272,6 +272,7 @@ groups: (
 		use_check: true
 		use_changemaptype: true
 		all_commands: true
+		channel_admin: true
 	}
 }
 )

+ 0 - 1
conf/help.txt

@@ -6,7 +6,6 @@
 // This file uses libconfig syntax.
 
 help: "Params: <command>\n" "Shows help for specified command."
-main: "Params: [on|off|<message>]\n" "Turns on or off main (server-wide) chat. Sends message to main chat."
 noask: "Auto rejects deals/invites."
 gmotd: "Broadcasts the Message of The Day to all players."
 me: "Params: <message>\n" "Displays normal text as a message in this format: *name message* (like /me in mIRC)."

+ 0 - 3
conf/inter_athena.conf

@@ -116,7 +116,4 @@ mapreg_db: mapreg
 // Use SQL item_db, mob_db and mob_skill_db for the map server? (yes/no)
 use_sql_db: no
 
-// Nick for sending main chat messages, similar to a whisper.
-main_chat_nick: Main
-
 import: conf/import/inter_conf.txt

+ 41 - 0
conf/msg_conf/map_msg.conf

@@ -1410,5 +1410,46 @@
 // @skillid (extension)
 1398: -- Displaying first %d partial matches:
 
+// @join
+1399: Unknown Channel (usage: %s <#channel_name>)
+1400: Unknown Channel '%s' (usage: %s <#channel_name>)
+1401: '%s' Channel is password protected (usage: %s <#channel_name> <password>)
+1402: You're not in that channel, type '@join <#channel_name>'
+1403: You're now in the '%s' channel.
+
+// @channel
+1404: %s failed.
+1405: Channel name must start with '#'.
+1406: Channel length must be between 3 and %d.
+1407: Channel '%s' is not available.
+1408: Channel password may not contain spaces.
+1409: - #%s (%d users)
+1410: ---- Public Channels ----
+1411: Unknown color '%s'.
+1412: You're not the owner of channel '%s'.
+1413: '%s' channel color updated to '%s'.
+1414: ---- Available options:
+1415: * %s create <#channel_name> <channel_password>
+1416: -- Creates a new channel.
+1417: * %s list
+1418: -- Lists all public channels.
+1419: * %s list colors
+1420: -- Lists all available colors for custom channels.
+1421: * %s setcolor <#channel_name> <color_name>
+1422: -- Changes channel text to the specified color (channel owners only).
+1423: * %s leave <#channel_name>
+1424: -- Leaves the specified channel.
+1425: You're not part of the '%s' channel.
+1426: You've left the '%s' channel.
+1427: * %s bindto <#channel_name>
+1428: -- Binds your global chat to the specified channel, sending all global messages to that channel.
+1429: * %s unbind
+1430: -- Unbinds your global chat from the attached channel, if any.
+1431: Your global chat is now binded to the '%s' channel.
+1432: Your global chat is not binded to any channel.
+1433: Your global chat is now unbinded from the '#%s' channel.
+1434: You're already in the '%s' channel.
+1435: You're now in the '#%s' channel for '%s'.
+
 //Custom translations
 import: conf/import/msg_conf.txt

+ 51 - 8
doc/atcommands.txt

@@ -24,6 +24,7 @@ The format of this file is as follows:
 	7. Guild Commands
 	8. Pet Commands
 	9. Homunculus Commands
+	10. Channel Commands
 
 ======================
 | 1. System Commands |
@@ -537,13 +538,6 @@ Displays the text as a normal message with the format "*name <message>*" instead
 
 ---------------------------------------
 
-@main {<message>}
-
-Broadcasts a message to all players with @main enabled.
-Using the command without a message will enable or disable main chat.
-
----------------------------------------
-
 @storage
 
 Opens your Kafra storage.
@@ -1428,4 +1422,53 @@ Sets the intimacy level of your homunculus, with 1000 being "Loyal".
 
 Sets the hunger level of your homunculus, with 100 being "Stuffed".
 
----------------------------------------
+---------------------------------------
+
+========================
+| 10. Channel Commands |
+========================
+
+@join <#channel_name> {<password>}
+
+Joins the specified channel.
+
+---------------------------------------
+
+@channel create <#channel_name> <channel_password>
+
+Creates a new channel.
+'allow_user_channel_creation' must be enabled in '/conf/channels.conf'.
+
+---------------------------------------
+
+@channel list
+
+Displays a list of all public channels.
+
+---------------------------------------
+
+@channel list colors
+
+Displays a list of all available colors for custom channels.
+
+---------------------------------------
+
+@channel setcolor <#channel_name> <color_name>
+
+Changes the text color of the specified channel.
+You must either be the channel's owner or have the channel_admin permission.
+
+---------------------------------------
+
+@channel leave <#channel_name>
+
+Leaves the specified channel.
+
+---------------------------------------
+
+@channel bindto <#channel_name>
+@channel unbind
+
+Binds or unbinds your global chat with the specified channel, which sends all global messages to the specified channel.
+
+---------------------------------------

+ 4 - 2
doc/permissions.txt

@@ -3,7 +3,7 @@
 //===== By: ==================================================
 //= rAthena Dev Team
 //===== Current Version: =====================================
-//= 20120606
+//= 20130402
 //===== Description: =========================================
 //= Player group permissions, configured in /conf/groups.conf.
 //============================================================
@@ -30,4 +30,6 @@ receive_requests : Ability to receive @requests.
 show_bossmobs : Ability to see boss mobs with @showmobs.
 disable_pvm : Ability to disable Player vs. Monster.
 disable_pvp : Ability to disable Player vs. Player.
-disable_commands_when_dead : Ability to disable @command usage when dead.
+disable_commands_when_dead : Ability to disable @command usage when dead.
+channel_admin : Ability to modify channel settings regardless of ownership and join password-protected
+                channels without a password.

+ 1 - 1
src/char/char.c

@@ -1484,7 +1484,7 @@ int check_char_name(char * name, char * esc_name)
 		return -2; // control chars in name
 
 	// check for reserved names
-	if( strcmpi(name, main_chat_nick) == 0 || strcmpi(name, wisp_server_name) == 0 )
+	if( strcmpi(name, wisp_server_name) == 0 )
 		return -1; // nick reserved for internal server messages
 
 	// Check Authorised letters/symbols in the name of the character

+ 0 - 3
src/char/inter.c

@@ -43,7 +43,6 @@ char default_codepage[32] = ""; //Feature by irmin.
 
 static struct accreg *accreg_pt;
 unsigned int party_share_level = 10;
-char main_chat_nick[16] = "Main";
 
 // recv. packet list
 int inter_recv_packet_length[] = {
@@ -667,8 +666,6 @@ static int inter_config_read(const char* cfgName)
 			party_share_level = atoi(w2);
 		else if(!strcmpi(w1,"log_inter"))
 			log_inter = atoi(w2);
-		else if(!strcmpi(w1,"main_chat_nick"))
-			safestrncpy(main_chat_nick, w2, sizeof(main_chat_nick));
 		else if(!strcmpi(w1,"import"))
 			inter_config_read(w2);
 	}

+ 0 - 2
src/char/inter.h

@@ -23,8 +23,6 @@ extern unsigned int party_share_level;
 extern Sql* sql_handle;
 extern Sql* lsql_handle;
 
-extern char main_chat_nick[16];
-
 int inter_accreg_tosql(int account_id, int char_id, struct accreg *reg, int type);
 
 uint64 inter_chk_lastuid(int8 flag, uint64 value);

+ 3 - 0
src/common/mmo.h

@@ -5,6 +5,7 @@
 #define	_MMO_H_
 
 #include "cbasetypes.h"
+#include "../common/db.h"
 #include <time.h>
 
 // server->client protocol version
@@ -514,7 +515,9 @@ struct guild {
 	struct guild_expulsion expulsion[MAX_GUILDEXPULSION];
 	struct guild_skill skill[MAX_GUILDSKILL];
 
+	/* TODO: still used for something? */
 	unsigned short save_flag; // for TXT saving
+	void *channel;
 };
 
 struct guild_castle {

+ 306 - 62
src/map/atcommand.c

@@ -597,7 +597,7 @@ ACMD_FUNC(who)
 				}
 				default: {
 					struct party_data *p = party_search(pl_sd->status.party_id);
-					struct guild *g = guild_search(pl_sd->status.guild_id);
+					struct guild *g = pl_sd->guild;
 
 					StringBuf_Printf(&buf, msg_txt(343), pl_sd->status.name); // "Name: %s "
 					if (pc_get_group_id(pl_sd) > 0) // Player title, if exists
@@ -700,7 +700,7 @@ ACMD_FUNC(whogm)
 		clif_displaymessage(fd, atcmd_output);
 
 		p = party_search(pl_sd->status.party_id);
-		g = guild_search(pl_sd->status.guild_id);
+		g = pl_sd->guild;
 
 		sprintf(atcmd_output,msg_txt(916),	// Party: '%s' | Guild: '%s'
 			p?p->party.name:msg_txt(917), g?g->name:msg_txt(917));	// None.
@@ -3244,7 +3244,7 @@ ACMD_FUNC(breakguild)
 
 	if (sd->status.guild_id) { // Check if the player has a guild
 		struct guild *g;
-		g = guild_search(sd->status.guild_id); // Search the guild
+		g = sd->guild; // Search the guild
 		if (g) { // Check if guild was found
 			if (sd->state.gmaster_flag) { // Check if player is guild master
 				int ret = 0;
@@ -5246,7 +5246,7 @@ ACMD_FUNC(cleargstorage)
 	struct guild_storage *gstorage;
 	nullpo_retr(-1, sd);
 
-	g = guild_search(sd->status.guild_id);
+	g = sd->guild;
 
 	if (g == NULL) {
 		clif_displaymessage(fd, msg_txt(43));
@@ -6163,7 +6163,7 @@ ACMD_FUNC(npctalk)
 	snprintf(temp, sizeof(temp), "%s : %s", name, mes);
 
 	if(ifcolor) clif_messagecolor(&nd->bl,color,temp);
-	else clif_message(&nd->bl, temp);
+	else clif_disp_overhead(&nd->bl, temp);
 
 	return 0;
 }
@@ -6227,7 +6227,7 @@ ACMD_FUNC(pettalk)
 	}
 
 	snprintf(temp, sizeof temp ,"%s : %s", pd->pet.name, mes);
-	clif_message(&pd->bl, temp);
+	clif_disp_overhead(&pd->bl, temp);
 
 	return 0;
 }
@@ -7017,7 +7017,7 @@ ACMD_FUNC(homtalk)
 	}
 
 	snprintf(temp, sizeof temp ,"%s : %s", sd->hd->homunculus.name, mes);
-	clif_message(&sd->hd->bl, temp);
+	clif_disp_overhead(&sd->hd->bl, temp);
 
 	return 0;
 }
@@ -7399,7 +7399,7 @@ ACMD_FUNC(me)
 	}
 
 	sprintf(atcmd_output, msg_txt(270), sd->status.name, tempmes);	// *%s %s*
-	clif_disp_overhead(sd, atcmd_output);
+	clif_disp_overhead(&sd->bl, atcmd_output);
 
 	return 0;
 
@@ -7944,58 +7944,6 @@ ACMD_FUNC(clone)
 	return 0;
 }
 
-/*===================================
- * Main chat [LuzZza]
- * Usage: @main <on|off|message>
- *-----------------------------------*/
-ACMD_FUNC(main)
-{
-	if( message[0] ) {
-
-		if(strcmpi(message, "on") == 0) {
-			if(!sd->state.mainchat) {
-				sd->state.mainchat = 1;
-				clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
-			} else {
-				clif_displaymessage(fd, msg_txt(381)); // Main chat already activated.
-			}
-		} else if(strcmpi(message, "off") == 0) {
-			if(sd->state.mainchat) {
-				sd->state.mainchat = 0;
-				clif_displaymessage(fd, msg_txt(382)); // Main chat has been disabled.
-			} else {
-				clif_displaymessage(fd, msg_txt(383)); // Main chat already disabled.
-			}
-		} else {
-			if(!sd->state.mainchat) {
-				sd->state.mainchat = 1;
-				clif_displaymessage(fd, msg_txt(380)); // Main chat has been activated.
-			}
-			if (sd->sc.data[SC_NOCHAT] && sd->sc.data[SC_NOCHAT]->val1&MANNER_NOCHAT) {
-				clif_displaymessage(fd, msg_txt(387));
-				return -1;
-			}
-
-			if ( battle_config.min_chat_delay ) {
-				if( DIFF_TICK(sd->cantalk_tick, gettick()) > 0 )
-					return 0;
-				sd->cantalk_tick = gettick() + battle_config.min_chat_delay;
-			}
-
-			// send the message using inter-server system
-			intif_main_message( sd, message );
-		}
-
-	} else {
-
-		if(sd->state.mainchat)
-			clif_displaymessage(fd, msg_txt(384)); // Main chat currently enabled. Usage: @main <on|off>, @main <message>.
-		else
-			clif_displaymessage(fd, msg_txt(385)); // Main chat currently disabled. Usage: @main <on|off>, @main <message>.
-	}
-	return 0;
-}
-
 /*=====================================
  * Autorejecting Invites/Deals [LuzZza]
  * Usage: @noask
@@ -8796,6 +8744,300 @@ ACMD_FUNC(cart) {
 	#undef MC_CART_MDFY
 }
 
+/* Channel System [Ind] */
+ACMD_FUNC(join) {
+	struct raChSysCh *channel;
+	char name[RACHSYS_NAME_LENGTH], pass[RACHSYS_NAME_LENGTH];
+	DBMap* channel_db = clif_get_channel_db();
+	
+	if( !message || !*message || sscanf(message, "%s %s", name, pass) < 1 ) {
+		sprintf(atcmd_output, msg_txt(1399),command); // Unknown Channel (usage: %s <#channel_name>)
+		clif_displaymessage(fd, atcmd_output);
+		return -1;
+	}
+	if( raChSys.local && strcmpi(name + 1, raChSys.local_name) == 0 ) {
+		if( !map[sd->bl.m].channel ) {
+			clif_chsys_mjoin(sd);
+			return 0;
+		} else
+			channel = map[sd->bl.m].channel;
+	} else if( raChSys.ally && sd->status.guild_id && strcmpi(name + 1, raChSys.ally_name) == 0 ) {
+		struct guild *g = sd->guild;
+		if( !g ) return -1;/* unlikely, but we wont let it crash anyway. */
+		channel = (struct raChSysCh *)g->channel;
+	} else if( !( channel = strdb_get(channel_db, name + 1) ) ) {
+		sprintf(atcmd_output, msg_txt(1400),name,command); // Unknown Channel '%s' (usage: %s <#channel_name>)
+		clif_displaymessage(fd, atcmd_output);
+		return -1;
+	}
+
+	if( idb_exists(channel->users, sd->status.char_id) ) {
+		sprintf(atcmd_output, msg_txt(1434),name); // You're already in the '%s' channel.
+		clif_displaymessage(fd, atcmd_output);
+		return -1;
+	}
+	if( channel->pass[0] != '\0'  && strcmp(channel->pass,pass) != 0 ) {
+		if( pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
+			sd->stealth = true;
+		} else {
+			sprintf(atcmd_output, msg_txt(1401),name,command); // '%s' Channel is password protected (usage: %s <#channel_name> <password>)
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		}
+	}
+
+	if( !( channel->opt & raChSys_OPT_ANNOUNCE_JOIN ) ) {
+		sprintf(atcmd_output, msg_txt(1403),name); // You're now in the '%s' channel.
+		clif_displaymessage(fd, atcmd_output);
+	}
+
+	clif_chsys_join(channel,sd);
+
+	return 0;
+}
+
+inline void atcmd_channel_help(int fd, const char *command, bool can_create) {
+	clif_displaymessage(fd, msg_txt(1414));// ---- Available options:
+	if( can_create ) {
+		sprintf(atcmd_output, msg_txt(1415),command);// * %s create <#channel_name> <channel_password>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(1416));// -- Creates a new channel.
+	}
+	sprintf(atcmd_output, msg_txt(1417),command);// * %s list
+	clif_displaymessage(fd, atcmd_output);
+	clif_displaymessage(fd, msg_txt(1418));// -- Lists all public channels.
+	if( can_create ) {
+		sprintf(atcmd_output, msg_txt(1419),command);// * %s list colors
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(1420));// -- Lists all available colors for custom channels.
+		sprintf(atcmd_output, msg_txt(1421),command);// * %s setcolor <#channel_name> <color_name>
+		clif_displaymessage(fd, atcmd_output);
+		clif_displaymessage(fd, msg_txt(1422));// -- Changes channel text to the specified color (channel owners only).
+	}
+	sprintf(atcmd_output, msg_txt(1423),command);// * %s leave <#channel_name>
+	clif_displaymessage(fd, atcmd_output);
+	clif_displaymessage(fd, msg_txt(1424));// -- Leaves the specified channel.
+	sprintf(atcmd_output, msg_txt(1427),command);// * %s bindto <#channel_name>
+	clif_displaymessage(fd, atcmd_output);
+	clif_displaymessage(fd, msg_txt(1428));// -- Binds your global chat to the specified channel, sending all global messages to that channel.
+	sprintf(atcmd_output, msg_txt(1429),command);// * %s unbind
+	clif_displaymessage(fd, atcmd_output);
+	clif_displaymessage(fd, msg_txt(1430));// -- Unbinds your global chat from the attached channel, if any.
+	sprintf(atcmd_output, msg_txt(1404),command); // %s failed.
+	clif_displaymessage(fd, atcmd_output);
+}
+
+ACMD_FUNC(channel) {
+	struct raChSysCh *channel;
+	char key[RACHSYS_NAME_LENGTH], sub1[RACHSYS_NAME_LENGTH], sub2[RACHSYS_NAME_LENGTH], sub3[RACHSYS_NAME_LENGTH];
+	unsigned char k = 0;
+	DBMap* channel_db = clif_get_channel_db();
+	sub1[0] = sub2[0] = sub3[0] = '\0';
+
+	if( !message || !*message || sscanf(message, "%s %s %s %s", key, sub1, sub2, sub3) < 1 ) {
+		atcmd_channel_help(fd,command,( raChSys.allow_user_channel_creation || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ));
+		return 0;
+	}
+
+	if( strcmpi(key,"create") == 0 && ( raChSys.allow_user_channel_creation || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) ) {
+		if( sub1[0] != '#' ) {
+			clif_displaymessage(fd, msg_txt(1405));// Channel name must start with '#'.
+			return -1;
+		} else if ( strlen(sub1) < 3 || strlen(sub1) > RACHSYS_NAME_LENGTH ) {
+			sprintf(atcmd_output, msg_txt(1406), RACHSYS_NAME_LENGTH);// Channel length must be between 3 and %d.
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		} else if ( sub3[0] != '\0' ) {
+			clif_displaymessage(fd, msg_txt(1408)); // Channel password may not contain spaces.
+			return -1;
+		}
+		if( strcmpi(sub1 + 1,raChSys.local_name) == 0 || strcmpi(sub1 + 1,raChSys.ally_name) == 0 || strdb_exists(channel_db, sub1 + 1) ) {
+			sprintf(atcmd_output, msg_txt(1407), sub1);// Channel '%s' is not available.
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		}
+
+		CREATE( channel, struct raChSysCh, 1 );
+
+		clif_chsys_create(channel,sub1 + 1,sub2,0);
+
+		channel->owner = sd->status.char_id;
+		channel->type = raChSys_PRIVATE;
+
+		if( !( channel->opt & raChSys_OPT_ANNOUNCE_JOIN ) ) {
+			sprintf(atcmd_output, msg_txt(1403),sub1); // You're now in the '%s' channel.
+			clif_displaymessage(fd, atcmd_output);
+		}
+
+		clif_chsys_join(channel,sd);
+
+	} else if ( strcmpi(key,"list") == 0 ) {
+		if( sub1[0] != '\0' && strcmpi(sub1,"colors") == 0 ) {
+			char mout[40];
+			for( k = 0; k < raChSys.colors_count; k++ ) {
+				unsigned short msg_len = 1;
+				msg_len += sprintf(mout, "[ %s list colors ] : %s",command,raChSys.colors_name[k]);
+
+				WFIFOHEAD(fd,msg_len + 12);
+				WFIFOW(fd,0) = 0x2C1;
+				WFIFOW(fd,2) = msg_len + 12;
+				WFIFOL(fd,4) = 0;
+				WFIFOL(fd,8) = raChSys.colors[k];
+				safestrncpy((char*)WFIFOP(fd,12), mout, msg_len);
+				WFIFOSET(fd, msg_len + 12);
+			}
+		} else {
+			DBIterator *iter = db_iterator(channel_db);
+			bool show_all = pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ? true : false;
+			clif_displaymessage(fd, msg_txt(1410)); // ---- Public Channels ----
+			if( raChSys.local ) {
+				sprintf(atcmd_output, msg_txt(1409), raChSys.local_name, map[sd->bl.m].channel ? db_size(map[sd->bl.m].channel->users) : 0);// - #%s ( %d users )
+				clif_displaymessage(fd, atcmd_output);
+			}
+			if( raChSys.ally && sd->status.guild_id ) {
+				struct guild *g = sd->guild;
+				if( !g ) return -1;
+				sprintf(atcmd_output, msg_txt(1409), raChSys.ally_name, db_size(((struct raChSysCh *)g->channel)->users));// - #%s ( %d users )
+				clif_displaymessage(fd, atcmd_output);
+			}
+			for(channel = dbi_first(iter); dbi_exists(iter); channel = dbi_next(iter)) {
+				if( show_all || channel->type == raChSys_PUBLIC ) {
+					sprintf(atcmd_output, msg_txt(1409), channel->name, db_size(channel->users));// - #%s (%d users)
+					clif_displaymessage(fd, atcmd_output);
+				}
+			}
+			dbi_destroy(iter);
+		}
+	} else if ( strcmpi(key,"setcolor") == 0 ) {
+
+		if( sub1[0] != '#' ) {
+			clif_displaymessage(fd, msg_txt(1405));// Channel name must start with '#'.
+			return -1;
+		}
+
+		if( !(channel = strdb_get(channel_db, sub1 + 1)) ) {
+			sprintf(atcmd_output, msg_txt(1407), sub1);// Channel '%s' is not available.
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		}
+
+		if( channel->owner != sd->status.char_id && !pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) {
+			sprintf(atcmd_output, msg_txt(1412), sub1);// You're not the owner of channel '%s'.
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		}
+
+		for( k = 0; k < raChSys.colors_count; k++ ) {
+			if( strcmpi(sub2,raChSys.colors_name[k]) == 0 )
+				break;
+		}
+		if( k == raChSys.colors_count ) {
+			sprintf(atcmd_output, msg_txt(1411), sub2);// Unknown color '%s'.
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		}
+		channel->color = k;
+		sprintf(atcmd_output, msg_txt(1413),sub1,raChSys.colors_name[k]);// '%s' channel color updated to '%s'.
+		clif_displaymessage(fd, atcmd_output);
+	} else if ( strcmpi(key,"leave") == 0 ) {
+
+		if( sub1[0] != '#' ) {
+			clif_displaymessage(fd, msg_txt(1405));// Channel name must start with '#'.
+			return -1;
+		}
+
+		for(k = 0; k < sd->channel_count; k++) {
+			if( strcmpi(sub1+1,sd->channels[k]->name) == 0 )
+				break;
+		}
+		if( k == sd->channel_count ) {
+			sprintf(atcmd_output, msg_txt(1425),sub1);// You're not part of the '%s' channel.
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		}
+		clif_chsys_left(sd->channels[k],sd);
+		sprintf(atcmd_output, msg_txt(1426),sub1); // You've left the '%s' channel.
+		clif_displaymessage(fd, atcmd_output);
+	} else if ( strcmpi(key,"bindto") == 0 ) {
+
+		if( sub1[0] != '#' ) {
+			clif_displaymessage(fd, msg_txt(1405));// Channel name must start with '#'.
+			return -1;
+		}
+
+		for(k = 0; k < sd->channel_count; k++) {
+			if( strcmpi(sub1+1,sd->channels[k]->name) == 0 )
+				break;
+		}
+		if( k == sd->channel_count ) {
+			sprintf(atcmd_output, msg_txt(1425),sub1);// You're not part of the '%s' channel.
+			clif_displaymessage(fd, atcmd_output);
+			return -1;
+		}
+
+		sd->gcbind = sd->channels[k];
+		sprintf(atcmd_output, msg_txt(1431),sub1); // Your global chat is now binded to the '%s' channel.
+		clif_displaymessage(fd, atcmd_output);
+	} else if ( strcmpi(key,"unbind") == 0 ) {
+
+		if( sd->gcbind == NULL ) {
+			clif_displaymessage(fd, msg_txt(1432));// Your global chat is not binded to any channel.
+			return -1;
+		}
+
+		sprintf(atcmd_output, msg_txt(1433),sd->gcbind->name); // Your global chat is now unbinded from the '#%s' channel.
+		clif_displaymessage(fd, atcmd_output);
+
+		sd->gcbind = NULL;
+	} else {
+		atcmd_channel_help(fd,command,( raChSys.allow_user_channel_creation || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ));
+	}
+
+	return 0;
+}
+/* debug only, delete after */
+ACMD_FUNC(fontcolor) {
+	unsigned char k;
+
+	if( !message || !*message ) {
+		char mout[40];
+		for( k = 0; k < raChSys.colors_count; k++ ) {
+			unsigned short msg_len = 1;
+			msg_len += sprintf(mout, "[ %s ] : %s",command,raChSys.colors_name[k]);
+
+			WFIFOHEAD(fd,msg_len + 12);
+			WFIFOW(fd,0) = 0x2C1;
+			WFIFOW(fd,2) = msg_len + 12;
+			WFIFOL(fd,4) = 0;
+			WFIFOL(fd,8) = raChSys.colors[k];
+			safestrncpy((char*)WFIFOP(fd,12), mout, msg_len);
+			WFIFOSET(fd, msg_len + 12);
+		}
+		return -1;
+	}
+
+	if( message[0] == '0' ) {
+		sd->fontcolor = 0;
+		pc_disguise(sd,0);
+		return 0;
+	}
+
+	for( k = 0; k < raChSys.colors_count; k++ ) {
+		if( strcmpi(message,raChSys.colors_name[k]) == 0 )
+			break;
+	}
+	if( k == raChSys.colors_count ) {
+		sprintf(atcmd_output, msg_txt(1411), message);// Unknown color '%s'.
+		clif_displaymessage(fd, atcmd_output);
+		return -1;
+	}
+
+	sd->fontcolor = k + 1;
+	pc_disguise(sd,sd->status.class_);
+
+	return 0;
+}
+
 /**
  * Fills the reference of available commands in atcommand DBMap
  **/
@@ -9008,7 +9250,6 @@ void atcommand_basecommands(void) {
 		ACMD_DEF(leave),
 		ACMD_DEF(accept),
 		ACMD_DEF(reject),
-		ACMD_DEF(main),
 		ACMD_DEF(clone),
 		ACMD_DEF2("slaveclone", clone),
 		ACMD_DEF2("evilclone", clone),
@@ -9056,7 +9297,10 @@ void atcommand_basecommands(void) {
 		ACMD_DEF2("rmvperm", addperm),
 		ACMD_DEF(unloadnpcfile),
 		ACMD_DEF(cart),
-		ACMD_DEF(mount2)
+		ACMD_DEF(mount2),
+		ACMD_DEF(join),
+		ACMD_DEF(channel),
+		ACMD_DEF(fontcolor)
 	};
 	AtCommandInfo* atcommand;
 	int i;

+ 1 - 1
src/map/battle.c

@@ -1326,7 +1326,7 @@ int battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int dama
 			}
 		}
 		if(src->type != BL_MOB) {
-			struct guild *g = guild_search(status_get_guild_id(src));
+			struct guild *g = src->type == BL_PC ? ((TBL_PC *)src)->guild : guild_search(status_get_guild_id(src));
 
 			if (class_ == MOBID_EMPERIUM && (!g || guild_checkskill(g,GD_APPROVAL) <= 0 ))
 				return 0;

File diff suppressed because it is too large
+ 423 - 180
src/map/clif.c


+ 53 - 4
src/map/clif.h

@@ -5,6 +5,7 @@
 #define _CLIF_H_
 
 #include "../common/cbasetypes.h"
+#include "../common/db.h" //dbmap
 //#include "../common/mmo.h"
 struct item;
 struct storage_data;
@@ -73,8 +74,8 @@ typedef enum send_target {
 	GUILD_NOBG,
 	DUEL,
 	DUEL_WOS,
-	CHAT_MAINCHAT,		// everyone on main chat
 	SELF,
+
 	BG,					// BattleGround System
 	BG_WOS,
 	BG_SAMEMAP,
@@ -573,7 +574,6 @@ void clif_displaymessage(const int fd, const char* mes);
 void clif_disp_onlyself(struct map_session_data *sd, const char *mes, int len);
 void clif_disp_message(struct block_list* src, const char* mes, int len, enum send_target target);
 void clif_broadcast(struct block_list* bl, const char* mes, int len, int type, enum send_target target);
-void clif_MainChatMessage(const char* message);
 void clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY, enum send_target target);
 void clif_heal(int fd,int type,int val);
 void clif_resurrection(struct block_list *bl,int type);
@@ -604,7 +604,6 @@ void clif_weather(int16 m); // [Valaris]
 void 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);
 void clif_messagecolor(struct block_list* bl, unsigned long color, const char* msg); // Mob/Npc color talk [SnakeDrak]
-void clif_message(struct block_list* bl, const char* msg);
 void clif_specialeffect_value(struct block_list* bl, int effect_id, int num, send_target target);
 
 void clif_GM_kickack(struct map_session_data *sd, int id);
@@ -612,7 +611,7 @@ void clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd);
 void clif_manner_message(struct map_session_data* sd, uint32 type);
 void clif_GM_silence(struct map_session_data* sd, struct map_session_data* tsd, uint8 type);
 
-void clif_disp_overhead(struct map_session_data *sd, const char* mes);
+void clif_disp_overhead(struct block_list *bl, const char* mes);
 
 void clif_get_weapon_view(struct map_session_data* sd, unsigned short *rhand, unsigned short *lhand);
 
@@ -767,6 +766,56 @@ enum clif_colors {
 unsigned long color_table[COLOR_MAX];
 int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg);
 
+/**
+ * Channel System
+ **/
+#define RACHSYS_NAME_LENGTH 20
+
+enum raChSysChOpt {
+	raChSys_OPT_BASE				= 0,
+	raChSys_OPT_ANNOUNCE_JOIN	= 1,
+};
+
+enum raChSysChType {
+	raChSys_PUBLIC	= 0,
+	raChSys_PRIVATE	= 1,
+	raChSys_MAP		= 2,
+	raChSys_ALLY		= 3,
+};
+
+struct {
+	unsigned long *colors;
+	char **colors_name;
+	unsigned char colors_count;
+	bool local, ally;
+	bool local_autojoin, ally_autojoin;
+	char local_name[RACHSYS_NAME_LENGTH], ally_name[RACHSYS_NAME_LENGTH];
+	unsigned char local_color, ally_color;
+	bool closing;
+	bool allow_user_channel_creation;
+} raChSys;
+
+struct raChSysCh {
+	char name[RACHSYS_NAME_LENGTH];
+	char pass[RACHSYS_NAME_LENGTH];
+	unsigned char color;
+	DBMap *users;
+	unsigned int opt;
+	unsigned int owner;
+	enum raChSysChType type;
+	uint16 m;
+};
+
+struct DBMap* clif_get_channel_db(void);
+void clif_chsys_create(struct raChSysCh *channel, char *name, char *pass, unsigned char color);
+void clif_chsys_msg(struct raChSysCh *channel, struct map_session_data *sd, char *msg);
+void clif_chsys_send(struct raChSysCh *channel, struct map_session_data *sd, char *msg);
+void clif_chsys_join(struct raChSysCh *channel, struct map_session_data *sd);
+void clif_chsys_left(struct raChSysCh *channel, struct map_session_data *sd);
+void clif_chsys_delete(struct raChSysCh *channel);
+void clif_chsys_mjoin(struct map_session_data *sd);
+void clif_read_channels_config(void);
+
 #define clif_menuskill_clear(sd) (sd)->menuskill_id = (sd)->menuskill_val = (sd)->menuskill_val2 = 0;
 
 #endif /* _CLIF_H_ */

+ 111 - 44
src/map/guild.c

@@ -259,7 +259,7 @@ int guild_getposition(struct guild* g, struct map_session_data* sd)
 {
 	int i;
 
-	if( g == NULL && (g=guild_search(sd->status.guild_id)) == NULL )
+	if( g == NULL && (g=sd->guild) == NULL )
 		return -1;
 	
 	ARR_FIND( 0, g->max_member, i, g->member[i].account_id == sd->status.account_id && g->member[i].char_id == sd->status.char_id );
@@ -481,8 +481,7 @@ int guild_recv_noinfo(int guild_id)
 	struct s_mapiterator* iter;
 
 	iter = mapit_getallusers();
-	for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) )
-	{
+	for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) {
 		if( sd->status.guild_id == guild_id )
 			sd->status.guild_id = 0; // erase guild
 	}
@@ -499,20 +498,52 @@ int guild_recv_info(struct guild *sg)
 	DBData data;
 	struct map_session_data *sd;
 	bool guild_new = false;
+	void *aChSysSave = NULL;
 
 	nullpo_ret(sg);
 
-	if((g = (struct guild*)idb_get(guild_db,sg->guild_id))==NULL)
-	{
+	if((g = guild_search(sg->guild_id))==NULL) {
 		guild_new = true;
 		g=(struct guild *)aCalloc(1,sizeof(struct guild));
 		idb_put(guild_db,sg->guild_id,g);
-		before=*sg;
+		if( raChSys.ally ) {
+			struct raChSysCh *channel;
+
+			CREATE(channel, struct raChSysCh , 1);
+			safestrncpy(channel->name, raChSys.ally_name, RACHSYS_NAME_LENGTH);
+			channel->type = raChSys_ALLY;
+
+			clif_chsys_create(channel,NULL,NULL,raChSys.ally_color);
+			if( raChSys.ally_autojoin ) {
+				struct s_mapiterator* iter = mapit_getallusers();
+
+				for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) {
+					if( sd->status.guild_id ) {
+						if( sd->status.guild_id == sg->guild_id ) {
+							clif_chsys_join(channel,sd);
+							sd->guild = g;
+							continue;
+						}
+
+						for (i = 0; i < MAX_GUILDALLIANCE; i++) {
+							if( sg->alliance[i].guild_id == sd->status.guild_id ) {
+								clif_chsys_join(channel,sd);
+								break;
+							}
+						}
+					}
+				}
 
-        //Perform the check on the user because the first load
+				mapit_free(iter);
+			}
+
+			aChSysSave = (void*)channel;
+
+		}
+		before=*sg;
+		//Perform the check on the user because the first load
 		guild_check_member(sg);
-		if ((sd = map_nick2sd(sg->master)) != NULL)
-		{
+		if ((sd = map_nick2sd(sg->master)) != NULL) {
 			//If the guild master is online the first time the guild_info is received,
 			//that means he was the first to join, so apply guild skill blocking here.
 			if( battle_config.guild_skill_relog_delay )
@@ -523,12 +554,16 @@ int guild_recv_info(struct guild *sg)
 			clif_charnameupdate(sd); // [LuzZza]
 			clif_guild_masterormember(sd);
 		}
-	}else
+	} else {
 		before=*g;
+		if( g->channel )
+			aChSysSave = g->channel;
+	}
 	memcpy(g,sg,sizeof(struct guild));
 
-	if(g->max_member > MAX_GUILD)
-	{
+	g->channel = aChSysSave;
+
+	if(g->max_member > MAX_GUILD) {
 		ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD);
 		g->max_member = MAX_GUILD;
 	}
@@ -570,10 +605,9 @@ int guild_recv_info(struct guild *sg)
     }
 
     //Occurrence of an event
-	if (guild_infoevent_db->remove(guild_infoevent_db, db_i2key(sg->guild_id), &data))
-	{
+	if (guild_infoevent_db->remove(guild_infoevent_db, db_i2key(sg->guild_id), &data)) {
 		struct eventlist *ev = db_data2ptr(&data), *ev2;
-		while(ev){
+		while(ev) {
 			npc_event_do(ev->name);
 			ev2=ev->next;
 			aFree(ev);
@@ -593,7 +627,7 @@ int guild_invite(struct map_session_data *sd, struct map_session_data *tsd) {
 
 	nullpo_ret(sd);
 
-	g=guild_search(sd->status.guild_id);
+	g=sd->guild;
 
 	if(tsd==NULL || g==NULL)
 		return 0;
@@ -684,6 +718,7 @@ int guild_reply_invite(struct map_session_data* sd, int guild_id, int flag)
 			return 0;
 		}
 
+		sd->guild = g;
 		guild_makemember(&m,sd);
 		intif_guild_addmember(guild_id, &m);
 		//TODO: send a minimap update to this player
@@ -705,8 +740,7 @@ void guild_member_joined(struct map_session_data *sd)
 		guild_request_info(sd->status.guild_id);
 		return;
 	}
-	if (strcmp(sd->status.name,g->master) == 0)
-	{	// set the Guild Master flag
+	if (strcmp(sd->status.name,g->master) == 0) {	// set the Guild Master flag
 		sd->state.gmaster_flag = g;
 		// prevent Guild Skills from being used directly after relog
 		if( battle_config.guild_skill_relog_delay )
@@ -715,8 +749,22 @@ void guild_member_joined(struct map_session_data *sd)
 	i = guild_getindex(g, sd->status.account_id, sd->status.char_id);
 	if (i == -1)
 		sd->status.guild_id = 0;
-	else
+	else {
 		g->member[i].sd = sd;
+		sd->guild = g;
+
+		if( raChSys.ally && raChSys.ally_autojoin ) {
+			struct guild* sg = NULL;
+			clif_chsys_join((struct raChSysCh*)g->channel,sd);
+
+			for (i = 0; i < MAX_GUILDALLIANCE; i++) {
+				if( g->alliance[i].guild_id && (sg = guild_search(g->alliance[i].guild_id) ) ) {
+					clif_chsys_join((struct raChSysCh*)sg->channel,sd);
+					break;
+				}
+			}
+		}
+	}
 }
 
 /*==========================================
@@ -775,7 +823,7 @@ int guild_leave(struct map_session_data* sd, int guild_id, int account_id, int c
 
 	nullpo_ret(sd);
 
-	g = guild_search(sd->status.guild_id);
+	g = sd->guild;
 
 	if(g==NULL)
 		return 0;
@@ -800,7 +848,7 @@ int guild_expulsion(struct map_session_data* sd, int guild_id, int account_id, i
 
 	nullpo_ret(sd);
 
-	g = guild_search(sd->status.guild_id);
+	g = sd->guild;
 
 	if(g==NULL)
 		return 0;
@@ -859,8 +907,14 @@ int guild_member_withdraw(int guild_id, int account_id, int char_id, int flag, c
 		if (sd->state.storage_flag == 2) //Close the guild storage.
 			storage_guild_storageclose(sd);
 		guild_send_dot_remove(sd);
-
+		if( raChSys.ally ) {
+			for (i = 0; i < sd->channel_count; i++) {
+				if( sd->channels[i] && sd->channels[i]->type == raChSys_ALLY )
+					clif_chsys_left(sd->channels[i],sd);
+			}
+		}
 		sd->status.guild_id = 0;
+		sd->guild = NULL;
 		sd->guild_emblem_id = 0;
 		
 		clif_charnameupdate(sd); //Update display name [Skotlex]
@@ -878,7 +932,7 @@ int guild_send_memberinfoshort(struct map_session_data *sd,int online)
 	if(sd->status.guild_id <= 0)
 		return 0;
 
-	if(!(g = guild_search(sd->status.guild_id)))
+	if(!(g = sd->guild))
 		return 0;
 
 	intif_guild_memberinfoshort(g->guild_id,
@@ -1097,7 +1151,7 @@ int guild_change_emblem(struct map_session_data *sd,int len,const char *data)
 	nullpo_ret(sd);
 
 	if (battle_config.require_glory_guild &&
-		!((g = guild_search(sd->status.guild_id)) && guild_checkskill(g, GD_GLORYGUILD)>0)) {
+		!((g = sd->guild) && guild_checkskill(g, GD_GLORYGUILD)>0)) {
 		clif_skill_fail(sd,GD_GLORYGUILD,USESKILL_FAIL_LEVEL,0);
 		return 0;
 	}
@@ -1196,7 +1250,7 @@ unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp)
 	if (!exp) return 0;
 	
 	if (sd->status.guild_id == 0 ||
-		(g = guild_search(sd->status.guild_id)) == NULL ||
+		(g = sd->guild) == NULL ||
 		(per = guild_getposition(g,sd)) < 0 ||
 		(per = g->position[per].exp_mode) < 1)
 		return 0;
@@ -1226,7 +1280,7 @@ int guild_getexp(struct map_session_data *sd,int exp)
 	struct guild_expcache *c;
 	nullpo_ret(sd);
 
-	if (sd->status.guild_id == 0 || guild_search(sd->status.guild_id) == NULL)
+	if (sd->status.guild_id == 0 || sd->guild == NULL)
 		return 0;
 
 	c = db_data2ptr(guild_expcache_db->ensure(guild_expcache_db, db_i2key(sd->status.char_id), create_expcache, sd));
@@ -1249,7 +1303,7 @@ int guild_skillup(TBL_PC* sd, uint16 skill_id)
 	nullpo_ret(sd);
 
 	if( idx < 0 || idx >= MAX_GUILDSKILL || // not a guild skill
-			sd->status.guild_id == 0 || (g=guild_search(sd->status.guild_id)) == NULL || // no guild
+			sd->status.guild_id == 0 || (g=sd->guild) == NULL || // no guild
 			strcmp(sd->status.name, g->master) ) // not the guild master
 		return 0;
 
@@ -1380,8 +1434,8 @@ int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd)
 	if(tsd==NULL || tsd->status.guild_id<=0)
 		return 0;
 
-	g[0]=guild_search(sd->status.guild_id);
-	g[1]=guild_search(tsd->status.guild_id);
+	g[0]=sd->guild;
+	g[1]=tsd->guild;
 
 	if(g[0]==NULL || g[1]==NULL)
 		return 0;
@@ -1433,15 +1487,15 @@ int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag)
 		return 0;
 	}
 
-    if (sd->guild_alliance != tsd->status.guild_id) // proposed guild_id alliance doesn't match tsd guildid
+	if (sd->guild_alliance != tsd->status.guild_id) // proposed guild_id alliance doesn't match tsd guildid
 		return 0;
 
-    if (flag == 1) { // consent
+	if (flag == 1) { // consent
 		int i;
 
-        struct guild *g, *tg; // Reconfirm the number of alliance
-		g=guild_search(sd->status.guild_id);
-		tg=guild_search(tsd->status.guild_id);
+	struct guild *g, *tg; // Reconfirm the number of alliance
+		g=sd->guild;
+		tg=tsd->guild;
 		
 		if(g==NULL || guild_get_alliance_count(g,0) >= battle_config.max_guild_alliance){
 			clif_guild_allianceack(sd,4);
@@ -1467,11 +1521,11 @@ int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag)
 					tsd->status.account_id,sd->status.account_id,9 );
 		}
 
-        // inform other servers
+	// inform other servers
 		intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
 			sd->status.account_id,tsd->status.account_id,0 );
 		return 0;
-    } else { // deny
+	} else { // deny
 		sd->guild_alliance=0;
 		sd->guild_alliance_account=0;
 		if(tsd!=NULL)
@@ -1506,7 +1560,7 @@ int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd)
 
 	nullpo_ret(sd);
 
-	g=guild_search(sd->status.guild_id);
+	g=sd->guild;
 	if(g==NULL || tsd==NULL)
 		return 0;
 
@@ -1683,6 +1737,7 @@ int guild_broken(int guild_id,int flag)
 			if(sd->state.storage_flag == 2)
 				storage_guild_storage_quit(sd,1);
 			sd->status.guild_id=0;
+			sd->guild = NULL;
 			clif_guild_broken(g->member[i].sd,0);
 			clif_charnameupdate(sd); // [LuzZza]
 		}
@@ -1691,6 +1746,11 @@ int guild_broken(int guild_id,int flag)
 	guild_db->foreach(guild_db,guild_broken_sub,guild_id);
 	castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id);
 	guild_storage_delete(guild_id);
+	if( raChSys.ally ) {
+		if( g->channel != NULL ) {
+			clif_chsys_delete(( struct raChSysCh * )g->channel);
+		}
+	}
 	idb_remove(guild_db,guild_id);
 	return 0;
 }
@@ -1743,14 +1803,12 @@ int guild_gm_changed(int guild_id, int account_id, int char_id)
 	g->member[0].position = 0; //Position 0: guild Master.
 	strcpy(g->master, g->member[0].name);
 
-	if (g->member[pos].sd && g->member[pos].sd->fd)
-	{
+	if (g->member[pos].sd && g->member[pos].sd->fd) {
 		clif_displaymessage(g->member[pos].sd->fd, msg_txt(678)); //"You no longer are the Guild Master."
 		g->member[pos].sd->state.gmaster_flag = 0;
 	}
 	
-	if (g->member[0].sd && g->member[0].sd->fd)
-	{
+	if (g->member[0].sd && g->member[0].sd->fd) {
 		clif_displaymessage(g->member[0].sd->fd, msg_txt(679)); //"You have become the Guild Master!"
 		g->member[0].sd->state.gmaster_flag = g;
 		//Block his skills for 5 minutes to prevent abuse.
@@ -1780,7 +1838,7 @@ int guild_break(struct map_session_data *sd,char *name)
 
 	nullpo_ret(sd);
 
-	if( (g=guild_search(sd->status.guild_id))==NULL )
+	if( (g=sd->guild)==NULL )
 		return 0;
 	if(strcmp(g->name,name)!=0)
 		return 0;
@@ -2136,12 +2194,21 @@ void do_init_guild(void) {
 }
 
 void do_final_guild(void) {
-	
+	DBIterator *iter = db_iterator(guild_db);
+	struct guild *g;
+
+	for( g = dbi_first(iter); dbi_exists(iter); g = dbi_next(iter) ) {
+		if( g->channel != NULL )
+			clif_chsys_delete((struct raChSysCh *)g->channel);
+	}
+
+	dbi_destroy(iter);
+
 	db_destroy(guild_db);
 	castle_db->destroy(castle_db,guild_castle_db_final);
 	guild_expcache_db->destroy(guild_expcache_db,guild_expcache_db_final);
 	guild_infoevent_db->destroy(guild_infoevent_db,eventlist_db_final);
 	ers_destroy(expcache_ers);
-			
+		
 	aFree(guild_flags);/* never empty; created on boot */
 }

+ 1 - 6
src/map/intif.c

@@ -170,10 +170,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)
 {
 	// Send to the local players
-	if (fontColor == 0xFE000000) // This is main chat message [LuzZza]
-		clif_MainChatMessage(mes);
-	else
-		clif_broadcast2(NULL, mes, len, fontColor, fontType, fontSize, fontAlign, fontY, ALL_CLIENT);
+	clif_broadcast2(NULL, mes, len, fontColor, fontType, fontSize, fontAlign, fontY, ALL_CLIENT);
 
 	if (CheckForCharServer())
 		return 0;
@@ -2183,8 +2180,6 @@ int intif_parse(int fd)
 	case 0x3800:
 		if (RFIFOL(fd,4) == 0xFF000000) //Normal announce.
 			clif_broadcast(NULL, (char *) RFIFOP(fd,16), packet_len-16, 0, ALL_CLIENT);
-		else if (RFIFOL(fd,4) == 0xFE000000) //Main chat message [LuzZza]
-			clif_MainChatMessage((char *)RFIFOP(fd,16));
 		else //Color announce.
 			clif_broadcast2(NULL, (char *) RFIFOP(fd,16), packet_len-16, RFIFOL(fd,4), RFIFOW(fd,8), RFIFOW(fd,10), RFIFOW(fd,12), RFIFOW(fd,14), ALL_CLIENT);
 		break;

+ 26 - 9
src/map/map.c

@@ -81,10 +81,6 @@ char log_db_pw[32] = "ragnarok";
 char log_db_db[32] = "log";
 Sql* logmysql_handle;
 
-// This param using for sending mainchat
-// messages like whispers to this nick. [LuzZza]
-char main_chat_nick[16] = "Main";
-
 // DBMap declaartion
 static DBMap* id_db=NULL; // int id -> struct block_list*
 static DBMap* pc_db=NULL; // int id -> struct map_session_data*
@@ -1698,10 +1694,33 @@ int map_quit(struct map_session_data *sd) {
 		unit_remove_map(&sd->ed->bl,CLR_TELEPORT);
 	}
 
+	if( raChSys.ally && sd->status.guild_id ) {
+		struct guild *g = sd->guild, *sg;
+		if( g ) {
+			if( idb_exists(((struct raChSysCh *)g->channel)->users, sd->status.char_id) )
+				clif_chsys_left((struct raChSysCh *)g->channel,sd);
+			for (i = 0; i < MAX_GUILDALLIANCE; i++) {
+				if( g->alliance[i].guild_id && (sg = guild_search(g->alliance[i].guild_id) ) ) {
+					if( idb_exists(((struct raChSysCh *)sg->channel)->users, sd->status.char_id) )
+						clif_chsys_left((struct raChSysCh *)sg->channel,sd);
+					break;
+				}
+			}
+		}
+	}
+
+	if( sd->channel_count ) {
+		for( i = 0; i < sd->channel_count; i++ ) {
+			if( sd->channels[i] != NULL )
+				clif_chsys_left(sd->channels[i],sd);
+		}
+		if( raChSys.closing )
+			aFree(sd->channels);
+	}
+
 	unit_remove_map_pc(sd,CLR_TELEPORT);
 
-	if( map[sd->bl.m].instance_id )
-	{ // Avoid map conflicts and warnings on next login
+	if( map[sd->bl.m].instance_id ) { // Avoid map conflicts and warnings on next login
 		int16 m;
 		struct point *pt;
 		if( map[sd->bl.m].save.map )
@@ -3354,9 +3373,6 @@ int inter_config_read(char *cfgName)
 		if( sscanf(line,"%[^:]: %[^\r\n]",w1,w2) < 2 )
 			continue;
 
-		if(strcmpi(w1, "main_chat_nick")==0)
-			safestrncpy(main_chat_nick, w2, sizeof(main_chat_nick));
-		else
 		if(strcmpi(w1,"item_db_db")==0)
 			strcpy(item_db_db,w2);
 		else
@@ -3553,6 +3569,7 @@ void do_final(void)
 	struct s_mapiterator* iter;
 
 	ShowStatus("Terminating...\n");
+	raChSys.closing = true;
 
 	//Ladies and babies first.
 	iter = mapit_getallusers();

+ 5 - 2
src/map/map.h

@@ -20,6 +20,7 @@
 
 struct npc_data;
 struct item_data;
+struct raChSysCh;
 
 enum E_MAPSERVER_ST
 {
@@ -573,6 +574,7 @@ struct map_data {
 		unsigned guildlock :1;
 		unsigned src4instance : 1; // To flag this map when it's used as a src map for instances
 		unsigned reset :1; // [Daegaladh]
+		unsigned chsysnolocalaj : 1;
 	} flag;
 	struct point save;
 	struct npc_data *npc[MAX_NPC_PER_MAP];
@@ -598,6 +600,9 @@ struct map_data {
 	// Instance Variables
 	int instance_id;
 	int instance_src_map;
+
+	/* rAthena Local Chat */
+	struct raChSysCh *channel;
 };
 
 /// Stores information about a remote map (for multi-mapserver setups).
@@ -775,8 +780,6 @@ typedef struct elemental_data	TBL_ELEM;
 	( ((bl) == (struct block_list*)NULL || (bl)->type != (type_)) ? (T ## type_ *)NULL : (T ## type_ *)(bl) )
 
 
-extern char main_chat_nick[16];
-
 #ifdef BETA_THREAD_TEST
 
 extern char default_codepage[32];

+ 2 - 0
src/map/npc.c

@@ -3424,6 +3424,8 @@ static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, con
 		map[m].flag.guildlock=state;
 	else if (!strcmpi(w3,"reset"))
 		map[m].flag.reset=state;
+	else if (!strcmpi(w3,"nomapchannelautojoin"))
+		map[m].flag.chsysnolocalaj = state;
 	else
 		ShowError("npc_parse_mapflag: unrecognized mapflag '%s' (file '%s', line '%d').\n", w3, filepath, strline(buffer,start-buffer));
 

+ 9 - 9
src/map/pc.c

@@ -1228,8 +1228,7 @@ int pc_reg_received(struct map_session_data *sd)
 	intif_Mail_requestinbox(sd->status.char_id, 0); // MAIL SYSTEM - Request Mail Inbox
 	intif_request_questlog(sd);
 
-	if (sd->state.connect_new == 0 && sd->fd)
-	{	//Character already loaded map! Gotta trigger LoadEndAck manually.
+	if (sd->state.connect_new == 0 && sd->fd) { //Character already loaded map! Gotta trigger LoadEndAck manually.
 		sd->state.connect_new = 1;
 		clif_parse_LoadEndAck(sd->fd, sd);
 	}
@@ -4743,6 +4742,10 @@ int pc_setpos(struct map_session_data* sd, unsigned short mapindex, int x, int y
 			clif_displaymessage (sd->fd, msg_txt(276)); // "You can't open a shop on this map"
 			vending_closevending(sd);
 		}
+
+		if( raChSys.local && map[sd->bl.m].channel && idb_exists(map[sd->bl.m].channel->users, sd->status.char_id) ) {
+			clif_chsys_left(map[sd->bl.m].channel,sd);
+		}
 	}
 
 	if( m < 0 )
@@ -4924,16 +4927,13 @@ int pc_memo(struct map_session_data* sd, int pos)
 int pc_checkskill(struct map_session_data *sd,uint16 skill_id)
 {
 	if(sd == NULL) return 0;
-	if( skill_id >= GD_SKILLBASE && skill_id < GD_MAX )
-	{
+	if( skill_id >= GD_SKILLBASE && skill_id < GD_MAX ) {
 		struct guild *g;
 
-		if( sd->status.guild_id>0 && (g=guild_search(sd->status.guild_id))!=NULL)
+		if( sd->status.guild_id>0 && (g=sd->guild)!=NULL)
 			return guild_checkskill(g,skill_id);
 		return 0;
-	}
-	else if(skill_id >= ARRAYLENGTH(sd->status.skill) )
-	{
+	} else if(skill_id >= ARRAYLENGTH(sd->status.skill) ) {
 		ShowError("pc_checkskill: Invalid skill id %d (char_id=%d).\n", skill_id, sd->status.char_id);
 		return 0;
 	}
@@ -8570,7 +8570,7 @@ int pc_equipitem(struct map_session_data *sd,int n,int req_pos)
 	}
 	if(pos & EQP_SHOES)
 		clif_changelook(&sd->bl,LOOK_SHOES,0);
-	if(pos&EQP_GARMENT) {
+	if(pos&EQP_GARMENT && pc_checkequip(sd,EQP_COSTUME_GARMENT) == -1) {
 		sd->status.robe = id ? id->look : 0;
 		clif_changelook(&sd->bl, LOOK_ROBE, sd->status.robe);
 	}

+ 8 - 1
src/map/pc.h

@@ -136,7 +136,6 @@ struct map_session_data {
 		unsigned int showdelay :1;
 		unsigned int showexp :1;
 		unsigned int showzeny :1;
-		unsigned int mainchat :1; //[LuzZza]
 		unsigned int noask :1; // [LuzZza]
 		unsigned int trading :1; //[Skotlex] is 1 only after a trade has started.
 		unsigned int deal_locked :2; //1: Clicked on OK. 2: Clicked on TRADE
@@ -386,6 +385,7 @@ struct map_session_data {
 	int party_invite, party_invite_account; // for handling party invitation (holds party id and account id)
 	int adopt_invite; // Adoption
 
+	struct guild *guild; // [Ind] speed everything up
 	int guild_invite,guild_invite_account;
 	int guild_emblem_id,guild_alliance,guild_alliance_account;
 	short guild_x,guild_y; // For guildmate position display. [Skotlex] should be short [zzo]
@@ -496,6 +496,13 @@ struct map_session_data {
 
 	int shadowform_id;
 
+	/* Channel System [Ind] */
+	struct raChSysCh **channels;
+	unsigned char channel_count;
+	struct raChSysCh *gcbind;
+	bool stealth;
+	unsigned char fontcolor; /* debug-only */
+
 	// temporary debugging of bug #3504
 	const char* delunit_prevfile;
 	int delunit_prevline;

+ 5 - 3
src/map/pc_groups.h

@@ -39,10 +39,11 @@ enum e_pc_permission {
 	PC_PERM_USE_CHANGEMAPTYPE   = 0x004000,
 	PC_PERM_USE_ALL_COMMANDS    = 0x008000,
 	PC_PERM_RECEIVE_REQUESTS    = 0x010000,
-	PC_PERM_SHOW_BOSS			= 0x020000,
-	PC_PERM_DISABLE_PVM			= 0x040000,
-	PC_PERM_DISABLE_PVP			= 0x080000,
+	PC_PERM_SHOW_BOSS           = 0x020000,
+	PC_PERM_DISABLE_PVM         = 0x040000,
+	PC_PERM_DISABLE_PVP         = 0x080000,
 	PC_PERM_DISABLE_CMD_DEAD    = 0x100000,
+	PC_PERM_CHANNEL_ADMIN       = 0x200000,
 };
 
 static const struct {
@@ -70,6 +71,7 @@ static const struct {
 	{ "disable_pvm", PC_PERM_DISABLE_PVM },
 	{ "disable_pvp", PC_PERM_DISABLE_PVP },
 	{ "disable_commands_when_dead", PC_PERM_DISABLE_CMD_DEAD },
+	{ "channel_admin", PC_PERM_CHANNEL_ADMIN },
 };
 
 #endif // _PC_GROUPS_H_

+ 6 - 12
src/map/script.c

@@ -7259,22 +7259,16 @@ BUILDIN_FUNC(strcharinfo)
 			script_pushstrcopy(st,sd->status.name);
 			break;
 		case 1:
-			if( ( p = party_search(sd->status.party_id) ) != NULL )
-			{
+			if( ( p = party_search(sd->status.party_id) ) != NULL ) {
 				script_pushstrcopy(st,p->party.name);
-			}
-			else
-			{
+			} else {
 				script_pushconststr(st,"");
 			}
 			break;
 		case 2:
-			if( ( g = guild_search(sd->status.guild_id) ) != NULL )
-			{
+			if( ( g = guild_search(sd->status.guild_id) ) != NULL ) {
 				script_pushstrcopy(st,g->name);
-			}
-			else
-			{
+			} else {
 				script_pushconststr(st,"");
 			}
 			break;
@@ -12973,7 +12967,7 @@ BUILDIN_FUNC(npctalk)
 		safestrncpy(name, nd->name, sizeof(name));
 		strtok(name, "#"); // discard extra name identifier if present
 		safesnprintf(message, sizeof(message), "%s : %s", name, str);
-		clif_message(&nd->bl, message);
+		clif_disp_overhead(&nd->bl, message);
 	}
 
 	return 0;
@@ -15459,7 +15453,7 @@ BUILDIN_FUNC(unittalk)
 		struct StringBuf sbuf;
 		StringBuf_Init(&sbuf);
 		StringBuf_Printf(&sbuf, "%s : %s", status_get_name(bl), message);
-		clif_message(bl, StringBuf_Value(&sbuf));
+		clif_disp_overhead(bl, StringBuf_Value(&sbuf));
 		if( bl->type == BL_PC )
 			clif_displaymessage(((TBL_PC*)bl)->fd, StringBuf_Value(&sbuf));
 		StringBuf_Destroy(&sbuf);

+ 1 - 1
src/map/skill.c

@@ -5992,7 +5992,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, ui
 			//NOTE: mobs don't have the sprite animation that is used when performing this skill (will cause glitches)
 			char temp[70];
 			snprintf(temp, sizeof(temp), "%s : %s !!",md->name,skill_db[skill_id].desc);
-			clif_message(&md->bl,temp);
+			clif_disp_overhead(&md->bl,temp);
 		}
 		break;
 

Some files were not shown because too many files changed in this diff