Channel System is Expanded! (#1933)

* Many changes on conf/channel.conf!
- Now capable of setting default values for channels through the config such as the channel name, channel password, member capacity, chat color, chat delay, and more.
* Many new channel script commands!
- Added script commands channel_create, channel_setopt channel_setcolor, channel_setpass, channel_setgroup, channel_chat, channel_ban, channel_unban, channel_kick, and channel_delete.
This commit is contained in:
Cydh Ramdh 2017-04-20 01:12:00 +07:00 committed by Aleos
parent 97f989f7d8
commit c87dba5a52
17 changed files with 1576 additions and 469 deletions

View File

@ -1,16 +1,5 @@
// Channel System Configuration File
chsys: (
{
/* Default channels (available to all players) */
default_channels: {
/* channel_name : channel_messages_color */
main: "Yellow"
support: "Blue"
trade: "Red"
chat: "Default"
/* Add as many channels as you'd like. */
}
channel_config: {
/* Colors available */
colors: {
@ -21,24 +10,112 @@ chsys: (
Cyan: "0x00b89d"
Yellow: "0xffff90"
Green: "0x28bf00"
White: "0xFFFFFF"
Purple: "0xD67FFF"
LightGreen: "0xB6FF00"
Normal: "0x00ff00"
/* Add as many colors as you'd like. */
}
/* Allow users to create their own (private) channels through @channel command? */
/* (must also allow players to use @channel in groups.conf) */
allow_user_channel_creation: true
/**
* Private channel config
* - Always CHAN_TYPE_PUBLIC
* - Always displayed in chat log as "#channel_name: <name>: <chat>"
* - ID of private channels start at 1000
**/
private_channel: {
allow: true // (bool) Allow player to create their own channel?
color: "Default" // (string) Default color, see colors
delay: 1000 // (int) Chat delay for each member
max_member: 1000 // (int) Max members
self_notif: true // (bool) Show message when player enters or leaves the channel
join_notif: false // (bool) Show message when player joined the channel
leave_notif: false // (bool) Show message when player leaves the channel
/* Moderation feature for channel owner, allowed to: */
ban: true // (bool) Ban players
kick: true // (bool) Kick players
color_override: false // (bool) Allow players to change the private channel color to their own
change_delay: false // (bool) Allow players to change the private channel delay to their own
}
/* "map_local_channel" is an instanced channel unique to each map. */
map_local_channel: false
map_local_channel_name: "map"
map_local_channel_color: "Yellow"
map_local_channel_autojoin: true /* Disable autojoin in specific maps through mapflag 'nomapchannelautojoin'. */
/**
* Default server channels
**/
channels: (
/**
* Structure
{
name: "#channel" // (string) Channel name
password: "" // (string) Channel password
alias: "[Channel]" // (string) Message from this that channel will be displayed instead the channel name
color: "Default" // (string) Channel color
type: "CHAN_TYPE_PUBLIC" // (string) Channel type: CHAN_TYPE_PUBLIC, CHAN_TYPE_ALLY, CHAN_TYPE_MAP
autojoin: false // (bool) Players will auto join channel
delay: 1000 // (int) Chat delay for each player
leave: true // (bool) Player is allowed to leave the channel
chat: true // (bool) Player is allowed to chat on this channel
color_override: false // (bool) Allow players to change the private channel color to their own
self_notif: true // (bool) Show message when player enters or leaves the channel
join_notif: false // (bool) Show message when player joined the channel
leave_notif: false // (bool) Show message when player leaves the channel
groupid: (0,..,99) // (list,int) Only players with valid group IDs are allowed to join. Group with 'channel_admin' can always enter the channel.
/// All values above are default settings
}, // Use comma if followed by other channel
**/
{
name: "#global"
alias: "[Global]"
color: "White"
type: "CHAN_TYPE_PUBLIC"
delay: 1000
autojoin: false
leave: false
},
{
name: "#support"
alias: "[Support]"
color: "Blue"
type: "CHAN_TYPE_PUBLIC"
delay: 1000
autojoin: false
},
{
name: "#trade"
alias: "[Trade]"
color: "LightGreen"
type: "CHAN_TYPE_PUBLIC"
delay: 1000
autojoin: false
}
)
/* "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
/**
* Channel config for guild alliance
* For the structure, see the 'channels' above
**/
ally: {
name: "#ally"
alias: "[Ally]"
color: "Green"
type: "CHAN_TYPE_ALLY" // DO NOT CHANGE THIS VALUE
delay: 1000
autojoin: false
leave: true
chat: true
}
/**
* Channel config for map channel
* For the structure, see the 'channels' above
**/
map: {
name: "#map"
alias: "[Map]"
color: "Yellow"
type: "CHAN_TYPE_MAP" // DO NOT CHANGE THIS VALUE
delay: 1000
autojoin: false
leave: true
chat: true
}
}
)

View File

@ -119,6 +119,9 @@ help_txt: conf/help.txt
help2_txt: conf/help2.txt
charhelp_txt: conf/charhelp.txt
// Load channel config from
channel_conf: conf/channels.conf
// Maps:
import: conf/maps_athena.conf

View File

@ -812,7 +812,20 @@
758: Baby Gunslinger
759: Baby Rebellion
//760-899 free
// Channel System
760: You cannot join channel '%s'. Limit of %d has been met.
761: %s %s has joined.
762: You cannot leave channel '%s'.
763: %s %s left.
764: You cannot change the color for channel '%s'.
765: You're not allowed to ban a player.
766: You cannot kick a player from channel '%s'.
767: You're not allowed to kick a player.
768: %s %s has been kicked.
769: %s %s has been banned.
770: %s %s has been unbanned.
//771-899 free
//------------------------------------
// More atcommands message

View File

@ -1,4 +1,4 @@
//===== rAthena Documentation ================================
//===== rAthena Documentation ================================
//= rAthena Script Commands
//===== By: ==================================================
//= rAthena Dev Team
@ -1033,6 +1033,7 @@ From here on, we will have the commands sorted as follow:
11.- Homunculus commands.
12.- Mercenary commands.
13.- Party commands.
14.- Channel commands.
=====================
|1.- Basic commands.|
@ -9444,3 +9445,159 @@ else false if the leave failed.
If <char id> is specified, the specified player is used rather than the attached one.
---------------------------------------
========================
|14.- Channel commands.|
========================
---------------------------------------
*channel_create "<chname>","<alias>"{,"<password>"{<option>{,<delay>{,<color>{,<char_id>}}}}};
Creates a public channel with <chname> as the channel name. To protect the
channel, use <password> or write "null" to create it without a password.
Channel name must start with '#' and cannot be the same as the map or ally
channel names.
<alias> will be used to change the channel name when the channel message
is displayed.
<option> values are:
CHAN_OPT_BASE - Default option including CHAN_OPT_ANNOUNCE_SELF|CHAN_OPT_MSG_DELAY|CHAN_OPT_CAN_CHAT|CHAN_OPT_CAN_LEAVE
CHAN_OPT_ANNOUNCE_SELF - Show info for player itself if player has joined/leaves the channel
CHAN_OPT_ANNOUNCE_JOIN - Display message when player is joining the channel
CHAN_OPT_ANNOUNCE_LEAVE - Display message when player is leaving the channel
CHAN_OPT_MSG_DELAY - Enable chat delay for the channel
CHAN_OPT_COLOR_OVERRIDE - Player's unique font color will override channel's color
CHAN_OPT_CAN_CHAT - Player can chat in the channel
CHAN_OPT_CAN_LEAVE - Player can leave the channel
CHAN_OPT_AUTOJOIN - Players will auto join the channel at login
The <delay> is the minimum chat delay in millisecond for a single player before
the player can chat again in the same channel.
Use <color> hex code to set the color for this channel, if not defined, default
channel color will be used.
If <char_id> is defined, the channel will be a private channel and the player
will be the the channel owner.
Returns 1 on success.
/**
* This example will shows the message on this channel as
* [rAthena] Admin : Hello world!
* instead of
* #rathena Admin : Hello world!
**/
channel_create("#rathena","[rAthena]");
channel_create("#vip","[VIP]","vipmemberonly");
---------------------------------------
*channel_setopt "<chname>",<option>,<value>;
Set option for the channel. Use 1 in <value> to set it, or 0 to unset.
The <option> values are the same as the 'channel_create' options.
For CHAN_OPT_MSG_DELAY, the delay in millisecond must be sent or use 0
to remove the delay at <value>.
Returns 1 on success.
// Example to set delay
channel_setopt("#global",CHAN_OPT_MSG_DELAY,5000);
Only for public and private channel.
---------------------------------------
*channel_setcolor "<chname>",<color>;
To change channel color.
<color> uses hex RGB values.
Returns 1 on success.
---------------------------------------
*channel_setpass "<chname>","<password>";
To set, unset, or change password of a channel.
Use "null" to remove the password.
Returns 1 on success.
Only for public and private channel.
---------------------------------------
*channel_setgroup "<chname>",<group_id>{,...,<group_id>};
*channel_setgroup2 "<chname>",<array_of_groups>;
Set group restriction for a channel. Only player with matching <group_id>
are allowed to to join the channel.
By using 0 in the first group channel, the group restriction will be
removed from the channel config.
'channel_setgroup2' receives input for group list as an array.
Returns 0 on failure, and 1 (or n groups count) on success.
// Example 1: Remove groups
channel_setgroup("#event",0);
// Example 2: Multiple values
channel_setgroup("#vip",2,5);
// Example 3: Using array
setarray .@staffs[0],2,3,4,10,99;
channel_setgroup("#staff",.@staffs);
Only for public and private channel.
---------------------------------------
*channel_chat "<chname>","<message>"{,<color>};
Sends message to the channel.
Returns 1 on success.
// Example if channel doesn't have alias
channel_chat(#rathena,"Hello World!"); // #rathena Hello World!
// Example if channel has alias
channel_chat(#rathena,"Hello World!"); // [rAthena] Hello World!
---------------------------------------
*channel_ban "<chname>",<char_id>;
Ban player from a public or private channel.
Channel's owner or group with PC_PERM_CHANNEL_ADMIN cannot be banned.
Returns 1 on success.
---------------------------------------
*channel_unban "<chname>",<char_id>;
Unban player from a public or private channel.
Returns 1 on success.
---------------------------------------
*channel_kick "<chname>",<char_id>;
*channel_kick "<chname>","<char_name>";
Kick player from a public or private channel.
Channel's owner or group with PC_PERM_CHANNEL_ADMIN cannot be kicked.
Returns 1 on success.
---------------------------------------
*channel_delete "<chname>";
Delete an existing public or private channel. Cannot delete ally or
local map channel.
Returns 0 on success.
---------------------------------------

View File

@ -1324,6 +1324,10 @@ int char_check_char_name(char * name, char * esc_name)
if( strcmpi(name, charserv_config.wisp_server_name) == 0 )
return -1; // nick reserved for internal server messages
// check for the channel symbol
if( name[0] == '#' )
return -2;
// Check Authorised letters/symbols in the name of the character
if( charserv_config.char_config.char_name_option == 1 )
{ // only letters/symbols in char_name_letters are authorised

View File

@ -9378,7 +9378,7 @@ static inline void atcmd_channel_help(struct map_session_data *sd, const char *c
{
int fd = sd->fd;
bool can_delete = pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN);
bool can_create = (can_delete || channel_config.user_chenable);
bool can_create = (can_delete || channel_config.private_channel.allow);
clif_displaymessage(fd, msg_txt(sd,1414));// ---- Available options:
//option create
@ -9487,6 +9487,8 @@ ACMD_FUNC(channel) {
return channel_pcunbind(sd);
} else if ( strcmpi(key,"ban") == 0 ) {
return channel_pcban(sd,sub1,sub2,0);
} else if ( strcmpi(key,"kick") == 0 ) {
return channel_pckick(sd,sub1,sub2);
} else if ( strcmpi(key,"banlist") == 0 ) {
return channel_pcban(sd,sub1,NULL,3);
} else if ( strcmpi(key,"unban") == 0 ) {
@ -9497,7 +9499,7 @@ ACMD_FUNC(channel) {
char sub3[CHAN_NAME_LENGTH], sub4[CHAN_NAME_LENGTH];
sub3[0] = sub4[0] = '\0';
sscanf(sub2, "%19s %19s", sub3, sub4);
if( strcmpi(key,"create") == 0 && ( channel_config.user_chenable || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) ) {
if( strcmpi(key,"create") == 0 && ( channel_config.private_channel.allow || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) ) ) {
if (sub4[0] != '\0') {
clif_displaymessage(fd, msg_txt(sd, 1408)); // Channel password may not contain spaces.
return -1;

File diff suppressed because it is too large Load Diff

View File

@ -14,49 +14,79 @@ extern "C" {
#define CHAN_MSG_LENGTH 150
enum Channel_Opt {
CHAN_OPT_BASE = 0,
CHAN_OPT_ANNOUNCE_JOIN = 1, //display message when join or leave
CHAN_OPT_MSG_DELAY = 2,
CHAN_OPT_COLOR_OVERRIDE = 3,
CHAN_OPT_NONE = 0, ///< None
CHAN_OPT_ANNOUNCE_SELF = 0x01, ///< Shows info when player joined/left channel to self
CHAN_OPT_ANNOUNCE_JOIN = 0x02, ///< Shows info if player joined the channel
CHAN_OPT_ANNOUNCE_LEAVE = 0x04, ///< Shows info if player left the channel
CHAN_OPT_MSG_DELAY = 0x08, ///< Enables chat delay
CHAN_OPT_COLOR_OVERRIDE = 0x10, ///< Enables color channel be override by player's font color
CHAN_OPT_CAN_CHAT = 0x20, ///< Allows player to chat in the channel
CHAN_OPT_CAN_LEAVE = 0x40, ///< Allows player to leave the channel
CHAN_OPT_AUTOJOIN = 0x80, ///< Player will be autojoined to the channel
CHAN_OPT_BASE = CHAN_OPT_ANNOUNCE_SELF|CHAN_OPT_MSG_DELAY|CHAN_OPT_CAN_CHAT|CHAN_OPT_CAN_LEAVE,
};
enum Channel_Type {
CHAN_TYPE_PUBLIC = 0, //config file made
CHAN_TYPE_PRIVATE = 1, //user made
CHAN_TYPE_MAP = 2, //made by map
CHAN_TYPE_ALLY = 3, //guild
CHAN_TYPE_PUBLIC = 0, ///< Config file made
CHAN_TYPE_PRIVATE = 1, ///< User's channel
CHAN_TYPE_MAP = 2, ///< Local map
CHAN_TYPE_ALLY = 3, ///< Guild + its alliance
};
struct Channel {
//unsigned short id; ///< Channel ID (unused yet)
char name[CHAN_NAME_LENGTH]; ///< Channel Name
char pass[CHAN_NAME_LENGTH]; ///< Channe display name
char alias[CHAN_NAME_LENGTH]; ///< Password
enum Channel_Type type; ///< Channel type @see enum Channel_Type
unsigned long color; ///< Channel color in BGR
unsigned char opt; ///< Channel options @see enum Channel_Opt
unsigned short msg_delay; ///< Chat delay in miliseconds
unsigned int char_id; ///< If CHAN_TYPE_PRIVATE, owner is char_id of channel creator
uint16 m; ///< If CHAN_TYPE_MAP, owner is map id
int gid; ///< If CHAN_TYPE_ALLY, owner is first logged guild_id
DBMap *users; ///< List of users
DBMap *banned; ///< List of banned chars -> char_id
unsigned short group_count; ///< Number of group id
unsigned short *groups; ///< List of group id, only these groups can join the channel
};
struct chan_banentry {
uint32 char_id;
char char_name[NAME_LENGTH];
} chan_banentry;
struct Channel_Config {
unsigned long *colors; //color avail int list
char **colors_name; //colors avail name list
unsigned char colors_count; //color avail count
unsigned char map_chcolor, ally_chcolor; //msg color for map, ally
bool map_enable, ally_enable, user_chenable; //map, ally, users channels enable ?
bool map_autojoin, ally_autojoin; //do user auto join in mapchange, guildjoin ?
char map_chname[CHAN_NAME_LENGTH], ally_chname[CHAN_NAME_LENGTH]; //channel name for map and ally
bool closing; //server is closing
unsigned long *colors; ///< List of available colors
char **colors_name; ///< Name list of available colors
unsigned char colors_count; ///< Number of available colors
/// Private channel default configs
struct {
unsigned char opt; ///< Options @see enum Channel_Opt
unsigned long color; ///< Default color
unsigned int delay; ///< Message delay
unsigned short max_member; ///< Max member for each channel
unsigned allow : 1; ///< Allow private channel creation?
unsigned ban : 1; ///< Allow player to ban
unsigned kick : 1; ///< Allow player to kick
unsigned color_override : 1; ///< Owner cannot change the color_override
unsigned change_delay : 1; ///< Owner cannot change the delay
} private_channel;
struct Channel map_tmpl; ///< Map channel default config
struct Channel ally_tmpl; ///< Alliance channel default config
bool closing; ///< Server is closing
};
extern struct Channel_Config channel_config;
struct Channel {
char name[CHAN_NAME_LENGTH]; //channel name
char pass[CHAN_NAME_LENGTH]; //channel password
unsigned char color; //msg color
DBMap *users; //users in channel charid list
DBMap *banned; //users banned from channel charid list
enum Channel_Opt opt; //flag for some treatement
enum Channel_Type type; //type of channel
unsigned int owner; //if chan_type private charid of creator
uint16 m; //if chan_type map guild_id
int gid; //if chan_type guild type guild_id
unsigned char msg_delay; //delay in second if opt_msg_delay
};
DBMap* channel_get_db(void);
struct Channel* channel_create(char *name, char *pass, unsigned char color, enum Channel_Type chantype, int val);
int channel_delete(struct Channel *channel);
struct Channel* channel_create(struct Channel *tmp_chan);
struct Channel* channel_create_simple(char *name, char *pass, enum Channel_Type chantype, unsigned int owner);
int channel_delete(struct Channel *channel, bool force);
int channel_join(struct Channel *channel, struct map_session_data *sd);
int channel_mjoin(struct map_session_data *sd);
@ -65,6 +95,8 @@ int channel_ajoin(struct guild *g);
int channel_clean(struct Channel *channel, struct map_session_data *sd, int flag);
int channel_pcquit(struct map_session_data *sd, int type);
unsigned long channel_getColor(const char *color_str);
int channel_send(struct Channel *channel, struct map_session_data *sd, const char *msg);
void channel_read_config(void);
@ -75,6 +107,9 @@ int channel_haspcbanned(struct Channel *channel,struct map_session_data *sd);
int channel_pc_haschan(struct map_session_data *sd, struct Channel *channel);
int channel_display_list(struct map_session_data *sd, char *option);
void channel_autojoin(struct map_session_data *sd);
bool channel_pccheckgroup(struct Channel *channel, int group_id);
int channel_pccreate(struct map_session_data *sd, char *chname, char *pass);
int channel_pcdelete(struct map_session_data *sd, char *chname);
int channel_pcjoin(struct map_session_data *sd, char *chname, char *pass);
@ -83,6 +118,7 @@ int channel_pccolor(struct map_session_data *sd, char *chname, char *color);
int channel_pcbind(struct map_session_data *sd, char *chname);
int channel_pcunbind(struct map_session_data *sd);
int channel_pcban(struct map_session_data *sd, char *chname, char *pname, int flag);
int channel_pckick(struct map_session_data *sd, char *chname, char *pname);
int channel_pcsetopt(struct map_session_data *sd, char *chname, const char *option, const char *val);
void do_init_channel(void);

View File

@ -6197,31 +6197,35 @@ void clif_broadcast2(struct block_list* bl, const char* mes, int len, unsigned l
}
/*
* Display *msg from *sd to all *users in channel
* Display *msg to all *users in channel
*/
void clif_channel_msg(struct Channel *channel, struct map_session_data *sd, char *msg, short color) {
void clif_channel_msg(struct Channel *channel, const char *msg, unsigned long color) {
DBIterator *iter;
struct map_session_data *user;
unsigned short msg_len = (unsigned short)(strlen(msg) + 1);
unsigned short msg_len = 0, len = 0;
unsigned char buf[CHAT_SIZE_MAX];
WFIFOHEAD(sd->fd,msg_len + 12);
WFIFOW(sd->fd,0) = 0x2C1;
WFIFOW(sd->fd,2) = msg_len + 12;
WFIFOL(sd->fd,4) = 0;
WFIFOL(sd->fd,8) = channel_config.colors[color];
safestrncpy(WFIFOCP(sd->fd,12), msg, msg_len);
if (!channel || !msg)
return;
msg_len = (unsigned short)(strlen(msg) + 1);
if( msg_len > sizeof(buf)-12 ) {
ShowWarning("clif_channel_msg: Truncating too long message '%s' (len=%u).\n", msg, msg_len);
msg_len = sizeof(buf)-12;
}
WBUFW(buf,0) = 0x2C1;
WBUFW(buf,2) = (len = msg_len + 12);
WBUFL(buf,4) = 0;
WBUFL(buf,8) = color;
safestrncpy(WBUFCP(buf,12), msg, msg_len);
iter = db_iterator(channel->users);
for( user = (struct map_session_data *)dbi_first(iter); dbi_exists(iter); user = (struct map_session_data *)dbi_next(iter) ) {
if( user->fd == sd->fd )
continue;
WFIFOHEAD(user->fd,msg_len + 12);
memcpy(WFIFOP(user->fd,0), WFIFOP(sd->fd,0), msg_len + 12);
WFIFOSET(user->fd, msg_len + 12);
clif_send(buf, len, &user->bl, SELF);
}
dbi_destroy(iter);
WFIFOSET(sd->fd, msg_len + 12);
}
/// Displays heal effect.
@ -10351,7 +10355,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
#endif
// Instances do not need their own channels
if( channel_config.map_enable && channel_config.map_autojoin && !map[sd->bl.m].flag.chmautojoin && !map[sd->bl.m].instance_id )
if( channel_config.map_tmpl.name != NULL && (channel_config.map_tmpl.opt&CHAN_OPT_AUTOJOIN) && !map[sd->bl.m].flag.chmautojoin && !map[sd->bl.m].instance_id )
channel_mjoin(sd); //join new map
} else if (sd->guild && (battle_config.guild_notice_changemap == 2 || guild_notice))
clif_guild_notice(sd); // Displays at end
@ -10679,7 +10683,7 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
if( !clif_process_message(sd, false, name, message, output ) )
return;
if( sd->gcbind ) {
if( sd->gcbind && ((sd->gcbind->opt&CHAN_OPT_CAN_CHAT) || pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN)) ) {
channel_send(sd->gcbind,sd,message);
return;
}
@ -11035,12 +11039,13 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
char* chname = target;
channel = channel_name2channel(chname,sd,3);
if(channel){
if(channel && (pc_has_permission(sd, PC_PERM_CHANNEL_ADMIN) || ((channel->opt&CHAN_OPT_CAN_CHAT) && channel_pccheckgroup(channel,sd->group_id)))){
if(channel_pc_haschan(sd,channel)>=0){ //we are in the chan
channel_send(channel,sd,message);
}
else if( channel->pass[0] == '\0') { //no pass needed
if (channel_join(channel,sd)==0) channel_send(channel,sd,message); //join success
if (channel_join(channel,sd)==0)
channel_send(channel,sd,message); //join success
}
else {
clif_displaymessage(fd, msg_txt(sd,1402)); //You're not in that channel, type '@join <#channel_name>'

View File

@ -1003,7 +1003,7 @@ enum clif_colors {
};
unsigned long color_table[COLOR_MAX];
void clif_channel_msg(struct Channel *channel, struct map_session_data *sd, char *msg, short color);
void clif_channel_msg(struct Channel *channel, const char *msg, unsigned long color);
#define clif_menuskill_clear(sd) (sd)->menuskill_id = (sd)->menuskill_val = (sd)->menuskill_val2 = 0;

View File

@ -534,7 +534,7 @@ int guild_recv_info(struct guild *sg) {
if( sd==NULL )
continue;
sd->guild = g;
if(channel_config.ally_autojoin ) {
if(channel_config.ally_tmpl.name && (channel_config.ally_tmpl.opt&CHAN_OPT_AUTOJOIN)) {
channel_gjoin(sd,3); //make all member join guildchan+allieschan
}
@ -703,7 +703,7 @@ void guild_member_joined(struct map_session_data *sd) {
if (g->instance_id != 0)
instance_reqinfo(sd, g->instance_id);
if( channel_config.ally_enable && channel_config.ally_autojoin ) {
if( channel_config.ally_tmpl.name != NULL && (channel_config.ally_tmpl.opt&CHAN_OPT_AUTOJOIN) ) {
channel_gjoin(sd,3);
}
}
@ -1745,8 +1745,8 @@ 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);
storage_guild_delete(guild_id);
if( channel_config.ally_enable ) {
channel_delete(g->channel);
if( channel_config.ally_tmpl.name != NULL ) {
channel_delete(g->channel,false);
}
idb_remove(guild_db,guild_id);
return 0;
@ -2303,7 +2303,7 @@ void do_final_guild(void) {
struct guild *g;
for( g = (struct guild *)dbi_first(iter); dbi_exists(iter); g = (struct guild *)dbi_next(iter) ) {
channel_delete(g->channel);
channel_delete(g->channel,false);
}
dbi_destroy(iter);

View File

@ -155,6 +155,7 @@ char motd_txt[256] = "conf/motd.txt";
char help_txt[256] = "conf/help.txt";
char help2_txt[256] = "conf/help2.txt";
char charhelp_txt[256] = "conf/charhelp.txt";
char channel_conf[256] = "conf/channels.conf";
char wisp_server_name[NAME_LENGTH] = "Server"; // can be modified in char-server configuration file
@ -3889,6 +3890,8 @@ int map_config_read(char *cfgName)
strcpy(help2_txt, w2);
else if (strcmpi(w1, "charhelp_txt") == 0)
strcpy(charhelp_txt, w2);
else if (strcmpi(w1, "channel_conf") == 0)
safestrncpy(channel_conf, w2, sizeof(channel_conf));
else if(strcmpi(w1,"db_path") == 0)
safestrncpy(db_path,w2,ARRAYLENGTH(db_path));
else if (strcmpi(w1, "console") == 0) {
@ -4404,7 +4407,7 @@ void do_final(void)
ShowStatus("Cleaning up maps [%d/%d]: %s..."CL_CLL"\r", i+1, map_num, map[i].name);
if (map[i].m >= 0) {
map_foreachinmap(cleanup_sub, i, BL_ALL);
channel_delete(map[i].channel);
channel_delete(map[i].channel,false);
}
}
ShowStatus("Cleaned up %d maps."CL_CLL"\n", map_num);
@ -4755,12 +4758,12 @@ int do_init(int argc, char *argv[])
do_init_atcommand();
do_init_battle();
do_init_instance();
do_init_channel();
do_init_chrif();
do_init_clan();
do_init_clif();
do_init_script();
do_init_itemdb();
do_init_channel();
do_init_cashshop();
do_init_skill();
do_init_mob();

View File

@ -758,6 +758,7 @@ extern char motd_txt[];
extern char help_txt[];
extern char help2_txt[];
extern char charhelp_txt[];
extern char channel_conf[];
extern char wisp_server_name[];

View File

@ -1462,6 +1462,8 @@ void pc_reg_received(struct map_session_data *sd)
clif_changeoption( &sd->bl );
}
channel_autojoin(sd);
}
static int pc_calc_skillpoint(struct map_session_data* sd)

View File

@ -643,7 +643,7 @@ struct map_session_data {
struct Channel *gcbind;
bool stealth;
unsigned char fontcolor;
unsigned int channel_tick;
unsigned int *channel_tick;
/* [Ind] */
struct sc_display_entry **sc_display;

View File

@ -49,6 +49,7 @@
#include "mail.h"
#include "quest.h"
#include "elemental.h"
#include "channel.h"
#include <math.h>
#include <stdlib.h> // atoi, strtol, strtoll, exit
@ -22366,6 +22367,455 @@ BUILDIN_FUNC(openstorage2) {
return SCRIPT_CMD_SUCCESS;
}
/**
* Create a new channel
* channel_create "<chname>","<alias>"{,"<password>"{<option>{,<delay>{,<color>{,<char_id>}}}}};
* @author [Cydh]
**/
BUILDIN_FUNC(channel_create) {
struct Channel tmp_chan, *ch = NULL;
const char *chname = script_getstr(st,2), *pass = NULL;
int i = channel_chk((char*)chname, NULL, 3);
TBL_PC *sd = NULL;
if (i != 0) {
ShowError("buildin_channel_create: Channel name '%s' is invalid. Errno %d\n", chname, i);
script_pushint(st,i);
return SCRIPT_CMD_FAILURE;
}
memset(&tmp_chan, 0, sizeof(struct Channel));
if (script_hasdata(st,8)) {
tmp_chan.char_id = script_getnum(st,8);
if (!(sd = map_charid2sd(tmp_chan.char_id))) {
ShowError("buildin_channel_create: Player with char id '%d' is not found.\n", tmp_chan.char_id);
script_pushint(st,-5);
return SCRIPT_CMD_FAILURE;
}
tmp_chan.type = CHAN_TYPE_PRIVATE;
i = 1;
}
else {
tmp_chan.type = CHAN_TYPE_PUBLIC;
i = 0;
}
safestrncpy(tmp_chan.name, chname+1, sizeof(tmp_chan.name));
safestrncpy(tmp_chan.alias, script_getstr(st,3), sizeof(tmp_chan.alias));
if (script_hasdata(st,4) && (pass = script_getstr(st,4)) && strcmpi(pass,"null") != 0)
safestrncpy(tmp_chan.pass, pass, sizeof(tmp_chan.pass));
if (script_hasdata(st,5))
tmp_chan.opt = script_getnum(st,5);
else
tmp_chan.opt = i ? channel_config.private_channel.opt : CHAN_OPT_BASE;
if (script_hasdata(st,6))
tmp_chan.msg_delay = script_getnum(st,6);
else
tmp_chan.msg_delay = i ? channel_config.private_channel.delay : 1000;
if (script_hasdata(st,7))
tmp_chan.color = script_getnum(st,7);
else
tmp_chan.color = i ? channel_config.private_channel.color : channel_getColor("Default");
if (!(ch = channel_create(&tmp_chan))) {
ShowError("buildin_channel_create: Cannot create channel '%s'.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (tmp_chan.char_id)
channel_join(ch, sd);
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Set channel option
* channel_setopt "<chname>",<option>,<value>;
* @author [Cydh]
**/
BUILDIN_FUNC(channel_setopt) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2);
int opt = script_getnum(st,3), value = script_getnum(st,4);
if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("buildin_channel_setopt: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
switch (opt) {
case CHAN_OPT_ANNOUNCE_SELF:
case CHAN_OPT_ANNOUNCE_JOIN:
case CHAN_OPT_ANNOUNCE_LEAVE:
case CHAN_OPT_COLOR_OVERRIDE:
case CHAN_OPT_CAN_CHAT:
case CHAN_OPT_CAN_LEAVE:
case CHAN_OPT_AUTOJOIN:
if (value)
ch->opt |= opt;
else
ch->opt &= ~opt;
break;
case CHAN_OPT_MSG_DELAY:
ch->msg_delay = value;
break;
default:
ShowError("buildin_channel_setopt: Invalid option %d!\n", opt);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Set channel color
* channel_setcolor "<chname>",<color>;
* @author [Cydh]
**/
BUILDIN_FUNC(channel_setcolor) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2);
int color = script_getnum(st,3);
if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("buildin_channel_setcolor: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
ch->color = (color & 0x0000FF) << 16 | (color & 0x00FF00) | (color & 0xFF0000) >> 16;//RGB to BGR
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Set channel password
* channel_setpass "<chname>","<password>";
* @author [Cydh]
**/
BUILDIN_FUNC(channel_setpass) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2), *passwd = script_getstr(st,3);
if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("buildin_channel_setpass: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (!passwd || !strcmpi(passwd,"null"))
memset(ch->pass, '\0', sizeof(ch->pass));
else
safestrncpy(ch->pass, passwd, sizeof(ch->pass));
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Set authorized groups
* channel_setgroup "<chname>",<group_id>{,...,<group_id>};
* @author [Cydh]
**/
BUILDIN_FUNC(channel_setgroup) {
struct Channel *ch = NULL;
const char *funcname = script_getfuncname(st), *chname = script_getstr(st,2);
int i, n = 0, group = 0;
if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("buildin_channel_setgroup: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (funcname[strlen(funcname)-1] == '2') {
struct script_data *data = script_getdata(st,3);
const char *varname = reference_getname(data);
int32 id, idx;
if (varname[strlen(varname)-1] == '$') {
ShowError("buildin_channel_setgroup: The array %s is not numeric type.\n", varname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
n = script_array_highest_key(st, NULL, reference_getname(data), reference_getref(data));
if (n < 1) {
ShowError("buildin_channel_setgroup: No group id listed.\n");
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (ch->groups)
aFree(ch->groups);
ch->groups = NULL;
ch->group_count = 0;
id = reference_getid(data);
idx = reference_getindex(data);
for (i = 0; i < n; i++) {
group = (int32)__64BPRTSIZE(get_val2(st,reference_uid(id,idx+i),reference_getref(data)));
if (group == 0) {
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
RECREATE(ch->groups, unsigned short, ++ch->group_count);
ch->groups[ch->group_count-1] = group;
ShowInfo("buildin_channel_setgroup: (2) Added group %d. Num: %d\n", ch->groups[ch->group_count-1], ch->group_count);
}
}
else {
group = script_getnum(st,3);
n = script_lastdata(st)-1;
if (n < 1) {
ShowError("buildin_channel_setgroup: Please input at least 1 group_id.\n");
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (ch->groups)
aFree(ch->groups);
ch->groups = NULL;
ch->group_count = 0;
if (group == 0) { // Removed group list
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
CREATE(ch->groups, unsigned short, n);
for (i = 3; i < n+2; i++) {
ch->groups[ch->group_count++] = script_getnum(st,i);
ShowInfo("buildin_channel_setgroup: (1) Added group %d. Num: %d\n", ch->groups[ch->group_count-1], ch->group_count);
}
}
script_pushint(st,n);
return SCRIPT_CMD_SUCCESS;
}
/**
* Send message on channel
* channel_chat "<chname>","<message>"{,<color>};
* @author [Cydh]
**/
BUILDIN_FUNC(channel_chat) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2), *msg = script_getstr(st,3);
char output[CHAT_SIZE_MAX+1];
unsigned long color = 0;
// Check also local channels
if (chname[0] != '#') { // By Map
int m = mapindex_name2id(chname);
if (!m || (m = map_mapindex2mapid(m)) < 0) {
ShowError("buildin_channel_chat: Invalid map '%s'.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (!(ch = map[m].channel)) {
ShowDebug("buildin_channel_chat: Map '%s' doesn't have local channel yet.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
}
}
else if (strcmpi(chname+1,channel_config.map_tmpl.name) == 0) {
TBL_NPC *nd = map_id2nd(st->oid);
if (!nd || nd->bl.m == -1) {
ShowError("buildin_channel_chat: Floating NPC needs map name instead '%s'.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (!(ch = map[nd->bl.m].channel)) {
ShowDebug("buildin_channel_chat: Map '%s' doesn't have local channel yet.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
}
}
else if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("buildin_channel_chat: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (!ch) {
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
color = ch->color;
FETCH(4, color);
safesnprintf(output, CHAT_SIZE_MAX, "%s %s", ch->alias, msg);
clif_channel_msg(ch,output,color);
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Ban player from a channel
* channel_ban "<chname>",<char_id>;
* @author [Cydh]
**/
BUILDIN_FUNC(channel_ban) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2);
unsigned int char_id = script_getnum(st,3);
TBL_PC *tsd;
if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("buildin_channel_ban: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (ch->char_id == char_id) {
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
}
tsd = map_charid2sd(char_id);
if (tsd && pc_has_permission(tsd,PC_PERM_CHANNEL_ADMIN)) {
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
}
if (!idb_exists(ch->banned, char_id)) {
struct chan_banentry *cbe;
char output[CHAT_SIZE_MAX+1];
CREATE(cbe, struct chan_banentry, 1);
cbe->char_id = char_id;
if (tsd) {
strcpy(cbe->char_name,tsd->status.name);
channel_clean(ch,tsd,0);
safesnprintf(output, CHAT_SIZE_MAX, msg_txt(tsd,769), ch->alias, tsd->status.name); // %s %s has been banned.
}
else
safesnprintf(output, CHAT_SIZE_MAX, msg_txt(tsd,769), ch->alias, "****"); // %s %s has been banned.
idb_put(ch->banned, char_id, cbe);
clif_channel_msg(ch,output,ch->color);
}
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Ban player from a channel
* channel_unban "<chname>",<char_id>;
* @author [Cydh]
**/
BUILDIN_FUNC(channel_unban) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2);
unsigned int char_id = script_getnum(st,3);
if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("buildin_channel_unban: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (ch->char_id == char_id) {
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
}
if (idb_exists(ch->banned, char_id)) {
char output[CHAT_SIZE_MAX+1];
TBL_PC *tsd = map_charid2sd(char_id);
if (tsd)
safesnprintf(output, CHAT_SIZE_MAX, msg_txt(tsd,770), ch->alias, tsd->status.name); // %s %s has been unbanned
else
safesnprintf(output, CHAT_SIZE_MAX, msg_txt(tsd,770), ch->alias, "****"); // %s %s has been unbanned.
idb_remove(ch->banned, char_id);
clif_channel_msg(ch,output,ch->color);
}
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Kick player from a channel
* channel_kick "<chname>",<char_id>;
* channel_kick "<chname>","<char_name>";
* @author [Cydh]
**/
BUILDIN_FUNC(channel_kick) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2);
struct script_data *data = script_getdata(st,3);
TBL_PC *tsd = NULL;
int res = 1;
get_val(st, data);
if (data_isstring(data)) {
if (!(tsd = map_nick2sd(conv_str(st,data),false))) {
ShowError("buildin_channel_kick: Player with nick '%s' is not online\n", conv_str(st,data));
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
}
else {
if (!(tsd = map_charid2sd(conv_num(st,data)))) {
ShowError("buildin_channel_kick: Player with char_id '%d' is not online\n", conv_num(st,data));
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
}
if (!(ch = channel_name2channel((char *)chname, tsd, 0))) {
ShowError("buildin_channel_kick: Channel name '%s' is invalid.\n", chname);
script_pushint(st,0);
return SCRIPT_CMD_FAILURE;
}
if (channel_pc_haschan(tsd, ch) < 0 || ch->char_id == tsd->status.char_id || pc_has_permission(tsd,PC_PERM_CHANNEL_ADMIN)) {
script_pushint(st,0);
return SCRIPT_CMD_SUCCESS;
}
switch(ch->type){
case CHAN_TYPE_ALLY: res = channel_pcquit(tsd,3); break;
case CHAN_TYPE_MAP: res = channel_pcquit(tsd,4); break;
default: res = channel_clean(ch,tsd,0); break;
}
if (res == 0) {
char output[CHAT_SIZE_MAX+1];
safesnprintf(output, CHAT_SIZE_MAX, msg_txt(tsd,889), ch->alias, tsd->status.name); // "%s %s is kicked"
clif_channel_msg(ch,output,ch->color);
}
script_pushint(st,1);
return SCRIPT_CMD_SUCCESS;
}
/**
* Delete a channel
* channel_delete "<chname>";
* @author [Cydh]
**/
BUILDIN_FUNC(channel_delete) {
struct Channel *ch = NULL;
const char *chname = script_getstr(st,2);
if (!(ch = channel_name2channel((char *)chname, NULL, 0))) {
ShowError("channel_delete: Channel name '%s' is invalid.\n", chname);
script_pushint(st,-1);
return SCRIPT_CMD_FAILURE;
}
script_pushint(st,channel_delete(ch,true));
return SCRIPT_CMD_SUCCESS;
}
#include "../custom/script.inc"
// declarations that were supposed to be exported from npc_chat.c
@ -22974,6 +23424,19 @@ struct script_function buildin_func[] = {
BUILDIN_DEF(gvgon3,"s"),
BUILDIN_DEF(gvgoff3,"s"),
// Channel System
BUILDIN_DEF(channel_create,"ss?????"),
BUILDIN_DEF(channel_setopt,"sii"),
BUILDIN_DEF(channel_setcolor,"si"),
BUILDIN_DEF(channel_setpass,"ss"),
BUILDIN_DEF(channel_setgroup,"si*"),
BUILDIN_DEF2(channel_setgroup,"channel_setgroup2","sr"),
BUILDIN_DEF(channel_chat,"ss?"),
BUILDIN_DEF(channel_ban,"si"),
BUILDIN_DEF(channel_unban,"si"),
BUILDIN_DEF(channel_kick,"sv"),
BUILDIN_DEF(channel_delete,"s"),
#include "../custom/script_def.inc"
{NULL,NULL,NULL},

View File

@ -3229,6 +3229,21 @@
export_constant(CARD0_CREATE);
export_constant(CARD0_PET);
/* Channel System */
export_constant(CHAN_TYPE_PUBLIC);
export_constant(CHAN_TYPE_PRIVATE);
export_constant(CHAN_TYPE_MAP);
export_constant(CHAN_TYPE_ALLY);
export_constant(CHAN_OPT_BASE);
export_constant(CHAN_OPT_ANNOUNCE_SELF);
export_constant(CHAN_OPT_ANNOUNCE_JOIN);
export_constant(CHAN_OPT_ANNOUNCE_LEAVE);
export_constant(CHAN_OPT_MSG_DELAY);
export_constant(CHAN_OPT_COLOR_OVERRIDE);
export_constant(CHAN_OPT_CAN_CHAT);
export_constant(CHAN_OPT_CAN_LEAVE);
export_constant(CHAN_OPT_AUTOJOIN);
export_constant(STOR_MODE_NONE);
export_constant(STOR_MODE_GET);
export_constant(STOR_MODE_PUT);