summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/HPMmap.c32
-rw-r--r--src/map/Makefile.in4
-rw-r--r--src/map/atcommand.c275
-rw-r--r--src/map/battle.c32
-rw-r--r--src/map/clan.c1071
-rw-r--r--src/map/clan.h85
-rw-r--r--src/map/clif.c183
-rw-r--r--src/map/clif.h8
-rw-r--r--src/map/guild.c5
-rw-r--r--src/map/intif.c86
-rw-r--r--src/map/intif.h5
-rw-r--r--src/map/log.c22
-rw-r--r--src/map/log.h1
-rw-r--r--src/map/map.c18
-rw-r--r--src/map/map.h3
-rw-r--r--src/map/mob.h1
-rw-r--r--src/map/npc.c49
-rw-r--r--src/map/npc.h2
-rw-r--r--src/map/packets.h15
-rw-r--r--src/map/packets_struct.h36
-rw-r--r--src/map/pc.c7
-rw-r--r--src/map/pc.h1
-rw-r--r--src/map/script.c145
-rw-r--r--src/map/skill.c2
-rw-r--r--src/map/skill.h1
-rw-r--r--src/map/status.c21
-rw-r--r--src/map/status.h3
-rw-r--r--src/map/unit.c2
28 files changed, 2020 insertions, 95 deletions
diff --git a/src/map/HPMmap.c b/src/map/HPMmap.c
index 381dbf599..17b4fd813 100644
--- a/src/map/HPMmap.c
+++ b/src/map/HPMmap.c
@@ -54,6 +54,7 @@
#include "map/channel.h"
#include "map/chat.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/date.h"
#include "map/duel.h"
@@ -114,21 +115,22 @@ unsigned int atcommand_list_items = 0;
bool HPM_map_data_store_validate(enum HPluginDataTypes type, struct hplugin_data_store **storeptr, bool initialize)
{
switch (type) {
- case HPDT_MSD:
- case HPDT_NPCD:
- case HPDT_MAP:
- case HPDT_PARTY:
- case HPDT_GUILD:
- case HPDT_INSTANCE:
- case HPDT_MOBDB:
- case HPDT_MOBDATA:
- case HPDT_ITEMDATA:
- case HPDT_BGDATA:
- case HPDT_AUTOTRADE_VEND:
- // Initialized by the caller.
- return true;
- default:
- break;
+ case HPDT_MSD:
+ case HPDT_NPCD:
+ case HPDT_MAP:
+ case HPDT_PARTY:
+ case HPDT_GUILD:
+ case HPDT_INSTANCE:
+ case HPDT_MOBDB:
+ case HPDT_MOBDATA:
+ case HPDT_ITEMDATA:
+ case HPDT_BGDATA:
+ case HPDT_AUTOTRADE_VEND:
+ case HPDT_CLAN:
+ // Initialized by the caller.
+ return true;
+ default:
+ break;
}
return false;
}
diff --git a/src/map/Makefile.in b/src/map/Makefile.in
index 91efba0ae..e6966d9a2 100644
--- a/src/map/Makefile.in
+++ b/src/map/Makefile.in
@@ -41,14 +41,14 @@ MT19937AR_OBJ = $(MT19937AR_D)/mt19937ar.o
MT19937AR_H = $(MT19937AR_D)/mt19937ar.h
MAP_C = atcommand.c battle.c battleground.c buyingstore.c channel.c chat.c \
- chrif.c clif.c date.c duel.c elemental.c guild.c homunculus.c HPMmap.c \
+ chrif.c clan.c clif.c date.c duel.c elemental.c guild.c homunculus.c HPMmap.c \
instance.c intif.c irc-bot.c itemdb.c log.c mail.c map.c mapreg_sql.c \
mercenary.c mob.c npc.c npc_chat.c party.c path.c pc.c pc_groups.c \
pet.c quest.c rodex.c script.c searchstore.c skill.c status.c storage.c \
trade.c unit.c vending.c
MAP_OBJ = $(addprefix obj_sql/, $(patsubst %c,%o,$(MAP_C)))
MAP_H = atcommand.h battle.h battleground.h buyingstore.h channel.h chat.h \
- chrif.h clif.h date.h duel.h elemental.h guild.h homunculus.h HPMmap.h \
+ chrif.h clan.h clif.h date.h duel.h elemental.h guild.h homunculus.h HPMmap.h \
instance.h intif.h irc-bot.h itemdb.h log.h mail.h map.h mapreg.h \
mercenary.h mob.h npc.h packets.h packets_keys_main.h packets_keys_zero.h \
packets_shuffle_main.h packets_shuffle_zero.h packets_struct.h party.h \
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index aa25f11d6..d0a9bb10e 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -28,6 +28,7 @@
#include "map/channel.h"
#include "map/chat.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/duel.h"
#include "map/elemental.h"
@@ -1677,6 +1678,45 @@ ACMD(gvgon)
/*==========================================
*
*------------------------------------------*/
+ACMD(cvcoff)
+{
+ if (!map->list[sd->bl.m].flag.cvc) {
+ clif->message(fd, msg_fd(fd, 141)); // CvC is already Off.
+ return false;
+ }
+
+ map->zone_change2(sd->bl.m, map->list[sd->bl.m].prev_zone);
+ map->list[sd->bl.m].flag.cvc = 0;
+ clif->map_property_mapall(sd->bl.m, MAPPROPERTY_NOTHING);
+ clif->maptypeproperty2(&sd->bl, ALL_SAMEMAP);
+ map->foreachinmap(atcommand->stopattack, sd->bl.m, BL_CHAR, 0);
+ clif->message(fd, msg_fd(fd, 26)); // CvC: Off.
+
+ return true;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
+ACMD(cvcon)
+{
+ if (map->list[sd->bl.m].flag.cvc) {
+ clif->message(fd, msg_fd(fd, 142)); // CvC is already On.
+ return false;
+ }
+
+ map->zone_change2(sd->bl.m, strdb_get(map->zone_db, MAP_ZONE_CVC_NAME));
+ map->list[sd->bl.m].flag.cvc = 1;
+ clif->map_property_mapall(sd->bl.m, MAPPROPERTY_AGITZONE);
+ clif->maptypeproperty2(&sd->bl, ALL_SAMEMAP);
+ clif->message(fd, msg_fd(fd, 27)); // CvC: On.
+
+ return true;
+}
+
+/*==========================================
+ *
+ *------------------------------------------*/
ACMD(model)
{
int hair_style = 0, hair_color = 0, cloth_color = 0;
@@ -3798,6 +3838,9 @@ ACMD(mapinfo)
if (map->list[m_id].flag.battleground)
clif->message(fd, msg_fd(fd,1045)); // Battlegrounds ON
+ if (map->list[m_id].flag.cvc)
+ clif->message(fd, msg_fd(fd, 139)); // CvC ON
+
strcpy(atcmd_output,msg_fd(fd,1046)); // PvP Flags:
if (map->list[m_id].flag.pvp)
strcat(atcmd_output, msg_fd(fd,1047)); // Pvp ON |
@@ -7551,11 +7594,11 @@ ACMD(mapflag) {
CHECKFLAG(noreturn); CHECKFLAG(monster_noteleport); CHECKFLAG(nosave); CHECKFLAG(nobranch);
CHECKFLAG(noexppenalty); CHECKFLAG(pvp); CHECKFLAG(pvp_noparty); CHECKFLAG(pvp_noguild);
CHECKFLAG(pvp_nightmaredrop); CHECKFLAG(pvp_nocalcrank); CHECKFLAG(gvg_castle); CHECKFLAG(gvg);
- CHECKFLAG(gvg_dungeon); CHECKFLAG(gvg_noparty); CHECKFLAG(battleground); CHECKFLAG(nozenypenalty);
- CHECKFLAG(notrade); CHECKFLAG(noskill); CHECKFLAG(nowarp); CHECKFLAG(nowarpto);
- CHECKFLAG(noicewall); CHECKFLAG(snow); CHECKFLAG(clouds); CHECKFLAG(clouds2);
- CHECKFLAG(fog); CHECKFLAG(fireworks); CHECKFLAG(sakura); CHECKFLAG(leaves);
- CHECKFLAG(nobaseexp);
+ CHECKFLAG(gvg_dungeon); CHECKFLAG(gvg_noparty); CHECKFLAG(battleground); CHECKFLAG(cvc);
+ CHECKFLAG(nozenypenalty); CHECKFLAG(notrade); CHECKFLAG(noskill); CHECKFLAG(nowarp);
+ CHECKFLAG(nowarpto); CHECKFLAG(noicewall); CHECKFLAG(snow); CHECKFLAG(clouds);
+ CHECKFLAG(clouds2); CHECKFLAG(fog); CHECKFLAG(fireworks); CHECKFLAG(sakura);
+ CHECKFLAG(leaves); CHECKFLAG(nobaseexp);
CHECKFLAG(nojobexp); CHECKFLAG(nomobloot); CHECKFLAG(nomvploot); CHECKFLAG(nightenabled);
CHECKFLAG(nodrop); CHECKFLAG(novending); CHECKFLAG(loadevent);
CHECKFLAG(nochat); CHECKFLAG(partylock); CHECKFLAG(guildlock); CHECKFLAG(src4instance);
@@ -7567,48 +7610,53 @@ ACMD(mapflag) {
}
for (i = 0; flag_name[i]; i++) flag_name[i] = TOLOWER(flag_name[i]); //lowercase
- if (strcmp( flag_name , "gvg" ) == 0) {
- if( flag && !map->list[sd->bl.m].flag.gvg )
- map->zone_change2(sd->bl.m,strdb_get(map->zone_db, MAP_ZONE_GVG_NAME));
- else if ( !flag && map->list[sd->bl.m].flag.gvg )
- map->zone_change2(sd->bl.m,map->list[sd->bl.m].prev_zone);
- } else if ( strcmp( flag_name , "pvp" ) == 0 ) {
- if ( flag && !map->list[sd->bl.m].flag.pvp )
- map->zone_change2(sd->bl.m,strdb_get(map->zone_db, MAP_ZONE_PVP_NAME));
- else if ( !flag && map->list[sd->bl.m].flag.pvp )
- map->zone_change2(sd->bl.m,map->list[sd->bl.m].prev_zone);
- } else if ( strcmp( flag_name , "battleground" ) == 0 ) {
- if ( flag && !map->list[sd->bl.m].flag.battleground )
- map->zone_change2(sd->bl.m,strdb_get(map->zone_db, MAP_ZONE_BG_NAME));
- else if ( !flag && map->list[sd->bl.m].flag.battleground )
- map->zone_change2(sd->bl.m,map->list[sd->bl.m].prev_zone);
+ if (strcmp(flag_name, "gvg") == 0) {
+ if (flag && !map->list[sd->bl.m].flag.gvg)
+ map->zone_change2(sd->bl.m, strdb_get(map->zone_db, MAP_ZONE_GVG_NAME));
+ else if (!flag && map->list[sd->bl.m].flag.gvg)
+ map->zone_change2(sd->bl.m, map->list[sd->bl.m].prev_zone);
+ } else if (strcmp(flag_name, "pvp") == 0) {
+ if (flag && !map->list[sd->bl.m].flag.pvp)
+ map->zone_change2(sd->bl.m, strdb_get(map->zone_db, MAP_ZONE_PVP_NAME));
+ else if (!flag && map->list[sd->bl.m].flag.pvp)
+ map->zone_change2(sd->bl.m, map->list[sd->bl.m].prev_zone);
+ } else if (strcmp(flag_name, "battleground") == 0) {
+ if (flag && !map->list[sd->bl.m].flag.battleground)
+ map->zone_change2(sd->bl.m, strdb_get(map->zone_db, MAP_ZONE_BG_NAME));
+ else if (!flag && map->list[sd->bl.m].flag.battleground)
+ map->zone_change2(sd->bl.m, map->list[sd->bl.m].prev_zone);
+ } else if (strcmp(flag_name, "cvc") == 0) {
+ if (flag && !map->list[sd->bl.m].flag.cvc)
+ map->zone_change2(sd->bl.m, strdb_get(map->zone_db, MAP_ZONE_CVC_NAME));
+ else if (!flag && map->list[sd->bl.m].flag.cvc)
+ map->zone_change2(sd->bl.m, map->list[sd->bl.m].prev_zone);
}
SETFLAG(autotrade); SETFLAG(allowks); SETFLAG(nomemo); SETFLAG(noteleport);
SETFLAG(noreturn); SETFLAG(monster_noteleport); SETFLAG(nosave); SETFLAG(nobranch);
SETFLAG(noexppenalty); SETFLAG(pvp); SETFLAG(pvp_noparty); SETFLAG(pvp_noguild);
SETFLAG(pvp_nightmaredrop); SETFLAG(pvp_nocalcrank); SETFLAG(gvg_castle); SETFLAG(gvg);
- SETFLAG(gvg_dungeon); SETFLAG(gvg_noparty); SETFLAG(battleground); SETFLAG(nozenypenalty);
- SETFLAG(notrade); SETFLAG(noskill); SETFLAG(nowarp); SETFLAG(nowarpto);
- SETFLAG(noicewall); SETFLAG(snow); SETFLAG(clouds); SETFLAG(clouds2);
- SETFLAG(fog); SETFLAG(fireworks); SETFLAG(sakura); SETFLAG(leaves);
- SETFLAG(nobaseexp);
- SETFLAG(nojobexp); SETFLAG(nomobloot); SETFLAG(nomvploot); SETFLAG(nightenabled);
- SETFLAG(nodrop); SETFLAG(novending); SETFLAG(loadevent);
- SETFLAG(nochat); SETFLAG(partylock); SETFLAG(guildlock); SETFLAG(src4instance);
- SETFLAG(notomb); SETFLAG(nocashshop); SETFLAG(noviewid);
-
- clif->message(sd->fd,msg_fd(fd,1314)); // Invalid flag name or flag.
- clif->message(sd->fd,msg_fd(fd,1312)); // Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On)
- clif->message(sd->fd,msg_fd(fd,1315)); // Available Flags:
- clif->message(sd->fd,"----------------------------------");
- clif->message(sd->fd,"town, autotrade, allowks, nomemo, noteleport, noreturn, monster_noteleport, nosave,");
- clif->message(sd->fd,"nobranch, noexppenalty, pvp, pvp_noparty, pvp_noguild, pvp_nightmaredrop,");
- clif->message(sd->fd,"pvp_nocalcrank, gvg_castle, gvg, gvg_dungeon, gvg_noparty, battleground,");
- clif->message(sd->fd,"nozenypenalty, notrade, noskill, nowarp, nowarpto, noicewall, snow, clouds, clouds2,");
- clif->message(sd->fd,"fog, fireworks, sakura, leaves, nobaseexp, nojobexp, nomobloot,");
- clif->message(sd->fd,"nomvploot, nightenabled, nodrop, novending, loadevent, nochat, partylock,");
- clif->message(sd->fd,"guildlock, src4instance, notomb, nocashshop, noviewid");
+ SETFLAG(gvg_dungeon); SETFLAG(gvg_noparty); SETFLAG(battleground); SETFLAG(cvc);
+ SETFLAG(nozenypenalty); SETFLAG(notrade); SETFLAG(noskill); SETFLAG(nowarp);
+ SETFLAG(nowarpto); SETFLAG(noicewall); SETFLAG(snow); SETFLAG(clouds);
+ SETFLAG(clouds2); SETFLAG(fog); SETFLAG(fireworks); SETFLAG(sakura);
+ SETFLAG(leaves); SETFLAG(nobaseexp); SETFLAG(nojobexp); SETFLAG(nomobloot);
+ SETFLAG(nomvploot); SETFLAG(nightenabled); SETFLAG(nodrop); SETFLAG(novending);
+ SETFLAG(loadevent); SETFLAG(nochat); SETFLAG(partylock); SETFLAG(guildlock);
+ SETFLAG(src4instance); SETFLAG(notomb); SETFLAG(nocashshop); SETFLAG(noviewid);
+
+
+ clif->message(sd->fd, msg_fd(fd, 1314)); // Invalid flag name or flag.
+ clif->message(sd->fd, msg_fd(fd, 1312)); // Usage: "@mapflag monster_noteleport 1" (0=Off | 1=On)
+ clif->message(sd->fd, msg_fd(fd, 1315)); // Available Flags:
+ clif->message(sd->fd, "----------------------------------");
+ clif->message(sd->fd, "town, autotrade, allowks, nomemo, noteleport, noreturn, monster_noteleport, nosave,");
+ clif->message(sd->fd, "nobranch, noexppenalty, pvp, pvp_noparty, pvp_noguild, pvp_nightmaredrop,");
+ clif->message(sd->fd, "pvp_nocalcrank, gvg_castle, gvg, gvg_dungeon, gvg_noparty, battleground, cvc,");
+ clif->message(sd->fd, "nozenypenalty, notrade, noskill, nowarp, nowarpto, noicewall, snow, clouds, clouds2,");
+ clif->message(sd->fd, "fog, fireworks, sakura, leaves, nobaseexp, nojobexp, nomobloot,");
+ clif->message(sd->fd, "nomvploot, nightenabled, nodrop, novending, loadevent, nochat, partylock,");
+ clif->message(sd->fd, "guildlock, src4instance, notomb, nocashshop, noviewid");
#undef CHECKFLAG
#undef SETFLAG
@@ -9451,6 +9499,145 @@ ACMD(lang) {
return true;
}
+
+ACMD(claninfo)
+{
+ struct DBIterator *iter = db_iterator(clan->db);
+ struct clan *c;
+ int i, count;
+
+ for (c = dbi_first(iter); dbi_exists(iter); c = dbi_next(iter)) {
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "Clan #%d:", c->clan_id);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Name: %s", c->name);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Constant: %s", c->constant);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Master: %s", c->master);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Map: %s", c->map);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Max Member: %d", c->max_member);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Kick Time: %dh", c->kick_time / 3600);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Check Time: %dh", c->check_time / 3600000);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Connected Members: %d", c->connect_member);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Total Members: %d", c->member_count);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Allies: %d", VECTOR_LENGTH(c->allies));
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ count = 0;
+ for (i = 0; i < VECTOR_LENGTH(c->allies); i++) {
+ struct clan_relationship *ally = &VECTOR_INDEX(c->allies, i);
+
+ if (ally == NULL)
+ continue;
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- - Ally #%d (Id: %d): %s", i + 1, ally->clan_id, ally->constant);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+ count++;
+ }
+
+ if (count == 0) {
+ clif->messagecolor_self(fd, COLOR_DEFAULT, "- - No Allies Found!");
+ }
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- Antagonists: %d", VECTOR_LENGTH(c->antagonists));
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+
+ count = 0;
+ for (i = 0; i < VECTOR_LENGTH(c->antagonists); i++) {
+ struct clan_relationship *antagonist = &VECTOR_INDEX(c->antagonists, i);
+
+ if (antagonist == NULL)
+ continue;
+
+ safesnprintf(atcmd_output, sizeof(atcmd_output), "- - Antagonist #%d (Id: %d): %s", i + 1, antagonist->clan_id, antagonist->constant);
+ clif->messagecolor_self(fd, COLOR_DEFAULT, atcmd_output);
+ count++;
+ }
+
+ if (count == 0) {
+ clif->messagecolor_self(fd, COLOR_DEFAULT, "- - No Antagonists Found!");
+ }
+
+ clif->messagecolor_self(fd, COLOR_DEFAULT, "============================");
+ }
+ dbi_destroy(iter);
+ return true;
+}
+
+/**
+ * Clan System: Joins in the given clan
+ */
+ACMD(joinclan)
+{
+ int clan_id;
+
+ if (*message == '\0') {
+ clif->message(fd, "Please enter a Clan ID (usage: @joinclan <clan ID>).");
+ return false;
+ }
+ if (sd->status.clan_id != 0) {
+ clif->messagecolor_self(fd, COLOR_RED, "You are already in a clan.");
+ return false;
+ } else if (sd->status.guild_id != 0) {
+ clif->messagecolor_self(fd, COLOR_RED, "You must leave your guild before enter in a clan.");
+ return false;
+ }
+
+ clan_id = atoi(message);
+ if (clan_id <= 0) {
+ clif->messagecolor_self(fd, COLOR_RED, "Invalid Clan ID.");
+ return false;
+ }
+ if (!clan->join(sd, clan_id)) {
+ clif->messagecolor_self(fd, COLOR_RED, "The clan couldn't be joined.");
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Clan System: Leaves current clan
+ */
+ACMD(leaveclan)
+{
+ if (sd->status.clan_id == 0) {
+ clif->messagecolor_self(fd, COLOR_RED, "You aren't in a clan.");
+ return false;
+ }
+ if (!clan->leave(sd, false)) {
+ clif->messagecolor_self(fd, COLOR_RED, "Failed on leaving clan.");
+ return false;
+ }
+ return true;
+}
+
+/**
+ * Clan System: Reloads clan db
+ */
+ACMD(reloadclans)
+{
+ clan->reload();
+ clif->messagecolor_self(fd, COLOR_DEFAULT, "Clan configuration and database have been reloaded.");
+ return true;
+}
+
/**
* Fills the reference of available commands in atcommand DBMap
**/
@@ -9723,6 +9910,12 @@ void atcommand_basecommands(void) {
ACMD_DEF(cddebug),
ACMD_DEF(lang),
ACMD_DEF(bodystyle),
+ ACMD_DEF(cvcoff),
+ ACMD_DEF(cvcon),
+ ACMD_DEF(claninfo),
+ ACMD_DEF(joinclan),
+ ACMD_DEF(leaveclan),
+ ACMD_DEF(reloadclans),
};
int i;
diff --git a/src/map/battle.c b/src/map/battle.c
index 70ebc4a08..d52d20f24 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -25,6 +25,7 @@
#include "map/battleground.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/elemental.h"
#include "map/guild.h"
@@ -6774,14 +6775,17 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
return (flag&state)?1:-1;
}
- if( map_flag_vs(m) ) {
+ if (map_flag_vs(m)) {
//Check rivalry settings.
- int sbg_id = 0, tbg_id = 0;
- if( map->list[m].flag.battleground ) {
+ int sbg_id = 0, tbg_id = 0, s_clan = 0, t_clan = 0;
+ if (map->list[m].flag.battleground) {
sbg_id = bg->team_get_id(s_bl);
tbg_id = bg->team_get_id(t_bl);
+ } else if (map->list[m].flag.cvc) {
+ s_clan = clan->get_id(s_bl);
+ t_clan = clan->get_id(t_bl);
}
- if( flag&(BCT_PARTY|BCT_ENEMY) ) {
+ if (flag & (BCT_PARTY | BCT_ENEMY)) {
int s_party = status->get_party_id(s_bl);
int s_guild = status->get_guild_id(s_bl);
int t_guild = status->get_guild_id(t_bl);
@@ -6791,10 +6795,12 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
if (s_guild != 0 && t_guild != 0 && (s_guild == t_guild || guild->isallied(s_guild, t_guild)))
state |= BCT_PARTY;
else
- state |= flag&BCT_ENEMY ? BCT_ENEMY : BCT_PARTY;
+ state |= flag & BCT_ENEMY ? BCT_ENEMY : BCT_PARTY;
} else if (!(map->list[m].flag.pvp && map->list[m].flag.pvp_noparty)
&& (!map->list[m].flag.battleground || sbg_id == tbg_id)) {
state |= BCT_PARTY;
+ } else if (!map->list[m].flag.cvc || s_clan == t_clan) {
+ state |= BCT_PARTY;
} else {
state |= BCT_ENEMY;
}
@@ -6802,18 +6808,22 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
state |= BCT_ENEMY;
}
}
- if( flag&(BCT_GUILD|BCT_ENEMY) ) {
+ if (flag & (BCT_GUILD | BCT_ENEMY)) {
int s_guild = status->get_guild_id(s_bl);
int t_guild = status->get_guild_id(t_bl);
- if( !(map->list[m].flag.pvp && map->list[m].flag.pvp_noguild)
+ if (!(map->list[m].flag.pvp && map->list[m].flag.pvp_noguild)
&& s_guild && t_guild
- && (s_guild == t_guild || (!(flag&BCT_SAMEGUILD) && guild->isallied(s_guild, t_guild)))
- && (!map->list[m].flag.battleground || sbg_id == tbg_id) )
+ && (s_guild == t_guild || (!(flag & BCT_SAMEGUILD) && guild->isallied(s_guild, t_guild)))
+ && (!map->list[m].flag.battleground || sbg_id == tbg_id)
+ && (!map->list[m].flag.cvc || s_clan == t_clan)
+ ) {
state |= BCT_GUILD;
- else
+ } else {
state |= BCT_ENEMY;
+ }
}
- if( state&BCT_ENEMY && map->list[m].flag.battleground && sbg_id && sbg_id == tbg_id )
+
+ if (state & BCT_ENEMY && ((map->list[m].flag.battleground && sbg_id && sbg_id == tbg_id) || (map->list[m].flag.cvc && s_clan && s_clan == t_clan)))
state &= ~BCT_ENEMY;
if (state&BCT_ENEMY && battle_config.pk_mode && !map_flag_gvg(m) && s_bl->type == BL_PC && t_bl->type == BL_PC) {
diff --git a/src/map/clan.c b/src/map/clan.c
new file mode 100644
index 000000000..b67726b37
--- /dev/null
+++ b/src/map/clan.c
@@ -0,0 +1,1071 @@
+/**
+ * This file is part of Hercules.
+ * http://herc.ws - http://github.com/HerculesWS/Hercules
+ *
+ * Copyright (C) 2017 Hercules Dev Team
+ *
+ * Hercules is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#define HERCULES_CORE
+
+#include "config/core.h"
+#include "clan.h"
+
+#include "map/clif.h"
+#include "map/chrif.h"
+#include "map/homunculus.h"
+#include "map/intif.h"
+#include "map/log.h"
+#include "map/mercenary.h"
+#include "map/mob.h"
+#include "map/npc.h"
+#include "map/pc.h"
+#include "map/pet.h"
+#include "map/script.h"
+#include "map/skill.h"
+#include "map/status.h"
+#include "common/HPM.h"
+#include "common/conf.h"
+#include "common/cbasetypes.h"
+#include "common/db.h"
+#include "common/memmgr.h"
+#include "common/mapindex.h"
+#include "common/nullpo.h"
+#include "common/showmsg.h"
+#include "common/socket.h"
+#include "common/strlib.h"
+#include "common/timer.h"
+#include "common/utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct clan_interface clan_s;
+struct clan_interface *clan;
+
+/**
+ * Searches a Clan by clan_id
+ *
+ * @param clan_id Clan ID
+ * @return struct clan*
+ */
+struct clan *clan_search(int clan_id)
+{
+ if (clan_id <= 0) {
+ return NULL;
+ }
+ return (struct clan *)idb_get(clan->db, clan_id);
+}
+
+/**
+ * Searches a Clan by clan_name or constant
+ *
+ * @param name Clan Name
+ * @return struct clan*
+ */
+struct clan *clan_searchname(const char *name)
+{
+ struct clan *c;
+ struct DBIterator *iter;
+
+ nullpo_retr(NULL, name);
+
+ iter = db_iterator(clan->db);
+ for (c = dbi_first(iter); dbi_exists(iter); c = dbi_next(iter)) {
+ if (strncmpi(c->name, name, NAME_LENGTH) == 0 || strncmpi(c->constant, name, NAME_LENGTH) == 0) {
+ break;
+ }
+ }
+ dbi_destroy(iter);
+ return c;
+}
+
+/**
+ * Returns the first online character of clan
+ *
+ * @param (struct clan *) c clan structure
+ * @return (struct map_session_data *)
+ */
+struct map_session_data *clan_getonlinesd(struct clan *c)
+{
+ int i;
+ nullpo_retr(NULL, c);
+
+ ARR_FIND(0, VECTOR_LENGTH(c->members), i, VECTOR_INDEX(c->members, i).sd != NULL);
+ return (i < VECTOR_LENGTH(c->members)) ? VECTOR_INDEX(c->members, i).sd : NULL;
+}
+
+/**
+ * Returns the member index of given Player
+ *
+ * @param c Clan Data
+ * @param char_id Player's Char ID
+ * @return int
+ */
+int clan_getindex(const struct clan *c, int char_id)
+{
+ int i;
+ nullpo_retr(INDEX_NOT_FOUND, c);
+
+ ARR_FIND(0, VECTOR_LENGTH(c->members), i, VECTOR_INDEX(c->members, i).char_id == char_id);
+
+ if (i == VECTOR_LENGTH(c->members)) {
+ return INDEX_NOT_FOUND;
+ }
+ return i;
+}
+
+/**
+ * Starts clan buff
+ */
+void clan_buff_start(struct map_session_data *sd, struct clan *c)
+{
+ nullpo_retv(sd);
+ nullpo_retv(c);
+
+ if (c->buff.icon <= 0) {
+ return;
+ }
+
+ clif->sc_load(&sd->bl, sd->bl.id, SELF, c->buff.icon, 0, c->clan_id, 0);
+ script->run(c->buff.script, 0, sd->bl.id, npc->fake_nd->bl.id);
+}
+
+/**
+ * Ends clan buff
+ */
+void clan_buff_end(struct map_session_data *sd, struct clan *c)
+{
+ nullpo_retv(sd);
+ nullpo_retv(c);
+
+ if (c->buff.icon <= 0) {
+ return;
+ }
+
+ clif->sc_end(&sd->bl, sd->bl.id, SELF, c->buff.icon);
+}
+
+/**
+ * Joins a Player into a Clan
+ *
+ * @param sd Player's Map Session Data
+ * @param clan_id Clan which will add this player
+ * @return bool
+ */
+bool clan_join(struct map_session_data *sd, int clan_id)
+{
+ struct clan *c;
+ struct clan_member m;
+
+ nullpo_ret(sd);
+
+ // Already joined a guild or clan
+ if (sd->status.guild_id > 0 || sd->guild != NULL) {
+ ShowError("clan_join: Player already joined in a guild. char_id: %d\n", sd->status.char_id);
+ return false;
+ } else if ( sd->status.clan_id > 0 || sd->clan != NULL) {
+ ShowError("clan_join: Player already joined in a clan. char_id: %d\n", sd->status.char_id);
+ return false;
+ }
+
+ c = clan->search(clan_id);
+ if (c == NULL) {
+ ShowError("clan_join: Invalid Clan ID: %d\n", clan_id);
+ return false;
+ }
+
+ if (!c->received) {
+ return false;
+ }
+
+ if (clan->getindex(c, sd->status.char_id) != INDEX_NOT_FOUND) {
+ ShowError("clan_join: Player already joined this clan. char_id: %d clan_id: %d\n", sd->status.char_id, clan_id);
+ return false;
+ }
+
+ if (VECTOR_LENGTH(c->members) >= c->max_member || c->member_count >= c->max_member) {
+ ShowError("clan_join: Clan '%s' already reached its max capacity!\n", c->name);
+ return false;
+ }
+
+ VECTOR_ENSURE(c->members, 1, 1);
+
+ m.sd = sd;
+ m.char_id = sd->status.char_id;
+ m.online = 1;
+ m.last_login = sd->status.last_login;
+ VECTOR_PUSH(c->members, m);
+
+ c->connect_member++;
+ c->member_count++;
+
+ sd->status.clan_id = c->clan_id;
+ sd->clan = c;
+
+ sc_start2(NULL, &sd->bl, SC_CLAN_INFO, 10000, 0, c->clan_id, INFINITE_DURATION);
+ status_calc_pc(sd, SCO_FORCE);
+
+ chrif->save(sd, 0);
+ clif->clan_basicinfo(sd);
+ clif->clan_onlinecount(c);
+ return true;
+}
+
+/**
+ * Invoked when a player joins
+ * It assumes that clan_id is not 0
+ *
+ * @param sd Player Data
+ */
+void clan_member_online(struct map_session_data *sd, bool first)
+{
+ struct clan *c;
+ int i, inactivity;
+ nullpo_retv(sd);
+
+ // For invalid values we must reset it to 0 (no clan)
+ if (sd->status.clan_id < 0) {
+ ShowError("clan_member_online: Invalid clan id, changing to '0'. clan_id='%d' char_id='%d'\n", sd->status.clan_id, sd->status.char_id);
+ sd->status.clan_id = 0;
+ return;
+ }
+
+ c = clan->search(sd->status.clan_id);
+ if (c == NULL) {
+ // This is a silent return because it will reset clan_id in case
+ // a custom clan that was removed and this is a remaining member
+ sd->status.clan_id = 0;
+ sd->clan = NULL;
+ if (!first) {
+ status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER); // Remove the status
+ status_calc_pc(sd, SCO_FORCE);
+ }
+ clif->clan_leave(sd);
+ return;
+ }
+
+ if (!c->received) {
+ return;
+ }
+
+ if (c->max_member <= c->member_count || VECTOR_LENGTH(c->members) >= c->max_member) {
+ ShowError("Clan System: More members than the maximum allowed in clan #%d\n", c->clan_id);
+ return;
+ }
+
+ i = clan->getindex(c, sd->status.char_id);
+ inactivity = (int)(time(NULL) - sd->status.last_login);
+ if (i == INDEX_NOT_FOUND) {
+ struct clan_member m;
+
+ if (c->kick_time > 0 && inactivity > c->kick_time) {
+ sd->status.clan_id = 0;
+ sd->clan = NULL;
+ clan->buff_end(sd, c);
+ status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER);
+ clif->clan_leave(sd);
+ return;
+ }
+
+ VECTOR_ENSURE(c->members, 1, 1);
+
+ m.sd = sd;
+ m.char_id = sd->status.char_id;
+ m.online = 1;
+ m.last_login = sd->status.last_login;
+ VECTOR_PUSH(c->members, m);
+ } else {
+ struct clan_member *m = &VECTOR_INDEX(c->members, i);
+
+
+ if (c->kick_time > 0 && inactivity > c->kick_time) {
+ if (m->online == 1) {
+ m->online = 0;
+ c->connect_member--;
+ c->member_count--;
+ }
+ clan->buff_end(sd, c);
+ sd->status.clan_id = 0;
+ sd->clan = NULL;
+ status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER);
+ VECTOR_ERASE(c->members, i);
+ clif->clan_leave(sd);
+ return;
+ }
+
+ m->online = 1;
+ m->last_login = sd->status.last_login;
+ }
+
+ sd->clan = c;
+ c->connect_member++;
+
+ sc_start2(NULL, &sd->bl, SC_CLAN_INFO, 10000, 0, c->clan_id, INFINITE_DURATION);
+
+ if (!first) {
+ // When first called from pc.c we don't need to do status_calc
+ status_calc_pc(sd, SCO_FORCE);
+ }
+
+ clif->clan_basicinfo(sd);
+ clif->clan_onlinecount(c);
+}
+
+/**
+ * Re-join a player on its clan
+ */
+int clan_rejoin(struct map_session_data *sd, va_list ap)
+{
+ nullpo_ret(sd);
+
+ if (sd->status.clan_id != 0) {
+ // Note: Even if the value is invalid (lower than zero)
+ // the function will fix the invalid value
+ clan->member_online(sd, false);
+ }
+ return 0;
+}
+
+/**
+ * Removes Player from clan
+ */
+bool clan_leave(struct map_session_data *sd, bool first)
+{
+ int i;
+ struct clan *c;
+
+ nullpo_ret(sd);
+
+ c = sd->clan;
+
+ if (c == NULL) {
+ return false;
+ }
+
+ if (!c->received) {
+ return false;
+ }
+
+ i = clan->getindex(c, sd->status.char_id);
+ if (i != INDEX_NOT_FOUND) {
+ VECTOR_ERASE(c->members, i);
+ c->connect_member--;
+ c->member_count--;
+ }
+
+ sd->status.clan_id = 0;
+ sd->clan = NULL;
+ clan->buff_end(sd, c);
+
+ status_change_end(&sd->bl, SC_CLAN_INFO, INVALID_TIMER);
+ if (!first) {
+ status_calc_pc(sd, SCO_FORCE);
+ }
+
+ chrif->save(sd, 0);
+ clif->clan_onlinecount(c);
+ clif->clan_leave(sd);
+ return true;
+}
+
+/**
+ * Sets a player offline
+ *
+ * @param (struct map_session_data *) sd Player Data
+ */
+void clan_member_offline(struct map_session_data *sd)
+{
+ struct clan *c;
+ int i;
+
+ nullpo_retv(sd);
+
+ c = sd->clan;
+
+ if (c == NULL) {
+ return;
+ }
+
+ i = clan->getindex(c, sd->status.char_id);
+ if (i != INDEX_NOT_FOUND && VECTOR_INDEX(c->members, i).online == 1) {
+ // Only if it is online, because unit->free is called twice
+ VECTOR_INDEX(c->members, i).online = 0;
+ c->connect_member--;
+ }
+ clif->clan_onlinecount(c);
+}
+
+
+/**
+ * Sends a message to the whole clan
+ */
+bool clan_send_message(struct map_session_data *sd, const char *mes)
+{
+ int len;
+ nullpo_retr(false, sd);
+ nullpo_retr(false, mes);
+
+ len = (int)strlen(mes);
+
+ if (sd->status.clan_id == 0) {
+ return false;
+ }
+ clan->recv_message(sd->clan, mes, len);
+
+ // Chat logging type 'C' / Clan Chat
+ logs->chat(LOG_CHAT_CLAN, sd->status.clan_id, sd->status.char_id, sd->status.account_id, mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
+
+ return true;
+}
+
+/**
+ * Clan receive a message, will be displayed to whole clan
+ */
+void clan_recv_message(struct clan *c, const char *mes, int len)
+{
+ clif->clan_message(c, mes, len);
+}
+
+/**
+ * Set constants for each clan
+ */
+void clan_set_constants(void)
+{
+ struct DBIterator *iter = db_iterator(clan->db);
+ struct clan *c;
+
+ for (c = dbi_first(iter); dbi_exists(iter); c = dbi_next(iter)) {
+ script->set_constant2(c->constant, c->clan_id, false, false);
+ }
+
+ dbi_destroy(iter);
+}
+
+/**
+ * Returns the clan_id of bl
+ */
+int clan_get_id(const struct block_list *bl)
+{
+ nullpo_ret(bl);
+
+ switch (bl->type) {
+ case BL_PC: {
+ const struct map_session_data *sd = BL_UCCAST(BL_PC, bl);
+ return sd->status.clan_id;
+ }
+ case BL_NPC: {
+ const struct npc_data *nd = BL_UCCAST(BL_NPC, bl);
+ return nd->clan_id;
+ }
+ case BL_PET: {
+ const struct pet_data *pd = BL_UCCAST(BL_PET, bl);
+ if (pd->msd != NULL)
+ return pd->msd->status.clan_id;
+ }
+ break;
+ case BL_MOB: {
+ const struct mob_data *md = BL_UCCAST(BL_MOB, bl);
+ const struct map_session_data *msd;
+ if (md->special_state.ai != AI_NONE && (msd = map->id2sd(md->master_id)) != NULL) {
+ return msd->status.clan_id;
+ }
+ return md->clan_id;
+ }
+ case BL_HOM: {
+ const struct homun_data *hd = BL_UCCAST(BL_HOM, bl);
+ if (hd->master != NULL) {
+ return hd->master->status.clan_id;
+ }
+ }
+ break;
+ case BL_MER: {
+ const struct mercenary_data *md = BL_UCCAST(BL_MER, bl);
+ if (md->master != NULL) {
+ return md->master->status.clan_id;
+ }
+ }
+ break;
+ case BL_SKILL: {
+ const struct skill_unit *su = BL_UCCAST(BL_SKILL, bl);
+ if (su->group != NULL)
+ return su->group->clan_id;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * Checks every clan player and remove those who are inactive
+ */
+int clan_inactivity_kick(int tid, int64 tick, int id, intptr_t data)
+{
+ struct clan *c = NULL;
+ int i;
+
+ if ((c = clan->search(id)) != NULL) {
+ if (!c->kick_time || c->tid != tid || tid == INVALID_TIMER || c->tid == INVALID_TIMER) {
+ ShowError("Timer Mismatch (Time: %d seconds) %d != %d", c->kick_time, c->tid, tid);
+ Assert_retr(0, 0);
+ return 0;
+ }
+ for (i = 0; i < VECTOR_LENGTH(c->members); i++) {
+ struct clan_member *m = &VECTOR_INDEX(c->members, i);
+ if (m->char_id <= 0 || m->online <= 0)
+ continue;
+
+ if (m->online) {
+ struct map_session_data *sd = m->sd;
+ if (DIFF_TICK32(sockt->last_tick, sd->idletime) > c->kick_time) {
+ clan->leave(sd, false);
+ }
+ } else if ((time(NULL) - m->last_login) > c->kick_time) {
+ VECTOR_ERASE(c->members, i);
+ c->member_count--;
+ clif->clan_onlinecount(c);
+ }
+ }
+ //Perform the kick for offline members that didn't connect after a server restart
+ c->received = false;
+ intif->clan_kickoffline(c->clan_id, c->kick_time);
+ c->tid = timer->add(timer->gettick() + c->check_time, clan->inactivity_kick, c->clan_id, 0);
+ }
+ return 1;
+}
+
+/**
+ * Timeout for the request of offline kick
+ */
+int clan_request_kickoffline(int tid, int64 tick, int id, intptr_t data)
+{
+ struct clan *c = NULL;
+
+ if ((c = clan->search(id)) != NULL) {
+ if (c->req_kick_tid != tid || c->req_kick_tid == INVALID_TIMER) {
+ ShowError("Timer Mismatch %d != %d", c->tid, tid);
+ return 0;
+ }
+
+ if (c->received) {
+ c->req_kick_tid = INVALID_TIMER;
+ return 1;
+ }
+
+ intif->clan_kickoffline(c->clan_id, c->kick_time);
+ c->req_kick_tid = timer->add(timer->gettick() + clan->req_timeout, clan->request_kickoffline, c->clan_id, 0);
+ }
+ return 1;
+}
+
+/**
+ * Timeout of the request for counting members
+ */
+int clan_request_membercount(int tid, int64 tick, int id, intptr_t data)
+{
+ struct clan *c = NULL;
+
+ if ((c = clan->search(id)) != NULL) {
+ if (c->req_count_tid != tid || c->req_count_tid == INVALID_TIMER) {
+ ShowError("Timer Mismatch %d != %d", c->tid, tid);
+ return 0;
+ }
+
+ if (c->received) {
+ c->req_count_tid = INVALID_TIMER;
+ return 1;
+ }
+
+ intif->clan_membercount(c->clan_id, c->kick_time);
+ c->req_count_tid = timer->add(timer->gettick() + clan->req_timeout, clan->request_membercount, c->clan_id, 0);
+ }
+ return 1;
+}
+
+/**
+ * Processes any (plugin-defined) additional fields for a clan entry.
+ *
+ * @param[in, out] entry The destination clan entry, already initialized (clan_id is expected to be already set).
+ * @param[in] t The libconfig entry.
+ * @param[in] n Ordinal number of the entry, to be displayed in case of validation errors.
+ * @param[in] source Source of the entry (file name), to be displayed in case of validation errors.
+ */
+void clan_read_db_additional_fields(struct clan *entry, struct config_setting_t *t, int n, const char *source)
+{
+ // do nothing. plugins can do own work
+}
+
+void clan_read_buffs(struct clan *c, struct config_setting_t *buff, const char *source)
+{
+ struct clan_buff *b;
+ const char *str = NULL;
+
+ nullpo_retv(c);
+ nullpo_retv(buff);
+
+ b = &c->buff;
+
+ if (!libconfig->setting_lookup_int(buff, "Icon", &b->icon)) {
+ const char *temp_str = NULL;
+ if (libconfig->setting_lookup_string(buff, "Icon", &temp_str)) {
+ if (*temp_str && !script->get_constant(temp_str, &b->icon)) {
+ ShowWarning("Clan %d: Invalid Buff icon value. Defaulting to SI_BLANK.\n", c->clan_id);
+ b->icon = -1; // SI_BLANK
+ }
+ }
+ }
+
+ if (libconfig->setting_lookup_string(buff, "Script", &str)) {
+ b->script = *str ? script->parse(str, source, -b->icon, SCRIPT_IGNORE_EXTERNAL_BRACKETS, NULL) : NULL;
+ }
+}
+
+/**
+ * Processes one clandb entry from the libconfig file, loading and inserting it
+ * into the clan database.
+ *
+ * @param settings Libconfig setting entry. It is expected to be valid and it
+ * won't be freed (it is care of the caller to do so if
+ * necessary).
+ * @param source Source of the entry (file name), to be displayed in case of
+ * validation errors.
+ * @return int.
+ */
+int clan_read_db_sub(struct config_setting_t *settings, const char *source, bool reload)
+{
+ int total, s, valid = 0;
+
+ nullpo_retr(false, settings);
+
+ total = libconfig->setting_length(settings);
+
+ for (s = 0; s < total; s++) {
+ struct clan *c;
+ struct config_setting_t *cl, *buff, *allies, *antagonists;
+ const char *aName, *aLeader, *aMap, *aConst;
+ int max_members, kicktime = 0, kickchecktime = 0, id, i, j;
+
+ cl = libconfig->setting_get_elem(settings, s);
+
+ if (libconfig->setting_lookup_int(cl, "Id", &id)) {
+ if (id <= 0) {
+ ShowError("clan_read_db: Invalid Clan Id %d, skipping...\n", id);
+ return false;
+ }
+
+ if (clan->search(id) != NULL) {
+ ShowError("clan_read_db: Duplicate entry for Clan Id %d, skipping...\n", id);
+ return false;
+ }
+ } else {
+ ShowError("clan_read_db: failed to find 'Id' for clan #%d\n", s);
+ return false;
+ }
+
+ if (libconfig->setting_lookup_string(cl, "Const", &aConst)) {
+ if (!*aConst) {
+ ShowError("clan_read_db: Invalid Clan Const '%s', skipping...\n", aConst);
+ return false;
+ }
+
+ if (strlen(aConst) > NAME_LENGTH) {
+ ShowError("clan_read_db: Clan Name '%s' is longer than %d characters, skipping...\n", aConst, NAME_LENGTH);
+ return false;
+ }
+
+ if (clan->searchname(aConst) != NULL) {
+ ShowError("clan_read_db: Duplicate entry for Clan Const '%s', skipping...\n", aConst);
+ return false;
+ }
+ } else {
+ ShowError("clan_read_db: failed to find 'Const' for clan #%d\n", s);
+ return false;
+ }
+
+ if (libconfig->setting_lookup_string(cl, "Name", &aName)) {
+ if (!*aName) {
+ ShowError("clan_read_db: Invalid Clan Name '%s', skipping...\n", aName);
+ return false;
+ }
+
+ if (strlen(aName) > NAME_LENGTH) {
+ ShowError("clan_read_db: Clan Name '%s' is longer than %d characters, skipping...\n", aName, NAME_LENGTH);
+ return false;
+ }
+
+ if (clan->searchname(aName) != NULL) {
+ ShowError("clan_read_db: Duplicate entry for Clan Name '%s', skipping...\n", aName);
+ return false;
+ }
+ } else {
+ ShowError("clan_read_db: failed to find 'Name' for clan #%d\n", s);
+ return false;
+ }
+
+ if (!libconfig->setting_lookup_string(cl, "Leader", &aLeader)) {
+ ShowError("clan_read_db: failed to find 'Leader' for clan #%d\n", s);
+ return false;
+ }
+
+ if (!libconfig->setting_lookup_string(cl, "Map", &aMap)) {
+ ShowError("clan_read_db: failed to find 'Map' for clan #%d\n", s);
+ return false;
+ }
+
+ CREATE(c, struct clan, 1);
+
+ c->clan_id = id;
+ safestrncpy(c->constant, aConst, NAME_LENGTH);
+ safestrncpy(c->name, aName, NAME_LENGTH);
+ safestrncpy(c->master, aLeader, NAME_LENGTH);
+ safestrncpy(c->map, aMap, MAP_NAME_LENGTH_EXT);
+ c->connect_member = 0;
+ c->member_count = 0; // Char server will count members for us
+ c->received = false;
+ c->req_count_tid = INVALID_TIMER;
+ c->req_kick_tid = INVALID_TIMER;
+ c->tid = INVALID_TIMER;
+
+ if (libconfig->setting_lookup_int(cl, "MaxMembers", &max_members)) {
+ if (max_members > 0) {
+ c->max_member = max_members;
+ } else {
+ ShowError("clan_read_db: Clan #%d has invalid value for 'MaxMembers' setting, defaulting to 'clan->max'...\n", id);
+ c->max_member = clan->max;
+ }
+ } else {
+ c->max_member = clan->max;
+ }
+
+ if (libconfig->setting_lookup_int(cl, "KickTime", &kicktime)) {
+ c->kick_time = 60 * 60 * kicktime;
+ } else {
+ c->kick_time = clan->kicktime;
+ }
+
+ if (libconfig->setting_lookup_int(cl, "CheckTime", &kickchecktime)) {
+ c->check_time = 60 * 60 * max(1, kickchecktime) * 1000;
+ } else {
+ c->check_time = clan->checktime;
+ }
+
+ if ((buff = libconfig->setting_get_member(cl, "Buff")) != NULL) {
+ clan->read_buffs(c, buff, source);
+ }
+
+ allies = libconfig->setting_get_member(cl, "Allies");
+
+ if (allies != NULL) {
+ int a = libconfig->setting_length(allies);
+
+ VECTOR_INIT(c->allies);
+ if (a > 0 && clan->max_relations > 0) {
+ const char *allyConst;
+
+ if (a > clan->max_relations) {
+ ShowWarning("clan_read_db: Clan %d has more allies(%d) than allowed(%d), reading only the first %d...\n", c->clan_id, a, clan->max_relations, clan->max_relations);
+ a = clan->max_relations;
+ }
+
+ VECTOR_ENSURE(c->allies, a, 1);
+ for (i = 0; i < a; i++) {
+ struct clan_relationship r;
+ if ((allyConst = libconfig->setting_get_string_elem(allies, i)) != NULL) {
+ ARR_FIND(0, VECTOR_LENGTH(c->allies), j, strcmp(VECTOR_INDEX(c->allies, j).constant, allyConst) == 0);
+ if (j != VECTOR_LENGTH(c->allies)) {
+ ShowError("clan_read_db: Duplicate entry '%s' in allies for Clan %d in '%s', skipping...\n", allyConst, c->clan_id, source);
+ continue;
+ } else if (strcmp(allyConst, c->constant) == 0) {
+ ShowError("clan_read_db: Clans can't be allies of themselves! Clan Id: %d, in '%s'\n", c->clan_id, source);
+ continue;
+ }
+ safestrncpy(r.constant, allyConst, NAME_LENGTH);
+ VECTOR_PUSH(c->allies, r);
+ }
+
+ }
+ }
+ }
+ antagonists = libconfig->setting_get_member(cl, "Antagonists");
+
+ if (antagonists != NULL) {
+ int a = libconfig->setting_length(antagonists);
+
+ VECTOR_INIT(c->antagonists);
+ if (a > 0 && clan->max_relations > 0) {
+ const char *antagonistConst;
+
+ if (a > clan->max_relations) {
+ ShowWarning("clan_read_db: Clan %d has more antagonists(%d) than allowed(%d), reading only the first %d...\n", c->clan_id, a, clan->max_relations, clan->max_relations);
+ a = clan->max_relations;
+ }
+
+ VECTOR_ENSURE(c->antagonists, a, 1);
+ for (i = 0; i < a; i++) {
+ struct clan_relationship r;
+ if ((antagonistConst = libconfig->setting_get_string_elem(antagonists, i)) != NULL) {
+ ARR_FIND(0, VECTOR_LENGTH(c->antagonists), j, strcmp(VECTOR_INDEX(c->antagonists, j).constant, antagonistConst) == 0);
+ if (j != VECTOR_LENGTH(c->antagonists)) {
+ ShowError("clan_read_db: Duplicate entry '%s' in antagonists for Clan %d in '%s', skipping...\n", antagonistConst, c->clan_id, source);
+ continue;
+ } else if (strcmp(antagonistConst, c->constant) == 0) {
+ ShowError("clan_read_db: Clans can't be antagonists of themselves! Clan Id: %d, in '%s'\n", c->clan_id, source);
+ continue;
+ }
+ safestrncpy(r.constant, antagonistConst, NAME_LENGTH);
+ VECTOR_PUSH(c->antagonists, r);
+ }
+ }
+ }
+ }
+
+ clan->read_db_additional_fields(c, cl, s, source);
+ if (c->kick_time > 0) {
+ c->tid = timer->add(timer->gettick() + c->check_time, clan->inactivity_kick, c->clan_id, 0);
+ }
+ c->received = false;
+ c->req_state = reload ? CLAN_REQ_RELOAD : CLAN_REQ_FIRST;
+ c->req_count_tid = timer->add(timer->gettick() + clan->req_timeout, clan->request_membercount, c->clan_id, 0);
+ idb_put(clan->db, c->clan_id, c);
+ VECTOR_INIT(c->members);
+ valid++;
+ }
+
+ // Validating Relations
+ if (valid > 0) {
+ struct DBIterator *iter = db_iterator(clan->db);
+ struct clan *c_ok, *c;
+ int i;
+
+ for (c_ok = dbi_first(iter); dbi_exists(iter); c_ok = dbi_next(iter)) {
+ i = VECTOR_LENGTH(c_ok->allies);
+ while ( i > 0) {
+ struct clan_relationship *r;
+
+ i--;
+ r = &VECTOR_INDEX(c_ok->allies, i);
+ if ((c = clan->searchname(r->constant)) == NULL) {
+ ShowError("clan_read_db: Invalid (nonexistent) Ally '%s' for clan %d in '%s', skipping ally...\n", r->constant, c_ok->clan_id, source);
+ VECTOR_ERASE(c_ok->allies, i);
+ } else {
+ r->clan_id = c->clan_id;
+ }
+ }
+
+ i = VECTOR_LENGTH(c_ok->antagonists);
+ while ( i > 0) {
+ struct clan_relationship *r;
+
+ i--;
+ r = &VECTOR_INDEX(c_ok->antagonists, i);
+ if ((c = clan->searchname(r->constant)) == NULL) {
+ ShowError("clan_read_db: Invalid (nonexistent) Antagonist '%s' for clan %d in '%s', skipping antagonist...", r->constant, c_ok->clan_id, source);
+ VECTOR_ERASE(c_ok->antagonists, i);
+ } else {
+ r->clan_id = c->clan_id;
+ }
+ }
+ }
+ dbi_destroy(iter);
+ }
+
+ ShowStatus("Done reading '" CL_WHITE "%d" CL_RESET "' valid clans of '" CL_WHITE "%d" CL_RESET "' entries in '" CL_WHITE "%s" CL_RESET "'.\n", valid, total, source);
+ return valid;
+}
+
+/**
+ * Reads Clan DB included by clan configuration file.
+ *
+ * @param settings The Settings Group from config file.
+ * @param source File name.
+ */
+void clan_read_db(struct config_setting_t *settings, const char *source, bool reload)
+{
+ struct config_setting_t *clans;
+
+ nullpo_retv(settings);
+
+ if ((clans = libconfig->setting_get_member(settings, "clans")) != NULL) {
+ int read;
+
+ read = clan->read_db_sub(clans, source, reload);
+ if (read > 0) {
+ clan->set_constants();
+ }
+ } else {
+ ShowError("clan_read_db: Can't find setting 'clans' in '%s'. No Clans found.\n", source);
+ }
+}
+
+/**
+ * Reads clan config file
+ *
+ * @param bool clear Whether to clear clan->db before reading clans
+ */
+bool clan_config_read(bool reload)
+{
+ struct config_t clan_conf;
+ struct config_setting_t *settings = NULL;
+ const char *config_filename = "conf/clans.conf"; // FIXME: hardcoded name
+ int kicktime = 0, kickchecktime = 0;
+
+ if (reload) {
+ struct DBIterator *iter = db_iterator(clan->db);
+ struct clan *c_clear;
+
+ for (c_clear = dbi_first(iter); dbi_exists(iter); c_clear = dbi_next(iter)) {
+ if (c_clear->buff.script != NULL) {
+ script->free_code(c_clear->buff.script);
+ }
+ if (c_clear->tid != INVALID_TIMER) {
+ timer->delete(c_clear->tid, clan->inactivity_kick);
+ }
+ VECTOR_CLEAR(c_clear->allies);
+ VECTOR_CLEAR(c_clear->antagonists);
+ VECTOR_CLEAR(c_clear->members);
+ HPM->data_store_destroy(&c_clear->hdata);
+ }
+ dbi_destroy(iter);
+ clan->db->clear(clan->db, NULL);
+ }
+
+ if (!libconfig->load_file(&clan_conf, config_filename)) {
+ return false;
+ }
+
+ if ((settings = libconfig->lookup(&clan_conf, "clan_configuration")) == NULL) {
+ ShowError("clan_config_read: failed to find 'clan_configuration'.\n");
+ return false;
+ }
+
+ libconfig->setting_lookup_int(settings, "MaxMembers", &clan->max);
+ libconfig->setting_lookup_int(settings, "MaxRelations", &clan->max_relations);
+ libconfig->setting_lookup_int(settings, "InactivityKickTime", &kicktime);
+ if (!libconfig->setting_lookup_int(settings, "InactivityCheckTime", &kickchecktime)) {
+ ShowError("clan_config_read: failed to find InactivityCheckTime using official value.\n");
+ kickchecktime = 24;
+ }
+
+ // On config file we set the time in hours but here we use in seconds
+ clan->kicktime = 60 * 60 * kicktime;
+ clan->checktime = 60 * 60 * max(kickchecktime, 1) * 1000;
+
+ clan->config_read_additional_settings(settings, config_filename);
+ clan->read_db(settings, config_filename, reload);
+ libconfig->destroy(&clan_conf);
+ return true;
+}
+
+/**
+ * Processes any (plugin-defined) additional settings for clan config.
+ *
+ * @param settings The Settings Group from config file.
+ * @param source Source of the entry (file name), to be displayed in
+ * case of validation errors.
+ */
+void clan_config_read_additional_settings(struct config_setting_t *settings, const char *source)
+{
+ // do nothing. plugins can do own work
+}
+
+/**
+ * Reloads Clan DB
+ */
+void clan_reload(void)
+{
+ clan->config_read(true);
+}
+
+/**
+ *
+ */
+void do_init_clan(bool minimal)
+{
+ if (minimal) {
+ return;
+ }
+ clan->db = idb_alloc(DB_OPT_RELEASE_DATA);
+ clan->config_read(false);
+ timer->add_func_list(clan->inactivity_kick, "clan_inactivity_kick");
+}
+
+/**
+ *
+ */
+void do_final_clan(void)
+{
+ struct DBIterator *iter = db_iterator(clan->db);
+ struct clan *c;
+
+ for (c = dbi_first(iter); dbi_exists(iter); c = dbi_next(iter)) {
+ if (c->buff.script) {
+ script->free_code(c->buff.script);
+ }
+ if (c->tid != INVALID_TIMER) {
+ timer->delete(c->tid, clan->inactivity_kick);
+ }
+ VECTOR_CLEAR(c->allies);
+ VECTOR_CLEAR(c->antagonists);
+ VECTOR_CLEAR(c->members);
+ HPM->data_store_destroy(&c->hdata);
+ }
+ dbi_destroy(iter);
+ db_destroy(clan->db);
+}
+
+/**
+ * Inits Clan Defaults
+ */
+void clan_defaults(void)
+{
+ clan = &clan_s;
+
+ clan->init = do_init_clan;
+ clan->final = do_final_clan;
+ /* */
+ clan->db = NULL;
+ clan->max = 0;
+ clan->max_relations = 0;
+ clan->kicktime = 0;
+ clan->checktime = 0;
+ clan->req_timeout = 60;
+ /* */
+ clan->config_read = clan_config_read;
+ clan->config_read_additional_settings = clan_config_read_additional_settings;
+ clan->read_db = clan_read_db;
+ clan->read_db_sub = clan_read_db_sub;
+ clan->read_db_additional_fields = clan_read_db_additional_fields;
+ clan->read_buffs = clan_read_buffs;
+ clan->search = clan_search;
+ clan->searchname = clan_searchname;
+ clan->getonlinesd = clan_getonlinesd;
+ clan->getindex = clan_getindex;
+ clan->join = clan_join;
+ clan->member_online = clan_member_online;
+ clan->leave = clan_leave;
+ clan->send_message = clan_send_message;
+ clan->recv_message = clan_recv_message;
+ clan->member_offline = clan_member_offline;
+ clan->set_constants = clan_set_constants;
+ clan->get_id = clan_get_id;
+ clan->buff_start = clan_buff_start;
+ clan->buff_end = clan_buff_end;
+ clan->reload = clan_reload;
+ clan->rejoin = clan_rejoin;
+ clan->inactivity_kick = clan_inactivity_kick;
+ clan->request_kickoffline = clan_request_kickoffline;
+ clan->request_membercount = clan_request_membercount;
+}
diff --git a/src/map/clan.h b/src/map/clan.h
new file mode 100644
index 000000000..15ed52680
--- /dev/null
+++ b/src/map/clan.h
@@ -0,0 +1,85 @@
+/**
+ * This file is part of Hercules.
+ * http://herc.ws - http://github.com/HerculesWS/Hercules
+ *
+ * Copyright (C) 2017 Hercules Dev Team
+ *
+ * Hercules is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef MAP_CLAN_H
+#define MAP_CLAN_H
+
+#include "map/map.h"
+#include "common/hercules.h"
+#include "common/db.h"
+#include "common/mmo.h"
+
+struct map_session_data;
+
+enum clan_req_state {
+ CLAN_REQ_NONE = 0,
+ CLAN_REQ_FIRST = 1,
+ CLAN_REQ_RELOAD = 2,
+ CLAN_REQ_AFTER_KICK = 3,
+};
+
+/**
+ * clan Interface
+ **/
+struct clan_interface {
+ struct DBMap *db; // int clan_id -> struct clan*
+
+ int max;
+ int max_relations;
+ int kicktime;
+ int checktime;
+ int req_timeout;
+
+ void (*init) (bool minimal);
+ void (*final) (void);
+
+ bool (*config_read) (bool reload);
+ void (*config_read_additional_settings) (struct config_setting_t *settings, const char *source);
+ void (*read_db) (struct config_setting_t *settings, const char *source, bool reload);
+ int (*read_db_sub) (struct config_setting_t *settings, const char *source, bool reload);
+ void (*read_db_additional_fields) (struct clan *entry, struct config_setting_t *t, int n, const char *source);
+ void (*read_buffs) (struct clan *c, struct config_setting_t *buff, const char *source);
+ struct clan *(*search) (int clan_id);
+ struct clan *(*searchname) (const char *name);
+ struct map_session_data *(*getonlinesd) (struct clan *c);
+ int (*getindex) (const struct clan *c, int char_id);
+ bool (*join) (struct map_session_data *sd, int clan_id);
+ void (*member_online) (struct map_session_data *sd, bool first);
+ bool (*leave) (struct map_session_data *sd, bool first);
+ bool (*send_message) (struct map_session_data *sd, const char *mes);
+ void (*recv_message) (struct clan *c, const char *mes, int len);
+ void (*member_offline) (struct map_session_data *sd);
+ void (*set_constants) (void);
+ int (*get_id) (const struct block_list *bl);
+ void (*buff_start) (struct map_session_data *sd, struct clan *c);
+ void (*buff_end) (struct map_session_data *sd, struct clan *c);
+ void (*reload) (void);
+ int (*rejoin) (struct map_session_data *sd, va_list ap);
+ int (*inactivity_kick) (int tid, int64 tick, int id, intptr_t data);
+ int (*request_kickoffline) (int tid, int64 tick, int id, intptr_t data);
+ int (*request_membercount) (int tid, int64 tick, int id, intptr_t data);
+};
+
+#ifdef HERCULES_CORE
+void clan_defaults (void);
+#endif // HERCULES_CORE
+
+HPShared struct clan_interface *clan;
+
+#endif /* MAP_CLAN_H */
diff --git a/src/map/clif.c b/src/map/clif.c
index 988e821e7..dc279f7ed 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -29,6 +29,7 @@
#include "map/channel.h"
#include "map/chat.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/elemental.h"
#include "map/guild.h"
#include "map/homunculus.h"
@@ -659,6 +660,22 @@ bool clif_send(const void* buf, int len, struct block_list* bl, enum send_target
}
break;
+ case CLAN:
+ if (sd && sd->status.clan_id) {
+ struct clan *c = clan->search(sd->status.clan_id);
+
+ nullpo_retr(false, c);
+
+ for (i = 0; i < VECTOR_LENGTH(c->members); i++) {
+ if ((sd = VECTOR_INDEX(c->members, i).sd) == NULL || (fd = sd->fd) <= 0)
+ continue;
+ WFIFOHEAD(fd, len);
+ memcpy(WFIFOP(fd, 0), buf, len);
+ WFIFOSET(fd, len);
+ }
+ }
+ break;
+
default:
ShowError("clif_send: Unrecognized type %u\n", type);
return false;
@@ -1534,6 +1551,8 @@ bool clif_spawn(struct block_list *bl)
clif->specialeffect(&nd->bl,423,AREA);
else if (nd->size == SZ_MEDIUM)
clif->specialeffect(&nd->bl,421,AREA);
+ if (nd->clan_id > 0)
+ clif->sc_load(&nd->bl, nd->bl.id, AREA, status->dbs->IconChangeTable[SC_CLAN_INFO], 0, nd->clan_id, 0);
}
break;
case BL_PET:
@@ -4388,6 +4407,8 @@ void clif_getareachar_unit(struct map_session_data* sd,struct block_list *bl) {
clif->specialeffect_single(bl,423,sd->fd);
else if (nd->size == SZ_MEDIUM)
clif->specialeffect_single(bl,421,sd->fd);
+ if (nd->clan_id > 0)
+ clif->sc_load(&nd->bl, nd->bl.id, AREA, status->dbs->IconChangeTable[SC_CLAN_INFO], 0, nd->clan_id, 0);
}
break;
case BL_MOB:
@@ -13340,6 +13361,12 @@ bool clif_sub_guild_invite(int fd, struct map_session_data *sd, struct map_sessi
return false;
}
+ // Players in a clan can't join a guild
+ if (t_sd->clan != NULL) {
+ clif->message(fd, msg_fd(fd, 140)); // You can't join in a clan if you're in a guild.
+ return false;
+ }
+
guild->invite(sd,t_sd);
return true;
}
@@ -19282,6 +19309,156 @@ const char *clif_get_bl_name(const struct block_list *bl)
return name;
}
+/**
+ * Clan System: Sends the basic clan informations to client.
+ * 098a <length>.W <clan id>.L <clan name>.24B <clan master>.24B <clan map>.16B <alliance count>.B
+ * <antagonist count>.B { <alliance>.24B } * alliance count { <antagonist>.24B } * antagonist count (ZC_CLANINFO)
+ */
+void clif_clan_basicinfo(struct map_session_data *sd)
+{
+#if PACKETVER >= 20120716
+ int len, i, fd;
+ struct clan *c, *ally, *antagonist;
+ struct PACKET_ZC_CLANINFO *packet = NULL;
+
+ nullpo_retv(sd);
+ nullpo_retv(c = sd->clan);
+
+ len = sizeof(struct PACKET_ZC_CLANINFO);
+ fd = sd->fd;
+
+ WFIFOHEAD(fd, len);
+ packet = WFIFOP(fd, 0);
+
+ packet->PacketType = clanBasicInfo;
+ packet->ClanID = c->clan_id;
+
+ safestrncpy(packet->ClanName, c->name, NAME_LENGTH);
+ safestrncpy(packet->MasterName, c->master, NAME_LENGTH);
+
+ mapindex->getmapname_ext(c->map, packet->Map);
+
+ packet->AllyCount = VECTOR_LENGTH(c->allies);
+ packet->AntagonistCount = VECTOR_LENGTH(c->antagonists);
+
+ // All allies and antagonists are assumed as valid entries
+ // since it only gets inside the vector after the validation
+ // on clan->config_read
+ for (i = 0; i < VECTOR_LENGTH(c->allies); i++) {
+ struct clan_relationship *al = &VECTOR_INDEX(c->allies, i);
+
+ if ((ally = clan->search(al->clan_id)) != NULL) {
+ safestrncpy(WFIFOP(fd, len), ally->name, NAME_LENGTH);
+ len += NAME_LENGTH;
+ }
+ }
+
+ for (i = 0; i < VECTOR_LENGTH(c->antagonists); i++) {
+ struct clan_relationship *an = &VECTOR_INDEX(c->antagonists, i);
+
+ if ((antagonist = clan->search(an->clan_id)) != NULL) {
+ safestrncpy(WFIFOP(fd, len), antagonist->name, NAME_LENGTH);
+ len += NAME_LENGTH;
+ }
+ }
+
+ packet->PacketLength = len;
+ WFIFOSET(fd, len);
+#endif
+}
+
+/**
+ * Clan System: Updates the online and maximum player count of a clan.
+ * 0988 <online count>.W <maximum member amount>.W (ZC_NOTIFY_CLAN_CONNECTINFO)
+ */
+void clif_clan_onlinecount(struct clan *c)
+{
+#if PACKETVER >= 20120716
+ struct map_session_data *sd;
+ struct PACKET_ZC_NOTIFY_CLAN_CONNECTINFO p;
+
+ nullpo_retv(c);
+
+ p.PacketType = clanOnlineCount;
+ p.NumConnect = c->connect_member;
+ p.NumTotal = c->max_member;
+
+ if ((sd = clan->getonlinesd(c)) != NULL) {
+ clif->send(&p, sizeof(p), &sd->bl, CLAN);
+ }
+#endif
+}
+
+/**
+* Clan System: Notifies the client that the player has left his clan.
+* 0989 (ZC_ACK_CLAN_LEAVE)
+**/
+void clif_clan_leave(struct map_session_data* sd)
+{
+#if PACKETVER >= 20131223
+ struct PACKET_ZC_ACK_CLAN_LEAVE p;
+
+ nullpo_retv(sd);
+
+ p.PacketType = clanLeave;
+
+ clif->send(&p, sizeof(p), &sd->bl, SELF);
+#endif
+}
+
+/**
+ * Clan System: Sends a clan message to a player
+ * 098e <length>.W <name>.24B <message>.?B (ZC_NOTIFY_CLAN_CHAT)
+ */
+void clif_clan_message(struct clan *c, const char *mes, int len)
+{
+#if PACKETVER >= 20120716
+ struct map_session_data *sd;
+ struct PACKET_ZC_NOTIFY_CLAN_CHAT *p;
+ unsigned int max_len = CHAT_SIZE_MAX - 5 - NAME_LENGTH;
+ int packet_length;
+
+ nullpo_retv(c);
+ nullpo_retv(mes);
+
+ if (len == 0) {
+ return;
+ } else if (len > max_len) {
+ ShowWarning("clif_clan_message: Truncated message '%s' (len=%d, max=%u, clan_id=%d).\n", mes, len, max_len, c->clan_id);
+ len = max_len;
+ }
+
+ packet_length = sizeof(*p) + len + 1;
+ p = (struct PACKET_ZC_NOTIFY_CLAN_CHAT *)aMalloc(packet_length);
+ p->PacketType = clanMessage;
+ p->PacketLength = packet_length;
+ // p->MemberName is being ignored on the client side.
+ safestrncpy(p->Message, mes, len + 1);
+
+ if ((sd = clan->getonlinesd(c)) != NULL)
+ clif->send(p, packet_length, &sd->bl, CLAN);
+ aFree(p);
+#endif
+}
+
+void clif_parse_ClanMessage(int fd, struct map_session_data *sd) __attribute__((nonnull (2)));
+/**
+ * Clan System: Parses a clan message from a player.
+ * 098d <length>.W <text>.?B (<name> : <message>) (CZ_CLAN_CHAT)
+ */
+void clif_parse_ClanMessage(int fd, struct map_session_data *sd)
+{
+#if PACKETVER >= 20120716
+ const struct packet_chat_message *packet = RP2PTR(fd);
+ char message[CHAT_SIZE_MAX + NAME_LENGTH + 3 + 1];
+
+ if (clif->process_chat_message(sd, packet, message, sizeof(message)) == NULL)
+ return;
+
+ clan->send_message(sd, message);
+#endif
+}
+
/* */
unsigned short clif_decrypt_cmd( int cmd, struct map_session_data *sd ) {
if( sd ) {
@@ -21006,4 +21183,10 @@ void clif_defaults(void) {
clif->rodex_icon = clif_rodex_icon;
clif->rodex_send_mails_all = clif_rodex_send_mails_all;
clif->skill_scale = clif_skill_scale;
+ // -- Clan system
+ clif->clan_basicinfo = clif_clan_basicinfo;
+ clif->clan_onlinecount = clif_clan_onlinecount;
+ clif->clan_leave = clif_clan_leave;
+ clif->clan_message = clif_clan_message;
+ clif->pClanMessage = clif_parse_ClanMessage;
}
diff --git a/src/map/clif.h b/src/map/clif.h
index 69567cc2c..c393c4f64 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -110,6 +110,8 @@ typedef enum send_target {
BG_AREA_WOS,
BG_QUEUE,
+
+ CLAN, // Clan System
} send_target;
typedef enum broadcast_flags {
@@ -1405,6 +1407,12 @@ struct clif_interface {
void (*rodex_request_items) (struct map_session_data *sd, int8 opentype, int64 mail_id, int8 result);
void (*rodex_icon) (int fd, bool show);
void (*skill_scale) (struct block_list *bl, int src_id, int x, int y, uint16 skill_id, uint16 skill_lv, int casttime);
+ /* Clan System */
+ void (*clan_basicinfo) (struct map_session_data *sd);
+ void (*clan_onlinecount) (struct clan *c);
+ void (*clan_leave) (struct map_session_data *sd);
+ void (*clan_message) (struct clan *c, const char *mes, int len);
+ void (*pClanMessage) (int fd, struct map_session_data* sd);
};
#ifdef HERCULES_CORE
diff --git a/src/map/guild.c b/src/map/guild.c
index bb0484477..11609ec81 100644
--- a/src/map/guild.c
+++ b/src/map/guild.c
@@ -365,6 +365,11 @@ int guild_create(struct map_session_data *sd, const char *name)
nullpo_ret(sd);
nullpo_ret(name);
+ if (sd->clan != NULL) {
+ clif->messagecolor_self(sd->fd, COLOR_RED, "You cannot create a guild because you are in a clan.");
+ return 0;
+ }
+
safestrncpy(tname, name, NAME_LENGTH);
trim(tname);
diff --git a/src/map/intif.c b/src/map/intif.c
index be6b75d96..c933ceb15 100644
--- a/src/map/intif.c
+++ b/src/map/intif.c
@@ -26,6 +26,7 @@
#include "map/atcommand.h"
#include "map/battle.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/elemental.h"
#include "map/guild.h"
@@ -744,6 +745,81 @@ int intif_party_leaderchange(int party_id,int account_id,int char_id)
return 0;
}
+//=========================
+// Clan System
+//-------------------------
+
+/**
+ * Request clan member count
+ *
+ * @param clan_id Id of the clan to have members counted
+ * @param kick_interval Interval of the inactivity kick
+ */
+int intif_clan_membercount(int clan_id, int kick_interval)
+{
+ if (intif->CheckForCharServer() || clan_id == 0 || kick_interval <= 0)
+ return 0;
+
+ WFIFOHEAD(inter_fd, 10);
+ WFIFOW(inter_fd, 0) = 0x3044;
+ WFIFOL(inter_fd, 2) = clan_id;
+ WFIFOL(inter_fd, 6) = kick_interval;
+ WFIFOSET(inter_fd, 10);
+ return 1;
+}
+
+int intif_clan_kickoffline(int clan_id, int kick_interval)
+{
+ if (intif->CheckForCharServer() || clan_id == 0 || kick_interval <= 0)
+ return 0;
+
+ WFIFOHEAD(inter_fd, 10);
+ WFIFOW(inter_fd, 0) = 0x3045;
+ WFIFOL(inter_fd, 2) = clan_id;
+ WFIFOL(inter_fd, 6) = kick_interval;
+ WFIFOSET(inter_fd, 10);
+ return 1;
+}
+
+void intif_parse_RecvClanMemberAction(int fd)
+{
+ struct clan *c;
+ int clan_id = RFIFOL(fd, 2);
+ int count = RFIFOL(fd, 6);
+
+ if ((c = clan->search(clan_id)) == NULL) {
+ ShowError("intif_parse_RecvClanMemberAction: Received invalid clan_id '%d'\n", clan_id);
+ return;
+ }
+
+ if (count < 0) {
+ ShowError("intif_parse_RecvClanMemberAction: Received invalid member count value '%d'\n", count);
+ return;
+ }
+
+ c->received = true;
+ if (c->req_count_tid != INVALID_TIMER) {
+ timer->delete(c->req_count_tid, clan->request_membercount);
+ c->req_count_tid = INVALID_TIMER;
+ }
+
+ c->member_count = count;
+ switch (c->req_state) {
+ case CLAN_REQ_AFTER_KICK:
+ if (c->req_kick_tid != INVALID_TIMER) {
+ timer->delete(c->req_kick_tid, clan->request_kickoffline);
+ c->req_kick_tid = INVALID_TIMER;
+ }
+ break;
+ case CLAN_REQ_RELOAD:
+ map->foreachpc(clan->rejoin);
+ break;
+ default:
+ break;
+ }
+ c->req_state = CLAN_REQ_NONE;
+}
+
// Request a Guild creation
int intif_guild_create(const char *name,const struct guild_member *master)
{
@@ -2744,6 +2820,9 @@ int intif_parse(int fd)
case 0x3896: intif->pRodexHasNew(fd); break;
case 0x3897: intif->pRodexSendMail(fd); break;
case 0x3898: intif->pRodexCheckName(fd); break;
+
+ // Clan System
+ case 0x3858: intif->pRecvClanMemberAction(fd); break;
default:
ShowError("intif_parse : unknown packet %d %x\n",fd,RFIFOW(fd,0));
return 0;
@@ -2765,7 +2844,7 @@ void intif_defaults(void) {
39,-1,15,15, 14,19, 7,-1, 0, 0, 0, 0, 0, 0, 0, 0, //0x3820
10,-1,15, 0, 79,19, 7,-1, 0,-1,-1,-1, 14,67,186,-1, //0x3830
-1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840
- -1,-1, 7, 7, 7,11, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari]
+ -1,-1, 7, 7, 7,11, 8, 0, 10, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus] itembound[Akinari] Clan System[Murilo BiO]
-1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish]
-1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil]
12,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880
@@ -2857,6 +2936,9 @@ void intif_defaults(void) {
intif->rodex_updatemail = intif_rodex_updatemail;
intif->rodex_sendmail = intif_rodex_sendmail;
intif->rodex_checkname = intif_rodex_checkname;
+ /* Clan System */
+ intif->clan_kickoffline = intif_clan_kickoffline;
+ intif->clan_membercount = intif_clan_membercount;
/* @accinfo */
intif->request_accinfo = intif_request_accinfo;
/* */
@@ -2933,4 +3015,6 @@ void intif_defaults(void) {
intif->pRodexHasNew = intif_parse_RodexNotifications;
intif->pRodexSendMail = intif_parse_RodexSendMail;
intif->pRodexCheckName = intif_parse_RodexCheckName;
+ /* Clan System */
+ intif->pRecvClanMemberAction = intif_parse_RecvClanMemberAction;
}
diff --git a/src/map/intif.h b/src/map/intif.h
index 4bca5f167..be8ff4181 100644
--- a/src/map/intif.h
+++ b/src/map/intif.h
@@ -138,6 +138,9 @@ struct intif_interface {
int(*rodex_updatemail) (int64 mail_id, int8 flag);
int(*rodex_sendmail) (struct rodex_message *msg);
int(*rodex_checkname) (struct map_session_data *sd, const char *name);
+ /* Clan System */
+ int (*clan_kickoffline) (int clan_id, int kick_interval);
+ int (*clan_membercount) (int clan_id, int kick_interval);
/* @accinfo */
void (*request_accinfo) (int u_fd, int aid, int group_lv, char* query);
/* */
@@ -212,6 +215,8 @@ struct intif_interface {
void(*pRodexHasNew) (int fd);
void(*pRodexSendMail) (int fd);
void(*pRodexCheckName) (int fd);
+ /* Clan System */
+ void (*pRecvClanMemberAction) (int fd);
};
#ifdef HERCULES_CORE
diff --git a/src/map/log.c b/src/map/log.c
index 6419c4766..c3fec077e 100644
--- a/src/map/log.c
+++ b/src/map/log.c
@@ -82,13 +82,21 @@ char log_picktype2char(e_log_pick_type type) {
}
/// obtain log type character for chat logs
-char log_chattype2char(e_log_chat_type type) {
- switch( type ) {
- case LOG_CHAT_GLOBAL: return 'O'; // Gl(O)bal
- case LOG_CHAT_WHISPER: return 'W'; // (W)hisper
- case LOG_CHAT_PARTY: return 'P'; // (P)arty
- case LOG_CHAT_GUILD: return 'G'; // (G)uild
- case LOG_CHAT_MAINCHAT: return 'M'; // (M)ain chat
+char log_chattype2char(e_log_chat_type type)
+{
+ switch (type) {
+ case LOG_CHAT_GLOBAL:
+ return 'O'; // Gl(O)bal
+ case LOG_CHAT_WHISPER:
+ return 'W'; // (W)hisper
+ case LOG_CHAT_PARTY:
+ return 'P'; // (P)arty
+ case LOG_CHAT_GUILD:
+ return 'G'; // (G)uild
+ case LOG_CHAT_MAINCHAT:
+ return 'M'; // (M)ain chat
+ case LOG_CHAT_CLAN:
+ return 'C'; // (C)lan
}
// should not get here, fallback
diff --git a/src/map/log.h b/src/map/log.h
index 7ff36d126..e7eb72713 100644
--- a/src/map/log.h
+++ b/src/map/log.h
@@ -52,6 +52,7 @@ typedef enum e_log_chat_type {
LOG_CHAT_PARTY = 0x04,
LOG_CHAT_GUILD = 0x08,
LOG_CHAT_MAINCHAT = 0x10,
+ LOG_CHAT_CLAN = 0x20,
// all
LOG_CHAT_ALL = 0xFF,
} e_log_chat_type;
diff --git a/src/map/map.c b/src/map/map.c
index 306f3a99d..106224a47 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -30,6 +30,7 @@
#include "map/channel.h"
#include "map/chat.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/duel.h"
#include "map/elemental.h"
@@ -1904,6 +1905,9 @@ int map_quit(struct map_session_data *sd) {
if( sd->bg_id && !sd->bg_queue.arena ) /* TODO: dump this chunk after bg_queue is fully enabled */
bg->team_leave(sd,BGTL_QUIT);
+ if (sd->status.clan_id)
+ clan->member_offline(sd);
+
if (sd->state.autotrade && core->runflag != MAPSERVER_ST_SHUTDOWN && !channel->config->closing)
pc->autotrade_update(sd,PAUC_REMOVE);
@@ -4815,6 +4819,15 @@ bool map_zone_mf_cache(int m, char *flag, char *params) {
else if( map->list[m].flag.battleground )
map_zone_mf_cache_add(m,"battleground");
}
+ } else if (!strcmpi(flag,"cvc")) {
+ if (state && map->list[m].flag.cvc) {
+ ;/* nothing to do */
+ } else {
+ if (state)
+ map_zone_mf_cache_add(m,"cvc\toff");
+ else if (map->list[m].flag.cvc)
+ map_zone_mf_cache_add(m,"cvc");
+ }
} else if (!strcmpi(flag,"noexppenalty")) {
if( state && map->list[m].flag.noexppenalty )
;/* nothing to do */
@@ -5839,6 +5852,8 @@ void read_map_zone_db(void) {
zone->merge_type = MZMT_MERGEABLE;
if( (zone = strdb_get(map->zone_db, MAP_ZONE_BG_NAME)) )
zone->merge_type = MZMT_MERGEABLE;
+ if ((zone = strdb_get(map->zone_db, MAP_ZONE_CVC_NAME)))
+ zone->merge_type = MZMT_MERGEABLE;
}
/* not supposed to go in here but in skill_final whatever */
libconfig->destroy(&map_zone_db);
@@ -5999,6 +6014,7 @@ int do_final(void) {
ircbot->final();/* before channel. */
channel->final();
chrif->final();
+ clan->final();
clif->final();
npc->final();
quest->final();
@@ -6185,6 +6201,7 @@ void map_load_defaults(void) {
battleground_defaults();
buyingstore_defaults();
channel_defaults();
+ clan_defaults();
clif_defaults();
chrif_defaults();
guild_defaults();
@@ -6517,6 +6534,7 @@ int do_init(int argc, char *argv[])
ircbot->init(minimal);
script->init(minimal);
itemdb->init(minimal);
+ clan->init(minimal);
skill->init(minimal);
if (!minimal)
map->read_zone_db();/* read after item and skill initialization */
diff --git a/src/map/map.h b/src/map/map.h
index fab8839d8..facf1d921 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -352,6 +352,7 @@ STATIC_ASSERT(((MAPID_1_1_MAX - 1) | MAPID_BASEMASK) == MAPID_BASEMASK, "First c
|| map->list[m].flag.gvg \
|| ((map->agit_flag || map->agit2_flag) && map->list[m].flag.gvg_castle) \
|| map->list[m].flag.battleground \
+ || map->list[m].flag.cvc \
)
// Specifies maps that have special GvG/WoE restrictions
#define map_flag_gvg(m) (map->list[m].flag.gvg || ((map->agit_flag || map->agit2_flag) && map->list[m].flag.gvg_castle))
@@ -737,6 +738,7 @@ enum map_zone_merge_type {
#define MAP_ZONE_PVP_NAME "PvP"
#define MAP_ZONE_GVG_NAME "GvG"
#define MAP_ZONE_BG_NAME "Battlegrounds"
+#define MAP_ZONE_CVC_NAME "CvC"
#define MAP_ZONE_PK_NAME "PK Mode"
#define MAP_ZONE_MAPFLAG_LENGTH 50
@@ -823,6 +825,7 @@ struct map_data {
unsigned gvg_dungeon : 1; // Celest
unsigned gvg_noparty : 1;
unsigned battleground : 2; // [BattleGround System]
+ unsigned cvc : 1;
unsigned nozenypenalty : 1;
unsigned notrade : 1;
unsigned noskill : 1;
diff --git a/src/map/mob.h b/src/map/mob.h
index 98d64873b..3d1b3aadf 100644
--- a/src/map/mob.h
+++ b/src/map/mob.h
@@ -208,6 +208,7 @@ struct mob_data {
int target_id,attacked_id;
int areanpc_id; //Required in OnTouchNPC (to avoid multiple area touchs)
unsigned int bg_id; // BattleGround System
+ int clan_id; // Clan System
int64 next_walktime, last_thinktime, last_linktime, last_pcneartime, dmgtick;
short move_fail_count;
diff --git a/src/map/npc.c b/src/map/npc.c
index d3dfb39d2..94ab57bd9 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -25,6 +25,7 @@
#include "map/battle.h"
#include "map/chat.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/guild.h"
#include "map/instance.h"
@@ -4144,6 +4145,11 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
ShowWarning("npc_parse_mapflag: You can't set PvP and GvG flags for the same map! Removing GvG flags from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
}
+ if (state && map->list[m].flag.cvc) {
+ map->list[m].flag.cvc = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and PvP flags for the same map! Removing CvC flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) *retval = EXIT_FAILURE;
+ }
if( state && map->list[m].flag.battleground ) {
map->list[m].flag.battleground = 0;
ShowWarning("npc_parse_mapflag: You can't set PvP and BattleGround flags for the same map! Removing BattleGround flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
@@ -4196,6 +4202,11 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
ShowWarning("npc_parse_mapflag: You can't set PvP and GvG flags for the same map! Removing PvP flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
}
+ if (state && map->list[m].flag.cvc) {
+ map->list[m].flag.cvc = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and GvG flags for the same map! Removing CvC flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) *retval = EXIT_FAILURE;
+ }
if( state && map->list[m].flag.battleground ) {
map->list[m].flag.battleground = 0;
ShowWarning("npc_parse_mapflag: You can't set GvG and BattleGround flags for the same map! Removing BattleGround flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
@@ -4238,11 +4249,47 @@ const char *npc_parse_mapflag(const char *w1, const char *w2, const char *w3, co
ShowWarning("npc_parse_mapflag: You can't set GvG and BattleGround flags for the same map! Removing GvG flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer,start-buffer));
if (retval) *retval = EXIT_FAILURE;
}
+ if (map->list[m].flag.cvc) {
+ map->list[m].flag.cvc = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and BattleGround flags for the same map! Removing CvC flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) *retval = EXIT_FAILURE;
+ }
if( state && (zone = strdb_get(map->zone_db, MAP_ZONE_BG_NAME)) != NULL && map->list[m].zone != zone ) {
map->zone_change(m,zone,start,buffer,filepath);
}
}
+ else if (!strcmpi(w3, "cvc")) {
+ struct map_zone_data *zone;
+
+ map->list[m].flag.cvc = state;
+ if (state && (map->list[m].flag.gvg || map->list[m].flag.gvg_dungeon || map->list[m].flag.gvg_castle)) {
+ map->list[m].flag.gvg = 0;
+ map->list[m].flag.gvg_dungeon = 0;
+ map->list[m].flag.gvg_castle = 0;
+ ShowWarning("npc_parse_mapflag: You can't set GvG and CvC flags for the same map! Removing GvG flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) {
+ *retval = EXIT_FAILURE;
+ }
+ }
+ if (state && map->list[m].flag.pvp) {
+ map->list[m].flag.pvp = 0;
+ ShowWarning("npc_parse_mapflag: You can't set PvP and CvC flags for the same map! Removing PvP flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) {
+ *retval = EXIT_FAILURE;
+ }
+ }
+ if (state && map->list[m].flag.battleground) {
+ map->list[m].flag.battleground = 0;
+ ShowWarning("npc_parse_mapflag: You can't set CvC and BattleGround flags for the same map! Removing BattleGround flag from %s in file '%s', line '%d'.\n", map->list[m].name, filepath, strline(buffer, start-buffer));
+ if (retval) {
+ *retval = EXIT_FAILURE;
+ }
+ }
+ if (state && (zone = strdb_get(map->zone_db, MAP_ZONE_CVC_NAME)) != NULL && map->list[m].zone != zone) {
+ map->zone_change(m, zone, start, buffer, filepath);
+ }
+ }
else if (!strcmpi(w3,"noexppenalty"))
map->list[m].flag.noexppenalty=state;
else if (!strcmpi(w3,"nozenypenalty"))
@@ -4887,6 +4934,7 @@ int npc_reload(void) {
// Reprocess npc files and reload constants
itemdb->name_constants();
+ clan->set_constants();
npc_process_files( npc_new_min );
instance->reload();
@@ -5026,6 +5074,7 @@ int do_init_npc(bool minimal) {
// Should be loaded before npc processing, otherwise labels could overwrite constant values
// and lead to undefined behavior [Panikon]
itemdb->name_constants();
+ clan->set_constants();
if (!minimal) {
npc->timer_event_ers = ers_new(sizeof(struct timer_event_data),"clif.c::timer_event_ers",ERS_OPT_NONE);
diff --git a/src/map/npc.h b/src/map/npc.h
index 8bb38f252..64a2b3a51 100644
--- a/src/map/npc.h
+++ b/src/map/npc.h
@@ -83,6 +83,8 @@ struct npc_data {
uint8 dir;
uint8 area_size;
+ int clan_id;
+
unsigned size : 2;
struct status_data status;
diff --git a/src/map/packets.h b/src/map/packets.h
index 451acce47..b90413793 100644
--- a/src/map/packets.h
+++ b/src/map/packets.h
@@ -2179,6 +2179,12 @@ packet(0x96e,-1,clif->ackmergeitems);
packet(0x0960,5,clif->pChangeDir,2,4); // CZ_CHANGE_DIRECTION
#endif
+//2012-07-02
+#if PACKETVER >= 20120702
+// new packets
+ packet(0x098a, -1); // ZC_CLANINFO
+#endif
+
//2012-07-10
#if PACKETVER >= 20120710
packet(0x0886,2,clif->pReqCloseBuyingStore,0); // CZ_REQ_CLOSE_BUYING_STORE
@@ -2218,6 +2224,15 @@ packet(0x96e,-1,clif->ackmergeitems);
packet(0x0436,4,clif->pDull); // CZ_GANGSI_RANK
#endif
+//2012-07-16aRagExe
+#if PACKETVER >= 20120716
+// new packets
+ packet(0x0988, 6); // ZC_NOTIFY_CLAN_CONNECTINFO
+ packet(0x0989, 2); // ZC_ACK_CLAN_LEAVE
+ packet(0x098d, -1, clif->pClanMessage, 2, 4); // CZ_CLAN_CHAT
+ packet(0x098e, -1); // ZC_NOTIFY_CLAN_CHAT
+#endif
+
// 2012-09-25aRagexe
#if PACKETVER >= 20120925
// new packets (not all)
diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h
index 1105bec96..2a65eb6cf 100644
--- a/src/map/packets_struct.h
+++ b/src/map/packets_struct.h
@@ -349,6 +349,14 @@ enum packet_headers {
partymemberinfo = 0x01e9,
partyinfo = 0x00fb,
#endif
+#if PACKETVER >= 20120702
+ clanBasicInfo = 0x098A, ///< ZC_CLANINFO
+#endif
+#if PACKETVER >= 20120716
+ clanOnlineCount = 0x0988, ///< ZC_NOTIFY_CLAN_CONNECTINFO
+ clanLeave = 0x0989, ///< ZC_ACK_CLAN_LEAVE
+ clanMessage = 0x098E, ///< ZC_NOTIFY_CLAN_CHAT
+#endif
};
#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
@@ -1562,6 +1570,34 @@ struct PACKET_ZC_GROUP_LIST {
struct PACKET_ZC_GROUP_LIST_SUB members[];
} __attribute__((packed));
+struct PACKET_ZC_CLANINFO {
+ int16 PacketType;
+ int16 PacketLength;
+ uint32 ClanID;
+ char ClanName[NAME_LENGTH];
+ char MasterName[NAME_LENGTH];
+ char Map[MAP_NAME_LENGTH_EXT];
+ uint8 AllyCount;
+ uint8 AntagonistCount;
+} __attribute__((packed));
+
+struct PACKET_ZC_NOTIFY_CLAN_CONNECTINFO {
+ int16 PacketType;
+ int16 NumConnect;
+ int16 NumTotal;
+} __attribute__((packed));
+
+struct PACKET_ZC_ACK_CLAN_LEAVE {
+ int16 PacketType;
+} __attribute__((packed));
+
+struct PACKET_ZC_NOTIFY_CLAN_CHAT {
+ int16 PacketType;
+ int16 PacketLength;
+ char MemberName[NAME_LENGTH];
+ char Message[];
+} __attribute__((packed));
+
#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#pragma pack(pop)
#endif // not NetBSD < 6 / Solaris
diff --git a/src/map/pc.c b/src/map/pc.c
index 449cb25d3..c18fdee9e 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -29,6 +29,7 @@
#include "map/channel.h"
#include "map/chat.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/date.h" // is_day_of_*()
#include "map/duel.h"
@@ -1523,6 +1524,12 @@ int pc_reg_received(struct map_session_data *sd)
status_calc_pc(sd,SCO_FIRST|SCO_FORCE);
chrif->scdata_request(sd->status.account_id, sd->status.char_id);
+
+ if (sd->status.clan_id)
+ clan->member_online(sd, true);
+
+ //Auth is fully okay, update last_login
+ sd->status.last_login = time(NULL);
// Storage Request
intif->request_account_storage(sd);
diff --git a/src/map/pc.h b/src/map/pc.h
index df0df979d..a01152df5 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -443,6 +443,7 @@ END_ZEROED_BLOCK;
int party_invite, party_invite_account; // for handling party invitation (holds party id and account id)
int adopt_invite; // Adoption
struct guild *guild;/* [Ind/Hercules] speed everything up */
+ struct clan *clan;
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]
diff --git a/src/map/script.c b/src/map/script.c
index fc304d4ff..36f37abb9 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -29,6 +29,7 @@
#include "map/channel.h"
#include "map/chat.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/date.h"
#include "map/elemental.h"
@@ -5593,6 +5594,8 @@ int script_reload(void)
itemdb->name_constants();
+ clan->set_constants();
+
sysinfo->vcsrevision_reload();
return 0;
@@ -8627,32 +8630,45 @@ BUILDIN(readparam) {
* 2 : guild_id
* 3 : account_id
* 4 : bg_id
+ * 5 : clan_id
*------------------------------------------*/
BUILDIN(getcharid) {
- int num;
+ int num = script_getnum(st, 2);
struct map_session_data *sd;
-
- num = script_getnum(st,2);
- if( script_hasdata(st,3) )
- sd=map->nick2sd(script_getstr(st,3));
+
+ if (script_hasdata(st, 3))
+ sd = map->nick2sd(script_getstr(st, 3));
else
- sd=script->rid2sd(st);
+ sd = script->rid2sd(st);
- if(sd==NULL) {
- script_pushint(st,0); //return 0, according docs
+ if (sd == NULL) {
+ script_pushint(st, 0); //return 0, according docs
return true;
}
- switch( num ) {
- case 0: script_pushint(st,sd->status.char_id); break;
- case 1: script_pushint(st,sd->status.party_id); break;
- case 2: script_pushint(st,sd->status.guild_id); break;
- case 3: script_pushint(st,sd->status.account_id); break;
- case 4: script_pushint(st,sd->bg_id); break;
- default:
- ShowError("buildin_getcharid: invalid parameter (%d).\n", num);
- script_pushint(st,0);
- break;
+ switch (num) {
+ case 0:
+ script_pushint(st, sd->status.char_id);
+ break;
+ case 1:
+ script_pushint(st, sd->status.party_id);
+ break;
+ case 2:
+ script_pushint(st, sd->status.guild_id);
+ break;
+ case 3:
+ script_pushint(st, sd->status.account_id);
+ break;
+ case 4:
+ script_pushint(st, sd->bg_id);
+ break;
+ case 5:
+ script_pushint(st, sd->status.clan_id);
+ break;
+ default:
+ ShowError("buildin_getcharid: invalid parameter (%d).\n", num);
+ script_pushint(st, 0);
+ break;
}
return true;
@@ -8900,10 +8916,12 @@ BUILDIN(getguildmember)
* 1 : party_name or ""
* 2 : guild_name or ""
* 3 : map_name
+ * 4 : clan_name or ""
* - : ""
*------------------------------------------*/
BUILDIN(strcharinfo)
{
+ struct clan *c;
struct guild* g;
struct party_data* p;
struct map_session_data *sd;
@@ -8943,6 +8961,13 @@ BUILDIN(strcharinfo)
case 3:
script_pushconststr(st, map->list[sd->bl.m].name);
break;
+ case 4:
+ if ((c = sd->clan) != NULL) {
+ script_pushstrcopy(st, c->name);
+ } else {
+ script_pushconststr(st, "");
+ }
+ break;
default:
ShowWarning("script:strcharinfo: unknown parameter.\n");
script_pushconststr(st, "");
@@ -23861,6 +23886,85 @@ BUILDIN(rodex_sendmail2)
}
/**
+ * Clan System: Add a player to a clan
+ */
+BUILDIN(clan_join)
+{
+ struct map_session_data *sd = NULL;
+ int clan_id = script_getnum(st, 2);
+
+ if (script_hasdata(st, 3))
+ sd = map->id2sd(script_getnum(st, 3));
+ else
+ sd = map->id2sd(st->rid);
+
+ if (sd == NULL) {
+ script_pushint(st, false);
+ return false;
+ }
+
+ if (clan->join(sd, clan_id))
+ script_pushint(st, true);
+ else
+ script_pushint(st, false);
+
+ return true;
+}
+
+/**
+ * Clan System: Remove a player from clan
+ */
+BUILDIN(clan_leave)
+{
+ struct map_session_data *sd = NULL;
+
+ if (script_hasdata(st, 2))
+ sd = map->id2sd(script_getnum(st, 2));
+ else
+ sd = map->id2sd(st->rid);
+
+ if (sd == NULL) {
+ script_pushint(st, false);
+ return false;
+ }
+
+ if (clan->leave(sd, false))
+ script_pushint(st, true);
+ else
+ script_pushint(st, false);
+
+ return true;
+}
+
+/**
+ * Clan System: Show clan emblem next to npc name
+ */
+BUILDIN(clan_master)
+{
+ struct npc_data *nd = map->id2nd(st->oid);
+ int clan_id = script_getnum(st, 2);
+
+ if (nd == NULL) {
+ script_pushint(st, false);
+ return false;
+ } else if (clan_id <= 0) {
+ script_pushint(st, false);
+ ShowError("buildin_clan_master: Received Invalid Clan ID %d\n", clan_id);
+ return false;
+ } else if (clan->search(clan_id) == NULL) {
+ script_pushint(st, false);
+ ShowError("buildin_clan_master: Received Id of a nonexistent Clan. Id: %d\n", clan_id);
+ return false;
+ }
+
+ nd->clan_id = clan_id;
+ clif->sc_load(&nd->bl, nd->bl.id, AREA, status->dbs->IconChangeTable[SC_CLAN_INFO], 0, clan_id, 0);
+
+ script_pushint(st, true);
+ return true;
+}
+
+/**
* Adds a built-in script function.
*
* @param buildin Script function data
@@ -24556,6 +24660,11 @@ void script_parse_builtin(void) {
/* Navigation */
BUILDIN_DEF(navigateto, "s??????"),
+ /* Clan System */
+ BUILDIN_DEF(clan_join,"i?"),
+ BUILDIN_DEF(clan_leave,"?"),
+ BUILDIN_DEF(clan_master,"i"),
+
BUILDIN_DEF(channelmes, "ss"),
BUILDIN_DEF(addchannelhandler, "ss"),
BUILDIN_DEF(removechannelhandler, "ss"),
diff --git a/src/map/skill.c b/src/map/skill.c
index 4c9e83579..3d541bf76 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -26,6 +26,7 @@
#include "map/battle.h"
#include "map/battleground.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/date.h"
#include "map/elemental.h"
@@ -17180,6 +17181,7 @@ struct skill_unit_group* skill_initunitgroup (struct block_list* src, int count,
group->party_id = status->get_party_id(src);
group->guild_id = status->get_guild_id(src);
group->bg_id = bg->team_get_id(src);
+ group->clan_id = clan->get_id(src);
group->group_id = skill->get_new_group_id();
CREATE(group->unit.data, struct skill_unit, count);
group->unit.count = count;
diff --git a/src/map/skill.h b/src/map/skill.h
index c494c0e83..bd4d82df2 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -1774,6 +1774,7 @@ struct skill_unit_group {
int party_id;
int guild_id;
int bg_id;
+ int clan_id;
int map;
int target_flag; //Holds BCT_* flag for battle_check_target
int bl_flag; //Holds BL_* flag for map_foreachin* functions
diff --git a/src/map/status.c b/src/map/status.c
index 9e578bc12..3bb511970 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -25,6 +25,7 @@
#include "map/battle.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/elemental.h"
#include "map/guild.h"
@@ -1019,6 +1020,9 @@ void initChangeTables(void)
// Summoner
status->dbs->IconChangeTable[SC_SPRITEMABLE] = SI_SPRITEMABLE;
+ // Clan System
+ status->dbs->IconChangeTable[SC_CLAN_INFO] = SI_CLAN_INFO;
+
// RoDEX
status->dbs->IconChangeTable[SC_DAILYSENDMAILCNT] = SI_DAILYSENDMAILCNT;
@@ -1181,6 +1185,9 @@ void initChangeTables(void)
status->dbs->ChangeFlagTable[SC_MVPCARD_ORCHERO] |= SCB_ALL;
status->dbs->ChangeFlagTable[SC_MVPCARD_ORCLORD] |= SCB_ALL;
+ // Clan System
+ status->dbs->ChangeFlagTable[SC_CLAN_INFO] |= SCB_NONE;
+
// Costumes
status->dbs->ChangeFlagTable[SC_DRESS_UP] |= SCB_NONE;
status->dbs->ChangeFlagTable[SC_MOONSTAR] |= SCB_NONE;
@@ -2685,6 +2692,12 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt)
status->current_equip_option_index = -1;
status->current_equip_item_index = -1;
+ // Clan Buffs
+ if (sd->status.clan_id > 0) {
+ struct clan *c = clan->search(sd->status.clan_id);
+ clan->buff_start(sd, c);
+ }
+
status->calc_pc_additional(sd, opt);
if( sd->pd ) { // Pet Bonus
@@ -9726,6 +9739,11 @@ void status_change_start_display(struct map_session_data *sd, enum sc_type type,
case SC_ALL_RIDING:
dval1 = 1;
break;
+ case SC_CLAN_INFO:
+ dval1 = val1;
+ dval2 = val2;
+ dval3 = val3;
+ break;
default: /* all others: just copy val1 */
dval1 = val1;
break;
@@ -9745,6 +9763,9 @@ int status_get_val_flag(enum sc_type type)
{
int val_flag = 0;
switch (type) {
+ case SC_CLAN_INFO:
+ val_flag |= 1 | 2;
+ break;
case SC_FIGHTINGSPIRIT:
val_flag |= 1 | 2;
break;
diff --git a/src/map/status.h b/src/map/status.h
index e9c2218e8..66e773720 100644
--- a/src/map/status.h
+++ b/src/map/status.h
@@ -845,6 +845,9 @@ typedef enum sc_type {
// Rodex
SC_DAILYSENDMAILCNT,
+
+ // Clan System
+ SC_CLAN_INFO,
#ifndef SC_MAX
SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
#endif
diff --git a/src/map/unit.c b/src/map/unit.c
index 0b5b21caf..6e98fc093 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -27,6 +27,7 @@
#include "map/battleground.h"
#include "map/chat.h"
#include "map/chrif.h"
+#include "map/clan.h"
#include "map/clif.h"
#include "map/duel.h"
#include "map/elemental.h"
@@ -2725,6 +2726,7 @@ int unit_free(struct block_list *bl, clr_type clrtype)
map->foreachpc(clif->friendslist_toggle_sub, sd->status.account_id, sd->status.char_id, 0);
party->send_logout(sd);
guild->send_memberinfoshort(sd,0);
+ clan->member_offline(sd);
pc->cleareventtimer(sd);
pc->inventory_rental_clear(sd);
pc->delspiritball(sd,sd->spiritball,1);