summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
authorFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2006-12-05 13:23:07 +0000
committerFlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec>2006-12-05 13:23:07 +0000
commit288490094a7fe9167747dc78d416940759a31197 (patch)
tree53dc4f5c2375f4b688b53ca8841630ddec5e1f88 /src/map
parent8ec1c47aed09c90343949d57c92760ba84738a46 (diff)
downloadhercules-288490094a7fe9167747dc78d416940759a31197.tar.gz
hercules-288490094a7fe9167747dc78d416940759a31197.tar.bz2
hercules-288490094a7fe9167747dc78d416940759a31197.tar.xz
hercules-288490094a7fe9167747dc78d416940759a31197.zip
- Massive EOL normalization & 'svn:eol-style native' flag setting for all txt/conf/h/c files.
git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@9410 54d463be-8e91-2dee-dedb-b68131a5f0ec
Diffstat (limited to 'src/map')
-rw-r--r--src/map/atcommand.h670
-rw-r--r--src/map/battle.h910
-rw-r--r--src/map/charcommand.c3692
-rw-r--r--src/map/charcommand.h148
-rw-r--r--src/map/charsave.c1046
-rw-r--r--src/map/charsave.h42
-rw-r--r--src/map/chat.c780
-rw-r--r--src/map/chat.h44
-rw-r--r--src/map/chrif.c3272
-rw-r--r--src/map/chrif.h116
-rw-r--r--src/map/clif.h736
-rw-r--r--src/map/date.c144
-rw-r--r--src/map/date.h34
-rw-r--r--src/map/guild.c4034
-rw-r--r--src/map/guild.h190
-rw-r--r--src/map/intif.h144
-rw-r--r--src/map/irc.c1086
-rw-r--r--src/map/irc.h110
-rw-r--r--src/map/itemdb.c2554
-rw-r--r--src/map/itemdb.h296
-rw-r--r--src/map/log.c1064
-rw-r--r--src/map/log.h86
-rw-r--r--src/map/mail.c712
-rw-r--r--src/map/mail.h24
-rw-r--r--src/map/map.h2982
-rw-r--r--src/map/mercenary.c1914
-rw-r--r--src/map/mercenary.h158
-rw-r--r--src/map/mob.h414
-rw-r--r--src/map/npc.c6168
-rw-r--r--src/map/npc.h180
-rw-r--r--src/map/npc_chat.c1034
-rw-r--r--src/map/party.c1804
-rw-r--r--src/map/party.h98
-rw-r--r--src/map/pc.h636
-rw-r--r--src/map/pcre.h516
-rw-r--r--src/map/pet.c2800
-rw-r--r--src/map/pet.h136
-rw-r--r--src/map/script.h182
-rw-r--r--src/map/skill.h1916
-rw-r--r--src/map/status.h1276
-rw-r--r--src/map/storage.c1522
-rw-r--r--src/map/storage.h90
-rw-r--r--src/map/trade.c1106
-rw-r--r--src/map/trade.h30
-rw-r--r--src/map/vending.c534
-rw-r--r--src/map/vending.h28
46 files changed, 23729 insertions, 23729 deletions
diff --git a/src/map/atcommand.h b/src/map/atcommand.h
index aebe49965..f3be9b0a0 100644
--- a/src/map/atcommand.h
+++ b/src/map/atcommand.h
@@ -1,335 +1,335 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _ATCOMMAND_H_
-#define _ATCOMMAND_H_
-
-enum AtCommandType {
- AtCommand_None = -1,
- AtCommand_Broadcast = 0,
- AtCommand_LocalBroadcast,
- AtCommand_MapMove,
- AtCommand_ResetState,
- AtCommand_RuraP,
- AtCommand_Rura,
- AtCommand_Warp,
- AtCommand_Where,
- AtCommand_JumpTo,
- AtCommand_Jump,
- AtCommand_Who,
- AtCommand_Who2,
- AtCommand_Who3,
- AtCommand_WhoMap,
- AtCommand_WhoMap2,
- AtCommand_WhoMap3,
- AtCommand_WhoGM,
- AtCommand_Save,
- AtCommand_Load,
- AtCommand_Speed,
- AtCommand_CharSpeed,
- AtCommand_Storage,
- AtCommand_GuildStorage,
- AtCommand_Option,
- AtCommand_Hide,
- AtCommand_JobChange,
- AtCommand_JobChange2,
- AtCommand_JobChange3,
- AtCommand_Die,
- AtCommand_Kill,
- AtCommand_Alive,
- AtCommand_Kami,
- AtCommand_KamiB,
- AtCommand_KamiC, //LuzZza
- AtCommand_Heal,
- AtCommand_Item,
- AtCommand_Item2,
- AtCommand_ItemReset,
- AtCommand_BaseLevelUp,
- AtCommand_JobLevelUp,
- AtCommand_H,
- AtCommand_Help,
- AtCommand_H2,
- AtCommand_Help2,
- AtCommand_GM,
- AtCommand_PvPOff,
- AtCommand_PvPOn,
- AtCommand_GvGOff,
- AtCommand_GvGOn,
- AtCommand_Model,
- AtCommand_Go,
- AtCommand_Spawn,
- //AtCommand_Monster, // removed for Skots [Reddozen]
- AtCommand_MonsterSmall,
- AtCommand_MonsterBig,
- AtCommand_KillMonster,
- AtCommand_KillMonster2,
- AtCommand_Refine,
- AtCommand_Produce,
- AtCommand_Memo,
- AtCommand_GAT,
- AtCommand_Packet,
- AtCommand_WaterLevel,
- AtCommand_StatusPoint,
- AtCommand_SkillPoint,
- AtCommand_Zeny,
- AtCommand_Param,
- AtCommand_Strength,
- AtCommand_Agility,
- AtCommand_Vitality,
- AtCommand_Intelligence,
- AtCommand_Dexterity,
- AtCommand_Luck,
- AtCommand_GuildLevelUp,
- AtCommand_MakeEgg,
- AtCommand_PetFriendly,
- AtCommand_PetHungry,
- AtCommand_PetRename,
- AtCommand_Recall,
- AtCommand_Revive,
- AtCommand_CharacterStatsAll,
- AtCommand_CharacterLoad,
- AtCommand_Night,
- AtCommand_Day,
- AtCommand_Doom,
- AtCommand_DoomMap,
- AtCommand_Raise,
- AtCommand_RaiseMap,
- AtCommand_Kick,
- AtCommand_KickAll,
- AtCommand_AllSkill,
- AtCommand_QuestSkill,
- AtCommand_LostSkill,
- AtCommand_SpiritBall,
- AtCommand_Party,
- AtCommand_Guild,
- AtCommand_AgitStart,
- AtCommand_AgitEnd,
- AtCommand_MapExit,
- AtCommand_IDSearch,
- AtCommand_RecallAll,
- AtCommand_ReloadItemDB,
- AtCommand_ReloadMobDB,
- AtCommand_ReloadSkillDB,
- AtCommand_ReloadScript,
- AtCommand_ReloadGMDB,
- AtCommand_ReloadAtcommand,
- AtCommand_ReloadBattleConf,
- AtCommand_ReloadStatusDB,
- AtCommand_ReloadPcDB,
- AtCommand_ReloadMOTD, // [Valaris]
- AtCommand_MapInfo,
- AtCommand_Dye,
- AtCommand_Hstyle,
- AtCommand_Hcolor,
- AtCommand_StatAll,
- AtCommand_CharBlock, // by Yor
- AtCommand_CharBan, // by Yor
- AtCommand_CharUnBlock, // by Yor
- AtCommand_CharUnBan, // by Yor
- AtCommand_MountPeco, // by Valaris
- AtCommand_CharMountPeco, // by Yor
- AtCommand_GuildSpy, // [Syrus22]
- AtCommand_PartySpy, // [Syrus22]
- AtCommand_RepairAll, // [Valaris]
- AtCommand_GuildRecall, // by Yor
- AtCommand_PartyRecall, // by Yor
- AtCommand_Nuke, // [Valaris]
- AtCommand_Shownpc,
- AtCommand_Hidenpc,
- AtCommand_Loadnpc,
- AtCommand_Unloadnpc,
- AtCommand_ServerTime, // by Yor
- AtCommand_CharDelItem, // by Yor
- AtCommand_Jail, // by Yor
- AtCommand_UnJail, // by Yor
- AtCommand_JailFor, // Meruru
- AtCommand_JailTime, // Coltaro
- AtCommand_CharJailTime, // Coltaro
- AtCommand_Disguise, // [Valaris]
- AtCommand_UnDisguise, // by Yor
- AtCommand_CharDisguise, // Kalaspuff
- AtCommand_CharUnDisguise, // Kalaspuff
- AtCommand_EMail, // by Yor
- AtCommand_Hatch,
- AtCommand_Effect, // by Apple
- AtCommand_Char_Cart_List, // by Yor
- AtCommand_AddWarp, // by MouseJstr
- AtCommand_Follow, // by MouseJstr
- AtCommand_SkillOn, // by MouseJstr
- AtCommand_SkillOff, // by MouseJstr
- AtCommand_Killer, // by MouseJstr
- AtCommand_NpcMove, // by MouseJstr
- AtCommand_Killable, // by MouseJstr
- AtCommand_CharKillable, // by MouseJstr
- AtCommand_Dropall, // by MouseJstr
- AtCommand_Chardropall, // by MouseJstr
- AtCommand_Storeall, // by MouseJstr
- AtCommand_Charstoreall, // by MouseJstr
- AtCommand_Skillid, // by MouseJstr
- AtCommand_Useskill, // by MouseJstr
- AtCommand_Summon,
- AtCommand_Rain,
- AtCommand_Snow,
- AtCommand_Sakura,
- AtCommand_Clouds,
- AtCommand_Clouds2, // [Valaris]
- AtCommand_Fog,
- AtCommand_Fireworks,
- AtCommand_Leaves,
- AtCommand_AdjGmLvl, // MouseJstr
- AtCommand_AdjCmdLvl, // MouseJstr
- AtCommand_Trade, // MouseJstr
- AtCommand_Send,
- AtCommand_SetBattleFlag,
- AtCommand_UnMute,
- AtCommand_Clearweather, // by Dexity
- AtCommand_UpTime, // by MC Cameri
- AtCommand_ChangeSex, // by MC Cameri
- AtCommand_Mute, // [celest]
- AtCommand_WhoZeny, // [Valaris] <-- LOL...(MC Cameri) worth it.
- AtCommand_HappyHappyJoyJoy, // [Valaris]
- AtCommand_Refresh, // by MC Cameri
- AtCommand_PetId, // by MC Cameri
- AtCommand_Identify, // by MC Cameri
- AtCommand_Gmotd, // Added by MC Cameri, created by davidsiaw
- AtCommand_MiscEffect, // by MC Cameri
- AtCommand_MobSearch,
- AtCommand_CleanMap,
- AtCommand_NpcTalk,
- AtCommand_PetTalk,
- AtCommand_Users,
- // SQL-only commands start
-#ifndef TXT_ONLY
- AtCommand_CheckMail, // [Valaris]
- AtCommand_ListMail, // [Valaris]
- AtCommand_ListNewMail, // [Valaris]
- AtCommand_ReadMail, // [Valaris]
- AtCommand_SendMail, // [Valaris]
- AtCommand_DeleteMail, // [Valaris]
- AtCommand_SendPriorityMail, // [Valaris]
- AtCommand_RefreshOnline, // [Valaris]
- // SQL-only commands end
-#endif
- AtCommand_SkillTree, // by MouseJstr
- AtCommand_Marry, // by MouseJstr
- AtCommand_Divorce, // by MouseJstr
- AtCommand_Grind, // by MouseJstr
- AtCommand_Grind2, // by MouseJstr
-
- AtCommand_Me, //added by massdriller, code by lordalfa
-
- AtCommand_DMStart, // by MouseJstr
- AtCommand_DMTick, // by MouseJstr
-
- AtCommand_JumpToId, // by Dino9021
- AtCommand_JumpToId2, // by Dino9021
- AtCommand_RecallId, // by Dino9021
- AtCommand_RecallId2, // by Dino9021
- AtCommand_KickId, // by Dino9021
- AtCommand_KickId2, // by Dino9021
- AtCommand_ReviveId, // by Dino9021
- AtCommand_ReviveId2, // by Dino9021
- AtCommand_KillId, // by Dino9021
- AtCommand_KillId2, // by Dino9021
- AtCommand_CharKillableId, // by Dino9021
- AtCommand_CharKillableId2, // by Dino9021
- AtCommand_Sound,
- AtCommand_UndisguiseAll,
- AtCommand_DisguiseAll,
- AtCommand_ChangeLook,
- AtCommand_AutoLoot, //by Upa-Kun
- AtCommand_MobInfo, //by Lupus
- AtCommand_Exp, // by Skotlex
- AtCommand_Adopt, // by Veider
- AtCommand_Version, // by Ancyker
-
- AtCommand_MuteArea, // MouseJstr
- AtCommand_Shuffle, // MouseJstr
- AtCommand_Rates, // MouseJstr
-
- AtCommand_ItemInfo, // Lupus
- AtCommand_WhoDrops, // Skotlex
- AtCommand_MapFlag, // Lupus
- AtCommand_MonsterIgnore, // [Valaris]
- AtCommand_FakeName, // [Valaris]
- AtCommand_Size, // [Valaris]
- AtCommand_ShowDelay,
- AtCommand_ShowExp,
- AtCommand_ShowZeny,
- AtCommand_AutoTrade,//durf
- AtCommand_ChangeGM,//durf
- AtCommand_ChangeLeader,
- AtCommand_PartyOption,
-
- AtCommand_Invite, // By LuzZza
- AtCommand_Duel, // By LuzZza
- AtCommand_Leave, // By LuzZza
- AtCommand_Accept, // By LuzZza
- AtCommand_Reject, // By LuzZza
-
- AtCommand_Away, // LuzZza
- AtCommand_Main, // LuzZza
-
- AtCommand_Clone, // [Valaris]
- AtCommand_ToNPC, // LuzZza
- AtCommand_Commands, // [Skotlex]
- AtCommand_NoAsk, // [LuzZza]
- AtCommand_Request, // [Skotlex], supposedly taken from Freya (heard the command was there, but I haven't seen the code yet)
- AtCommand_HomLevel, //[orn]
- AtCommand_HomEvolution, //[orn]
- AtCommand_MakeHomun, //[orn]
- AtCommand_HomFriendly, //[orn]
- AtCommand_HomHungry, //[orn]
- AtCommand_HomTalk, //[orn]
- AtCommand_HomInfo, //[Toms]
- AtCommand_ShowMobs, //KarLaeda
- // end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex]
- AtCommand_Unknown,
- AtCommand_MAX
-};
-
-typedef enum AtCommandType AtCommandType;
-
-typedef struct AtCommandInfo {
- AtCommandType type;
- const char* command;
- int level;
- int (*proc)(const int, struct map_session_data*,
- const char* command, const char* message);
-} AtCommandInfo;
-
-AtCommandType
-is_atcommand(const int fd, struct map_session_data* sd, const char* message);
-AtCommandType
-atcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl);
-
-AtCommandType atcommand(
- struct map_session_data *sd,
- const int level, const char* message, AtCommandInfo* info);
-int get_atcommand_level(const AtCommandType type);
-
-char * msg_txt(int msg_number); // [Yor]
-char * player_title_txt(int level); // [Lupus]
-
-void do_init_atcommand(void);
-void do_final_atcommand(void);
-
-int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Valaris]
-int atcommand_rura(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Yor]
-int atcommand_jumpto(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
-int atcommand_recall(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
-int atcommand_monster(const int fd, struct map_session_data* sd, const char* command, const char* message);
-
-int atcommand_config_read(const char *cfgName);
-int msg_config_read(const char *cfgName);
-void do_final_msg(void);
-
-char *estr_lower(char *str);
-
-int e_mail_check(char *email);
-
-#define MAX_MSG 1000
-extern char *msg_table[MAX_MSG];
-
-#endif
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ATCOMMAND_H_
+#define _ATCOMMAND_H_
+
+enum AtCommandType {
+ AtCommand_None = -1,
+ AtCommand_Broadcast = 0,
+ AtCommand_LocalBroadcast,
+ AtCommand_MapMove,
+ AtCommand_ResetState,
+ AtCommand_RuraP,
+ AtCommand_Rura,
+ AtCommand_Warp,
+ AtCommand_Where,
+ AtCommand_JumpTo,
+ AtCommand_Jump,
+ AtCommand_Who,
+ AtCommand_Who2,
+ AtCommand_Who3,
+ AtCommand_WhoMap,
+ AtCommand_WhoMap2,
+ AtCommand_WhoMap3,
+ AtCommand_WhoGM,
+ AtCommand_Save,
+ AtCommand_Load,
+ AtCommand_Speed,
+ AtCommand_CharSpeed,
+ AtCommand_Storage,
+ AtCommand_GuildStorage,
+ AtCommand_Option,
+ AtCommand_Hide,
+ AtCommand_JobChange,
+ AtCommand_JobChange2,
+ AtCommand_JobChange3,
+ AtCommand_Die,
+ AtCommand_Kill,
+ AtCommand_Alive,
+ AtCommand_Kami,
+ AtCommand_KamiB,
+ AtCommand_KamiC, //LuzZza
+ AtCommand_Heal,
+ AtCommand_Item,
+ AtCommand_Item2,
+ AtCommand_ItemReset,
+ AtCommand_BaseLevelUp,
+ AtCommand_JobLevelUp,
+ AtCommand_H,
+ AtCommand_Help,
+ AtCommand_H2,
+ AtCommand_Help2,
+ AtCommand_GM,
+ AtCommand_PvPOff,
+ AtCommand_PvPOn,
+ AtCommand_GvGOff,
+ AtCommand_GvGOn,
+ AtCommand_Model,
+ AtCommand_Go,
+ AtCommand_Spawn,
+ //AtCommand_Monster, // removed for Skots [Reddozen]
+ AtCommand_MonsterSmall,
+ AtCommand_MonsterBig,
+ AtCommand_KillMonster,
+ AtCommand_KillMonster2,
+ AtCommand_Refine,
+ AtCommand_Produce,
+ AtCommand_Memo,
+ AtCommand_GAT,
+ AtCommand_Packet,
+ AtCommand_WaterLevel,
+ AtCommand_StatusPoint,
+ AtCommand_SkillPoint,
+ AtCommand_Zeny,
+ AtCommand_Param,
+ AtCommand_Strength,
+ AtCommand_Agility,
+ AtCommand_Vitality,
+ AtCommand_Intelligence,
+ AtCommand_Dexterity,
+ AtCommand_Luck,
+ AtCommand_GuildLevelUp,
+ AtCommand_MakeEgg,
+ AtCommand_PetFriendly,
+ AtCommand_PetHungry,
+ AtCommand_PetRename,
+ AtCommand_Recall,
+ AtCommand_Revive,
+ AtCommand_CharacterStatsAll,
+ AtCommand_CharacterLoad,
+ AtCommand_Night,
+ AtCommand_Day,
+ AtCommand_Doom,
+ AtCommand_DoomMap,
+ AtCommand_Raise,
+ AtCommand_RaiseMap,
+ AtCommand_Kick,
+ AtCommand_KickAll,
+ AtCommand_AllSkill,
+ AtCommand_QuestSkill,
+ AtCommand_LostSkill,
+ AtCommand_SpiritBall,
+ AtCommand_Party,
+ AtCommand_Guild,
+ AtCommand_AgitStart,
+ AtCommand_AgitEnd,
+ AtCommand_MapExit,
+ AtCommand_IDSearch,
+ AtCommand_RecallAll,
+ AtCommand_ReloadItemDB,
+ AtCommand_ReloadMobDB,
+ AtCommand_ReloadSkillDB,
+ AtCommand_ReloadScript,
+ AtCommand_ReloadGMDB,
+ AtCommand_ReloadAtcommand,
+ AtCommand_ReloadBattleConf,
+ AtCommand_ReloadStatusDB,
+ AtCommand_ReloadPcDB,
+ AtCommand_ReloadMOTD, // [Valaris]
+ AtCommand_MapInfo,
+ AtCommand_Dye,
+ AtCommand_Hstyle,
+ AtCommand_Hcolor,
+ AtCommand_StatAll,
+ AtCommand_CharBlock, // by Yor
+ AtCommand_CharBan, // by Yor
+ AtCommand_CharUnBlock, // by Yor
+ AtCommand_CharUnBan, // by Yor
+ AtCommand_MountPeco, // by Valaris
+ AtCommand_CharMountPeco, // by Yor
+ AtCommand_GuildSpy, // [Syrus22]
+ AtCommand_PartySpy, // [Syrus22]
+ AtCommand_RepairAll, // [Valaris]
+ AtCommand_GuildRecall, // by Yor
+ AtCommand_PartyRecall, // by Yor
+ AtCommand_Nuke, // [Valaris]
+ AtCommand_Shownpc,
+ AtCommand_Hidenpc,
+ AtCommand_Loadnpc,
+ AtCommand_Unloadnpc,
+ AtCommand_ServerTime, // by Yor
+ AtCommand_CharDelItem, // by Yor
+ AtCommand_Jail, // by Yor
+ AtCommand_UnJail, // by Yor
+ AtCommand_JailFor, // Meruru
+ AtCommand_JailTime, // Coltaro
+ AtCommand_CharJailTime, // Coltaro
+ AtCommand_Disguise, // [Valaris]
+ AtCommand_UnDisguise, // by Yor
+ AtCommand_CharDisguise, // Kalaspuff
+ AtCommand_CharUnDisguise, // Kalaspuff
+ AtCommand_EMail, // by Yor
+ AtCommand_Hatch,
+ AtCommand_Effect, // by Apple
+ AtCommand_Char_Cart_List, // by Yor
+ AtCommand_AddWarp, // by MouseJstr
+ AtCommand_Follow, // by MouseJstr
+ AtCommand_SkillOn, // by MouseJstr
+ AtCommand_SkillOff, // by MouseJstr
+ AtCommand_Killer, // by MouseJstr
+ AtCommand_NpcMove, // by MouseJstr
+ AtCommand_Killable, // by MouseJstr
+ AtCommand_CharKillable, // by MouseJstr
+ AtCommand_Dropall, // by MouseJstr
+ AtCommand_Chardropall, // by MouseJstr
+ AtCommand_Storeall, // by MouseJstr
+ AtCommand_Charstoreall, // by MouseJstr
+ AtCommand_Skillid, // by MouseJstr
+ AtCommand_Useskill, // by MouseJstr
+ AtCommand_Summon,
+ AtCommand_Rain,
+ AtCommand_Snow,
+ AtCommand_Sakura,
+ AtCommand_Clouds,
+ AtCommand_Clouds2, // [Valaris]
+ AtCommand_Fog,
+ AtCommand_Fireworks,
+ AtCommand_Leaves,
+ AtCommand_AdjGmLvl, // MouseJstr
+ AtCommand_AdjCmdLvl, // MouseJstr
+ AtCommand_Trade, // MouseJstr
+ AtCommand_Send,
+ AtCommand_SetBattleFlag,
+ AtCommand_UnMute,
+ AtCommand_Clearweather, // by Dexity
+ AtCommand_UpTime, // by MC Cameri
+ AtCommand_ChangeSex, // by MC Cameri
+ AtCommand_Mute, // [celest]
+ AtCommand_WhoZeny, // [Valaris] <-- LOL...(MC Cameri) worth it.
+ AtCommand_HappyHappyJoyJoy, // [Valaris]
+ AtCommand_Refresh, // by MC Cameri
+ AtCommand_PetId, // by MC Cameri
+ AtCommand_Identify, // by MC Cameri
+ AtCommand_Gmotd, // Added by MC Cameri, created by davidsiaw
+ AtCommand_MiscEffect, // by MC Cameri
+ AtCommand_MobSearch,
+ AtCommand_CleanMap,
+ AtCommand_NpcTalk,
+ AtCommand_PetTalk,
+ AtCommand_Users,
+ // SQL-only commands start
+#ifndef TXT_ONLY
+ AtCommand_CheckMail, // [Valaris]
+ AtCommand_ListMail, // [Valaris]
+ AtCommand_ListNewMail, // [Valaris]
+ AtCommand_ReadMail, // [Valaris]
+ AtCommand_SendMail, // [Valaris]
+ AtCommand_DeleteMail, // [Valaris]
+ AtCommand_SendPriorityMail, // [Valaris]
+ AtCommand_RefreshOnline, // [Valaris]
+ // SQL-only commands end
+#endif
+ AtCommand_SkillTree, // by MouseJstr
+ AtCommand_Marry, // by MouseJstr
+ AtCommand_Divorce, // by MouseJstr
+ AtCommand_Grind, // by MouseJstr
+ AtCommand_Grind2, // by MouseJstr
+
+ AtCommand_Me, //added by massdriller, code by lordalfa
+
+ AtCommand_DMStart, // by MouseJstr
+ AtCommand_DMTick, // by MouseJstr
+
+ AtCommand_JumpToId, // by Dino9021
+ AtCommand_JumpToId2, // by Dino9021
+ AtCommand_RecallId, // by Dino9021
+ AtCommand_RecallId2, // by Dino9021
+ AtCommand_KickId, // by Dino9021
+ AtCommand_KickId2, // by Dino9021
+ AtCommand_ReviveId, // by Dino9021
+ AtCommand_ReviveId2, // by Dino9021
+ AtCommand_KillId, // by Dino9021
+ AtCommand_KillId2, // by Dino9021
+ AtCommand_CharKillableId, // by Dino9021
+ AtCommand_CharKillableId2, // by Dino9021
+ AtCommand_Sound,
+ AtCommand_UndisguiseAll,
+ AtCommand_DisguiseAll,
+ AtCommand_ChangeLook,
+ AtCommand_AutoLoot, //by Upa-Kun
+ AtCommand_MobInfo, //by Lupus
+ AtCommand_Exp, // by Skotlex
+ AtCommand_Adopt, // by Veider
+ AtCommand_Version, // by Ancyker
+
+ AtCommand_MuteArea, // MouseJstr
+ AtCommand_Shuffle, // MouseJstr
+ AtCommand_Rates, // MouseJstr
+
+ AtCommand_ItemInfo, // Lupus
+ AtCommand_WhoDrops, // Skotlex
+ AtCommand_MapFlag, // Lupus
+ AtCommand_MonsterIgnore, // [Valaris]
+ AtCommand_FakeName, // [Valaris]
+ AtCommand_Size, // [Valaris]
+ AtCommand_ShowDelay,
+ AtCommand_ShowExp,
+ AtCommand_ShowZeny,
+ AtCommand_AutoTrade,//durf
+ AtCommand_ChangeGM,//durf
+ AtCommand_ChangeLeader,
+ AtCommand_PartyOption,
+
+ AtCommand_Invite, // By LuzZza
+ AtCommand_Duel, // By LuzZza
+ AtCommand_Leave, // By LuzZza
+ AtCommand_Accept, // By LuzZza
+ AtCommand_Reject, // By LuzZza
+
+ AtCommand_Away, // LuzZza
+ AtCommand_Main, // LuzZza
+
+ AtCommand_Clone, // [Valaris]
+ AtCommand_ToNPC, // LuzZza
+ AtCommand_Commands, // [Skotlex]
+ AtCommand_NoAsk, // [LuzZza]
+ AtCommand_Request, // [Skotlex], supposedly taken from Freya (heard the command was there, but I haven't seen the code yet)
+ AtCommand_HomLevel, //[orn]
+ AtCommand_HomEvolution, //[orn]
+ AtCommand_MakeHomun, //[orn]
+ AtCommand_HomFriendly, //[orn]
+ AtCommand_HomHungry, //[orn]
+ AtCommand_HomTalk, //[orn]
+ AtCommand_HomInfo, //[Toms]
+ AtCommand_ShowMobs, //KarLaeda
+ // end <- Ahem, guys, don't place AtCommands after AtCommand_Unknown! [Skotlex]
+ AtCommand_Unknown,
+ AtCommand_MAX
+};
+
+typedef enum AtCommandType AtCommandType;
+
+typedef struct AtCommandInfo {
+ AtCommandType type;
+ const char* command;
+ int level;
+ int (*proc)(const int, struct map_session_data*,
+ const char* command, const char* message);
+} AtCommandInfo;
+
+AtCommandType
+is_atcommand(const int fd, struct map_session_data* sd, const char* message);
+AtCommandType
+atcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl);
+
+AtCommandType atcommand(
+ struct map_session_data *sd,
+ const int level, const char* message, AtCommandInfo* info);
+int get_atcommand_level(const AtCommandType type);
+
+char * msg_txt(int msg_number); // [Yor]
+char * player_title_txt(int level); // [Lupus]
+
+void do_init_atcommand(void);
+void do_final_atcommand(void);
+
+int atcommand_item(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Valaris]
+int atcommand_rura(const int fd, struct map_session_data* sd,const char* command, const char* message); // [Yor]
+int atcommand_jumpto(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
+int atcommand_recall(const int fd, struct map_session_data* sd, const char* command, const char* message); // [Yor]
+int atcommand_monster(const int fd, struct map_session_data* sd, const char* command, const char* message);
+
+int atcommand_config_read(const char *cfgName);
+int msg_config_read(const char *cfgName);
+void do_final_msg(void);
+
+char *estr_lower(char *str);
+
+int e_mail_check(char *email);
+
+#define MAX_MSG 1000
+extern char *msg_table[MAX_MSG];
+
+#endif
+
diff --git a/src/map/battle.h b/src/map/battle.h
index 709cc3c78..94ced24f7 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -1,455 +1,455 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _BATTLE_H_
-#define _BATTLE_H_
-
-// ダメージ
-struct Damage {
- int damage,damage2;
- int type,div_;
- int amotion,dmotion;
- int blewcount;
- int flag;
- int dmg_lv; //ATK_LUCKY,ATK_FLEE,ATK_DEF
-};
-
-// 属性表(読み込みはpc.c、battle_attr_fixで使用)
-extern int attr_fix_table[4][10][10];
-
-struct map_session_data;
-struct mob_data;
-struct block_list;
-
-// ダメージ計算
-
-struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag);
-
-int battle_calc_return_damage(struct block_list *bl, int skill, int *damage, int flag);
-
-void battle_drain(struct map_session_data *sd, struct block_list *tbl, int rdamage, int ldamage, int race, int boss);
-
-int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_type, int def_lv);
-
-// ダメージ最終計算
-int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag);
-int battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag);
-
-enum { // 最終計算のフラグ
- BF_WEAPON = 0x0001,
- BF_MAGIC = 0x0002,
- BF_MISC = 0x0004,
- BF_SHORT = 0x0010,
- BF_LONG = 0x0040,
- BF_SKILL = 0x0100,
- BF_NORMAL = 0x0200,
- BF_WEAPONMASK=0x000f,
- BF_RANGEMASK= 0x00f0,
- BF_SKILLMASK= 0x0f00,
-};
-
-int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int ddelay);
-
-// 通常攻撃処理まとめ
-int battle_weapon_attack( struct block_list *bl,struct block_list *target,
- unsigned int tick,int flag);
-
-// 各種パラメータを得る
-struct block_list* battle_get_master(struct block_list *src);
-struct block_list* battle_gettargeted(struct block_list *target);
-int battle_gettarget(struct block_list *bl);
-int battle_getcurrentskill(struct block_list *bl);
-
-//New definitions [Skotlex]
-#define BCT_ENEMY 0x020000
-//This should be (~BCT_ENEMY&BCT_ALL)
-#define BCT_NOENEMY 0x1d0000
-#define BCT_PARTY 0x040000
-//This should be (~BCT_PARTY&BCT_ALL)
-#define BCT_NOPARTY 0x1b0000
-#define BCT_GUILD 0x080000
-//This should be (~BCT_GUILD&BCT_ALL)
-#define BCT_NOGUILD 0x170000
-#define BCT_ALL 0x1f0000
-#define BCT_NOONE 0x000000
-#define BCT_SELF 0x010000
-#define BCT_NEUTRAL 0x100000
-
-#define is_boss(bl) (status_get_mode(bl)&MD_BOSS) // Can refine later [Aru]
-
-int battle_check_undead(int race,int element);
-int battle_check_target(struct block_list *src, struct block_list *target,int flag);
-int battle_check_range(struct block_list *src,struct block_list *bl,int range);
-
-void battle_consume_ammo(struct map_session_data* sd, int skill, int lv);
-// 設定
-
-int battle_config_switch(const char *str); // [Valaris]
-
-extern struct Battle_Config {
- unsigned short warp_point_debug;
- unsigned short enable_critical;
- unsigned short mob_critical_rate;
- unsigned short critical_rate;
- unsigned short enable_baseatk;
- unsigned short enable_perfect_flee;
- unsigned short cast_rate,delay_rate,delay_dependon_agi;
- unsigned short sdelay_attack_enable;
- unsigned short left_cardfix_to_right;
- unsigned short skill_add_range;
- unsigned short skill_out_range_consume;
- unsigned short skillrange_by_distance; //[Skotlex]
- unsigned short use_weapon_skill_range; //[Skotlex]
- unsigned short pc_damage_delay_rate;
- unsigned short defnotenemy;
- unsigned short vs_traps_bctall;
- unsigned short traps_setting;
- unsigned short summon_flora; //[Skotlex]
- unsigned short clear_unit_ondeath; //[Skotlex]
- unsigned short clear_unit_onwarp; //[Skotlex]
- unsigned short random_monster_checklv;
- unsigned short attr_recover;
- unsigned short flooritem_lifetime;
- unsigned short item_auto_get;
- int item_first_get_time;
- int item_second_get_time;
- int item_third_get_time;
- int mvp_item_first_get_time;
- int mvp_item_second_get_time;
- int mvp_item_third_get_time;
- int base_exp_rate,job_exp_rate;
- unsigned short drop_rate0item;
- unsigned short death_penalty_type;
- unsigned short death_penalty_base,death_penalty_job;
- unsigned short pvp_exp; // [MouseJstr]
- unsigned short gtb_sc_immunity;
- int zeny_penalty;
- unsigned short restart_hp_rate;
- unsigned short restart_sp_rate;
- int mvp_exp_rate;
- unsigned short mvp_hp_rate;
- unsigned short monster_hp_rate;
- unsigned short monster_max_aspd;
- unsigned short view_range_rate;
- unsigned short chase_range_rate;
- unsigned short atc_gmonly;
- unsigned short atc_spawn_quantity_limit;
- unsigned short atc_slave_clone_limit;
- unsigned short partial_name_scan;
- unsigned short gm_allskill;
- unsigned short gm_allequip;
- unsigned short gm_skilluncond;
- unsigned short gm_join_chat;
- unsigned short gm_kick_chat;
- unsigned short skillfree;
- unsigned short skillup_limit;
- unsigned short wp_rate;
- unsigned short pp_rate;
- unsigned short monster_active_enable;
- unsigned short monster_damage_delay_rate;
- unsigned short monster_loot_type;
- unsigned short mob_skill_rate; //[Skotlex]
- unsigned short mob_skill_delay; //[Skotlex]
- unsigned short mob_count_rate;
- unsigned short no_spawn_on_player; //[Skotlex]
- unsigned short force_random_spawn; //[Skotlex]
- unsigned short mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex]
- unsigned short slaves_inherit_mode;
- unsigned short slaves_inherit_speed;
- unsigned short summons_trigger_autospells;
- unsigned short pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex]
- unsigned short walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex]
- unsigned short multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex]
- unsigned short quest_skill_learn;
- unsigned short quest_skill_reset;
- unsigned short basic_skill_check;
- unsigned short guild_emperium_check;
- unsigned short guild_exp_limit;
- unsigned short guild_max_castles;
- unsigned short emergency_call;
- unsigned short guild_aura;
- unsigned short pc_invincible_time;
- unsigned short pet_catch_rate;
- unsigned short pet_rename;
- unsigned short pet_friendly_rate;
- unsigned short pet_hungry_delay_rate;
- unsigned short pet_hungry_friendly_decrease;
- unsigned short pet_status_support;
- unsigned short pet_attack_support;
- unsigned short pet_damage_support;
- unsigned short pet_support_min_friendly; //[Skotlex]
- unsigned short pet_support_rate;
- unsigned short pet_attack_exp_to_master;
- unsigned short pet_attack_exp_rate;
- unsigned short pet_lv_rate; //[Skotlex]
- unsigned short pet_max_stats; //[Skotlex]
- unsigned short pet_max_atk1; //[Skotlex]
- unsigned short pet_max_atk2; //[Skotlex]
- unsigned short pet_no_gvg; //Disables pets in gvg. [Skotlex]
- unsigned short skill_min_damage;
- unsigned short finger_offensive_type;
- unsigned short heal_exp;
- unsigned short max_heal_lv;
- int max_heal; //Mitternacht
- unsigned short resurrection_exp;
- unsigned short shop_exp;
- unsigned short combo_delay_rate;
- unsigned short item_check;
- unsigned short item_use_interval; //[Skotlex]
- unsigned short wedding_modifydisplay;
- unsigned short wedding_ignorepalette; //[Skotlex]
- unsigned short xmas_ignorepalette; // [Valaris]
- int natural_healhp_interval;
- int natural_healsp_interval;
- int natural_heal_skill_interval;
- unsigned short natural_heal_weight_rate;
- unsigned short item_name_override_grffile;
- unsigned short indoors_override_grffile; // [Celest]
- unsigned short skill_sp_override_grffile; // [Celest]
- unsigned short cardillust_read_grffile;
- unsigned short item_equip_override_grffile;
- unsigned short item_slots_override_grffile;
- unsigned short arrow_decrement;
- unsigned short max_aspd;
- unsigned short max_walk_speed; //Maximum walking speed after buffs [Skotlex]
- int max_hp;
- int max_sp;
- unsigned short max_lv, aura_lv;
- unsigned short max_parameter, max_baby_parameter;
- int max_cart_weight;
- unsigned short skill_log;
- unsigned short battle_log;
- unsigned short save_log;
- unsigned short error_log;
- unsigned short etc_log;
- unsigned short save_clothcolor;
- unsigned short undead_detect_type;
- unsigned short auto_counter_type;
- unsigned short min_hitrate; //[Skotlex]
- unsigned short max_hitrate; //[Skotlex]
- unsigned short agi_penalty_target;
- unsigned short agi_penalty_type;
- unsigned short agi_penalty_count;
- unsigned short agi_penalty_num;
- unsigned short vit_penalty_target;
- unsigned short vit_penalty_type;
- unsigned short vit_penalty_count;
- unsigned short vit_penalty_num;
- unsigned short weapon_defense_type;
- unsigned short magic_defense_type;
- unsigned short skill_reiteration;
- unsigned short skill_nofootset;
- unsigned short pc_cloak_check_type;
- unsigned short monster_cloak_check_type;
- unsigned short estimation_type;
- unsigned short gvg_short_damage_rate;
- unsigned short gvg_long_damage_rate;
- unsigned short gvg_weapon_damage_rate;
- unsigned short gvg_magic_damage_rate;
- unsigned short gvg_misc_damage_rate;
- unsigned short gvg_flee_penalty;
- int gvg_eliminate_time;
- unsigned short pk_short_damage_rate;
- unsigned short pk_long_damage_rate;
- unsigned short pk_weapon_damage_rate;
- unsigned short pk_magic_damage_rate;
- unsigned short pk_misc_damage_rate;
- unsigned short mob_changetarget_byskill;
- unsigned short attack_direction_change;
- unsigned short land_skill_limit;
- unsigned short party_skill_penalty;
- unsigned short monster_class_change_full_recover;
- unsigned short produce_item_name_input;
- unsigned short produce_potion_name_input;
- unsigned short making_arrow_name_input;
- unsigned short holywater_name_input;
- unsigned short cdp_name_input;
- unsigned short display_skill_fail;
- unsigned short chat_warpportal;
- unsigned short mob_warp;
- unsigned short dead_branch_active;
- unsigned int vending_max_value;
- unsigned short show_steal_in_same_party;
- unsigned short party_share_type;
- unsigned short party_hp_mode;
- unsigned short party_show_share_picker;
- unsigned short attack_attr_none;
- int item_rate_mvp, item_rate_common, item_rate_common_boss, item_rate_card, item_rate_card_boss, // added support for MVP drops [Reddozen]
- item_rate_equip, item_rate_equip_boss, item_rate_heal, item_rate_heal_boss, item_rate_use,
- item_rate_use_boss, item_rate_treasure, // Added by RoVeRT, Additional Heal and Usable item rate by Val
- item_rate_adddrop;
-
- unsigned short logarithmic_drops;
- unsigned short item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^
- unsigned short item_drop_card_min,item_drop_card_max;
- unsigned short item_drop_equip_min,item_drop_equip_max;
- unsigned short item_drop_mvp_min,item_drop_mvp_max; // End Addition
- unsigned short item_drop_heal_min,item_drop_heal_max; // Added by Valatris
- unsigned short item_drop_use_min,item_drop_use_max; //End
- unsigned short item_drop_treasure_min,item_drop_treasure_max; //by [Skotlex]
- unsigned short item_drop_adddrop_min,item_drop_adddrop_max; //[Skotlex]
- unsigned short prevent_logout; // Added by RoVeRT
-
- unsigned short alchemist_summon_reward; // [Valaris]
- unsigned short drops_by_luk;
- unsigned short drops_by_luk2;
- unsigned short equip_natural_break_rate; //Base Natural break rate for attacks.
- unsigned short equip_self_break_rate; //Natural & Penalty skills break rate
- unsigned short equip_skill_break_rate; //Offensive skills break rate
- unsigned short pet_equip_required;
- unsigned short multi_level_up;
- unsigned short max_exp_gain_rate; //Max amount of exp bar % you can get in one go.
- unsigned short pk_mode;
- unsigned short pk_level_range;
-
- unsigned short manner_system; // end additions [Valaris]
- unsigned short show_mob_info;
-
- unsigned short agi_penalty_count_lv;
- unsigned short vit_penalty_count_lv;
-
- unsigned short gx_allhit;
- unsigned short gx_disptype;
- unsigned short devotion_level_difference;
- unsigned short player_skill_partner_check;
- unsigned short hide_GM_session;
- unsigned short invite_request_check;
- unsigned short skill_removetrap_type;
- unsigned short disp_experience;
- unsigned short disp_zeny;
- unsigned short castle_defense_rate;
- unsigned short backstab_bow_penalty;
- unsigned short hp_rate;
- unsigned short sp_rate;
- unsigned short gm_cant_drop_min_lv;
- unsigned short gm_cant_drop_max_lv;
- unsigned short disp_hpmeter;
- unsigned short bone_drop;
- unsigned short buyer_name;
-
-// eAthena additions
- unsigned short night_at_start; // added by [Yor]
- int day_duration; // added by [Yor]
- int night_duration; // added by [Yor]
- short ban_hack_trade; // added by [Yor]
- unsigned short hack_info_GM_level; // added by [Yor]
- unsigned short any_warp_GM_min_level; // added by [Yor]
- unsigned short packet_ver_flag; // added by [Yor]
-
- unsigned short min_hair_style; // added by [MouseJstr]
- unsigned short max_hair_style; // added by [MouseJstr]
- unsigned short min_hair_color; // added by [MouseJstr]
- unsigned short max_hair_color; // added by [MouseJstr]
- unsigned short min_cloth_color; // added by [MouseJstr]
- unsigned short max_cloth_color; // added by [MouseJstr]
- unsigned short pet_hair_style; // added by [Skotlex]
-
- unsigned short castrate_dex_scale; // added by [MouseJstr]
- unsigned short area_size; // added by [MouseJstr]
-
- unsigned short max_def, over_def_bonus; //added by [Skotlex]
-
- unsigned short zeny_from_mobs; // [Valaris]
- unsigned short mobs_level_up; // [Valaris]
- unsigned short mobs_level_up_exp_rate; // [Valaris]
- unsigned short pk_min_level; // [celest]
- unsigned short skill_steal_type; // [celest]
- unsigned short skill_steal_rate; // [celest]
- unsigned short skill_steal_max_tries; //max steal skill tries on a mob. if 0, then w/o limit [Lupus]
- unsigned short motd_type; // [celest]
- unsigned short finding_ore_rate; // orn
- unsigned short exp_calc_type;
- unsigned short exp_bonus_attacker;
- unsigned short exp_bonus_max_attacker;
- unsigned short min_skill_delay_limit;
- unsigned short default_skill_delay;
- unsigned short no_skill_delay;
- unsigned short attack_walk_delay;
- unsigned short require_glory_guild;
- unsigned short idle_no_share;
- unsigned short party_update_interval;
- unsigned short party_even_share_bonus;
- unsigned short delay_battle_damage;
- unsigned short hide_woe_damage;
- unsigned short display_version;
- unsigned short who_display_aid;
-
- unsigned short display_hallucination; // [Skotlex]
- unsigned short use_statpoint_table; // [Skotlex]
-
- unsigned short ignore_items_gender; //[Lupus]
-
- unsigned short copyskill_restrict; // [Aru]
- unsigned short berserk_cancels_buffs; // [Aru]
- unsigned short debuff_on_logout; // Removes a few "official" negative Scs on logout. [Skotlex]
- unsigned short mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex]
- unsigned short dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random]
- unsigned short mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
- int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex]
-
- unsigned short show_hp_sp_drain, show_hp_sp_gain; //[Skotlex]
-
- unsigned short mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex]
- unsigned short mob_clear_delay; // [Valaris]
-
- unsigned short character_size; // if riders have size=2, and baby class riders size=1 [Lupus]
- unsigned short mob_max_skilllvl; // Max possible skill level [Lupus]
- unsigned short rare_drop_announce; // chance <= to show rare drops global announces
-
- unsigned short retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex]
- unsigned short firewall_hits_on_undead; //Number of hits firewall does at a time on undead. [Skotlex]
-
- unsigned short title_lvl1; // Players titles [Lupus]
- unsigned short title_lvl2; // Players titles [Lupus]
- unsigned short title_lvl3; // Players titles [Lupus]
- unsigned short title_lvl4; // Players titles [Lupus]
- unsigned short title_lvl5; // Players titles [Lupus]
- unsigned short title_lvl6; // Players titles [Lupus]
- unsigned short title_lvl7; // Players titles [Lupus]
- unsigned short title_lvl8; // Players titles [Lupus]
-
- unsigned short duel_enable; // [LuzZza]
- unsigned short duel_allow_pvp; // [LuzZza]
- unsigned short duel_allow_gvg; // [LuzZza]
- unsigned short duel_allow_teleport; // [LuzZza]
- unsigned short duel_autoleave_when_die; // [LuzZza]
- unsigned short duel_time_interval; // [LuzZza]
- unsigned short duel_only_on_same_map; // [Toms]
-
- unsigned short skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza]
-
- unsigned short allow_skill_without_day; // [Komurka]
- unsigned short allow_es_magic_pc; // [Skotlex]
- unsigned short skill_wall_check; // [Skotlex]
- unsigned short cell_stack_limit; // [Skotlex]
- unsigned short skill_caster_check; // [Skotlex]
- unsigned short sc_castcancel; // [Skotlex]
- unsigned short pc_sc_def_rate; // [Skotlex]
- unsigned short mob_sc_def_rate;
- unsigned short pc_luk_sc_def;
- unsigned short mob_luk_sc_def;
- unsigned short pc_max_sc_def;
- unsigned short mob_max_sc_def;
-
- unsigned short sg_angel_skill_ratio;
- unsigned short sg_miracle_skill_ratio;
- int sg_miracle_skill_duration_min;
- int sg_miracle_skill_duration_max;
- unsigned short autospell_stacking; //Enables autospell cards to stack. [Skotlex]
- unsigned short override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex]
- unsigned short min_chat_delay; //Minimum time between client messages. [Skotlex]
- unsigned short friend_auto_add; //When accepting friends, both get friended. [Skotlex]
- unsigned int hvan_explosion_intimate ; // fix [albator]
- unsigned short homunculus_show_growth ; //[orn]
- unsigned short homunculus_friendly_rate;
-} battle_config;
-
-void do_init_battle(void);
-void do_final_battle(void);
-extern int battle_config_read(const char *cfgName);
-extern void battle_validate_conf(void);
-extern void battle_set_defaults(void);
-extern int battle_set_value(char *, char *);
-int battle_get_value(char *);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _BATTLE_H_
+#define _BATTLE_H_
+
+// ダメージ
+struct Damage {
+ int damage,damage2;
+ int type,div_;
+ int amotion,dmotion;
+ int blewcount;
+ int flag;
+ int dmg_lv; //ATK_LUCKY,ATK_FLEE,ATK_DEF
+};
+
+// 属性表(読み込みはpc.c、battle_attr_fixで使用)
+extern int attr_fix_table[4][10][10];
+
+struct map_session_data;
+struct mob_data;
+struct block_list;
+
+// ダメージ計算
+
+struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag);
+
+int battle_calc_return_damage(struct block_list *bl, int skill, int *damage, int flag);
+
+void battle_drain(struct map_session_data *sd, struct block_list *tbl, int rdamage, int ldamage, int race, int boss);
+
+int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_type, int def_lv);
+
+// ダメージ最終計算
+int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag);
+int battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag);
+
+enum { // 最終計算のフラグ
+ BF_WEAPON = 0x0001,
+ BF_MAGIC = 0x0002,
+ BF_MISC = 0x0004,
+ BF_SHORT = 0x0010,
+ BF_LONG = 0x0040,
+ BF_SKILL = 0x0100,
+ BF_NORMAL = 0x0200,
+ BF_WEAPONMASK=0x000f,
+ BF_RANGEMASK= 0x00f0,
+ BF_SKILLMASK= 0x0f00,
+};
+
+int battle_delay_damage (unsigned int tick, struct block_list *src, struct block_list *target, int attack_type, int skill_id, int skill_lv, int damage, int dmg_lv, int ddelay);
+
+// 通常攻撃処理まとめ
+int battle_weapon_attack( struct block_list *bl,struct block_list *target,
+ unsigned int tick,int flag);
+
+// 各種パラメータを得る
+struct block_list* battle_get_master(struct block_list *src);
+struct block_list* battle_gettargeted(struct block_list *target);
+int battle_gettarget(struct block_list *bl);
+int battle_getcurrentskill(struct block_list *bl);
+
+//New definitions [Skotlex]
+#define BCT_ENEMY 0x020000
+//This should be (~BCT_ENEMY&BCT_ALL)
+#define BCT_NOENEMY 0x1d0000
+#define BCT_PARTY 0x040000
+//This should be (~BCT_PARTY&BCT_ALL)
+#define BCT_NOPARTY 0x1b0000
+#define BCT_GUILD 0x080000
+//This should be (~BCT_GUILD&BCT_ALL)
+#define BCT_NOGUILD 0x170000
+#define BCT_ALL 0x1f0000
+#define BCT_NOONE 0x000000
+#define BCT_SELF 0x010000
+#define BCT_NEUTRAL 0x100000
+
+#define is_boss(bl) (status_get_mode(bl)&MD_BOSS) // Can refine later [Aru]
+
+int battle_check_undead(int race,int element);
+int battle_check_target(struct block_list *src, struct block_list *target,int flag);
+int battle_check_range(struct block_list *src,struct block_list *bl,int range);
+
+void battle_consume_ammo(struct map_session_data* sd, int skill, int lv);
+// 設定
+
+int battle_config_switch(const char *str); // [Valaris]
+
+extern struct Battle_Config {
+ unsigned short warp_point_debug;
+ unsigned short enable_critical;
+ unsigned short mob_critical_rate;
+ unsigned short critical_rate;
+ unsigned short enable_baseatk;
+ unsigned short enable_perfect_flee;
+ unsigned short cast_rate,delay_rate,delay_dependon_agi;
+ unsigned short sdelay_attack_enable;
+ unsigned short left_cardfix_to_right;
+ unsigned short skill_add_range;
+ unsigned short skill_out_range_consume;
+ unsigned short skillrange_by_distance; //[Skotlex]
+ unsigned short use_weapon_skill_range; //[Skotlex]
+ unsigned short pc_damage_delay_rate;
+ unsigned short defnotenemy;
+ unsigned short vs_traps_bctall;
+ unsigned short traps_setting;
+ unsigned short summon_flora; //[Skotlex]
+ unsigned short clear_unit_ondeath; //[Skotlex]
+ unsigned short clear_unit_onwarp; //[Skotlex]
+ unsigned short random_monster_checklv;
+ unsigned short attr_recover;
+ unsigned short flooritem_lifetime;
+ unsigned short item_auto_get;
+ int item_first_get_time;
+ int item_second_get_time;
+ int item_third_get_time;
+ int mvp_item_first_get_time;
+ int mvp_item_second_get_time;
+ int mvp_item_third_get_time;
+ int base_exp_rate,job_exp_rate;
+ unsigned short drop_rate0item;
+ unsigned short death_penalty_type;
+ unsigned short death_penalty_base,death_penalty_job;
+ unsigned short pvp_exp; // [MouseJstr]
+ unsigned short gtb_sc_immunity;
+ int zeny_penalty;
+ unsigned short restart_hp_rate;
+ unsigned short restart_sp_rate;
+ int mvp_exp_rate;
+ unsigned short mvp_hp_rate;
+ unsigned short monster_hp_rate;
+ unsigned short monster_max_aspd;
+ unsigned short view_range_rate;
+ unsigned short chase_range_rate;
+ unsigned short atc_gmonly;
+ unsigned short atc_spawn_quantity_limit;
+ unsigned short atc_slave_clone_limit;
+ unsigned short partial_name_scan;
+ unsigned short gm_allskill;
+ unsigned short gm_allequip;
+ unsigned short gm_skilluncond;
+ unsigned short gm_join_chat;
+ unsigned short gm_kick_chat;
+ unsigned short skillfree;
+ unsigned short skillup_limit;
+ unsigned short wp_rate;
+ unsigned short pp_rate;
+ unsigned short monster_active_enable;
+ unsigned short monster_damage_delay_rate;
+ unsigned short monster_loot_type;
+ unsigned short mob_skill_rate; //[Skotlex]
+ unsigned short mob_skill_delay; //[Skotlex]
+ unsigned short mob_count_rate;
+ unsigned short no_spawn_on_player; //[Skotlex]
+ unsigned short force_random_spawn; //[Skotlex]
+ unsigned short mob_spawn_delay, plant_spawn_delay, boss_spawn_delay; // [Skotlex]
+ unsigned short slaves_inherit_mode;
+ unsigned short slaves_inherit_speed;
+ unsigned short summons_trigger_autospells;
+ unsigned short pc_walk_delay_rate; //Adjusts can't walk delay after being hit for players. [Skotlex]
+ unsigned short walk_delay_rate; //Adjusts can't walk delay after being hit. [Skotlex]
+ unsigned short multihit_delay; //Adjusts can't walk delay per hit on multi-hitting skills. [Skotlex]
+ unsigned short quest_skill_learn;
+ unsigned short quest_skill_reset;
+ unsigned short basic_skill_check;
+ unsigned short guild_emperium_check;
+ unsigned short guild_exp_limit;
+ unsigned short guild_max_castles;
+ unsigned short emergency_call;
+ unsigned short guild_aura;
+ unsigned short pc_invincible_time;
+ unsigned short pet_catch_rate;
+ unsigned short pet_rename;
+ unsigned short pet_friendly_rate;
+ unsigned short pet_hungry_delay_rate;
+ unsigned short pet_hungry_friendly_decrease;
+ unsigned short pet_status_support;
+ unsigned short pet_attack_support;
+ unsigned short pet_damage_support;
+ unsigned short pet_support_min_friendly; //[Skotlex]
+ unsigned short pet_support_rate;
+ unsigned short pet_attack_exp_to_master;
+ unsigned short pet_attack_exp_rate;
+ unsigned short pet_lv_rate; //[Skotlex]
+ unsigned short pet_max_stats; //[Skotlex]
+ unsigned short pet_max_atk1; //[Skotlex]
+ unsigned short pet_max_atk2; //[Skotlex]
+ unsigned short pet_no_gvg; //Disables pets in gvg. [Skotlex]
+ unsigned short skill_min_damage;
+ unsigned short finger_offensive_type;
+ unsigned short heal_exp;
+ unsigned short max_heal_lv;
+ int max_heal; //Mitternacht
+ unsigned short resurrection_exp;
+ unsigned short shop_exp;
+ unsigned short combo_delay_rate;
+ unsigned short item_check;
+ unsigned short item_use_interval; //[Skotlex]
+ unsigned short wedding_modifydisplay;
+ unsigned short wedding_ignorepalette; //[Skotlex]
+ unsigned short xmas_ignorepalette; // [Valaris]
+ int natural_healhp_interval;
+ int natural_healsp_interval;
+ int natural_heal_skill_interval;
+ unsigned short natural_heal_weight_rate;
+ unsigned short item_name_override_grffile;
+ unsigned short indoors_override_grffile; // [Celest]
+ unsigned short skill_sp_override_grffile; // [Celest]
+ unsigned short cardillust_read_grffile;
+ unsigned short item_equip_override_grffile;
+ unsigned short item_slots_override_grffile;
+ unsigned short arrow_decrement;
+ unsigned short max_aspd;
+ unsigned short max_walk_speed; //Maximum walking speed after buffs [Skotlex]
+ int max_hp;
+ int max_sp;
+ unsigned short max_lv, aura_lv;
+ unsigned short max_parameter, max_baby_parameter;
+ int max_cart_weight;
+ unsigned short skill_log;
+ unsigned short battle_log;
+ unsigned short save_log;
+ unsigned short error_log;
+ unsigned short etc_log;
+ unsigned short save_clothcolor;
+ unsigned short undead_detect_type;
+ unsigned short auto_counter_type;
+ unsigned short min_hitrate; //[Skotlex]
+ unsigned short max_hitrate; //[Skotlex]
+ unsigned short agi_penalty_target;
+ unsigned short agi_penalty_type;
+ unsigned short agi_penalty_count;
+ unsigned short agi_penalty_num;
+ unsigned short vit_penalty_target;
+ unsigned short vit_penalty_type;
+ unsigned short vit_penalty_count;
+ unsigned short vit_penalty_num;
+ unsigned short weapon_defense_type;
+ unsigned short magic_defense_type;
+ unsigned short skill_reiteration;
+ unsigned short skill_nofootset;
+ unsigned short pc_cloak_check_type;
+ unsigned short monster_cloak_check_type;
+ unsigned short estimation_type;
+ unsigned short gvg_short_damage_rate;
+ unsigned short gvg_long_damage_rate;
+ unsigned short gvg_weapon_damage_rate;
+ unsigned short gvg_magic_damage_rate;
+ unsigned short gvg_misc_damage_rate;
+ unsigned short gvg_flee_penalty;
+ int gvg_eliminate_time;
+ unsigned short pk_short_damage_rate;
+ unsigned short pk_long_damage_rate;
+ unsigned short pk_weapon_damage_rate;
+ unsigned short pk_magic_damage_rate;
+ unsigned short pk_misc_damage_rate;
+ unsigned short mob_changetarget_byskill;
+ unsigned short attack_direction_change;
+ unsigned short land_skill_limit;
+ unsigned short party_skill_penalty;
+ unsigned short monster_class_change_full_recover;
+ unsigned short produce_item_name_input;
+ unsigned short produce_potion_name_input;
+ unsigned short making_arrow_name_input;
+ unsigned short holywater_name_input;
+ unsigned short cdp_name_input;
+ unsigned short display_skill_fail;
+ unsigned short chat_warpportal;
+ unsigned short mob_warp;
+ unsigned short dead_branch_active;
+ unsigned int vending_max_value;
+ unsigned short show_steal_in_same_party;
+ unsigned short party_share_type;
+ unsigned short party_hp_mode;
+ unsigned short party_show_share_picker;
+ unsigned short attack_attr_none;
+ int item_rate_mvp, item_rate_common, item_rate_common_boss, item_rate_card, item_rate_card_boss, // added support for MVP drops [Reddozen]
+ item_rate_equip, item_rate_equip_boss, item_rate_heal, item_rate_heal_boss, item_rate_use,
+ item_rate_use_boss, item_rate_treasure, // Added by RoVeRT, Additional Heal and Usable item rate by Val
+ item_rate_adddrop;
+
+ unsigned short logarithmic_drops;
+ unsigned short item_drop_common_min,item_drop_common_max; // Added by TyrNemesis^
+ unsigned short item_drop_card_min,item_drop_card_max;
+ unsigned short item_drop_equip_min,item_drop_equip_max;
+ unsigned short item_drop_mvp_min,item_drop_mvp_max; // End Addition
+ unsigned short item_drop_heal_min,item_drop_heal_max; // Added by Valatris
+ unsigned short item_drop_use_min,item_drop_use_max; //End
+ unsigned short item_drop_treasure_min,item_drop_treasure_max; //by [Skotlex]
+ unsigned short item_drop_adddrop_min,item_drop_adddrop_max; //[Skotlex]
+ unsigned short prevent_logout; // Added by RoVeRT
+
+ unsigned short alchemist_summon_reward; // [Valaris]
+ unsigned short drops_by_luk;
+ unsigned short drops_by_luk2;
+ unsigned short equip_natural_break_rate; //Base Natural break rate for attacks.
+ unsigned short equip_self_break_rate; //Natural & Penalty skills break rate
+ unsigned short equip_skill_break_rate; //Offensive skills break rate
+ unsigned short pet_equip_required;
+ unsigned short multi_level_up;
+ unsigned short max_exp_gain_rate; //Max amount of exp bar % you can get in one go.
+ unsigned short pk_mode;
+ unsigned short pk_level_range;
+
+ unsigned short manner_system; // end additions [Valaris]
+ unsigned short show_mob_info;
+
+ unsigned short agi_penalty_count_lv;
+ unsigned short vit_penalty_count_lv;
+
+ unsigned short gx_allhit;
+ unsigned short gx_disptype;
+ unsigned short devotion_level_difference;
+ unsigned short player_skill_partner_check;
+ unsigned short hide_GM_session;
+ unsigned short invite_request_check;
+ unsigned short skill_removetrap_type;
+ unsigned short disp_experience;
+ unsigned short disp_zeny;
+ unsigned short castle_defense_rate;
+ unsigned short backstab_bow_penalty;
+ unsigned short hp_rate;
+ unsigned short sp_rate;
+ unsigned short gm_cant_drop_min_lv;
+ unsigned short gm_cant_drop_max_lv;
+ unsigned short disp_hpmeter;
+ unsigned short bone_drop;
+ unsigned short buyer_name;
+
+// eAthena additions
+ unsigned short night_at_start; // added by [Yor]
+ int day_duration; // added by [Yor]
+ int night_duration; // added by [Yor]
+ short ban_hack_trade; // added by [Yor]
+ unsigned short hack_info_GM_level; // added by [Yor]
+ unsigned short any_warp_GM_min_level; // added by [Yor]
+ unsigned short packet_ver_flag; // added by [Yor]
+
+ unsigned short min_hair_style; // added by [MouseJstr]
+ unsigned short max_hair_style; // added by [MouseJstr]
+ unsigned short min_hair_color; // added by [MouseJstr]
+ unsigned short max_hair_color; // added by [MouseJstr]
+ unsigned short min_cloth_color; // added by [MouseJstr]
+ unsigned short max_cloth_color; // added by [MouseJstr]
+ unsigned short pet_hair_style; // added by [Skotlex]
+
+ unsigned short castrate_dex_scale; // added by [MouseJstr]
+ unsigned short area_size; // added by [MouseJstr]
+
+ unsigned short max_def, over_def_bonus; //added by [Skotlex]
+
+ unsigned short zeny_from_mobs; // [Valaris]
+ unsigned short mobs_level_up; // [Valaris]
+ unsigned short mobs_level_up_exp_rate; // [Valaris]
+ unsigned short pk_min_level; // [celest]
+ unsigned short skill_steal_type; // [celest]
+ unsigned short skill_steal_rate; // [celest]
+ unsigned short skill_steal_max_tries; //max steal skill tries on a mob. if 0, then w/o limit [Lupus]
+ unsigned short motd_type; // [celest]
+ unsigned short finding_ore_rate; // orn
+ unsigned short exp_calc_type;
+ unsigned short exp_bonus_attacker;
+ unsigned short exp_bonus_max_attacker;
+ unsigned short min_skill_delay_limit;
+ unsigned short default_skill_delay;
+ unsigned short no_skill_delay;
+ unsigned short attack_walk_delay;
+ unsigned short require_glory_guild;
+ unsigned short idle_no_share;
+ unsigned short party_update_interval;
+ unsigned short party_even_share_bonus;
+ unsigned short delay_battle_damage;
+ unsigned short hide_woe_damage;
+ unsigned short display_version;
+ unsigned short who_display_aid;
+
+ unsigned short display_hallucination; // [Skotlex]
+ unsigned short use_statpoint_table; // [Skotlex]
+
+ unsigned short ignore_items_gender; //[Lupus]
+
+ unsigned short copyskill_restrict; // [Aru]
+ unsigned short berserk_cancels_buffs; // [Aru]
+ unsigned short debuff_on_logout; // Removes a few "official" negative Scs on logout. [Skotlex]
+ unsigned short mob_ai; //Configures various mob_ai settings to make them smarter or dumber(official). [Skotlex]
+ unsigned short dynamic_mobs; // Dynamic Mobs [Wizputer] - battle_athena flag implemented by [random]
+ unsigned short mob_remove_damaged; // Dynamic Mobs - Remove mobs even if damaged [Wizputer]
+ int mob_remove_delay; // Dynamic Mobs - delay before removing mobs from a map [Skotlex]
+
+ unsigned short show_hp_sp_drain, show_hp_sp_gain; //[Skotlex]
+
+ unsigned short mob_npc_event_type; //Determines on who the npc_event is executed. [Skotlex]
+ unsigned short mob_clear_delay; // [Valaris]
+
+ unsigned short character_size; // if riders have size=2, and baby class riders size=1 [Lupus]
+ unsigned short mob_max_skilllvl; // Max possible skill level [Lupus]
+ unsigned short rare_drop_announce; // chance <= to show rare drops global announces
+
+ unsigned short retaliate_to_master; //Whether when a mob is attacked by another mob, it will retaliate versus the mob or the mob's master. [Skotlex]
+ unsigned short firewall_hits_on_undead; //Number of hits firewall does at a time on undead. [Skotlex]
+
+ unsigned short title_lvl1; // Players titles [Lupus]
+ unsigned short title_lvl2; // Players titles [Lupus]
+ unsigned short title_lvl3; // Players titles [Lupus]
+ unsigned short title_lvl4; // Players titles [Lupus]
+ unsigned short title_lvl5; // Players titles [Lupus]
+ unsigned short title_lvl6; // Players titles [Lupus]
+ unsigned short title_lvl7; // Players titles [Lupus]
+ unsigned short title_lvl8; // Players titles [Lupus]
+
+ unsigned short duel_enable; // [LuzZza]
+ unsigned short duel_allow_pvp; // [LuzZza]
+ unsigned short duel_allow_gvg; // [LuzZza]
+ unsigned short duel_allow_teleport; // [LuzZza]
+ unsigned short duel_autoleave_when_die; // [LuzZza]
+ unsigned short duel_time_interval; // [LuzZza]
+ unsigned short duel_only_on_same_map; // [Toms]
+
+ unsigned short skip_teleport_lv1_menu; // possibility to disable (skip) Teleport Lv1 menu, that have only two lines `Random` and `Cancel` [LuzZza]
+
+ unsigned short allow_skill_without_day; // [Komurka]
+ unsigned short allow_es_magic_pc; // [Skotlex]
+ unsigned short skill_wall_check; // [Skotlex]
+ unsigned short cell_stack_limit; // [Skotlex]
+ unsigned short skill_caster_check; // [Skotlex]
+ unsigned short sc_castcancel; // [Skotlex]
+ unsigned short pc_sc_def_rate; // [Skotlex]
+ unsigned short mob_sc_def_rate;
+ unsigned short pc_luk_sc_def;
+ unsigned short mob_luk_sc_def;
+ unsigned short pc_max_sc_def;
+ unsigned short mob_max_sc_def;
+
+ unsigned short sg_angel_skill_ratio;
+ unsigned short sg_miracle_skill_ratio;
+ int sg_miracle_skill_duration_min;
+ int sg_miracle_skill_duration_max;
+ unsigned short autospell_stacking; //Enables autospell cards to stack. [Skotlex]
+ unsigned short override_mob_names; //Enables overriding spawn mob names with the mob_db names. [Skotlex]
+ unsigned short min_chat_delay; //Minimum time between client messages. [Skotlex]
+ unsigned short friend_auto_add; //When accepting friends, both get friended. [Skotlex]
+ unsigned int hvan_explosion_intimate ; // fix [albator]
+ unsigned short homunculus_show_growth ; //[orn]
+ unsigned short homunculus_friendly_rate;
+} battle_config;
+
+void do_init_battle(void);
+void do_final_battle(void);
+extern int battle_config_read(const char *cfgName);
+extern void battle_validate_conf(void);
+extern void battle_set_defaults(void);
+extern int battle_set_value(char *, char *);
+int battle_get_value(char *);
+
+#endif
diff --git a/src/map/charcommand.c b/src/map/charcommand.c
index 0e796fba6..32290d8fd 100644
--- a/src/map/charcommand.c
+++ b/src/map/charcommand.c
@@ -1,1846 +1,1846 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <limits.h>
-
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/showmsg.h"
-
-#include "log.h"
-#include "clif.h"
-#include "chrif.h"
-#include "intif.h"
-#include "itemdb.h"
-#include "map.h"
-#include "pc.h"
-#include "status.h"
-#include "skill.h"
-#include "mob.h"
-#include "pet.h"
-#include "battle.h"
-#include "charcommand.h"
-#include "atcommand.h"
-
-static char command_symbol = '#';
-
-extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
-
-#define CCMD_FUNC(x) int charcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message)
-CCMD_FUNC(jobchange);
-CCMD_FUNC(petrename);
-CCMD_FUNC(petfriendly);
-CCMD_FUNC(stats);
-CCMD_FUNC(option);
-CCMD_FUNC(save);
-CCMD_FUNC(stats_all);
-CCMD_FUNC(reset);
-CCMD_FUNC(spiritball);
-CCMD_FUNC(itemlist);
-CCMD_FUNC(effect);
-CCMD_FUNC(storagelist);
-CCMD_FUNC(item);
-CCMD_FUNC(warp);
-CCMD_FUNC(zeny);
-CCMD_FUNC(fakename);
-CCMD_FUNC(baselevel);
-CCMD_FUNC(joblevel);
-CCMD_FUNC(questskill);
-CCMD_FUNC(lostskill);
-CCMD_FUNC(skreset);
-CCMD_FUNC(streset);
-CCMD_FUNC(model);
-CCMD_FUNC(stpoint);
-CCMD_FUNC(skpoint);
-CCMD_FUNC(changesex);
-CCMD_FUNC(feelreset);
-CCMD_FUNC(help);
-
-
-/*==========================================
- *CharCommandInfo charcommand_info[]構造体の定義
- *------------------------------------------
- */
-
-// First char of commands is configured in charcommand_athena.conf. Leave # in this list for default value.
-// to set default level, read charcommand_athena.conf first please.
-static CharCommandInfo charcommand_info[] = {
- { CharCommandJobChange, "#job", 60, charcommand_jobchange },
- { CharCommandJobChange, "#jobchange", 60, charcommand_jobchange },
- { CharCommandPetRename, "#petrename", 50, charcommand_petrename },
- { CharCommandPetFriendly, "#petfriendly", 50, charcommand_petfriendly },
- { CharCommandStats, "#stats", 40, charcommand_stats },
- { CharCommandOption, "#option", 60, charcommand_option },
- { CharCommandReset, "#reset", 60, charcommand_reset },
- { CharCommandSave, "#save", 60, charcommand_save },
- { CharCommandStatsAll, "#statsall", 40, charcommand_stats_all },
- { CharCommandSpiritball, "#spiritball", 40, charcommand_spiritball },
- { CharCommandItemList, "#itemlist", 40, charcommand_itemlist },
- { CharCommandEffect, "#effect", 40, charcommand_effect },
- { CharCommandStorageList, "#storagelist", 40, charcommand_storagelist },
- { CharCommandItem, "#item", 60, charcommand_item },
- { CharCommandWarp, "#warp", 60, charcommand_warp },
- { CharCommandWarp, "#rura", 60, charcommand_warp },
- { CharCommandWarp, "#rura+", 60, charcommand_warp },
- { CharCommandZeny, "#zeny", 60, charcommand_zeny },
- { CharCommandFakeName, "#fakename", 20, charcommand_fakename},
-
- //*********************************Recently added commands*********************************************
- { CharCommandBaseLevel, "#baselvl", 20, charcommand_baselevel},
- { CharCommandBaseLevel, "#blvl", 60, charcommand_baselevel},
- { CharCommandBaseLevel, "#baselvlup", 60, charcommand_baselevel},
- { CharCommandJobLevel, "#joblvl", 60, charcommand_joblevel},
- { CharCommandJobLevel, "#jlvl", 60, charcommand_joblevel},
- { CharCommandJobLevel, "#joblvlup", 60, charcommand_joblevel},
- { CharCommandQuestSkill, "#questskill", 60, charcommand_questskill },
- { CharCommandLostSkill, "#lostskill", 60, charcommand_lostskill },
- { CharCommandSkReset, "#skreset", 60, charcommand_skreset },
- { CharCommandStReset, "#streset", 60, charcommand_streset },
- { CharCommandModel, "#model", 50, charcommand_model },
- { CharCommandSKPoint, "#skpoint", 60, charcommand_skpoint },
- { CharCommandSTPoint, "#stpoint", 60, charcommand_stpoint },
- { CharCommandChangeSex, "#changesex", 60, charcommand_changesex },
- { CharCommandFeelReset, "#feelreset", 60, charcommand_feelreset },
- { CharCommandHelp, "#help", 20, charcommand_help },
-// add new commands before this line
- { CharCommand_Unknown, NULL, 1, NULL }
-};
-
-int get_charcommand_level(const CharCommandType type) {
- int i;
-
- for (i = 0; charcommand_info[i].type != CharCommand_None; i++)
- if (charcommand_info[i].type == type)
- return charcommand_info[i].level;
-
- return 100; // 100: command can not be used
-}
-
-CharCommandType
-charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl) {
- CharCommandInfo info;
- CharCommandType type;
-
- malloc_set(&info, 0, sizeof(info));
-
- type = charcommand(sd, gmlvl, str, &info);
- if (type != CharCommand_None) {
- char command[100];
- char output[200];
- const char* p = str;
-
- if (map[sd->bl.m].nocommand &&
- gmlvl < map[sd->bl.m].nocommand)
- { //Command not allowed on this map.
- sprintf(output, msg_txt(143));
- clif_displaymessage(fd, output);
- return AtCommand_None;
- }
-
- malloc_tsetdword(command, '\0', sizeof(command));
- malloc_tsetdword(output, '\0', sizeof(output));
- while (*p && !isspace(*p))
- p++;
- if (p - str >= sizeof(command)) // too long
- return CharCommand_Unknown;
- strncpy(command, str, p - str);
- while (isspace(*p))
- p++;
-
- if (type == CharCommand_Unknown || info.proc == NULL) {
- snprintf(output, sizeof(output),msg_txt(153), command); // %s is Unknown Command.
- clif_displaymessage(fd, output);
- } else {
- if (info.proc(fd, sd, command, p) != 0) {
- // Command can not be executed
- snprintf(output, sizeof(output), msg_txt(154), command); // %s failed.
- clif_displaymessage(fd, output);
- }
- }
-
- return info.type;
- }
-
- return CharCommand_None;
-}
-
-/*==========================================
- *is_charcommand @コマンドに存在するかどうか確認する
- *------------------------------------------
- */
-CharCommandType
-is_charcommand(const int fd, struct map_session_data* sd, const char* message) {
- const char* str = message;
- int s_flag = 0;
-
- nullpo_retr(CharCommand_None, sd);
-
- if (!message || !*message)
- return CharCommand_None;
-
- str += strlen(sd->status.name);
- while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) {
- if (*str == ':')
- s_flag = 1;
- str++;
- }
-
- if (!*str)
- return CharCommand_None;
-
- return charcommand_sub(fd,sd,str,pc_isGM(sd));
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-CharCommandType charcommand(struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info) {
- char* p = (char *)message;
-
- if (!info)
- return CharCommand_None;
- if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd)
- return CharCommand_None;
- if (!p || !*p) {
- ShowError("char command message is empty\n");
- return CharCommand_None;
- }
-
- if(p[0] == '|')
- p += 3;
-
- if (*p == command_symbol) { // check first char, try to skip |00 (or something else) [Lance]
- char command[101];
- int i = 0;
- malloc_set(info, 0, sizeof(CharCommandInfo));
- sscanf(p, "%100s", command);
- command[sizeof(command)-1] = '\0';
-
- while (charcommand_info[i].type != CharCommand_Unknown) {
- if (strcmpi(command+1, charcommand_info[i].command+1) == 0 && level >= charcommand_info[i].level) {
- p[0] = charcommand_info[i].command[0]; // set correct first symbol for after.
- break;
- }
- i++;
- }
-
- if (charcommand_info[i].type == CharCommand_Unknown) {
- // doesn't return Unknown if player is normal player (display the text, not display: unknown command)
- if (level == 0)
- return CharCommand_None;
- else
- return CharCommand_Unknown;
- } else if((log_config.gm) && (charcommand_info[i].level >= log_config.gm)) {
- log_atcommand(sd, message);
- }
- memcpy(info, &charcommand_info[i], sizeof charcommand_info[i]);
- } else {
- return CharCommand_None;
- }
-
- return info->type;
-}
-
-
-/*==========================================
- *
- *------------------------------------------
- */
-static CharCommandInfo* get_charcommandinfo_byname(const char* name) {
- int i;
-
- for (i = 0; charcommand_info[i].type != CharCommand_Unknown; i++)
- if (strcmpi(charcommand_info[i].command + 1, name) == 0)
- return &charcommand_info[i];
-
- return NULL;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int charcommand_config_read(const char *cfgName) {
- char line[1024], w1[1024], w2[1024];
- CharCommandInfo* p;
- FILE* fp;
-
- if ((fp = fopen(cfgName, "r")) == NULL) {
- ShowError("CharCommands configuration file not found: %s\n", cfgName);
- return 1;
- }
-
- while (fgets(line, sizeof(line)-1, fp)) {
- if (line[0] == '/' && line[1] == '/')
- continue;
-
- if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2)
- continue;
- p = get_charcommandinfo_byname(w1);
- if (p != NULL) {
- p->level = atoi(w2);
- if (p->level > 100)
- p->level = 100;
- else if (p->level < 0)
- p->level = 0;
- }
-
- if (strcmpi(w1, "import") == 0)
- charcommand_config_read(w2);
- else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 &&
- w2[0] != '/' && // symbol of standard ragnarok GM commands
- w2[0] != '%' && // symbol of party chat speaking
- w2[0] != '$' && // symbol of guild chat speaking
- w2[0] != '@') // symbol of atcommand
- command_symbol = w2[0];
- }
- fclose(fp);
-
- return 0;
-}
-
-/*==========================================
- * 対象キャラクターを転職させる upper指定で転生や養子も可能
- *------------------------------------------
- */
-int charcommand_jobchange(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char character[100];
- struct map_session_data* pl_sd;
- int job = 0, upper = -1;
-
- malloc_tsetdword(character, '\0', sizeof(character));
-
- if (!message || !*message) {
- clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
- return -1;
- }
-
- if (sscanf(message, "%d %d %99[^\n]", &job, &upper, character) < 3) { //upper指定してある
- upper = -1;
- if (sscanf(message, "%d %99[^\n]", &job, character) < 2) { //upper指定してない上に何か足りない
- clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
- return -1;
- }
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- int j;
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job only to lower or same level
- if ((job >= 0 && job < MAX_PC_CLASS)) {
- for (j=0; j < MAX_INVENTORY; j++) {
- if(pl_sd->status.inventory[j].nameid>0 && pl_sd->status.inventory[j].equip!=0)
- pc_unequipitem(pl_sd, j, 3);
- }
- if (pc_jobchange(pl_sd, job, upper) == 0)
- clif_displaymessage(fd, msg_table[48]); // Character's job changed.
- else {
- clif_displaymessage(fd, msg_table[192]); // Impossible to change the character's job.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[49]); // Invalid job ID.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int charcommand_petrename(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char character[NAME_LENGTH];
- struct map_session_data *pl_sd;
- struct pet_data *pd;
-
- malloc_tsetdword(character, '\0', sizeof(character));
-
- if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: #petrename <char name>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) == NULL) {
- clif_displaymessage(fd, msg_txt(3)); // Character not found.
- return -1;
- }
-
- if (!pl_sd->status.pet_id || !pl_sd->pd) {
- clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet.
- return -1;
- }
-
- pd = pl_sd->pd;
-
- if (pd->pet.rename_flag) {
- clif_displaymessage(fd, msg_table[190]); // This player can already rename his/her pet.
- return -1;
- }
- pd->pet.rename_flag = 0;
- intif_save_petdata(pl_sd->status.account_id, &pd->pet);
- clif_send_petstatus(pl_sd);
- clif_displaymessage(fd, msg_table[189]); // This player can now rename his/her pet.
- return 0;
-}
-
-
-/*==========================================
- *
- *------------------------------------------
- */
-int charcommand_petfriendly(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- int friendly = 0;
- char character[NAME_LENGTH];
- struct map_session_data *pl_sd;
- struct pet_data *pd;
-
- malloc_tsetdword(character, '\0', sizeof(character));
- if (!message || !*message || sscanf(message,"%d %23s",&friendly,character) < 2) {
- clif_displaymessage(fd, "Please, enter a valid value (usage: "
- "#petfriendly <0-1000> <player>).");
- return -1;
- }
-
- if (((pl_sd = map_nick2sd(character)) == NULL) ||
- pc_isGM(sd)<pc_isGM(pl_sd)) {
- clif_displaymessage(fd, msg_txt(3)); // Character not found.
- return -1;
- }
-
- if (!pl_sd->status.pet_id || !pl_sd->pd) {
- clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet.
- return -1;
- }
-
- if (friendly < 0 || friendly > 1000) {
- clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
- return -1;
- }
-
- pd = pl_sd->pd;
- if (friendly == pd->pet.intimate) {
- clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value.
- return -1;
- }
-
- pd->pet.intimate = friendly;
- clif_send_petstatus(pl_sd);
- clif_pet_emotion(pd,0);
- clif_displaymessage(pl_sd->fd, msg_table[182]); // Pet friendly value changed!
- clif_displaymessage(sd->fd, msg_table[182]); // Pet friendly value changed!
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int charcommand_stats(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char character[NAME_LENGTH];
- char job_jobname[100];
- char output[200];
- struct map_session_data *pl_sd;
- int i;
-
- malloc_tsetdword(character, '\0', sizeof(character));
- malloc_tsetdword(job_jobname, '\0', sizeof(job_jobname));
- malloc_tsetdword(output, '\0', sizeof(output));
-
- if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: #stats <char name>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- struct {
- const char* format;
- int value;
- } output_table[] = {
- { "Base Level - %d", 0 },
- { NULL, 0 },
- { "Hp - %d", 0 },
- { "MaxHp - %d", 0 },
- { "Sp - %d", 0 },
- { "MaxSp - %d", 0 },
- { "Str - %3d", 0 },
- { "Agi - %3d", 0 },
- { "Vit - %3d", 0 },
- { "Int - %3d", 0 },
- { "Dex - %3d", 0 },
- { "Luk - %3d", 0 },
- { "Zeny - %d", 0 },
- { NULL, 0 }
- };
- //direct array initialization with variables is not standard C compliant.
- output_table[0].value = pl_sd->status.base_level;
- output_table[1].format = job_jobname;
- output_table[1].value = pl_sd->status.job_level;
- output_table[2].value = pl_sd->status.hp;
- output_table[3].value = pl_sd->status.max_hp;
- output_table[4].value = pl_sd->status.sp;
- output_table[5].value = pl_sd->status.max_sp;
- output_table[6].value = pl_sd->status.str;
- output_table[7].value = pl_sd->status.agi;
- output_table[8].value = pl_sd->status.vit;
- output_table[9].value = pl_sd->status.int_;
- output_table[10].value = pl_sd->status.dex;
- output_table[11].value = pl_sd->status.luk;
- output_table[12].value = pl_sd->status.zeny;
- sprintf(job_jobname, "Job - %s %s", job_name(pl_sd->status.class_), "(level %d)");
- sprintf(output, msg_table[53], pl_sd->status.name); // '%s' stats:
- clif_displaymessage(fd, output);
- for (i = 0; output_table[i].format != NULL; i++) {
- sprintf(output, output_table[i].format, output_table[i].value);
- clif_displaymessage(fd, output);
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Character Reset
- *------------------------------------------
- */
-int charcommand_reset(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char character[NAME_LENGTH];
- char output[200];
- struct map_session_data *pl_sd;
-
- malloc_tsetdword(character, '\0', sizeof(character));
- malloc_tsetdword(output, '\0', sizeof(output));
-
- if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: #reset <charname>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
- pc_resetstate(pl_sd);
- pc_resetskill(pl_sd,1);
- sprintf(output, msg_table[208], character); // '%s' skill and stats points reseted!
- clif_displaymessage(fd, output);
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int charcommand_option(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char character[NAME_LENGTH];
- int opt1 = 0, opt2 = 0, opt3 = 0;
- struct map_session_data* pl_sd;
-
- malloc_tsetdword(character, '\0', sizeof(character));
-
- if (!message || !*message ||
- sscanf(message, "%d %d %d %23[^\n]", &opt1, &opt2, &opt3, character) < 4 ||
- opt1 < 0 || opt2 < 0 || opt3 < 0) {
- clif_displaymessage(fd, "Please, enter valid options and a player name (usage: #option <param1> <param2> <param3> <charname>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change option only to lower or same level
- pl_sd->sc.opt1 = opt1;
- pl_sd->sc.opt2 = opt2;
- pc_setoption(pl_sd, opt3);
- clif_displaymessage(fd, msg_table[58]); // Character's options changed.
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int charcommand_save(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char map_name[MAP_NAME_LENGTH];
- char character[NAME_LENGTH];
- struct map_session_data* pl_sd;
- int x = 0, y = 0;
- int m;
-
- malloc_tsetdword(map_name, '\0', sizeof(map_name));
- malloc_tsetdword(character, '\0', sizeof(character));
-
- if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4 || x < 0 || y < 0) {
- clif_displaymessage(fd, "Please, enter a valid save point and a player name (usage: #save <map> <x> <y> <charname>).");
- return -1;
- }
-
- if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
- strcat(map_name, ".gat");
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change save point only to lower or same gm level
- m = map_mapname2mapid(map_name);
- if (m < 0 && !mapindex_name2id(map_name)) {
- clif_displaymessage(fd, msg_table[1]); // Map not found.
- return -1;
- } else {
- //FIXME: What do you do if the map is in another map server with the nowarpto flag?
- if (m>=0 && map[m].flag.nosave && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
- clif_displaymessage(fd, "You are not authorised to set this map as a save map.");
- return -1;
- }
- if (m>=0)
- pc_setsavepoint(pl_sd, map[m].index, x, y);
- else
- pc_setsavepoint(pl_sd, mapindex_name2id(map_name), x, y);
- clif_displaymessage(fd, msg_table[57]); // Character's respawn point changed.
- }
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-//** Character Stats All by fritz
-int charcommand_stats_all(const int fd, struct map_session_data* sd, const char* command, const char* message)
-{
- char output[1024], gmlevel[1024];
- int i;
- int count, users;
- struct map_session_data *pl_sd, **pl_allsd;
-
- malloc_tsetdword(output, '\0', sizeof(output));
- malloc_tsetdword(gmlevel, '\0', sizeof(gmlevel));
-
- count = 0;
- pl_allsd = map_getallusers(&users);
- for(i = 0; i < users; i++) {
- if ((pl_sd = pl_allsd[i]))
- {
- if (pc_isGM(pl_sd) > 0)
- sprintf(gmlevel, "| GM Lvl: %d", pc_isGM(pl_sd));
- else
- sprintf(gmlevel, " ");
-
- sprintf(output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level, pl_sd->status.hp, pl_sd->status.max_hp, pl_sd->status.sp, pl_sd->status.max_sp);
- clif_displaymessage(fd, output);
- sprintf(output, "STR: %d | AGI: %d | VIT: %d | INT: %d | DEX: %d | LUK: %d | Zeny: %d %s", pl_sd->status.str, pl_sd->status.agi, pl_sd->status.vit, pl_sd->status.int_, pl_sd->status.dex, pl_sd->status.luk, pl_sd->status.zeny, gmlevel);
- clif_displaymessage(fd, output);
- clif_displaymessage(fd, "--------");
- count++;
- }
- }
-
- if (count == 0)
- clif_displaymessage(fd, msg_table[28]); // No player found.
- else if (count == 1)
- clif_displaymessage(fd, msg_table[29]); // 1 player found.
- else {
- sprintf(output, msg_table[30], count); // %d players found.
- clif_displaymessage(fd, output);
- }
-
- return 0;
-}
-
-/*==========================================
- * CharSpiritBall Function by PalasX
- *------------------------------------------
- */
-int charcommand_spiritball(const int fd, struct map_session_data* sd,const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char character[NAME_LENGTH];
- int spirit = 0;
-
- malloc_tsetdword(character, '\0', sizeof(character));
-
- if(!message || !*message || sscanf(message, "%d %23[^\n]", &spirit, character) < 2 || spirit < 0 || spirit > 1000) {
- clif_displaymessage(fd, "Usage: @spiritball <number: 0-1000>) <CHARACTER_NAME>.");
- return -1;
- }
-
- if((pl_sd = map_nick2sd(character)) != NULL) {
- if (spirit >= 0 && spirit <= 0x7FFF) {
- if (pl_sd->spiritball != spirit || spirit > 999) {
- if (pl_sd->spiritball > 0)
- pc_delspiritball(pl_sd, pl_sd->spiritball, 1);
- pl_sd->spiritball = spirit;
- clif_spiritball(pl_sd);
- // no message, player can look the difference
- if (spirit > 1000)
- clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client!
- } else {
- clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
- return 0;
-}
-
-/*==========================================
- * #itemlist <character>: Displays the list of a player's items.
- *------------------------------------------
- */
-int
-charcommand_itemlist(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- struct item_data *item_data, *item_temp;
- int i, j, equip, count, counter, counter2;
- char character[NAME_LENGTH], output[200], equipstr[100], outputtmp[200];
- struct item *i_item; //Current inventory item.
- nullpo_retr(-1, sd);
-
- malloc_tsetdword(character, '\0', sizeof(character));
- malloc_tsetdword(output, '\0', sizeof(output));
- malloc_tsetdword(equipstr, '\0', sizeof(equipstr));
- malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp));
-
- if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
- counter = 0;
- count = 0;
- for (i = 0; i < MAX_INVENTORY; i++) {
- i_item = &pl_sd->status.inventory[i];
- if (pl_sd->status.inventory[i].nameid > 0 && (item_data = itemdb_exists(i_item->nameid)) != NULL) {
- counter = counter + i_item->amount;
- count++;
- if (count == 1) {
- sprintf(output, "------ Items list of '%s' ------", pl_sd->status.name);
- clif_displaymessage(fd, output);
- }
- if ((equip = i_item->equip)) {
- strcpy(equipstr, "| equiped: ");
- if (equip & EQP_GARMENT)
- strcat(equipstr, "robe/gargment, ");
- if (equip & EQP_ACC_L)
- strcat(equipstr, "left accessory, ");
- if (equip & EQP_ARMOR)
- strcat(equipstr, "body/armor, ");
- if ((equip & EQP_ARMS) == EQP_HAND_R)
- strcat(equipstr, "right hand, ");
- if ((equip & EQP_ARMS) == EQP_HAND_L)
- strcat(equipstr, "left hand, ");
- if ((equip & EQP_ARMS) == EQP_ARMS)
- strcat(equipstr, "both hands, ");
- if (equip & EQP_SHOES)
- strcat(equipstr, "feet, ");
- if (equip & EQP_ACC_R)
- strcat(equipstr, "right accessory, ");
- if ((equip & EQP_HELM) == EQP_HEAD_LOW)
- strcat(equipstr, "lower head, ");
- if ((equip & EQP_HELM) == EQP_HEAD_TOP)
- strcat(equipstr, "top head, ");
- if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_TOP))
- strcat(equipstr, "lower/top head, ");
- if ((equip & EQP_HELM) == EQP_HEAD_MID)
- strcat(equipstr, "mid head, ");
- if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_MID))
- strcat(equipstr, "lower/mid head, ");
- if ((equip & EQP_HELM) == EQP_HELM)
- strcat(equipstr, "lower/mid/top head, ");
- // remove final ', '
- equipstr[strlen(equipstr) - 2] = '\0';
- } else
- malloc_tsetdword(equipstr, '\0', sizeof(equipstr));
- if (i_item->refine)
- sprintf(output, "%d %s %+d (%s %+d, id: %d) %s", i_item->amount, item_data->name, i_item->refine, item_data->jname, i_item->refine, i_item->nameid, equipstr);
- else
- sprintf(output, "%d %s (%s, id: %d) %s", i_item->amount, item_data->name, item_data->jname, i_item->nameid, equipstr);
- clif_displaymessage(fd, output);
- malloc_tsetdword(output, '\0', sizeof(output));
- counter2 = 0;
-
- if(i_item->card[0]==CARD0_PET) { //pet eggs
- if (i_item->card[3])
- sprintf(outputtmp, " -> (pet egg, pet id: %u, named)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2]));
- else
- sprintf(outputtmp, " -> (pet egg, pet id: %u, unnamed)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2]));
- strcat(output, outputtmp);
- } else
- if(i_item->card[0]==CARD0_FORGE) { //forged items.
- sprintf(outputtmp, " -> (crafted item, creator id: %u, star crumbs %d, element %d)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3]), i_item->card[1]>>8, i_item->card[1]&0x0f);
- } else
- if(i_item->card[0]==CARD0_CREATE) { //created items.
- sprintf(outputtmp, " -> (produced item, creator id: %u)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3]));
- strcat(output, outputtmp);
- } else //Normal slots
- for (j = 0; j < item_data->slot; j++) {
- if (pl_sd->status.inventory[i].card[j]) {
- if ((item_temp = itemdb_exists(i_item->card[j])) != NULL) {
- if (output[0] == '\0')
- sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
- else
- sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
- strcat(output, outputtmp);
- }
- }
- }
- if (output[0] != '\0') {
- output[strlen(output) - 2] = ')';
- output[strlen(output) - 1] = '\0';
- clif_displaymessage(fd, output);
- }
- }
- }
- if (count == 0)
- clif_displaymessage(fd, "No item found on this player.");
- else {
- sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
- clif_displaymessage(fd, output);
- }
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * #effect by [MouseJstr]
- *
- * Create a effect localized on another character
- *------------------------------------------
- */
-int
-charcommand_effect(const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd = NULL;
- char target[255];
- int type = 0;
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %s", &type, target) != 2) {
- clif_displaymessage(fd, "usage: #effect <type+> <target>.");
- return -1;
- }
-
- if((pl_sd=map_nick2sd((char *) target)) == NULL)
- return -1;
-
- clif_specialeffect(&pl_sd->bl, type, AREA);
- clif_displaymessage(fd, msg_table[229]); // Your effect has changed.
-
- return 0;
-}
-
-/*==========================================
- * #storagelist <character>: Displays the items list of a player's storage.
- *------------------------------------------
- */
-int
-charcommand_storagelist(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct storage *stor;
- struct map_session_data *pl_sd;
- struct item_data *item_data, *item_temp;
- int i, j, count, counter, counter2;
- char character[NAME_LENGTH], output[200], outputtmp[200];
- nullpo_retr(-1, sd);
-
- malloc_tsetdword(character, '\0', sizeof(character));
- malloc_tsetdword(output, '\0', sizeof(output));
- malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp));
-
- if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
- if((stor = account2storage2(pl_sd->status.account_id)) != NULL) {
- counter = 0;
- count = 0;
- for (i = 0; i < MAX_STORAGE; i++) {
- if (stor->storage_[i].nameid > 0 && (item_data = itemdb_search(stor->storage_[i].nameid)) != NULL) {
- counter = counter + stor->storage_[i].amount;
- count++;
- if (count == 1) {
- sprintf(output, "------ Storage items list of '%s' ------", pl_sd->status.name);
- clif_displaymessage(fd, output);
- }
- if (stor->storage_[i].refine)
- sprintf(output, "%d %s %+d (%s %+d, id: %d)", stor->storage_[i].amount, item_data->name, stor->storage_[i].refine, item_data->jname, stor->storage_[i].refine, stor->storage_[i].nameid);
- else
- sprintf(output, "%d %s (%s, id: %d)", stor->storage_[i].amount, item_data->name, item_data->jname, stor->storage_[i].nameid);
- clif_displaymessage(fd, output);
- malloc_tsetdword(output, '\0', sizeof(output));
- counter2 = 0;
- for (j = 0; j < item_data->slot; j++) {
- if (stor->storage_[i].card[j]) {
- if ((item_temp = itemdb_search(stor->storage_[i].card[j])) != NULL) {
- if (output[0] == '\0')
- sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
- else
- sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
- strcat(output, outputtmp);
- }
- }
- }
- if (output[0] != '\0') {
- output[strlen(output) - 2] = ')';
- output[strlen(output) - 1] = '\0';
- clif_displaymessage(fd, output);
- }
- }
- }
- if (count == 0)
- clif_displaymessage(fd, "No item found in the storage of this player.");
- else {
- sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
- clif_displaymessage(fd, output);
- }
- } else {
- clif_displaymessage(fd, "This player has no storage.");
- return 0;
- }
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-static void
-charcommand_giveitem_sub(struct map_session_data *sd,struct item_data *item_data,int number)
-{
- int flag = 0;
- int loop = 1, get_count = number,i;
- struct item item_tmp;
-
- if(sd && item_data){
- if (item_data->type == 4 || item_data->type == 5 ||
- item_data->type == 7 || item_data->type == 8) {
- loop = number;
- get_count = 1;
- }
- for (i = 0; i < loop; i++) {
- malloc_set(&item_tmp, 0, sizeof(item_tmp));
- item_tmp.nameid = item_data->nameid;
- item_tmp.identify = 1;
-
- if ((flag = pc_additem((struct map_session_data*)sd,
- &item_tmp, get_count)))
- clif_additem((struct map_session_data*)sd, 0, 0, flag);
- }
- //Logs (A)dmins items [Lupus]
- if(log_config.enable_logs&0x400)
- log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp);
-
- }
-}
-/*==========================================
- * #item command (usage: #item <name/id_of_item> <quantity> <player>)
- * by MC Cameri
- *------------------------------------------
- */
-int charcommand_item(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char item_name[100];
- char character[NAME_LENGTH];
- struct map_session_data *pl_sd;
- int number = 0, item_id, flag;
- struct item item_tmp;
- struct item_data *item_data;
- int get_count, i, pet_id;
- char tmp_cmdoutput[1024];
- nullpo_retr(-1, sd);
-
- malloc_tsetdword(item_name, '\0', sizeof(item_name));
-
- if (!message || !*message || sscanf(message, "%99s %d %23[^\n]", item_name, &number, character) < 3) {
- clif_displaymessage(fd, "Please, enter an item name/id (usage: #item <item name or ID> <quantity> <char name>).");
- return -1;
- }
-
- if (number <= 0)
- number = 1;
-
- item_id = 0;
- if ((item_data = itemdb_searchname(item_name)) != NULL ||
- (item_data = itemdb_exists(atoi(item_name))) != NULL)
- item_id = item_data->nameid;
-
- if (item_id >= 500) {
- get_count = number;
- // check pet egg
- pet_id = search_petDB_index(item_id, PET_EGG);
- if (item_data->type == 4 || item_data->type == 5 ||
- item_data->type == 7 || item_data->type == 8) {
- get_count = 1;
- }
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
- for (i = 0; i < number; i += get_count) {
- // if pet egg
- if (pet_id >= 0) {
- pl_sd->catch_target_class = pet_db[pet_id].class_;
- intif_create_pet(pl_sd->status.account_id, pl_sd->status.char_id,
- (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
- (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
- 100, 0, 1, pet_db[pet_id].jname);
- // if not pet egg
- } else {
- malloc_set(&item_tmp, 0, sizeof(item_tmp));
- item_tmp.nameid = item_id;
- item_tmp.identify = 1;
-
- if ((flag = pc_additem(pl_sd, &item_tmp, get_count)))
- clif_additem(pl_sd, 0, 0, flag);
- }
- }
-
- //Logs (A)dmins items [Lupus]
- if(log_config.enable_logs&0x400)
- log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp);
-
- clif_displaymessage(fd, msg_table[18]); // Item created.
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else if(/* from jA's @giveitem */strcmpi(character,"all")==0 || strcmpi(character,"everyone")==0){
- struct map_session_data **pl_allsd;
- int users;
- pl_allsd = map_getallusers(&users);
- for (i = 0; i < users; i++) {
- if ((pl_sd = pl_allsd[i])) {
- charcommand_giveitem_sub(pl_sd,item_data,number);
- snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "You got %s %d.", item_name,number);
- clif_displaymessage(pl_sd->fd, tmp_cmdoutput);
- }
- }
- snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "%s received %s %d.","Everyone",item_name,number);
- clif_displaymessage(fd, tmp_cmdoutput);
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * #warp/#rura/#rura+ <mapname> <x> <y> <char name>
- *------------------------------------------
- */
-int charcommand_warp(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char map_name[MAP_NAME_LENGTH];
- char character[NAME_LENGTH];
- int x = 0, y = 0;
- struct map_session_data *pl_sd;
- int m;
-
- nullpo_retr(-1, sd);
-
- malloc_tsetdword(map_name, '\0', sizeof(map_name));
- malloc_tsetdword(character, '\0', sizeof(character));
-
- if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4) {
- clif_displaymessage(fd, "Usage: #warp/#rura/#rura+ <mapname> <x> <y> <char name>");
- return -1;
- }
-
- if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
- strcat(map_name, ".gat");
-
- if ((pl_sd = map_nick2sd(character)) == NULL) {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
- if (pc_isGM(sd) < pc_isGM(pl_sd)) { // you can rura+ only lower or same GM level
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- m = map_mapname2mapid(map_name);
- if (m < 0) {
- clif_displaymessage(fd, msg_table[1]); // Map not found.
- return -1;
- }
- if ((x || y) && map_getcell(m, x, y, CELL_CHKNOREACH)) {
- clif_displaymessage(fd, msg_table[2]); // Coordinates out of range.
- x = y = 0;
- }
- if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
- clif_displaymessage(fd, "You are not authorised to warp someone to this map.");
- return -1;
- }
- if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
- clif_displaymessage(fd, "You are not authorised to warp this player from its actual map.");
- return -1;
- }
- if (pc_setpos(pl_sd, map[m].index, x, y, 3) == 0) {
- clif_displaymessage(pl_sd->fd, msg_table[0]); // Warped.
- clif_displaymessage(fd, msg_table[15]); // Player warped (message sends to player too).
- return 0;
- }
- //No error message specified...?
- return -1;
-}
-
-/*==========================================
- * #zeny <charname>
- *------------------------------------------
- */
-int charcommand_zeny(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char character[NAME_LENGTH];
- int zeny = 0, new_zeny;
- nullpo_retr(-1, sd);
-
- malloc_tsetdword(character, '\0', sizeof(character));
-
- if (!message || !*message || sscanf(message, "%d %23[^\n]", &zeny, character) < 2 || zeny == 0) {
- clif_displaymessage(fd, "Please, enter a number and a player name (usage: #zeny <zeny> <name>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- new_zeny = pl_sd->status.zeny + zeny;
- if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow
- new_zeny = MAX_ZENY;
- else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow
- new_zeny = 0;
- if (new_zeny != pl_sd->status.zeny) {
- pl_sd->status.zeny = new_zeny;
- clif_updatestatus(pl_sd, SP_ZENY);
- clif_displaymessage(fd, msg_table[211]); // Character's number of zenys changed!
- } else {
- if (zeny < 0)
- clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
- else
- clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * #fakename <char name> <fake name>
- *------------------------------------------
- */
-
-int charcommand_fakename(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char name[NAME_LENGTH];
- char char_name[NAME_LENGTH];
-
- nullpo_retr(-1, sd);
-
- name[0] = '\0'; //If you don't pass a second word, name is left as garbage, most definitely not a blank name! [Skotlex]
- if (!message || !*message || sscanf(message, "%23s %23[^\n]", char_name, name) < 1) {
- clif_displaymessage(sd->fd,"Usage: #fakename <char name> <fake name>.");
- clif_displaymessage(sd->fd,"Or: #fakename <char name> to disable.");
- return 0;
- }
-
- if(!(pl_sd = map_nick2sd(char_name))) {
- clif_displaymessage(sd->fd,"Character not found.");
- return -1;
- }
-
- if(strlen(name) < 1 || !name) {
- if(strlen(pl_sd->fakename) > 1) {
- pl_sd->fakename[0]='\0';
- clif_charnameack(0, &pl_sd->bl);
- clif_displaymessage(sd->fd,"Returned to real name.");
- } else {
- clif_displaymessage(sd->fd,"Character does not has a fake name.");
- }
- return 0;
- }
-
- if(strlen(name) < 2) {
- clif_displaymessage(sd->fd,"Fake name must be at least two characters.");
- return 0;
- }
-
- memcpy(pl_sd->fakename,name, NAME_LENGTH-1);
- clif_charnameack(0, &pl_sd->bl);
- clif_displaymessage(sd->fd,"Fake name enabled.");
-
- return 0;
-}
-
-
-/*==========================================
- * #baselvl <#> <nickname>
- * Transferred by: Kevin
- *------------------------------------------
-*/
-int charcommand_baselevel(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- int level = 0, i, status_point=0;
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
- clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #baselvl <#> <nickname>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change base level only lower or same gm level
-
- if (level > 0) {
- if (pl_sd->status.base_level == pc_maxbaselv(sd)) { // check for max level by Valaris
- clif_displaymessage(fd, msg_table[91]); // Character's base level can't go any higher.
- return 0;
- } // End Addition
- if ((unsigned int)level > pc_maxbaselv(pl_sd) ||
- pl_sd->status.base_level > pc_maxbaselv(pl_sd) -level)
- level = pc_maxbaselv(pl_sd) - pl_sd->status.base_level;
- for (i = 1; i <= level; i++)
- status_point += (pl_sd->status.base_level + i + 14) / 5;
- if (pl_sd->status.status_point > USHRT_MAX - status_point)
- pl_sd->status.status_point = USHRT_MAX;
- else
- pl_sd->status.status_point += status_point;
- pl_sd->status.base_level += (unsigned int)level;
- clif_updatestatus(pl_sd, SP_BASELEVEL);
- clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
- clif_updatestatus(pl_sd, SP_STATUSPOINT);
- status_calc_pc(pl_sd, 0);
- status_percent_heal(&pl_sd->bl, 100, 100);
- clif_misceffect(&pl_sd->bl, 0);
- clif_displaymessage(fd, msg_table[65]); // Character's base level raised.
- } else {
- if (pl_sd->status.base_level == 1) {
- clif_displaymessage(fd, msg_table[193]); // Character's base level can't go any lower.
- return -1;
- }
- level *= -1;
- if ((unsigned int)level >= pl_sd->status.base_level)
- level = pl_sd->status.base_level -1;
- if (pl_sd->status.status_point > 0) {
- for (i = 0; i > -level; i--)
- status_point += (pl_sd->status.base_level +i + 14) / 5;
- if (pl_sd->status.status_point < status_point)
- pc_resetstate(pl_sd);
- if (pl_sd->status.status_point < status_point)
- pl_sd->status.status_point = 0;
- else
- pl_sd->status.status_point -= status_point;
- clif_updatestatus(pl_sd, SP_STATUSPOINT);
- } // to add: remove status points from stats
- pl_sd->status.base_level -= (unsigned int)level;
- clif_updatestatus(pl_sd, SP_BASELEVEL);
- clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
- status_calc_pc(pl_sd, 0);
- clif_displaymessage(fd, msg_table[66]); // Character's base level lowered.
- }
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0; //正常終了
-}
-
-/*==========================================
- * #jlvl <#> <nickname>
- * Transferred by: Kevin
- *------------------------------------------
- */
-int charcommand_joblevel(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- int level = 0;
- //転生や養子の場合の元の職業を算出する
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
- clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #joblvl <#> <nickname>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job level only lower or same gm level
- if (level > 0) {
- if (pl_sd->status.job_level == pc_maxjoblv(pl_sd)) {
- clif_displaymessage(fd, msg_table[67]); // Character's job level can't go any higher.
- return -1;
- }
- if ((unsigned int)level > pc_maxjoblv(pl_sd) ||
- pl_sd->status.job_level > pc_maxjoblv(pl_sd) -level)
- level = pc_maxjoblv(pl_sd) - pl_sd->status.job_level;
- pl_sd->status.job_level += (unsigned int)level;
- clif_updatestatus(pl_sd, SP_JOBLEVEL);
- clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
-
- if (pl_sd->status.skill_point > USHRT_MAX - level)
- pl_sd->status.skill_point = USHRT_MAX;
- else
- pl_sd->status.skill_point += level;
- clif_updatestatus(pl_sd, SP_SKILLPOINT);
- status_calc_pc(pl_sd, 0);
- clif_misceffect(&pl_sd->bl, 1);
- clif_displaymessage(fd, msg_table[68]); // character's job level raised.
- } else {
- if (pl_sd->status.job_level == 1) {
- clif_displaymessage(fd, msg_table[194]); // Character's job level can't go any lower.
- return -1;
- }
- level*=-1;
- if ((unsigned int)level >= pl_sd->status.job_level)
- level = pl_sd->status.job_level-1;
- pl_sd->status.job_level -= (unsigned int)level;
- clif_updatestatus(pl_sd, SP_JOBLEVEL);
- clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
- if (pl_sd->status.skill_point < level)
- pc_resetskill(pl_sd, 0); //Need more skill points to substract
- if (pl_sd->status.skill_point < level)
- pl_sd->status.skill_point = 0;
- else
- pl_sd->status.skill_point -= level;
- clif_updatestatus(pl_sd, SP_SKILLPOINT);
- status_calc_pc(pl_sd, 0);
- clif_displaymessage(fd, msg_table[69]); // Character's job level lowered.
- }
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-
-/*==========================================
- * #questskill <skill_#> <nickname>
- * Transferred by: Kevin
- *------------------------------------------
- */
-int charcommand_questskill(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- int skill_id = 0;
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
- clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: #questskill <#:0+> <nickname>).");
- return -1;
- }
-
- if (skill_id >= 0 && skill_id < MAX_SKILL_DB) {
- if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (pc_checkskill(pl_sd, skill_id) == 0) {
- pc_skill(pl_sd, skill_id, 1, 0);
- clif_displaymessage(fd, msg_table[199]); // This player has learned the skill.
- } else {
- clif_displaymessage(fd, msg_table[200]); // This player already has this quest skill.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
- return -1;
- }
-
- return 0;
-}
-
-
-/*==========================================
- * #lostskill <skill_#> <nickname>
- * Transferred by: Kevin
- *------------------------------------------
- */
-int charcommand_lostskill(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- int skill_id = 0;
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
- clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charlostskill <#:0+> <char_name>).");
- return -1;
- }
-
- if (skill_id >= 0 && skill_id < MAX_SKILL) {
- if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (pc_checkskill(pl_sd, skill_id) > 0) {
- pl_sd->status.skill[skill_id].lv = 0;
- pl_sd->status.skill[skill_id].flag = 0;
- clif_skillinfoblock(pl_sd);
- clif_displaymessage(fd, msg_table[202]); // This player has forgotten the skill.
- } else {
- clif_displaymessage(fd, msg_table[203]); // This player doesn't have this quest skill.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Character Skill Reset
- *------------------------------------------
- */
-int charcommand_skreset(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- char tmp_cmdoutput[1024];
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: @charskreset <charname>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset skill points only lower or same gm level
- pc_resetskill(pl_sd,1);
- sprintf(tmp_cmdoutput, msg_table[206], player); // '%s' skill points reseted!
- clif_displaymessage(fd, tmp_cmdoutput);
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Character Stat Reset
- *------------------------------------------
- */
-int charcommand_streset(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- char tmp_cmdoutput[1024];
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: @charstreset <charname>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset stats points only lower or same gm level
- pc_resetstate(pl_sd);
- sprintf(tmp_cmdoutput, msg_table[207], player); // '%s' stats points reseted!
- clif_displaymessage(fd, tmp_cmdoutput);
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Character Model by chbrules
- *------------------------------------------
- */
-int charcommand_model(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- int hair_style = 0, hair_color = 0, cloth_color = 0;
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- char tmp_cmdoutput[1024];
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %d %d %23[^\n]", &hair_style, &hair_color, &cloth_color, player) < 4 || hair_style < 0 || hair_color < 0 || cloth_color < 0) {
- sprintf(tmp_cmdoutput, "Please, enter a valid model and a player name (usage: @charmodel <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d> <name>).",
- MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
- clif_displaymessage(fd, tmp_cmdoutput);
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE &&
- hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR &&
- cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
- /* Removed this check for being too strange. [Skotlex]
- if (cloth_color != 0 &&
- pl_sd->status.sex == 1 &&
- (pl_sd->status.class_ == JOB_ASSASSIN || pl_sd->status.class_ == JOB_ROGUE)) {
- clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
- return -1;
- } else {
- */
- pc_changelook(pl_sd, LOOK_HAIR, hair_style);
- pc_changelook(pl_sd, LOOK_HAIR_COLOR, hair_color);
- pc_changelook(pl_sd, LOOK_CLOTHES_COLOR, cloth_color);
- clif_displaymessage(fd, msg_table[36]); // Appearence changed.
-// }
- } else {
- clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Character Skill Point (Rewritten by [Yor])
- *------------------------------------------
- */
-int charcommand_skpoint(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- int new_skill_point;
- int point = 0;
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
- clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charskpoint <amount> <name>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (point > 0 && pl_sd->status.skill_point > USHRT_MAX - point)
- new_skill_point = USHRT_MAX;
- else if (point < 0 && pl_sd->status.skill_point < -point)
- new_skill_point = 0;
- else
- new_skill_point = pl_sd->status.skill_point + point;
- if (new_skill_point != (int)pl_sd->status.skill_point) {
- pl_sd->status.skill_point = new_skill_point;
- clif_updatestatus(pl_sd, SP_SKILLPOINT);
- clif_displaymessage(fd, msg_table[209]); // Character's number of skill points changed!
- } else {
- if (point < 0)
- clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
- else
- clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * Character Status Point (rewritten by [Yor])
- *------------------------------------------
- */
-int charcommand_stpoint(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- struct map_session_data *pl_sd;
- char player[NAME_LENGTH];
- int new_status_point;
- int point = 0;
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
- clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charstpoint <amount> <name>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(player)) != NULL) {
- if (point > 0 && pl_sd->status.status_point > USHRT_MAX - point)
- new_status_point = USHRT_MAX;
- else if (point < 0 && pl_sd->status.status_point < -point)
- new_status_point = 0;
- else
- new_status_point = pl_sd->status.status_point + point;
- if (new_status_point != (int)pl_sd->status.status_point) {
- pl_sd->status.status_point = new_status_point;
- clif_updatestatus(pl_sd, SP_STATUSPOINT);
- clif_displaymessage(fd, msg_table[210]); // Character's number of status points changed!
- } else {
- if (point < 0)
- clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
- else
- clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * charchangesex command (usage: charchangesex <player_name>)
- *------------------------------------------
- */
-int charcommand_changesex(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char player[NAME_LENGTH];
- nullpo_retr(-1, sd);
-
- if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: @charchangesex <name>).");
- return -1;
- }
-
- // check player name
- if (strlen(player) < 4) {
- clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
- return -1;
- } else if (strlen(player) > 23) {
- clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
- return -1;
- } else {
- chrif_char_ask_name(sd->status.account_id, player, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex
- clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
- }
-
- return 0;
-}
-
-/*==========================================
- * Feel (SG save map) Reset
- *------------------------------------------
- */
-int charcommand_feelreset(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char character[NAME_LENGTH];
- char output[200];
- struct map_session_data *pl_sd;
-
- malloc_tsetdword(character, '\0', sizeof(character));
- malloc_tsetdword(output, '\0', sizeof(output));
-
- if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
- clif_displaymessage(fd, "Please, enter a player name (usage: #feelreset <charname>).");
- return -1;
- }
-
- if ((pl_sd = map_nick2sd(character)) != NULL) {
- if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
- pc_resetfeel(pl_sd);
- sprintf(output, msg_table[267], character); // '%s' designated maps reseted!
- clif_displaymessage(fd, output);
- } else {
- clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
- return -1;
- }
- } else {
- clif_displaymessage(fd, msg_table[3]); // Character not found.
- return -1;
- }
-
- return 0;
-}
-
-/*==========================================
- * #help - Char commands [Kayla]
- *------------------------------------------
- */
-int charcommand_help(
- const int fd, struct map_session_data* sd,
- const char* command, const char* message)
-{
- char buf[2048], w1[2048], w2[2048];
- int i, gm_level;
- FILE* fp;
- nullpo_retr(-1, sd);
-
- malloc_tsetdword(buf, '\0', sizeof(buf));
-
- if ((fp = fopen(charhelp_txt, "r")) != NULL) {
- clif_displaymessage(fd, msg_table[26]); /* Help commands: */
- gm_level = pc_isGM(sd);
- while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
- if (buf[0] == '/' && buf[1] == '/')
- continue;
- for (i = 0; buf[i] != '\0'; i++) {
- if (buf[i] == '\r' || buf[i] == '\n') {
- buf[i] = '\0';
- break;
- }
- }
- if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
- clif_displaymessage(fd, buf);
- else if (gm_level >= atoi(w1))
- clif_displaymessage(fd, w2);
- }
- fclose(fp);
- } else {
- clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
- return -1;
- }
-
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <limits.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "charcommand.h"
+#include "atcommand.h"
+
+static char command_symbol = '#';
+
+extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+#define CCMD_FUNC(x) int charcommand_ ## x (const int fd, struct map_session_data* sd, const char* command, const char* message)
+CCMD_FUNC(jobchange);
+CCMD_FUNC(petrename);
+CCMD_FUNC(petfriendly);
+CCMD_FUNC(stats);
+CCMD_FUNC(option);
+CCMD_FUNC(save);
+CCMD_FUNC(stats_all);
+CCMD_FUNC(reset);
+CCMD_FUNC(spiritball);
+CCMD_FUNC(itemlist);
+CCMD_FUNC(effect);
+CCMD_FUNC(storagelist);
+CCMD_FUNC(item);
+CCMD_FUNC(warp);
+CCMD_FUNC(zeny);
+CCMD_FUNC(fakename);
+CCMD_FUNC(baselevel);
+CCMD_FUNC(joblevel);
+CCMD_FUNC(questskill);
+CCMD_FUNC(lostskill);
+CCMD_FUNC(skreset);
+CCMD_FUNC(streset);
+CCMD_FUNC(model);
+CCMD_FUNC(stpoint);
+CCMD_FUNC(skpoint);
+CCMD_FUNC(changesex);
+CCMD_FUNC(feelreset);
+CCMD_FUNC(help);
+
+
+/*==========================================
+ *CharCommandInfo charcommand_info[]構造体の定義
+ *------------------------------------------
+ */
+
+// First char of commands is configured in charcommand_athena.conf. Leave # in this list for default value.
+// to set default level, read charcommand_athena.conf first please.
+static CharCommandInfo charcommand_info[] = {
+ { CharCommandJobChange, "#job", 60, charcommand_jobchange },
+ { CharCommandJobChange, "#jobchange", 60, charcommand_jobchange },
+ { CharCommandPetRename, "#petrename", 50, charcommand_petrename },
+ { CharCommandPetFriendly, "#petfriendly", 50, charcommand_petfriendly },
+ { CharCommandStats, "#stats", 40, charcommand_stats },
+ { CharCommandOption, "#option", 60, charcommand_option },
+ { CharCommandReset, "#reset", 60, charcommand_reset },
+ { CharCommandSave, "#save", 60, charcommand_save },
+ { CharCommandStatsAll, "#statsall", 40, charcommand_stats_all },
+ { CharCommandSpiritball, "#spiritball", 40, charcommand_spiritball },
+ { CharCommandItemList, "#itemlist", 40, charcommand_itemlist },
+ { CharCommandEffect, "#effect", 40, charcommand_effect },
+ { CharCommandStorageList, "#storagelist", 40, charcommand_storagelist },
+ { CharCommandItem, "#item", 60, charcommand_item },
+ { CharCommandWarp, "#warp", 60, charcommand_warp },
+ { CharCommandWarp, "#rura", 60, charcommand_warp },
+ { CharCommandWarp, "#rura+", 60, charcommand_warp },
+ { CharCommandZeny, "#zeny", 60, charcommand_zeny },
+ { CharCommandFakeName, "#fakename", 20, charcommand_fakename},
+
+ //*********************************Recently added commands*********************************************
+ { CharCommandBaseLevel, "#baselvl", 20, charcommand_baselevel},
+ { CharCommandBaseLevel, "#blvl", 60, charcommand_baselevel},
+ { CharCommandBaseLevel, "#baselvlup", 60, charcommand_baselevel},
+ { CharCommandJobLevel, "#joblvl", 60, charcommand_joblevel},
+ { CharCommandJobLevel, "#jlvl", 60, charcommand_joblevel},
+ { CharCommandJobLevel, "#joblvlup", 60, charcommand_joblevel},
+ { CharCommandQuestSkill, "#questskill", 60, charcommand_questskill },
+ { CharCommandLostSkill, "#lostskill", 60, charcommand_lostskill },
+ { CharCommandSkReset, "#skreset", 60, charcommand_skreset },
+ { CharCommandStReset, "#streset", 60, charcommand_streset },
+ { CharCommandModel, "#model", 50, charcommand_model },
+ { CharCommandSKPoint, "#skpoint", 60, charcommand_skpoint },
+ { CharCommandSTPoint, "#stpoint", 60, charcommand_stpoint },
+ { CharCommandChangeSex, "#changesex", 60, charcommand_changesex },
+ { CharCommandFeelReset, "#feelreset", 60, charcommand_feelreset },
+ { CharCommandHelp, "#help", 20, charcommand_help },
+// add new commands before this line
+ { CharCommand_Unknown, NULL, 1, NULL }
+};
+
+int get_charcommand_level(const CharCommandType type) {
+ int i;
+
+ for (i = 0; charcommand_info[i].type != CharCommand_None; i++)
+ if (charcommand_info[i].type == type)
+ return charcommand_info[i].level;
+
+ return 100; // 100: command can not be used
+}
+
+CharCommandType
+charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl) {
+ CharCommandInfo info;
+ CharCommandType type;
+
+ malloc_set(&info, 0, sizeof(info));
+
+ type = charcommand(sd, gmlvl, str, &info);
+ if (type != CharCommand_None) {
+ char command[100];
+ char output[200];
+ const char* p = str;
+
+ if (map[sd->bl.m].nocommand &&
+ gmlvl < map[sd->bl.m].nocommand)
+ { //Command not allowed on this map.
+ sprintf(output, msg_txt(143));
+ clif_displaymessage(fd, output);
+ return AtCommand_None;
+ }
+
+ malloc_tsetdword(command, '\0', sizeof(command));
+ malloc_tsetdword(output, '\0', sizeof(output));
+ while (*p && !isspace(*p))
+ p++;
+ if (p - str >= sizeof(command)) // too long
+ return CharCommand_Unknown;
+ strncpy(command, str, p - str);
+ while (isspace(*p))
+ p++;
+
+ if (type == CharCommand_Unknown || info.proc == NULL) {
+ snprintf(output, sizeof(output),msg_txt(153), command); // %s is Unknown Command.
+ clif_displaymessage(fd, output);
+ } else {
+ if (info.proc(fd, sd, command, p) != 0) {
+ // Command can not be executed
+ snprintf(output, sizeof(output), msg_txt(154), command); // %s failed.
+ clif_displaymessage(fd, output);
+ }
+ }
+
+ return info.type;
+ }
+
+ return CharCommand_None;
+}
+
+/*==========================================
+ *is_charcommand @コマンドに存在するかどうか確認する
+ *------------------------------------------
+ */
+CharCommandType
+is_charcommand(const int fd, struct map_session_data* sd, const char* message) {
+ const char* str = message;
+ int s_flag = 0;
+
+ nullpo_retr(CharCommand_None, sd);
+
+ if (!message || !*message)
+ return CharCommand_None;
+
+ str += strlen(sd->status.name);
+ while (*str && (isspace(*str) || (s_flag == 0 && *str == ':'))) {
+ if (*str == ':')
+ s_flag = 1;
+ str++;
+ }
+
+ if (!*str)
+ return CharCommand_None;
+
+ return charcommand_sub(fd,sd,str,pc_isGM(sd));
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+CharCommandType charcommand(struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info) {
+ char* p = (char *)message;
+
+ if (!info)
+ return CharCommand_None;
+ if (battle_config.atc_gmonly != 0 && !level) // level = pc_isGM(sd)
+ return CharCommand_None;
+ if (!p || !*p) {
+ ShowError("char command message is empty\n");
+ return CharCommand_None;
+ }
+
+ if(p[0] == '|')
+ p += 3;
+
+ if (*p == command_symbol) { // check first char, try to skip |00 (or something else) [Lance]
+ char command[101];
+ int i = 0;
+ malloc_set(info, 0, sizeof(CharCommandInfo));
+ sscanf(p, "%100s", command);
+ command[sizeof(command)-1] = '\0';
+
+ while (charcommand_info[i].type != CharCommand_Unknown) {
+ if (strcmpi(command+1, charcommand_info[i].command+1) == 0 && level >= charcommand_info[i].level) {
+ p[0] = charcommand_info[i].command[0]; // set correct first symbol for after.
+ break;
+ }
+ i++;
+ }
+
+ if (charcommand_info[i].type == CharCommand_Unknown) {
+ // doesn't return Unknown if player is normal player (display the text, not display: unknown command)
+ if (level == 0)
+ return CharCommand_None;
+ else
+ return CharCommand_Unknown;
+ } else if((log_config.gm) && (charcommand_info[i].level >= log_config.gm)) {
+ log_atcommand(sd, message);
+ }
+ memcpy(info, &charcommand_info[i], sizeof charcommand_info[i]);
+ } else {
+ return CharCommand_None;
+ }
+
+ return info->type;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static CharCommandInfo* get_charcommandinfo_byname(const char* name) {
+ int i;
+
+ for (i = 0; charcommand_info[i].type != CharCommand_Unknown; i++)
+ if (strcmpi(charcommand_info[i].command + 1, name) == 0)
+ return &charcommand_info[i];
+
+ return NULL;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_config_read(const char *cfgName) {
+ char line[1024], w1[1024], w2[1024];
+ CharCommandInfo* p;
+ FILE* fp;
+
+ if ((fp = fopen(cfgName, "r")) == NULL) {
+ ShowError("CharCommands configuration file not found: %s\n", cfgName);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line)-1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ if (sscanf(line, "%1023[^:]:%1023s", w1, w2) != 2)
+ continue;
+ p = get_charcommandinfo_byname(w1);
+ if (p != NULL) {
+ p->level = atoi(w2);
+ if (p->level > 100)
+ p->level = 100;
+ else if (p->level < 0)
+ p->level = 0;
+ }
+
+ if (strcmpi(w1, "import") == 0)
+ charcommand_config_read(w2);
+ else if (strcmpi(w1, "command_symbol") == 0 && w2[0] > 31 &&
+ w2[0] != '/' && // symbol of standard ragnarok GM commands
+ w2[0] != '%' && // symbol of party chat speaking
+ w2[0] != '$' && // symbol of guild chat speaking
+ w2[0] != '@') // symbol of atcommand
+ command_symbol = w2[0];
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+/*==========================================
+ * 対象キャラクターを転職させる upper指定で転生や養子も可能
+ *------------------------------------------
+ */
+int charcommand_jobchange(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[100];
+ struct map_session_data* pl_sd;
+ int job = 0, upper = -1;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+
+ if (!message || !*message) {
+ clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
+ return -1;
+ }
+
+ if (sscanf(message, "%d %d %99[^\n]", &job, &upper, character) < 3) { //upper指定してある
+ upper = -1;
+ if (sscanf(message, "%d %99[^\n]", &job, character) < 2) { //upper指定してない上に何か足りない
+ clif_displaymessage(fd, "Please, enter a job and a player name (usage: #job/#jobchange <job ID> <char name>).");
+ return -1;
+ }
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ int j;
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job only to lower or same level
+ if ((job >= 0 && job < MAX_PC_CLASS)) {
+ for (j=0; j < MAX_INVENTORY; j++) {
+ if(pl_sd->status.inventory[j].nameid>0 && pl_sd->status.inventory[j].equip!=0)
+ pc_unequipitem(pl_sd, j, 3);
+ }
+ if (pc_jobchange(pl_sd, job, upper) == 0)
+ clif_displaymessage(fd, msg_table[48]); // Character's job changed.
+ else {
+ clif_displaymessage(fd, msg_table[192]); // Impossible to change the character's job.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[49]); // Invalid job ID.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_petrename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+ struct pet_data *pd;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #petrename <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) == NULL) {
+ clif_displaymessage(fd, msg_txt(3)); // Character not found.
+ return -1;
+ }
+
+ if (!pl_sd->status.pet_id || !pl_sd->pd) {
+ clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet.
+ return -1;
+ }
+
+ pd = pl_sd->pd;
+
+ if (pd->pet.rename_flag) {
+ clif_displaymessage(fd, msg_table[190]); // This player can already rename his/her pet.
+ return -1;
+ }
+ pd->pet.rename_flag = 0;
+ intif_save_petdata(pl_sd->status.account_id, &pd->pet);
+ clif_send_petstatus(pl_sd);
+ clif_displaymessage(fd, msg_table[189]); // This player can now rename his/her pet.
+ return 0;
+}
+
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_petfriendly(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int friendly = 0;
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+ struct pet_data *pd;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+ if (!message || !*message || sscanf(message,"%d %23s",&friendly,character) < 2) {
+ clif_displaymessage(fd, "Please, enter a valid value (usage: "
+ "#petfriendly <0-1000> <player>).");
+ return -1;
+ }
+
+ if (((pl_sd = map_nick2sd(character)) == NULL) ||
+ pc_isGM(sd)<pc_isGM(pl_sd)) {
+ clif_displaymessage(fd, msg_txt(3)); // Character not found.
+ return -1;
+ }
+
+ if (!pl_sd->status.pet_id || !pl_sd->pd) {
+ clif_displaymessage(fd, msg_table[191]); // Sorry, but this player has no pet.
+ return -1;
+ }
+
+ if (friendly < 0 || friendly > 1000) {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+
+ pd = pl_sd->pd;
+ if (friendly == pd->pet.intimate) {
+ clif_displaymessage(fd, msg_table[183]); // Pet friendly is already the good value.
+ return -1;
+ }
+
+ pd->pet.intimate = friendly;
+ clif_send_petstatus(pl_sd);
+ clif_pet_emotion(pd,0);
+ clif_displaymessage(pl_sd->fd, msg_table[182]); // Pet friendly value changed!
+ clif_displaymessage(sd->fd, msg_table[182]); // Pet friendly value changed!
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_stats(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char job_jobname[100];
+ char output[200];
+ struct map_session_data *pl_sd;
+ int i;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+ malloc_tsetdword(job_jobname, '\0', sizeof(job_jobname));
+ malloc_tsetdword(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #stats <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ struct {
+ const char* format;
+ int value;
+ } output_table[] = {
+ { "Base Level - %d", 0 },
+ { NULL, 0 },
+ { "Hp - %d", 0 },
+ { "MaxHp - %d", 0 },
+ { "Sp - %d", 0 },
+ { "MaxSp - %d", 0 },
+ { "Str - %3d", 0 },
+ { "Agi - %3d", 0 },
+ { "Vit - %3d", 0 },
+ { "Int - %3d", 0 },
+ { "Dex - %3d", 0 },
+ { "Luk - %3d", 0 },
+ { "Zeny - %d", 0 },
+ { NULL, 0 }
+ };
+ //direct array initialization with variables is not standard C compliant.
+ output_table[0].value = pl_sd->status.base_level;
+ output_table[1].format = job_jobname;
+ output_table[1].value = pl_sd->status.job_level;
+ output_table[2].value = pl_sd->status.hp;
+ output_table[3].value = pl_sd->status.max_hp;
+ output_table[4].value = pl_sd->status.sp;
+ output_table[5].value = pl_sd->status.max_sp;
+ output_table[6].value = pl_sd->status.str;
+ output_table[7].value = pl_sd->status.agi;
+ output_table[8].value = pl_sd->status.vit;
+ output_table[9].value = pl_sd->status.int_;
+ output_table[10].value = pl_sd->status.dex;
+ output_table[11].value = pl_sd->status.luk;
+ output_table[12].value = pl_sd->status.zeny;
+ sprintf(job_jobname, "Job - %s %s", job_name(pl_sd->status.class_), "(level %d)");
+ sprintf(output, msg_table[53], pl_sd->status.name); // '%s' stats:
+ clif_displaymessage(fd, output);
+ for (i = 0; output_table[i].format != NULL; i++) {
+ sprintf(output, output_table[i].format, output_table[i].value);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Reset
+ *------------------------------------------
+ */
+int charcommand_reset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char output[200];
+ struct map_session_data *pl_sd;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+ malloc_tsetdword(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #reset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
+ pc_resetstate(pl_sd);
+ pc_resetskill(pl_sd,1);
+ sprintf(output, msg_table[208], character); // '%s' skill and stats points reseted!
+ clif_displaymessage(fd, output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_option(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ int opt1 = 0, opt2 = 0, opt3 = 0;
+ struct map_session_data* pl_sd;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+
+ if (!message || !*message ||
+ sscanf(message, "%d %d %d %23[^\n]", &opt1, &opt2, &opt3, character) < 4 ||
+ opt1 < 0 || opt2 < 0 || opt3 < 0) {
+ clif_displaymessage(fd, "Please, enter valid options and a player name (usage: #option <param1> <param2> <param3> <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change option only to lower or same level
+ pl_sd->sc.opt1 = opt1;
+ pl_sd->sc.opt2 = opt2;
+ pc_setoption(pl_sd, opt3);
+ clif_displaymessage(fd, msg_table[58]); // Character's options changed.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int charcommand_save(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ char character[NAME_LENGTH];
+ struct map_session_data* pl_sd;
+ int x = 0, y = 0;
+ int m;
+
+ malloc_tsetdword(map_name, '\0', sizeof(map_name));
+ malloc_tsetdword(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4 || x < 0 || y < 0) {
+ clif_displaymessage(fd, "Please, enter a valid save point and a player name (usage: #save <map> <x> <y> <charname>).");
+ return -1;
+ }
+
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change save point only to lower or same gm level
+ m = map_mapname2mapid(map_name);
+ if (m < 0 && !mapindex_name2id(map_name)) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ } else {
+ //FIXME: What do you do if the map is in another map server with the nowarpto flag?
+ if (m>=0 && map[m].flag.nosave && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to set this map as a save map.");
+ return -1;
+ }
+ if (m>=0)
+ pc_setsavepoint(pl_sd, map[m].index, x, y);
+ else
+ pc_setsavepoint(pl_sd, mapindex_name2id(map_name), x, y);
+ clif_displaymessage(fd, msg_table[57]); // Character's respawn point changed.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+//** Character Stats All by fritz
+int charcommand_stats_all(const int fd, struct map_session_data* sd, const char* command, const char* message)
+{
+ char output[1024], gmlevel[1024];
+ int i;
+ int count, users;
+ struct map_session_data *pl_sd, **pl_allsd;
+
+ malloc_tsetdword(output, '\0', sizeof(output));
+ malloc_tsetdword(gmlevel, '\0', sizeof(gmlevel));
+
+ count = 0;
+ pl_allsd = map_getallusers(&users);
+ for(i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i]))
+ {
+ if (pc_isGM(pl_sd) > 0)
+ sprintf(gmlevel, "| GM Lvl: %d", pc_isGM(pl_sd));
+ else
+ sprintf(gmlevel, " ");
+
+ sprintf(output, "Name: %s | BLvl: %d | Job: %s (Lvl: %d) | HP: %d/%d | SP: %d/%d", pl_sd->status.name, pl_sd->status.base_level, job_name(pl_sd->status.class_), pl_sd->status.job_level, pl_sd->status.hp, pl_sd->status.max_hp, pl_sd->status.sp, pl_sd->status.max_sp);
+ clif_displaymessage(fd, output);
+ sprintf(output, "STR: %d | AGI: %d | VIT: %d | INT: %d | DEX: %d | LUK: %d | Zeny: %d %s", pl_sd->status.str, pl_sd->status.agi, pl_sd->status.vit, pl_sd->status.int_, pl_sd->status.dex, pl_sd->status.luk, pl_sd->status.zeny, gmlevel);
+ clif_displaymessage(fd, output);
+ clif_displaymessage(fd, "--------");
+ count++;
+ }
+ }
+
+ if (count == 0)
+ clif_displaymessage(fd, msg_table[28]); // No player found.
+ else if (count == 1)
+ clif_displaymessage(fd, msg_table[29]); // 1 player found.
+ else {
+ sprintf(output, msg_table[30], count); // %d players found.
+ clif_displaymessage(fd, output);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * CharSpiritBall Function by PalasX
+ *------------------------------------------
+ */
+int charcommand_spiritball(const int fd, struct map_session_data* sd,const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char character[NAME_LENGTH];
+ int spirit = 0;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+
+ if(!message || !*message || sscanf(message, "%d %23[^\n]", &spirit, character) < 2 || spirit < 0 || spirit > 1000) {
+ clif_displaymessage(fd, "Usage: @spiritball <number: 0-1000>) <CHARACTER_NAME>.");
+ return -1;
+ }
+
+ if((pl_sd = map_nick2sd(character)) != NULL) {
+ if (spirit >= 0 && spirit <= 0x7FFF) {
+ if (pl_sd->spiritball != spirit || spirit > 999) {
+ if (pl_sd->spiritball > 0)
+ pc_delspiritball(pl_sd, pl_sd->spiritball, 1);
+ pl_sd->spiritball = spirit;
+ clif_spiritball(pl_sd);
+ // no message, player can look the difference
+ if (spirit > 1000)
+ clif_displaymessage(fd, msg_table[204]); // WARNING: more than 1000 spiritballs can CRASH your server and/or client!
+ } else {
+ clif_displaymessage(fd, msg_table[205]); // You already have this number of spiritballs.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ return 0;
+}
+
+/*==========================================
+ * #itemlist <character>: Displays the list of a player's items.
+ *------------------------------------------
+ */
+int
+charcommand_itemlist(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, equip, count, counter, counter2;
+ char character[NAME_LENGTH], output[200], equipstr[100], outputtmp[200];
+ struct item *i_item; //Current inventory item.
+ nullpo_retr(-1, sd);
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+ malloc_tsetdword(output, '\0', sizeof(output));
+ malloc_tsetdword(equipstr, '\0', sizeof(equipstr));
+ malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ i_item = &pl_sd->status.inventory[i];
+ if (pl_sd->status.inventory[i].nameid > 0 && (item_data = itemdb_exists(i_item->nameid)) != NULL) {
+ counter = counter + i_item->amount;
+ count++;
+ if (count == 1) {
+ sprintf(output, "------ Items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, output);
+ }
+ if ((equip = i_item->equip)) {
+ strcpy(equipstr, "| equiped: ");
+ if (equip & EQP_GARMENT)
+ strcat(equipstr, "robe/gargment, ");
+ if (equip & EQP_ACC_L)
+ strcat(equipstr, "left accessory, ");
+ if (equip & EQP_ARMOR)
+ strcat(equipstr, "body/armor, ");
+ if ((equip & EQP_ARMS) == EQP_HAND_R)
+ strcat(equipstr, "right hand, ");
+ if ((equip & EQP_ARMS) == EQP_HAND_L)
+ strcat(equipstr, "left hand, ");
+ if ((equip & EQP_ARMS) == EQP_ARMS)
+ strcat(equipstr, "both hands, ");
+ if (equip & EQP_SHOES)
+ strcat(equipstr, "feet, ");
+ if (equip & EQP_ACC_R)
+ strcat(equipstr, "right accessory, ");
+ if ((equip & EQP_HELM) == EQP_HEAD_LOW)
+ strcat(equipstr, "lower head, ");
+ if ((equip & EQP_HELM) == EQP_HEAD_TOP)
+ strcat(equipstr, "top head, ");
+ if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_TOP))
+ strcat(equipstr, "lower/top head, ");
+ if ((equip & EQP_HELM) == EQP_HEAD_MID)
+ strcat(equipstr, "mid head, ");
+ if ((equip & EQP_HELM) == (EQP_HEAD_LOW|EQP_HEAD_MID))
+ strcat(equipstr, "lower/mid head, ");
+ if ((equip & EQP_HELM) == EQP_HELM)
+ strcat(equipstr, "lower/mid/top head, ");
+ // remove final ', '
+ equipstr[strlen(equipstr) - 2] = '\0';
+ } else
+ malloc_tsetdword(equipstr, '\0', sizeof(equipstr));
+ if (i_item->refine)
+ sprintf(output, "%d %s %+d (%s %+d, id: %d) %s", i_item->amount, item_data->name, i_item->refine, item_data->jname, i_item->refine, i_item->nameid, equipstr);
+ else
+ sprintf(output, "%d %s (%s, id: %d) %s", i_item->amount, item_data->name, item_data->jname, i_item->nameid, equipstr);
+ clif_displaymessage(fd, output);
+ malloc_tsetdword(output, '\0', sizeof(output));
+ counter2 = 0;
+
+ if(i_item->card[0]==CARD0_PET) { //pet eggs
+ if (i_item->card[3])
+ sprintf(outputtmp, " -> (pet egg, pet id: %u, named)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2]));
+ else
+ sprintf(outputtmp, " -> (pet egg, pet id: %u, unnamed)", (unsigned int)MakeDWord(i_item->card[1], i_item->card[2]));
+ strcat(output, outputtmp);
+ } else
+ if(i_item->card[0]==CARD0_FORGE) { //forged items.
+ sprintf(outputtmp, " -> (crafted item, creator id: %u, star crumbs %d, element %d)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3]), i_item->card[1]>>8, i_item->card[1]&0x0f);
+ } else
+ if(i_item->card[0]==CARD0_CREATE) { //created items.
+ sprintf(outputtmp, " -> (produced item, creator id: %u)", (unsigned int)MakeDWord(i_item->card[2], i_item->card[3]));
+ strcat(output, outputtmp);
+ } else //Normal slots
+ for (j = 0; j < item_data->slot; j++) {
+ if (pl_sd->status.inventory[i].card[j]) {
+ if ((item_temp = itemdb_exists(i_item->card[j])) != NULL) {
+ if (output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(output, outputtmp);
+ }
+ }
+ }
+ if (output[0] != '\0') {
+ output[strlen(output) - 2] = ')';
+ output[strlen(output) - 1] = '\0';
+ clif_displaymessage(fd, output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found on this player.");
+ else {
+ sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #effect by [MouseJstr]
+ *
+ * Create a effect localized on another character
+ *------------------------------------------
+ */
+int
+charcommand_effect(const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd = NULL;
+ char target[255];
+ int type = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %s", &type, target) != 2) {
+ clif_displaymessage(fd, "usage: #effect <type+> <target>.");
+ return -1;
+ }
+
+ if((pl_sd=map_nick2sd((char *) target)) == NULL)
+ return -1;
+
+ clif_specialeffect(&pl_sd->bl, type, AREA);
+ clif_displaymessage(fd, msg_table[229]); // Your effect has changed.
+
+ return 0;
+}
+
+/*==========================================
+ * #storagelist <character>: Displays the items list of a player's storage.
+ *------------------------------------------
+ */
+int
+charcommand_storagelist(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct storage *stor;
+ struct map_session_data *pl_sd;
+ struct item_data *item_data, *item_temp;
+ int i, j, count, counter, counter2;
+ char character[NAME_LENGTH], output[200], outputtmp[200];
+ nullpo_retr(-1, sd);
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+ malloc_tsetdword(output, '\0', sizeof(output));
+ malloc_tsetdword(outputtmp, '\0', sizeof(outputtmp));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #itemlist <char name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ if((stor = account2storage2(pl_sd->status.account_id)) != NULL) {
+ counter = 0;
+ count = 0;
+ for (i = 0; i < MAX_STORAGE; i++) {
+ if (stor->storage_[i].nameid > 0 && (item_data = itemdb_search(stor->storage_[i].nameid)) != NULL) {
+ counter = counter + stor->storage_[i].amount;
+ count++;
+ if (count == 1) {
+ sprintf(output, "------ Storage items list of '%s' ------", pl_sd->status.name);
+ clif_displaymessage(fd, output);
+ }
+ if (stor->storage_[i].refine)
+ sprintf(output, "%d %s %+d (%s %+d, id: %d)", stor->storage_[i].amount, item_data->name, stor->storage_[i].refine, item_data->jname, stor->storage_[i].refine, stor->storage_[i].nameid);
+ else
+ sprintf(output, "%d %s (%s, id: %d)", stor->storage_[i].amount, item_data->name, item_data->jname, stor->storage_[i].nameid);
+ clif_displaymessage(fd, output);
+ malloc_tsetdword(output, '\0', sizeof(output));
+ counter2 = 0;
+ for (j = 0; j < item_data->slot; j++) {
+ if (stor->storage_[i].card[j]) {
+ if ((item_temp = itemdb_search(stor->storage_[i].card[j])) != NULL) {
+ if (output[0] == '\0')
+ sprintf(outputtmp, " -> (card(s): #%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ else
+ sprintf(outputtmp, "#%d %s (%s), ", ++counter2, item_temp->name, item_temp->jname);
+ strcat(output, outputtmp);
+ }
+ }
+ }
+ if (output[0] != '\0') {
+ output[strlen(output) - 2] = ')';
+ output[strlen(output) - 1] = '\0';
+ clif_displaymessage(fd, output);
+ }
+ }
+ }
+ if (count == 0)
+ clif_displaymessage(fd, "No item found in the storage of this player.");
+ else {
+ sprintf(output, "%d item(s) found in %d kind(s) of items.", counter, count);
+ clif_displaymessage(fd, output);
+ }
+ } else {
+ clif_displaymessage(fd, "This player has no storage.");
+ return 0;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+charcommand_giveitem_sub(struct map_session_data *sd,struct item_data *item_data,int number)
+{
+ int flag = 0;
+ int loop = 1, get_count = number,i;
+ struct item item_tmp;
+
+ if(sd && item_data){
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ loop = number;
+ get_count = 1;
+ }
+ for (i = 0; i < loop; i++) {
+ malloc_set(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_data->nameid;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem((struct map_session_data*)sd,
+ &item_tmp, get_count)))
+ clif_additem((struct map_session_data*)sd, 0, 0, flag);
+ }
+ //Logs (A)dmins items [Lupus]
+ if(log_config.enable_logs&0x400)
+ log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp);
+
+ }
+}
+/*==========================================
+ * #item command (usage: #item <name/id_of_item> <quantity> <player>)
+ * by MC Cameri
+ *------------------------------------------
+ */
+int charcommand_item(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char item_name[100];
+ char character[NAME_LENGTH];
+ struct map_session_data *pl_sd;
+ int number = 0, item_id, flag;
+ struct item item_tmp;
+ struct item_data *item_data;
+ int get_count, i, pet_id;
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ malloc_tsetdword(item_name, '\0', sizeof(item_name));
+
+ if (!message || !*message || sscanf(message, "%99s %d %23[^\n]", item_name, &number, character) < 3) {
+ clif_displaymessage(fd, "Please, enter an item name/id (usage: #item <item name or ID> <quantity> <char name>).");
+ return -1;
+ }
+
+ if (number <= 0)
+ number = 1;
+
+ item_id = 0;
+ if ((item_data = itemdb_searchname(item_name)) != NULL ||
+ (item_data = itemdb_exists(atoi(item_name))) != NULL)
+ item_id = item_data->nameid;
+
+ if (item_id >= 500) {
+ get_count = number;
+ // check pet egg
+ pet_id = search_petDB_index(item_id, PET_EGG);
+ if (item_data->type == 4 || item_data->type == 5 ||
+ item_data->type == 7 || item_data->type == 8) {
+ get_count = 1;
+ }
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can look items only lower or same level
+ for (i = 0; i < number; i += get_count) {
+ // if pet egg
+ if (pet_id >= 0) {
+ pl_sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(pl_sd->status.account_id, pl_sd->status.char_id,
+ (short)pet_db[pet_id].class_, (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0, (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ // if not pet egg
+ } else {
+ malloc_set(&item_tmp, 0, sizeof(item_tmp));
+ item_tmp.nameid = item_id;
+ item_tmp.identify = 1;
+
+ if ((flag = pc_additem(pl_sd, &item_tmp, get_count)))
+ clif_additem(pl_sd, 0, 0, flag);
+ }
+ }
+
+ //Logs (A)dmins items [Lupus]
+ if(log_config.enable_logs&0x400)
+ log_pick_pc(sd, "A", item_tmp.nameid, number, &item_tmp);
+
+ clif_displaymessage(fd, msg_table[18]); // Item created.
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else if(/* from jA's @giveitem */strcmpi(character,"all")==0 || strcmpi(character,"everyone")==0){
+ struct map_session_data **pl_allsd;
+ int users;
+ pl_allsd = map_getallusers(&users);
+ for (i = 0; i < users; i++) {
+ if ((pl_sd = pl_allsd[i])) {
+ charcommand_giveitem_sub(pl_sd,item_data,number);
+ snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "You got %s %d.", item_name,number);
+ clif_displaymessage(pl_sd->fd, tmp_cmdoutput);
+ }
+ }
+ snprintf(tmp_cmdoutput, sizeof(tmp_cmdoutput), "%s received %s %d.","Everyone",item_name,number);
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[19]); // Invalid item ID or name.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #warp/#rura/#rura+ <mapname> <x> <y> <char name>
+ *------------------------------------------
+ */
+int charcommand_warp(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char map_name[MAP_NAME_LENGTH];
+ char character[NAME_LENGTH];
+ int x = 0, y = 0;
+ struct map_session_data *pl_sd;
+ int m;
+
+ nullpo_retr(-1, sd);
+
+ malloc_tsetdword(map_name, '\0', sizeof(map_name));
+ malloc_tsetdword(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%15s %d %d %23[^\n]", map_name, &x, &y, character) < 4) {
+ clif_displaymessage(fd, "Usage: #warp/#rura/#rura+ <mapname> <x> <y> <char name>");
+ return -1;
+ }
+
+ if (strstr(map_name, ".gat") == NULL && strstr(map_name, ".afm") == NULL && strlen(map_name) < MAP_NAME_LENGTH-4) // 16 - 4 (.gat)
+ strcat(map_name, ".gat");
+
+ if ((pl_sd = map_nick2sd(character)) == NULL) {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ if (pc_isGM(sd) < pc_isGM(pl_sd)) { // you can rura+ only lower or same GM level
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ m = map_mapname2mapid(map_name);
+ if (m < 0) {
+ clif_displaymessage(fd, msg_table[1]); // Map not found.
+ return -1;
+ }
+ if ((x || y) && map_getcell(m, x, y, CELL_CHKNOREACH)) {
+ clif_displaymessage(fd, msg_table[2]); // Coordinates out of range.
+ x = y = 0;
+ }
+ if (map[m].flag.nowarpto && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp someone to this map.");
+ return -1;
+ }
+ if (pl_sd->bl.m >= 0 && map[pl_sd->bl.m].flag.nowarp && battle_config.any_warp_GM_min_level > pc_isGM(sd)) {
+ clif_displaymessage(fd, "You are not authorised to warp this player from its actual map.");
+ return -1;
+ }
+ if (pc_setpos(pl_sd, map[m].index, x, y, 3) == 0) {
+ clif_displaymessage(pl_sd->fd, msg_table[0]); // Warped.
+ clif_displaymessage(fd, msg_table[15]); // Player warped (message sends to player too).
+ return 0;
+ }
+ //No error message specified...?
+ return -1;
+}
+
+/*==========================================
+ * #zeny <charname>
+ *------------------------------------------
+ */
+int charcommand_zeny(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char character[NAME_LENGTH];
+ int zeny = 0, new_zeny;
+ nullpo_retr(-1, sd);
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &zeny, character) < 2 || zeny == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: #zeny <zeny> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ new_zeny = pl_sd->status.zeny + zeny;
+ if (zeny > 0 && (zeny > MAX_ZENY || new_zeny > MAX_ZENY)) // fix positiv overflow
+ new_zeny = MAX_ZENY;
+ else if (zeny < 0 && (zeny < -MAX_ZENY || new_zeny < 0)) // fix negativ overflow
+ new_zeny = 0;
+ if (new_zeny != pl_sd->status.zeny) {
+ pl_sd->status.zeny = new_zeny;
+ clif_updatestatus(pl_sd, SP_ZENY);
+ clif_displaymessage(fd, msg_table[211]); // Character's number of zenys changed!
+ } else {
+ if (zeny < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #fakename <char name> <fake name>
+ *------------------------------------------
+ */
+
+int charcommand_fakename(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char name[NAME_LENGTH];
+ char char_name[NAME_LENGTH];
+
+ nullpo_retr(-1, sd);
+
+ name[0] = '\0'; //If you don't pass a second word, name is left as garbage, most definitely not a blank name! [Skotlex]
+ if (!message || !*message || sscanf(message, "%23s %23[^\n]", char_name, name) < 1) {
+ clif_displaymessage(sd->fd,"Usage: #fakename <char name> <fake name>.");
+ clif_displaymessage(sd->fd,"Or: #fakename <char name> to disable.");
+ return 0;
+ }
+
+ if(!(pl_sd = map_nick2sd(char_name))) {
+ clif_displaymessage(sd->fd,"Character not found.");
+ return -1;
+ }
+
+ if(strlen(name) < 1 || !name) {
+ if(strlen(pl_sd->fakename) > 1) {
+ pl_sd->fakename[0]='\0';
+ clif_charnameack(0, &pl_sd->bl);
+ clif_displaymessage(sd->fd,"Returned to real name.");
+ } else {
+ clif_displaymessage(sd->fd,"Character does not has a fake name.");
+ }
+ return 0;
+ }
+
+ if(strlen(name) < 2) {
+ clif_displaymessage(sd->fd,"Fake name must be at least two characters.");
+ return 0;
+ }
+
+ memcpy(pl_sd->fakename,name, NAME_LENGTH-1);
+ clif_charnameack(0, &pl_sd->bl);
+ clif_displaymessage(sd->fd,"Fake name enabled.");
+
+ return 0;
+}
+
+
+/*==========================================
+ * #baselvl <#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+*/
+int charcommand_baselevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int level = 0, i, status_point=0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #baselvl <#> <nickname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change base level only lower or same gm level
+
+ if (level > 0) {
+ if (pl_sd->status.base_level == pc_maxbaselv(sd)) { // check for max level by Valaris
+ clif_displaymessage(fd, msg_table[91]); // Character's base level can't go any higher.
+ return 0;
+ } // End Addition
+ if ((unsigned int)level > pc_maxbaselv(pl_sd) ||
+ pl_sd->status.base_level > pc_maxbaselv(pl_sd) -level)
+ level = pc_maxbaselv(pl_sd) - pl_sd->status.base_level;
+ for (i = 1; i <= level; i++)
+ status_point += (pl_sd->status.base_level + i + 14) / 5;
+ if (pl_sd->status.status_point > USHRT_MAX - status_point)
+ pl_sd->status.status_point = USHRT_MAX;
+ else
+ pl_sd->status.status_point += status_point;
+ pl_sd->status.base_level += (unsigned int)level;
+ clif_updatestatus(pl_sd, SP_BASELEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ status_calc_pc(pl_sd, 0);
+ status_percent_heal(&pl_sd->bl, 100, 100);
+ clif_misceffect(&pl_sd->bl, 0);
+ clif_displaymessage(fd, msg_table[65]); // Character's base level raised.
+ } else {
+ if (pl_sd->status.base_level == 1) {
+ clif_displaymessage(fd, msg_table[193]); // Character's base level can't go any lower.
+ return -1;
+ }
+ level *= -1;
+ if ((unsigned int)level >= pl_sd->status.base_level)
+ level = pl_sd->status.base_level -1;
+ if (pl_sd->status.status_point > 0) {
+ for (i = 0; i > -level; i--)
+ status_point += (pl_sd->status.base_level +i + 14) / 5;
+ if (pl_sd->status.status_point < status_point)
+ pc_resetstate(pl_sd);
+ if (pl_sd->status.status_point < status_point)
+ pl_sd->status.status_point = 0;
+ else
+ pl_sd->status.status_point -= status_point;
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ } // to add: remove status points from stats
+ pl_sd->status.base_level -= (unsigned int)level;
+ clif_updatestatus(pl_sd, SP_BASELEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTBASEEXP);
+ status_calc_pc(pl_sd, 0);
+ clif_displaymessage(fd, msg_table[66]); // Character's base level lowered.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0; //正常終了
+}
+
+/*==========================================
+ * #jlvl <#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_joblevel(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int level = 0;
+ //転生や養子の場合の元の職業を算出する
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &level, player) < 2 || level == 0) {
+ clif_displaymessage(fd, "Please, enter a level adjustement and a player name (usage: #joblvl <#> <nickname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can change job level only lower or same gm level
+ if (level > 0) {
+ if (pl_sd->status.job_level == pc_maxjoblv(pl_sd)) {
+ clif_displaymessage(fd, msg_table[67]); // Character's job level can't go any higher.
+ return -1;
+ }
+ if ((unsigned int)level > pc_maxjoblv(pl_sd) ||
+ pl_sd->status.job_level > pc_maxjoblv(pl_sd) -level)
+ level = pc_maxjoblv(pl_sd) - pl_sd->status.job_level;
+ pl_sd->status.job_level += (unsigned int)level;
+ clif_updatestatus(pl_sd, SP_JOBLEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
+
+ if (pl_sd->status.skill_point > USHRT_MAX - level)
+ pl_sd->status.skill_point = USHRT_MAX;
+ else
+ pl_sd->status.skill_point += level;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ status_calc_pc(pl_sd, 0);
+ clif_misceffect(&pl_sd->bl, 1);
+ clif_displaymessage(fd, msg_table[68]); // character's job level raised.
+ } else {
+ if (pl_sd->status.job_level == 1) {
+ clif_displaymessage(fd, msg_table[194]); // Character's job level can't go any lower.
+ return -1;
+ }
+ level*=-1;
+ if ((unsigned int)level >= pl_sd->status.job_level)
+ level = pl_sd->status.job_level-1;
+ pl_sd->status.job_level -= (unsigned int)level;
+ clif_updatestatus(pl_sd, SP_JOBLEVEL);
+ clif_updatestatus(pl_sd, SP_NEXTJOBEXP);
+ if (pl_sd->status.skill_point < level)
+ pc_resetskill(pl_sd, 0); //Need more skill points to substract
+ if (pl_sd->status.skill_point < level)
+ pl_sd->status.skill_point = 0;
+ else
+ pl_sd->status.skill_point -= level;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ status_calc_pc(pl_sd, 0);
+ clif_displaymessage(fd, msg_table[69]); // Character's job level lowered.
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * #questskill <skill_#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_questskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int skill_id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: #questskill <#:0+> <nickname>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL_DB) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_checkskill(pl_sd, skill_id) == 0) {
+ pc_skill(pl_sd, skill_id, 1, 0);
+ clif_displaymessage(fd, msg_table[199]); // This player has learned the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[200]); // This player already has this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*==========================================
+ * #lostskill <skill_#> <nickname>
+ * Transferred by: Kevin
+ *------------------------------------------
+ */
+int charcommand_lostskill(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int skill_id = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &skill_id, player) < 2 || skill_id < 0) {
+ clif_displaymessage(fd, "Please, enter a quest skill number and a player name (usage: @charlostskill <#:0+> <char_name>).");
+ return -1;
+ }
+
+ if (skill_id >= 0 && skill_id < MAX_SKILL) {
+ if (skill_get_inf2(skill_id) & INF2_QUEST_SKILL) {
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_checkskill(pl_sd, skill_id) > 0) {
+ pl_sd->status.skill[skill_id].lv = 0;
+ pl_sd->status.skill[skill_id].flag = 0;
+ clif_skillinfoblock(pl_sd);
+ clif_displaymessage(fd, msg_table[202]); // This player has forgotten the skill.
+ } else {
+ clif_displaymessage(fd, msg_table[203]); // This player doesn't have this quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[197]); // This skill number doesn't exist or isn't a quest skill.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[198]); // This skill number doesn't exist.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Skill Reset
+ *------------------------------------------
+ */
+int charcommand_skreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charskreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset skill points only lower or same gm level
+ pc_resetskill(pl_sd,1);
+ sprintf(tmp_cmdoutput, msg_table[206], player); // '%s' skill points reseted!
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Stat Reset
+ *------------------------------------------
+ */
+int charcommand_streset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charstreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset stats points only lower or same gm level
+ pc_resetstate(pl_sd);
+ sprintf(tmp_cmdoutput, msg_table[207], player); // '%s' stats points reseted!
+ clif_displaymessage(fd, tmp_cmdoutput);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Model by chbrules
+ *------------------------------------------
+ */
+int charcommand_model(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ int hair_style = 0, hair_color = 0, cloth_color = 0;
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ char tmp_cmdoutput[1024];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %d %d %23[^\n]", &hair_style, &hair_color, &cloth_color, player) < 4 || hair_style < 0 || hair_color < 0 || cloth_color < 0) {
+ sprintf(tmp_cmdoutput, "Please, enter a valid model and a player name (usage: @charmodel <hair ID: %d-%d> <hair color: %d-%d> <clothes color: %d-%d> <name>).",
+ MIN_HAIR_STYLE, MAX_HAIR_STYLE, MIN_HAIR_COLOR, MAX_HAIR_COLOR, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
+ clif_displaymessage(fd, tmp_cmdoutput);
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (hair_style >= MIN_HAIR_STYLE && hair_style <= MAX_HAIR_STYLE &&
+ hair_color >= MIN_HAIR_COLOR && hair_color <= MAX_HAIR_COLOR &&
+ cloth_color >= MIN_CLOTH_COLOR && cloth_color <= MAX_CLOTH_COLOR) {
+ /* Removed this check for being too strange. [Skotlex]
+ if (cloth_color != 0 &&
+ pl_sd->status.sex == 1 &&
+ (pl_sd->status.class_ == JOB_ASSASSIN || pl_sd->status.class_ == JOB_ROGUE)) {
+ clif_displaymessage(fd, msg_table[35]); // You can't use this command with this class.
+ return -1;
+ } else {
+ */
+ pc_changelook(pl_sd, LOOK_HAIR, hair_style);
+ pc_changelook(pl_sd, LOOK_HAIR_COLOR, hair_color);
+ pc_changelook(pl_sd, LOOK_CLOTHES_COLOR, cloth_color);
+ clif_displaymessage(fd, msg_table[36]); // Appearence changed.
+// }
+ } else {
+ clif_displaymessage(fd, msg_table[37]); // An invalid number was specified.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Skill Point (Rewritten by [Yor])
+ *------------------------------------------
+ */
+int charcommand_skpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int new_skill_point;
+ int point = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charskpoint <amount> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (point > 0 && pl_sd->status.skill_point > USHRT_MAX - point)
+ new_skill_point = USHRT_MAX;
+ else if (point < 0 && pl_sd->status.skill_point < -point)
+ new_skill_point = 0;
+ else
+ new_skill_point = pl_sd->status.skill_point + point;
+ if (new_skill_point != (int)pl_sd->status.skill_point) {
+ pl_sd->status.skill_point = new_skill_point;
+ clif_updatestatus(pl_sd, SP_SKILLPOINT);
+ clif_displaymessage(fd, msg_table[209]); // Character's number of skill points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Character Status Point (rewritten by [Yor])
+ *------------------------------------------
+ */
+int charcommand_stpoint(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ struct map_session_data *pl_sd;
+ char player[NAME_LENGTH];
+ int new_status_point;
+ int point = 0;
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%d %23[^\n]", &point, player) < 2 || point == 0) {
+ clif_displaymessage(fd, "Please, enter a number and a player name (usage: @charstpoint <amount> <name>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(player)) != NULL) {
+ if (point > 0 && pl_sd->status.status_point > USHRT_MAX - point)
+ new_status_point = USHRT_MAX;
+ else if (point < 0 && pl_sd->status.status_point < -point)
+ new_status_point = 0;
+ else
+ new_status_point = pl_sd->status.status_point + point;
+ if (new_status_point != (int)pl_sd->status.status_point) {
+ pl_sd->status.status_point = new_status_point;
+ clif_updatestatus(pl_sd, SP_STATUSPOINT);
+ clif_displaymessage(fd, msg_table[210]); // Character's number of status points changed!
+ } else {
+ if (point < 0)
+ clif_displaymessage(fd, msg_table[41]); // Impossible to decrease the number/value.
+ else
+ clif_displaymessage(fd, msg_table[149]); // Impossible to increase the number/value.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * charchangesex command (usage: charchangesex <player_name>)
+ *------------------------------------------
+ */
+int charcommand_changesex(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char player[NAME_LENGTH];
+ nullpo_retr(-1, sd);
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", player) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: @charchangesex <name>).");
+ return -1;
+ }
+
+ // check player name
+ if (strlen(player) < 4) {
+ clif_displaymessage(fd, msg_table[86]); // Sorry, but a player name have at least 4 characters.
+ return -1;
+ } else if (strlen(player) > 23) {
+ clif_displaymessage(fd, msg_table[87]); // Sorry, but a player name have 23 characters maximum.
+ return -1;
+ } else {
+ chrif_char_ask_name(sd->status.account_id, player, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex
+ clif_displaymessage(fd, msg_table[88]); // Character name sends to char-server to ask it.
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Feel (SG save map) Reset
+ *------------------------------------------
+ */
+int charcommand_feelreset(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char character[NAME_LENGTH];
+ char output[200];
+ struct map_session_data *pl_sd;
+
+ malloc_tsetdword(character, '\0', sizeof(character));
+ malloc_tsetdword(output, '\0', sizeof(output));
+
+ if (!message || !*message || sscanf(message, "%23[^\n]", character) < 1) {
+ clif_displaymessage(fd, "Please, enter a player name (usage: #feelreset <charname>).");
+ return -1;
+ }
+
+ if ((pl_sd = map_nick2sd(character)) != NULL) {
+ if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can reset a character only for lower or same GM level
+ pc_resetfeel(pl_sd);
+ sprintf(output, msg_table[267], character); // '%s' designated maps reseted!
+ clif_displaymessage(fd, output);
+ } else {
+ clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player.
+ return -1;
+ }
+ } else {
+ clif_displaymessage(fd, msg_table[3]); // Character not found.
+ return -1;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * #help - Char commands [Kayla]
+ *------------------------------------------
+ */
+int charcommand_help(
+ const int fd, struct map_session_data* sd,
+ const char* command, const char* message)
+{
+ char buf[2048], w1[2048], w2[2048];
+ int i, gm_level;
+ FILE* fp;
+ nullpo_retr(-1, sd);
+
+ malloc_tsetdword(buf, '\0', sizeof(buf));
+
+ if ((fp = fopen(charhelp_txt, "r")) != NULL) {
+ clif_displaymessage(fd, msg_table[26]); /* Help commands: */
+ gm_level = pc_isGM(sd);
+ while(fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (buf[0] == '/' && buf[1] == '/')
+ continue;
+ for (i = 0; buf[i] != '\0'; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = '\0';
+ break;
+ }
+ }
+ if (sscanf(buf, "%2047[^:]:%2047[^\n]", w1, w2) < 2)
+ clif_displaymessage(fd, buf);
+ else if (gm_level >= atoi(w1))
+ clif_displaymessage(fd, w2);
+ }
+ fclose(fp);
+ } else {
+ clif_displaymessage(fd, msg_table[27]); /* File help.txt not found. */
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/map/charcommand.h b/src/map/charcommand.h
index 2f75f75c5..54215257e 100644
--- a/src/map/charcommand.h
+++ b/src/map/charcommand.h
@@ -1,74 +1,74 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _CHARCOMMAND_H_
-#define _CHARCOMMAND_H_
-
-enum CharCommandType {
- CharCommand_None = -1,
- CharCommandJobChange,
- CharCommandPetRename,
- CharCommandPetFriendly,
- CharCommandReset,
- CharCommandStats,
- CharCommandOption,
- CharCommandSave,
- CharCommandStatsAll,
- CharCommandSpiritball,
- CharCommandItemList,
- CharCommandEffect,
- CharCommandStorageList,
- CharCommandItem, // by MC Cameri
- CharCommandWarp,
- CharCommandZeny,
- CharCommandFakeName,
- CharCommandBaseLevel,
- CharCommandJobLevel,
- CharCommandQuestSkill,
- CharCommandLostSkill,
- CharCommandSkReset,
- CharCommandStReset,
- CharCommandModel,
- CharCommandSKPoint,
- CharCommandSTPoint,
- CharCommandChangeSex,
- CharCommandFeelReset, // Komurka
- CharCommandHelp,
-
-#ifdef TXT_ONLY
-/* TXT_ONLY */
-
-/* TXT_ONLY */
-#else
-/* SQL-only */
-
-/* SQL Only */
-#endif
-
- // End. No more commans after this line.
- CharCommand_Unknown,
- CharCommand_MAX
-};
-
-typedef enum CharCommandType CharCommandType;
-typedef struct CharCommandInfo {
- CharCommandType type;
- const char* command;
- int level;
- int (*proc)(const int, struct map_session_data*,
- const char* command, const char* message);
-} CharCommandInfo;
-
-CharCommandType
-is_charcommand(const int fd, struct map_session_data* sd, const char* message);
-CharCommandType
-charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl);
-
-CharCommandType charcommand(
- struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info);
-int get_charcommand_level(const CharCommandType type);
-
-int charcommand_config_read(const char *cfgName);
-
-#endif
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHARCOMMAND_H_
+#define _CHARCOMMAND_H_
+
+enum CharCommandType {
+ CharCommand_None = -1,
+ CharCommandJobChange,
+ CharCommandPetRename,
+ CharCommandPetFriendly,
+ CharCommandReset,
+ CharCommandStats,
+ CharCommandOption,
+ CharCommandSave,
+ CharCommandStatsAll,
+ CharCommandSpiritball,
+ CharCommandItemList,
+ CharCommandEffect,
+ CharCommandStorageList,
+ CharCommandItem, // by MC Cameri
+ CharCommandWarp,
+ CharCommandZeny,
+ CharCommandFakeName,
+ CharCommandBaseLevel,
+ CharCommandJobLevel,
+ CharCommandQuestSkill,
+ CharCommandLostSkill,
+ CharCommandSkReset,
+ CharCommandStReset,
+ CharCommandModel,
+ CharCommandSKPoint,
+ CharCommandSTPoint,
+ CharCommandChangeSex,
+ CharCommandFeelReset, // Komurka
+ CharCommandHelp,
+
+#ifdef TXT_ONLY
+/* TXT_ONLY */
+
+/* TXT_ONLY */
+#else
+/* SQL-only */
+
+/* SQL Only */
+#endif
+
+ // End. No more commans after this line.
+ CharCommand_Unknown,
+ CharCommand_MAX
+};
+
+typedef enum CharCommandType CharCommandType;
+typedef struct CharCommandInfo {
+ CharCommandType type;
+ const char* command;
+ int level;
+ int (*proc)(const int, struct map_session_data*,
+ const char* command, const char* message);
+} CharCommandInfo;
+
+CharCommandType
+is_charcommand(const int fd, struct map_session_data* sd, const char* message);
+CharCommandType
+charcommand_sub(const int fd, struct map_session_data* sd, const char* str, int gmlvl);
+
+CharCommandType charcommand(
+ struct map_session_data* sd, const int level, const char* message, CharCommandInfo* info);
+int get_charcommand_level(const CharCommandType type);
+
+int charcommand_config_read(const char *cfgName);
+
+#endif
+
diff --git a/src/map/charsave.c b/src/map/charsave.c
index 1be249e5a..abcd7d152 100644
--- a/src/map/charsave.c
+++ b/src/map/charsave.c
@@ -1,523 +1,523 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "../common/core.h"
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/mmo.h"
-#include "../common/strlib.h"
-#include "../common/showmsg.h"
-#include "../common/malloc.h"
-
-#include "charsave.h"
-#include "map.h"
-
-#ifndef TXT_ONLY
-
-struct mmo_charstatus *charsave_loadchar(int charid){
- int i,j, friends;
- struct mmo_charstatus *c;
- char *str_p;
- double exp;
- friends = 0;
-
- c = (struct mmo_charstatus *)aCalloc(1,sizeof(struct mmo_charstatus));
-
- if(charid <= 0){
- ShowError("charsave_loadchar() charid <= 0! (%d)", charid);
- aFree(c);
- return NULL;
- }
- // add homun_id [albator]
- //Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) .. [Sirius]
- sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id` FROM `char` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- aFree(c);
- return NULL;
- }
-
- charsql_res = mysql_store_result(&charsql_handle);
- if(mysql_num_rows(charsql_res) <= 0){
- ShowWarning("charsave_loadchar() -> CHARACTER NOT FOUND! (id: %d)\n", charid);
- mysql_free_result(charsql_res);
- aFree(c);
- return NULL;
- }
-
- //fetch data
- charsql_row = mysql_fetch_row(charsql_res);
-
- //fill with data
- c->char_id = charid;
- c->account_id = atoi(charsql_row[1]);
- c->char_num = atoi(charsql_row[2]);
- strcpy(c->name, charsql_row[3]);
- c->class_ = atoi(charsql_row[4]);
- c->base_level = atoi(charsql_row[5]);
- c->job_level = atoi(charsql_row[6]);
- exp = atof(charsql_row[7]);
- c->base_exp = (unsigned int)cap_value(exp,0,UINT_MAX);
- exp = atof(charsql_row[8]);
- c->job_exp = (unsigned int)cap_value(exp,0,UINT_MAX);
- c->zeny = atoi(charsql_row[9]);
- c->str = atoi(charsql_row[10]);
- c->agi = atoi(charsql_row[11]);
- c->vit = atoi(charsql_row[12]);
- c->int_ = atoi(charsql_row[13]);
- c->dex = atoi(charsql_row[14]);
- c->luk = atoi(charsql_row[15]);
- c->max_hp = atoi(charsql_row[16]);
- c->hp = atoi(charsql_row[17]);
- c->max_sp = atoi(charsql_row[18]);
- c->sp = atoi(charsql_row[19]);
- c->status_point = atoi(charsql_row[20]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[20]);
- c->skill_point = atoi(charsql_row[21]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[21]);
- c->option = atoi(charsql_row[22]);
- c->karma = atoi(charsql_row[23]);
- c->manner = atoi(charsql_row[24]);
- c->party_id = atoi(charsql_row[25]);
- c->guild_id = atoi(charsql_row[26]);
- c->pet_id = atoi(charsql_row[27]);
- c->hair = atoi(charsql_row[28]);
- c->hair_color = atoi(charsql_row[29]);
- c->clothes_color = atoi(charsql_row[30]);
- c->weapon = atoi(charsql_row[31]);
- c->shield = atoi(charsql_row[32]);
- c->head_top = atoi(charsql_row[33]);
- c->head_mid = atoi(charsql_row[34]);
- c->head_bottom = atoi(charsql_row[35]);
- c->last_point.map = mapindex_name2id(charsql_row[36]);
- c->last_point.x = atoi(charsql_row[37]);
- c->last_point.y = atoi(charsql_row[38]);
- c->save_point.map = mapindex_name2id(charsql_row[39]);
- c->save_point.x = atoi(charsql_row[40]);
- c->save_point.y = atoi(charsql_row[41]);
- c->partner_id = atoi(charsql_row[42]);
- c->father = atoi(charsql_row[43]);
- c->mother = atoi(charsql_row[44]);
- c->child = atoi(charsql_row[45]);
- c->fame = atoi(charsql_row[46]);
- c->hom_id = atoi(charsql_row[47]); // albator
-
- mysql_free_result(charsql_res);
-
- //Check for '0' Savepoint / LastPoint
- if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){
- c->last_point.map = mapindex_name2id(MAP_PRONTERA);
- c->last_point.x = 100;
- c->last_point.y = 100;
- }
-
- if (c->save_point.x == 0 || c->save_point.y == 0 || c->save_point.map == 0){
- c->save_point.map = mapindex_name2id(MAP_PRONTERA);
- c->save_point.x = 100;
- c->save_point.y = 100;
- }
-
-
- //read the memo points
- sprintf(tmp_sql, "SELECT `memo_id`, `char_id`, `map`, `x`, `y` FROM `memo` WHERE `char_id` = '%d' ORDER BY `memo_id`", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- aFree(c);
- return NULL;
- }
-
- charsql_res = mysql_store_result(&charsql_handle);
- if(charsql_res){
- for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
- c->memo_point[i].map = mapindex_name2id(charsql_row[2]);
- c->memo_point[i].x = atoi(charsql_row[3]);
- c->memo_point[i].y = atoi(charsql_row[4]);
- }
- mysql_free_result(charsql_res);
- }
-
- //read inventory...
- str_p = tmp_sql;
- str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
- for (i = 0; i < MAX_SLOTS; i++)
- str_p += sprintf(str_p, ", `card%d`", i);
- str_p += sprintf(str_p, " FROM `inventory` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- aFree(c);
- return NULL;
- }
-
- charsql_res = mysql_store_result(&charsql_handle);
- if(charsql_res){
- for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
- //c->inventory[i].id = atoi(charsql_row[0]);
- c->inventory[i].nameid = atoi(charsql_row[0]);
- c->inventory[i].amount = atoi(charsql_row[1]);
- c->inventory[i].equip = atoi(charsql_row[2]);
- c->inventory[i].identify = atoi(charsql_row[3]);
- c->inventory[i].refine = atoi(charsql_row[4]);
- c->inventory[i].attribute = atoi(charsql_row[5]);
- for (j = 0; j < MAX_SLOTS; j++)
- c->inventory[i].card[j] = atoi(charsql_row[6+j]);
- }
- mysql_free_result(charsql_res);
- }
-
-
- //cart inventory ..
- str_p = tmp_sql;
- str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
- for (i = 0; i < MAX_SLOTS; i++)
- str_p += sprintf(str_p, ", `card%d`", i);
- str_p += sprintf(str_p, " FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
-
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- aFree(c);
- return NULL;
- }
-
- charsql_res = mysql_store_result(&charsql_handle);
- if(charsql_res){
- for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
- //c->cart[i].id = atoi(charsql_row[0]);
- c->cart[i].nameid = atoi(charsql_row[0]);
- c->cart[i].amount = atoi(charsql_row[1]);
- c->cart[i].equip = atoi(charsql_row[2]);
- c->cart[i].identify = atoi(charsql_row[3]);
- c->cart[i].refine = atoi(charsql_row[4]);
- c->cart[i].attribute = atoi(charsql_row[5]);
- for (j = 0; j < MAX_SLOTS; j++)
- c->cart[i].card[j] = atoi(charsql_row[6+j]);
- }
- mysql_free_result(charsql_res);
- }
-
-
- //Skills...
- sprintf(tmp_sql, "SELECT `char_id`, `id`, `lv` FROM `skill` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- aFree(c);
- return NULL;
- }
-
- charsql_res = mysql_store_result(&charsql_handle);
- if(charsql_res){
- while((charsql_row = mysql_fetch_row(charsql_res))){
- i = atoi(charsql_row[1]);
- c->skill[i].id = i;
- c->skill[i].lv = atoi(charsql_row[2]);
- }
- mysql_free_result(charsql_res);
- }
-/* Reg values are handled by the char server.
- //Global REG
- sprintf(tmp_sql, "SELECT `char_id`, `str`, `value` FROM `global_reg_value` WHERE `type` = '3' AND `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- aFree(c);
- return NULL;
- }
-
- charsql_res = mysql_store_result(&charsql_handle);
- if(charsql_res){
- for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
- strcpy(c->global_reg[i].str, charsql_row[1]);
- strcpy(c->global_reg[i].value, charsql_row[2]);
- }
- mysql_free_result(charsql_res);
- c->global_reg_num = i;
- }
-*/
-
- //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex]
- //Friend list
- sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid);
-
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- sql_res = NULL; //To avoid trying to read data.
- }
- else
- sql_res = mysql_store_result(&charsql_handle);
-
- if(sql_res)
- {
- for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && i<MAX_FRIENDS; i++)
- {
- if (sql_row[2] != NULL)
- {
- c->friends[i].account_id = atoi(sql_row[0]);
- c->friends[i].char_id = atoi(sql_row[1]);
- strncpy(c->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex]
- }
- }
- mysql_free_result(sql_res);
- }
-
- ShowInfo("charsql_loadchar(): loading of '%d' (%s) complete.\n", charid, c->name);
- return c;
-}
-
-int charsave_savechar(int charid, struct mmo_charstatus *c){
- int i,j;
- char *str_p;
-// char tmp_str[64];
-// char tmp_str2[512];
- //First save the 'char'
- sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d',"
- "`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
- "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
- "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
- "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
- "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
- "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d',"
- "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d', `homun_id`='%d'"
- "WHERE `account_id`='%d' AND `char_id` = '%d'",
- c->class_, c->base_level, c->job_level,
- c->base_exp, c->job_exp, c->zeny,
- c->max_hp, c->hp, c->max_sp, c->sp, c->status_point, c->skill_point,
- c->str, c->agi, c->vit, c->int_, c->dex, c->luk,
- c->option, c->karma, c->manner, c->party_id, c->guild_id, c->pet_id,
- c->hair, c->hair_color, c->clothes_color,
- c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom,
- mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y,
- mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother,
- c->child, c->fame, c->hom_id, c->account_id, c->char_id
- );
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
-
-
- //Save the inventory
- sprintf(tmp_sql, "DELETE FROM `inventory` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- for(i = 0; i < MAX_INVENTORY; i++){
- if(c->inventory[i].nameid > 0){
- str_p = tmp_sql;
- str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
- for (j = 0; j < MAX_SLOTS; j++)
- str_p += sprintf(str_p, ", `card%d`", j);
-
- str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
- charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip,
- c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute);
-
- for (j = 0; j < MAX_SLOTS; j++)
- str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]);
-
- strcat(tmp_sql,")");
-
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
- }
-
- //Save the cart
- sprintf(tmp_sql, "DELETE FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- for(i = 0; i < MAX_CART; i++){
- if(c->cart[i].nameid > 0){
- str_p = tmp_sql;
- str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
- for (j = 0; j < MAX_SLOTS; j++)
- str_p += sprintf(str_p, ", `card%d`", j);
-
- str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
- charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip,
- c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute);
-
- for (j = 0; j < MAX_SLOTS; j++)
- str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]);
-
- strcat(tmp_sql,")");
-
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
- }
-
-
- //Save memo points
- sprintf(tmp_sql, "DELETE FROM `memo` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- for(i = 0; i < MAX_MEMOPOINTS; i++){
- if(c->memo_point[i].map && c->memo_point[i].x > 0 && c->memo_point[i].y > 0){
- sprintf(tmp_sql, "INSERT INTO `memo` ( `char_id`, `map`, `x`, `y` ) VALUES ('%d', '%s', '%d', '%d')", charid, mapindex_id2name(c->memo_point[i].map), c->memo_point[i].x, c->memo_point[i].y);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
- }
-
-
- //Save skills
- sprintf(tmp_sql, "DELETE FROM `skill` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- for(i = 0; i < MAX_SKILL; i++){
- if(c->skill[i].id > 0){
- sprintf(tmp_sql, "INSERT INTO `skill` (`char_id`, `id`, `lv`) VALUES ('%d', '%d', '%d')", charid, c->skill[i].id, c->skill[i].lv);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
- }
-
-
-/* Reg values are handled by the char server.
- //global_reg_value saving
- sprintf(tmp_sql, "DELETE FROM `global_reg_value` WHERE `type`=3 AND `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- for(i = 0; i < c->global_reg_num; i++){
- if(c->global_reg[i].str){
- if(c->global_reg[i].value){
- //jstrescapecpy(tmp_str, c->global_reg[i].str);
- sprintf(tmp_sql, "INSERT INTO `global_reg_value` (`char_id`, `str`, `value`) VALUES ('%d', '%s', '%s')", charid, jstrescapecpy(tmp_str,c->global_reg[i].str), jstrescapecpy(tmp_str2,c->global_reg[i].value));
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
- }
- }
-*/
-
- //friendlist saving
- sprintf(tmp_sql, "DELETE FROM `friends` WHERE `char_id` = '%d'", charid);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- for(i = 0; i < MAX_FRIENDS; i++){
- if(c->friends[i].char_id > 0){
- sprintf(tmp_sql, "INSERT INTO `friends` (`char_id`, `friend_account`, `friend_id`) VALUES ('%d','%d','%d')", charid, c->friends[i].account_id, c->friends[i].char_id);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
- }
-
- ShowInfo("charsql_savechar(): saving of '%d' (%s) complete.\n", charid, c->name);
- return 0;
-}
-
-int charsave_load_scdata(int account_id, int char_id)
-{ //Loads character's sc_data
- struct map_session_data *sd;
-
- sd = map_id2sd(account_id);
- if (!sd)
- {
- ShowError("charsave_load_scdata: Player of AID %d not found!\n", account_id);
- return -1;
- }
- if (sd->status.char_id != char_id)
- {
- ShowError("charsave_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", account_id, sd->status.char_id, char_id);
- return -1;
- }
- sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`"
- "WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
-
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return -1;
- }
-
- sql_res = mysql_store_result(&charsql_handle);
- if(sql_res)
- {
- while ((sql_row = mysql_fetch_row(sql_res)))
- {
- if (atoi(sql_row[1]) < 1)
- { //Protection against invalid tick values. [Skotlex]
- ShowWarning("charsave_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", atoi(sql_row[1]), sd->status.name);
- continue;
- }
-
- status_change_start(&sd->bl, atoi(sql_row[0]), 10000, atoi(sql_row[2]), atoi(sql_row[3]),
- atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 15);
- }
- }
-
- //Once loaded, sc_data must be disposed.
- sprintf(tmp_sql, "DELETE FROM `sc_data` WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- return 0;
-}
-
-void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc)
-{ //Saves character's sc_data.
- int i,count =0;
- struct TimerData *timer;
- unsigned int tick = gettick();
- char *p = tmp_sql;
-
- p += sprintf(p, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ");
-
- for(i = 0; i < max_sc; i++)
- {
- if (sc_data->data[i].timer == -1)
- continue;
- timer = get_timer(sc_data->data[i].timer);
- if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
- continue;
-
- p += sprintf(p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", account_id, char_id,
- i, DIFF_TICK(timer->tick,tick), sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4);
-
- count++;
- }
- if (count > 0)
- {
- *--p = '\0'; //Remove the trailing comma.
- if(mysql_query(&charsql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
- ShowInfo("charsql_save_scdata(): saved %d status changes of '%d:%d'.\n", count, account_id, char_id);
- return;
-}
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/strlib.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+
+#include "charsave.h"
+#include "map.h"
+
+#ifndef TXT_ONLY
+
+struct mmo_charstatus *charsave_loadchar(int charid){
+ int i,j, friends;
+ struct mmo_charstatus *c;
+ char *str_p;
+ double exp;
+ friends = 0;
+
+ c = (struct mmo_charstatus *)aCalloc(1,sizeof(struct mmo_charstatus));
+
+ if(charid <= 0){
+ ShowError("charsave_loadchar() charid <= 0! (%d)", charid);
+ aFree(c);
+ return NULL;
+ }
+ // add homun_id [albator]
+ //Tested, Mysql 4.1.9+ has no problems with the long query, the buf is 65k big and the sql server needs for it 0.00009 secs on an athlon xp 2400+ WinXP (1GB Mem) .. [Sirius]
+ sprintf(tmp_sql, "SELECT `char_id`,`account_id`,`char_num`,`name`,`class`,`base_level`,`job_level`,`base_exp`,`job_exp`,`zeny`, `str`,`agi`,`vit`,`int`,`dex`,`luk`, `max_hp`,`hp`,`max_sp`,`sp`,`status_point`,`skill_point`, `option`,`karma`,`manner`,`party_id`,`guild_id`,`pet_id`,`hair`,`hair_color`, `clothes_color`,`weapon`,`shield`,`head_top`,`head_mid`,`head_bottom`, `last_map`,`last_x`,`last_y`,`save_map`,`save_x`,`save_y`, `partner_id`, `father`, `mother`, `child`, `fame`, `homun_id` FROM `char` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(mysql_num_rows(charsql_res) <= 0){
+ ShowWarning("charsave_loadchar() -> CHARACTER NOT FOUND! (id: %d)\n", charid);
+ mysql_free_result(charsql_res);
+ aFree(c);
+ return NULL;
+ }
+
+ //fetch data
+ charsql_row = mysql_fetch_row(charsql_res);
+
+ //fill with data
+ c->char_id = charid;
+ c->account_id = atoi(charsql_row[1]);
+ c->char_num = atoi(charsql_row[2]);
+ strcpy(c->name, charsql_row[3]);
+ c->class_ = atoi(charsql_row[4]);
+ c->base_level = atoi(charsql_row[5]);
+ c->job_level = atoi(charsql_row[6]);
+ exp = atof(charsql_row[7]);
+ c->base_exp = (unsigned int)cap_value(exp,0,UINT_MAX);
+ exp = atof(charsql_row[8]);
+ c->job_exp = (unsigned int)cap_value(exp,0,UINT_MAX);
+ c->zeny = atoi(charsql_row[9]);
+ c->str = atoi(charsql_row[10]);
+ c->agi = atoi(charsql_row[11]);
+ c->vit = atoi(charsql_row[12]);
+ c->int_ = atoi(charsql_row[13]);
+ c->dex = atoi(charsql_row[14]);
+ c->luk = atoi(charsql_row[15]);
+ c->max_hp = atoi(charsql_row[16]);
+ c->hp = atoi(charsql_row[17]);
+ c->max_sp = atoi(charsql_row[18]);
+ c->sp = atoi(charsql_row[19]);
+ c->status_point = atoi(charsql_row[20]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[20]);
+ c->skill_point = atoi(charsql_row[21]) > USHRT_MAX? USHRT_MAX : atoi(charsql_row[21]);
+ c->option = atoi(charsql_row[22]);
+ c->karma = atoi(charsql_row[23]);
+ c->manner = atoi(charsql_row[24]);
+ c->party_id = atoi(charsql_row[25]);
+ c->guild_id = atoi(charsql_row[26]);
+ c->pet_id = atoi(charsql_row[27]);
+ c->hair = atoi(charsql_row[28]);
+ c->hair_color = atoi(charsql_row[29]);
+ c->clothes_color = atoi(charsql_row[30]);
+ c->weapon = atoi(charsql_row[31]);
+ c->shield = atoi(charsql_row[32]);
+ c->head_top = atoi(charsql_row[33]);
+ c->head_mid = atoi(charsql_row[34]);
+ c->head_bottom = atoi(charsql_row[35]);
+ c->last_point.map = mapindex_name2id(charsql_row[36]);
+ c->last_point.x = atoi(charsql_row[37]);
+ c->last_point.y = atoi(charsql_row[38]);
+ c->save_point.map = mapindex_name2id(charsql_row[39]);
+ c->save_point.x = atoi(charsql_row[40]);
+ c->save_point.y = atoi(charsql_row[41]);
+ c->partner_id = atoi(charsql_row[42]);
+ c->father = atoi(charsql_row[43]);
+ c->mother = atoi(charsql_row[44]);
+ c->child = atoi(charsql_row[45]);
+ c->fame = atoi(charsql_row[46]);
+ c->hom_id = atoi(charsql_row[47]); // albator
+
+ mysql_free_result(charsql_res);
+
+ //Check for '0' Savepoint / LastPoint
+ if (c->last_point.x == 0 || c->last_point.y == 0 || c->last_point.map == 0){
+ c->last_point.map = mapindex_name2id(MAP_PRONTERA);
+ c->last_point.x = 100;
+ c->last_point.y = 100;
+ }
+
+ if (c->save_point.x == 0 || c->save_point.y == 0 || c->save_point.map == 0){
+ c->save_point.map = mapindex_name2id(MAP_PRONTERA);
+ c->save_point.x = 100;
+ c->save_point.y = 100;
+ }
+
+
+ //read the memo points
+ sprintf(tmp_sql, "SELECT `memo_id`, `char_id`, `map`, `x`, `y` FROM `memo` WHERE `char_id` = '%d' ORDER BY `memo_id`", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ c->memo_point[i].map = mapindex_name2id(charsql_row[2]);
+ c->memo_point[i].x = atoi(charsql_row[3]);
+ c->memo_point[i].y = atoi(charsql_row[4]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+ //read inventory...
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (i = 0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+ str_p += sprintf(str_p, " FROM `inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ //c->inventory[i].id = atoi(charsql_row[0]);
+ c->inventory[i].nameid = atoi(charsql_row[0]);
+ c->inventory[i].amount = atoi(charsql_row[1]);
+ c->inventory[i].equip = atoi(charsql_row[2]);
+ c->inventory[i].identify = atoi(charsql_row[3]);
+ c->inventory[i].refine = atoi(charsql_row[4]);
+ c->inventory[i].attribute = atoi(charsql_row[5]);
+ for (j = 0; j < MAX_SLOTS; j++)
+ c->inventory[i].card[j] = atoi(charsql_row[6+j]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+
+ //cart inventory ..
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "SELECT `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (i = 0; i < MAX_SLOTS; i++)
+ str_p += sprintf(str_p, ", `card%d`", i);
+ str_p += sprintf(str_p, " FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ //c->cart[i].id = atoi(charsql_row[0]);
+ c->cart[i].nameid = atoi(charsql_row[0]);
+ c->cart[i].amount = atoi(charsql_row[1]);
+ c->cart[i].equip = atoi(charsql_row[2]);
+ c->cart[i].identify = atoi(charsql_row[3]);
+ c->cart[i].refine = atoi(charsql_row[4]);
+ c->cart[i].attribute = atoi(charsql_row[5]);
+ for (j = 0; j < MAX_SLOTS; j++)
+ c->cart[i].card[j] = atoi(charsql_row[6+j]);
+ }
+ mysql_free_result(charsql_res);
+ }
+
+
+ //Skills...
+ sprintf(tmp_sql, "SELECT `char_id`, `id`, `lv` FROM `skill` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ while((charsql_row = mysql_fetch_row(charsql_res))){
+ i = atoi(charsql_row[1]);
+ c->skill[i].id = i;
+ c->skill[i].lv = atoi(charsql_row[2]);
+ }
+ mysql_free_result(charsql_res);
+ }
+/* Reg values are handled by the char server.
+ //Global REG
+ sprintf(tmp_sql, "SELECT `char_id`, `str`, `value` FROM `global_reg_value` WHERE `type` = '3' AND `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ aFree(c);
+ return NULL;
+ }
+
+ charsql_res = mysql_store_result(&charsql_handle);
+ if(charsql_res){
+ for(i = 0; (charsql_row = mysql_fetch_row(charsql_res)); i++){
+ strcpy(c->global_reg[i].str, charsql_row[1]);
+ strcpy(c->global_reg[i].value, charsql_row[2]);
+ }
+ mysql_free_result(charsql_res);
+ c->global_reg_num = i;
+ }
+*/
+
+ //Shamelessly stolen from its_sparky (ie: thanks) and then assimilated by [Skotlex]
+ //Friend list
+ sprintf(tmp_sql, "SELECT f.friend_account, f.friend_id, c.name FROM friends f LEFT JOIN `char` c ON f.friend_account=c.account_id AND f.friend_id=c.char_id WHERE f.char_id='%d'", charid);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ sql_res = NULL; //To avoid trying to read data.
+ }
+ else
+ sql_res = mysql_store_result(&charsql_handle);
+
+ if(sql_res)
+ {
+ for(i = 0; (sql_row = mysql_fetch_row(sql_res)) && i<MAX_FRIENDS; i++)
+ {
+ if (sql_row[2] != NULL)
+ {
+ c->friends[i].account_id = atoi(sql_row[0]);
+ c->friends[i].char_id = atoi(sql_row[1]);
+ strncpy(c->friends[i].name, sql_row[2], NAME_LENGTH-1); //The -1 is to avoid losing the ending \0 [Skotlex]
+ }
+ }
+ mysql_free_result(sql_res);
+ }
+
+ ShowInfo("charsql_loadchar(): loading of '%d' (%s) complete.\n", charid, c->name);
+ return c;
+}
+
+int charsave_savechar(int charid, struct mmo_charstatus *c){
+ int i,j;
+ char *str_p;
+// char tmp_str[64];
+// char tmp_str2[512];
+ //First save the 'char'
+ sprintf(tmp_sql ,"UPDATE `char` SET `class`='%d', `base_level`='%d', `job_level`='%d',"
+ "`base_exp`='%u', `job_exp`='%u', `zeny`='%d',"
+ "`max_hp`='%d',`hp`='%d',`max_sp`='%d',`sp`='%d',`status_point`='%d',`skill_point`='%d',"
+ "`str`='%d',`agi`='%d',`vit`='%d',`int`='%d',`dex`='%d',`luk`='%d',"
+ "`option`='%d',`karma`='%d',`manner`='%d',`party_id`='%d',`guild_id`='%d',`pet_id`='%d',"
+ "`hair`='%d',`hair_color`='%d',`clothes_color`='%d',`weapon`='%d',`shield`='%d',`head_top`='%d',`head_mid`='%d',`head_bottom`='%d',"
+ "`last_map`='%s',`last_x`='%d',`last_y`='%d',`save_map`='%s',`save_x`='%d',`save_y`='%d',"
+ "`partner_id`='%d', `father`='%d', `mother`='%d', `child`='%d', `fame`='%d', `homun_id`='%d'"
+ "WHERE `account_id`='%d' AND `char_id` = '%d'",
+ c->class_, c->base_level, c->job_level,
+ c->base_exp, c->job_exp, c->zeny,
+ c->max_hp, c->hp, c->max_sp, c->sp, c->status_point, c->skill_point,
+ c->str, c->agi, c->vit, c->int_, c->dex, c->luk,
+ c->option, c->karma, c->manner, c->party_id, c->guild_id, c->pet_id,
+ c->hair, c->hair_color, c->clothes_color,
+ c->weapon, c->shield, c->head_top, c->head_mid, c->head_bottom,
+ mapindex_id2name(c->last_point.map), c->last_point.x, c->last_point.y,
+ mapindex_id2name(c->save_point.map), c->save_point.x, c->save_point.y, c->partner_id, c->father, c->mother,
+ c->child, c->fame, c->hom_id, c->account_id, c->char_id
+ );
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+
+ //Save the inventory
+ sprintf(tmp_sql, "DELETE FROM `inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_INVENTORY; i++){
+ if(c->inventory[i].nameid > 0){
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT INTO `inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
+ charid, c->inventory[i].nameid, c->inventory[i].amount, c->inventory[i].equip,
+ c->inventory[i].identify, c->inventory[i].refine, c->inventory[i].attribute);
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", '%d'", c->inventory[i].card[j]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ //Save the cart
+ sprintf(tmp_sql, "DELETE FROM `cart_inventory` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_CART; i++){
+ if(c->cart[i].nameid > 0){
+ str_p = tmp_sql;
+ str_p += sprintf(str_p, "INSERT INTO `cart_inventory` (`char_id`, `nameid`, `amount`, `equip`, `identify`, `refine`, `attribute`");
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", `card%d`", j);
+
+ str_p += sprintf(str_p, ") VALUES ('%d', '%d', '%d', '%d', '%d', '%d', '%d'",
+ charid, c->cart[i].nameid, c->cart[i].amount, c->cart[i].equip,
+ c->cart[i].identify, c->cart[i].refine, c->cart[i].attribute);
+
+ for (j = 0; j < MAX_SLOTS; j++)
+ str_p += sprintf(str_p, ", '%d'", c->cart[i].card[j]);
+
+ strcat(tmp_sql,")");
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+ //Save memo points
+ sprintf(tmp_sql, "DELETE FROM `memo` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_MEMOPOINTS; i++){
+ if(c->memo_point[i].map && c->memo_point[i].x > 0 && c->memo_point[i].y > 0){
+ sprintf(tmp_sql, "INSERT INTO `memo` ( `char_id`, `map`, `x`, `y` ) VALUES ('%d', '%s', '%d', '%d')", charid, mapindex_id2name(c->memo_point[i].map), c->memo_point[i].x, c->memo_point[i].y);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+ //Save skills
+ sprintf(tmp_sql, "DELETE FROM `skill` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_SKILL; i++){
+ if(c->skill[i].id > 0){
+ sprintf(tmp_sql, "INSERT INTO `skill` (`char_id`, `id`, `lv`) VALUES ('%d', '%d', '%d')", charid, c->skill[i].id, c->skill[i].lv);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+
+/* Reg values are handled by the char server.
+ //global_reg_value saving
+ sprintf(tmp_sql, "DELETE FROM `global_reg_value` WHERE `type`=3 AND `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < c->global_reg_num; i++){
+ if(c->global_reg[i].str){
+ if(c->global_reg[i].value){
+ //jstrescapecpy(tmp_str, c->global_reg[i].str);
+ sprintf(tmp_sql, "INSERT INTO `global_reg_value` (`char_id`, `str`, `value`) VALUES ('%d', '%s', '%s')", charid, jstrescapecpy(tmp_str,c->global_reg[i].str), jstrescapecpy(tmp_str2,c->global_reg[i].value));
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+ }
+*/
+
+ //friendlist saving
+ sprintf(tmp_sql, "DELETE FROM `friends` WHERE `char_id` = '%d'", charid);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ for(i = 0; i < MAX_FRIENDS; i++){
+ if(c->friends[i].char_id > 0){
+ sprintf(tmp_sql, "INSERT INTO `friends` (`char_id`, `friend_account`, `friend_id`) VALUES ('%d','%d','%d')", charid, c->friends[i].account_id, c->friends[i].char_id);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ }
+
+ ShowInfo("charsql_savechar(): saving of '%d' (%s) complete.\n", charid, c->name);
+ return 0;
+}
+
+int charsave_load_scdata(int account_id, int char_id)
+{ //Loads character's sc_data
+ struct map_session_data *sd;
+
+ sd = map_id2sd(account_id);
+ if (!sd)
+ {
+ ShowError("charsave_load_scdata: Player of AID %d not found!\n", account_id);
+ return -1;
+ }
+ if (sd->status.char_id != char_id)
+ {
+ ShowError("charsave_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", account_id, sd->status.char_id, char_id);
+ return -1;
+ }
+ sprintf(tmp_sql, "SELECT `type`, `tick`, `val1`, `val2`, `val3`, `val4` FROM `sc_data`"
+ "WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
+
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return -1;
+ }
+
+ sql_res = mysql_store_result(&charsql_handle);
+ if(sql_res)
+ {
+ while ((sql_row = mysql_fetch_row(sql_res)))
+ {
+ if (atoi(sql_row[1]) < 1)
+ { //Protection against invalid tick values. [Skotlex]
+ ShowWarning("charsave_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", atoi(sql_row[1]), sd->status.name);
+ continue;
+ }
+
+ status_change_start(&sd->bl, atoi(sql_row[0]), 10000, atoi(sql_row[2]), atoi(sql_row[3]),
+ atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 15);
+ }
+ }
+
+ //Once loaded, sc_data must be disposed.
+ sprintf(tmp_sql, "DELETE FROM `sc_data` WHERE `account_id`='%d' AND `char_id`='%d'", account_id, char_id);
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ return 0;
+}
+
+void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc)
+{ //Saves character's sc_data.
+ int i,count =0;
+ struct TimerData *timer;
+ unsigned int tick = gettick();
+ char *p = tmp_sql;
+
+ p += sprintf(p, "INSERT INTO `sc_data` (`account_id`, `char_id`, `type`, `tick`, `val1`, `val2`, `val3`, `val4`) VALUES ");
+
+ for(i = 0; i < max_sc; i++)
+ {
+ if (sc_data->data[i].timer == -1)
+ continue;
+ timer = get_timer(sc_data->data[i].timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+
+ p += sprintf(p, " ('%d','%d','%hu','%d','%d','%d','%d','%d'),", account_id, char_id,
+ i, DIFF_TICK(timer->tick,tick), sc_data->data[i].val1, sc_data->data[i].val2, sc_data->data[i].val3, sc_data->data[i].val4);
+
+ count++;
+ }
+ if (count > 0)
+ {
+ *--p = '\0'; //Remove the trailing comma.
+ if(mysql_query(&charsql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&charsql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+ ShowInfo("charsql_save_scdata(): saved %d status changes of '%d:%d'.\n", count, account_id, char_id);
+ return;
+}
+#endif
diff --git a/src/map/charsave.h b/src/map/charsave.h
index 049efebda..5743c1c65 100644
--- a/src/map/charsave.h
+++ b/src/map/charsave.h
@@ -1,21 +1,21 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _CHARSAVE_H_
-#define _CHARSAVE_H_
-
-#include "status.h"
-
-#ifndef TXT_ONLY
- int charsave_loadHomunculus(int hom_id, struct homun_data *p);
- int charsave_saveHomunculus(struct homun_data *hd);
- int charsave_saveHomunculusSkills(struct homun_data *hd);
- int charsave_deleteHomunculus(struct homun_data *hd);
-
- struct mmo_charstatus *charsave_loadchar(int charid);
- int charsave_savechar(int charid, struct mmo_charstatus *c);
- int charsave_load_scdata(int account_id, int char_id);
- void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc);
-#endif
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHARSAVE_H_
+#define _CHARSAVE_H_
+
+#include "status.h"
+
+#ifndef TXT_ONLY
+ int charsave_loadHomunculus(int hom_id, struct homun_data *p);
+ int charsave_saveHomunculus(struct homun_data *hd);
+ int charsave_saveHomunculusSkills(struct homun_data *hd);
+ int charsave_deleteHomunculus(struct homun_data *hd);
+
+ struct mmo_charstatus *charsave_loadchar(int charid);
+ int charsave_savechar(int charid, struct mmo_charstatus *c);
+ int charsave_load_scdata(int account_id, int char_id);
+ void charsave_save_scdata(int account_id, int char_id, struct status_change* sc_data, int max_sc);
+#endif
+
+#endif
diff --git a/src/map/chat.c b/src/map/chat.c
index 9cc06a4ec..8a0844015 100644
--- a/src/map/chat.c
+++ b/src/map/chat.c
@@ -1,390 +1,390 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <string.h>
-
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "battle.h"
-#include "chat.h"
-#include "map.h"
-#include "clif.h"
-#include "pc.h"
-#include "npc.h"
-#include "atcommand.h"
-
-int chat_triggerevent(struct chat_data *cd);
-
-/*==========================================
- * チャットルーム作成
- *------------------------------------------
- */
-int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
-{
- struct chat_data *cd;
-
- nullpo_retr(0, sd);
-
- if (sd->chatID)
- return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex]
-
- if (map[sd->bl.m].flag.nochat) {
- clif_displaymessage (sd->fd, msg_txt(281));
- return 0; //Can't create chatrooms on this map.
- }
- pc_stop_walking(sd,1);
- cd = (struct chat_data *) aMalloc(sizeof(struct chat_data));
-
- cd->limit = limit;
- cd->pub = pub;
- cd->users = 1;
- memcpy(cd->pass,pass,8);
- cd->pass[7]= '\0'; //Overflow check... [Skotlex]
- if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
- memcpy(cd->title,title,titlelen);
- cd->title[titlelen]=0;
-
- cd->owner = (struct block_list **)(&cd->usersd[0]);
- cd->usersd[0] = sd;
- cd->bl.m = sd->bl.m;
- cd->bl.x = sd->bl.x;
- cd->bl.y = sd->bl.y;
- cd->bl.type = BL_CHAT;
- cd->bl.next = cd->bl.prev = NULL;
- cd->bl.id = map_addobject(&cd->bl);
- if(cd->bl.id==0){
- clif_createchat(sd,1);
- aFree(cd);
- return 0;
- }
- pc_setchatid(sd,cd->bl.id);
-
- clif_createchat(sd,0);
- clif_dispchat(cd,0);
-
- return 0;
-}
-
-/*==========================================
- * 既存チャットルームに参加
- *------------------------------------------
- */
-int chat_joinchat (struct map_session_data *sd, int chatid, char* pass)
-{
- struct chat_data *cd;
-
- nullpo_retr(0, sd);
- cd = (struct chat_data*)map_id2bl(chatid);
-
- //No need for a nullpo check. The chatid was sent by the client, if they lag or mess with the packet
- //a wrong chat id can be received. [Skotlex]
- if (cd == NULL)
- return 1;
- if (cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->limit <= cd->users) {
- clif_joinchatfail(sd,0);
- return 0;
- }
- //Allows Gm access to protected room with any password they want by valaris
- if ((cd->pub == 0 && strncmp(pass, (char *)cd->pass, 8) && (pc_isGM(sd) < battle_config.gm_join_chat || !battle_config.gm_join_chat)) ||
- chatid == (int)sd->chatID) //Double Chat fix by Alex14, thx CHaNGeTe
- {
- clif_joinchatfail(sd,1);
- return 0;
- }
-
- pc_stop_walking(sd,1);
- cd->usersd[cd->users] = sd;
- cd->users++;
-
- pc_setchatid(sd,cd->bl.id);
-
- clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト
- clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告
- clif_dispchat(cd,0); // 周囲の人には人数変化報告
-
- chat_triggerevent(cd); // イベント
-
- return 0;
-}
-
-/*==========================================
- * チャットルームから抜ける
- *------------------------------------------
- */
-int chat_leavechat(struct map_session_data *sd)
-{
- struct chat_data *cd;
- int i,leavechar;
-
- nullpo_retr(1, sd);
-
- cd=(struct chat_data*)map_id2bl(sd->chatID);
- if(cd==NULL) {
- sd->chatID = 0;
- return 1;
- }
-
- for(i = 0,leavechar=-1;i < cd->users;i++){
- if(cd->usersd[i] == sd){
- leavechar=i;
- break;
- }
- }
- if(leavechar<0)
- { //Not found in the chatroom?
- sd->chatID = 0;
- return -1;
- }
-
- if(leavechar==0 && cd->users>1 && (*cd->owner)->type==BL_PC){
- // 所有者だった&他に人が居る&PCのチャット
- clif_changechatowner(cd,cd->usersd[1]);
- clif_clearchat(cd,0);
- }
-
- // 抜けるPCにも送るのでusersを減らす前に実行
- clif_leavechat(cd,sd);
-
- cd->users--;
- pc_setchatid(sd,0);
-
- if(cd->users == 0 && (*cd->owner)->type==BL_PC){
- //Delete empty chatroom
- clif_clearchat(cd,0);
- map_delobject(cd->bl.id);
- return 1;
- }
- for(i=leavechar;i < cd->users;i++)
- cd->usersd[i] = cd->usersd[i+1];
-
- if(leavechar==0 && (*cd->owner)->type==BL_PC){
- //Adjust Chat location after owner has been changed.
- map_delblock( &cd->bl );
- cd->bl.x=cd->usersd[0]->bl.x;
- cd->bl.y=cd->usersd[0]->bl.y;
- map_addblock( &cd->bl );
- }
- clif_dispchat(cd,0);
-
- return 0;
-}
-
-/*==========================================
- * チャットルームの持ち主を譲る
- *------------------------------------------
- */
-int chat_changechatowner(struct map_session_data *sd,char *nextownername)
-{
- struct chat_data *cd;
- struct map_session_data *tmp_sd;
- int i, nextowner;
-
- nullpo_retr(1, sd);
-
- cd = (struct chat_data*)map_id2bl(sd->chatID);
- if (cd == NULL || (struct block_list *)sd != (*cd->owner))
- return 1;
-
- for(i = 1,nextowner=-1;i < cd->users;i++){
- if(strcmp(cd->usersd[i]->status.name,nextownername)==0){
- nextowner=i;
- break;
- }
- }
- if(nextowner<0) // そんな人は居ない
- return -1;
-
- clif_changechatowner(cd,cd->usersd[nextowner]);
- // 一旦消す
- clif_clearchat(cd,0);
-
- // userlistの順番変更 (0が所有者なので)
- if( (tmp_sd = cd->usersd[0]) == NULL ) //FIXME: How is this even possible!? Invoking character should be owner, hence, it SHOULD be on sc->usersd[0]!
- return 1; //ありえるのかな?
- cd->usersd[0] = cd->usersd[nextowner];
- cd->usersd[nextowner] = tmp_sd;
-
- map_delblock( &cd->bl );
- cd->bl.x=cd->usersd[0]->bl.x;
- cd->bl.y=cd->usersd[0]->bl.y;
- map_addblock( &cd->bl );
-
- // 再度表示
- clif_dispchat(cd,0);
-
- return 0;
-}
-
-/*==========================================
- * チャットの状態(タイトル等)を変更
- *------------------------------------------
- */
-int chat_changechatstatus(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
-{
- struct chat_data *cd;
-
- nullpo_retr(1, sd);
-
- cd=(struct chat_data*)map_id2bl(sd->chatID);
- if(cd==NULL || (struct block_list *)sd != (*cd->owner))
- return 1;
-
- cd->limit = limit;
- cd->pub = pub;
- memcpy(cd->pass,pass,8);
- cd->pass[7]= '\0'; //Overflow check... [Skotlex]
- if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
- memcpy(cd->title,title,titlelen);
- cd->title[titlelen]=0;
-
- clif_changechatstatus(cd);
- clif_dispchat(cd,0);
-
- return 0;
-}
-
-/*==========================================
- * チャットルームから蹴り出す
- *------------------------------------------
- */
-int chat_kickchat(struct map_session_data *sd,char *kickusername)
-{
- struct chat_data *cd;
- int i;
-
- nullpo_retr(1, sd);
-
- cd = (struct chat_data *)map_id2bl(sd->chatID);
-
- if (!cd) return -1;
-
- for(i = 0; i < cd->users; i++) {
- if (strcmp(cd->usersd[i]->status.name, kickusername) == 0) {
- if (battle_config.gm_kick_chat && pc_isGM(cd->usersd[i]) >= battle_config.gm_kick_chat)
- //gm kick protection by valaris
- return 0;
-
- chat_leavechat(cd->usersd[i]);
- return 0;
- }
- }
-
- return -1;
-}
-
-/*==========================================
- * npcチャットルーム作成
- *------------------------------------------
- */
-int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev)
-{
- struct chat_data *cd;
-
- nullpo_retr(1, nd);
-
- cd = (struct chat_data *) aMalloc(sizeof(struct chat_data));
-
- cd->limit = cd->trigger = limit;
- if(trigger>0)
- cd->trigger = trigger;
- cd->pub = pub;
- cd->users = 0;
- memcpy(cd->pass,"",1);
- if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
- memcpy(cd->title,title,titlelen);
- cd->title[titlelen]=0;
-
- cd->bl.m = nd->bl.m;
- cd->bl.x = nd->bl.x;
- cd->bl.y = nd->bl.y;
- cd->bl.type = BL_CHAT;
- cd->bl.prev= cd->bl.next = NULL;
- cd->owner_ = (struct block_list *)nd;
- cd->owner = &cd->owner_;
- if (strlen(ev) > 49)
- { //npc_event is a char[50] [Skotlex]
- memcpy(cd->npc_event,ev,49);
- cd->npc_event[49] = '\0';
- } else
- memcpy(cd->npc_event,ev,strlen(ev)+1); //Include the \0
-
- cd->bl.id = map_addobject(&cd->bl);
- if(cd->bl.id==0){
- aFree(cd);
- return 0;
- }
- nd->chat_id=cd->bl.id;
-
- clif_dispchat(cd,0);
-
- return 0;
-}
-/*==========================================
- * npcチャットルーム削除
- *------------------------------------------
- */
-int chat_deletenpcchat(struct npc_data *nd)
-{
- struct chat_data *cd;
-
- nullpo_retr(0, nd);
- nullpo_retr(0, cd=(struct chat_data*)map_id2bl(nd->chat_id));
-
- chat_npckickall(cd);
- clif_clearchat(cd,0);
- map_delobject(cd->bl.id); // freeまでしてくれる
- nd->chat_id=0;
-
- return 0;
-}
-
-/*==========================================
- * 規定人数以上でイベントが定義されてるなら実行
- *------------------------------------------
- */
-int chat_triggerevent(struct chat_data *cd)
-{
- nullpo_retr(0, cd);
-
- if(cd->users>=cd->trigger && cd->npc_event[0])
- npc_event_do(cd->npc_event);
- return 0;
-}
-
-/*==========================================
- * イベントの有効化
- *------------------------------------------
- */
-int chat_enableevent(struct chat_data *cd)
-{
- nullpo_retr(0, cd);
-
- cd->trigger&=0x7f;
- chat_triggerevent(cd);
- return 0;
-}
-/*==========================================
- * イベントの無効化
- *------------------------------------------
- */
-int chat_disableevent(struct chat_data *cd)
-{
- nullpo_retr(0, cd);
-
- cd->trigger|=0x80;
- return 0;
-}
-/*==========================================
- * チャットルームから全員蹴り出す
- *------------------------------------------
- */
-int chat_npckickall(struct chat_data *cd)
-{
- nullpo_retr(0, cd);
-
- while(cd->users>0){
- chat_leavechat(cd->usersd[cd->users-1]);
- }
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "battle.h"
+#include "chat.h"
+#include "map.h"
+#include "clif.h"
+#include "pc.h"
+#include "npc.h"
+#include "atcommand.h"
+
+int chat_triggerevent(struct chat_data *cd);
+
+/*==========================================
+ * チャットルーム作成
+ *------------------------------------------
+ */
+int chat_createchat(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, sd);
+
+ if (sd->chatID)
+ return 0; //Prevent people abusing the chat system by creating multiple chats, as pointed out by End of Exam. [Skotlex]
+
+ if (map[sd->bl.m].flag.nochat) {
+ clif_displaymessage (sd->fd, msg_txt(281));
+ return 0; //Can't create chatrooms on this map.
+ }
+ pc_stop_walking(sd,1);
+ cd = (struct chat_data *) aMalloc(sizeof(struct chat_data));
+
+ cd->limit = limit;
+ cd->pub = pub;
+ cd->users = 1;
+ memcpy(cd->pass,pass,8);
+ cd->pass[7]= '\0'; //Overflow check... [Skotlex]
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ cd->owner = (struct block_list **)(&cd->usersd[0]);
+ cd->usersd[0] = sd;
+ cd->bl.m = sd->bl.m;
+ cd->bl.x = sd->bl.x;
+ cd->bl.y = sd->bl.y;
+ cd->bl.type = BL_CHAT;
+ cd->bl.next = cd->bl.prev = NULL;
+ cd->bl.id = map_addobject(&cd->bl);
+ if(cd->bl.id==0){
+ clif_createchat(sd,1);
+ aFree(cd);
+ return 0;
+ }
+ pc_setchatid(sd,cd->bl.id);
+
+ clif_createchat(sd,0);
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * 既存チャットルームに参加
+ *------------------------------------------
+ */
+int chat_joinchat (struct map_session_data *sd, int chatid, char* pass)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, sd);
+ cd = (struct chat_data*)map_id2bl(chatid);
+
+ //No need for a nullpo check. The chatid was sent by the client, if they lag or mess with the packet
+ //a wrong chat id can be received. [Skotlex]
+ if (cd == NULL)
+ return 1;
+ if (cd->bl.type != BL_CHAT || cd->bl.m != sd->bl.m || sd->vender_id || sd->chatID || cd->limit <= cd->users) {
+ clif_joinchatfail(sd,0);
+ return 0;
+ }
+ //Allows Gm access to protected room with any password they want by valaris
+ if ((cd->pub == 0 && strncmp(pass, (char *)cd->pass, 8) && (pc_isGM(sd) < battle_config.gm_join_chat || !battle_config.gm_join_chat)) ||
+ chatid == (int)sd->chatID) //Double Chat fix by Alex14, thx CHaNGeTe
+ {
+ clif_joinchatfail(sd,1);
+ return 0;
+ }
+
+ pc_stop_walking(sd,1);
+ cd->usersd[cd->users] = sd;
+ cd->users++;
+
+ pc_setchatid(sd,cd->bl.id);
+
+ clif_joinchatok(sd,cd); // 新たに参加した人には全員のリスト
+ clif_addchat(cd,sd); // 既に中に居た人には追加した人の報告
+ clif_dispchat(cd,0); // 周囲の人には人数変化報告
+
+ chat_triggerevent(cd); // イベント
+
+ return 0;
+}
+
+/*==========================================
+ * チャットルームから抜ける
+ *------------------------------------------
+ */
+int chat_leavechat(struct map_session_data *sd)
+{
+ struct chat_data *cd;
+ int i,leavechar;
+
+ nullpo_retr(1, sd);
+
+ cd=(struct chat_data*)map_id2bl(sd->chatID);
+ if(cd==NULL) {
+ sd->chatID = 0;
+ return 1;
+ }
+
+ for(i = 0,leavechar=-1;i < cd->users;i++){
+ if(cd->usersd[i] == sd){
+ leavechar=i;
+ break;
+ }
+ }
+ if(leavechar<0)
+ { //Not found in the chatroom?
+ sd->chatID = 0;
+ return -1;
+ }
+
+ if(leavechar==0 && cd->users>1 && (*cd->owner)->type==BL_PC){
+ // 所有者だった&他に人が居る&PCのチャット
+ clif_changechatowner(cd,cd->usersd[1]);
+ clif_clearchat(cd,0);
+ }
+
+ // 抜けるPCにも送るのでusersを減らす前に実行
+ clif_leavechat(cd,sd);
+
+ cd->users--;
+ pc_setchatid(sd,0);
+
+ if(cd->users == 0 && (*cd->owner)->type==BL_PC){
+ //Delete empty chatroom
+ clif_clearchat(cd,0);
+ map_delobject(cd->bl.id);
+ return 1;
+ }
+ for(i=leavechar;i < cd->users;i++)
+ cd->usersd[i] = cd->usersd[i+1];
+
+ if(leavechar==0 && (*cd->owner)->type==BL_PC){
+ //Adjust Chat location after owner has been changed.
+ map_delblock( &cd->bl );
+ cd->bl.x=cd->usersd[0]->bl.x;
+ cd->bl.y=cd->usersd[0]->bl.y;
+ map_addblock( &cd->bl );
+ }
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * チャットルームの持ち主を譲る
+ *------------------------------------------
+ */
+int chat_changechatowner(struct map_session_data *sd,char *nextownername)
+{
+ struct chat_data *cd;
+ struct map_session_data *tmp_sd;
+ int i, nextowner;
+
+ nullpo_retr(1, sd);
+
+ cd = (struct chat_data*)map_id2bl(sd->chatID);
+ if (cd == NULL || (struct block_list *)sd != (*cd->owner))
+ return 1;
+
+ for(i = 1,nextowner=-1;i < cd->users;i++){
+ if(strcmp(cd->usersd[i]->status.name,nextownername)==0){
+ nextowner=i;
+ break;
+ }
+ }
+ if(nextowner<0) // そんな人は居ない
+ return -1;
+
+ clif_changechatowner(cd,cd->usersd[nextowner]);
+ // 一旦消す
+ clif_clearchat(cd,0);
+
+ // userlistの順番変更 (0が所有者なので)
+ if( (tmp_sd = cd->usersd[0]) == NULL ) //FIXME: How is this even possible!? Invoking character should be owner, hence, it SHOULD be on sc->usersd[0]!
+ return 1; //ありえるのかな?
+ cd->usersd[0] = cd->usersd[nextowner];
+ cd->usersd[nextowner] = tmp_sd;
+
+ map_delblock( &cd->bl );
+ cd->bl.x=cd->usersd[0]->bl.x;
+ cd->bl.y=cd->usersd[0]->bl.y;
+ map_addblock( &cd->bl );
+
+ // 再度表示
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * チャットの状態(タイトル等)を変更
+ *------------------------------------------
+ */
+int chat_changechatstatus(struct map_session_data *sd,int limit,int pub,char* pass,char* title,int titlelen)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(1, sd);
+
+ cd=(struct chat_data*)map_id2bl(sd->chatID);
+ if(cd==NULL || (struct block_list *)sd != (*cd->owner))
+ return 1;
+
+ cd->limit = limit;
+ cd->pub = pub;
+ memcpy(cd->pass,pass,8);
+ cd->pass[7]= '\0'; //Overflow check... [Skotlex]
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ clif_changechatstatus(cd);
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+
+/*==========================================
+ * チャットルームから蹴り出す
+ *------------------------------------------
+ */
+int chat_kickchat(struct map_session_data *sd,char *kickusername)
+{
+ struct chat_data *cd;
+ int i;
+
+ nullpo_retr(1, sd);
+
+ cd = (struct chat_data *)map_id2bl(sd->chatID);
+
+ if (!cd) return -1;
+
+ for(i = 0; i < cd->users; i++) {
+ if (strcmp(cd->usersd[i]->status.name, kickusername) == 0) {
+ if (battle_config.gm_kick_chat && pc_isGM(cd->usersd[i]) >= battle_config.gm_kick_chat)
+ //gm kick protection by valaris
+ return 0;
+
+ chat_leavechat(cd->usersd[i]);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+/*==========================================
+ * npcチャットルーム作成
+ *------------------------------------------
+ */
+int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(1, nd);
+
+ cd = (struct chat_data *) aMalloc(sizeof(struct chat_data));
+
+ cd->limit = cd->trigger = limit;
+ if(trigger>0)
+ cd->trigger = trigger;
+ cd->pub = pub;
+ cd->users = 0;
+ memcpy(cd->pass,"",1);
+ if(titlelen>=sizeof(cd->title)-1) titlelen=sizeof(cd->title)-1;
+ memcpy(cd->title,title,titlelen);
+ cd->title[titlelen]=0;
+
+ cd->bl.m = nd->bl.m;
+ cd->bl.x = nd->bl.x;
+ cd->bl.y = nd->bl.y;
+ cd->bl.type = BL_CHAT;
+ cd->bl.prev= cd->bl.next = NULL;
+ cd->owner_ = (struct block_list *)nd;
+ cd->owner = &cd->owner_;
+ if (strlen(ev) > 49)
+ { //npc_event is a char[50] [Skotlex]
+ memcpy(cd->npc_event,ev,49);
+ cd->npc_event[49] = '\0';
+ } else
+ memcpy(cd->npc_event,ev,strlen(ev)+1); //Include the \0
+
+ cd->bl.id = map_addobject(&cd->bl);
+ if(cd->bl.id==0){
+ aFree(cd);
+ return 0;
+ }
+ nd->chat_id=cd->bl.id;
+
+ clif_dispchat(cd,0);
+
+ return 0;
+}
+/*==========================================
+ * npcチャットルーム削除
+ *------------------------------------------
+ */
+int chat_deletenpcchat(struct npc_data *nd)
+{
+ struct chat_data *cd;
+
+ nullpo_retr(0, nd);
+ nullpo_retr(0, cd=(struct chat_data*)map_id2bl(nd->chat_id));
+
+ chat_npckickall(cd);
+ clif_clearchat(cd,0);
+ map_delobject(cd->bl.id); // freeまでしてくれる
+ nd->chat_id=0;
+
+ return 0;
+}
+
+/*==========================================
+ * 規定人数以上でイベントが定義されてるなら実行
+ *------------------------------------------
+ */
+int chat_triggerevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ if(cd->users>=cd->trigger && cd->npc_event[0])
+ npc_event_do(cd->npc_event);
+ return 0;
+}
+
+/*==========================================
+ * イベントの有効化
+ *------------------------------------------
+ */
+int chat_enableevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ cd->trigger&=0x7f;
+ chat_triggerevent(cd);
+ return 0;
+}
+/*==========================================
+ * イベントの無効化
+ *------------------------------------------
+ */
+int chat_disableevent(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ cd->trigger|=0x80;
+ return 0;
+}
+/*==========================================
+ * チャットルームから全員蹴り出す
+ *------------------------------------------
+ */
+int chat_npckickall(struct chat_data *cd)
+{
+ nullpo_retr(0, cd);
+
+ while(cd->users>0){
+ chat_leavechat(cd->usersd[cd->users-1]);
+ }
+ return 0;
+}
diff --git a/src/map/chat.h b/src/map/chat.h
index 1251ad98c..702fd7dad 100644
--- a/src/map/chat.h
+++ b/src/map/chat.h
@@ -1,22 +1,22 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _CHAT_H_
-#define _CHAT_H_
-
-#include "map.h"
-
-int chat_createchat(struct map_session_data *,int,int,char*,char*,int);
-int chat_joinchat(struct map_session_data *,int,char*);
-int chat_leavechat(struct map_session_data* );
-int chat_changechatowner(struct map_session_data *,char *);
-int chat_changechatstatus(struct map_session_data *,int,int,char*,char*,int);
-int chat_kickchat(struct map_session_data *,char *);
-
-int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev);
-int chat_deletenpcchat(struct npc_data *nd);
-int chat_enableevent(struct chat_data *cd);
-int chat_disableevent(struct chat_data *cd);
-int chat_npckickall(struct chat_data *cd);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHAT_H_
+#define _CHAT_H_
+
+#include "map.h"
+
+int chat_createchat(struct map_session_data *,int,int,char*,char*,int);
+int chat_joinchat(struct map_session_data *,int,char*);
+int chat_leavechat(struct map_session_data* );
+int chat_changechatowner(struct map_session_data *,char *);
+int chat_changechatstatus(struct map_session_data *,int,int,char*,char*,int);
+int chat_kickchat(struct map_session_data *,char *);
+
+int chat_createnpcchat(struct npc_data *nd,int limit,int pub,int trigger,char* title,int titlelen,const char *ev);
+int chat_deletenpcchat(struct npc_data *nd);
+int chat_enableevent(struct chat_data *cd);
+int chat_disableevent(struct chat_data *cd);
+int chat_npckickall(struct chat_data *cd);
+
+#endif
diff --git a/src/map/chrif.c b/src/map/chrif.c
index df37aaf1d..1e8183641 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -1,1636 +1,1636 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <time.h>
-#include <limits.h>
-
-#include "../common/malloc.h"
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/showmsg.h"
-
-#include "map.h"
-#include "battle.h"
-#include "chrif.h"
-#include "clif.h"
-#include "intif.h"
-#include "npc.h"
-#include "pc.h"
-#include "status.h"
-#include "mercenary.h"
-#ifndef TXT_ONLY
-#include "charsave.h"
-#endif
-//Updated table (only doc^^) [Sirius]
-//Used Packets: U->2af8
-//Free Packets: F->2af8
-
-struct dbt *auth_db;
-
-static const int packet_len_table[0x3d] = {
- 60, 3,-1,27,10,-1, 6,-1, // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff
- 6,-1,18, 7,-1,49,30,10, // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07
- 6,30,-1,10,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
- 0,-1,10, 6,11,-1, 0, 0, // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17
- -1,-1,-1,-1,-1,-1, 2, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f
- -1,10,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, U->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27
-};
-
-//Used Packets:
-//2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver'
-//2af9: Incomming, chrif_connectack -> 'answer of the 2af8 login(ok / fail)'
-//2afa: Outgoing, chrif_sendmap -> 'sending our maps'
-//2afb: Incomming, chrif_sendmapack -> 'Maps received successfully / or not ..'
-//2afc: Outgoing, chrif_scdata_request -> request sc_data for pc_authok'ed char. <- new command reuses previous one.
-//2afd: Incomming, chrif_authok -> 'character selected, add to auth db'
-//2afe: Outgoing, send_usercount_tochar -> 'sends player count of this map server to charserver'
-//2aff: Outgoing, send_users_tochar -> 'sends all actual connected character ids to charserver'
-//2b00: Incomming, map_setusers -> 'set the actual usercount? PACKET.2B COUNT.L.. ?' (not sure)
-//2b01: Outgoing, chrif_save -> 'charsave of char XY account XY (complete struct)'
-//2b02: Outgoing, chrif_charselectreq -> 'player returns from ingame to charserver to select another char.., this packets includes sessid etc' ? (not 100% sure)
-//2b03: Incomming, clif_charselectok -> '' (i think its the packet after enterworld?) (not sure)
-//2b04: Incomming, chrif_recvmap -> 'getting maps from charserver of other mapserver's'
-//2b05: Outgoing, chrif_changemapserver -> 'Tell the charserver the mapchange / quest for ok...'
-//2b06: Incomming, chrif_changemapserverack -> 'awnser of 2b05, ok/fail, data: dunno^^'
-//2b07: Incoming, clif_updatemaxid -> Received when updating the max account/char known
-//2b08: Outgoing, chrif_searchcharid -> '...'
-//2b09: Incomming, map_addchariddb -> 'Adds a name to the nick db'
-//2b0a: Outgoing, chrif_changegm -> 'level change of acc/char XY'
-//2b0b: Incomming, chrif_changedgm -> 'answer of 2b0a..'
-//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...'
-//2b0d: Incomming, chrif_changedsex -> 'Change sex of acc XY'
-//2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)'
-//2b0f: Incomming, chrif_char_ask_name_answer -> 'answer of the 2b0e'
-//2b10: Outgoing, chrif_updatefamelist -> 'Update the fame ranking lists and send them'
-//2b11: Outgoing, chrif_changesex -> 'change sex of acc X'
-//2b12: Incomming, chrif_divorce -> 'divorce a wedding of charid X and partner id X'
-//2b13: Incomming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....'
-//2b14: Incomming, chrif_accountban -> 'not sure: kick the player with message XY'
-//2b15: Incomming, chrif_recvgmaccounts -> 'recieve gm accs from charserver (seems to be incomplete !)'
-//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....'
-//2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline'
-//2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!'
-//2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online'
-//2b1a: Outgoing, chrif_buildfamelist -> 'Build the fame ranking lists and send them'
-//2b1b: Incomming, chrif_recvfamelist -> 'Receive fame ranking lists'
-//2b1c: Outgoing, chrif_save_scdata -> 'Send sc_data of player for saving.'
-//2b1d: Incomming, chrif_load_scdata -> 'received sc_data of player for loading.'
-//2b1e: Incoming, chrif_update_ip -> 'Reqest forwarded from char-server for interserver IP sync.' [Lance]
-//2b1f: Incomming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius]
-//2b20: Incomming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius]
-//2b21: Incomming, chrif_save_ack. Returned after a character has been "final saved" on the char-server. [Skotlex]
-//2b22-2b27: FREE
-
-int chrif_connected = 0;
-int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
-int srvinfo;
-static char char_ip_str[128];
-static in_addr_t char_ip= 0;
-static int char_port = 6121;
-static char userid[NAME_LENGTH], passwd[NAME_LENGTH];
-static int chrif_state = 0;
-static int char_init_done = 0;
-int other_mapserver_count=0; //Holds count of how many other map servers are online (apart of this instance) [Skotlex]
-
-//Interval at which map server updates online listing. [Valaris]
-#define CHECK_INTERVAL 3600000
-//Interval at which map server sends number of connected users. [Skotlex]
-#define UPDATE_INTERVAL 10000
-//This define should spare writing the check in every function. [Skotlex]
-#define chrif_check(a) { if(!chrif_isconnect()) return a; }
-
-// 設定ファイル読み込み関係
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_setuserid(char *id)
-{
- memcpy(userid, id, NAME_LENGTH);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_setpasswd(char *pwd)
-{
- memcpy(passwd, pwd, NAME_LENGTH);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_checkdefaultlogin(void)
-{
- if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) {
- ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n");
-#ifdef TXT_ONLY
- ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n");
-#else
- ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n");
-#endif
- ShowNotice("and then edit your user/password in conf/map_athena.conf (or conf/import/map_conf.txt)\n");
- }
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int chrif_setip(char *ip)
-{
- char ip_str[16];
- char_ip = resolve_hostbyname(ip,NULL,ip_str);
-
- if (!char_ip) {
- ShowWarning("Failed to Resolve Char Server Address! (%s)\n", ip);
- return 0;
- }
- strncpy(char_ip_str, ip, sizeof(char_ip_str));
- ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip_str);
- return 1;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-void chrif_setport(int port)
-{
- char_port = port;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int chrif_isconnect(void)
-{
- return (char_fd > 0 && session[char_fd] != NULL && chrif_state == 2);
-}
-
-/*==========================================
- * Saves char.
- * Flag = 1: Character is quitting.
- * Flag = 2: Character is changing map-servers
- *------------------------------------------
- */
-int chrif_save(struct map_session_data *sd, int flag)
-{
- nullpo_retr(-1, sd);
-
- pc_makesavestatus(sd);
- if(!chrif_isconnect())
- {
- if (flag) sd->state.finalsave = 1; //Will save character on reconnect.
- return -1;
- }
-
- if (sd->state.finalsave)
- return -1; //Refuse to save a char already tagged for final saving. [Skotlex]
- //For data sync
- if (sd->state.storage_flag == 1)
- storage_storage_save(sd->status.account_id, flag);
- else if (sd->state.storage_flag == 2)
- storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag);
- if (flag) sd->state.storage_flag = 0; //Force close it.
-
- //Saving of registry values.
- if (sd->state.reg_dirty&4)
- intif_saveregistry(sd, 3); //Save char regs
- if (sd->state.reg_dirty&2)
- intif_saveregistry(sd, 2); //Save account regs
- if (sd->state.reg_dirty&1)
- intif_saveregistry(sd, 1); //Save account2 regs
-#ifndef TXT_ONLY
- if(charsave_method){ //New 'Local' save
- charsave_savechar(sd->status.char_id, &sd->status);
- if (flag) //Character final saved.
- sd->state.finalsave = 1;
- if (flag == 1)
- chrif_char_offline(sd); //Tell char server that character went offline.
- return 0;
- }
-#endif
- WFIFOHEAD(char_fd, sizeof(sd->status) + 13);
- WFIFOW(char_fd,0) = 0x2b01;
- WFIFOW(char_fd,2) = sizeof(sd->status) + 13;
- WFIFOL(char_fd,4) = sd->status.account_id;
- WFIFOL(char_fd,8) = sd->status.char_id;
- WFIFOB(char_fd,12) = (flag==1)?1:0; //Flag to tell char-server this character is quitting.
- memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status));
- WFIFOSET(char_fd, WFIFOW(char_fd,2));
-
- if (sd->hd && merc_is_hom_active(sd->hd))
- merc_save(sd->hd);
-
- if (flag)
- sd->state.finalsave = 1; //Mark the last save as done.
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int chrif_connect(int fd)
-{
- ShowStatus("Logging in to char server...\n", char_fd);
- WFIFOHEAD(fd, 60);
- WFIFOW(fd,0) = 0x2af8;
- memcpy(WFIFOP(fd,2), userid, NAME_LENGTH);
- memcpy(WFIFOP(fd,26), passwd, NAME_LENGTH);
- WFIFOL(fd,50) = 0;
- WFIFOL(fd,54) = clif_getip_long();
- WFIFOW(fd,58) = clif_getport(); // [Valaris] thanks to fov
- WFIFOSET(fd,60);
-
- return 0;
-}
-
-/*==========================================
- * マップ送信
- *------------------------------------------
- */
-int chrif_sendmap(int fd)
-{
- int i;
- ShowStatus("Sending maps to char server...\n");
- WFIFOHEAD(fd, 4 + map_num * 4);
- WFIFOW(fd,0) = 0x2afa;
- for(i = 0; i < map_num; i++)
- WFIFOW(fd,4+i*4) = map[i].index;
- WFIFOW(fd,2) = 4 + i * 4;
- WFIFOSET(fd,WFIFOW(fd,2));
-
- return 0;
-}
-
-/*==========================================
- * マップ受信
- *------------------------------------------
- */
-int chrif_recvmap(int fd)
-{
- int i, j, ip, port;
- unsigned char *p = (unsigned char *)&ip;
- RFIFOHEAD(fd);
- ip = RFIFOL(fd,4);
- port = RFIFOW(fd,8);
- for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) {
- map_setipport(RFIFOW(fd,i), ip, port);
-// if (battle_config.etc_log)
-// printf("recv map %d %s\n", j, RFIFOP(fd,i));
- }
- if (battle_config.etc_log)
- ShowStatus("recv map on %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
-
- other_mapserver_count++;
- return 0;
-}
-
-/*==========================================
- * Delete maps of other servers, (if an other mapserver is going OFF)
- *------------------------------------------
- */
-int chrif_removemap(int fd){
- int i, j, ip, port;
- unsigned char *p = (unsigned char *)&ip;
- RFIFOHEAD(fd);
-
- ip = RFIFOL(fd, 4);
- port = RFIFOW(fd, 8);
-
- for(i = 10, j = 0; i < RFIFOW(fd, 2); i += 4, j++){
- map_eraseipport(RFIFOW(fd, i), ip, port);
- }
-
- other_mapserver_count--;
- if(battle_config.etc_log)
- ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
- return 0;
-}
-
-int chrif_save_ack(int fd) {
- struct map_session_data *sd;
- RFIFOHEAD(fd);
- sd = map_id2sd(RFIFOL(fd,2));
-
- if (sd && sd->status.char_id == RFIFOL(fd,6))
- map_quit_ack(sd);
- return 0;
-}
-
-/*==========================================
- * マップ鯖間移動のためのデータ準備要求
- *------------------------------------------
- */
-int chrif_changemapserver(struct map_session_data *sd, short map, int x, int y, int ip, short port)
-{
- int s_ip;
-
- nullpo_retr(-1, sd);
-
- chrif_check(-1);
-
- if (other_mapserver_count < 1)
- { //No other map servers are online!
- clif_authfail_fd(sd->fd, 0);
- return -1;
- }
-
- if (sd->fd && sd->fd < fd_max && session[sd->fd])
- s_ip = session[sd->fd]->client_addr.sin_addr.s_addr;
- else //Not connected? Can't retrieve IP
- s_ip = 0;
-
- WFIFOHEAD(char_fd, 35);
- WFIFOW(char_fd, 0) = 0x2b05;
- WFIFOL(char_fd, 2) = sd->bl.id;
- WFIFOL(char_fd, 6) = sd->login_id1;
- WFIFOL(char_fd,10) = sd->login_id2;
- WFIFOL(char_fd,14) = sd->status.char_id;
- WFIFOW(char_fd,18) = map;
- WFIFOW(char_fd,20) = x;
- WFIFOW(char_fd,22) = y;
- WFIFOL(char_fd,24) = ip;
- WFIFOW(char_fd,28) = port;
- WFIFOB(char_fd,30) = sd->status.sex;
- WFIFOL(char_fd,31) = s_ip;
- WFIFOSET(char_fd,35);
-
- return 0;
-}
-
-/*==========================================
- * マップ鯖間移動ack
- *------------------------------------------
- */
-int chrif_changemapserverack(int fd)
-{
- struct map_session_data *sd;
- RFIFOHEAD(fd);
- sd = map_id2sd(RFIFOL(fd,2));
-
- if (sd == NULL || sd->status.char_id != RFIFOL(fd,14))
- return -1;
-
- if (RFIFOL(fd,6) == 1) {
- if (battle_config.error_log)
- ShowError("map server change failed.\n");
- clif_authfail_fd(sd->fd, 0);
- return 0;
- }
- clif_changemapserver(sd, (char*)mapindex_id2name(RFIFOW(fd,18)), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28));
-
- //Player has been saved already, remove him from memory. [Skotlex]
- map_quit(sd);
- map_quit_ack(sd);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int chrif_connectack(int fd)
-{
- RFIFOHEAD(fd);
- if (RFIFOB(fd,2)) {
- ShowFatalError("Connection to char-server failed %d.\n", RFIFOB(fd,2));
- exit(1);
- }
- ShowStatus("Successfully logged on to Char Server (Connection: '"CL_WHITE"%d"CL_RESET"').\n",fd);
- chrif_state = 1;
- chrif_connected=1;
-
- chrif_sendmap(fd);
-
- ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit"));
- ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
- if(!char_init_done) {
- char_init_done = 1;
- ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce"));
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int chrif_sendmapack(int fd)
-{
- RFIFOHEAD(fd);
- if (RFIFOB(fd,2)) {
- ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
- exit(1);
- }
- memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
- ShowStatus("Map sending complete. Map Server is now online.\n");
- chrif_state = 2;
-
- //If there are players online, send them to the char-server. [Skotlex]
- send_users_tochar(-1, gettick(), 0, 0);
-
- //Re-save any storages that were modified in the disconnection time. [Skotlex]
- do_reconnect_map();
- do_reconnect_storage();
-
- return 0;
-}
-
-/*==========================================
- * Request sc_data from charserver [Skotlex]
- *------------------------------------------
- */
-int chrif_scdata_request(int account_id, int char_id)
-{
-#ifdef ENABLE_SC_SAVING
-#ifndef TXT_ONLY
- if (charsave_method)
- return charsave_load_scdata(account_id, char_id);
-#endif
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 10);
- WFIFOW(char_fd, 0) = 0x2afc;
- WFIFOL(char_fd, 2) = account_id;
- WFIFOL(char_fd, 6) = char_id;
- WFIFOSET(char_fd,10);
-#endif
- return 0;
-}
-
-/*==========================================
- * new auth system [Kevin]
- *------------------------------------------
- */
-void chrif_authreq(struct map_session_data *sd)
-{
- struct auth_node *auth_data;
- auth_data=idb_get(auth_db, sd->bl.id);
-
- if(auth_data) {
- if(auth_data->char_dat &&
- auth_data->account_id== sd->bl.id &&
- auth_data->login_id1 == sd->login_id1)
- { //auth ok
- pc_authok(sd, auth_data->login_id2, auth_data->connect_until_time, auth_data->char_dat);
- } else { //auth failed
- pc_authfail(sd);
- chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
- }
- if (auth_data->char_dat)
- aFree(auth_data->char_dat);
- idb_remove(auth_db, sd->bl.id);
- } else { //data from char server has not arrived yet.
- auth_data = aCalloc(1,sizeof(struct auth_node));
- auth_data->sd = sd;
- auth_data->fd = sd->fd;
- auth_data->account_id = sd->bl.id;
- auth_data->login_id1 = sd->login_id1;
- auth_data->node_created = gettick();
- uidb_put(auth_db, sd->bl.id, auth_data);
- }
- return;
-}
-
-//character selected, insert into auth db
-void chrif_authok(int fd) {
- struct auth_node *auth_data;
- RFIFOHEAD(fd);
-
- if (map_id2sd(RFIFOL(fd, 4)) != NULL)
- //Someone with this account is already in! Do not store the info to prevent possible sync exploits. [Skotlex]
- return;
-
- if ((auth_data =uidb_get(auth_db, RFIFOL(fd, 4))) != NULL)
- { //Is the character already awaiting authorization?
- if (auth_data->sd)
- {
- //First, check to see if the session data still exists (avoid dangling pointers)
- if(session[auth_data->fd] && session[auth_data->fd]->session_data == auth_data->sd)
- {
- if (auth_data->char_dat == NULL &&
- auth_data->account_id == RFIFOL(fd, 4) &&
- auth_data->login_id1 == RFIFOL(fd, 8))
- { //Auth Ok
- pc_authok(auth_data->sd, RFIFOL(fd, 16), RFIFOL(fd, 12), (struct mmo_charstatus*)RFIFOP(fd, 20));
- } else { //Auth Failed
- pc_authfail(auth_data->sd);
- chrif_char_offline(auth_data->sd); //Set him offline, the char server likely has it set as online already.
- }
- } //else: Character no longer exists, just go through.
- }
- //Delete the data of this node...
- if (auth_data->char_dat)
- aFree (auth_data->char_dat);
- uidb_remove(auth_db, RFIFOL(fd, 4));
- return;
- }
- // Awaiting for client to connect.
- auth_data = (struct auth_node *)aCalloc(1,sizeof(struct auth_node));
- auth_data->char_dat = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus));
-
- auth_data->account_id=RFIFOL(fd, 4);
- auth_data->login_id1=RFIFOL(fd, 8);
- auth_data->connect_until_time=RFIFOL(fd, 12);
- auth_data->login_id2=RFIFOL(fd, 16);
- memcpy(auth_data->char_dat,RFIFOP(fd, 20),sizeof(struct mmo_charstatus));
- auth_data->node_created=gettick();
- uidb_put(auth_db, RFIFOL(fd, 4), auth_data);
-}
-
-int auth_db_cleanup_sub(DBKey key,void *data,va_list ap)
-{
- struct auth_node *node=(struct auth_node*)data;
-
- if(DIFF_TICK(gettick(),node->node_created)>30000) {
- ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id);
- if (node->char_dat)
- aFree(node->char_dat);
- db_remove(auth_db, key);
- return 1;
- }
- return 0;
-}
-
-int auth_db_cleanup(int tid, unsigned int tick, int id, int data) {
- auth_db->foreach(auth_db, auth_db_cleanup_sub);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip)
-{
- nullpo_retr(-1, sd);
-
- if( !sd || !sd->bl.id || !sd->login_id1 )
- return -1;
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 18);
- WFIFOW(char_fd, 0) = 0x2b02;
- WFIFOL(char_fd, 2) = sd->bl.id;
- WFIFOL(char_fd, 6) = sd->login_id1;
- WFIFOL(char_fd,10) = sd->login_id2;
- WFIFOL(char_fd,14) = s_ip;
- WFIFOSET(char_fd,18);
-
- return 0;
-}
-
-/*==========================================
- * キャラ名問い合わせ
- *------------------------------------------
- */
-int chrif_searchcharid(int char_id)
-{
- if( !char_id )
- return -1;
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 6);
- WFIFOW(char_fd,0) = 0x2b08;
- WFIFOL(char_fd,2) = char_id;
- WFIFOSET(char_fd,6);
-
- return 0;
-}
-
-/*==========================================
- * GMに変化要求
- *------------------------------------------
- */
-int chrif_changegm(int id, const char *pass, int len)
-{
- if (battle_config.etc_log)
- ShowInfo("chrif_changegm: account: %d, password: '%s'.\n", id, pass);
-
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, len + 8);
- WFIFOW(char_fd,0) = 0x2b0a;
- WFIFOW(char_fd,2) = len + 8;
- WFIFOL(char_fd,4) = id;
- memcpy(WFIFOP(char_fd,8), pass, len);
- WFIFOSET(char_fd, len + 8);
-
- return 0;
-}
-
-/*==========================================
- * Change Email
- *------------------------------------------
- */
-int chrif_changeemail(int id, const char *actual_email, const char *new_email)
-{
- if (battle_config.etc_log)
- ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email);
-
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 86);
- WFIFOW(char_fd,0) = 0x2b0c;
- WFIFOL(char_fd,2) = id;
- memcpy(WFIFOP(char_fd,6), actual_email, 40);
- memcpy(WFIFOP(char_fd,46), new_email, 40);
- WFIFOSET(char_fd,86);
-
- return 0;
-}
-
-/*==========================================
- * Send message to char-server with a character name to do some operations (by Yor)
- * Used to ask Char-server about a character name to have the account number to modify account file in login-server.
- * type of operation:
- * 1: block
- * 2: ban
- * 3: unblock
- * 4: unban
- * 5: changesex
- *------------------------------------------
- */
-int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second)
-{
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 44);
- WFIFOW(char_fd, 0) = 0x2b0e;
- WFIFOL(char_fd, 2) = id; // account_id of who ask (for answer) -1 if nobody
- memcpy(WFIFOP(char_fd,6), character_name, NAME_LENGTH);
- WFIFOW(char_fd, 30) = operation_type; // type of operation
- if (operation_type == 2) {
- WFIFOW(char_fd, 32) = year;
- WFIFOW(char_fd, 34) = month;
- WFIFOW(char_fd, 36) = day;
- WFIFOW(char_fd, 38) = hour;
- WFIFOW(char_fd, 40) = minute;
- WFIFOW(char_fd, 42) = second;
- }
-// ShowInfo("chrif : sent 0x2b0e\n");
- WFIFOSET(char_fd,44);
-
- return 0;
-}
-
-/*==========================================
- * 性別変化要求
- *------------------------------------------
- */
-int chrif_changesex(int id, int sex) {
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 9);
- WFIFOW(char_fd,0) = 0x2b11;
- WFIFOW(char_fd,2) = 9;
- WFIFOL(char_fd,4) = id;
- WFIFOB(char_fd,8) = sex;
-// ShowInfo("chrif : sent 0x3000(changesex)\n");
- WFIFOSET(char_fd,9);
- return 0;
-}
-
-/*==========================================
- * Answer after a request about a character name to do some operations (by Yor)
- * Used to answer of chrif_char_ask_name.
- * type of operation:
- * 1: block
- * 2: ban
- * 3: unblock
- * 4: unban
- * 5: changesex
- * type of answer:
- * 0: login-server resquest done
- * 1: player not found
- * 2: gm level too low
- * 3: login-server offline
- *------------------------------------------
- */
-int chrif_char_ask_name_answer(int fd)
-{
- int acc;
- struct map_session_data *sd;
- char output[256];
- char player_name[NAME_LENGTH];
- RFIFOHEAD(fd);
-
- acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody)
- memcpy(player_name, RFIFOP(fd,6), NAME_LENGTH-1);
- player_name[NAME_LENGTH-1] = '\0';
-
- sd = map_id2sd(acc);
- if (acc >= 0 && sd != NULL) {
- if (RFIFOW(fd, 32) == 1) // player not found
- sprintf(output, "The player '%s' doesn't exist.", player_name);
- else {
- switch(RFIFOW(fd, 30)) {
- case 1: // block
- switch(RFIFOW(fd, 32)) {
- case 0: // login-server resquest done
- sprintf(output, "Login-server has been asked to block the player '%s'.", player_name);
- break;
- //case 1: // player not found
- case 2: // gm level too low
- sprintf(output, "Your GM level don't authorise you to block the player '%s'.", player_name);
- break;
- case 3: // login-server offline
- sprintf(output, "Login-server is offline. Impossible to block the the player '%s'.", player_name);
- break;
- }
- break;
- case 2: // ban
- switch(RFIFOW(fd, 32)) {
- case 0: // login-server resquest done
- sprintf(output, "Login-server has been asked to ban the player '%s'.", player_name);
- break;
- //case 1: // player not found
- case 2: // gm level too low
- sprintf(output, "Your GM level don't authorise you to ban the player '%s'.", player_name);
- break;
- case 3: // login-server offline
- sprintf(output, "Login-server is offline. Impossible to ban the the player '%s'.", player_name);
- break;
- }
- break;
- case 3: // unblock
- switch(RFIFOW(fd, 32)) {
- case 0: // login-server resquest done
- sprintf(output, "Login-server has been asked to unblock the player '%s'.", player_name);
- break;
- //case 1: // player not found
- case 2: // gm level too low
- sprintf(output, "Your GM level don't authorise you to unblock the player '%s'.", player_name);
- break;
- case 3: // login-server offline
- sprintf(output, "Login-server is offline. Impossible to unblock the the player '%s'.", player_name);
- break;
- }
- break;
- case 4: // unban
- switch(RFIFOW(fd, 32)) {
- case 0: // login-server resquest done
- sprintf(output, "Login-server has been asked to unban the player '%s'.", player_name);
- break;
- //case 1: // player not found
- case 2: // gm level too low
- sprintf(output, "Your GM level don't authorise you to unban the player '%s'.", player_name);
- break;
- case 3: // login-server offline
- sprintf(output, "Login-server is offline. Impossible to unban the the player '%s'.", player_name);
- break;
- }
- break;
- case 5: // changesex
- switch(RFIFOW(fd, 32)) {
- case 0: // login-server resquest done
- sprintf(output, "Login-server has been asked to change the sex of the player '%s'.", player_name);
- break;
- //case 1: // player not found
- case 2: // gm level too low
- sprintf(output, "Your GM level don't authorise you to change the sex of the player '%s'.", player_name);
- break;
- case 3: // login-server offline
- sprintf(output, "Login-server is offline. Impossible to change the sex of the the player '%s'.", player_name);
- break;
- }
- break;
- }
- }
- if (output[0] != '\0') {
- output[sizeof(output)-1] = '\0';
- clif_displaymessage(sd->fd, output);
- }
- } else
- ShowError("chrif_char_ask_name_answer failed - player not online.\n");
-
- return 0;
-}
-
-/*==========================================
- * End of GM change (@GM) (modified by Yor)
- *------------------------------------------
- */
-int chrif_changedgm(int fd)
-{
- int acc, level;
- struct map_session_data *sd = NULL;
- RFIFOHEAD(fd);
-
- acc = RFIFOL(fd,2);
- level = RFIFOL(fd,6);
-
- sd = map_id2sd(acc);
-
- if (battle_config.etc_log)
- ShowNotice("chrif_changedgm: account: %d, GM level 0 -> %d.\n", acc, level);
- if (sd != NULL) {
- if (level > 0)
- clif_displaymessage(sd->fd, "GM modification success.");
- else
- clif_displaymessage(sd->fd, "Failure of GM modification.");
- }
-
- return 0;
-}
-
-/*==========================================
- * 性別変化終了 (modified by Yor)
- *------------------------------------------
- */
-int chrif_changedsex(int fd)
-{
- int acc, sex, i;
- struct map_session_data *sd;
- RFIFOHEAD(fd);
-
- acc = RFIFOL(fd,2);
- sex = RFIFOL(fd,6);
- if (battle_config.etc_log)
- ShowNotice("chrif_changedsex %d.\n", acc);
- sd = map_id2sd(acc);
- if (acc > 0) {
- if (sd != NULL && sd->status.sex != sex) {
- sd->status.sex = !sd->status.sex;
-
- // to avoid any problem with equipment and invalid sex, equipment is unequiped.
- for (i = 0; i < MAX_INVENTORY; i++) {
- if (sd->status.inventory[i].nameid && sd->status.inventory[i].equip)
- pc_unequipitem((struct map_session_data*)sd, i, 2);
- }
- // reset skill of some job
- if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) {
- // remove specifical skills of Bard classes
- for(i = 315; i <= 322; i++) {
- if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
- if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv)
- sd->status.skill_point = USHRT_MAX;
- else
- sd->status.skill_point += sd->status.skill[i].lv;
- sd->status.skill[i].id = 0;
- sd->status.skill[i].lv = 0;
- }
- }
- // remove specifical skills of Dancer classes
- for(i = 323; i <= 330; i++) {
- if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
- if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv)
- sd->status.skill_point = USHRT_MAX;
- else
- sd->status.skill_point += sd->status.skill[i].lv;
- sd->status.skill[i].id = 0;
- sd->status.skill[i].lv = 0;
- }
- }
- clif_updatestatus(sd, SP_SKILLPOINT);
- // change job if necessary
- if (sd->status.sex) //Changed from Dancer
- sd->status.class_ -= 1;
- else //Changed from Bard
- sd->status.class_ += 1;
- //sd->class_ needs not be updated as both Dancer/Bard are the same.
- }
- // save character
- //chrif_save(sd,1); Character will be saved on session closed -> map_quit
- sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
- // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it)
- clif_displaymessage(sd->fd, "Your sex has been changed (need disconnection by the server)...");
- clif_setwaitclose(sd->fd); // forced to disconnect for the change
- }
- } else {
- if (sd != NULL) {
- ShowError("chrif_changedsex failed.\n");
- }
- }
-
- return 0;
-}
-
-/*==========================================
- * 離婚情報同期要求
- *------------------------------------------
- */
-int chrif_divorce(int char_id, int partner_id)
-{
- struct map_session_data *sd = NULL;
-
- if (!char_id || !partner_id)
- return 0;
-
- nullpo_retr(0, sd = map_nick2sd(map_charid2nick(partner_id)));
- if (sd->status.partner_id == char_id) {
- int i;
- //離婚(相方は既にキャラが消えている筈なので)
- sd->status.partner_id = 0;
-
- //相方の結婚指輪を剥奪
- for(i = 0; i < MAX_INVENTORY; i++)
- if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
- pc_delitem(sd, i, 1, 0);
- }
-
- return 0;
-}
-
-/*==========================================
- * Disconnection of a player (account has been deleted in login-server) by [Yor]
- *------------------------------------------
- */
-int chrif_accountdeletion(int fd)
-{
- int acc;
- struct map_session_data *sd;
- RFIFOHEAD(fd);
-
- acc = RFIFOL(fd,2);
- if (battle_config.etc_log)
- ShowNotice("chrif_accountdeletion %d.\n", acc);
- sd = map_id2sd(acc);
- if (acc > 0) {
- if (sd != NULL) {
- sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
- clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)...");
- clif_setwaitclose(sd->fd); // forced to disconnect for the change
- }
- } else {
- if (sd != NULL)
- ShowError("chrif_accountdeletion failed - player not online.\n");
- }
-
- return 0;
-}
-
-/*==========================================
- * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor]
- *------------------------------------------
- */
-int chrif_accountban(int fd)
-{
- int acc;
- struct map_session_data *sd;
- RFIFOHEAD(fd);
-
- acc = RFIFOL(fd,2);
- if (battle_config.etc_log)
- ShowNotice("chrif_accountban %d.\n", acc);
- sd = map_id2sd(acc);
- if (acc > 0) {
- if (sd != NULL) {
- sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
- if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban
- switch (RFIFOL(fd,7)) { // status or final date of a banishment
- case 1: // 0 = Unregistered ID
- clif_displaymessage(sd->fd, "Your account has 'Unregistered'.");
- break;
- case 2: // 1 = Incorrect Password
- clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'...");
- break;
- case 3: // 2 = This ID is expired
- clif_displaymessage(sd->fd, "Your account has expired.");
- break;
- case 4: // 3 = Rejected from Server
- clif_displaymessage(sd->fd, "Your account has been rejected from server.");
- break;
- case 5: // 4 = You have been blocked by the GM Team
- clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team.");
- break;
- case 6: // 5 = Your Game's EXE file is not the latest version
- clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version.");
- break;
- case 7: // 6 = Your are Prohibited to log in until %s
- clif_displaymessage(sd->fd, "Your account has been prohibited to log in.");
- break;
- case 8: // 7 = Server is jammed due to over populated
- clif_displaymessage(sd->fd, "Server is jammed due to over populated.");
- break;
- case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this)
- clif_displaymessage(sd->fd, "Your account has not more authorised.");
- break;
- case 100: // 99 = This ID has been totally erased
- clif_displaymessage(sd->fd, "Your account has been totally erased.");
- break;
- default:
- clif_displaymessage(sd->fd, "Your account has not more authorised.");
- break;
- }
- } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban
- time_t timestamp;
- char tmpstr[2048];
- timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment
- strcpy(tmpstr, "Your account has been banished until ");
- strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(&timestamp));
- clif_displaymessage(sd->fd, tmpstr);
- }
- clif_setwaitclose(sd->fd); // forced to disconnect for the change
- }
- } else {
- if (sd != NULL)
- ShowError("chrif_accountban failed - player not online.\n");
- }
-
- return 0;
-}
-
-//Disconnect the player out of the game, simple packet
-//packet.w AID.L WHY.B 2+4+1 = 7byte
-int chrif_disconnectplayer(int fd){
- struct map_session_data *sd;
- RFIFOHEAD(fd);
-
- sd = map_id2sd(RFIFOL(fd, 2));
-
- if(sd == NULL){
- return -1;
- }
-
- if (!sd->fd)
- { //No connection
- if (sd->state.autotrade)
- map_quit(sd); //Remove it.
- //Else we don't remove it because the char should have a timer to remove the player because it force-quit before,
- //and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex]
- return 0;
- }
-
- switch(RFIFOB(fd, 6)){
- //clif_authfail_fd
- case 1: //server closed
- clif_authfail_fd(sd->fd, 1);
- break;
-
- case 2: //someone else logged in
- clif_authfail_fd(sd->fd, 2);
- break;
-
- case 3: //server overpopulated
- clif_authfail_fd(sd->fd, 4);
-
- break;
-
- case 4: //out of time payd for .. (avail)
- clif_authfail_fd(sd->fd, 10);
- break;
-
- case 5: //forced to dc by gm
- clif_authfail_fd(sd->fd, 15);
- break;
- }
-
-return 0;
-}
-
-/*==========================================
- * Request to reload GM accounts and their levels: send to char-server by [Yor]
- *------------------------------------------
- */
-int chrif_reloadGMdb(void)
-{
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 2);
- WFIFOW(char_fd,0) = 0x2af7;
- WFIFOSET(char_fd, 2);
-
- return 0;
-}
-
-/*==========================================
- * Receiving GM accounts and their levels from char-server by [Yor]
- *------------------------------------------
- */
-int chrif_recvgmaccounts(int fd)
-{
- ShowInfo("From login-server: receiving information of '"CL_WHITE"%d"CL_RESET"' GM accounts.\n", pc_read_gm_account(fd));
- return 0;
-}
-
-/*==========================================
- * Request/Receive top 10 Fame character list
- *------------------------------------------
- */
-
-int chrif_updatefamelist(struct map_session_data *sd)
-{
- char type;
- chrif_check(-1);
-
- switch(sd->class_&MAPID_UPPERMASK) {
- case MAPID_BLACKSMITH:
- type = 1;
- break;
- case MAPID_ALCHEMIST:
- type = 2;
- break;
- case MAPID_TAEKWON:
- type = 3;
- break;
- default:
- return 0;
- }
-
- WFIFOHEAD(char_fd, 12);
- WFIFOW(char_fd, 0) = 0x2b10;
- WFIFOL(char_fd, 2) = sd->status.char_id;
- WFIFOL(char_fd, 6) = sd->status.fame;
- WFIFOB(char_fd, 10) = type;
- WFIFOB(char_fd, 11) = pc_famerank(sd->status.char_id, sd->class_&MAPID_UPPERMASK);
- WFIFOSET(char_fd, 12);
-
- return 0;
-}
-
-int chrif_buildfamelist(void)
-{
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 2);
- WFIFOW(char_fd, 0) = 0x2b1a;
- WFIFOSET(char_fd, 2);
-
- return 0;
-}
-
-int chrif_recvfamelist(int fd)
-{
- int num, size;
- int total = 0, len = 8;
- RFIFOHEAD(fd);
-
- malloc_tsetdword (smith_fame_list, 0, sizeof(smith_fame_list));
- malloc_tsetdword (chemist_fame_list, 0, sizeof(chemist_fame_list));
- malloc_tsetdword (taekwon_fame_list, 0, sizeof(taekwon_fame_list));
-
- size = RFIFOW(fd, 6); //Blacksmith block size
- for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
- memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
- len += sizeof(struct fame_list);
- }
- total += num;
-
- size = RFIFOW(fd, 4); //Alchemist block size
- for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
- memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
- len += sizeof(struct fame_list);
- }
- total += num;
-
- size = RFIFOW(fd, 2); //Total packet length
- for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
- memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
- len += sizeof(struct fame_list);
- }
- total += num;
-
- ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total);
-
- return 0;
-}
-
-int chrif_save_scdata(struct map_session_data *sd)
-{ //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex]
-#ifdef ENABLE_SC_SAVING
- int i, count=0;
- unsigned int tick;
- struct status_change_data data;
- struct TimerData *timer;
-
- if (sd->state.finalsave) //Character was already saved?
- return -1;
-#ifndef TXT_ONLY
- if(charsave_method) //New 'Local' save
- {
- charsave_save_scdata(sd->status.account_id, sd->status.char_id, &sd->sc, MAX_STATUSCHANGE);
- return 0;
- }
-#endif
-
- chrif_check(-1);
- tick = gettick();
-
- WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data));
- WFIFOW(char_fd,0) = 0x2b1c;
- WFIFOL(char_fd,4) = sd->status.account_id;
- WFIFOL(char_fd,8) = sd->status.char_id;
- for (i = 0; i < SC_MAX; i++)
- {
- if (sd->sc.data[i].timer == -1)
- continue;
- timer = get_timer(sd->sc.data[i].timer);
- if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
- continue;
- data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending.
- data.type = i;
- data.val1 = sd->sc.data[i].val1;
- data.val2 = sd->sc.data[i].val2;
- data.val3 = sd->sc.data[i].val3;
- data.val4 = sd->sc.data[i].val4;
- memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)),
- &data, sizeof(struct status_change_data));
- count++;
- }
- if (count == 0)
- return 0; //Nothing to save.
- WFIFOW(char_fd,12) = count;
- WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
- WFIFOSET(char_fd,WFIFOW(char_fd,2));
-#endif
- return 0;
-}
-
-int chrif_load_scdata(int fd)
-{ //Retrieve and load sc_data for a player. [Skotlex]
-#ifdef ENABLE_SC_SAVING
- struct map_session_data *sd;
- struct status_change_data *data;
- int aid, cid, i, count;
- RFIFOHEAD(fd);
-
- aid = RFIFOL(fd,4); //Player Account ID
- cid = RFIFOL(fd,8); //Player Char ID
-
- sd = map_id2sd(aid);
- if (!sd)
- {
- ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid);
- return -1;
- }
- if (sd->status.char_id != cid)
- {
- ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
- return -1;
- }
- count = RFIFOW(fd,12); //sc_count
- for (i = 0; i < count; i++)
- {
- data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data));
- if (data->tick < 1)
- { //Protection against invalid tick values. [Skotlex]
- ShowWarning("chrif_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", data->tick, data->type, sd->status.name);
- continue;
- }
- status_change_start(&sd->bl, data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 15);
- }
-#endif
- return 0;
-}
-
-/*==========================================
- * Send rates and motd to char server [Wizputer]
- *------------------------------------------
- */
- int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate)
-{
- char buf[256];
- FILE *fp;
- int i;
-
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, sizeof(buf) + 10);
- WFIFOW(char_fd,0) = 0x2b16;
- WFIFOW(char_fd,2) = base_rate;
- WFIFOW(char_fd,4) = job_rate;
- WFIFOW(char_fd,6) = drop_rate;
-
- if ((fp = fopen(motd_txt, "r")) != NULL) {
- if (fgets(buf, 250, fp) != NULL) {
- for(i = 0; buf[i]; i++) {
- if (buf[i] == '\r' || buf[i] == '\n') {
- buf[i] = 0;
- break;
- }
- }
- WFIFOW(char_fd,8) = sizeof(buf) + 10;
- memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
- }
- fclose(fp);
- } else {
- malloc_tsetdword(buf, 0, sizeof(buf)); //No data found, send empty packets?
- WFIFOW(char_fd,8) = sizeof(buf) + 10;
- memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
- }
- WFIFOSET(char_fd,WFIFOW(char_fd,8));
- return 0;
-}
-
-
-/*=========================================
- * Tell char-server charcter disconnected [Wizputer]
- *-----------------------------------------
- */
-
-int chrif_char_offline(struct map_session_data *sd)
-{
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 10);
- WFIFOW(char_fd,0) = 0x2b17;
- WFIFOL(char_fd,2) = sd->status.char_id;
- WFIFOL(char_fd,6) = sd->status.account_id;
- WFIFOSET(char_fd,10);
-
- return 0;
-}
-
-/*=========================================
- * Tell char-server to reset all chars offline [Wizputer]
- *-----------------------------------------
- */
-int chrif_flush_fifo(void) {
- chrif_check(-1);
-
- set_nonblocking(char_fd, 0);
- flush_fifos();
- set_nonblocking(char_fd, 1);
-
- return 0;
-}
-
-/*=========================================
- * Tell char-server to reset all chars offline [Wizputer]
- *-----------------------------------------
- */
-int chrif_char_reset_offline(void) {
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 2);
- WFIFOW(char_fd,0) = 0x2b18;
- WFIFOSET(char_fd,2);
-
- return 0;
-}
-
-/*=========================================
- * Tell char-server charcter is online [Wizputer]
- *-----------------------------------------
- */
-
-int chrif_char_online(struct map_session_data *sd)
-{
- chrif_check(-1);
-
- WFIFOHEAD(char_fd, 10);
- WFIFOW(char_fd,0) = 0x2b19;
- WFIFOL(char_fd,2) = sd->status.char_id;
- WFIFOL(char_fd,6) = sd->status.account_id;
- WFIFOSET(char_fd,10);
-
- return 0;
-}
-
-int chrif_disconnect(int fd) {
- if(fd == char_fd) {
- char_fd = 0;
- ShowWarning("Map Server disconnected from Char Server.\n\n");
- chrif_connected = 0;
-
- other_mapserver_count=0; //Reset counter. We receive ALL maps from all map-servers on reconnect.
- map_eraseallipport();
-
- //Attempt to reconnect in a second. [Skotlex]
- add_timer(gettick() + 1000, check_connect_char_server, 0, 0);
- }
- return 0;
-}
-
-void chrif_update_ip(int fd){
- unsigned long new_ip;
- WFIFOHEAD(fd, 6);
- new_ip = resolve_hostbyname(char_ip_str, NULL, NULL);
- if (new_ip && new_ip != char_ip)
- char_ip = new_ip; //Update char_ip
-
- new_ip = clif_refresh_ip();
- if (!new_ip) return; //No change
- WFIFOW(fd, 0) = 0x2736;
- WFIFOL(fd, 2) = new_ip;
- WFIFOSET(fd, 6);
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int chrif_parse(int fd)
-{
- int packet_len, cmd;
- // only char-server can have an access to here.
- // so, if it isn't the char-server, we disconnect the session (fd != char_fd).
- if (fd != char_fd || session[fd]->eof) {
- if (fd == char_fd && chrif_connected == 1) {
- chrif_disconnect (fd);
- }
- else if (fd != char_fd)
- ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd);
-
- do_close(fd);
- return 0;
- }
-
- while (RFIFOREST(fd) >= 2 && !session[fd]->eof) { //Infinite loop on broken pipe fix. [Skotlex]
- RFIFOHEAD(fd);
- cmd = RFIFOW(fd,0);
- if (cmd < 0x2af8 || cmd >= 0x2af8 + (sizeof(packet_len_table) / sizeof(packet_len_table[0])) ||
- packet_len_table[cmd-0x2af8] == 0) {
-
- int r = intif_parse(fd); // intifに渡す
-
- if (r == 1) continue; // intifで処理した
- if (r == 2) return 0; // intifで処理したが、データが足りない
-
- session[fd]->eof = 1;
- ShowWarning("chrif_parse: session #%d, intif_parse failed -> disconnected.\n", fd);
- return 0;
- }
- packet_len = packet_len_table[cmd-0x2af8];
- if (packet_len == -1) {
- if (RFIFOREST(fd) < 4)
- return 0;
- packet_len = RFIFOW(fd,2);
- }
- if ((int)RFIFOREST(fd) < packet_len)
- return 0;
-
- switch(cmd) {
- case 0x2af9: chrif_connectack(fd); break;
- case 0x2afb: chrif_sendmapack(fd); break;
- case 0x2afd: chrif_authok(fd); break;
- case 0x2b00: map_setusers(fd); break;
- case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break;
- case 0x2b04: chrif_recvmap(fd); break;
- case 0x2b06: chrif_changemapserverack(fd); break;
- case 0x2b07: clif_updatemaxid(RFIFOL(fd,2), RFIFOL(fd,6)); break;
- case 0x2b09: map_addchariddb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
- case 0x2b0b: chrif_changedgm(fd); break;
- case 0x2b0d: chrif_changedsex(fd); break;
- case 0x2b0f: chrif_char_ask_name_answer(fd); break;
- case 0x2b12: chrif_divorce(RFIFOL(fd,2), RFIFOL(fd,6)); break;
- case 0x2b13: chrif_accountdeletion(fd); break;
- case 0x2b14: chrif_accountban(fd); break;
- case 0x2b15: chrif_recvgmaccounts(fd); break;
- case 0x2b1b: chrif_recvfamelist(fd); break;
- case 0x2b1d: chrif_load_scdata(fd); break;
- case 0x2b1e: chrif_update_ip(fd); break;
- case 0x2b1f: chrif_disconnectplayer(fd); break;
- case 0x2b20: chrif_removemap(fd); break;
- case 0x2b21: chrif_save_ack(fd); break;
-
- default:
- if (battle_config.error_log)
- ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
- session[fd]->eof = 1;
- return 0;
- }
- if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex]
- RFIFOSKIP(fd, packet_len);
- }
-
- return 0;
-}
-
-int send_usercount_tochar(int tid, unsigned int tick, int id, int data) {
- int count;
- static int last_count = 0;
-
- chrif_check(-1);
-
- map_getallusers(&count);
-
- if (count == last_count) //No need to waste packets.
- return 0;
- last_count = count;
-
- WFIFOHEAD(char_fd, 4);
- WFIFOW(char_fd,0) = 0x2afe;
- WFIFOW(char_fd,2) = count;
- WFIFOSET(char_fd,4);
- return 0;
-}
-
-/*==========================================
- * timer関数
- * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る
- *------------------------------------------
- */
-int send_users_tochar(int tid, unsigned int tick, int id, int data) {
- int count, users=0, i;
- struct map_session_data **all_sd;
-
- chrif_check(-1);
-
- all_sd = map_getallusers(&count);
- WFIFOHEAD(char_fd, 6+8*users);
- WFIFOW(char_fd,0) = 0x2aff;
- for (i = 0; i < count; i++) {
- WFIFOL(char_fd,6+8*users) = all_sd[i]->status.account_id;
- WFIFOL(char_fd,6+8*users+4) = all_sd[i]->status.char_id;
- users++;
- }
- WFIFOW(char_fd,2) = 6 + 8 * users;
- WFIFOW(char_fd,4) = users;
- WFIFOSET(char_fd,6+8*users);
-
- return 0;
-}
-
-/*==========================================
- * timer関数
- * char鯖との接続を確認し、もし切れていたら再度接続する
- *------------------------------------------
- */
-int check_connect_char_server(int tid, unsigned int tick, int id, int data) {
- static int displayed = 0;
- if (char_fd <= 0 || session[char_fd] == NULL) {
- if (!displayed) {
- ShowStatus("Attempting to connect to Char Server. Please wait.\n");
- displayed = 1;
- }
- chrif_state = 0;
- char_fd = make_connection(char_ip, char_port);
- if (char_fd == -1)
- { //Attempt to connect later. [Skotlex]
- char_fd = 0;
- return 0;
- }
- session[char_fd]->func_parse = chrif_parse;
- realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
-
- chrif_connect(char_fd);
- chrif_connected = (chrif_state == 2);
-#ifndef TXT_ONLY
- srvinfo = 0;
-#endif /* not TXT_ONLY */
- } else {
-#ifndef TXT_ONLY
- if (srvinfo == 0) {
- chrif_ragsrvinfo(battle_config.base_exp_rate, battle_config.job_exp_rate, battle_config.item_rate_common);
- srvinfo = 1;
- }
-#endif /* not TXT_ONLY */
-/* There is no need, the connection is TCP, so the packet is assured to arrive unless the connection dies [Skotlex]
- //If for some reason the next iteration (10 secs) we are still not connected,
- //assume the packets got lost, so we need to resend them. [Skotlex]
- if (chrif_state == 0)
- chrif_connect(char_fd);
- else if (chrif_state == 1)
- chrif_sendmap(char_fd);
-*/
- }
- if (chrif_isconnect()) displayed = 0;
- return 0;
-}
-
-int auth_db_final(DBKey k,void *d,va_list ap) {
- struct auth_node *node=(struct auth_node*)d;
- if (node->char_dat)
- aFree(node->char_dat);
- return 0;
-}
-
-/*==========================================
- * 終了
- *------------------------------------------
- */
-int do_final_chrif(void)
-{
- delete_session(char_fd);
- auth_db->destroy(auth_db, auth_db_final);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int do_init_chrif(void)
-{
- add_timer_func_list(check_connect_char_server, "check_connect_char_server");
- add_timer_func_list(send_usercount_tochar, "send_usercount_tochar");
- add_timer_func_list(send_users_tochar, "send_users_tochar");
- add_timer_func_list(auth_db_cleanup, "auth_db_cleanup");
- add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000);
-#ifdef TXT_ONLY
- //Txt needs this more frequently because it is used for the online.html file.
- add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, UPDATE_INTERVAL);
-#else
- add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, CHECK_INTERVAL);
- add_timer_interval(gettick() + 1000, send_usercount_tochar, 0, 0, UPDATE_INTERVAL);
-#endif
- add_timer_interval(gettick() + 1000, auth_db_cleanup, 0, 0, 30 * 1000);
-
- auth_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
-
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <limits.h>
+
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "battle.h"
+#include "chrif.h"
+#include "clif.h"
+#include "intif.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "mercenary.h"
+#ifndef TXT_ONLY
+#include "charsave.h"
+#endif
+//Updated table (only doc^^) [Sirius]
+//Used Packets: U->2af8
+//Free Packets: F->2af8
+
+struct dbt *auth_db;
+
+static const int packet_len_table[0x3d] = {
+ 60, 3,-1,27,10,-1, 6,-1, // 2af8-2aff: U->2af8, U->2af9, U->2afa, U->2afb, U->2afc, U->2afd, U->2afe, U->2aff
+ 6,-1,18, 7,-1,49,30,10, // 2b00-2b07: U->2b00, U->2b01, U->2b02, U->2b03, U->2b04, U->2b05, U->2b06, U->2b07
+ 6,30,-1,10,86, 7,44,34, // 2b08-2b0f: U->2b08, U->2b09, U->2b0a, U->2b0b, U->2b0c, U->2b0d, U->2b0e, U->2b0f
+ 0,-1,10, 6,11,-1, 0, 0, // 2b10-2b17: U->2b10, U->2b11, U->2b12, U->2b13, U->2b14, U->2b15, U->2b16, U->2b17
+ -1,-1,-1,-1,-1,-1, 2, 7, // 2b18-2b1f: U->2b18, U->2b19, U->2b1a, U->2b1b, U->2b1c, U->2b1d, U->2b1e, U->2b1f
+ -1,10,-1,-1,-1,-1,-1,-1, // 2b20-2b27: U->2b20, U->2b21, F->2b22, F->2b23, F->2b24, F->2b25, F->2b26, F->2b27
+};
+
+//Used Packets:
+//2af8: Outgoing, chrif_connect -> 'connect to charserver / auth @ charserver'
+//2af9: Incomming, chrif_connectack -> 'answer of the 2af8 login(ok / fail)'
+//2afa: Outgoing, chrif_sendmap -> 'sending our maps'
+//2afb: Incomming, chrif_sendmapack -> 'Maps received successfully / or not ..'
+//2afc: Outgoing, chrif_scdata_request -> request sc_data for pc_authok'ed char. <- new command reuses previous one.
+//2afd: Incomming, chrif_authok -> 'character selected, add to auth db'
+//2afe: Outgoing, send_usercount_tochar -> 'sends player count of this map server to charserver'
+//2aff: Outgoing, send_users_tochar -> 'sends all actual connected character ids to charserver'
+//2b00: Incomming, map_setusers -> 'set the actual usercount? PACKET.2B COUNT.L.. ?' (not sure)
+//2b01: Outgoing, chrif_save -> 'charsave of char XY account XY (complete struct)'
+//2b02: Outgoing, chrif_charselectreq -> 'player returns from ingame to charserver to select another char.., this packets includes sessid etc' ? (not 100% sure)
+//2b03: Incomming, clif_charselectok -> '' (i think its the packet after enterworld?) (not sure)
+//2b04: Incomming, chrif_recvmap -> 'getting maps from charserver of other mapserver's'
+//2b05: Outgoing, chrif_changemapserver -> 'Tell the charserver the mapchange / quest for ok...'
+//2b06: Incomming, chrif_changemapserverack -> 'awnser of 2b05, ok/fail, data: dunno^^'
+//2b07: Incoming, clif_updatemaxid -> Received when updating the max account/char known
+//2b08: Outgoing, chrif_searchcharid -> '...'
+//2b09: Incomming, map_addchariddb -> 'Adds a name to the nick db'
+//2b0a: Outgoing, chrif_changegm -> 'level change of acc/char XY'
+//2b0b: Incomming, chrif_changedgm -> 'answer of 2b0a..'
+//2b0c: Outgoing, chrif_changeemail -> 'change mail address ...'
+//2b0d: Incomming, chrif_changedsex -> 'Change sex of acc XY'
+//2b0e: Outgoing, chrif_char_ask_name -> 'Do some operations (change sex, ban / unban etc)'
+//2b0f: Incomming, chrif_char_ask_name_answer -> 'answer of the 2b0e'
+//2b10: Outgoing, chrif_updatefamelist -> 'Update the fame ranking lists and send them'
+//2b11: Outgoing, chrif_changesex -> 'change sex of acc X'
+//2b12: Incomming, chrif_divorce -> 'divorce a wedding of charid X and partner id X'
+//2b13: Incomming, chrif_accountdeletion -> 'Delete acc XX, if the player is on, kick ....'
+//2b14: Incomming, chrif_accountban -> 'not sure: kick the player with message XY'
+//2b15: Incomming, chrif_recvgmaccounts -> 'recieve gm accs from charserver (seems to be incomplete !)'
+//2b16: Outgoing, chrif_ragsrvinfo -> 'sends motd / rates ....'
+//2b17: Outgoing, chrif_char_offline -> 'tell the charserver that the char is now offline'
+//2b18: Outgoing, chrif_char_reset_offline -> 'set all players OFF!'
+//2b19: Outgoing, chrif_char_online -> 'tell the charserver that the char .. is online'
+//2b1a: Outgoing, chrif_buildfamelist -> 'Build the fame ranking lists and send them'
+//2b1b: Incomming, chrif_recvfamelist -> 'Receive fame ranking lists'
+//2b1c: Outgoing, chrif_save_scdata -> 'Send sc_data of player for saving.'
+//2b1d: Incomming, chrif_load_scdata -> 'received sc_data of player for loading.'
+//2b1e: Incoming, chrif_update_ip -> 'Reqest forwarded from char-server for interserver IP sync.' [Lance]
+//2b1f: Incomming, chrif_disconnectplayer -> 'disconnects a player (aid X) with the message XY ... 0x81 ..' [Sirius]
+//2b20: Incomming, chrif_removemap -> 'remove maps of a server (sample: its going offline)' [Sirius]
+//2b21: Incomming, chrif_save_ack. Returned after a character has been "final saved" on the char-server. [Skotlex]
+//2b22-2b27: FREE
+
+int chrif_connected = 0;
+int char_fd = 0; //Using 0 instead of -1 is safer against crashes. [Skotlex]
+int srvinfo;
+static char char_ip_str[128];
+static in_addr_t char_ip= 0;
+static int char_port = 6121;
+static char userid[NAME_LENGTH], passwd[NAME_LENGTH];
+static int chrif_state = 0;
+static int char_init_done = 0;
+int other_mapserver_count=0; //Holds count of how many other map servers are online (apart of this instance) [Skotlex]
+
+//Interval at which map server updates online listing. [Valaris]
+#define CHECK_INTERVAL 3600000
+//Interval at which map server sends number of connected users. [Skotlex]
+#define UPDATE_INTERVAL 10000
+//This define should spare writing the check in every function. [Skotlex]
+#define chrif_check(a) { if(!chrif_isconnect()) return a; }
+
+// 設定ファイル読み込み関係
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setuserid(char *id)
+{
+ memcpy(userid, id, NAME_LENGTH);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setpasswd(char *pwd)
+{
+ memcpy(passwd, pwd, NAME_LENGTH);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_checkdefaultlogin(void)
+{
+ if (strcmp(userid, "s1")==0 && strcmp(passwd, "p1")==0) {
+ ShowError("Using the default user/password s1/p1 is NOT RECOMMENDED.\n");
+#ifdef TXT_ONLY
+ ShowNotice("Please edit your save/account.txt file to create a proper inter-server user/password (gender 'S')\n");
+#else
+ ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n");
+#endif
+ ShowNotice("and then edit your user/password in conf/map_athena.conf (or conf/import/map_conf.txt)\n");
+ }
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_setip(char *ip)
+{
+ char ip_str[16];
+ char_ip = resolve_hostbyname(ip,NULL,ip_str);
+
+ if (!char_ip) {
+ ShowWarning("Failed to Resolve Char Server Address! (%s)\n", ip);
+ return 0;
+ }
+ strncpy(char_ip_str, ip, sizeof(char_ip_str));
+ ShowInfo("Char Server IP Address : '"CL_WHITE"%s"CL_RESET"' -> '"CL_WHITE"%s"CL_RESET"'.\n", ip, ip_str);
+ return 1;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+void chrif_setport(int port)
+{
+ char_port = port;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_isconnect(void)
+{
+ return (char_fd > 0 && session[char_fd] != NULL && chrif_state == 2);
+}
+
+/*==========================================
+ * Saves char.
+ * Flag = 1: Character is quitting.
+ * Flag = 2: Character is changing map-servers
+ *------------------------------------------
+ */
+int chrif_save(struct map_session_data *sd, int flag)
+{
+ nullpo_retr(-1, sd);
+
+ pc_makesavestatus(sd);
+ if(!chrif_isconnect())
+ {
+ if (flag) sd->state.finalsave = 1; //Will save character on reconnect.
+ return -1;
+ }
+
+ if (sd->state.finalsave)
+ return -1; //Refuse to save a char already tagged for final saving. [Skotlex]
+ //For data sync
+ if (sd->state.storage_flag == 1)
+ storage_storage_save(sd->status.account_id, flag);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storagesave(sd->status.account_id, sd->status.guild_id, flag);
+ if (flag) sd->state.storage_flag = 0; //Force close it.
+
+ //Saving of registry values.
+ if (sd->state.reg_dirty&4)
+ intif_saveregistry(sd, 3); //Save char regs
+ if (sd->state.reg_dirty&2)
+ intif_saveregistry(sd, 2); //Save account regs
+ if (sd->state.reg_dirty&1)
+ intif_saveregistry(sd, 1); //Save account2 regs
+#ifndef TXT_ONLY
+ if(charsave_method){ //New 'Local' save
+ charsave_savechar(sd->status.char_id, &sd->status);
+ if (flag) //Character final saved.
+ sd->state.finalsave = 1;
+ if (flag == 1)
+ chrif_char_offline(sd); //Tell char server that character went offline.
+ return 0;
+ }
+#endif
+ WFIFOHEAD(char_fd, sizeof(sd->status) + 13);
+ WFIFOW(char_fd,0) = 0x2b01;
+ WFIFOW(char_fd,2) = sizeof(sd->status) + 13;
+ WFIFOL(char_fd,4) = sd->status.account_id;
+ WFIFOL(char_fd,8) = sd->status.char_id;
+ WFIFOB(char_fd,12) = (flag==1)?1:0; //Flag to tell char-server this character is quitting.
+ memcpy(WFIFOP(char_fd,13), &sd->status, sizeof(sd->status));
+ WFIFOSET(char_fd, WFIFOW(char_fd,2));
+
+ if (sd->hd && merc_is_hom_active(sd->hd))
+ merc_save(sd->hd);
+
+ if (flag)
+ sd->state.finalsave = 1; //Mark the last save as done.
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_connect(int fd)
+{
+ ShowStatus("Logging in to char server...\n", char_fd);
+ WFIFOHEAD(fd, 60);
+ WFIFOW(fd,0) = 0x2af8;
+ memcpy(WFIFOP(fd,2), userid, NAME_LENGTH);
+ memcpy(WFIFOP(fd,26), passwd, NAME_LENGTH);
+ WFIFOL(fd,50) = 0;
+ WFIFOL(fd,54) = clif_getip_long();
+ WFIFOW(fd,58) = clif_getport(); // [Valaris] thanks to fov
+ WFIFOSET(fd,60);
+
+ return 0;
+}
+
+/*==========================================
+ * マップ送信
+ *------------------------------------------
+ */
+int chrif_sendmap(int fd)
+{
+ int i;
+ ShowStatus("Sending maps to char server...\n");
+ WFIFOHEAD(fd, 4 + map_num * 4);
+ WFIFOW(fd,0) = 0x2afa;
+ for(i = 0; i < map_num; i++)
+ WFIFOW(fd,4+i*4) = map[i].index;
+ WFIFOW(fd,2) = 4 + i * 4;
+ WFIFOSET(fd,WFIFOW(fd,2));
+
+ return 0;
+}
+
+/*==========================================
+ * マップ受信
+ *------------------------------------------
+ */
+int chrif_recvmap(int fd)
+{
+ int i, j, ip, port;
+ unsigned char *p = (unsigned char *)&ip;
+ RFIFOHEAD(fd);
+ ip = RFIFOL(fd,4);
+ port = RFIFOW(fd,8);
+ for(i = 10, j = 0; i < RFIFOW(fd,2); i += 4, j++) {
+ map_setipport(RFIFOW(fd,i), ip, port);
+// if (battle_config.etc_log)
+// printf("recv map %d %s\n", j, RFIFOP(fd,i));
+ }
+ if (battle_config.etc_log)
+ ShowStatus("recv map on %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
+
+ other_mapserver_count++;
+ return 0;
+}
+
+/*==========================================
+ * Delete maps of other servers, (if an other mapserver is going OFF)
+ *------------------------------------------
+ */
+int chrif_removemap(int fd){
+ int i, j, ip, port;
+ unsigned char *p = (unsigned char *)&ip;
+ RFIFOHEAD(fd);
+
+ ip = RFIFOL(fd, 4);
+ port = RFIFOW(fd, 8);
+
+ for(i = 10, j = 0; i < RFIFOW(fd, 2); i += 4, j++){
+ map_eraseipport(RFIFOW(fd, i), ip, port);
+ }
+
+ other_mapserver_count--;
+ if(battle_config.etc_log)
+ ShowStatus("remove map of server %d.%d.%d.%d:%d (%d maps)\n", p[0], p[1], p[2], p[3], port, j);
+ return 0;
+}
+
+int chrif_save_ack(int fd) {
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+ sd = map_id2sd(RFIFOL(fd,2));
+
+ if (sd && sd->status.char_id == RFIFOL(fd,6))
+ map_quit_ack(sd);
+ return 0;
+}
+
+/*==========================================
+ * マップ鯖間移動のためのデータ準備要求
+ *------------------------------------------
+ */
+int chrif_changemapserver(struct map_session_data *sd, short map, int x, int y, int ip, short port)
+{
+ int s_ip;
+
+ nullpo_retr(-1, sd);
+
+ chrif_check(-1);
+
+ if (other_mapserver_count < 1)
+ { //No other map servers are online!
+ clif_authfail_fd(sd->fd, 0);
+ return -1;
+ }
+
+ if (sd->fd && sd->fd < fd_max && session[sd->fd])
+ s_ip = session[sd->fd]->client_addr.sin_addr.s_addr;
+ else //Not connected? Can't retrieve IP
+ s_ip = 0;
+
+ WFIFOHEAD(char_fd, 35);
+ WFIFOW(char_fd, 0) = 0x2b05;
+ WFIFOL(char_fd, 2) = sd->bl.id;
+ WFIFOL(char_fd, 6) = sd->login_id1;
+ WFIFOL(char_fd,10) = sd->login_id2;
+ WFIFOL(char_fd,14) = sd->status.char_id;
+ WFIFOW(char_fd,18) = map;
+ WFIFOW(char_fd,20) = x;
+ WFIFOW(char_fd,22) = y;
+ WFIFOL(char_fd,24) = ip;
+ WFIFOW(char_fd,28) = port;
+ WFIFOB(char_fd,30) = sd->status.sex;
+ WFIFOL(char_fd,31) = s_ip;
+ WFIFOSET(char_fd,35);
+
+ return 0;
+}
+
+/*==========================================
+ * マップ鯖間移動ack
+ *------------------------------------------
+ */
+int chrif_changemapserverack(int fd)
+{
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+ sd = map_id2sd(RFIFOL(fd,2));
+
+ if (sd == NULL || sd->status.char_id != RFIFOL(fd,14))
+ return -1;
+
+ if (RFIFOL(fd,6) == 1) {
+ if (battle_config.error_log)
+ ShowError("map server change failed.\n");
+ clif_authfail_fd(sd->fd, 0);
+ return 0;
+ }
+ clif_changemapserver(sd, (char*)mapindex_id2name(RFIFOW(fd,18)), RFIFOW(fd,20), RFIFOW(fd,22), RFIFOL(fd,24), RFIFOW(fd,28));
+
+ //Player has been saved already, remove him from memory. [Skotlex]
+ map_quit(sd);
+ map_quit_ack(sd);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_connectack(int fd)
+{
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2)) {
+ ShowFatalError("Connection to char-server failed %d.\n", RFIFOB(fd,2));
+ exit(1);
+ }
+ ShowStatus("Successfully logged on to Char Server (Connection: '"CL_WHITE"%d"CL_RESET"').\n",fd);
+ chrif_state = 1;
+ chrif_connected=1;
+
+ chrif_sendmap(fd);
+
+ ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit"));
+ ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
+ if(!char_init_done) {
+ char_init_done = 1;
+ ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce"));
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_sendmapack(int fd)
+{
+ RFIFOHEAD(fd);
+ if (RFIFOB(fd,2)) {
+ ShowFatalError("chrif : send map list to char server failed %d\n", RFIFOB(fd,2));
+ exit(1);
+ }
+ memcpy(wisp_server_name, RFIFOP(fd,3), NAME_LENGTH);
+ ShowStatus("Map sending complete. Map Server is now online.\n");
+ chrif_state = 2;
+
+ //If there are players online, send them to the char-server. [Skotlex]
+ send_users_tochar(-1, gettick(), 0, 0);
+
+ //Re-save any storages that were modified in the disconnection time. [Skotlex]
+ do_reconnect_map();
+ do_reconnect_storage();
+
+ return 0;
+}
+
+/*==========================================
+ * Request sc_data from charserver [Skotlex]
+ *------------------------------------------
+ */
+int chrif_scdata_request(int account_id, int char_id)
+{
+#ifdef ENABLE_SC_SAVING
+#ifndef TXT_ONLY
+ if (charsave_method)
+ return charsave_load_scdata(account_id, char_id);
+#endif
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd, 0) = 0x2afc;
+ WFIFOL(char_fd, 2) = account_id;
+ WFIFOL(char_fd, 6) = char_id;
+ WFIFOSET(char_fd,10);
+#endif
+ return 0;
+}
+
+/*==========================================
+ * new auth system [Kevin]
+ *------------------------------------------
+ */
+void chrif_authreq(struct map_session_data *sd)
+{
+ struct auth_node *auth_data;
+ auth_data=idb_get(auth_db, sd->bl.id);
+
+ if(auth_data) {
+ if(auth_data->char_dat &&
+ auth_data->account_id== sd->bl.id &&
+ auth_data->login_id1 == sd->login_id1)
+ { //auth ok
+ pc_authok(sd, auth_data->login_id2, auth_data->connect_until_time, auth_data->char_dat);
+ } else { //auth failed
+ pc_authfail(sd);
+ chrif_char_offline(sd); //Set him offline, the char server likely has it set as online already.
+ }
+ if (auth_data->char_dat)
+ aFree(auth_data->char_dat);
+ idb_remove(auth_db, sd->bl.id);
+ } else { //data from char server has not arrived yet.
+ auth_data = aCalloc(1,sizeof(struct auth_node));
+ auth_data->sd = sd;
+ auth_data->fd = sd->fd;
+ auth_data->account_id = sd->bl.id;
+ auth_data->login_id1 = sd->login_id1;
+ auth_data->node_created = gettick();
+ uidb_put(auth_db, sd->bl.id, auth_data);
+ }
+ return;
+}
+
+//character selected, insert into auth db
+void chrif_authok(int fd) {
+ struct auth_node *auth_data;
+ RFIFOHEAD(fd);
+
+ if (map_id2sd(RFIFOL(fd, 4)) != NULL)
+ //Someone with this account is already in! Do not store the info to prevent possible sync exploits. [Skotlex]
+ return;
+
+ if ((auth_data =uidb_get(auth_db, RFIFOL(fd, 4))) != NULL)
+ { //Is the character already awaiting authorization?
+ if (auth_data->sd)
+ {
+ //First, check to see if the session data still exists (avoid dangling pointers)
+ if(session[auth_data->fd] && session[auth_data->fd]->session_data == auth_data->sd)
+ {
+ if (auth_data->char_dat == NULL &&
+ auth_data->account_id == RFIFOL(fd, 4) &&
+ auth_data->login_id1 == RFIFOL(fd, 8))
+ { //Auth Ok
+ pc_authok(auth_data->sd, RFIFOL(fd, 16), RFIFOL(fd, 12), (struct mmo_charstatus*)RFIFOP(fd, 20));
+ } else { //Auth Failed
+ pc_authfail(auth_data->sd);
+ chrif_char_offline(auth_data->sd); //Set him offline, the char server likely has it set as online already.
+ }
+ } //else: Character no longer exists, just go through.
+ }
+ //Delete the data of this node...
+ if (auth_data->char_dat)
+ aFree (auth_data->char_dat);
+ uidb_remove(auth_db, RFIFOL(fd, 4));
+ return;
+ }
+ // Awaiting for client to connect.
+ auth_data = (struct auth_node *)aCalloc(1,sizeof(struct auth_node));
+ auth_data->char_dat = (struct mmo_charstatus *) aCalloc(1,sizeof(struct mmo_charstatus));
+
+ auth_data->account_id=RFIFOL(fd, 4);
+ auth_data->login_id1=RFIFOL(fd, 8);
+ auth_data->connect_until_time=RFIFOL(fd, 12);
+ auth_data->login_id2=RFIFOL(fd, 16);
+ memcpy(auth_data->char_dat,RFIFOP(fd, 20),sizeof(struct mmo_charstatus));
+ auth_data->node_created=gettick();
+ uidb_put(auth_db, RFIFOL(fd, 4), auth_data);
+}
+
+int auth_db_cleanup_sub(DBKey key,void *data,va_list ap)
+{
+ struct auth_node *node=(struct auth_node*)data;
+
+ if(DIFF_TICK(gettick(),node->node_created)>30000) {
+ ShowNotice("Character (aid: %d) not authed within 30 seconds of character select!\n", node->account_id);
+ if (node->char_dat)
+ aFree(node->char_dat);
+ db_remove(auth_db, key);
+ return 1;
+ }
+ return 0;
+}
+
+int auth_db_cleanup(int tid, unsigned int tick, int id, int data) {
+ auth_db->foreach(auth_db, auth_db_cleanup_sub);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip)
+{
+ nullpo_retr(-1, sd);
+
+ if( !sd || !sd->bl.id || !sd->login_id1 )
+ return -1;
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 18);
+ WFIFOW(char_fd, 0) = 0x2b02;
+ WFIFOL(char_fd, 2) = sd->bl.id;
+ WFIFOL(char_fd, 6) = sd->login_id1;
+ WFIFOL(char_fd,10) = sd->login_id2;
+ WFIFOL(char_fd,14) = s_ip;
+ WFIFOSET(char_fd,18);
+
+ return 0;
+}
+
+/*==========================================
+ * キャラ名問い合わせ
+ *------------------------------------------
+ */
+int chrif_searchcharid(int char_id)
+{
+ if( !char_id )
+ return -1;
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 6);
+ WFIFOW(char_fd,0) = 0x2b08;
+ WFIFOL(char_fd,2) = char_id;
+ WFIFOSET(char_fd,6);
+
+ return 0;
+}
+
+/*==========================================
+ * GMに変化要求
+ *------------------------------------------
+ */
+int chrif_changegm(int id, const char *pass, int len)
+{
+ if (battle_config.etc_log)
+ ShowInfo("chrif_changegm: account: %d, password: '%s'.\n", id, pass);
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, len + 8);
+ WFIFOW(char_fd,0) = 0x2b0a;
+ WFIFOW(char_fd,2) = len + 8;
+ WFIFOL(char_fd,4) = id;
+ memcpy(WFIFOP(char_fd,8), pass, len);
+ WFIFOSET(char_fd, len + 8);
+
+ return 0;
+}
+
+/*==========================================
+ * Change Email
+ *------------------------------------------
+ */
+int chrif_changeemail(int id, const char *actual_email, const char *new_email)
+{
+ if (battle_config.etc_log)
+ ShowInfo("chrif_changeemail: account: %d, actual_email: '%s', new_email: '%s'.\n", id, actual_email, new_email);
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 86);
+ WFIFOW(char_fd,0) = 0x2b0c;
+ WFIFOL(char_fd,2) = id;
+ memcpy(WFIFOP(char_fd,6), actual_email, 40);
+ memcpy(WFIFOP(char_fd,46), new_email, 40);
+ WFIFOSET(char_fd,86);
+
+ return 0;
+}
+
+/*==========================================
+ * Send message to char-server with a character name to do some operations (by Yor)
+ * Used to ask Char-server about a character name to have the account number to modify account file in login-server.
+ * type of operation:
+ * 1: block
+ * 2: ban
+ * 3: unblock
+ * 4: unban
+ * 5: changesex
+ *------------------------------------------
+ */
+int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 44);
+ WFIFOW(char_fd, 0) = 0x2b0e;
+ WFIFOL(char_fd, 2) = id; // account_id of who ask (for answer) -1 if nobody
+ memcpy(WFIFOP(char_fd,6), character_name, NAME_LENGTH);
+ WFIFOW(char_fd, 30) = operation_type; // type of operation
+ if (operation_type == 2) {
+ WFIFOW(char_fd, 32) = year;
+ WFIFOW(char_fd, 34) = month;
+ WFIFOW(char_fd, 36) = day;
+ WFIFOW(char_fd, 38) = hour;
+ WFIFOW(char_fd, 40) = minute;
+ WFIFOW(char_fd, 42) = second;
+ }
+// ShowInfo("chrif : sent 0x2b0e\n");
+ WFIFOSET(char_fd,44);
+
+ return 0;
+}
+
+/*==========================================
+ * 性別変化要求
+ *------------------------------------------
+ */
+int chrif_changesex(int id, int sex) {
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 9);
+ WFIFOW(char_fd,0) = 0x2b11;
+ WFIFOW(char_fd,2) = 9;
+ WFIFOL(char_fd,4) = id;
+ WFIFOB(char_fd,8) = sex;
+// ShowInfo("chrif : sent 0x3000(changesex)\n");
+ WFIFOSET(char_fd,9);
+ return 0;
+}
+
+/*==========================================
+ * Answer after a request about a character name to do some operations (by Yor)
+ * Used to answer of chrif_char_ask_name.
+ * type of operation:
+ * 1: block
+ * 2: ban
+ * 3: unblock
+ * 4: unban
+ * 5: changesex
+ * type of answer:
+ * 0: login-server resquest done
+ * 1: player not found
+ * 2: gm level too low
+ * 3: login-server offline
+ *------------------------------------------
+ */
+int chrif_char_ask_name_answer(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ char output[256];
+ char player_name[NAME_LENGTH];
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2); // account_id of who has asked (-1 if nobody)
+ memcpy(player_name, RFIFOP(fd,6), NAME_LENGTH-1);
+ player_name[NAME_LENGTH-1] = '\0';
+
+ sd = map_id2sd(acc);
+ if (acc >= 0 && sd != NULL) {
+ if (RFIFOW(fd, 32) == 1) // player not found
+ sprintf(output, "The player '%s' doesn't exist.", player_name);
+ else {
+ switch(RFIFOW(fd, 30)) {
+ case 1: // block
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to block the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to block the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to block the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 2: // ban
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to ban the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to ban the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to ban the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 3: // unblock
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to unblock the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to unblock the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to unblock the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 4: // unban
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to unban the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to unban the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to unban the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ case 5: // changesex
+ switch(RFIFOW(fd, 32)) {
+ case 0: // login-server resquest done
+ sprintf(output, "Login-server has been asked to change the sex of the player '%s'.", player_name);
+ break;
+ //case 1: // player not found
+ case 2: // gm level too low
+ sprintf(output, "Your GM level don't authorise you to change the sex of the player '%s'.", player_name);
+ break;
+ case 3: // login-server offline
+ sprintf(output, "Login-server is offline. Impossible to change the sex of the the player '%s'.", player_name);
+ break;
+ }
+ break;
+ }
+ }
+ if (output[0] != '\0') {
+ output[sizeof(output)-1] = '\0';
+ clif_displaymessage(sd->fd, output);
+ }
+ } else
+ ShowError("chrif_char_ask_name_answer failed - player not online.\n");
+
+ return 0;
+}
+
+/*==========================================
+ * End of GM change (@GM) (modified by Yor)
+ *------------------------------------------
+ */
+int chrif_changedgm(int fd)
+{
+ int acc, level;
+ struct map_session_data *sd = NULL;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ level = RFIFOL(fd,6);
+
+ sd = map_id2sd(acc);
+
+ if (battle_config.etc_log)
+ ShowNotice("chrif_changedgm: account: %d, GM level 0 -> %d.\n", acc, level);
+ if (sd != NULL) {
+ if (level > 0)
+ clif_displaymessage(sd->fd, "GM modification success.");
+ else
+ clif_displaymessage(sd->fd, "Failure of GM modification.");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 性別変化終了 (modified by Yor)
+ *------------------------------------------
+ */
+int chrif_changedsex(int fd)
+{
+ int acc, sex, i;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ sex = RFIFOL(fd,6);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_changedsex %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL && sd->status.sex != sex) {
+ sd->status.sex = !sd->status.sex;
+
+ // to avoid any problem with equipment and invalid sex, equipment is unequiped.
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].nameid && sd->status.inventory[i].equip)
+ pc_unequipitem((struct map_session_data*)sd, i, 2);
+ }
+ // reset skill of some job
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) {
+ // remove specifical skills of Bard classes
+ for(i = 315; i <= 322; i++) {
+ if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
+ if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv)
+ sd->status.skill_point = USHRT_MAX;
+ else
+ sd->status.skill_point += sd->status.skill[i].lv;
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ // remove specifical skills of Dancer classes
+ for(i = 323; i <= 330; i++) {
+ if (sd->status.skill[i].id > 0 && !sd->status.skill[i].flag) {
+ if (sd->status.skill_point > USHRT_MAX - sd->status.skill[i].lv)
+ sd->status.skill_point = USHRT_MAX;
+ else
+ sd->status.skill_point += sd->status.skill[i].lv;
+ sd->status.skill[i].id = 0;
+ sd->status.skill[i].lv = 0;
+ }
+ }
+ clif_updatestatus(sd, SP_SKILLPOINT);
+ // change job if necessary
+ if (sd->status.sex) //Changed from Dancer
+ sd->status.class_ -= 1;
+ else //Changed from Bard
+ sd->status.class_ += 1;
+ //sd->class_ needs not be updated as both Dancer/Bard are the same.
+ }
+ // save character
+ //chrif_save(sd,1); Character will be saved on session closed -> map_quit
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ // do same modify in login-server for the account, but no in char-server (it ask again login_id1 to login, and don't remember it)
+ clif_displaymessage(sd->fd, "Your sex has been changed (need disconnection by the server)...");
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL) {
+ ShowError("chrif_changedsex failed.\n");
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * 離婚情報同期要求
+ *------------------------------------------
+ */
+int chrif_divorce(int char_id, int partner_id)
+{
+ struct map_session_data *sd = NULL;
+
+ if (!char_id || !partner_id)
+ return 0;
+
+ nullpo_retr(0, sd = map_nick2sd(map_charid2nick(partner_id)));
+ if (sd->status.partner_id == char_id) {
+ int i;
+ //離婚(相方は既にキャラが消えている筈なので)
+ sd->status.partner_id = 0;
+
+ //相方の結婚指輪を剥奪
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (sd->status.inventory[i].nameid == WEDDING_RING_M || sd->status.inventory[i].nameid == WEDDING_RING_F)
+ pc_delitem(sd, i, 1, 0);
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Disconnection of a player (account has been deleted in login-server) by [Yor]
+ *------------------------------------------
+ */
+int chrif_accountdeletion(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_accountdeletion %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL) {
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ clif_displaymessage(sd->fd, "Your account has been deleted (disconnection)...");
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL)
+ ShowError("chrif_accountdeletion failed - player not online.\n");
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Disconnection of a player (account has been banned of has a status, from login-server) by [Yor]
+ *------------------------------------------
+ */
+int chrif_accountban(int fd)
+{
+ int acc;
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ acc = RFIFOL(fd,2);
+ if (battle_config.etc_log)
+ ShowNotice("chrif_accountban %d.\n", acc);
+ sd = map_id2sd(acc);
+ if (acc > 0) {
+ if (sd != NULL) {
+ sd->login_id1++; // change identify, because if player come back in char within the 5 seconds, he can change its characters
+ if (RFIFOB(fd,6) == 0) { // 0: change of statut, 1: ban
+ switch (RFIFOL(fd,7)) { // status or final date of a banishment
+ case 1: // 0 = Unregistered ID
+ clif_displaymessage(sd->fd, "Your account has 'Unregistered'.");
+ break;
+ case 2: // 1 = Incorrect Password
+ clif_displaymessage(sd->fd, "Your account has an 'Incorrect Password'...");
+ break;
+ case 3: // 2 = This ID is expired
+ clif_displaymessage(sd->fd, "Your account has expired.");
+ break;
+ case 4: // 3 = Rejected from Server
+ clif_displaymessage(sd->fd, "Your account has been rejected from server.");
+ break;
+ case 5: // 4 = You have been blocked by the GM Team
+ clif_displaymessage(sd->fd, "Your account has been blocked by the GM Team.");
+ break;
+ case 6: // 5 = Your Game's EXE file is not the latest version
+ clif_displaymessage(sd->fd, "Your Game's EXE file is not the latest version.");
+ break;
+ case 7: // 6 = Your are Prohibited to log in until %s
+ clif_displaymessage(sd->fd, "Your account has been prohibited to log in.");
+ break;
+ case 8: // 7 = Server is jammed due to over populated
+ clif_displaymessage(sd->fd, "Server is jammed due to over populated.");
+ break;
+ case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this)
+ clif_displaymessage(sd->fd, "Your account has not more authorised.");
+ break;
+ case 100: // 99 = This ID has been totally erased
+ clif_displaymessage(sd->fd, "Your account has been totally erased.");
+ break;
+ default:
+ clif_displaymessage(sd->fd, "Your account has not more authorised.");
+ break;
+ }
+ } else if (RFIFOB(fd,6) == 1) { // 0: change of statut, 1: ban
+ time_t timestamp;
+ char tmpstr[2048];
+ timestamp = (time_t)RFIFOL(fd,7); // status or final date of a banishment
+ strcpy(tmpstr, "Your account has been banished until ");
+ strftime(tmpstr + strlen(tmpstr), 24, "%d-%m-%Y %H:%M:%S", localtime(&timestamp));
+ clif_displaymessage(sd->fd, tmpstr);
+ }
+ clif_setwaitclose(sd->fd); // forced to disconnect for the change
+ }
+ } else {
+ if (sd != NULL)
+ ShowError("chrif_accountban failed - player not online.\n");
+ }
+
+ return 0;
+}
+
+//Disconnect the player out of the game, simple packet
+//packet.w AID.L WHY.B 2+4+1 = 7byte
+int chrif_disconnectplayer(int fd){
+ struct map_session_data *sd;
+ RFIFOHEAD(fd);
+
+ sd = map_id2sd(RFIFOL(fd, 2));
+
+ if(sd == NULL){
+ return -1;
+ }
+
+ if (!sd->fd)
+ { //No connection
+ if (sd->state.autotrade)
+ map_quit(sd); //Remove it.
+ //Else we don't remove it because the char should have a timer to remove the player because it force-quit before,
+ //and we don't want them kicking their previous instance before the 10 secs penalty time passes. [Skotlex]
+ return 0;
+ }
+
+ switch(RFIFOB(fd, 6)){
+ //clif_authfail_fd
+ case 1: //server closed
+ clif_authfail_fd(sd->fd, 1);
+ break;
+
+ case 2: //someone else logged in
+ clif_authfail_fd(sd->fd, 2);
+ break;
+
+ case 3: //server overpopulated
+ clif_authfail_fd(sd->fd, 4);
+
+ break;
+
+ case 4: //out of time payd for .. (avail)
+ clif_authfail_fd(sd->fd, 10);
+ break;
+
+ case 5: //forced to dc by gm
+ clif_authfail_fd(sd->fd, 15);
+ break;
+ }
+
+return 0;
+}
+
+/*==========================================
+ * Request to reload GM accounts and their levels: send to char-server by [Yor]
+ *------------------------------------------
+ */
+int chrif_reloadGMdb(void)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2af7;
+ WFIFOSET(char_fd, 2);
+
+ return 0;
+}
+
+/*==========================================
+ * Receiving GM accounts and their levels from char-server by [Yor]
+ *------------------------------------------
+ */
+int chrif_recvgmaccounts(int fd)
+{
+ ShowInfo("From login-server: receiving information of '"CL_WHITE"%d"CL_RESET"' GM accounts.\n", pc_read_gm_account(fd));
+ return 0;
+}
+
+/*==========================================
+ * Request/Receive top 10 Fame character list
+ *------------------------------------------
+ */
+
+int chrif_updatefamelist(struct map_session_data *sd)
+{
+ char type;
+ chrif_check(-1);
+
+ switch(sd->class_&MAPID_UPPERMASK) {
+ case MAPID_BLACKSMITH:
+ type = 1;
+ break;
+ case MAPID_ALCHEMIST:
+ type = 2;
+ break;
+ case MAPID_TAEKWON:
+ type = 3;
+ break;
+ default:
+ return 0;
+ }
+
+ WFIFOHEAD(char_fd, 12);
+ WFIFOW(char_fd, 0) = 0x2b10;
+ WFIFOL(char_fd, 2) = sd->status.char_id;
+ WFIFOL(char_fd, 6) = sd->status.fame;
+ WFIFOB(char_fd, 10) = type;
+ WFIFOB(char_fd, 11) = pc_famerank(sd->status.char_id, sd->class_&MAPID_UPPERMASK);
+ WFIFOSET(char_fd, 12);
+
+ return 0;
+}
+
+int chrif_buildfamelist(void)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd, 0) = 0x2b1a;
+ WFIFOSET(char_fd, 2);
+
+ return 0;
+}
+
+int chrif_recvfamelist(int fd)
+{
+ int num, size;
+ int total = 0, len = 8;
+ RFIFOHEAD(fd);
+
+ malloc_tsetdword (smith_fame_list, 0, sizeof(smith_fame_list));
+ malloc_tsetdword (chemist_fame_list, 0, sizeof(chemist_fame_list));
+ malloc_tsetdword (taekwon_fame_list, 0, sizeof(taekwon_fame_list));
+
+ size = RFIFOW(fd, 6); //Blacksmith block size
+ for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
+ memcpy(&smith_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ size = RFIFOW(fd, 4); //Alchemist block size
+ for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
+ memcpy(&chemist_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ size = RFIFOW(fd, 2); //Total packet length
+ for (num = 0; len < size && num < MAX_FAME_LIST; num++) {
+ memcpy(&taekwon_fame_list[num], RFIFOP(fd,len), sizeof(struct fame_list));
+ len += sizeof(struct fame_list);
+ }
+ total += num;
+
+ ShowInfo("Received Fame List of '"CL_WHITE"%d"CL_RESET"' characters.\n", total);
+
+ return 0;
+}
+
+int chrif_save_scdata(struct map_session_data *sd)
+{ //parses the sc_data of the player and sends it to the char-server for saving. [Skotlex]
+#ifdef ENABLE_SC_SAVING
+ int i, count=0;
+ unsigned int tick;
+ struct status_change_data data;
+ struct TimerData *timer;
+
+ if (sd->state.finalsave) //Character was already saved?
+ return -1;
+#ifndef TXT_ONLY
+ if(charsave_method) //New 'Local' save
+ {
+ charsave_save_scdata(sd->status.account_id, sd->status.char_id, &sd->sc, MAX_STATUSCHANGE);
+ return 0;
+ }
+#endif
+
+ chrif_check(-1);
+ tick = gettick();
+
+ WFIFOHEAD(char_fd, 14 + SC_MAX*sizeof(struct status_change_data));
+ WFIFOW(char_fd,0) = 0x2b1c;
+ WFIFOL(char_fd,4) = sd->status.account_id;
+ WFIFOL(char_fd,8) = sd->status.char_id;
+ for (i = 0; i < SC_MAX; i++)
+ {
+ if (sd->sc.data[i].timer == -1)
+ continue;
+ timer = get_timer(sd->sc.data[i].timer);
+ if (timer == NULL || timer->func != status_change_timer || DIFF_TICK(timer->tick,tick) < 0)
+ continue;
+ data.tick = DIFF_TICK(timer->tick,tick); //Duration that is left before ending.
+ data.type = i;
+ data.val1 = sd->sc.data[i].val1;
+ data.val2 = sd->sc.data[i].val2;
+ data.val3 = sd->sc.data[i].val3;
+ data.val4 = sd->sc.data[i].val4;
+ memcpy(WFIFOP(char_fd,14 +count*sizeof(struct status_change_data)),
+ &data, sizeof(struct status_change_data));
+ count++;
+ }
+ if (count == 0)
+ return 0; //Nothing to save.
+ WFIFOW(char_fd,12) = count;
+ WFIFOW(char_fd,2) = 14 +count*sizeof(struct status_change_data); //Total packet size
+ WFIFOSET(char_fd,WFIFOW(char_fd,2));
+#endif
+ return 0;
+}
+
+int chrif_load_scdata(int fd)
+{ //Retrieve and load sc_data for a player. [Skotlex]
+#ifdef ENABLE_SC_SAVING
+ struct map_session_data *sd;
+ struct status_change_data *data;
+ int aid, cid, i, count;
+ RFIFOHEAD(fd);
+
+ aid = RFIFOL(fd,4); //Player Account ID
+ cid = RFIFOL(fd,8); //Player Char ID
+
+ sd = map_id2sd(aid);
+ if (!sd)
+ {
+ ShowError("chrif_load_scdata: Player of AID %d not found!\n", aid);
+ return -1;
+ }
+ if (sd->status.char_id != cid)
+ {
+ ShowError("chrif_load_scdata: Receiving data for account %d, char id does not matches (%d != %d)!\n", aid, sd->status.char_id, cid);
+ return -1;
+ }
+ count = RFIFOW(fd,12); //sc_count
+ for (i = 0; i < count; i++)
+ {
+ data = (struct status_change_data*)RFIFOP(fd,14 + i*sizeof(struct status_change_data));
+ if (data->tick < 1)
+ { //Protection against invalid tick values. [Skotlex]
+ ShowWarning("chrif_load_scdata: Received invalid duration (%d ms) for status change %d (character %s)\n", data->tick, data->type, sd->status.name);
+ continue;
+ }
+ status_change_start(&sd->bl, data->type, 10000, data->val1, data->val2, data->val3, data->val4, data->tick, 15);
+ }
+#endif
+ return 0;
+}
+
+/*==========================================
+ * Send rates and motd to char server [Wizputer]
+ *------------------------------------------
+ */
+ int chrif_ragsrvinfo(int base_rate, int job_rate, int drop_rate)
+{
+ char buf[256];
+ FILE *fp;
+ int i;
+
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, sizeof(buf) + 10);
+ WFIFOW(char_fd,0) = 0x2b16;
+ WFIFOW(char_fd,2) = base_rate;
+ WFIFOW(char_fd,4) = job_rate;
+ WFIFOW(char_fd,6) = drop_rate;
+
+ if ((fp = fopen(motd_txt, "r")) != NULL) {
+ if (fgets(buf, 250, fp) != NULL) {
+ for(i = 0; buf[i]; i++) {
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ buf[i] = 0;
+ break;
+ }
+ }
+ WFIFOW(char_fd,8) = sizeof(buf) + 10;
+ memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
+ }
+ fclose(fp);
+ } else {
+ malloc_tsetdword(buf, 0, sizeof(buf)); //No data found, send empty packets?
+ WFIFOW(char_fd,8) = sizeof(buf) + 10;
+ memcpy(WFIFOP(char_fd,10), buf, sizeof(buf));
+ }
+ WFIFOSET(char_fd,WFIFOW(char_fd,8));
+ return 0;
+}
+
+
+/*=========================================
+ * Tell char-server charcter disconnected [Wizputer]
+ *-----------------------------------------
+ */
+
+int chrif_char_offline(struct map_session_data *sd)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd,0) = 0x2b17;
+ WFIFOL(char_fd,2) = sd->status.char_id;
+ WFIFOL(char_fd,6) = sd->status.account_id;
+ WFIFOSET(char_fd,10);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server to reset all chars offline [Wizputer]
+ *-----------------------------------------
+ */
+int chrif_flush_fifo(void) {
+ chrif_check(-1);
+
+ set_nonblocking(char_fd, 0);
+ flush_fifos();
+ set_nonblocking(char_fd, 1);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server to reset all chars offline [Wizputer]
+ *-----------------------------------------
+ */
+int chrif_char_reset_offline(void) {
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 2);
+ WFIFOW(char_fd,0) = 0x2b18;
+ WFIFOSET(char_fd,2);
+
+ return 0;
+}
+
+/*=========================================
+ * Tell char-server charcter is online [Wizputer]
+ *-----------------------------------------
+ */
+
+int chrif_char_online(struct map_session_data *sd)
+{
+ chrif_check(-1);
+
+ WFIFOHEAD(char_fd, 10);
+ WFIFOW(char_fd,0) = 0x2b19;
+ WFIFOL(char_fd,2) = sd->status.char_id;
+ WFIFOL(char_fd,6) = sd->status.account_id;
+ WFIFOSET(char_fd,10);
+
+ return 0;
+}
+
+int chrif_disconnect(int fd) {
+ if(fd == char_fd) {
+ char_fd = 0;
+ ShowWarning("Map Server disconnected from Char Server.\n\n");
+ chrif_connected = 0;
+
+ other_mapserver_count=0; //Reset counter. We receive ALL maps from all map-servers on reconnect.
+ map_eraseallipport();
+
+ //Attempt to reconnect in a second. [Skotlex]
+ add_timer(gettick() + 1000, check_connect_char_server, 0, 0);
+ }
+ return 0;
+}
+
+void chrif_update_ip(int fd){
+ unsigned long new_ip;
+ WFIFOHEAD(fd, 6);
+ new_ip = resolve_hostbyname(char_ip_str, NULL, NULL);
+ if (new_ip && new_ip != char_ip)
+ char_ip = new_ip; //Update char_ip
+
+ new_ip = clif_refresh_ip();
+ if (!new_ip) return; //No change
+ WFIFOW(fd, 0) = 0x2736;
+ WFIFOL(fd, 2) = new_ip;
+ WFIFOSET(fd, 6);
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int chrif_parse(int fd)
+{
+ int packet_len, cmd;
+ // only char-server can have an access to here.
+ // so, if it isn't the char-server, we disconnect the session (fd != char_fd).
+ if (fd != char_fd || session[fd]->eof) {
+ if (fd == char_fd && chrif_connected == 1) {
+ chrif_disconnect (fd);
+ }
+ else if (fd != char_fd)
+ ShowDebug("chrif_parse: Disconnecting invalid session #%d (is not the char-server)\n", fd);
+
+ do_close(fd);
+ return 0;
+ }
+
+ while (RFIFOREST(fd) >= 2 && !session[fd]->eof) { //Infinite loop on broken pipe fix. [Skotlex]
+ RFIFOHEAD(fd);
+ cmd = RFIFOW(fd,0);
+ if (cmd < 0x2af8 || cmd >= 0x2af8 + (sizeof(packet_len_table) / sizeof(packet_len_table[0])) ||
+ packet_len_table[cmd-0x2af8] == 0) {
+
+ int r = intif_parse(fd); // intifに渡す
+
+ if (r == 1) continue; // intifで処理した
+ if (r == 2) return 0; // intifで処理したが、データが足りない
+
+ session[fd]->eof = 1;
+ ShowWarning("chrif_parse: session #%d, intif_parse failed -> disconnected.\n", fd);
+ return 0;
+ }
+ packet_len = packet_len_table[cmd-0x2af8];
+ if (packet_len == -1) {
+ if (RFIFOREST(fd) < 4)
+ return 0;
+ packet_len = RFIFOW(fd,2);
+ }
+ if ((int)RFIFOREST(fd) < packet_len)
+ return 0;
+
+ switch(cmd) {
+ case 0x2af9: chrif_connectack(fd); break;
+ case 0x2afb: chrif_sendmapack(fd); break;
+ case 0x2afd: chrif_authok(fd); break;
+ case 0x2b00: map_setusers(fd); break;
+ case 0x2b03: clif_charselectok(RFIFOL(fd,2)); break;
+ case 0x2b04: chrif_recvmap(fd); break;
+ case 0x2b06: chrif_changemapserverack(fd); break;
+ case 0x2b07: clif_updatemaxid(RFIFOL(fd,2), RFIFOL(fd,6)); break;
+ case 0x2b09: map_addchariddb(RFIFOL(fd,2), (char*)RFIFOP(fd,6)); break;
+ case 0x2b0b: chrif_changedgm(fd); break;
+ case 0x2b0d: chrif_changedsex(fd); break;
+ case 0x2b0f: chrif_char_ask_name_answer(fd); break;
+ case 0x2b12: chrif_divorce(RFIFOL(fd,2), RFIFOL(fd,6)); break;
+ case 0x2b13: chrif_accountdeletion(fd); break;
+ case 0x2b14: chrif_accountban(fd); break;
+ case 0x2b15: chrif_recvgmaccounts(fd); break;
+ case 0x2b1b: chrif_recvfamelist(fd); break;
+ case 0x2b1d: chrif_load_scdata(fd); break;
+ case 0x2b1e: chrif_update_ip(fd); break;
+ case 0x2b1f: chrif_disconnectplayer(fd); break;
+ case 0x2b20: chrif_removemap(fd); break;
+ case 0x2b21: chrif_save_ack(fd); break;
+
+ default:
+ if (battle_config.error_log)
+ ShowError("chrif_parse : unknown packet (session #%d): 0x%x. Disconnecting.\n", fd, cmd);
+ session[fd]->eof = 1;
+ return 0;
+ }
+ if (fd == char_fd) //There's the slight chance we lost the connection during parse, in which case this would segfault if not checked [Skotlex]
+ RFIFOSKIP(fd, packet_len);
+ }
+
+ return 0;
+}
+
+int send_usercount_tochar(int tid, unsigned int tick, int id, int data) {
+ int count;
+ static int last_count = 0;
+
+ chrif_check(-1);
+
+ map_getallusers(&count);
+
+ if (count == last_count) //No need to waste packets.
+ return 0;
+ last_count = count;
+
+ WFIFOHEAD(char_fd, 4);
+ WFIFOW(char_fd,0) = 0x2afe;
+ WFIFOW(char_fd,2) = count;
+ WFIFOSET(char_fd,4);
+ return 0;
+}
+
+/*==========================================
+ * timer関数
+ * 今このmap鯖に繋がっているクライアント人数をchar鯖へ送る
+ *------------------------------------------
+ */
+int send_users_tochar(int tid, unsigned int tick, int id, int data) {
+ int count, users=0, i;
+ struct map_session_data **all_sd;
+
+ chrif_check(-1);
+
+ all_sd = map_getallusers(&count);
+ WFIFOHEAD(char_fd, 6+8*users);
+ WFIFOW(char_fd,0) = 0x2aff;
+ for (i = 0; i < count; i++) {
+ WFIFOL(char_fd,6+8*users) = all_sd[i]->status.account_id;
+ WFIFOL(char_fd,6+8*users+4) = all_sd[i]->status.char_id;
+ users++;
+ }
+ WFIFOW(char_fd,2) = 6 + 8 * users;
+ WFIFOW(char_fd,4) = users;
+ WFIFOSET(char_fd,6+8*users);
+
+ return 0;
+}
+
+/*==========================================
+ * timer関数
+ * char鯖との接続を確認し、もし切れていたら再度接続する
+ *------------------------------------------
+ */
+int check_connect_char_server(int tid, unsigned int tick, int id, int data) {
+ static int displayed = 0;
+ if (char_fd <= 0 || session[char_fd] == NULL) {
+ if (!displayed) {
+ ShowStatus("Attempting to connect to Char Server. Please wait.\n");
+ displayed = 1;
+ }
+ chrif_state = 0;
+ char_fd = make_connection(char_ip, char_port);
+ if (char_fd == -1)
+ { //Attempt to connect later. [Skotlex]
+ char_fd = 0;
+ return 0;
+ }
+ session[char_fd]->func_parse = chrif_parse;
+ realloc_fifo(char_fd, FIFOSIZE_SERVERLINK, FIFOSIZE_SERVERLINK);
+
+ chrif_connect(char_fd);
+ chrif_connected = (chrif_state == 2);
+#ifndef TXT_ONLY
+ srvinfo = 0;
+#endif /* not TXT_ONLY */
+ } else {
+#ifndef TXT_ONLY
+ if (srvinfo == 0) {
+ chrif_ragsrvinfo(battle_config.base_exp_rate, battle_config.job_exp_rate, battle_config.item_rate_common);
+ srvinfo = 1;
+ }
+#endif /* not TXT_ONLY */
+/* There is no need, the connection is TCP, so the packet is assured to arrive unless the connection dies [Skotlex]
+ //If for some reason the next iteration (10 secs) we are still not connected,
+ //assume the packets got lost, so we need to resend them. [Skotlex]
+ if (chrif_state == 0)
+ chrif_connect(char_fd);
+ else if (chrif_state == 1)
+ chrif_sendmap(char_fd);
+*/
+ }
+ if (chrif_isconnect()) displayed = 0;
+ return 0;
+}
+
+int auth_db_final(DBKey k,void *d,va_list ap) {
+ struct auth_node *node=(struct auth_node*)d;
+ if (node->char_dat)
+ aFree(node->char_dat);
+ return 0;
+}
+
+/*==========================================
+ * 終了
+ *------------------------------------------
+ */
+int do_final_chrif(void)
+{
+ delete_session(char_fd);
+ auth_db->destroy(auth_db, auth_db_final);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int do_init_chrif(void)
+{
+ add_timer_func_list(check_connect_char_server, "check_connect_char_server");
+ add_timer_func_list(send_usercount_tochar, "send_usercount_tochar");
+ add_timer_func_list(send_users_tochar, "send_users_tochar");
+ add_timer_func_list(auth_db_cleanup, "auth_db_cleanup");
+ add_timer_interval(gettick() + 1000, check_connect_char_server, 0, 0, 10 * 1000);
+#ifdef TXT_ONLY
+ //Txt needs this more frequently because it is used for the online.html file.
+ add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, UPDATE_INTERVAL);
+#else
+ add_timer_interval(gettick() + 1000, send_users_tochar, 0, 0, CHECK_INTERVAL);
+ add_timer_interval(gettick() + 1000, send_usercount_tochar, 0, 0, UPDATE_INTERVAL);
+#endif
+ add_timer_interval(gettick() + 1000, auth_db_cleanup, 0, 0, 30 * 1000);
+
+ auth_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+
+ return 0;
+}
diff --git a/src/map/chrif.h b/src/map/chrif.h
index fc87f9b18..302b31f56 100644
--- a/src/map/chrif.h
+++ b/src/map/chrif.h
@@ -1,58 +1,58 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _CHRIF_H_
-#define _CHRIF_H_
-
-struct auth_node{
- int account_id, login_id1, login_id2, sex, fd;
- time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
- struct map_session_data *sd; //Data from logged on char.
- struct mmo_charstatus *char_dat; //Data from char server.
- unsigned int node_created; //For node auto-deleting
-};
-
-void chrif_setuserid(char*);
-void chrif_setpasswd(char*);
-void chrif_checkdefaultlogin(void);
-int chrif_setip(char*);
-void chrif_setport(int);
-
-int chrif_isconnect(void);
-
-extern int chrif_connected;
-extern int other_mapserver_count;
-
-void chrif_authreq(struct map_session_data *);
-void chrif_authok(int fd);
-int chrif_scdata_request(int account_id, int char_id);
-int chrif_save(struct map_session_data*, int flag);
-int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip);
-void check_fake_id(int fd, struct map_session_data *sd, int target_id);
-int chrif_changemapserver(struct map_session_data *sd,short map,int x,int y,int ip,short port);
-
-int chrif_searchcharid(int char_id);
-int chrif_changegm(int id,const char *pass,int len);
-int chrif_changeemail(int id, const char *actual_email, const char *new_email);
-int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second);
-int chrif_reloadGMdb(void);
-int chrif_updatefamelist(struct map_session_data *sd);
-int chrif_buildfamelist(void);
-int chrif_save_scdata(struct map_session_data *sd);
-int chrif_ragsrvinfo(int base_rate,int job_rate, int drop_rate);
-int chrif_char_offline(struct map_session_data *sd);
-int chrif_char_reset_offline(void);
-int send_users_tochar(int tid, unsigned int tick, int id, int data);
-int chrif_char_online(struct map_session_data *sd);
-int chrif_changesex(int id, int sex);
-int chrif_chardisconnect(struct map_session_data *sd);
-int check_connect_char_server(int tid, unsigned int tick, int id, int data);
-
-int chrif_pcauthok(int fd);
-
-int do_final_chrif(void);
-int do_init_chrif(void);
-
-int chrif_flush_fifo(void);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CHRIF_H_
+#define _CHRIF_H_
+
+struct auth_node{
+ int account_id, login_id1, login_id2, sex, fd;
+ time_t connect_until_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited)
+ struct map_session_data *sd; //Data from logged on char.
+ struct mmo_charstatus *char_dat; //Data from char server.
+ unsigned int node_created; //For node auto-deleting
+};
+
+void chrif_setuserid(char*);
+void chrif_setpasswd(char*);
+void chrif_checkdefaultlogin(void);
+int chrif_setip(char*);
+void chrif_setport(int);
+
+int chrif_isconnect(void);
+
+extern int chrif_connected;
+extern int other_mapserver_count;
+
+void chrif_authreq(struct map_session_data *);
+void chrif_authok(int fd);
+int chrif_scdata_request(int account_id, int char_id);
+int chrif_save(struct map_session_data*, int flag);
+int chrif_charselectreq(struct map_session_data *sd, unsigned long s_ip);
+void check_fake_id(int fd, struct map_session_data *sd, int target_id);
+int chrif_changemapserver(struct map_session_data *sd,short map,int x,int y,int ip,short port);
+
+int chrif_searchcharid(int char_id);
+int chrif_changegm(int id,const char *pass,int len);
+int chrif_changeemail(int id, const char *actual_email, const char *new_email);
+int chrif_char_ask_name(int id, char * character_name, short operation_type, int year, int month, int day, int hour, int minute, int second);
+int chrif_reloadGMdb(void);
+int chrif_updatefamelist(struct map_session_data *sd);
+int chrif_buildfamelist(void);
+int chrif_save_scdata(struct map_session_data *sd);
+int chrif_ragsrvinfo(int base_rate,int job_rate, int drop_rate);
+int chrif_char_offline(struct map_session_data *sd);
+int chrif_char_reset_offline(void);
+int send_users_tochar(int tid, unsigned int tick, int id, int data);
+int chrif_char_online(struct map_session_data *sd);
+int chrif_changesex(int id, int sex);
+int chrif_chardisconnect(struct map_session_data *sd);
+int check_connect_char_server(int tid, unsigned int tick, int id, int data);
+
+int chrif_pcauthok(int fd);
+
+int do_final_chrif(void);
+int do_init_chrif(void);
+
+int chrif_flush_fifo(void);
+
+#endif
diff --git a/src/map/clif.h b/src/map/clif.h
index 5ba025065..df8ac3441 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -1,368 +1,368 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _CLIF_H_
-#define _CLIF_H_
-
-#include "map.h"
-
-// protocol version
-#define PACKETVER 7
-
-// packet DB
-#define MAX_PACKET_DB 0x25f
-#define MAX_PACKET_VER 25
-
-struct packet_db {
- short len;
- void (*func)(int, struct map_session_data *);
- short pos[20];
-};
-
-// local define
-enum {
- ALL_CLIENT,
- ALL_SAMEMAP,
- AREA,
- AREA_WOS,
- AREA_WOC,
- AREA_WOSC,
- AREA_CHAT_WOC,
- CHAT,
- CHAT_WOS,
- CHAT_MAINCHAT,
- PARTY,
- PARTY_WOS,
- PARTY_SAMEMAP,
- PARTY_SAMEMAP_WOS,
- PARTY_AREA,
- PARTY_AREA_WOS,
- GUILD,
- GUILD_WOS,
- GUILD_SAMEMAP, // [Valaris]
- GUILD_SAMEMAP_WOS,
- GUILD_AREA,
- GUILD_AREA_WOS, // end additions [Valaris]
- SELF,
- DUEL,
- DUEL_WOS
-};
-
-extern struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB];
-
-int clif_setip(char*);
-void clif_setbindip(char*);
-void clif_setport(int);
-
-unsigned long clif_getip_long(void);
-unsigned long clif_refresh_ip(void);
-int clif_getport(void);
-int clif_countusers(void);
-void clif_setwaitclose(int);
-
-int clif_authok(struct map_session_data *);
-int clif_authfail_fd(int,int);
-void clif_updatemaxid(int, int);
-int clif_charselectok(int);
-void check_fake_id(int fd, struct map_session_data *sd, int target_id);
-int clif_dropflooritem(struct flooritem_data *);
-int clif_clearflooritem(struct flooritem_data *,int);
-int clif_clearchar(struct block_list*,int); // area or fd
-int clif_clearchar_delay(unsigned int,struct block_list *,int);
-#define clif_clearchar_area(bl,type) clif_clearchar(bl,type)
-int clif_clearchar_id(int,int,int);
-int clif_spawn(struct block_list*); //area
-int clif_walkok(struct map_session_data*); // self
-int clif_move(struct block_list*); // area
-int clif_changemap(struct map_session_data*,short,int,int); //self
-int clif_changemapserver(struct map_session_data*,char*,int,int,int,int); //self
-int clif_blown(struct block_list *); // area
-int clif_slide(struct block_list *,int,int); // area
-int clif_fixpos(struct block_list *); // area
-int clif_fixpos2(struct block_list *); // area
-int clif_npcbuysell(struct map_session_data*,int); //self
-int clif_buylist(struct map_session_data*,struct npc_data*); //self
-int clif_selllist(struct map_session_data*); //self
-int clif_scriptmes(struct map_session_data*,int,char*); //self
-int clif_scriptnext(struct map_session_data*,int); //self
-int clif_scriptclose(struct map_session_data*,int); //self
-int clif_scriptmenu(struct map_session_data*,int,char*); //self
-int clif_scriptinput(struct map_session_data*,int); //self
-int clif_scriptinputstr(struct map_session_data *sd,int npcid); // self
-int clif_cutin(struct map_session_data*,char*,int); //self
-int clif_viewpoint(struct map_session_data*,int,int,int,int,int,int); //self
-int clif_additem(struct map_session_data*,int,int,int); //self
-int clif_delitem(struct map_session_data*,int,int); //self
-int clif_updatestatus(struct map_session_data*,int); //self
-int clif_changestatus(struct block_list*,int,int); //area
-int clif_damage(struct block_list *,struct block_list *,unsigned int,int,int,int,int,int,int); // area
-#define clif_takeitem(src,dst) clif_damage(src,dst,0,0,0,0,0,1,0)
-int clif_changelook(struct block_list *,int,int); // area
-void clif_changetraplook(struct block_list *bl,int val); // area
-void clif_refreshlook(struct block_list *bl,int id,int type,int val,int area); //area specified in 'area'
-int clif_arrowequip(struct map_session_data *sd,int val); //self
-int clif_arrow_fail(struct map_session_data *sd,int type); //self
-int clif_arrow_create_list(struct map_session_data *sd); //self
-int clif_statusupack(struct map_session_data *,int,int,int); // self
-int clif_equipitemack(struct map_session_data *,int,int,int); // self
-int clif_unequipitemack(struct map_session_data *,int,int,int); // self
-int clif_misceffect(struct block_list*,int); // area
-int clif_misceffect2(struct block_list *bl,int type);
-int clif_changeoption(struct block_list*); // area
-int clif_useitemack(struct map_session_data*,int,int,int); // self
-void clif_GlobalMessage(struct block_list *bl,char *message);
-int clif_createchat(struct map_session_data*,int); // self
-int clif_dispchat(struct chat_data*,int); // area or fd
-int clif_joinchatfail(struct map_session_data*,int); // self
-int clif_joinchatok(struct map_session_data*,struct chat_data*); // self
-int clif_addchat(struct chat_data*,struct map_session_data*); // chat
-int clif_changechatowner(struct chat_data*,struct map_session_data*); // chat
-int clif_clearchat(struct chat_data*,int); // area or fd
-int clif_leavechat(struct chat_data*,struct map_session_data*); // chat
-int clif_changechatstatus(struct chat_data*); // chat
-int clif_refresh(struct map_session_data*); // self
-
-int clif_fame_blacksmith(struct map_session_data *, int);
-int clif_fame_alchemist(struct map_session_data *, int);
-int clif_fame_taekwon(struct map_session_data *, int);
-
-void clif_emotion(struct block_list *bl,int type);
-void clif_talkiebox(struct block_list *bl,char* talkie);
-void clif_wedding_effect(struct block_list *bl);
-void clif_divorced(struct map_session_data *sd, char *);
-//void clif_sitting(int fd, struct map_session_data *sd);
-//void clif_callpartner(struct map_session_data *sd);
-void clif_adopt_process(struct map_session_data *sd);
-void clif_sitting(struct map_session_data *sd);
-void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type);
-int clif_soundeffectall(struct block_list *bl, char *name, int type, int coverage);
-void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick);
-void clif_parse_LoadEndAck(int fd,struct map_session_data *sd);
-
-// trade
-int clif_traderequest(struct map_session_data *sd,char *name);
-int clif_tradestart(struct map_session_data *sd,int type);
-int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount);
-int clif_tradeitemok(struct map_session_data *sd,int index,int fail);
-int clif_tradedeal_lock(struct map_session_data *sd,int fail);
-int clif_tradecancelled(struct map_session_data *sd);
-int clif_tradecompleted(struct map_session_data *sd,int fail);
-
-// storage
-#include "storage.h"
-void clif_storagelist(struct map_session_data *sd,struct storage *stor);
-int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor);
-int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount);
-int clif_storageitemremoved(struct map_session_data *sd,int index,int amount);
-int clif_storageclose(struct map_session_data *sd);
-void clif_guildstoragelist(struct map_session_data *sd,struct guild_storage *stor);
-int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor);
-int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount);
-
-int clif_insight(struct block_list *,va_list); // map_forallinmovearea callback
-int clif_outsight(struct block_list *,va_list); // map_forallinmovearea callback
-
-int clif_class_change(struct block_list *bl,int class_,int type);
-#define clif_mob_class_change(md, class_) clif_class_change(&md->bl, class_, 1)
-int clif_mob_equip(struct mob_data *md,int nameid); // [Valaris]
-
-int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range);
-int clif_skillinfoblock(struct map_session_data *sd);
-int clif_skillup(struct map_session_data *sd,int skill_num);
-
-int clif_skillcasting(struct block_list* bl,
- int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime);
-int clif_skillcastcancel(struct block_list* bl);
-int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype);
-int clif_skill_damage(struct block_list *src,struct block_list *dst,
- unsigned int tick,int sdelay,int ddelay,int damage,int div,
- int skill_id,int skill_lv,int type);
-int clif_skill_damage2(struct block_list *src,struct block_list *dst,
- unsigned int tick,int sdelay,int ddelay,int damage,int div,
- int skill_id,int skill_lv,int type);
-int clif_skill_nodamage(struct block_list *src,struct block_list *dst,
- int skill_id,int heal,int fail);
-int clif_skill_poseffect(struct block_list *src,int skill_id,
- int val,int x,int y,int tick);
-int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst);
-int clif_skill_warppoint(struct map_session_data *sd,int skill_num, int skill_lv,
- const char *map1,const char *map2,const char *map3,const char *map4);
-int clif_skill_memo(struct map_session_data *sd,int flag);
-int clif_skill_teleportmessage(struct map_session_data *sd,int flag);
-int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger);
-
-int clif_produceeffect(struct map_session_data *sd,int flag,int nameid);
-
-int clif_skill_setunit(struct skill_unit *unit);
-int clif_skill_delunit(struct skill_unit *unit);
-
-int clif_01ac(struct block_list *bl);
-
-int clif_autospell(struct map_session_data *sd,int skilllv);
-int clif_devotion(struct map_session_data *sd);
-int clif_marionette(struct block_list *src, struct block_list *target);
-int clif_spiritball(struct map_session_data *sd);
-int clif_combo_delay(struct block_list *src,int wait);
-int clif_bladestop(struct block_list *src,struct block_list *dst,int bool_);
-int clif_changemapcell(int m,int x,int y,int cell_type,int type);
-
-int clif_status_load(struct block_list *bl,int type, int flag);
-int clif_status_change(struct block_list *bl,int type,int flag);
-
-int clif_wis_message(int fd,char *nick,char *mes,int mes_len);
-int clif_wis_end(int fd,int flag);
-
-int clif_solved_charname(struct map_session_data *sd,int char_id);
-int clif_charnameack(int fd, struct block_list *bl);
-int clif_charnameupdate(struct map_session_data *ssd);
-
-int clif_use_card(struct map_session_data *sd,int idx);
-int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag);
-
-void clif_inventorylist(struct map_session_data *sd);
-void clif_equiplist(struct map_session_data *sd);
-
-int clif_cart_additem(struct map_session_data*,int,int,int);
-int clif_cart_delitem(struct map_session_data*,int,int);
-void clif_cartlist(struct map_session_data *sd);
-
-int clif_item_identify_list(struct map_session_data *sd);
-int clif_item_identified(struct map_session_data *sd,int idx,int flag);
-int clif_item_repair_list (struct map_session_data *sd, struct map_session_data *dstsd);
-int clif_item_repaireffect(struct map_session_data *sd, int nameid, int flag);
-int clif_item_refine_list(struct map_session_data *sd);
-
-int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name);
-
-int clif_mvp_effect(struct map_session_data *sd);
-int clif_mvp_item(struct map_session_data *sd,int nameid);
-int clif_mvp_exp(struct map_session_data *sd,unsigned long exp);
-void clif_changed_dir(struct block_list *bl, int area);
-
-// vending
-int clif_openvendingreq(struct map_session_data *sd,int num);
-int clif_showvendingboard(struct block_list* bl,char *message,int fd);
-int clif_closevendingboard(struct block_list* bl,int fd);
-int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending);
-int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail);
-int clif_openvending(struct map_session_data *sd,int id,struct vending *vending);
-int clif_vendingreport(struct map_session_data *sd,int index,int amount);
-
-int clif_movetoattack(struct map_session_data *sd,struct block_list *bl);
-
-// party
-int clif_party_created(struct map_session_data *sd,int flag);
-int clif_party_main_info(struct party_data *p, int fd);
-int clif_party_join_info(struct party *p, struct map_session_data *sd);
-int clif_party_info(struct party_data *p,int fd);
-int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd);
-int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag);
-int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag);
-int clif_party_leaved(struct party_data *p,struct map_session_data *sd,int account_id,char *name,int flag);
-int clif_party_message(struct party_data *p,int account_id,char *mes,int len);
-int clif_party_move(struct party *p,struct map_session_data *sd,int online);
-int clif_party_xy(struct map_session_data *sd);
-int clif_party_xy_single(int fd, struct map_session_data *sd);
-int clif_party_hp(struct map_session_data *sd);
-int clif_hpmeter(struct map_session_data *sd);
-
-// guild
-int clif_guild_created(struct map_session_data *sd,int flag);
-int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g);
-int clif_guild_basicinfo(struct map_session_data *sd);
-int clif_guild_allianceinfo(struct map_session_data *sd);
-int clif_guild_memberlist(struct map_session_data *sd);
-int clif_guild_skillinfo(struct map_session_data *sd);
-int clif_guild_send_onlineinfo(struct map_session_data *sd); //[LuzZza]
-int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag);
-int clif_guild_invite(struct map_session_data *sd,struct guild *g);
-int clif_guild_inviteack(struct map_session_data *sd,int flag);
-int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes);
-int clif_guild_expulsion(struct map_session_data *sd,const char *name,const char *mes,int account_id);
-int clif_guild_positionchanged(struct guild *g,int idx);
-int clif_guild_memberpositionchanged(struct guild *g,int idx);
-int clif_guild_emblem(struct map_session_data *sd,struct guild *g);
-int clif_guild_notice(struct map_session_data *sd,struct guild *g);
-int clif_guild_message(struct guild *g,int account_id,const char *mes,int len);
-int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv);
-int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name);
-int clif_guild_allianceack(struct map_session_data *sd,int flag);
-int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
-int clif_guild_oppositionack(struct map_session_data *sd,int flag);
-int clif_guild_broken(struct map_session_data *sd,int flag);
-int clif_guild_xy(struct map_session_data *sd);
-int clif_guild_xy_single(int fd, struct map_session_data *sd);
-int clif_guild_xy_remove(struct map_session_data *sd);
-
-
-// atcommand
-int clif_displaymessage(const int fd,char* mes);
-int clif_disp_onlyself(struct map_session_data *sd,char *mes,int len);
-void clif_disp_message(struct block_list *src, char *mes, int len, int type);
-int clif_GMmessage(struct block_list *bl,char* mes,int len,int flag);
-void clif_MainChatMessage(char* message); //luzza
-int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag);
-int clif_heal(int fd,int type,int val);
-int clif_resurrection(struct block_list *bl,int type);
-int clif_set0199(int fd,int type);
-int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type);
-int clif_send0199(int map,int type);
-int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val);
-
-//petsystem
-int clif_catch_process(struct map_session_data *sd);
-int clif_pet_rulet(struct map_session_data *sd,int data);
-int clif_sendegg(struct map_session_data *sd);
-int clif_send_petdata(struct map_session_data *sd,int type,int param);
-int clif_send_petstatus(struct map_session_data *sd);
-int clif_pet_emotion(struct pet_data *pd,int param);
-int clif_pet_performance(struct block_list *bl,int param);
-int clif_pet_equip(struct pet_data *pd);
-int clif_pet_food(struct map_session_data *sd,int foodid,int fail);
-int clif_send (unsigned char *buf, int len, struct block_list *bl, int type);
-int clif_send_debug(struct map_session_data *sd, int cmd, int* args, int args_num);
-
-//friends list
-int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap);
-void clif_friendslist_send(struct map_session_data *sd);
-void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type);
-
-// [Valaris]
-int clif_mob_hp(struct mob_data *md);
-int clif_weather(int m); // [Valaris]
-int clif_specialeffect(struct block_list *bl,int type, int flag); // special effects [Valaris]
-int clif_message(struct block_list *bl, char* msg); // messages (from mobs/npcs) [Valaris]
-
-int clif_GM_kickack(struct map_session_data *sd,int id);
-int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type);
-int clif_GM_silence(struct map_session_data *sd,struct map_session_data *tsd,int type);
-int clif_timedout(struct map_session_data *sd);
-
-int clif_foreachclient(int (*)(struct map_session_data*,va_list),...);
-int clif_disp_overhead(struct map_session_data *sd, char* mes);
-
-int do_final_clif(void);
-int do_init_clif(void);
-
-void clif_get_weapon_view(TBL_PC* sd, unsigned short *rhand, unsigned short *lhand);
-
-int clif_party_xy_remove(struct map_session_data *sd); //Fix for minimap [Kevin]
-void clif_gospel_info(struct map_session_data *sd, int type);
-void clif_parse_ReqFeel(int fd, struct map_session_data *sd, int skilllv);
-void clif_feel_info(struct map_session_data *sd, unsigned char feel_level, unsigned char type);
-void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type);
-void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress);
-void clif_feel_hate_reset(struct map_session_data *sd);
-
-// [blackhole89]
-int clif_spawnhomun(struct homun_data *hd);
-int clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag);
-int clif_homskillinfoblock(struct map_session_data *sd);
-void clif_homskillup(struct map_session_data *sd, int skill_num) ; //[orn]
-int clif_hom_food(struct map_session_data *sd,int foodid,int fail); //[orn]
-void clif_send_homdata(struct map_session_data *sd, int type, int param); //[orn]
-int clif_hwalkok(struct homun_data *hd); //[orn]
-
-#endif
-
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _CLIF_H_
+#define _CLIF_H_
+
+#include "map.h"
+
+// protocol version
+#define PACKETVER 7
+
+// packet DB
+#define MAX_PACKET_DB 0x25f
+#define MAX_PACKET_VER 25
+
+struct packet_db {
+ short len;
+ void (*func)(int, struct map_session_data *);
+ short pos[20];
+};
+
+// local define
+enum {
+ ALL_CLIENT,
+ ALL_SAMEMAP,
+ AREA,
+ AREA_WOS,
+ AREA_WOC,
+ AREA_WOSC,
+ AREA_CHAT_WOC,
+ CHAT,
+ CHAT_WOS,
+ CHAT_MAINCHAT,
+ PARTY,
+ PARTY_WOS,
+ PARTY_SAMEMAP,
+ PARTY_SAMEMAP_WOS,
+ PARTY_AREA,
+ PARTY_AREA_WOS,
+ GUILD,
+ GUILD_WOS,
+ GUILD_SAMEMAP, // [Valaris]
+ GUILD_SAMEMAP_WOS,
+ GUILD_AREA,
+ GUILD_AREA_WOS, // end additions [Valaris]
+ SELF,
+ DUEL,
+ DUEL_WOS
+};
+
+extern struct packet_db packet_db[MAX_PACKET_VER + 1][MAX_PACKET_DB];
+
+int clif_setip(char*);
+void clif_setbindip(char*);
+void clif_setport(int);
+
+unsigned long clif_getip_long(void);
+unsigned long clif_refresh_ip(void);
+int clif_getport(void);
+int clif_countusers(void);
+void clif_setwaitclose(int);
+
+int clif_authok(struct map_session_data *);
+int clif_authfail_fd(int,int);
+void clif_updatemaxid(int, int);
+int clif_charselectok(int);
+void check_fake_id(int fd, struct map_session_data *sd, int target_id);
+int clif_dropflooritem(struct flooritem_data *);
+int clif_clearflooritem(struct flooritem_data *,int);
+int clif_clearchar(struct block_list*,int); // area or fd
+int clif_clearchar_delay(unsigned int,struct block_list *,int);
+#define clif_clearchar_area(bl,type) clif_clearchar(bl,type)
+int clif_clearchar_id(int,int,int);
+int clif_spawn(struct block_list*); //area
+int clif_walkok(struct map_session_data*); // self
+int clif_move(struct block_list*); // area
+int clif_changemap(struct map_session_data*,short,int,int); //self
+int clif_changemapserver(struct map_session_data*,char*,int,int,int,int); //self
+int clif_blown(struct block_list *); // area
+int clif_slide(struct block_list *,int,int); // area
+int clif_fixpos(struct block_list *); // area
+int clif_fixpos2(struct block_list *); // area
+int clif_npcbuysell(struct map_session_data*,int); //self
+int clif_buylist(struct map_session_data*,struct npc_data*); //self
+int clif_selllist(struct map_session_data*); //self
+int clif_scriptmes(struct map_session_data*,int,char*); //self
+int clif_scriptnext(struct map_session_data*,int); //self
+int clif_scriptclose(struct map_session_data*,int); //self
+int clif_scriptmenu(struct map_session_data*,int,char*); //self
+int clif_scriptinput(struct map_session_data*,int); //self
+int clif_scriptinputstr(struct map_session_data *sd,int npcid); // self
+int clif_cutin(struct map_session_data*,char*,int); //self
+int clif_viewpoint(struct map_session_data*,int,int,int,int,int,int); //self
+int clif_additem(struct map_session_data*,int,int,int); //self
+int clif_delitem(struct map_session_data*,int,int); //self
+int clif_updatestatus(struct map_session_data*,int); //self
+int clif_changestatus(struct block_list*,int,int); //area
+int clif_damage(struct block_list *,struct block_list *,unsigned int,int,int,int,int,int,int); // area
+#define clif_takeitem(src,dst) clif_damage(src,dst,0,0,0,0,0,1,0)
+int clif_changelook(struct block_list *,int,int); // area
+void clif_changetraplook(struct block_list *bl,int val); // area
+void clif_refreshlook(struct block_list *bl,int id,int type,int val,int area); //area specified in 'area'
+int clif_arrowequip(struct map_session_data *sd,int val); //self
+int clif_arrow_fail(struct map_session_data *sd,int type); //self
+int clif_arrow_create_list(struct map_session_data *sd); //self
+int clif_statusupack(struct map_session_data *,int,int,int); // self
+int clif_equipitemack(struct map_session_data *,int,int,int); // self
+int clif_unequipitemack(struct map_session_data *,int,int,int); // self
+int clif_misceffect(struct block_list*,int); // area
+int clif_misceffect2(struct block_list *bl,int type);
+int clif_changeoption(struct block_list*); // area
+int clif_useitemack(struct map_session_data*,int,int,int); // self
+void clif_GlobalMessage(struct block_list *bl,char *message);
+int clif_createchat(struct map_session_data*,int); // self
+int clif_dispchat(struct chat_data*,int); // area or fd
+int clif_joinchatfail(struct map_session_data*,int); // self
+int clif_joinchatok(struct map_session_data*,struct chat_data*); // self
+int clif_addchat(struct chat_data*,struct map_session_data*); // chat
+int clif_changechatowner(struct chat_data*,struct map_session_data*); // chat
+int clif_clearchat(struct chat_data*,int); // area or fd
+int clif_leavechat(struct chat_data*,struct map_session_data*); // chat
+int clif_changechatstatus(struct chat_data*); // chat
+int clif_refresh(struct map_session_data*); // self
+
+int clif_fame_blacksmith(struct map_session_data *, int);
+int clif_fame_alchemist(struct map_session_data *, int);
+int clif_fame_taekwon(struct map_session_data *, int);
+
+void clif_emotion(struct block_list *bl,int type);
+void clif_talkiebox(struct block_list *bl,char* talkie);
+void clif_wedding_effect(struct block_list *bl);
+void clif_divorced(struct map_session_data *sd, char *);
+//void clif_sitting(int fd, struct map_session_data *sd);
+//void clif_callpartner(struct map_session_data *sd);
+void clif_adopt_process(struct map_session_data *sd);
+void clif_sitting(struct map_session_data *sd);
+void clif_soundeffect(struct map_session_data *sd,struct block_list *bl,char *name,int type);
+int clif_soundeffectall(struct block_list *bl, char *name, int type, int coverage);
+void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type, int target_id, unsigned int tick);
+void clif_parse_LoadEndAck(int fd,struct map_session_data *sd);
+
+// trade
+int clif_traderequest(struct map_session_data *sd,char *name);
+int clif_tradestart(struct map_session_data *sd,int type);
+int clif_tradeadditem(struct map_session_data *sd,struct map_session_data *tsd,int index,int amount);
+int clif_tradeitemok(struct map_session_data *sd,int index,int fail);
+int clif_tradedeal_lock(struct map_session_data *sd,int fail);
+int clif_tradecancelled(struct map_session_data *sd);
+int clif_tradecompleted(struct map_session_data *sd,int fail);
+
+// storage
+#include "storage.h"
+void clif_storagelist(struct map_session_data *sd,struct storage *stor);
+int clif_updatestorageamount(struct map_session_data *sd,struct storage *stor);
+int clif_storageitemadded(struct map_session_data *sd,struct storage *stor,int index,int amount);
+int clif_storageitemremoved(struct map_session_data *sd,int index,int amount);
+int clif_storageclose(struct map_session_data *sd);
+void clif_guildstoragelist(struct map_session_data *sd,struct guild_storage *stor);
+int clif_updateguildstorageamount(struct map_session_data *sd,struct guild_storage *stor);
+int clif_guildstorageitemadded(struct map_session_data *sd,struct guild_storage *stor,int index,int amount);
+
+int clif_insight(struct block_list *,va_list); // map_forallinmovearea callback
+int clif_outsight(struct block_list *,va_list); // map_forallinmovearea callback
+
+int clif_class_change(struct block_list *bl,int class_,int type);
+#define clif_mob_class_change(md, class_) clif_class_change(&md->bl, class_, 1)
+int clif_mob_equip(struct mob_data *md,int nameid); // [Valaris]
+
+int clif_skillinfo(struct map_session_data *sd,int skillid,int type,int range);
+int clif_skillinfoblock(struct map_session_data *sd);
+int clif_skillup(struct map_session_data *sd,int skill_num);
+
+int clif_skillcasting(struct block_list* bl,
+ int src_id,int dst_id,int dst_x,int dst_y,int skill_num,int casttime);
+int clif_skillcastcancel(struct block_list* bl);
+int clif_skill_fail(struct map_session_data *sd,int skill_id,int type,int btype);
+int clif_skill_damage(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_damage2(struct block_list *src,struct block_list *dst,
+ unsigned int tick,int sdelay,int ddelay,int damage,int div,
+ int skill_id,int skill_lv,int type);
+int clif_skill_nodamage(struct block_list *src,struct block_list *dst,
+ int skill_id,int heal,int fail);
+int clif_skill_poseffect(struct block_list *src,int skill_id,
+ int val,int x,int y,int tick);
+int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst);
+int clif_skill_warppoint(struct map_session_data *sd,int skill_num, int skill_lv,
+ const char *map1,const char *map2,const char *map3,const char *map4);
+int clif_skill_memo(struct map_session_data *sd,int flag);
+int clif_skill_teleportmessage(struct map_session_data *sd,int flag);
+int clif_skill_produce_mix_list(struct map_session_data *sd, int trigger);
+
+int clif_produceeffect(struct map_session_data *sd,int flag,int nameid);
+
+int clif_skill_setunit(struct skill_unit *unit);
+int clif_skill_delunit(struct skill_unit *unit);
+
+int clif_01ac(struct block_list *bl);
+
+int clif_autospell(struct map_session_data *sd,int skilllv);
+int clif_devotion(struct map_session_data *sd);
+int clif_marionette(struct block_list *src, struct block_list *target);
+int clif_spiritball(struct map_session_data *sd);
+int clif_combo_delay(struct block_list *src,int wait);
+int clif_bladestop(struct block_list *src,struct block_list *dst,int bool_);
+int clif_changemapcell(int m,int x,int y,int cell_type,int type);
+
+int clif_status_load(struct block_list *bl,int type, int flag);
+int clif_status_change(struct block_list *bl,int type,int flag);
+
+int clif_wis_message(int fd,char *nick,char *mes,int mes_len);
+int clif_wis_end(int fd,int flag);
+
+int clif_solved_charname(struct map_session_data *sd,int char_id);
+int clif_charnameack(int fd, struct block_list *bl);
+int clif_charnameupdate(struct map_session_data *ssd);
+
+int clif_use_card(struct map_session_data *sd,int idx);
+int clif_insert_card(struct map_session_data *sd,int idx_equip,int idx_card,int flag);
+
+void clif_inventorylist(struct map_session_data *sd);
+void clif_equiplist(struct map_session_data *sd);
+
+int clif_cart_additem(struct map_session_data*,int,int,int);
+int clif_cart_delitem(struct map_session_data*,int,int);
+void clif_cartlist(struct map_session_data *sd);
+
+int clif_item_identify_list(struct map_session_data *sd);
+int clif_item_identified(struct map_session_data *sd,int idx,int flag);
+int clif_item_repair_list (struct map_session_data *sd, struct map_session_data *dstsd);
+int clif_item_repaireffect(struct map_session_data *sd, int nameid, int flag);
+int clif_item_refine_list(struct map_session_data *sd);
+
+int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const char *name);
+
+int clif_mvp_effect(struct map_session_data *sd);
+int clif_mvp_item(struct map_session_data *sd,int nameid);
+int clif_mvp_exp(struct map_session_data *sd,unsigned long exp);
+void clif_changed_dir(struct block_list *bl, int area);
+
+// vending
+int clif_openvendingreq(struct map_session_data *sd,int num);
+int clif_showvendingboard(struct block_list* bl,char *message,int fd);
+int clif_closevendingboard(struct block_list* bl,int fd);
+int clif_vendinglist(struct map_session_data *sd,int id,struct vending *vending);
+int clif_buyvending(struct map_session_data *sd,int index,int amount,int fail);
+int clif_openvending(struct map_session_data *sd,int id,struct vending *vending);
+int clif_vendingreport(struct map_session_data *sd,int index,int amount);
+
+int clif_movetoattack(struct map_session_data *sd,struct block_list *bl);
+
+// party
+int clif_party_created(struct map_session_data *sd,int flag);
+int clif_party_main_info(struct party_data *p, int fd);
+int clif_party_join_info(struct party *p, struct map_session_data *sd);
+int clif_party_info(struct party_data *p,int fd);
+int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd);
+int clif_party_inviteack(struct map_session_data *sd,char *nick,int flag);
+int clif_party_option(struct party_data *p,struct map_session_data *sd,int flag);
+int clif_party_leaved(struct party_data *p,struct map_session_data *sd,int account_id,char *name,int flag);
+int clif_party_message(struct party_data *p,int account_id,char *mes,int len);
+int clif_party_move(struct party *p,struct map_session_data *sd,int online);
+int clif_party_xy(struct map_session_data *sd);
+int clif_party_xy_single(int fd, struct map_session_data *sd);
+int clif_party_hp(struct map_session_data *sd);
+int clif_hpmeter(struct map_session_data *sd);
+
+// guild
+int clif_guild_created(struct map_session_data *sd,int flag);
+int clif_guild_belonginfo(struct map_session_data *sd,struct guild *g);
+int clif_guild_basicinfo(struct map_session_data *sd);
+int clif_guild_allianceinfo(struct map_session_data *sd);
+int clif_guild_memberlist(struct map_session_data *sd);
+int clif_guild_skillinfo(struct map_session_data *sd);
+int clif_guild_send_onlineinfo(struct map_session_data *sd); //[LuzZza]
+int clif_guild_memberlogin_notice(struct guild *g,int idx,int flag);
+int clif_guild_invite(struct map_session_data *sd,struct guild *g);
+int clif_guild_inviteack(struct map_session_data *sd,int flag);
+int clif_guild_leave(struct map_session_data *sd,const char *name,const char *mes);
+int clif_guild_expulsion(struct map_session_data *sd,const char *name,const char *mes,int account_id);
+int clif_guild_positionchanged(struct guild *g,int idx);
+int clif_guild_memberpositionchanged(struct guild *g,int idx);
+int clif_guild_emblem(struct map_session_data *sd,struct guild *g);
+int clif_guild_notice(struct map_session_data *sd,struct guild *g);
+int clif_guild_message(struct guild *g,int account_id,const char *mes,int len);
+int clif_guild_skillup(struct map_session_data *sd,int skill_num,int lv);
+int clif_guild_reqalliance(struct map_session_data *sd,int account_id,const char *name);
+int clif_guild_allianceack(struct map_session_data *sd,int flag);
+int clif_guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
+int clif_guild_oppositionack(struct map_session_data *sd,int flag);
+int clif_guild_broken(struct map_session_data *sd,int flag);
+int clif_guild_xy(struct map_session_data *sd);
+int clif_guild_xy_single(int fd, struct map_session_data *sd);
+int clif_guild_xy_remove(struct map_session_data *sd);
+
+
+// atcommand
+int clif_displaymessage(const int fd,char* mes);
+int clif_disp_onlyself(struct map_session_data *sd,char *mes,int len);
+void clif_disp_message(struct block_list *src, char *mes, int len, int type);
+int clif_GMmessage(struct block_list *bl,char* mes,int len,int flag);
+void clif_MainChatMessage(char* message); //luzza
+int clif_announce(struct block_list *bl, char* mes, int len, unsigned long color, int flag);
+int clif_heal(int fd,int type,int val);
+int clif_resurrection(struct block_list *bl,int type);
+int clif_set0199(int fd,int type);
+int clif_pvpset(struct map_session_data *sd, int pvprank, int pvpnum,int type);
+int clif_send0199(int map,int type);
+int clif_refine(int fd,struct map_session_data *sd,int fail,int index,int val);
+
+//petsystem
+int clif_catch_process(struct map_session_data *sd);
+int clif_pet_rulet(struct map_session_data *sd,int data);
+int clif_sendegg(struct map_session_data *sd);
+int clif_send_petdata(struct map_session_data *sd,int type,int param);
+int clif_send_petstatus(struct map_session_data *sd);
+int clif_pet_emotion(struct pet_data *pd,int param);
+int clif_pet_performance(struct block_list *bl,int param);
+int clif_pet_equip(struct pet_data *pd);
+int clif_pet_food(struct map_session_data *sd,int foodid,int fail);
+int clif_send (unsigned char *buf, int len, struct block_list *bl, int type);
+int clif_send_debug(struct map_session_data *sd, int cmd, int* args, int args_num);
+
+//friends list
+int clif_friendslist_toggle_sub(struct map_session_data *sd,va_list ap);
+void clif_friendslist_send(struct map_session_data *sd);
+void clif_friendslist_reqack(struct map_session_data *sd, struct map_session_data *f_sd, int type);
+
+// [Valaris]
+int clif_mob_hp(struct mob_data *md);
+int clif_weather(int m); // [Valaris]
+int clif_specialeffect(struct block_list *bl,int type, int flag); // special effects [Valaris]
+int clif_message(struct block_list *bl, char* msg); // messages (from mobs/npcs) [Valaris]
+
+int clif_GM_kickack(struct map_session_data *sd,int id);
+int clif_GM_kick(struct map_session_data *sd,struct map_session_data *tsd,int type);
+int clif_GM_silence(struct map_session_data *sd,struct map_session_data *tsd,int type);
+int clif_timedout(struct map_session_data *sd);
+
+int clif_foreachclient(int (*)(struct map_session_data*,va_list),...);
+int clif_disp_overhead(struct map_session_data *sd, char* mes);
+
+int do_final_clif(void);
+int do_init_clif(void);
+
+void clif_get_weapon_view(TBL_PC* sd, unsigned short *rhand, unsigned short *lhand);
+
+int clif_party_xy_remove(struct map_session_data *sd); //Fix for minimap [Kevin]
+void clif_gospel_info(struct map_session_data *sd, int type);
+void clif_parse_ReqFeel(int fd, struct map_session_data *sd, int skilllv);
+void clif_feel_info(struct map_session_data *sd, unsigned char feel_level, unsigned char type);
+void clif_hate_info(struct map_session_data *sd, unsigned char hate_level,int class_, unsigned char type);
+void clif_mission_info(struct map_session_data *sd, int mob_id, unsigned char progress);
+void clif_feel_hate_reset(struct map_session_data *sd);
+
+// [blackhole89]
+int clif_spawnhomun(struct homun_data *hd);
+int clif_hominfo(struct map_session_data *sd, struct homun_data *hd, int flag);
+int clif_homskillinfoblock(struct map_session_data *sd);
+void clif_homskillup(struct map_session_data *sd, int skill_num) ; //[orn]
+int clif_hom_food(struct map_session_data *sd,int foodid,int fail); //[orn]
+void clif_send_homdata(struct map_session_data *sd, int type, int param); //[orn]
+int clif_hwalkok(struct homun_data *hd); //[orn]
+
+#endif
+
+
diff --git a/src/map/date.c b/src/map/date.c
index 3bb7dca66..4643d6780 100644
--- a/src/map/date.c
+++ b/src/map/date.c
@@ -1,72 +1,72 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include "date.h"
-#include <time.h>
-
-int date_get_year(void)
-{
- time_t t;
- struct tm * lt;
- t = time(NULL);
- lt = localtime(&t);
- return lt->tm_year+1900;
-}
-int date_get_month(void)
-{
- time_t t;
- struct tm * lt;
- t = time(NULL);
- lt = localtime(&t);
- return lt->tm_mon+1;
-}
-int date_get_day(void)
-{
- time_t t;
- struct tm * lt;
- t = time(NULL);
- lt = localtime(&t);
- return lt->tm_mday;
-}
-int date_get_hour(void)
-{
- time_t t;
- struct tm * lt;
- t = time(NULL);
- lt = localtime(&t);
- return lt->tm_hour;
-}
-
-int date_get_min(void)
-{
- time_t t;
- struct tm * lt;
- t = time(NULL);
- lt = localtime(&t);
- return lt->tm_min;
-}
-
-int date_get_sec(void)
-{
- time_t t;
- struct tm * lt;
- t = time(NULL);
- lt = localtime(&t);
- return lt->tm_sec;
-}
-
-int is_day_of_sun(void)
-{
- return date_get_day()%2 == 0;
-}
-
-int is_day_of_moon(void)
-{
- return date_get_day()%2 == 1;
-}
-
-int is_day_of_star(void)
-{
- return date_get_day()%5 == 0;
-}
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "date.h"
+#include <time.h>
+
+int date_get_year(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_year+1900;
+}
+int date_get_month(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_mon+1;
+}
+int date_get_day(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_mday;
+}
+int date_get_hour(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_hour;
+}
+
+int date_get_min(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_min;
+}
+
+int date_get_sec(void)
+{
+ time_t t;
+ struct tm * lt;
+ t = time(NULL);
+ lt = localtime(&t);
+ return lt->tm_sec;
+}
+
+int is_day_of_sun(void)
+{
+ return date_get_day()%2 == 0;
+}
+
+int is_day_of_moon(void)
+{
+ return date_get_day()%2 == 1;
+}
+
+int is_day_of_star(void)
+{
+ return date_get_day()%5 == 0;
+}
+
diff --git a/src/map/date.h b/src/map/date.h
index 2dfbf58dd..2b8ffe991 100644
--- a/src/map/date.h
+++ b/src/map/date.h
@@ -1,17 +1,17 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _DATE_H_
-#define _DATE_H_
-#endif
-
-int date_get_year(void);
-int date_get_month(void);
-int date_get_day(void);
-int date_get_hour(void);
-int date_get_min(void);
-int date_get_sec(void);
-
-int is_day_of_sun(void);
-int is_day_of_moon(void);
-int is_day_of_star(void);
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _DATE_H_
+#define _DATE_H_
+#endif
+
+int date_get_year(void);
+int date_get_month(void);
+int date_get_day(void);
+int date_get_hour(void);
+int date_get_min(void);
+int date_get_sec(void);
+
+int is_day_of_sun(void);
+int is_day_of_moon(void);
+int is_day_of_star(void);
diff --git a/src/map/guild.c b/src/map/guild.c
index 1fe0f526d..0b3be181b 100644
--- a/src/map/guild.c
+++ b/src/map/guild.c
@@ -1,2017 +1,2017 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "../common/ers.h"
-
-#include "map.h"
-#include "guild.h"
-#include "storage.h"
-#include "battle.h"
-#include "npc.h"
-#include "pc.h"
-#include "status.h"
-#include "mob.h"
-#include "intif.h"
-#include "clif.h"
-#include "skill.h"
-#include "log.h"
-
-static struct guild* guild_cache; //For fast retrieval of the same guild over and over. [Skotlex]
-static struct dbt *guild_db;
-static struct dbt *castle_db;
-static struct dbt *guild_expcache_db;
-static struct dbt *guild_infoevent_db;
-static struct dbt *guild_castleinfoevent_db;
-
-struct eventlist {
- char name[50];
- struct eventlist *next;
-};
-
-// ギルドのEXPキャッシュのフラッシュに関連する定数
-#define GUILD_SEND_XY_INVERVAL 5000 // 座標やHP送信の間隔
-#define GUILD_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒)
-#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数
-
-// ギルドのEXPキャッシュ
-struct guild_expcache {
- int guild_id, account_id, char_id;
- unsigned int exp;
-};
-static struct eri *expcache_ers; //For handling of guild exp payment.
-
-struct{
- int id;
- int max;
- struct{
- short id;
- short lv;
- }need[6];
-} guild_skill_tree[MAX_GUILDSKILL];
-
-// timer for auto saving guild data during WoE
-#define GUILD_SAVE_INTERVAL 300000
-int guild_save_timer = -1;
-
-int guild_payexp_timer(int tid,unsigned int tick,int id,int data);
-int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data);
-int guild_save_sub(int tid,unsigned int tick,int id,int data);
-static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data);
-
- // Modified [Komurka]
-int guild_skill_get_max (int id)
-{
- if (id < GD_SKILLBASE || id > GD_SKILLBASE+MAX_GUILDSKILL)
- return 0;
- return guild_skill_tree[id-GD_SKILLBASE].max;
-}
-
-// ギルドスキルがあるか確認
-int guild_checkskill(struct guild *g,int id)
-{
- int idx = id-GD_SKILLBASE;
- if (idx < 0 || idx >= MAX_GUILDSKILL)
- return 0;
- return g->skill[idx].lv;
-}
-
-/*==========================================
- * guild_skill_tree.txt reading - from jA [Komurka]
- *------------------------------------------
- */
-int guild_read_guildskill_tree_db(void)
-{
- int i,k,id=0,ln=0;
- FILE *fp;
- char line[1024],*p;
-
- malloc_set(guild_skill_tree,0,sizeof(guild_skill_tree));
- sprintf(line, "%s/guild_skill_tree.txt", db_path);
- if( (fp=fopen(line,"r"))==NULL){
- ShowError("can't read %s\n", line);
- return -1;
- }
- while(fgets(line,1020,fp)){
- char *split[50];
- if(line[0]=='/' && line[1]=='/')
- continue;
- for(i=0,p=line;i<12 && p;i++){
- split[i]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if(i<12)
- continue;
- id = atoi(split[0]) - GD_SKILLBASE;
- if(id<0 || id>=MAX_GUILDSKILL)
- continue;
- guild_skill_tree[id].id=atoi(split[0]);
- guild_skill_tree[id].max=atoi(split[1]);
- if (guild_skill_tree[id].id==GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max==0) guild_skill_tree[id].max=1;
- for(k=0;k<5;k++){
- guild_skill_tree[id].need[k].id=atoi(split[k*2+2]);
- guild_skill_tree[id].need[k].lv=atoi(split[k*2+3]);
- }
- ln++;
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"guild_skill_tree.txt");
-
- return 0;
-}
-
-/*==========================================
- * Guild skill check - from jA [Komurka]
- *------------------------------------------
- */
-int guild_check_skill_require(struct guild *g,int id)
-{
- int i;
- int idx = id-GD_SKILLBASE;
-
- if(g == NULL)
- return 0;
-
- if (idx < 0 || idx >= MAX_GUILDSKILL)
- return 0;
-
- for(i=0;i<5;i++)
- {
- if(guild_skill_tree[idx].need[i].id == 0) break;
- if(guild_skill_tree[idx].need[i].lv > guild_checkskill(g,guild_skill_tree[idx].need[i].id))
- return 0;
- }
- return 1;
-}
-
-static int guild_read_castledb(void)
-{
- FILE *fp;
- char line[1024];
- int j,ln=0;
- char *str[32],*p;
- struct guild_castle *gc;
-
- sprintf(line, "%s/castle_db.txt", db_path);
- if( (fp=fopen(line,"r"))==NULL){
- ShowError("can't read %s\n", line);
- return -1;
- }
-
- while(fgets(line,1020,fp)){
- if(line[0]=='/' && line[1]=='/')
- continue;
- malloc_tsetdword(str,0,sizeof(str));
- for(j=0,p=line;j<6 && p;j++){
- str[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if (j < 4) //Insufficient data for castle. [Skotlex]
- {
- ShowError("castle_db.txt: invalid line '%s'\n", line);
- continue;
- }
-
- gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle));
- gc->castle_id=atoi(str[0]);
- memcpy(gc->map_name,str[1],MAP_NAME_LENGTH-1);
- memcpy(gc->castle_name,str[2],NAME_LENGTH-1);
- memcpy(gc->castle_event,str[3],NAME_LENGTH-1);
-
- idb_put(castle_db,gc->castle_id,gc);
-
- //intif_guild_castle_info(gc->castle_id);
-
- ln++;
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"castle_db.txt");
- return 0;
-}
-
-// 初期化
-void do_init_guild(void)
-{
- guild_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
- castle_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
- guild_expcache_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
- guild_infoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
- expcache_ers = ers_new((uint32)sizeof(struct guild_expcache));
- guild_castleinfoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
-
- guild_read_castledb();
-
- guild_read_guildskill_tree_db(); //guild skill tree [Komurka]
-
- add_timer_func_list(guild_gvg_eliminate_timer,"guild_gvg_eliminate_timer");
- add_timer_func_list(guild_payexp_timer,"guild_payexp_timer");
- add_timer_func_list(guild_save_sub, "guild_save_sub");
- add_timer_func_list(guild_send_xy_timer, "guild_send_xy_timer");
- add_timer_interval(gettick()+GUILD_PAYEXP_INVERVAL,guild_payexp_timer,0,0,GUILD_PAYEXP_INVERVAL);
- add_timer_interval(gettick()+GUILD_SEND_XY_INVERVAL,guild_send_xy_timer,0,0,GUILD_SEND_XY_INVERVAL);
-}
-
-
-// 検索
-struct guild *guild_search(int guild_id)
-{
- if(guild_cache && guild_cache->guild_id == guild_id)
- return guild_cache;
- guild_cache = idb_get(guild_db,guild_id);
- return guild_cache;
-}
-int guild_searchname_sub(DBKey key,void *data,va_list ap)
-{
- struct guild *g=(struct guild *)data,**dst;
- char *str;
- str=va_arg(ap,char *);
- dst=va_arg(ap,struct guild **);
- if(strcmpi(g->name,str)==0)
- *dst=g;
- return 0;
-}
-// ギルド名検索
-struct guild* guild_searchname(char *str)
-{
- struct guild *g=NULL;
- guild_db->foreach(guild_db,guild_searchname_sub,str,&g);
- return g;
-}
-struct guild_castle *guild_castle_search(int gcid)
-{
- return idb_get(castle_db,gcid);
-}
-
-// mapnameに対応したアジトのgcを返す
-struct guild_castle *guild_mapname2gc(char *mapname)
-{
- int i;
- struct guild_castle *gc=NULL;
- for(i=0;i<MAX_GUILDCASTLE;i++){
- gc=guild_castle_search(i);
- if(!gc) continue;
- if(strcmp(gc->map_name,mapname)==0) return gc;
- }
- return NULL;
-}
-
-struct guild_castle *guild_mapindex2gc(short mapname)
-{
- int i;
- struct guild_castle *gc=NULL;
- for(i=0;i<MAX_GUILDCASTLE;i++){
- gc=guild_castle_search(i);
- if(!gc) continue;
- if(strcmp(gc->map_name,mapindex_id2name(mapname))==0) return gc;
- }
- return NULL;
-}
-
-
-
-// ログイン中のギルドメンバーの1人のsdを返す
-struct map_session_data *guild_getavailablesd(struct guild *g)
-{
- int i;
-
- nullpo_retr(NULL, g);
-
- for(i=0;i<g->max_member;i++)
- if(g->member[i].sd!=NULL)
- return g->member[i].sd;
- return NULL;
-}
-
-// ギルドメンバーのインデックスを返す
-int guild_getindex(struct guild *g,int account_id,int char_id)
-{
- int i;
- if(g==NULL)
- return -1;
- for(i=0;i<g->max_member;i++)
- if( g->member[i].account_id==account_id &&
- g->member[i].char_id==char_id )
- return i;
- return -1;
-}
-// ギルドメンバーの役職を返す
-int guild_getposition(struct map_session_data *sd,struct guild *g)
-{
- int i;
-
- nullpo_retr(-1, sd);
-
- if(g==NULL && (g=guild_search(sd->status.guild_id))==NULL)
- return -1;
- for(i=0;i<g->max_member;i++)
- if( g->member[i].account_id==sd->status.account_id &&
- g->member[i].char_id==sd->status.char_id )
- return g->member[i].position;
- return -1;
-}
-
-// メンバー情報の作成
-void guild_makemember(struct guild_member *m,struct map_session_data *sd)
-{
- nullpo_retv(sd);
-
- malloc_set(m,0,sizeof(struct guild_member));
- m->account_id =sd->status.account_id;
- m->char_id =sd->status.char_id;
- m->hair =sd->status.hair;
- m->hair_color =sd->status.hair_color;
- m->gender =sd->status.sex;
- m->class_ =sd->status.class_;
- m->lv =sd->status.base_level;
-// m->exp =0;
-// m->exp_payper =0;
- m->online =1;
- m->position =MAX_GUILDPOSITION-1;
- memcpy(m->name,sd->status.name,NAME_LENGTH-1);
- return;
-}
-// ギルド競合確認
-int guild_check_conflict(struct map_session_data *sd)
-{
- nullpo_retr(0, sd);
-
- intif_guild_checkconflict(sd->status.guild_id,
- sd->status.account_id,sd->status.char_id);
- return 0;
-}
-
-// ギルドのEXPキャッシュをinter鯖にフラッシュする
-int guild_payexp_timer_sub(DBKey dataid, void *data, va_list ap)
-{
- int i;
- struct guild_expcache *c;
- struct guild *g;
-
- c = (struct guild_expcache *)data;
-
- if (
- (g = guild_search(c->guild_id)) == NULL ||
- (i = guild_getindex(g, c->account_id, c->char_id)) < 0
- ) {
- ers_free(expcache_ers, data);
- return 0;
- }
-
- if (g->member[i].exp > UINT_MAX - c->exp)
- g->member[i].exp = UINT_MAX;
- else
- g->member[i].exp+= c->exp;
-
- intif_guild_change_memberinfo(g->guild_id,c->account_id,c->char_id,
- GMI_EXP,&g->member[i].exp,sizeof(g->member[i].exp));
- c->exp=0;
-
- ers_free(expcache_ers, data);
- return 0;
-}
-
-int guild_payexp_timer(int tid, unsigned int tick, int id, int data)
-{
- guild_expcache_db->clear(guild_expcache_db,guild_payexp_timer_sub);
- return 0;
-}
-
-//Taken from party_send_xy_timer_sub. [Skotlex]
-int guild_send_xy_timer_sub(DBKey key,void *data,va_list ap)
-{
- struct guild *g=(struct guild *)data;
- int i;
-
- nullpo_retr(0, g);
-
- for(i=0;i<g->max_member;i++){
- struct map_session_data *sd;
- if((sd=g->member[i].sd)!=NULL){
- if(sd->guild_x!=sd->bl.x || sd->guild_y!=sd->bl.y){
- clif_guild_xy(sd);
- sd->guild_x=sd->bl.x;
- sd->guild_y=sd->bl.y;
- }
- }
- }
- return 0;
-}
-
-//Code from party_send_xy_timer [Skotlex]
-static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data)
-{
- guild_db->foreach(guild_db,guild_send_xy_timer_sub,tick);
- return 0;
-}
-
-int guild_send_dot_remove(struct map_session_data *sd)
-{
- if (sd->status.guild_id)
- clif_guild_xy_remove(sd);
- return 0;
-}
-//------------------------------------------------------------------------
-
-// 作成要求
-int guild_create(struct map_session_data *sd,char *name)
-{
- nullpo_retr(0, sd);
-
- if(sd->status.guild_id)
- {
- clif_guild_created(sd,1); // すでに所属している
- return 0;
- }
- if(!battle_config.guild_emperium_check || pc_search_inventory(sd,714) >= 0) {
- struct guild_member m;
- guild_makemember(&m,sd);
- m.position=0;
- intif_guild_create(name,&m);
- return 1;
- }
- clif_guild_created(sd,3); // エンペリウムがいない
- return 0;
-}
-
-// 作成可否
-int guild_created(int account_id,int guild_id)
-{
- struct map_session_data *sd=map_id2sd(account_id);
-
- if(sd==NULL)
- return 0;
- if(!guild_id) {
- clif_guild_created(sd,2); // 作成失敗(同名ギルド存在)
- return 0;
- }
- //struct guild *g;
- sd->status.guild_id=guild_id;
- sd->state.guild_sent=0;
- clif_guild_created(sd,0);
- if(battle_config.guild_emperium_check)
- pc_delitem(sd,pc_search_inventory(sd,714),1,0); // エンペリウム消耗
- return 0;
-}
-
-// 情報要求
-int guild_request_info(int guild_id)
-{
-// if(battle_config.etc_log)
-// printf("guild_request_info\n");
- return intif_guild_request_info(guild_id);
-}
-// イベント付き情報要求
-int guild_npc_request_info(int guild_id,const char *event)
-{
- struct eventlist *ev;
-
- if( guild_search(guild_id) ){
- if(event && *event)
- npc_event_do(event);
- return 0;
- }
-
- if(event==NULL || *event==0)
- return guild_request_info(guild_id);
-
- ev=(struct eventlist *)aCalloc(sizeof(struct eventlist),1);
- memcpy(ev->name,event,strlen(event));
- //The one in the db becomes the next event from this.
- ev->next=idb_put(guild_infoevent_db,guild_id,ev);
- return guild_request_info(guild_id);
-}
-
-// 所属キャラの確認
-int guild_check_member(struct guild *g)
-{
- int i, j, users;
- struct map_session_data *sd, **all_sd;
-
- nullpo_retr(0, g);
-
- all_sd = map_getallusers(&users);
-
- for(i=0;i<users;i++){
- sd=all_sd[i];
- if(sd->status.guild_id==g->guild_id){
- j=guild_getindex(g,sd->status.account_id,sd->status.char_id);
- if (j < 0) {
- sd->status.guild_id=0;
- sd->state.guild_sent=0;
- sd->guild_emblem_id=0;
- if(battle_config.error_log)
- ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
- }
- }
- }
- return 0;
-}
-// 情報所得失敗(そのIDのキャラを全部未所属にする)
-int guild_recv_noinfo(int guild_id)
-{
- int i, users;
- struct map_session_data *sd, **all_sd;
-
- all_sd = map_getallusers(&users);
-
- for(i=0;i<users;i++){
- if((sd=all_sd[i])){
- if(sd->status.guild_id==guild_id)
- sd->status.guild_id=0;
- }
- }
- return 0;
-}
-// 情報所得
-int guild_recv_info(struct guild *sg)
-{
- struct guild *g,before;
- int i,bm,m;
- struct eventlist *ev,*ev2;
-
- nullpo_retr(0, sg);
-
- if((g=idb_get(guild_db,sg->guild_id))==NULL){
- struct map_session_data *sd;
- g=(struct guild *)aCalloc(1,sizeof(struct guild));
- idb_put(guild_db,sg->guild_id,g);
- before=*sg;
-
- // 最初のロードなのでユーザーのチェックを行う
- guild_check_member(sg);
- //If the guild master is online the first time the guild_info is received, that means he was the first to join,
- //and as such, his guild skills should be blocked to avoid login/logout abuse [Skotlex]
- if ((sd = map_nick2sd(sg->master)) != NULL)
- {
- guild_block_skill(sd, 300000);
- //Also set the guild master flag.
- sd->state.gmaster_flag = g;
- clif_charnameupdate(sd); // [LuzZza]
- }
- }else
- before=*g;
- memcpy(g,sg,sizeof(struct guild));
-
- if(g->max_member > MAX_GUILD)
- {
- if (battle_config.error_log)
- ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD);
- g->max_member = MAX_GUILD;
- }
-
- for(i=bm=m=0;i<g->max_member;i++){ // sdの設定と人数の確認
- if(g->member[i].account_id>0){
- struct map_session_data *sd = map_id2sd(g->member[i].account_id);
- if (sd && sd->status.char_id == g->member[i].char_id &&
- sd->status.guild_id == g->guild_id &&
- !sd->state.waitingdisconnect) {
- g->member[i].sd = sd;
- clif_charnameupdate(sd); // [LuzZza]
- } else g->member[i].sd = NULL;
- m++;
- }else
- g->member[i].sd=NULL;
- if(before.member[i].account_id>0)
- bm++;
- }
-
- for(i=0;i<g->max_member;i++){ // 情報の送信
- struct map_session_data *sd = g->member[i].sd;
- if( sd==NULL )
- continue;
-
- if( before.guild_lv!=g->guild_lv || bm!=m ||
- before.max_member!=g->max_member ){
- clif_guild_basicinfo(sd); // 基本情報送信
- clif_guild_emblem(sd,g); // エンブレム送信
- }
-
- if(bm!=m){ // メンバー情報送信
- clif_guild_memberlist(g->member[i].sd);
- }
-
- if( before.skill_point!=g->skill_point)
- clif_guild_skillinfo(sd); // スキル情報送信
-
- if( sd->state.guild_sent==0){ // 未送信なら所属情報も送る
- clif_guild_belonginfo(sd,g);
- clif_guild_notice(sd,g);
- sd->guild_emblem_id=g->emblem_id;
- sd->state.guild_sent=1;
- }
- }
-
- // イベントの発生
- if( (ev=idb_remove(guild_infoevent_db,sg->guild_id))!=NULL ){
- while(ev){
- npc_event_do(ev->name);
- ev2=ev->next;
- aFree(ev);
- ev=ev2;
- }
- }
-
- return 0;
-}
-
-
-// ギルドへの勧誘
-int guild_invite(struct map_session_data *sd,struct map_session_data *tsd)
-{
- struct guild *g;
- int i;
-
- nullpo_retr(0, sd);
-
- g=guild_search(sd->status.guild_id);
-
- if(tsd==NULL || g==NULL)
- return 0;
-
- if(!battle_config.invite_request_check) {
- if (tsd->party_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか
- clif_guild_inviteack(sd,0);
- return 0;
- }
- }
- if(tsd->status.guild_id>0 ||
- tsd->guild_invite>0 ||
- map[tsd->bl.m].flag.gvg_castle)
- { //Can't invite people inside castles. [Skotlex]
- clif_guild_inviteack(sd,0);
- return 0;
- }
-
- // 定員確認
- for(i=0;i<g->max_member;i++)
- if(g->member[i].account_id==0)
- break;
- if(i==g->max_member){
- clif_guild_inviteack(sd,3);
- return 0;
- }
-
- tsd->guild_invite=sd->status.guild_id;
- tsd->guild_invite_account=sd->status.account_id;
-
- clif_guild_invite(tsd,g);
- return 0;
-}
-// ギルド勧誘への返答
-int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag)
-{
- struct map_session_data *tsd;
-
- nullpo_retr(0, sd);
-
- //nullpo_retr(0, tsd= map_id2sd( sd->guild_invite_account ));
- //I checked the code, and there's no "check" for the case where the guy
- //that invites another to a guild quits the map-server before being replied.
- //Hence that's a valid null pointer scenario. :) [Skotlex]
- if ((tsd= map_id2sd( sd->guild_invite_account )) == NULL)
- { //Do we send a "invitation failed" msg or something to the player?
- //Or should we accept the invitation and add it to the guild anyway?
- //afterall, guild_invite holds the guild id that the player was invited to.
- sd->guild_invite=0;
- sd->guild_invite_account=0;
- return 0;
- }
-
- if(sd->guild_invite!=guild_id) // 勧誘とギルドIDが違う
- return 0;
-
- if(flag==1){ // 承諾
- struct guild_member m;
- struct guild *g;
- int i;
-
- // 定員確認
- if( (g=guild_search(tsd->status.guild_id))==NULL ){
- sd->guild_invite=0;
- sd->guild_invite_account=0;
- return 0;
- }
- for(i=0;i<g->max_member;i++)
- if(g->member[i].account_id==0)
- break;
- if(i==g->max_member){
- sd->guild_invite=0;
- sd->guild_invite_account=0;
- clif_guild_inviteack(tsd,3);
- return 0;
- }
-
-
- //inter鯖へ追加要求
- guild_makemember(&m,sd);
- intif_guild_addmember( sd->guild_invite, &m );
- return 0;
- }else{ // 拒否
- sd->guild_invite=0;
- sd->guild_invite_account=0;
- clif_guild_inviteack(tsd,1);
- }
- return 0;
-}
-// ギルドメンバが追加された
-int guild_member_added(int guild_id,int account_id,int char_id,int flag)
-{
- struct map_session_data *sd= map_id2sd(account_id),*sd2;
- struct guild *g;
-
- if( (g=guild_search(guild_id))==NULL )
- return 0;
-
- if(sd==NULL || sd->guild_invite==0){
- // キャラ側に登録できなかったため脱退要求を出す
- if (flag == 0) {
- if(battle_config.error_log)
- ShowError("guild: member added error %d is not online\n",account_id);
- intif_guild_leave(guild_id,account_id,char_id,0,"**登録失敗**");
- }
- return 0;
- }
- sd2 = map_id2sd(sd->guild_invite_account);
- sd->guild_invite = 0;
- sd->guild_invite_account = 0;
-
- if(flag==1){ // 失敗
- if( sd2!=NULL )
- clif_guild_inviteack(sd2,3);
- return 0;
- }
-
- // 成功
- sd->state.guild_sent=0;
- sd->status.guild_id=guild_id;
-
- if( sd2!=NULL )
- clif_guild_inviteack(sd2,2);
-
- // いちおう競合確認
- guild_check_conflict(sd);
- //Next line commented because it do nothing, look at guild_recv_info [LuzZza]
- //clif_charnameupdate(sd); //Update display name [Skotlex]
- return 0;
-}
-
-// ギルド脱退要求
-int guild_leave(struct map_session_data *sd,int guild_id,
- int account_id,int char_id,const char *mes)
-{
- struct guild *g;
- int i;
-
- nullpo_retr(0, sd);
-
- g = guild_search(sd->status.guild_id);
-
- if(g==NULL)
- return 0;
-
- if( sd->status.account_id!=account_id ||
- sd->status.char_id!=char_id || sd->status.guild_id!=guild_id ||
- map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles.
- return 0;
-
- for(i=0;i<g->max_member;i++){ // 所属しているか
- if( g->member[i].account_id==sd->status.account_id &&
- g->member[i].char_id==sd->status.char_id ){
- intif_guild_leave(g->guild_id,sd->status.account_id,sd->status.char_id,0,mes);
- return 0;
- }
- }
- return 0;
-}
-// ギルド追放要求
-int guild_expulsion(struct map_session_data *sd,int guild_id,
- int account_id,int char_id,const char *mes)
-{
- struct guild *g;
- int i,ps;
-
- nullpo_retr(0, sd);
-
- g = guild_search(sd->status.guild_id);
-
- if(g==NULL)
- return 0;
-
- if(sd->status.guild_id!=guild_id || map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles.
- return 0;
-
- if( (ps=guild_getposition(sd,g))<0 || !(g->position[ps].mode&0x0010) )
- return 0; // 処罰権限無し
-
- for(i=0;i<g->max_member;i++){ // 所属しているか
- if( g->member[i].account_id==account_id &&
- g->member[i].char_id==char_id ){
- intif_guild_leave(g->guild_id,account_id,char_id,1,mes);
- //It's wrong way, member info will erased later
- //see guild_member_leaved [LuzZza]
- //malloc_set(&g->member[i],0,sizeof(struct guild_member));
- return 0;
- }
- }
- return 0;
-}
-
-int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
- const char *name,const char *mes) // rewrote [LuzZza]
-{
- int i;
- struct guild *g = guild_search(guild_id);
- struct map_session_data *sd = map_charid2sd(char_id);
- struct map_session_data *online_member_sd;
-
- if(g == NULL)
- return 0;
-
- for(i=0;i<g->max_member;i++) {
- if( g->member[i].account_id == account_id &&
- g->member[i].char_id == char_id ){
-
- if((online_member_sd = guild_getavailablesd(g)) == NULL)
- return 0;
-
- if(!flag)
- clif_guild_leave(online_member_sd, name, mes);
- else
- clif_guild_expulsion(online_member_sd, name, mes, account_id);
-
- malloc_set(&g->member[i],0,sizeof(struct guild_member));
- clif_guild_memberlist(online_member_sd);
-
- if(sd != NULL && sd->status.guild_id == guild_id) {
- if (sd->state.storage_flag == 2) //Close the guild storage.
- storage_guild_storageclose(sd);
- sd->status.guild_id=0;
- sd->guild_emblem_id=0;
- sd->state.guild_sent=0;
-
- guild_send_dot_remove(sd);
- clif_charnameupdate(sd); //Update display name [Skotlex]
- }
- return 0;
- }
-
- }
-
- return 0;
-}
-
-int guild_send_memberinfoshort(struct map_session_data *sd,int online)
-{ // cleaned up [LuzZza]
- struct guild *g;
-
- nullpo_retr(0, sd);
-
- if(!(g = guild_search(sd->status.guild_id)))
- return 0;
-
- //Moved to place before intif_guild_memberinfoshort because
- //If it's not a member, needn't send it's info to intif. [LuzZza]
- guild_check_member(g);
-
- if(sd->status.guild_id <= 0)
- return 0;
-
- intif_guild_memberinfoshort(g->guild_id,
- sd->status.account_id,sd->status.char_id,online,sd->status.base_level,sd->status.class_);
-
- if(!online){
- int i=guild_getindex(g,sd->status.account_id,sd->status.char_id);
- if(i>=0)
- g->member[i].sd=NULL;
- return 0;
- }
-
- if(sd->state.guild_sent)
- return 0;
-
- clif_guild_belonginfo(sd,g);
- clif_guild_notice(sd,g);
-
- sd->state.guild_sent = 1;
- sd->guild_emblem_id = g->emblem_id;
-
- return 0;
-}
-
-int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_)
-{ // cleaned up [LuzZza]
-
- int i,alv,c,idx=-1,om=0,oldonline=-1;
- struct guild *g = guild_search(guild_id);
-
- if(g == NULL)
- return 0;
-
- for(i=0,alv=0,c=0,om=0;i<g->max_member;i++){
- struct guild_member *m=&g->member[i];
- if(m->account_id==account_id && m->char_id==char_id ){
- oldonline=m->online;
- m->online=online;
- m->lv=lv;
- m->class_=class_;
- idx=i;
- }
- if(m->account_id>0){
- alv+=m->lv;
- c++;
- }
- if(m->online)
- om++;
- }
-
- if(idx == -1 || c == 0) {
- // ギルドのメンバー外なので追放扱いする
- struct map_session_data *sd = map_id2sd(account_id);
- if(sd && sd->status.char_id == char_id) {
- sd->status.guild_id=0;
- sd->guild_emblem_id=0;
- sd->state.guild_sent=0;
- }
- if(battle_config.error_log)
- ShowWarning("guild: not found member %d,%d on %d[%s]\n", account_id,char_id,guild_id,g->name);
- return 0;
- }
-
- g->average_lv=alv/c;
- g->connect_member=om;
-
- for(i=0;i<g->max_member;i++) {
- struct map_session_data *sd= map_id2sd(g->member[i].account_id);
- g->member[i].sd = (sd && sd->status.char_id == g->member[i].char_id &&
- sd->status.guild_id == g->guild_id && !sd->state.waitingdisconnect) ? sd : NULL;
- }
-
- if(oldonline!=online)
- clif_guild_memberlogin_notice(g, idx, online);
-
-
- if(!g->member[idx].sd)
- return 0;
-
- //Send XY dot updates. [Skotlex]
- //Moved from guild_send_memberinfoshort [LuzZza]
- for(i=0; i < g->max_member; i++) {
-
- if(!g->member[i].sd || i == idx ||
- g->member[i].sd->bl.m != g->member[idx].sd->bl.m)
- continue;
-
- clif_guild_xy_single(g->member[idx].sd->fd, g->member[i].sd);
- }
-
- return 0;
-}
-// ギルド会話送信
-int guild_send_message(struct map_session_data *sd,char *mes,int len)
-{
- nullpo_retr(0, sd);
-
- if(sd->status.guild_id==0)
- return 0;
- intif_guild_message(sd->status.guild_id,sd->status.account_id,mes,len);
- guild_recv_message(sd->status.guild_id,sd->status.account_id,mes,len);
-
- //Chatlogging type 'G'
- if(log_config.chat&1 //we log everything then
- || ( log_config.chat&8 //if Guild bit is on
- && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
- log_chat("G", sd->status.guild_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
-
- return 0;
-}
-// ギルド会話受信
-int guild_recv_message(int guild_id,int account_id,char *mes,int len)
-{
- struct guild *g;
- if( (g=guild_search(guild_id))==NULL)
- return 0;
- clif_guild_message(g,account_id,mes,len);
- return 0;
-}
-// ギルドメンバの役職変更
-int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx)
-{
- return intif_guild_change_memberinfo(
- guild_id,account_id,char_id,GMI_POSITION,&idx,sizeof(idx));
-}
-// ギルドメンバの役職変更通知
-int guild_memberposition_changed(struct guild *g,int idx,int pos)
-{
- nullpo_retr(0, g);
-
- g->member[idx].position=pos;
- clif_guild_memberpositionchanged(g,idx);
-
- // Update char position in client [LuzZza]
- if(g->member[idx].sd != NULL)
- clif_charnameupdate(g->member[idx].sd);
- return 0;
-}
-// ギルド役職変更
-int guild_change_position(struct map_session_data *sd,int idx,
- int mode,int exp_mode,const char *name)
-{
- struct guild_position p;
-
- nullpo_retr(0, sd);
-
- if(exp_mode>battle_config.guild_exp_limit)
- exp_mode=battle_config.guild_exp_limit;
- if(exp_mode<0)exp_mode=0;
- p.mode=mode;
- p.exp_mode=exp_mode;
- memcpy(p.name,name,NAME_LENGTH-1);
- p.name[NAME_LENGTH-1] = '\0'; //Security check... [Skotlex]
- return intif_guild_position(sd->status.guild_id,idx,&p);
-}
-// ギルド役職変更通知
-int guild_position_changed(int guild_id,int idx,struct guild_position *p)
-{
- struct guild *g=guild_search(guild_id);
- int i;
- if(g==NULL)
- return 0;
- memcpy(&g->position[idx],p,sizeof(struct guild_position));
- clif_guild_positionchanged(g,idx);
-
- // Update char name in client [LuzZza]
- for(i=0;i<g->max_member;i++)
- if(g->member[i].position == idx && g->member[i].sd != NULL)
- clif_charnameupdate(g->member[i].sd);
- return 0;
-}
-// ギルド告知変更
-int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2)
-{
- nullpo_retr(0, sd);
-
- if(guild_id!=sd->status.guild_id)
- return 0;
- return intif_guild_notice(guild_id,mes1,mes2);
-}
-// ギルド告知変更通知
-int guild_notice_changed(int guild_id,const char *mes1,const char *mes2)
-{
- int i;
- struct map_session_data *sd;
- struct guild *g=guild_search(guild_id);
- if(g==NULL)
- return 0;
-
- memcpy(g->mes1,mes1,60);
- memcpy(g->mes2,mes2,120);
-
- for(i=0;i<g->max_member;i++){
- if((sd=g->member[i].sd)!=NULL)
- clif_guild_notice(sd,g);
- }
- return 0;
-}
-// ギルドエンブレム変更
-int guild_change_emblem(struct map_session_data *sd,int len,const char *data)
-{
- struct guild *g;
- nullpo_retr(0, sd);
-
- if (battle_config.require_glory_guild &&
- !((g = guild_search(sd->status.guild_id)) && guild_checkskill(g, GD_GLORYGUILD)>0)) {
- clif_skill_fail(sd,GD_GLORYGUILD,0,0);
- return 0;
- }
-
- return intif_guild_emblem(sd->status.guild_id,len,data);
-}
-// ギルドエンブレム変更通知
-int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data)
-{
- int i;
- struct map_session_data *sd;
- struct guild *g=guild_search(guild_id);
- if(g==NULL)
- return 0;
-
- memcpy(g->emblem_data,data,len);
- g->emblem_len=len;
- g->emblem_id=emblem_id;
-
- for(i=0;i<g->max_member;i++){
- if((sd=g->member[i].sd)!=NULL){
- sd->guild_emblem_id=emblem_id;
- clif_guild_belonginfo(sd,g);
- clif_guild_emblem(sd,g);
- }
- }
- return 0;
-}
-
-static void* create_expcache(DBKey key, va_list args) {
- struct guild_expcache *c;
- struct map_session_data *sd = va_arg(args, struct map_session_data*);
-
- c = ers_alloc(expcache_ers, struct guild_expcache);
- c->guild_id = sd->status.guild_id;
- c->account_id = sd->status.account_id;
- c->char_id = sd->status.char_id;
- c->exp = 0;
- return c;
-}
-
-// ギルドのEXP上納
-unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp)
-{
- struct guild *g;
- struct guild_expcache *c;
- int per;
-
- nullpo_retr(0, sd);
-
- if (!exp) return 0;
-
- if (sd->status.guild_id == 0 ||
- (g = guild_search(sd->status.guild_id)) == NULL ||
- (per = guild_getposition(sd,g)) < 0 ||
- (per = g->position[per].exp_mode) < 1)
- return 0;
-
-
- if (per < 100)
- exp = (unsigned int) exp * per / 100;
- //Otherwise tax everything.
-
- c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
-
- if (c->exp > UINT_MAX - exp)
- c->exp = UINT_MAX;
- else
- c->exp += exp;
-
- return exp;
-}
-
-// Celest
-int guild_getexp(struct map_session_data *sd,int exp)
-{
- struct guild *g;
- struct guild_expcache *c;
- nullpo_retr(0, sd);
-
- if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL)
- return 0;
-
- c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
- if (c->exp > UINT_MAX - exp)
- c->exp = UINT_MAX;
- else
- c->exp += exp;
- return exp;
-}
-
-// スキルポイント割り振り
-int guild_skillup(struct map_session_data *sd,int skill_num,int flag)
-{
- struct guild *g;
- int idx = skill_num - GD_SKILLBASE;
-
- nullpo_retr(0, sd);
-
- if(idx < 0 || idx >= MAX_GUILDSKILL)
-
- return 0;
- if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL)
- return 0;
- if(strcmp(sd->status.name,g->master))
- return 0;
-
- if( (g->skill_point>0 || flag&1) &&
- g->skill[idx].id!=0 &&
- g->skill[idx].lv < guild_skill_get_max(skill_num) ){
- intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id,flag);
- }
- return 0;
-}
-// スキルポイント割り振り通知
-int guild_skillupack(int guild_id,int skill_num,int account_id)
-{
- struct map_session_data *sd=map_id2sd(account_id);
- struct guild *g=guild_search(guild_id);
- int i;
- if(g==NULL)
- return 0;
- if(sd!=NULL)
- clif_guild_skillup(sd,skill_num,g->skill[skill_num-GD_SKILLBASE].lv);
- // 全員に通知
- for(i=0;i<g->max_member;i++)
- if((sd=g->member[i].sd)!=NULL)
- clif_guild_skillinfo(sd);
- return 0;
-}
-
-// ギルド同盟数所得
-int guild_get_alliance_count(struct guild *g,int flag)
-{
- int i,c;
-
- nullpo_retr(0, g);
-
- for(i=c=0;i<MAX_GUILDALLIANCE;i++){
- if( g->alliance[i].guild_id>0 &&
- g->alliance[i].opposition==flag )
- c++;
- }
- return c;
-}
-
-// Blocks all guild skills which have a common delay time.
-void guild_block_skill(struct map_session_data *sd, int time) {
- int skill_num[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL };
- int i;
- for (i = 0; i < 4; i++)
- skill_blockpc_start(sd, skill_num[i], time);
-}
-
-// 同盟関係かどうかチェック
-// 同盟なら1、それ以外は0
-int guild_check_alliance(int guild_id1, int guild_id2, int flag)
-{
- struct guild *g;
- int i;
-
- g = guild_search(guild_id1);
- if (g == NULL)
- return 0;
-
- for (i=0; i<MAX_GUILDALLIANCE; i++)
- if ((g->alliance[i].guild_id == guild_id2) && (g->alliance[i].opposition == flag))
- return 1;
-
- return 0;
-}
-// ギルド同盟要求
-int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd)
-{
- struct guild *g[2];
- int i;
-
- if(agit_flag) { // Disable alliance creation during woe [Valaris]
- clif_displaymessage(sd->fd,"Alliances cannot be made during Guild Wars!");
- return 0;
- } // end addition [Valaris]
-
-
- nullpo_retr(0, sd);
-
- if(tsd==NULL || tsd->status.guild_id<=0)
- return 0;
-
- g[0]=guild_search(sd->status.guild_id);
- g[1]=guild_search(tsd->status.guild_id);
-
- if(g[0]==NULL || g[1]==NULL)
- return 0;
-
- // Prevent creation alliance with same guilds [LuzZza]
- if(sd->status.guild_id == tsd->status.guild_id)
- return 0;
-
- if( guild_get_alliance_count(g[0],0)>=3 ) {
- clif_guild_allianceack(sd,4);
- return 0;
- }
- if( guild_get_alliance_count(g[1],0)>=3 ) {
- clif_guild_allianceack(sd,3);
- return 0;
- }
-
- if( tsd->guild_alliance>0 ){
- clif_guild_allianceack(sd,1);
- return 0;
- }
-
- for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに同盟状態か確認
- if( g[0]->alliance[i].guild_id==tsd->status.guild_id &&
- g[0]->alliance[i].opposition==0){
- clif_guild_allianceack(sd,0);
- return 0;
- }
- }
-
- tsd->guild_alliance=sd->status.guild_id;
- tsd->guild_alliance_account=sd->status.account_id;
-
- clif_guild_reqalliance(tsd,sd->status.account_id,g[0]->name);
- return 0;
-}
-// ギルド勧誘への返答
-int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag)
-{
- struct map_session_data *tsd;
-
- nullpo_retr(0, sd);
- tsd= map_id2sd( account_id );
- if (!tsd) { //Character left? Cancel alliance.
- clif_guild_allianceack(sd,3);
- return 0;
- }
-
- if(sd->guild_alliance!=tsd->status.guild_id) // 勧誘とギルドIDが違う
- return 0;
-
- if(flag==1){ // 承諾
- int i;
-
- struct guild *g,*tg; // 同盟数再確認
- g=guild_search(sd->status.guild_id);
- tg=guild_search(tsd->status.guild_id);
-
- if(g==NULL || guild_get_alliance_count(g,0)>=3){
- clif_guild_allianceack(sd,4);
- clif_guild_allianceack(tsd,3);
- return 0;
- }
- if(tg==NULL || guild_get_alliance_count(tg,0)>=3){
- clif_guild_allianceack(sd,3);
- clif_guild_allianceack(tsd,4);
- return 0;
- }
-
- for(i=0;i<MAX_GUILDALLIANCE;i++){
- if(g->alliance[i].guild_id==tsd->status.guild_id &&
- g->alliance[i].opposition==1)
- intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
- sd->status.account_id,tsd->status.account_id,9 );
- }
- for(i=0;i<MAX_GUILDALLIANCE;i++){
- if(tg->alliance[i].guild_id==sd->status.guild_id &&
- tg->alliance[i].opposition==1)
- intif_guild_alliance( tsd->status.guild_id,sd->status.guild_id,
- tsd->status.account_id,sd->status.account_id,9 );
- }
-
- // inter鯖へ同盟要請
- intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
- sd->status.account_id,tsd->status.account_id,0 );
- return 0;
- }else{ // 拒否
- sd->guild_alliance=0;
- sd->guild_alliance_account=0;
- if(tsd!=NULL)
- clif_guild_allianceack(tsd,3);
- }
- return 0;
-}
-// ギルド関係解消
-int guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
-{
- nullpo_retr(0, sd);
-
- if(agit_flag) { // Disable alliance breaking during woe [Valaris]
- clif_displaymessage(sd->fd,"Alliances cannot be broken during Guild Wars!");
- return 0;
- } // end addition [Valaris]
-
- intif_guild_alliance( sd->status.guild_id,guild_id,
- sd->status.account_id,0,flag|8 );
- return 0;
-}
-// ギルド敵対
-int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd)
-{
- struct guild *g;
- int i;
-
- nullpo_retr(0, sd);
-
- g=guild_search(sd->status.guild_id);
- if(g==NULL || tsd==NULL)
- return 0;
-
- // Prevent creation opposition with same guilds [LuzZza]
- if(sd->status.guild_id == tsd->status.guild_id)
- return 0;
-
- if( guild_get_alliance_count(g,1)>=3 ) {
- clif_guild_oppositionack(sd,1);
- return 0;
- }
-
- if(agit_flag) {
- clif_displaymessage(sd->fd,"You cannot make oppositions during Guild Wars!");
- return 0;
- }
-
- for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに関係を持っているか確認
- if(g->alliance[i].guild_id==tsd->status.guild_id){
- if(g->alliance[i].opposition==1){ // すでに敵対
- clif_guild_oppositionack(sd,2);
- return 0;
- }
- //Change alliance to opposition.
- intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
- sd->status.account_id,tsd->status.account_id,8 );
- }
- }
-
- // inter鯖に敵対要請
- intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
- sd->status.account_id,tsd->status.account_id,1 );
- return 0;
-}
-// ギルド同盟/敵対通知
-int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
- int flag,const char *name1,const char *name2)
-{
- struct guild *g[2];
- int guild_id[2];
- const char *guild_name[2];
- struct map_session_data *sd[2];
- int j,i;
-
- guild_id[0] = guild_id1;
- guild_id[1] = guild_id2;
- guild_name[0] = name1;
- guild_name[1] = name2;
- sd[0] = map_id2sd(account_id1);
- sd[1] = map_id2sd(account_id2);
-
- g[0]=guild_search(guild_id1);
- g[1]=guild_search(guild_id2);
-
- if(sd[0]!=NULL && (flag&0x0f)==0){
- sd[0]->guild_alliance=0;
- sd[0]->guild_alliance_account=0;
- }
-
- if(flag&0x70){ // 失敗
- for(i=0;i<2-(flag&1);i++)
- if( sd[i]!=NULL )
- clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4);
- return 0;
- }
-// if(battle_config.etc_log)
-// printf("guild alliance_ack %d %d %d %d %d %s %s\n",guild_id1,guild_id2,account_id1,account_id2,flag,name1,name2);
-
- if(!(flag&0x08)){ // 関係追加
- for(i=0;i<2-(flag&1);i++)
- if(g[i]!=NULL)
- for(j=0;j<MAX_GUILDALLIANCE;j++)
- if(g[i]->alliance[j].guild_id==0){
- g[i]->alliance[j].guild_id=guild_id[1-i];
- memcpy(g[i]->alliance[j].name,guild_name[1-i],NAME_LENGTH-1);
- g[i]->alliance[j].opposition=flag&1;
- break;
- }
- }else{ // 関係解消
- for(i=0;i<2-(flag&1);i++){
- if(g[i]!=NULL)
- for(j=0;j<MAX_GUILDALLIANCE;j++)
- if( g[i]->alliance[j].guild_id==guild_id[1-i] &&
- g[i]->alliance[j].opposition==(flag&1)){
- g[i]->alliance[j].guild_id=0;
- break;
- }
- if( sd[i]!=NULL ) // 解消通知
- clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1));
- }
- }
-
- if((flag&0x0f)==0){ // 同盟通知
- if( sd[1]!=NULL )
- clif_guild_allianceack(sd[1],2);
- }else if((flag&0x0f)==1){ // 敵対通知
- if( sd[0]!=NULL )
- clif_guild_oppositionack(sd[0],0);
- }
-
-
- for(i=0;i<2-(flag&1);i++){ // 同盟/敵対リストの再送信
- struct map_session_data *sd;
- if(g[i]!=NULL)
- for(j=0;j<g[i]->max_member;j++)
- if((sd=g[i]->member[j].sd)!=NULL)
- clif_guild_allianceinfo(sd);
- }
- return 0;
-}
-// ギルド解散通知用
-int guild_broken_sub(DBKey key,void *data,va_list ap)
-{
- struct guild *g=(struct guild *)data;
- int guild_id=va_arg(ap,int);
- int i,j;
- struct map_session_data *sd=NULL;
-
- nullpo_retr(0, g);
-
- for(i=0;i<MAX_GUILDALLIANCE;i++){ // 関係を破棄
- if(g->alliance[i].guild_id==guild_id){
- for(j=0;j<g->max_member;j++)
- if( (sd=g->member[j].sd)!=NULL )
- clif_guild_delalliance(sd,guild_id,g->alliance[i].opposition);
- intif_guild_alliance(g->guild_id, guild_id,0,0,g->alliance[i].opposition|8);
- g->alliance[i].guild_id=0;
- }
- }
- return 0;
-}
-
-//Invoked on Castles when a guild is broken. [Skotlex]
-int castle_guild_broken_sub(DBKey key,void *data,va_list ap)
-{
- struct guild_castle *gc=(struct guild_castle *)data;
- int guild_id=va_arg(ap,int);
-
- nullpo_retr(0, gc);
-
- if (gc->guild_id == guild_id)
- { //Save the new 'owner', this should invoke guardian clean up and other such things.
- gc->guild_id = 0;
- guild_castledatasave(gc->castle_id, 1, 0);
- }
- return 0;
-}
-
-//Innvoked on /breakguild "Guild name"
-int guild_broken(int guild_id,int flag)
-{
- struct guild *g=guild_search(guild_id);
-// struct guild_castle *gc=NULL;
- struct map_session_data *sd;
- int i;
-// char *name;;
-
- if(flag!=0 || g==NULL)
- return 0;
-
- //we call castle_event::OnGuildBreak of all castlesof the guild
- //you can set all castle_events in the castle_db.txt
-/* name=(char *)aCalloc(50,sizeof(char)); //24 char = event name, + space for "::OnGuildBreak"
- for(i=0;i<MAX_GUILDCASTLE;i++){
- if( (gc=guild_castle_search(i)) != NULL ){
- if(gc->guild_id == guild_id){
- memcpy(name,gc->castle_event,50);
- npc_event_do(strcat(name,"::OnGuildBreak"));
- }
- }
- }
- free(name);
-*/
- for(i=0;i<g->max_member;i++){ // ギルド解散を通知
- if((sd=g->member[i].sd)!=NULL){
- if(sd->state.storage_flag == 2)
- storage_guild_storage_quit(sd,1);
- sd->status.guild_id=0;
- sd->state.guild_sent=0;
- clif_guild_broken(g->member[i].sd,0);
- clif_charnameupdate(sd); // [LuzZza]
- }
- }
-
- guild_db->foreach(guild_db,guild_broken_sub,guild_id);
- castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id);
- if (guild_cache && guild_cache->guild_id == guild_id)
- guild_cache = NULL;
- guild_storage_delete(guild_id);
- idb_remove(guild_db,guild_id);
- return 0;
-}
-
-//Changes the Guild Master to the specified player. [Skotlex]
-int guild_gm_change(int guild_id, struct map_session_data *sd)
-{
- struct guild *g;
- nullpo_retr(0, sd);
-
- if (sd->status.guild_id != guild_id)
- return 0;
-
- g=guild_search(guild_id);
-
- nullpo_retr(0, g);
-
- if (strcmp(g->master, sd->status.name) == 0) //Nothing to change.
- return 0;
-
- //Notify servers that master has changed.
- intif_guild_change_gm(guild_id, sd->status.name, strlen(sd->status.name));
- return 1;
-}
-
-//Notification from Char server that a guild's master has changed. [Skotlex]
-int guild_gm_changed(int guild_id, int pos)
-{
- struct guild *g;
- struct guild_member gm;
-
- g=guild_search(guild_id);
-
- if (!g || pos < 0 || pos > g->max_member)
- return 0;
-
- memcpy(&gm, &g->member[pos], sizeof (struct guild_member));
- memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member));
- memcpy(&g->member[0], &gm, sizeof(struct guild_member));
-
- g->member[pos].position = g->member[0].position;
- g->member[0].position = 0; //Position 0: guild Master.
- strcpy(g->master, g->member[0].name);
-
- if (g->member[pos].sd && g->member[pos].sd->fd)
- {
- clif_displaymessage(g->member[pos].sd->fd, "You no longer are the Guild Master.");
- g->member[pos].sd->state.gmaster_flag = 0;
- }
-
- if (g->member[0].sd && g->member[0].sd->fd)
- {
- clif_displaymessage(g->member[0].sd->fd, "You have become the Guild Master!");
- g->member[0].sd->state.gmaster_flag = g;
- //Block his skills for 5 minutes to prevent abuse.
- guild_block_skill(g->member[0].sd, 300000);
- }
- return 1;
-}
-
-// ギルド解散
-int guild_break(struct map_session_data *sd,char *name)
-{
- struct guild *g;
- int i;
-
- nullpo_retr(0, sd);
-
- if( (g=guild_search(sd->status.guild_id))==NULL )
- return 0;
- if(strcmp(g->name,name)!=0)
- return 0;
- if(strcmp(sd->status.name,g->master)!=0)
- return 0;
- for(i=0;i<g->max_member;i++){
- if( g->member[i].account_id>0 && (
- g->member[i].account_id!=sd->status.account_id ||
- g->member[i].char_id!=sd->status.char_id ))
- break;
- }
- if(i<g->max_member){
- clif_guild_broken(sd,2);
- return 0;
- }
-
- intif_guild_break(g->guild_id);
- return 0;
-}
-
-// ギルド城データ要求
-int guild_castledataload(int castle_id,int index)
-{
- return intif_guild_castle_dataload(castle_id,index);
-}
-// ギルド城情報所得時イベント追加
-int guild_addcastleinfoevent(int castle_id,int index,const char *name)
-{
- struct eventlist *ev;
- int code=castle_id|(index<<16);
-
- if( name==NULL || *name==0 )
- return 0;
-
- ev=(struct eventlist *)aMalloc(sizeof(struct eventlist));
- memcpy(ev->name,name,sizeof(ev->name));
- //The next event becomes whatever was currently stored.
- ev->next= idb_put(guild_castleinfoevent_db,code,ev);
- return 0;
-}
-
-// ギルド城データ要求返信
-int guild_castledataloadack(int castle_id,int index,int value)
-{
- struct guild_castle *gc=guild_castle_search(castle_id);
- int code=castle_id|(index<<16);
- struct eventlist *ev,*ev2;
-
- if(gc==NULL){
- return 0;
- }
- switch(index){
- case 1:
- gc->guild_id = value;
- if (value && guild_search(value)==NULL) //Request guild data which will be required for spawned guardians. [Skotlex]
- guild_request_info(value);
- break;
- case 2: gc->economy = value; break;
- case 3: gc->defense = value; break;
- case 4: gc->triggerE = value; break;
- case 5: gc->triggerD = value; break;
- case 6: gc->nextTime = value; break;
- case 7: gc->payTime = value; break;
- case 8: gc->createTime = value; break;
- case 9: gc->visibleC = value; break;
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- case 16:
- case 17:
- gc->guardian[index-10].visible = value; break;
- case 18:
- case 19:
- case 20:
- case 21:
- case 22:
- case 23:
- case 24:
- case 25:
- gc->guardian[index-18].hp = value; break;
- default:
- ShowError("guild_castledataloadack ERROR!! (Not found index=%d)\n", index);
- return 0;
- }
- if( (ev=idb_remove(guild_castleinfoevent_db,code))!=NULL ){
- while(ev){
- npc_event_do(ev->name);
- ev2=ev->next;
- aFree(ev);
- ev=ev2;
- }
- }
- return 1;
-}
-// ギルド城データ変更要求
-int guild_castledatasave(int castle_id,int index,int value)
-{
- if (index == 1)
- { //The castle's owner has changed? Update Guardian ownership, too. [Skotlex]
- struct guild_castle *gc = guild_castle_search(castle_id);
- int m = -1;
- if (gc) m = map_mapname2mapid(gc->map_name);
- if (m != -1)
- map_foreachinmap(mob_guardian_guildchange, m, BL_MOB);
- }
- return intif_guild_castle_datasave(castle_id,index,value);
-}
-
-// ギルド城データ変更通知
-int guild_castledatasaveack(int castle_id,int index,int value)
-{
- struct guild_castle *gc=guild_castle_search(castle_id);
- if(gc==NULL){
- return 0;
- }
- switch(index){
- case 1: gc->guild_id = value; break;
- case 2: gc->economy = value; break;
- case 3: gc->defense = value; break;
- case 4: gc->triggerE = value; break;
- case 5: gc->triggerD = value; break;
- case 6: gc->nextTime = value; break;
- case 7: gc->payTime = value; break;
- case 8: gc->createTime = value; break;
- case 9: gc->visibleC = value; break;
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
- case 16:
- case 17:
- gc->guardian[index-10].visible = value; break;
- case 18:
- case 19:
- case 20:
- case 21:
- case 22:
- case 23:
- case 24:
- case 25:
- gc->guardian[index-18].hp = value; break;
- default:
- ShowError("guild_castledatasaveack ERROR!! (Not found index=%d)\n", index);
- return 0;
- }
- return 1;
-}
-
-// ギルドデータ一括受信(初期化時)
-int guild_castlealldataload(int len,struct guild_castle *gc)
-{
- int i;
- int n = (len-4) / sizeof(struct guild_castle), ev = -1;
-
- nullpo_retr(0, gc);
-
- //Last owned castle in the list invokes ::OnAgitinit
- for(i = 0; i < n; i++) {
- if ((gc + i)->guild_id)
- ev = i;
- }
-
- // 城データ格納とギルド情報要求
- for(i = 0; i < n; i++, gc++) {
- struct guild_castle *c = guild_castle_search(gc->castle_id);
- if (!c) {
- ShowError("guild_castlealldataload Castle id=%d not found.\n", gc->castle_id);
- continue;
- }
- memcpy(&c->guild_id,&gc->guild_id,
- sizeof(struct guild_castle) - ((int)&c->guild_id - (int)c) );
- if( c->guild_id ){
- if(i!=ev)
- guild_request_info(c->guild_id);
- else
- guild_npc_request_info(c->guild_id, "::OnAgitInit");
- }
- }
- if (ev == -1) //No castles owned, invoke OnAgitInit as it is.
- npc_event_doall("OnAgitInit");
- return 0;
-}
-
-int guild_agit_start(void)
-{ // Run All NPC_Event[OnAgitStart]
- int c = npc_event_doall("OnAgitStart");
- ShowStatus("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n",c);
- // Start auto saving
- guild_save_timer = add_timer_interval (gettick() + GUILD_SAVE_INTERVAL, guild_save_sub, 0, 0, GUILD_SAVE_INTERVAL);
- return 0;
-}
-
-int guild_agit_end(void)
-{ // Run All NPC_Event[OnAgitEnd]
- int c = npc_event_doall("OnAgitEnd");
- ShowStatus("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n",c);
- // Stop auto saving
- delete_timer (guild_save_timer, guild_save_sub);
- return 0;
-}
-
-int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data)
-{ // Run One NPC_Event[OnAgitEliminate]
- char *name = (char*)data;
- size_t len = (name) ? strlen(name) : 0;
- // the rest is dangerous, but let it crash,
- // if this happens, it's ruined anyway
- int c=0;
-
- if(agit_flag) // Agit not already End
- {
- char *evname=(char*)aMalloc( (len + 10) * sizeof(char));
- memcpy(evname,name,len - 5);
- strcpy(evname + len - 5,"Eliminate");
- c = npc_event_do(evname);
- ShowStatus("NPC_Event:[%s] Run (%d) Events.\n",evname,c);
- aFree(evname); // [Lance] Should fix this
- }
- if(name) aFree(name);
- return 0;
-}
-
-static int Ghp[MAX_GUILDCASTLE][MAX_GUARDIANS]; // so save only if HP are changed // experimental code [Yor]
-static int Gid[MAX_GUILDCASTLE];
-int guild_save_sub(int tid,unsigned int tick,int id,int data)
-{
- struct guild_castle *gc;
- int i,j;
-
- for(i = 0; i < MAX_GUILDCASTLE; i++) { // [Yor]
- gc = guild_castle_search(i);
- if (!gc) continue;
- if (gc->guild_id != Gid[i]) {
- // Re-save guild id if its owner guild has changed
- guild_castledatasave(gc->castle_id, 1, gc->guild_id);
- Gid[i] = gc->guild_id;
- }
- for (j = 0; j < MAX_GUARDIANS; j++)
- {
- if (gc->guardian[j].visible && Ghp[i][j] != gc->guardian[j].hp)
- guild_castledatasave(gc->castle_id, 18+j, gc->guardian[j].hp);
- }
- }
-
- return 0;
-}
-
-int guild_agit_break(struct mob_data *md)
-{ // Run One NPC_Event[OnAgitBreak]
- char *evname;
-
- nullpo_retr(0, md);
-
- evname=(char *)aMallocA((strlen(md->npc_event) + 1)*sizeof(char));
-
- strcpy(evname,md->npc_event);
-// Now By User to Run [OnAgitBreak] NPC Event...
-// It's a little impossible to null point with player disconnect in this!
-// But Script will be stop, so nothing...
-// Maybe will be changed in the futher..
-// int c = npc_event_do(evname);
- if(!agit_flag) return 0; // Agit already End
- add_timer(gettick()+battle_config.gvg_eliminate_time,guild_gvg_eliminate_timer,md->bl.m,(int)evname);
- return 0;
-}
-
-// [MouseJstr]
-// How many castles does this guild have?
-int guild_checkcastles(struct guild *g) {
- int i,nb_cas=0, id,cas_id=0;
- struct guild_castle *gc;
- id=g->guild_id;
- for(i=0;i<MAX_GUILDCASTLE;i++){
- gc=guild_castle_search(i);
- cas_id=gc->guild_id;
- if(g->guild_id==cas_id)
- nb_cas=nb_cas+1;
- } //end for
- return nb_cas;
-}
-
-// [MouseJstr]
-// is this guild allied with this castle?
-int guild_isallied(struct guild *g, struct guild_castle *gc)
-{
- int i;
-
- nullpo_retr(0, g);
-
- if(g->guild_id == gc->guild_id)
- return 1;
-
- if (gc->guild_id == 0)
- return 0;
-
-
- for(i=0;i<MAX_GUILDALLIANCE;i++)
- if(g->alliance[i].guild_id == gc->guild_id) {
- if(g->alliance[i].opposition == 0)
- return 1;
- else
- return 0;
- }
-
- return 0;
-}
-
-int guild_idisallied(int guild_id, int guild_id2)
-{
- int i;
- struct guild *g;
-
- if (guild_id <= 0 || guild_id2 <= 0)
- return 0;
-
- if(guild_id == guild_id2)
- return 1;
-
- g = guild_search(guild_id);
-
- nullpo_retr(0, g);
-
-
- for(i=0;i<MAX_GUILDALLIANCE;i++)
- if(g->alliance[i].guild_id == guild_id2) {
- if(g->alliance[i].opposition == 0)
- return 1;
- else
- return 0;
- }
-
- return 0;
-}
-
-static int guild_infoevent_db_final(DBKey key,void *data,va_list ap)
-{
- aFree(data);
- return 0;
-}
-
-static int guild_expcache_db_final(DBKey key,void *data,va_list ap)
-{
- ers_free(expcache_ers, data);
- return 0;
-}
-
-void do_final_guild(void)
-{
- guild_db->destroy(guild_db,NULL);
- castle_db->destroy(castle_db,NULL);
- guild_expcache_db->destroy(guild_expcache_db,guild_expcache_db_final);
- guild_infoevent_db->destroy(guild_infoevent_db,guild_infoevent_db_final);
- guild_castleinfoevent_db->destroy(guild_castleinfoevent_db,guild_infoevent_db_final);
- ers_destroy(expcache_ers);
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "../common/ers.h"
+
+#include "map.h"
+#include "guild.h"
+#include "storage.h"
+#include "battle.h"
+#include "npc.h"
+#include "pc.h"
+#include "status.h"
+#include "mob.h"
+#include "intif.h"
+#include "clif.h"
+#include "skill.h"
+#include "log.h"
+
+static struct guild* guild_cache; //For fast retrieval of the same guild over and over. [Skotlex]
+static struct dbt *guild_db;
+static struct dbt *castle_db;
+static struct dbt *guild_expcache_db;
+static struct dbt *guild_infoevent_db;
+static struct dbt *guild_castleinfoevent_db;
+
+struct eventlist {
+ char name[50];
+ struct eventlist *next;
+};
+
+// ギルドのEXPキャッシュのフラッシュに関連する定数
+#define GUILD_SEND_XY_INVERVAL 5000 // 座標やHP送信の間隔
+#define GUILD_PAYEXP_INVERVAL 10000 // 間隔(キャッシュの最大生存時間、ミリ秒)
+#define GUILD_PAYEXP_LIST 8192 // キャッシュの最大数
+
+// ギルドのEXPキャッシュ
+struct guild_expcache {
+ int guild_id, account_id, char_id;
+ unsigned int exp;
+};
+static struct eri *expcache_ers; //For handling of guild exp payment.
+
+struct{
+ int id;
+ int max;
+ struct{
+ short id;
+ short lv;
+ }need[6];
+} guild_skill_tree[MAX_GUILDSKILL];
+
+// timer for auto saving guild data during WoE
+#define GUILD_SAVE_INTERVAL 300000
+int guild_save_timer = -1;
+
+int guild_payexp_timer(int tid,unsigned int tick,int id,int data);
+int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data);
+int guild_save_sub(int tid,unsigned int tick,int id,int data);
+static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data);
+
+ // Modified [Komurka]
+int guild_skill_get_max (int id)
+{
+ if (id < GD_SKILLBASE || id > GD_SKILLBASE+MAX_GUILDSKILL)
+ return 0;
+ return guild_skill_tree[id-GD_SKILLBASE].max;
+}
+
+// ギルドスキルがあるか確認
+int guild_checkskill(struct guild *g,int id)
+{
+ int idx = id-GD_SKILLBASE;
+ if (idx < 0 || idx >= MAX_GUILDSKILL)
+ return 0;
+ return g->skill[idx].lv;
+}
+
+/*==========================================
+ * guild_skill_tree.txt reading - from jA [Komurka]
+ *------------------------------------------
+ */
+int guild_read_guildskill_tree_db(void)
+{
+ int i,k,id=0,ln=0;
+ FILE *fp;
+ char line[1024],*p;
+
+ malloc_set(guild_skill_tree,0,sizeof(guild_skill_tree));
+ sprintf(line, "%s/guild_skill_tree.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ char *split[50];
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ for(i=0,p=line;i<12 && p;i++){
+ split[i]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(i<12)
+ continue;
+ id = atoi(split[0]) - GD_SKILLBASE;
+ if(id<0 || id>=MAX_GUILDSKILL)
+ continue;
+ guild_skill_tree[id].id=atoi(split[0]);
+ guild_skill_tree[id].max=atoi(split[1]);
+ if (guild_skill_tree[id].id==GD_GLORYGUILD && battle_config.require_glory_guild && guild_skill_tree[id].max==0) guild_skill_tree[id].max=1;
+ for(k=0;k<5;k++){
+ guild_skill_tree[id].need[k].id=atoi(split[k*2+2]);
+ guild_skill_tree[id].need[k].lv=atoi(split[k*2+3]);
+ }
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"guild_skill_tree.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * Guild skill check - from jA [Komurka]
+ *------------------------------------------
+ */
+int guild_check_skill_require(struct guild *g,int id)
+{
+ int i;
+ int idx = id-GD_SKILLBASE;
+
+ if(g == NULL)
+ return 0;
+
+ if (idx < 0 || idx >= MAX_GUILDSKILL)
+ return 0;
+
+ for(i=0;i<5;i++)
+ {
+ if(guild_skill_tree[idx].need[i].id == 0) break;
+ if(guild_skill_tree[idx].need[i].lv > guild_checkskill(g,guild_skill_tree[idx].need[i].id))
+ return 0;
+ }
+ return 1;
+}
+
+static int guild_read_castledb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int j,ln=0;
+ char *str[32],*p;
+ struct guild_castle *gc;
+
+ sprintf(line, "%s/castle_db.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ malloc_tsetdword(str,0,sizeof(str));
+ for(j=0,p=line;j<6 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if (j < 4) //Insufficient data for castle. [Skotlex]
+ {
+ ShowError("castle_db.txt: invalid line '%s'\n", line);
+ continue;
+ }
+
+ gc=(struct guild_castle *)aCalloc(1,sizeof(struct guild_castle));
+ gc->castle_id=atoi(str[0]);
+ memcpy(gc->map_name,str[1],MAP_NAME_LENGTH-1);
+ memcpy(gc->castle_name,str[2],NAME_LENGTH-1);
+ memcpy(gc->castle_event,str[3],NAME_LENGTH-1);
+
+ idb_put(castle_db,gc->castle_id,gc);
+
+ //intif_guild_castle_info(gc->castle_id);
+
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"castle_db.txt");
+ return 0;
+}
+
+// 初期化
+void do_init_guild(void)
+{
+ guild_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ castle_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_expcache_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ guild_infoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ expcache_ers = ers_new((uint32)sizeof(struct guild_expcache));
+ guild_castleinfoevent_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+
+ guild_read_castledb();
+
+ guild_read_guildskill_tree_db(); //guild skill tree [Komurka]
+
+ add_timer_func_list(guild_gvg_eliminate_timer,"guild_gvg_eliminate_timer");
+ add_timer_func_list(guild_payexp_timer,"guild_payexp_timer");
+ add_timer_func_list(guild_save_sub, "guild_save_sub");
+ add_timer_func_list(guild_send_xy_timer, "guild_send_xy_timer");
+ add_timer_interval(gettick()+GUILD_PAYEXP_INVERVAL,guild_payexp_timer,0,0,GUILD_PAYEXP_INVERVAL);
+ add_timer_interval(gettick()+GUILD_SEND_XY_INVERVAL,guild_send_xy_timer,0,0,GUILD_SEND_XY_INVERVAL);
+}
+
+
+// 検索
+struct guild *guild_search(int guild_id)
+{
+ if(guild_cache && guild_cache->guild_id == guild_id)
+ return guild_cache;
+ guild_cache = idb_get(guild_db,guild_id);
+ return guild_cache;
+}
+int guild_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct guild **);
+ if(strcmpi(g->name,str)==0)
+ *dst=g;
+ return 0;
+}
+// ギルド名検索
+struct guild* guild_searchname(char *str)
+{
+ struct guild *g=NULL;
+ guild_db->foreach(guild_db,guild_searchname_sub,str,&g);
+ return g;
+}
+struct guild_castle *guild_castle_search(int gcid)
+{
+ return idb_get(castle_db,gcid);
+}
+
+// mapnameに対応したアジトのgcを返す
+struct guild_castle *guild_mapname2gc(char *mapname)
+{
+ int i;
+ struct guild_castle *gc=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(strcmp(gc->map_name,mapname)==0) return gc;
+ }
+ return NULL;
+}
+
+struct guild_castle *guild_mapindex2gc(short mapname)
+{
+ int i;
+ struct guild_castle *gc=NULL;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ if(!gc) continue;
+ if(strcmp(gc->map_name,mapindex_id2name(mapname))==0) return gc;
+ }
+ return NULL;
+}
+
+
+
+// ログイン中のギルドメンバーの1人のsdを返す
+struct map_session_data *guild_getavailablesd(struct guild *g)
+{
+ int i;
+
+ nullpo_retr(NULL, g);
+
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].sd!=NULL)
+ return g->member[i].sd;
+ return NULL;
+}
+
+// ギルドメンバーのインデックスを返す
+int guild_getindex(struct guild *g,int account_id,int char_id)
+{
+ int i;
+ if(g==NULL)
+ return -1;
+ for(i=0;i<g->max_member;i++)
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id )
+ return i;
+ return -1;
+}
+// ギルドメンバーの役職を返す
+int guild_getposition(struct map_session_data *sd,struct guild *g)
+{
+ int i;
+
+ nullpo_retr(-1, sd);
+
+ if(g==NULL && (g=guild_search(sd->status.guild_id))==NULL)
+ return -1;
+ for(i=0;i<g->max_member;i++)
+ if( g->member[i].account_id==sd->status.account_id &&
+ g->member[i].char_id==sd->status.char_id )
+ return g->member[i].position;
+ return -1;
+}
+
+// メンバー情報の作成
+void guild_makemember(struct guild_member *m,struct map_session_data *sd)
+{
+ nullpo_retv(sd);
+
+ malloc_set(m,0,sizeof(struct guild_member));
+ m->account_id =sd->status.account_id;
+ m->char_id =sd->status.char_id;
+ m->hair =sd->status.hair;
+ m->hair_color =sd->status.hair_color;
+ m->gender =sd->status.sex;
+ m->class_ =sd->status.class_;
+ m->lv =sd->status.base_level;
+// m->exp =0;
+// m->exp_payper =0;
+ m->online =1;
+ m->position =MAX_GUILDPOSITION-1;
+ memcpy(m->name,sd->status.name,NAME_LENGTH-1);
+ return;
+}
+// ギルド競合確認
+int guild_check_conflict(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ intif_guild_checkconflict(sd->status.guild_id,
+ sd->status.account_id,sd->status.char_id);
+ return 0;
+}
+
+// ギルドのEXPキャッシュをinter鯖にフラッシュする
+int guild_payexp_timer_sub(DBKey dataid, void *data, va_list ap)
+{
+ int i;
+ struct guild_expcache *c;
+ struct guild *g;
+
+ c = (struct guild_expcache *)data;
+
+ if (
+ (g = guild_search(c->guild_id)) == NULL ||
+ (i = guild_getindex(g, c->account_id, c->char_id)) < 0
+ ) {
+ ers_free(expcache_ers, data);
+ return 0;
+ }
+
+ if (g->member[i].exp > UINT_MAX - c->exp)
+ g->member[i].exp = UINT_MAX;
+ else
+ g->member[i].exp+= c->exp;
+
+ intif_guild_change_memberinfo(g->guild_id,c->account_id,c->char_id,
+ GMI_EXP,&g->member[i].exp,sizeof(g->member[i].exp));
+ c->exp=0;
+
+ ers_free(expcache_ers, data);
+ return 0;
+}
+
+int guild_payexp_timer(int tid, unsigned int tick, int id, int data)
+{
+ guild_expcache_db->clear(guild_expcache_db,guild_payexp_timer_sub);
+ return 0;
+}
+
+//Taken from party_send_xy_timer_sub. [Skotlex]
+int guild_send_xy_timer_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data;
+ int i;
+
+ nullpo_retr(0, g);
+
+ for(i=0;i<g->max_member;i++){
+ struct map_session_data *sd;
+ if((sd=g->member[i].sd)!=NULL){
+ if(sd->guild_x!=sd->bl.x || sd->guild_y!=sd->bl.y){
+ clif_guild_xy(sd);
+ sd->guild_x=sd->bl.x;
+ sd->guild_y=sd->bl.y;
+ }
+ }
+ }
+ return 0;
+}
+
+//Code from party_send_xy_timer [Skotlex]
+static int guild_send_xy_timer(int tid,unsigned int tick,int id,int data)
+{
+ guild_db->foreach(guild_db,guild_send_xy_timer_sub,tick);
+ return 0;
+}
+
+int guild_send_dot_remove(struct map_session_data *sd)
+{
+ if (sd->status.guild_id)
+ clif_guild_xy_remove(sd);
+ return 0;
+}
+//------------------------------------------------------------------------
+
+// 作成要求
+int guild_create(struct map_session_data *sd,char *name)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id)
+ {
+ clif_guild_created(sd,1); // すでに所属している
+ return 0;
+ }
+ if(!battle_config.guild_emperium_check || pc_search_inventory(sd,714) >= 0) {
+ struct guild_member m;
+ guild_makemember(&m,sd);
+ m.position=0;
+ intif_guild_create(name,&m);
+ return 1;
+ }
+ clif_guild_created(sd,3); // エンペリウムがいない
+ return 0;
+}
+
+// 作成可否
+int guild_created(int account_id,int guild_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+
+ if(sd==NULL)
+ return 0;
+ if(!guild_id) {
+ clif_guild_created(sd,2); // 作成失敗(同名ギルド存在)
+ return 0;
+ }
+ //struct guild *g;
+ sd->status.guild_id=guild_id;
+ sd->state.guild_sent=0;
+ clif_guild_created(sd,0);
+ if(battle_config.guild_emperium_check)
+ pc_delitem(sd,pc_search_inventory(sd,714),1,0); // エンペリウム消耗
+ return 0;
+}
+
+// 情報要求
+int guild_request_info(int guild_id)
+{
+// if(battle_config.etc_log)
+// printf("guild_request_info\n");
+ return intif_guild_request_info(guild_id);
+}
+// イベント付き情報要求
+int guild_npc_request_info(int guild_id,const char *event)
+{
+ struct eventlist *ev;
+
+ if( guild_search(guild_id) ){
+ if(event && *event)
+ npc_event_do(event);
+ return 0;
+ }
+
+ if(event==NULL || *event==0)
+ return guild_request_info(guild_id);
+
+ ev=(struct eventlist *)aCalloc(sizeof(struct eventlist),1);
+ memcpy(ev->name,event,strlen(event));
+ //The one in the db becomes the next event from this.
+ ev->next=idb_put(guild_infoevent_db,guild_id,ev);
+ return guild_request_info(guild_id);
+}
+
+// 所属キャラの確認
+int guild_check_member(struct guild *g)
+{
+ int i, j, users;
+ struct map_session_data *sd, **all_sd;
+
+ nullpo_retr(0, g);
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ sd=all_sd[i];
+ if(sd->status.guild_id==g->guild_id){
+ j=guild_getindex(g,sd->status.account_id,sd->status.char_id);
+ if (j < 0) {
+ sd->status.guild_id=0;
+ sd->state.guild_sent=0;
+ sd->guild_emblem_id=0;
+ if(battle_config.error_log)
+ ShowWarning("guild: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
+ }
+ }
+ }
+ return 0;
+}
+// 情報所得失敗(そのIDのキャラを全部未所属にする)
+int guild_recv_noinfo(int guild_id)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd=all_sd[i])){
+ if(sd->status.guild_id==guild_id)
+ sd->status.guild_id=0;
+ }
+ }
+ return 0;
+}
+// 情報所得
+int guild_recv_info(struct guild *sg)
+{
+ struct guild *g,before;
+ int i,bm,m;
+ struct eventlist *ev,*ev2;
+
+ nullpo_retr(0, sg);
+
+ if((g=idb_get(guild_db,sg->guild_id))==NULL){
+ struct map_session_data *sd;
+ g=(struct guild *)aCalloc(1,sizeof(struct guild));
+ idb_put(guild_db,sg->guild_id,g);
+ before=*sg;
+
+ // 最初のロードなのでユーザーのチェックを行う
+ guild_check_member(sg);
+ //If the guild master is online the first time the guild_info is received, that means he was the first to join,
+ //and as such, his guild skills should be blocked to avoid login/logout abuse [Skotlex]
+ if ((sd = map_nick2sd(sg->master)) != NULL)
+ {
+ guild_block_skill(sd, 300000);
+ //Also set the guild master flag.
+ sd->state.gmaster_flag = g;
+ clif_charnameupdate(sd); // [LuzZza]
+ }
+ }else
+ before=*g;
+ memcpy(g,sg,sizeof(struct guild));
+
+ if(g->max_member > MAX_GUILD)
+ {
+ if (battle_config.error_log)
+ ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD);
+ g->max_member = MAX_GUILD;
+ }
+
+ for(i=bm=m=0;i<g->max_member;i++){ // sdの設定と人数の確認
+ if(g->member[i].account_id>0){
+ struct map_session_data *sd = map_id2sd(g->member[i].account_id);
+ if (sd && sd->status.char_id == g->member[i].char_id &&
+ sd->status.guild_id == g->guild_id &&
+ !sd->state.waitingdisconnect) {
+ g->member[i].sd = sd;
+ clif_charnameupdate(sd); // [LuzZza]
+ } else g->member[i].sd = NULL;
+ m++;
+ }else
+ g->member[i].sd=NULL;
+ if(before.member[i].account_id>0)
+ bm++;
+ }
+
+ for(i=0;i<g->max_member;i++){ // 情報の送信
+ struct map_session_data *sd = g->member[i].sd;
+ if( sd==NULL )
+ continue;
+
+ if( before.guild_lv!=g->guild_lv || bm!=m ||
+ before.max_member!=g->max_member ){
+ clif_guild_basicinfo(sd); // 基本情報送信
+ clif_guild_emblem(sd,g); // エンブレム送信
+ }
+
+ if(bm!=m){ // メンバー情報送信
+ clif_guild_memberlist(g->member[i].sd);
+ }
+
+ if( before.skill_point!=g->skill_point)
+ clif_guild_skillinfo(sd); // スキル情報送信
+
+ if( sd->state.guild_sent==0){ // 未送信なら所属情報も送る
+ clif_guild_belonginfo(sd,g);
+ clif_guild_notice(sd,g);
+ sd->guild_emblem_id=g->emblem_id;
+ sd->state.guild_sent=1;
+ }
+ }
+
+ // イベントの発生
+ if( (ev=idb_remove(guild_infoevent_db,sg->guild_id))!=NULL ){
+ while(ev){
+ npc_event_do(ev->name);
+ ev2=ev->next;
+ aFree(ev);
+ ev=ev2;
+ }
+ }
+
+ return 0;
+}
+
+
+// ギルドへの勧誘
+int guild_invite(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ g=guild_search(sd->status.guild_id);
+
+ if(tsd==NULL || g==NULL)
+ return 0;
+
+ if(!battle_config.invite_request_check) {
+ if (tsd->party_invite>0 || tsd->trade_partner) { // 相手が取引中かどうか
+ clif_guild_inviteack(sd,0);
+ return 0;
+ }
+ }
+ if(tsd->status.guild_id>0 ||
+ tsd->guild_invite>0 ||
+ map[tsd->bl.m].flag.gvg_castle)
+ { //Can't invite people inside castles. [Skotlex]
+ clif_guild_inviteack(sd,0);
+ return 0;
+ }
+
+ // 定員確認
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].account_id==0)
+ break;
+ if(i==g->max_member){
+ clif_guild_inviteack(sd,3);
+ return 0;
+ }
+
+ tsd->guild_invite=sd->status.guild_id;
+ tsd->guild_invite_account=sd->status.account_id;
+
+ clif_guild_invite(tsd,g);
+ return 0;
+}
+// ギルド勧誘への返答
+int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag)
+{
+ struct map_session_data *tsd;
+
+ nullpo_retr(0, sd);
+
+ //nullpo_retr(0, tsd= map_id2sd( sd->guild_invite_account ));
+ //I checked the code, and there's no "check" for the case where the guy
+ //that invites another to a guild quits the map-server before being replied.
+ //Hence that's a valid null pointer scenario. :) [Skotlex]
+ if ((tsd= map_id2sd( sd->guild_invite_account )) == NULL)
+ { //Do we send a "invitation failed" msg or something to the player?
+ //Or should we accept the invitation and add it to the guild anyway?
+ //afterall, guild_invite holds the guild id that the player was invited to.
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ return 0;
+ }
+
+ if(sd->guild_invite!=guild_id) // 勧誘とギルドIDが違う
+ return 0;
+
+ if(flag==1){ // 承諾
+ struct guild_member m;
+ struct guild *g;
+ int i;
+
+ // 定員確認
+ if( (g=guild_search(tsd->status.guild_id))==NULL ){
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ return 0;
+ }
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].account_id==0)
+ break;
+ if(i==g->max_member){
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ clif_guild_inviteack(tsd,3);
+ return 0;
+ }
+
+
+ //inter鯖へ追加要求
+ guild_makemember(&m,sd);
+ intif_guild_addmember( sd->guild_invite, &m );
+ return 0;
+ }else{ // 拒否
+ sd->guild_invite=0;
+ sd->guild_invite_account=0;
+ clif_guild_inviteack(tsd,1);
+ }
+ return 0;
+}
+// ギルドメンバが追加された
+int guild_member_added(int guild_id,int account_id,int char_id,int flag)
+{
+ struct map_session_data *sd= map_id2sd(account_id),*sd2;
+ struct guild *g;
+
+ if( (g=guild_search(guild_id))==NULL )
+ return 0;
+
+ if(sd==NULL || sd->guild_invite==0){
+ // キャラ側に登録できなかったため脱退要求を出す
+ if (flag == 0) {
+ if(battle_config.error_log)
+ ShowError("guild: member added error %d is not online\n",account_id);
+ intif_guild_leave(guild_id,account_id,char_id,0,"**登録失敗**");
+ }
+ return 0;
+ }
+ sd2 = map_id2sd(sd->guild_invite_account);
+ sd->guild_invite = 0;
+ sd->guild_invite_account = 0;
+
+ if(flag==1){ // 失敗
+ if( sd2!=NULL )
+ clif_guild_inviteack(sd2,3);
+ return 0;
+ }
+
+ // 成功
+ sd->state.guild_sent=0;
+ sd->status.guild_id=guild_id;
+
+ if( sd2!=NULL )
+ clif_guild_inviteack(sd2,2);
+
+ // いちおう競合確認
+ guild_check_conflict(sd);
+ //Next line commented because it do nothing, look at guild_recv_info [LuzZza]
+ //clif_charnameupdate(sd); //Update display name [Skotlex]
+ return 0;
+}
+
+// ギルド脱退要求
+int guild_leave(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ g = guild_search(sd->status.guild_id);
+
+ if(g==NULL)
+ return 0;
+
+ if( sd->status.account_id!=account_id ||
+ sd->status.char_id!=char_id || sd->status.guild_id!=guild_id ||
+ map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles.
+ return 0;
+
+ for(i=0;i<g->max_member;i++){ // 所属しているか
+ if( g->member[i].account_id==sd->status.account_id &&
+ g->member[i].char_id==sd->status.char_id ){
+ intif_guild_leave(g->guild_id,sd->status.account_id,sd->status.char_id,0,mes);
+ return 0;
+ }
+ }
+ return 0;
+}
+// ギルド追放要求
+int guild_expulsion(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes)
+{
+ struct guild *g;
+ int i,ps;
+
+ nullpo_retr(0, sd);
+
+ g = guild_search(sd->status.guild_id);
+
+ if(g==NULL)
+ return 0;
+
+ if(sd->status.guild_id!=guild_id || map[sd->bl.m].flag.gvg_castle) //Can't leave inside guild castles.
+ return 0;
+
+ if( (ps=guild_getposition(sd,g))<0 || !(g->position[ps].mode&0x0010) )
+ return 0; // 処罰権限無し
+
+ for(i=0;i<g->max_member;i++){ // 所属しているか
+ if( g->member[i].account_id==account_id &&
+ g->member[i].char_id==char_id ){
+ intif_guild_leave(g->guild_id,account_id,char_id,1,mes);
+ //It's wrong way, member info will erased later
+ //see guild_member_leaved [LuzZza]
+ //malloc_set(&g->member[i],0,sizeof(struct guild_member));
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
+ const char *name,const char *mes) // rewrote [LuzZza]
+{
+ int i;
+ struct guild *g = guild_search(guild_id);
+ struct map_session_data *sd = map_charid2sd(char_id);
+ struct map_session_data *online_member_sd;
+
+ if(g == NULL)
+ return 0;
+
+ for(i=0;i<g->max_member;i++) {
+ if( g->member[i].account_id == account_id &&
+ g->member[i].char_id == char_id ){
+
+ if((online_member_sd = guild_getavailablesd(g)) == NULL)
+ return 0;
+
+ if(!flag)
+ clif_guild_leave(online_member_sd, name, mes);
+ else
+ clif_guild_expulsion(online_member_sd, name, mes, account_id);
+
+ malloc_set(&g->member[i],0,sizeof(struct guild_member));
+ clif_guild_memberlist(online_member_sd);
+
+ if(sd != NULL && sd->status.guild_id == guild_id) {
+ if (sd->state.storage_flag == 2) //Close the guild storage.
+ storage_guild_storageclose(sd);
+ sd->status.guild_id=0;
+ sd->guild_emblem_id=0;
+ sd->state.guild_sent=0;
+
+ guild_send_dot_remove(sd);
+ clif_charnameupdate(sd); //Update display name [Skotlex]
+ }
+ return 0;
+ }
+
+ }
+
+ return 0;
+}
+
+int guild_send_memberinfoshort(struct map_session_data *sd,int online)
+{ // cleaned up [LuzZza]
+ struct guild *g;
+
+ nullpo_retr(0, sd);
+
+ if(!(g = guild_search(sd->status.guild_id)))
+ return 0;
+
+ //Moved to place before intif_guild_memberinfoshort because
+ //If it's not a member, needn't send it's info to intif. [LuzZza]
+ guild_check_member(g);
+
+ if(sd->status.guild_id <= 0)
+ return 0;
+
+ intif_guild_memberinfoshort(g->guild_id,
+ sd->status.account_id,sd->status.char_id,online,sd->status.base_level,sd->status.class_);
+
+ if(!online){
+ int i=guild_getindex(g,sd->status.account_id,sd->status.char_id);
+ if(i>=0)
+ g->member[i].sd=NULL;
+ return 0;
+ }
+
+ if(sd->state.guild_sent)
+ return 0;
+
+ clif_guild_belonginfo(sd,g);
+ clif_guild_notice(sd,g);
+
+ sd->state.guild_sent = 1;
+ sd->guild_emblem_id = g->emblem_id;
+
+ return 0;
+}
+
+int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_)
+{ // cleaned up [LuzZza]
+
+ int i,alv,c,idx=-1,om=0,oldonline=-1;
+ struct guild *g = guild_search(guild_id);
+
+ if(g == NULL)
+ return 0;
+
+ for(i=0,alv=0,c=0,om=0;i<g->max_member;i++){
+ struct guild_member *m=&g->member[i];
+ if(m->account_id==account_id && m->char_id==char_id ){
+ oldonline=m->online;
+ m->online=online;
+ m->lv=lv;
+ m->class_=class_;
+ idx=i;
+ }
+ if(m->account_id>0){
+ alv+=m->lv;
+ c++;
+ }
+ if(m->online)
+ om++;
+ }
+
+ if(idx == -1 || c == 0) {
+ // ギルドのメンバー外なので追放扱いする
+ struct map_session_data *sd = map_id2sd(account_id);
+ if(sd && sd->status.char_id == char_id) {
+ sd->status.guild_id=0;
+ sd->guild_emblem_id=0;
+ sd->state.guild_sent=0;
+ }
+ if(battle_config.error_log)
+ ShowWarning("guild: not found member %d,%d on %d[%s]\n", account_id,char_id,guild_id,g->name);
+ return 0;
+ }
+
+ g->average_lv=alv/c;
+ g->connect_member=om;
+
+ for(i=0;i<g->max_member;i++) {
+ struct map_session_data *sd= map_id2sd(g->member[i].account_id);
+ g->member[i].sd = (sd && sd->status.char_id == g->member[i].char_id &&
+ sd->status.guild_id == g->guild_id && !sd->state.waitingdisconnect) ? sd : NULL;
+ }
+
+ if(oldonline!=online)
+ clif_guild_memberlogin_notice(g, idx, online);
+
+
+ if(!g->member[idx].sd)
+ return 0;
+
+ //Send XY dot updates. [Skotlex]
+ //Moved from guild_send_memberinfoshort [LuzZza]
+ for(i=0; i < g->max_member; i++) {
+
+ if(!g->member[i].sd || i == idx ||
+ g->member[i].sd->bl.m != g->member[idx].sd->bl.m)
+ continue;
+
+ clif_guild_xy_single(g->member[idx].sd->fd, g->member[i].sd);
+ }
+
+ return 0;
+}
+// ギルド会話送信
+int guild_send_message(struct map_session_data *sd,char *mes,int len)
+{
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id==0)
+ return 0;
+ intif_guild_message(sd->status.guild_id,sd->status.account_id,mes,len);
+ guild_recv_message(sd->status.guild_id,sd->status.account_id,mes,len);
+
+ //Chatlogging type 'G'
+ if(log_config.chat&1 //we log everything then
+ || ( log_config.chat&8 //if Guild bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("G", sd->status.guild_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
+
+ return 0;
+}
+// ギルド会話受信
+int guild_recv_message(int guild_id,int account_id,char *mes,int len)
+{
+ struct guild *g;
+ if( (g=guild_search(guild_id))==NULL)
+ return 0;
+ clif_guild_message(g,account_id,mes,len);
+ return 0;
+}
+// ギルドメンバの役職変更
+int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx)
+{
+ return intif_guild_change_memberinfo(
+ guild_id,account_id,char_id,GMI_POSITION,&idx,sizeof(idx));
+}
+// ギルドメンバの役職変更通知
+int guild_memberposition_changed(struct guild *g,int idx,int pos)
+{
+ nullpo_retr(0, g);
+
+ g->member[idx].position=pos;
+ clif_guild_memberpositionchanged(g,idx);
+
+ // Update char position in client [LuzZza]
+ if(g->member[idx].sd != NULL)
+ clif_charnameupdate(g->member[idx].sd);
+ return 0;
+}
+// ギルド役職変更
+int guild_change_position(struct map_session_data *sd,int idx,
+ int mode,int exp_mode,const char *name)
+{
+ struct guild_position p;
+
+ nullpo_retr(0, sd);
+
+ if(exp_mode>battle_config.guild_exp_limit)
+ exp_mode=battle_config.guild_exp_limit;
+ if(exp_mode<0)exp_mode=0;
+ p.mode=mode;
+ p.exp_mode=exp_mode;
+ memcpy(p.name,name,NAME_LENGTH-1);
+ p.name[NAME_LENGTH-1] = '\0'; //Security check... [Skotlex]
+ return intif_guild_position(sd->status.guild_id,idx,&p);
+}
+// ギルド役職変更通知
+int guild_position_changed(int guild_id,int idx,struct guild_position *p)
+{
+ struct guild *g=guild_search(guild_id);
+ int i;
+ if(g==NULL)
+ return 0;
+ memcpy(&g->position[idx],p,sizeof(struct guild_position));
+ clif_guild_positionchanged(g,idx);
+
+ // Update char name in client [LuzZza]
+ for(i=0;i<g->max_member;i++)
+ if(g->member[i].position == idx && g->member[i].sd != NULL)
+ clif_charnameupdate(g->member[i].sd);
+ return 0;
+}
+// ギルド告知変更
+int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2)
+{
+ nullpo_retr(0, sd);
+
+ if(guild_id!=sd->status.guild_id)
+ return 0;
+ return intif_guild_notice(guild_id,mes1,mes2);
+}
+// ギルド告知変更通知
+int guild_notice_changed(int guild_id,const char *mes1,const char *mes2)
+{
+ int i;
+ struct map_session_data *sd;
+ struct guild *g=guild_search(guild_id);
+ if(g==NULL)
+ return 0;
+
+ memcpy(g->mes1,mes1,60);
+ memcpy(g->mes2,mes2,120);
+
+ for(i=0;i<g->max_member;i++){
+ if((sd=g->member[i].sd)!=NULL)
+ clif_guild_notice(sd,g);
+ }
+ return 0;
+}
+// ギルドエンブレム変更
+int guild_change_emblem(struct map_session_data *sd,int len,const char *data)
+{
+ struct guild *g;
+ nullpo_retr(0, sd);
+
+ if (battle_config.require_glory_guild &&
+ !((g = guild_search(sd->status.guild_id)) && guild_checkskill(g, GD_GLORYGUILD)>0)) {
+ clif_skill_fail(sd,GD_GLORYGUILD,0,0);
+ return 0;
+ }
+
+ return intif_guild_emblem(sd->status.guild_id,len,data);
+}
+// ギルドエンブレム変更通知
+int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data)
+{
+ int i;
+ struct map_session_data *sd;
+ struct guild *g=guild_search(guild_id);
+ if(g==NULL)
+ return 0;
+
+ memcpy(g->emblem_data,data,len);
+ g->emblem_len=len;
+ g->emblem_id=emblem_id;
+
+ for(i=0;i<g->max_member;i++){
+ if((sd=g->member[i].sd)!=NULL){
+ sd->guild_emblem_id=emblem_id;
+ clif_guild_belonginfo(sd,g);
+ clif_guild_emblem(sd,g);
+ }
+ }
+ return 0;
+}
+
+static void* create_expcache(DBKey key, va_list args) {
+ struct guild_expcache *c;
+ struct map_session_data *sd = va_arg(args, struct map_session_data*);
+
+ c = ers_alloc(expcache_ers, struct guild_expcache);
+ c->guild_id = sd->status.guild_id;
+ c->account_id = sd->status.account_id;
+ c->char_id = sd->status.char_id;
+ c->exp = 0;
+ return c;
+}
+
+// ギルドのEXP上納
+unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp)
+{
+ struct guild *g;
+ struct guild_expcache *c;
+ int per;
+
+ nullpo_retr(0, sd);
+
+ if (!exp) return 0;
+
+ if (sd->status.guild_id == 0 ||
+ (g = guild_search(sd->status.guild_id)) == NULL ||
+ (per = guild_getposition(sd,g)) < 0 ||
+ (per = g->position[per].exp_mode) < 1)
+ return 0;
+
+
+ if (per < 100)
+ exp = (unsigned int) exp * per / 100;
+ //Otherwise tax everything.
+
+ c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
+
+ if (c->exp > UINT_MAX - exp)
+ c->exp = UINT_MAX;
+ else
+ c->exp += exp;
+
+ return exp;
+}
+
+// Celest
+int guild_getexp(struct map_session_data *sd,int exp)
+{
+ struct guild *g;
+ struct guild_expcache *c;
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id == 0 || (g = guild_search(sd->status.guild_id)) == NULL)
+ return 0;
+
+ c = guild_expcache_db->ensure(guild_expcache_db, i2key(sd->status.char_id), create_expcache, sd);
+ if (c->exp > UINT_MAX - exp)
+ c->exp = UINT_MAX;
+ else
+ c->exp += exp;
+ return exp;
+}
+
+// スキルポイント割り振り
+int guild_skillup(struct map_session_data *sd,int skill_num,int flag)
+{
+ struct guild *g;
+ int idx = skill_num - GD_SKILLBASE;
+
+ nullpo_retr(0, sd);
+
+ if(idx < 0 || idx >= MAX_GUILDSKILL)
+
+ return 0;
+ if(sd->status.guild_id==0 || (g=guild_search(sd->status.guild_id))==NULL)
+ return 0;
+ if(strcmp(sd->status.name,g->master))
+ return 0;
+
+ if( (g->skill_point>0 || flag&1) &&
+ g->skill[idx].id!=0 &&
+ g->skill[idx].lv < guild_skill_get_max(skill_num) ){
+ intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id,flag);
+ }
+ return 0;
+}
+// スキルポイント割り振り通知
+int guild_skillupack(int guild_id,int skill_num,int account_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+ struct guild *g=guild_search(guild_id);
+ int i;
+ if(g==NULL)
+ return 0;
+ if(sd!=NULL)
+ clif_guild_skillup(sd,skill_num,g->skill[skill_num-GD_SKILLBASE].lv);
+ // 全員に通知
+ for(i=0;i<g->max_member;i++)
+ if((sd=g->member[i].sd)!=NULL)
+ clif_guild_skillinfo(sd);
+ return 0;
+}
+
+// ギルド同盟数所得
+int guild_get_alliance_count(struct guild *g,int flag)
+{
+ int i,c;
+
+ nullpo_retr(0, g);
+
+ for(i=c=0;i<MAX_GUILDALLIANCE;i++){
+ if( g->alliance[i].guild_id>0 &&
+ g->alliance[i].opposition==flag )
+ c++;
+ }
+ return c;
+}
+
+// Blocks all guild skills which have a common delay time.
+void guild_block_skill(struct map_session_data *sd, int time) {
+ int skill_num[] = { GD_BATTLEORDER, GD_REGENERATION, GD_RESTORE, GD_EMERGENCYCALL };
+ int i;
+ for (i = 0; i < 4; i++)
+ skill_blockpc_start(sd, skill_num[i], time);
+}
+
+// 同盟関係かどうかチェック
+// 同盟なら1、それ以外は0
+int guild_check_alliance(int guild_id1, int guild_id2, int flag)
+{
+ struct guild *g;
+ int i;
+
+ g = guild_search(guild_id1);
+ if (g == NULL)
+ return 0;
+
+ for (i=0; i<MAX_GUILDALLIANCE; i++)
+ if ((g->alliance[i].guild_id == guild_id2) && (g->alliance[i].opposition == flag))
+ return 1;
+
+ return 0;
+}
+// ギルド同盟要求
+int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ struct guild *g[2];
+ int i;
+
+ if(agit_flag) { // Disable alliance creation during woe [Valaris]
+ clif_displaymessage(sd->fd,"Alliances cannot be made during Guild Wars!");
+ return 0;
+ } // end addition [Valaris]
+
+
+ nullpo_retr(0, sd);
+
+ if(tsd==NULL || tsd->status.guild_id<=0)
+ return 0;
+
+ g[0]=guild_search(sd->status.guild_id);
+ g[1]=guild_search(tsd->status.guild_id);
+
+ if(g[0]==NULL || g[1]==NULL)
+ return 0;
+
+ // Prevent creation alliance with same guilds [LuzZza]
+ if(sd->status.guild_id == tsd->status.guild_id)
+ return 0;
+
+ if( guild_get_alliance_count(g[0],0)>=3 ) {
+ clif_guild_allianceack(sd,4);
+ return 0;
+ }
+ if( guild_get_alliance_count(g[1],0)>=3 ) {
+ clif_guild_allianceack(sd,3);
+ return 0;
+ }
+
+ if( tsd->guild_alliance>0 ){
+ clif_guild_allianceack(sd,1);
+ return 0;
+ }
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに同盟状態か確認
+ if( g[0]->alliance[i].guild_id==tsd->status.guild_id &&
+ g[0]->alliance[i].opposition==0){
+ clif_guild_allianceack(sd,0);
+ return 0;
+ }
+ }
+
+ tsd->guild_alliance=sd->status.guild_id;
+ tsd->guild_alliance_account=sd->status.account_id;
+
+ clif_guild_reqalliance(tsd,sd->status.account_id,g[0]->name);
+ return 0;
+}
+// ギルド勧誘への返答
+int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag)
+{
+ struct map_session_data *tsd;
+
+ nullpo_retr(0, sd);
+ tsd= map_id2sd( account_id );
+ if (!tsd) { //Character left? Cancel alliance.
+ clif_guild_allianceack(sd,3);
+ return 0;
+ }
+
+ if(sd->guild_alliance!=tsd->status.guild_id) // 勧誘とギルドIDが違う
+ return 0;
+
+ if(flag==1){ // 承諾
+ int i;
+
+ struct guild *g,*tg; // 同盟数再確認
+ g=guild_search(sd->status.guild_id);
+ tg=guild_search(tsd->status.guild_id);
+
+ if(g==NULL || guild_get_alliance_count(g,0)>=3){
+ clif_guild_allianceack(sd,4);
+ clif_guild_allianceack(tsd,3);
+ return 0;
+ }
+ if(tg==NULL || guild_get_alliance_count(tg,0)>=3){
+ clif_guild_allianceack(sd,3);
+ clif_guild_allianceack(tsd,4);
+ return 0;
+ }
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){
+ if(g->alliance[i].guild_id==tsd->status.guild_id &&
+ g->alliance[i].opposition==1)
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,9 );
+ }
+ for(i=0;i<MAX_GUILDALLIANCE;i++){
+ if(tg->alliance[i].guild_id==sd->status.guild_id &&
+ tg->alliance[i].opposition==1)
+ intif_guild_alliance( tsd->status.guild_id,sd->status.guild_id,
+ tsd->status.account_id,sd->status.account_id,9 );
+ }
+
+ // inter鯖へ同盟要請
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,0 );
+ return 0;
+ }else{ // 拒否
+ sd->guild_alliance=0;
+ sd->guild_alliance_account=0;
+ if(tsd!=NULL)
+ clif_guild_allianceack(tsd,3);
+ }
+ return 0;
+}
+// ギルド関係解消
+int guild_delalliance(struct map_session_data *sd,int guild_id,int flag)
+{
+ nullpo_retr(0, sd);
+
+ if(agit_flag) { // Disable alliance breaking during woe [Valaris]
+ clif_displaymessage(sd->fd,"Alliances cannot be broken during Guild Wars!");
+ return 0;
+ } // end addition [Valaris]
+
+ intif_guild_alliance( sd->status.guild_id,guild_id,
+ sd->status.account_id,0,flag|8 );
+ return 0;
+}
+// ギルド敵対
+int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ g=guild_search(sd->status.guild_id);
+ if(g==NULL || tsd==NULL)
+ return 0;
+
+ // Prevent creation opposition with same guilds [LuzZza]
+ if(sd->status.guild_id == tsd->status.guild_id)
+ return 0;
+
+ if( guild_get_alliance_count(g,1)>=3 ) {
+ clif_guild_oppositionack(sd,1);
+ return 0;
+ }
+
+ if(agit_flag) {
+ clif_displaymessage(sd->fd,"You cannot make oppositions during Guild Wars!");
+ return 0;
+ }
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // すでに関係を持っているか確認
+ if(g->alliance[i].guild_id==tsd->status.guild_id){
+ if(g->alliance[i].opposition==1){ // すでに敵対
+ clif_guild_oppositionack(sd,2);
+ return 0;
+ }
+ //Change alliance to opposition.
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,8 );
+ }
+ }
+
+ // inter鯖に敵対要請
+ intif_guild_alliance( sd->status.guild_id,tsd->status.guild_id,
+ sd->status.account_id,tsd->status.account_id,1 );
+ return 0;
+}
+// ギルド同盟/敵対通知
+int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
+ int flag,const char *name1,const char *name2)
+{
+ struct guild *g[2];
+ int guild_id[2];
+ const char *guild_name[2];
+ struct map_session_data *sd[2];
+ int j,i;
+
+ guild_id[0] = guild_id1;
+ guild_id[1] = guild_id2;
+ guild_name[0] = name1;
+ guild_name[1] = name2;
+ sd[0] = map_id2sd(account_id1);
+ sd[1] = map_id2sd(account_id2);
+
+ g[0]=guild_search(guild_id1);
+ g[1]=guild_search(guild_id2);
+
+ if(sd[0]!=NULL && (flag&0x0f)==0){
+ sd[0]->guild_alliance=0;
+ sd[0]->guild_alliance_account=0;
+ }
+
+ if(flag&0x70){ // 失敗
+ for(i=0;i<2-(flag&1);i++)
+ if( sd[i]!=NULL )
+ clif_guild_allianceack(sd[i],((flag>>4)==i+1)?3:4);
+ return 0;
+ }
+// if(battle_config.etc_log)
+// printf("guild alliance_ack %d %d %d %d %d %s %s\n",guild_id1,guild_id2,account_id1,account_id2,flag,name1,name2);
+
+ if(!(flag&0x08)){ // 関係追加
+ for(i=0;i<2-(flag&1);i++)
+ if(g[i]!=NULL)
+ for(j=0;j<MAX_GUILDALLIANCE;j++)
+ if(g[i]->alliance[j].guild_id==0){
+ g[i]->alliance[j].guild_id=guild_id[1-i];
+ memcpy(g[i]->alliance[j].name,guild_name[1-i],NAME_LENGTH-1);
+ g[i]->alliance[j].opposition=flag&1;
+ break;
+ }
+ }else{ // 関係解消
+ for(i=0;i<2-(flag&1);i++){
+ if(g[i]!=NULL)
+ for(j=0;j<MAX_GUILDALLIANCE;j++)
+ if( g[i]->alliance[j].guild_id==guild_id[1-i] &&
+ g[i]->alliance[j].opposition==(flag&1)){
+ g[i]->alliance[j].guild_id=0;
+ break;
+ }
+ if( sd[i]!=NULL ) // 解消通知
+ clif_guild_delalliance(sd[i],guild_id[1-i],(flag&1));
+ }
+ }
+
+ if((flag&0x0f)==0){ // 同盟通知
+ if( sd[1]!=NULL )
+ clif_guild_allianceack(sd[1],2);
+ }else if((flag&0x0f)==1){ // 敵対通知
+ if( sd[0]!=NULL )
+ clif_guild_oppositionack(sd[0],0);
+ }
+
+
+ for(i=0;i<2-(flag&1);i++){ // 同盟/敵対リストの再送信
+ struct map_session_data *sd;
+ if(g[i]!=NULL)
+ for(j=0;j<g[i]->max_member;j++)
+ if((sd=g[i]->member[j].sd)!=NULL)
+ clif_guild_allianceinfo(sd);
+ }
+ return 0;
+}
+// ギルド解散通知用
+int guild_broken_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild *g=(struct guild *)data;
+ int guild_id=va_arg(ap,int);
+ int i,j;
+ struct map_session_data *sd=NULL;
+
+ nullpo_retr(0, g);
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++){ // 関係を破棄
+ if(g->alliance[i].guild_id==guild_id){
+ for(j=0;j<g->max_member;j++)
+ if( (sd=g->member[j].sd)!=NULL )
+ clif_guild_delalliance(sd,guild_id,g->alliance[i].opposition);
+ intif_guild_alliance(g->guild_id, guild_id,0,0,g->alliance[i].opposition|8);
+ g->alliance[i].guild_id=0;
+ }
+ }
+ return 0;
+}
+
+//Invoked on Castles when a guild is broken. [Skotlex]
+int castle_guild_broken_sub(DBKey key,void *data,va_list ap)
+{
+ struct guild_castle *gc=(struct guild_castle *)data;
+ int guild_id=va_arg(ap,int);
+
+ nullpo_retr(0, gc);
+
+ if (gc->guild_id == guild_id)
+ { //Save the new 'owner', this should invoke guardian clean up and other such things.
+ gc->guild_id = 0;
+ guild_castledatasave(gc->castle_id, 1, 0);
+ }
+ return 0;
+}
+
+//Innvoked on /breakguild "Guild name"
+int guild_broken(int guild_id,int flag)
+{
+ struct guild *g=guild_search(guild_id);
+// struct guild_castle *gc=NULL;
+ struct map_session_data *sd;
+ int i;
+// char *name;;
+
+ if(flag!=0 || g==NULL)
+ return 0;
+
+ //we call castle_event::OnGuildBreak of all castlesof the guild
+ //you can set all castle_events in the castle_db.txt
+/* name=(char *)aCalloc(50,sizeof(char)); //24 char = event name, + space for "::OnGuildBreak"
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ if( (gc=guild_castle_search(i)) != NULL ){
+ if(gc->guild_id == guild_id){
+ memcpy(name,gc->castle_event,50);
+ npc_event_do(strcat(name,"::OnGuildBreak"));
+ }
+ }
+ }
+ free(name);
+*/
+ for(i=0;i<g->max_member;i++){ // ギルド解散を通知
+ if((sd=g->member[i].sd)!=NULL){
+ if(sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,1);
+ sd->status.guild_id=0;
+ sd->state.guild_sent=0;
+ clif_guild_broken(g->member[i].sd,0);
+ clif_charnameupdate(sd); // [LuzZza]
+ }
+ }
+
+ guild_db->foreach(guild_db,guild_broken_sub,guild_id);
+ castle_db->foreach(castle_db,castle_guild_broken_sub,guild_id);
+ if (guild_cache && guild_cache->guild_id == guild_id)
+ guild_cache = NULL;
+ guild_storage_delete(guild_id);
+ idb_remove(guild_db,guild_id);
+ return 0;
+}
+
+//Changes the Guild Master to the specified player. [Skotlex]
+int guild_gm_change(int guild_id, struct map_session_data *sd)
+{
+ struct guild *g;
+ nullpo_retr(0, sd);
+
+ if (sd->status.guild_id != guild_id)
+ return 0;
+
+ g=guild_search(guild_id);
+
+ nullpo_retr(0, g);
+
+ if (strcmp(g->master, sd->status.name) == 0) //Nothing to change.
+ return 0;
+
+ //Notify servers that master has changed.
+ intif_guild_change_gm(guild_id, sd->status.name, strlen(sd->status.name));
+ return 1;
+}
+
+//Notification from Char server that a guild's master has changed. [Skotlex]
+int guild_gm_changed(int guild_id, int pos)
+{
+ struct guild *g;
+ struct guild_member gm;
+
+ g=guild_search(guild_id);
+
+ if (!g || pos < 0 || pos > g->max_member)
+ return 0;
+
+ memcpy(&gm, &g->member[pos], sizeof (struct guild_member));
+ memcpy(&g->member[pos], &g->member[0], sizeof(struct guild_member));
+ memcpy(&g->member[0], &gm, sizeof(struct guild_member));
+
+ g->member[pos].position = g->member[0].position;
+ g->member[0].position = 0; //Position 0: guild Master.
+ strcpy(g->master, g->member[0].name);
+
+ if (g->member[pos].sd && g->member[pos].sd->fd)
+ {
+ clif_displaymessage(g->member[pos].sd->fd, "You no longer are the Guild Master.");
+ g->member[pos].sd->state.gmaster_flag = 0;
+ }
+
+ if (g->member[0].sd && g->member[0].sd->fd)
+ {
+ clif_displaymessage(g->member[0].sd->fd, "You have become the Guild Master!");
+ g->member[0].sd->state.gmaster_flag = g;
+ //Block his skills for 5 minutes to prevent abuse.
+ guild_block_skill(g->member[0].sd, 300000);
+ }
+ return 1;
+}
+
+// ギルド解散
+int guild_break(struct map_session_data *sd,char *name)
+{
+ struct guild *g;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (g=guild_search(sd->status.guild_id))==NULL )
+ return 0;
+ if(strcmp(g->name,name)!=0)
+ return 0;
+ if(strcmp(sd->status.name,g->master)!=0)
+ return 0;
+ for(i=0;i<g->max_member;i++){
+ if( g->member[i].account_id>0 && (
+ g->member[i].account_id!=sd->status.account_id ||
+ g->member[i].char_id!=sd->status.char_id ))
+ break;
+ }
+ if(i<g->max_member){
+ clif_guild_broken(sd,2);
+ return 0;
+ }
+
+ intif_guild_break(g->guild_id);
+ return 0;
+}
+
+// ギルド城データ要求
+int guild_castledataload(int castle_id,int index)
+{
+ return intif_guild_castle_dataload(castle_id,index);
+}
+// ギルド城情報所得時イベント追加
+int guild_addcastleinfoevent(int castle_id,int index,const char *name)
+{
+ struct eventlist *ev;
+ int code=castle_id|(index<<16);
+
+ if( name==NULL || *name==0 )
+ return 0;
+
+ ev=(struct eventlist *)aMalloc(sizeof(struct eventlist));
+ memcpy(ev->name,name,sizeof(ev->name));
+ //The next event becomes whatever was currently stored.
+ ev->next= idb_put(guild_castleinfoevent_db,code,ev);
+ return 0;
+}
+
+// ギルド城データ要求返信
+int guild_castledataloadack(int castle_id,int index,int value)
+{
+ struct guild_castle *gc=guild_castle_search(castle_id);
+ int code=castle_id|(index<<16);
+ struct eventlist *ev,*ev2;
+
+ if(gc==NULL){
+ return 0;
+ }
+ switch(index){
+ case 1:
+ gc->guild_id = value;
+ if (value && guild_search(value)==NULL) //Request guild data which will be required for spawned guardians. [Skotlex]
+ guild_request_info(value);
+ break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default:
+ ShowError("guild_castledataloadack ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+ if( (ev=idb_remove(guild_castleinfoevent_db,code))!=NULL ){
+ while(ev){
+ npc_event_do(ev->name);
+ ev2=ev->next;
+ aFree(ev);
+ ev=ev2;
+ }
+ }
+ return 1;
+}
+// ギルド城データ変更要求
+int guild_castledatasave(int castle_id,int index,int value)
+{
+ if (index == 1)
+ { //The castle's owner has changed? Update Guardian ownership, too. [Skotlex]
+ struct guild_castle *gc = guild_castle_search(castle_id);
+ int m = -1;
+ if (gc) m = map_mapname2mapid(gc->map_name);
+ if (m != -1)
+ map_foreachinmap(mob_guardian_guildchange, m, BL_MOB);
+ }
+ return intif_guild_castle_datasave(castle_id,index,value);
+}
+
+// ギルド城データ変更通知
+int guild_castledatasaveack(int castle_id,int index,int value)
+{
+ struct guild_castle *gc=guild_castle_search(castle_id);
+ if(gc==NULL){
+ return 0;
+ }
+ switch(index){
+ case 1: gc->guild_id = value; break;
+ case 2: gc->economy = value; break;
+ case 3: gc->defense = value; break;
+ case 4: gc->triggerE = value; break;
+ case 5: gc->triggerD = value; break;
+ case 6: gc->nextTime = value; break;
+ case 7: gc->payTime = value; break;
+ case 8: gc->createTime = value; break;
+ case 9: gc->visibleC = value; break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ gc->guardian[index-10].visible = value; break;
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ gc->guardian[index-18].hp = value; break;
+ default:
+ ShowError("guild_castledatasaveack ERROR!! (Not found index=%d)\n", index);
+ return 0;
+ }
+ return 1;
+}
+
+// ギルドデータ一括受信(初期化時)
+int guild_castlealldataload(int len,struct guild_castle *gc)
+{
+ int i;
+ int n = (len-4) / sizeof(struct guild_castle), ev = -1;
+
+ nullpo_retr(0, gc);
+
+ //Last owned castle in the list invokes ::OnAgitinit
+ for(i = 0; i < n; i++) {
+ if ((gc + i)->guild_id)
+ ev = i;
+ }
+
+ // 城データ格納とギルド情報要求
+ for(i = 0; i < n; i++, gc++) {
+ struct guild_castle *c = guild_castle_search(gc->castle_id);
+ if (!c) {
+ ShowError("guild_castlealldataload Castle id=%d not found.\n", gc->castle_id);
+ continue;
+ }
+ memcpy(&c->guild_id,&gc->guild_id,
+ sizeof(struct guild_castle) - ((int)&c->guild_id - (int)c) );
+ if( c->guild_id ){
+ if(i!=ev)
+ guild_request_info(c->guild_id);
+ else
+ guild_npc_request_info(c->guild_id, "::OnAgitInit");
+ }
+ }
+ if (ev == -1) //No castles owned, invoke OnAgitInit as it is.
+ npc_event_doall("OnAgitInit");
+ return 0;
+}
+
+int guild_agit_start(void)
+{ // Run All NPC_Event[OnAgitStart]
+ int c = npc_event_doall("OnAgitStart");
+ ShowStatus("NPC_Event:[OnAgitStart] Run (%d) Events by @AgitStart.\n",c);
+ // Start auto saving
+ guild_save_timer = add_timer_interval (gettick() + GUILD_SAVE_INTERVAL, guild_save_sub, 0, 0, GUILD_SAVE_INTERVAL);
+ return 0;
+}
+
+int guild_agit_end(void)
+{ // Run All NPC_Event[OnAgitEnd]
+ int c = npc_event_doall("OnAgitEnd");
+ ShowStatus("NPC_Event:[OnAgitEnd] Run (%d) Events by @AgitEnd.\n",c);
+ // Stop auto saving
+ delete_timer (guild_save_timer, guild_save_sub);
+ return 0;
+}
+
+int guild_gvg_eliminate_timer(int tid,unsigned int tick,int id,int data)
+{ // Run One NPC_Event[OnAgitEliminate]
+ char *name = (char*)data;
+ size_t len = (name) ? strlen(name) : 0;
+ // the rest is dangerous, but let it crash,
+ // if this happens, it's ruined anyway
+ int c=0;
+
+ if(agit_flag) // Agit not already End
+ {
+ char *evname=(char*)aMalloc( (len + 10) * sizeof(char));
+ memcpy(evname,name,len - 5);
+ strcpy(evname + len - 5,"Eliminate");
+ c = npc_event_do(evname);
+ ShowStatus("NPC_Event:[%s] Run (%d) Events.\n",evname,c);
+ aFree(evname); // [Lance] Should fix this
+ }
+ if(name) aFree(name);
+ return 0;
+}
+
+static int Ghp[MAX_GUILDCASTLE][MAX_GUARDIANS]; // so save only if HP are changed // experimental code [Yor]
+static int Gid[MAX_GUILDCASTLE];
+int guild_save_sub(int tid,unsigned int tick,int id,int data)
+{
+ struct guild_castle *gc;
+ int i,j;
+
+ for(i = 0; i < MAX_GUILDCASTLE; i++) { // [Yor]
+ gc = guild_castle_search(i);
+ if (!gc) continue;
+ if (gc->guild_id != Gid[i]) {
+ // Re-save guild id if its owner guild has changed
+ guild_castledatasave(gc->castle_id, 1, gc->guild_id);
+ Gid[i] = gc->guild_id;
+ }
+ for (j = 0; j < MAX_GUARDIANS; j++)
+ {
+ if (gc->guardian[j].visible && Ghp[i][j] != gc->guardian[j].hp)
+ guild_castledatasave(gc->castle_id, 18+j, gc->guardian[j].hp);
+ }
+ }
+
+ return 0;
+}
+
+int guild_agit_break(struct mob_data *md)
+{ // Run One NPC_Event[OnAgitBreak]
+ char *evname;
+
+ nullpo_retr(0, md);
+
+ evname=(char *)aMallocA((strlen(md->npc_event) + 1)*sizeof(char));
+
+ strcpy(evname,md->npc_event);
+// Now By User to Run [OnAgitBreak] NPC Event...
+// It's a little impossible to null point with player disconnect in this!
+// But Script will be stop, so nothing...
+// Maybe will be changed in the futher..
+// int c = npc_event_do(evname);
+ if(!agit_flag) return 0; // Agit already End
+ add_timer(gettick()+battle_config.gvg_eliminate_time,guild_gvg_eliminate_timer,md->bl.m,(int)evname);
+ return 0;
+}
+
+// [MouseJstr]
+// How many castles does this guild have?
+int guild_checkcastles(struct guild *g) {
+ int i,nb_cas=0, id,cas_id=0;
+ struct guild_castle *gc;
+ id=g->guild_id;
+ for(i=0;i<MAX_GUILDCASTLE;i++){
+ gc=guild_castle_search(i);
+ cas_id=gc->guild_id;
+ if(g->guild_id==cas_id)
+ nb_cas=nb_cas+1;
+ } //end for
+ return nb_cas;
+}
+
+// [MouseJstr]
+// is this guild allied with this castle?
+int guild_isallied(struct guild *g, struct guild_castle *gc)
+{
+ int i;
+
+ nullpo_retr(0, g);
+
+ if(g->guild_id == gc->guild_id)
+ return 1;
+
+ if (gc->guild_id == 0)
+ return 0;
+
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == gc->guild_id) {
+ if(g->alliance[i].opposition == 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+int guild_idisallied(int guild_id, int guild_id2)
+{
+ int i;
+ struct guild *g;
+
+ if (guild_id <= 0 || guild_id2 <= 0)
+ return 0;
+
+ if(guild_id == guild_id2)
+ return 1;
+
+ g = guild_search(guild_id);
+
+ nullpo_retr(0, g);
+
+
+ for(i=0;i<MAX_GUILDALLIANCE;i++)
+ if(g->alliance[i].guild_id == guild_id2) {
+ if(g->alliance[i].opposition == 0)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 0;
+}
+
+static int guild_infoevent_db_final(DBKey key,void *data,va_list ap)
+{
+ aFree(data);
+ return 0;
+}
+
+static int guild_expcache_db_final(DBKey key,void *data,va_list ap)
+{
+ ers_free(expcache_ers, data);
+ return 0;
+}
+
+void do_final_guild(void)
+{
+ guild_db->destroy(guild_db,NULL);
+ castle_db->destroy(castle_db,NULL);
+ guild_expcache_db->destroy(guild_expcache_db,guild_expcache_db_final);
+ guild_infoevent_db->destroy(guild_infoevent_db,guild_infoevent_db_final);
+ guild_castleinfoevent_db->destroy(guild_castleinfoevent_db,guild_infoevent_db_final);
+ ers_destroy(expcache_ers);
+}
diff --git a/src/map/guild.h b/src/map/guild.h
index 5a71c4525..69174e021 100644
--- a/src/map/guild.h
+++ b/src/map/guild.h
@@ -1,95 +1,95 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _GUILD_H_
-#define _GUILD_H_
-
-struct map_session_data;
-struct mob_data;
-struct guild;
-struct guild_member;
-struct guild_position;
-struct guild_castle;
-
-int guild_skill_get_max(int id);
-
-int guild_checkskill(struct guild *g,int id);
-int guild_check_skill_require(struct guild *g,int id); // [Komurka]
-int guild_checkcastles(struct guild *g); // [MouseJstr]
-int guild_isallied(struct guild *g, struct guild_castle *gc);
-int guild_idisallied(int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex]
-
-void do_init_guild(void);
-struct guild *guild_search(int guild_id);
-struct guild *guild_searchname(char *str);
-struct guild_castle *guild_castle_search(int gcid);
-
-struct guild_castle *guild_mapname2gc(char *mapname);
-struct guild_castle *guild_mapindex2gc(short mapname);
-
-struct map_session_data *guild_getavailablesd(struct guild *g);
-int guild_getindex(struct guild *g,int account_id,int char_id);
-int guild_getposition(struct map_session_data *sd,struct guild *g);
-unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp);
-int guild_getexp(struct map_session_data *sd,int exp); // [Celest]
-
-int guild_create(struct map_session_data *sd,char *name);
-int guild_created(int account_id,int guild_id);
-int guild_request_info(int guild_id);
-int guild_recv_noinfo(int guild_id);
-int guild_recv_info(struct guild *sg);
-int guild_npc_request_info(int guild_id,const char *ev);
-int guild_invite(struct map_session_data *sd,struct map_session_data *tsd);
-int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag);
-int guild_member_added(int guild_id,int account_id,int char_id,int flag);
-int guild_leave(struct map_session_data *sd,int guild_id,
- int account_id,int char_id,const char *mes);
-int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
- const char *name,const char *mes);
-int guild_expulsion(struct map_session_data *sd,int guild_id,
- int account_id,int char_id,const char *mes);
-int guild_skillup(struct map_session_data *sd,int skill_num,int flag);
-void guild_block_skill(struct map_session_data *sd, int time);
-int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd);
-int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag);
-int guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2);
-int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
- int flag,const char *name1,const char *name2);
-int guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
-int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd);
-int guild_check_alliance(int guild_id1, int guild_id2, int flag);
-
-int guild_send_memberinfoshort(struct map_session_data *sd,int online);
-int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_);
-int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx);
-int guild_memberposition_changed(struct guild *g,int idx,int pos);
-int guild_change_position(struct map_session_data *sd,int idx,
- int mode,int exp_mode,const char *name);
-int guild_position_changed(int guild_id,int idx,struct guild_position *p);
-int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2);
-int guild_notice_changed(int guild_id,const char *mes1,const char *mes2);
-int guild_change_emblem(struct map_session_data *sd,int len,const char *data);
-int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data);
-int guild_send_message(struct map_session_data *sd,char *mes,int len);
-int guild_recv_message(int guild_id,int account_id,char *mes,int len);
-int guild_send_dot_remove(struct map_session_data *sd);
-int guild_skillupack(int guild_id,int skill_num,int account_id);
-int guild_break(struct map_session_data *sd,char *name);
-int guild_broken(int guild_id,int flag);
-int guild_gm_change(int guild_id, struct map_session_data *sd);
-int guild_gm_changed(int guild_id, int pos);
-
-int guild_addcastleinfoevent(int castle_id,int index,const char *name);
-int guild_castledataload(int castle_id,int index);
-int guild_castledataloadack(int castle_id,int index,int value);
-int guild_castledatasave(int castle_id,int index,int value);
-int guild_castledatasaveack(int castle_id,int index,int value);
-int guild_castlealldataload(int len,struct guild_castle *gc);
-
-int guild_agit_start(void);
-int guild_agit_end(void);
-int guild_agit_break(struct mob_data *md);
-
-void do_final_guild(void);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _GUILD_H_
+#define _GUILD_H_
+
+struct map_session_data;
+struct mob_data;
+struct guild;
+struct guild_member;
+struct guild_position;
+struct guild_castle;
+
+int guild_skill_get_max(int id);
+
+int guild_checkskill(struct guild *g,int id);
+int guild_check_skill_require(struct guild *g,int id); // [Komurka]
+int guild_checkcastles(struct guild *g); // [MouseJstr]
+int guild_isallied(struct guild *g, struct guild_castle *gc);
+int guild_idisallied(int guild_id, int guild_id2); //Checks alliance based on guild Ids. [Skotlex]
+
+void do_init_guild(void);
+struct guild *guild_search(int guild_id);
+struct guild *guild_searchname(char *str);
+struct guild_castle *guild_castle_search(int gcid);
+
+struct guild_castle *guild_mapname2gc(char *mapname);
+struct guild_castle *guild_mapindex2gc(short mapname);
+
+struct map_session_data *guild_getavailablesd(struct guild *g);
+int guild_getindex(struct guild *g,int account_id,int char_id);
+int guild_getposition(struct map_session_data *sd,struct guild *g);
+unsigned int guild_payexp(struct map_session_data *sd,unsigned int exp);
+int guild_getexp(struct map_session_data *sd,int exp); // [Celest]
+
+int guild_create(struct map_session_data *sd,char *name);
+int guild_created(int account_id,int guild_id);
+int guild_request_info(int guild_id);
+int guild_recv_noinfo(int guild_id);
+int guild_recv_info(struct guild *sg);
+int guild_npc_request_info(int guild_id,const char *ev);
+int guild_invite(struct map_session_data *sd,struct map_session_data *tsd);
+int guild_reply_invite(struct map_session_data *sd,int guild_id,int flag);
+int guild_member_added(int guild_id,int account_id,int char_id,int flag);
+int guild_leave(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes);
+int guild_member_leaved(int guild_id,int account_id,int char_id,int flag,
+ const char *name,const char *mes);
+int guild_expulsion(struct map_session_data *sd,int guild_id,
+ int account_id,int char_id,const char *mes);
+int guild_skillup(struct map_session_data *sd,int skill_num,int flag);
+void guild_block_skill(struct map_session_data *sd, int time);
+int guild_reqalliance(struct map_session_data *sd,struct map_session_data *tsd);
+int guild_reply_reqalliance(struct map_session_data *sd,int account_id,int flag);
+int guild_alliance(int guild_id1,int guild_id2,int account_id1,int account_id2);
+int guild_allianceack(int guild_id1,int guild_id2,int account_id1,int account_id2,
+ int flag,const char *name1,const char *name2);
+int guild_delalliance(struct map_session_data *sd,int guild_id,int flag);
+int guild_opposition(struct map_session_data *sd,struct map_session_data *tsd);
+int guild_check_alliance(int guild_id1, int guild_id2, int flag);
+
+int guild_send_memberinfoshort(struct map_session_data *sd,int online);
+int guild_recv_memberinfoshort(int guild_id,int account_id,int char_id,int online,int lv,int class_);
+int guild_change_memberposition(int guild_id,int account_id,int char_id,int idx);
+int guild_memberposition_changed(struct guild *g,int idx,int pos);
+int guild_change_position(struct map_session_data *sd,int idx,
+ int mode,int exp_mode,const char *name);
+int guild_position_changed(int guild_id,int idx,struct guild_position *p);
+int guild_change_notice(struct map_session_data *sd,int guild_id,const char *mes1,const char *mes2);
+int guild_notice_changed(int guild_id,const char *mes1,const char *mes2);
+int guild_change_emblem(struct map_session_data *sd,int len,const char *data);
+int guild_emblem_changed(int len,int guild_id,int emblem_id,const char *data);
+int guild_send_message(struct map_session_data *sd,char *mes,int len);
+int guild_recv_message(int guild_id,int account_id,char *mes,int len);
+int guild_send_dot_remove(struct map_session_data *sd);
+int guild_skillupack(int guild_id,int skill_num,int account_id);
+int guild_break(struct map_session_data *sd,char *name);
+int guild_broken(int guild_id,int flag);
+int guild_gm_change(int guild_id, struct map_session_data *sd);
+int guild_gm_changed(int guild_id, int pos);
+
+int guild_addcastleinfoevent(int castle_id,int index,const char *name);
+int guild_castledataload(int castle_id,int index);
+int guild_castledataloadack(int castle_id,int index,int value);
+int guild_castledatasave(int castle_id,int index,int value);
+int guild_castledatasaveack(int castle_id,int index,int value);
+int guild_castlealldataload(int len,struct guild_castle *gc);
+
+int guild_agit_start(void);
+int guild_agit_end(void);
+int guild_agit_break(struct mob_data *md);
+
+void do_final_guild(void);
+
+#endif
diff --git a/src/map/intif.h b/src/map/intif.h
index 0351642b1..8fc005508 100644
--- a/src/map/intif.h
+++ b/src/map/intif.h
@@ -1,72 +1,72 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _INTIF_H_
-#define _INFIF_H_
-
-int intif_parse(int fd);
-
-int intif_GMmessage(char* mes,int len,int flag);
-int intif_announce(char* mes,int len, unsigned long color, int flag);
-
-int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len);
-int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes);
-
-int intif_saveregistry(struct map_session_data *sd, int type);
-int intif_request_registry(struct map_session_data *sd, int flag);
-
-int intif_request_storage(int account_id);
-int intif_send_storage(struct storage *stor);
-int intif_request_guild_storage(int account_id, int guild_id);
-int intif_send_guild_storage(int account_id, struct guild_storage *gstor);
-
-
-int intif_create_party(struct party_member *member,char *name,int item,int item2);
-int intif_request_partyinfo(int party_id);
-
-int intif_party_addmember(int party_id,struct party_member *member);
-int intif_party_changeoption(int party_id, int account_id, int exp, int item);
-int intif_party_leave(int party_id,int account_id, int char_id);
-int intif_party_changemap(struct map_session_data *sd, int online);
-int intif_break_party(int party_id);
-int intif_party_message(int party_id, int account_id, char *mes,int len);
-int intif_party_checkconflict(int party_id,int account_id,int char_id);
-int intif_party_leaderchange(int party_id,int account_id,int char_id);
-
-
-int intif_guild_create(const char *name, const struct guild_member *master);
-int intif_guild_request_info(int guild_id);
-int intif_guild_addmember(int guild_id, struct guild_member *m);
-int intif_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes);
-int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class_);
-int intif_guild_break(int guild_id);
-int intif_guild_message(int guild_id, int account_id, char *mes, int len);
-int intif_guild_checkconflict(int guild_id, int account_id, int char_id);
-int intif_guild_change_gm(int guild_id, const char* name, int len);
-int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len);
-int intif_guild_change_memberinfo(int guild_id, int account_id, int char_id, int type, const void *data, int len);
-int intif_guild_position(int guild_id, int idx, struct guild_position *p);
-int intif_guild_skillup(int guild_id, int skill_num, int account_id, int flag);
-int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag);
-int intif_guild_notice(int guild_id, const char *mes1, const char *mes2);
-int intif_guild_emblem(int guild_id, int len, const char *data);
-int intif_guild_castle_dataload(int castle_id, int index);
-int intif_guild_castle_datasave(int castle_id, int index, int value);
-
-int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id,
- short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name);
-int intif_request_petdata(int account_id, int char_id, int pet_id);
-int intif_save_petdata(int account_id, struct s_pet *p);
-int intif_delete_petdata(int pet_id);
-int intif_rename_pet(struct map_session_data *sd, char *name);
-
-
-int intif_homunculus_create(int account_id, struct s_homunculus *sh);
-int intif_homunculus_requestload(int account_id, int homun_id);
-int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh);
-int intif_homunculus_requestdelete(int homun_id);
-
-
-int CheckForCharServer(void);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _INTIF_H_
+#define _INFIF_H_
+
+int intif_parse(int fd);
+
+int intif_GMmessage(char* mes,int len,int flag);
+int intif_announce(char* mes,int len, unsigned long color, int flag);
+
+int intif_wis_message(struct map_session_data *sd,char *nick,char *mes,int mes_len);
+int intif_wis_message_to_gm(char *Wisp_name, int min_gm_level, char *mes);
+
+int intif_saveregistry(struct map_session_data *sd, int type);
+int intif_request_registry(struct map_session_data *sd, int flag);
+
+int intif_request_storage(int account_id);
+int intif_send_storage(struct storage *stor);
+int intif_request_guild_storage(int account_id, int guild_id);
+int intif_send_guild_storage(int account_id, struct guild_storage *gstor);
+
+
+int intif_create_party(struct party_member *member,char *name,int item,int item2);
+int intif_request_partyinfo(int party_id);
+
+int intif_party_addmember(int party_id,struct party_member *member);
+int intif_party_changeoption(int party_id, int account_id, int exp, int item);
+int intif_party_leave(int party_id,int account_id, int char_id);
+int intif_party_changemap(struct map_session_data *sd, int online);
+int intif_break_party(int party_id);
+int intif_party_message(int party_id, int account_id, char *mes,int len);
+int intif_party_checkconflict(int party_id,int account_id,int char_id);
+int intif_party_leaderchange(int party_id,int account_id,int char_id);
+
+
+int intif_guild_create(const char *name, const struct guild_member *master);
+int intif_guild_request_info(int guild_id);
+int intif_guild_addmember(int guild_id, struct guild_member *m);
+int intif_guild_leave(int guild_id, int account_id, int char_id, int flag, const char *mes);
+int intif_guild_memberinfoshort(int guild_id, int account_id, int char_id, int online, int lv, int class_);
+int intif_guild_break(int guild_id);
+int intif_guild_message(int guild_id, int account_id, char *mes, int len);
+int intif_guild_checkconflict(int guild_id, int account_id, int char_id);
+int intif_guild_change_gm(int guild_id, const char* name, int len);
+int intif_guild_change_basicinfo(int guild_id, int type, const void *data, int len);
+int intif_guild_change_memberinfo(int guild_id, int account_id, int char_id, int type, const void *data, int len);
+int intif_guild_position(int guild_id, int idx, struct guild_position *p);
+int intif_guild_skillup(int guild_id, int skill_num, int account_id, int flag);
+int intif_guild_alliance(int guild_id1, int guild_id2, int account_id1, int account_id2, int flag);
+int intif_guild_notice(int guild_id, const char *mes1, const char *mes2);
+int intif_guild_emblem(int guild_id, int len, const char *data);
+int intif_guild_castle_dataload(int castle_id, int index);
+int intif_guild_castle_datasave(int castle_id, int index, int value);
+
+int intif_create_pet(int account_id, int char_id, short pet_type, short pet_lv, short pet_egg_id,
+ short pet_equip, short intimate, short hungry, char rename_flag, char incuvate, char *pet_name);
+int intif_request_petdata(int account_id, int char_id, int pet_id);
+int intif_save_petdata(int account_id, struct s_pet *p);
+int intif_delete_petdata(int pet_id);
+int intif_rename_pet(struct map_session_data *sd, char *name);
+
+
+int intif_homunculus_create(int account_id, struct s_homunculus *sh);
+int intif_homunculus_requestload(int account_id, int homun_id);
+int intif_homunculus_requestsave(int account_id, struct s_homunculus* sh);
+int intif_homunculus_requestdelete(int homun_id);
+
+
+int CheckForCharServer(void);
+
+#endif
diff --git a/src/map/irc.c b/src/map/irc.c
index 4b9187107..4722e9c2e 100644
--- a/src/map/irc.c
+++ b/src/map/irc.c
@@ -1,543 +1,543 @@
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "../common/core.h"
-#include "../common/socket.h"
-#include "../common/malloc.h"
-#include "../common/db.h"
-#include "../common/timer.h"
-#include "../common/strlib.h"
-#include "../common/mmo.h"
-#include "../common/showmsg.h"
-#include "../common/version.h"
-#include "../common/nullpo.h"
-
-#include "map.h"
-#include "pc.h"
-#include "irc.h"
-#include "intif.h" //For GM Broadcast [Zido]
-
-short use_irc=0;
-
-short irc_autojoin=0;
-
-short irc_announce_flag=1;
-short irc_announce_mvp_flag=1;
-short irc_announce_jobchange_flag=1;
-short irc_announce_shop_flag=1;
-
-IRC_SI *irc_si=NULL;
-
-char irc_nick[30]="";
-char irc_password[32]="";
-
-char irc_channel[32]="";
-char irc_channel_pass[32]="";
-char irc_trade_channel[32]="";
-
-unsigned char irc_ip_str[128]="";
-unsigned long irc_ip=0;
-unsigned short irc_port = 6667;
-int irc_fd=0;
-
-struct channel_data cd;
-int last_cd_user=0;
-
-int irc_connect_timer(int tid, unsigned int tick, int id, int data)
-{
- if(irc_si && session[irc_si->fd])
- return 0;
- //Ok, this ShowInfo and printf are a little ugly, but they are meant to
- //debug just how long the code freezes here. [Skotlex]
- ShowInfo("(IRC) Connecting to %s... ", irc_ip_str);
- irc_fd = make_connection(irc_ip,irc_port);
- if(irc_fd > 0){
- printf("ok\n");
- session[irc_fd]->func_parse = irc_parse;
- } else
- printf("failed\n");
- return 0;
-}
-
-void irc_announce(char *buf)
-{
- char send_string[256];
- // malloc_tsetdword(send_string,'\0',256); // NOT REQUIRED
-
- sprintf(send_string,"PRIVMSG %s :",irc_channel);
- strcat(send_string, buf);
- irc_send(send_string);
-}
-
-void irc_announce_jobchange(struct map_session_data *sd)
-{
- char send_string[256];
-
- nullpo_retv(sd);
- malloc_tsetdword(send_string,'\0',256);
-
- sprintf(send_string,"PRIVMSG %s :%s has changed into a %s.",irc_channel,sd->status.name,job_name(sd->status.class_));
- irc_send(send_string);
-}
-
-void irc_announce_shop(struct map_session_data *sd, int flag)
-{
- char send_string[256];
- char mapname[16];
- int maplen = 0;
- nullpo_retv(sd);
-
- malloc_tsetdword(send_string,'\0',256);
- malloc_tsetdword(mapname,'\0',16);
-
- if(flag){
- strcpy(mapname, map[sd->bl.m].name);
- maplen = strcspn(mapname,".");
- mapname[maplen] = '\0';
- mapname[0]=toupper(mapname[0]);
-
- sprintf(send_string,"PRIVMSG %s :%s has opened a shop, %s, at <%d,%d> in %s.",irc_trade_channel,sd->status.name,sd->message,sd->bl.x,sd->bl.y,mapname);
- } else
- sprintf(send_string,"PRIVMSG %s :%s has closed their shop.",irc_trade_channel,sd->status.name);
-
- irc_send(send_string);
-}
-
-void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md)
-{
- char send_string[256];
- char mapname[16];
- int maplen = 0;
-
- nullpo_retv(sd);
- nullpo_retv(md);
-
- malloc_tsetdword(send_string,'\0',256);
- malloc_tsetdword(mapname,'\0',16);
- mapname[15]='\0'; // 15 is the final index, not 16 [Lance]
- strcpy(mapname, map[md->bl.m].name);
- maplen = strcspn(mapname,".");
- mapname[maplen] = '\0';
- mapname[0]=toupper(mapname[0]);
-
- sprintf(send_string,"PRIVMSG %s :%s the %s has MVP'd %s in %s.",irc_channel,sd->status.name,job_name(sd->status.class_),md->name, mapname);
- irc_send(send_string);
-}
-
-int irc_parse(int fd)
-{
- if (session[fd]->eof){
- do_close(fd);
- irc_si = NULL;
- add_timer(gettick() + 15000, irc_connect_timer, 0, 0);
- return 0;
- }
- if (session[fd]->session_data == NULL){
- irc_si = (struct IRC_Session_Info*)aMalloc(sizeof(struct IRC_Session_Info));
- irc_si->fd = fd;
- irc_si->state = 0;
- session[fd]->session_data = irc_si;
- } else if (!irc_si) {
- irc_si = (struct IRC_Session_Info*)session[fd]->session_data;
- irc_si->fd = fd;
- }
- if(RFIFOREST(fd) > 0){
- char *incoming_string=aMalloc(RFIFOREST(fd)*sizeof(char));
- RFIFOHEAD(fd);
- memcpy(incoming_string,RFIFOP(fd,0),RFIFOREST(fd));
- send_to_parser(fd,incoming_string,"\n");
- RFIFOSKIP(fd,RFIFOREST(fd));
- aFree(incoming_string);
- }
- return 0;
-}
-
-int irc_keepalive_timer(int tid, unsigned int tick, int id, int data)
-{
- char send_string[128];
- malloc_tsetdword(send_string,'\0',128);
-
- sprintf(send_string,"PRIVMSG %s : ", irc_nick);
- irc_send(send_string);
- add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0);
- return 0;
-}
-
-void irc_send_sub(int fd, char transmit[4096])
-{
- sprintf(transmit,"%s%c",transmit,10);
- WFIFOHEAD(fd,strlen(transmit));
- memcpy(WFIFOP(fd,0),transmit, strlen(transmit));
- WFIFOSET(fd,strlen(transmit));
-}
-
-void irc_send(char *buf)
-{
- char transmit[4096];
-
- if(!irc_si || !session[irc_si->fd])
- return;
-
- malloc_tsetdword(transmit,'\0',4096);
-
- sprintf(transmit,buf);
- irc_send_sub(irc_si->fd,transmit);
-}
-
-void irc_parse_sub(int fd, char *incoming_string)
-{
- char source[256];
- char command[256];
- char target[256];
- char message[8192];
- char send_string[8192];
- char *source_nick=NULL;
- char *source_ident=NULL;
- char *source_host=NULL;
- char *state_mgr=NULL;
-
- char cmd1[256];
- char cmd2[256];
- char cmdname[256];
- char cmdargs[256];
-
- int users=0;
- int i=0;
-
- struct map_session_data **allsd;
-
- malloc_tsetdword(source,'\0',256);
- malloc_tsetdword(command,'\0',256);
- malloc_tsetdword(target,'\0',256);
- malloc_tsetdword(message,'\0',8192);
- malloc_tsetdword(send_string,'\0',8192);
-
- malloc_tsetdword(cmd1,'\0',256);
- malloc_tsetdword(cmd2,'\0',256);
- malloc_tsetdword(cmdname,'\0',256);
- malloc_tsetdword(cmdargs,'\0',256);
-
- sscanf(incoming_string, ":%255s %255s %255s :%4095[^\r\n]", source, command, target, message);
- if (source != NULL) {
- if (strstr(source,"!") != NULL) {
- source_nick = strtok_r(source,"!",&state_mgr);
- source_ident = strtok_r(NULL,"@",&state_mgr);
- source_host = strtok_r(NULL,"%%",&state_mgr);
- }
- }
- if (irc_si->state == 0){
- sprintf(send_string, "NICK %s", irc_nick);
- irc_send(send_string);
- sprintf(send_string, "USER eABot 8 * : eABot");
- irc_send(send_string);
- irc_si->state = 1;
- }
- else if (irc_si->state == 1){
- if(!strcmp(command,"001")){
- ShowStatus("IRC: Connected to IRC.\n");
- sprintf(send_string, "PRIVMSG nickserv :identify %s", irc_password);
- irc_send(send_string);
- sprintf(send_string, "JOIN %s %s", irc_channel, irc_channel_pass);
- irc_send(send_string);
- sprintf(send_string,"NAMES %s",irc_channel);
- irc_send(send_string);
- irc_si->state = 2;
- }
- else if(!strcmp(command,"433")){
- ShowError("IRC: Nickname %s is already taken, IRC Client unable to connect.\n", irc_nick);
- sprintf(send_string, "QUIT");
- irc_send(send_string);
- if(session[fd])
- session[fd]->eof=1;
- }
- }
- else if (irc_si->state == 2){
- if(!strcmp(source, "PING")){
- sprintf(send_string, "PONG %s", command);
- irc_send(send_string);
- }
-
- else if((strcmpi(target,irc_channel)==0)||(strcmpi(target,irc_channel+1)==0)) {
-
- // Broadcast [Zido] (Work in Progress)
- if((strcmpi(command,"privmsg")==0)&&(sscanf(message,"@%255s %255[^\r\n]",cmdname,cmdargs)>0)&&(target[0]=='#')) {
- if(strcmpi(cmdname,"kami")==0) {
- if(get_access(source_nick)<ACCESS_OP)
- sprintf(send_string,"NOTICE %s :Access Denied",source_nick);
- else {
- sprintf(send_string,"%s: %s",source_nick,cmdargs);
- intif_GMmessage(send_string,strlen(send_string)+1,0);
- sprintf(send_string,"NOTICE %s :Message Sent",source_nick);
- }
- irc_send(send_string);
- // Number of users online [Zido]
- } else if(strcmpi(cmdname,"users")==0) {
- map_getallusers(&users);
- sprintf(send_string,"PRIVMSG %s :Users Online: %d",irc_channel,users);
- irc_send(send_string);
- // List all users online [Zido]
- } else if(strcmpi(cmdname,"who")==0) {
- allsd=map_getallusers(&users);
- if(users>0) {
- sprintf(send_string,"NOTICE %s :%d Users Online",source_nick,users);
- irc_send(send_string);
- for(i=0;i<users;i++) {
- sprintf(send_string,"NOTICE %s :Name: \"%s\"",source_nick,allsd[i]->status.name);
- irc_send(send_string);
- }
- } else {
- sprintf(send_string,"NOTICE %s :No Users Online",source_nick);
- irc_send(send_string);
- }
- }
- }
-
- // Refresh Names [Zido]
- else if((strcmpi(command,"join")==0)||(strcmpi(command,"part")==0)||(strcmpi(command,"mode")==0)||(strcmpi(command,"nick")==0)) {
- ShowInfo("IRC: Refreshing User List");
- irc_rmnames();
- printf("...");
- sprintf(send_string,"NAMES %s",irc_channel);
- printf("...");
- irc_send(send_string);
- printf("Done\n");
- }
-
- // Autojoin on kick [Zido]
- else if((strcmpi(command,"kick")==0)&&(irc_autojoin==1)) {
- sprintf(send_string, "JOIN %s %s", target, irc_channel_pass);
- irc_send(send_string);
- }
- }
-
- // Names Reply [Zido]
- else if((strcmpi(command,"353")==0)) {
- ShowInfo("IRC: NAMES recieved\n");
- parse_names_packet(incoming_string);
- }
- }
-
- return;
-}
-
-int send_to_parser(int fd, char *input,char key[2])
-{
- char *temp_string=NULL;
- char *state_mgr=NULL;
- int total_loops=0;
-
- temp_string = strtok_r(input,key,&state_mgr);
- while (temp_string != NULL){
- total_loops = total_loops+1;
- irc_parse_sub(fd,temp_string);
- temp_string = strtok_r(NULL,key,&state_mgr);
- }
- return total_loops;
-}
-
-void do_final_irc(void)
-{
-
-}
-
-void do_init_irc(void)
-{
- if(!use_irc)
- return;
- if (irc_ip_str[strlen(irc_ip_str)-1] == '\n')
- irc_ip_str[strlen(irc_ip_str)-1] = '\0';
- irc_ip = resolve_hostbyname(irc_ip_str, NULL, irc_ip_str);
- if (!irc_ip)
- {
- ShowError("Unable to resolve %s! Cannot connect to IRC server, disabling irc_bot.\n", irc_ip_str);
- use_irc = 0;
- return;
- }
-
- irc_connect_timer(0, 0, 0, 0);
-
- add_timer_func_list(irc_connect_timer, "irc_connect_timer");
- add_timer_func_list(irc_keepalive_timer, "irc_keepalive_timer");
- add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0);
-}
-
-//NAMES Packet(353) parser [Zido]
-int parse_names_packet(char *str) {
- char *tok;
- char source[256];
- char numeric[10];
- char target[256];
- char channel[256];
- char names[1024];
-
- malloc_tsetdword(source,'\0',256);
- malloc_tsetword(numeric,'\0',10);
- malloc_tsetdword(target,'\0',256);
- malloc_tsetdword(channel,'\0',256);
- malloc_tsetdword(names,'\0',1024);
-
- tok=strtok(str,"\r\n");
- sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names);
- if(strcmpi(numeric,"353")==0)
- parse_names(names);
-
- while((tok=strtok(NULL,"\r\n"))!=NULL) {
- sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names);
- if(strcmpi(numeric,"353")==0)
- parse_names(names);
- }
-
- return 0;
-}
-
-//User access level prefix parser [Zido]
-int parse_names(char *str) {
- char *tok;
- if (str == NULL) return 0; //Nothing to parse!
- tok=strtok(str," ");
- switch(tok[0]) {
- case '~':
- set_access(tok+1,ACCESS_OWNER);
- break;
- case '&':
- set_access(tok+1,ACCESS_SOP);
- break;
- case '@':
- set_access(tok+1,ACCESS_OP);
- break;
- case '%':
- set_access(tok+1,ACCESS_HOP);
- break;
- case '+':
- set_access(tok+1,ACCESS_VOICE);
- break;
- default:
- set_access(tok,ACCESS_NORM);
- break;
- }
-
- while((tok=strtok(NULL," "))!=NULL) {
- switch(tok[0]) {
- case '~':
- set_access(tok+1,ACCESS_OWNER);
- break;
- case '&':
- set_access(tok+1,ACCESS_SOP);
- break;
- case '@':
- set_access(tok+1,ACCESS_OP);
- break;
- case '%':
- set_access(tok+1,ACCESS_HOP);
- break;
- case '+':
- set_access(tok+1,ACCESS_VOICE);
- break;
- default:
- set_access(tok,ACCESS_NORM);
- break;
- }
- }
-
- return 1;
-}
-
-//Store user's access level [Zido]
-int set_access(char *nick,int newlevel) {
- int i=0;
-
- for(i=0;i<=MAX_CHANNEL_USERS;i++) {
- if(strcmpi(cd.user[i].name,nick)==0) {
- cd.user[i].level=newlevel;
- return 1;
- }
- }
-
- strcpy(cd.user[last_cd_user].name,nick);
- cd.user[last_cd_user].level=newlevel;
- last_cd_user++;
-
- return 0;
-}
-
-//Returns users access level [Zido]
-int get_access(char *nick) {
- int i=0;
-
- for(i=0;i<=MAX_CHANNEL_USERS;i++) {
- if(strcmpi(cd.user[i].name,nick)==0) {
- return (cd.user[i].level);
- }
- }
-
- return -1;
-}
-
-int irc_rmnames() {
- int i=0;
-
- for(i=0;i<=MAX_CHANNEL_USERS;i++) {
- //malloc_tsetdword(cd.user[i].name,'\0',256);
- cd.user[i].level=0;
- }
-
- last_cd_user=0;
-
-
- return 0;
-}
-
-int irc_read_conf(char *file) {
- FILE *fp=NULL;
- char w1[256];
- char w2[256];
- char path[256];
- char row[1024];
-
- malloc_tsetdword(w1,'\0',256);
- malloc_tsetdword(w2,'\0',256);
- malloc_tsetdword(path,'\0',256);
- malloc_tsetdword(row,'\0',256);
-
- sprintf(path,"conf/%s",file);
-
- if(!(fp=fopen(path,"r"))) {
- ShowError("Cannot find file: %s\n",path);
- return 0;
- }
-
- while(fgets(row,1023,fp)!=NULL) {
- if(row[0]=='/'&&row[1]=='/')
- continue;
- sscanf(row,"%[^:]: %255[^\r\n]",w1,w2);
- if(strcmpi(w1,"use_irc")==0) {
- if(strcmpi(w2,"on")==0)
- use_irc=1;
- else
- use_irc=0;
- }
- else if(strcmpi(w1,"irc_server")==0)
- strcpy(irc_ip_str,w2);
- else if(strcmpi(w1,"irc_port")==0)
- irc_port=atoi(w2);
- else if(strcmpi(w1,"irc_autojoin")==0)
- irc_autojoin=atoi(w2);
- else if(strcmpi(w1,"irc_channel")==0)
- strcpy(irc_channel,w2);
- else if(strcmpi(w1,"irc_channel_pass")==0)
- strcpy(irc_channel_pass,w2);
- else if(strcmpi(w1,"irc_trade_channel")==0)
- strcpy(irc_trade_channel,w2);
- else if(strcmpi(w1,"irc_nick")==0)
- strcpy(irc_nick,w2);
- else if(strcmpi(w1,"irc_pass")==0) {
- if(strcmpi(w2,"0")!=0)
- strcpy(irc_password,w2);
- }
- }
-
- ShowInfo("IRC Config read successfully\n");
-
- return 1;
-}
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../common/core.h"
+#include "../common/socket.h"
+#include "../common/malloc.h"
+#include "../common/db.h"
+#include "../common/timer.h"
+#include "../common/strlib.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+
+#include "map.h"
+#include "pc.h"
+#include "irc.h"
+#include "intif.h" //For GM Broadcast [Zido]
+
+short use_irc=0;
+
+short irc_autojoin=0;
+
+short irc_announce_flag=1;
+short irc_announce_mvp_flag=1;
+short irc_announce_jobchange_flag=1;
+short irc_announce_shop_flag=1;
+
+IRC_SI *irc_si=NULL;
+
+char irc_nick[30]="";
+char irc_password[32]="";
+
+char irc_channel[32]="";
+char irc_channel_pass[32]="";
+char irc_trade_channel[32]="";
+
+unsigned char irc_ip_str[128]="";
+unsigned long irc_ip=0;
+unsigned short irc_port = 6667;
+int irc_fd=0;
+
+struct channel_data cd;
+int last_cd_user=0;
+
+int irc_connect_timer(int tid, unsigned int tick, int id, int data)
+{
+ if(irc_si && session[irc_si->fd])
+ return 0;
+ //Ok, this ShowInfo and printf are a little ugly, but they are meant to
+ //debug just how long the code freezes here. [Skotlex]
+ ShowInfo("(IRC) Connecting to %s... ", irc_ip_str);
+ irc_fd = make_connection(irc_ip,irc_port);
+ if(irc_fd > 0){
+ printf("ok\n");
+ session[irc_fd]->func_parse = irc_parse;
+ } else
+ printf("failed\n");
+ return 0;
+}
+
+void irc_announce(char *buf)
+{
+ char send_string[256];
+ // malloc_tsetdword(send_string,'\0',256); // NOT REQUIRED
+
+ sprintf(send_string,"PRIVMSG %s :",irc_channel);
+ strcat(send_string, buf);
+ irc_send(send_string);
+}
+
+void irc_announce_jobchange(struct map_session_data *sd)
+{
+ char send_string[256];
+
+ nullpo_retv(sd);
+ malloc_tsetdword(send_string,'\0',256);
+
+ sprintf(send_string,"PRIVMSG %s :%s has changed into a %s.",irc_channel,sd->status.name,job_name(sd->status.class_));
+ irc_send(send_string);
+}
+
+void irc_announce_shop(struct map_session_data *sd, int flag)
+{
+ char send_string[256];
+ char mapname[16];
+ int maplen = 0;
+ nullpo_retv(sd);
+
+ malloc_tsetdword(send_string,'\0',256);
+ malloc_tsetdword(mapname,'\0',16);
+
+ if(flag){
+ strcpy(mapname, map[sd->bl.m].name);
+ maplen = strcspn(mapname,".");
+ mapname[maplen] = '\0';
+ mapname[0]=toupper(mapname[0]);
+
+ sprintf(send_string,"PRIVMSG %s :%s has opened a shop, %s, at <%d,%d> in %s.",irc_trade_channel,sd->status.name,sd->message,sd->bl.x,sd->bl.y,mapname);
+ } else
+ sprintf(send_string,"PRIVMSG %s :%s has closed their shop.",irc_trade_channel,sd->status.name);
+
+ irc_send(send_string);
+}
+
+void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md)
+{
+ char send_string[256];
+ char mapname[16];
+ int maplen = 0;
+
+ nullpo_retv(sd);
+ nullpo_retv(md);
+
+ malloc_tsetdword(send_string,'\0',256);
+ malloc_tsetdword(mapname,'\0',16);
+ mapname[15]='\0'; // 15 is the final index, not 16 [Lance]
+ strcpy(mapname, map[md->bl.m].name);
+ maplen = strcspn(mapname,".");
+ mapname[maplen] = '\0';
+ mapname[0]=toupper(mapname[0]);
+
+ sprintf(send_string,"PRIVMSG %s :%s the %s has MVP'd %s in %s.",irc_channel,sd->status.name,job_name(sd->status.class_),md->name, mapname);
+ irc_send(send_string);
+}
+
+int irc_parse(int fd)
+{
+ if (session[fd]->eof){
+ do_close(fd);
+ irc_si = NULL;
+ add_timer(gettick() + 15000, irc_connect_timer, 0, 0);
+ return 0;
+ }
+ if (session[fd]->session_data == NULL){
+ irc_si = (struct IRC_Session_Info*)aMalloc(sizeof(struct IRC_Session_Info));
+ irc_si->fd = fd;
+ irc_si->state = 0;
+ session[fd]->session_data = irc_si;
+ } else if (!irc_si) {
+ irc_si = (struct IRC_Session_Info*)session[fd]->session_data;
+ irc_si->fd = fd;
+ }
+ if(RFIFOREST(fd) > 0){
+ char *incoming_string=aMalloc(RFIFOREST(fd)*sizeof(char));
+ RFIFOHEAD(fd);
+ memcpy(incoming_string,RFIFOP(fd,0),RFIFOREST(fd));
+ send_to_parser(fd,incoming_string,"\n");
+ RFIFOSKIP(fd,RFIFOREST(fd));
+ aFree(incoming_string);
+ }
+ return 0;
+}
+
+int irc_keepalive_timer(int tid, unsigned int tick, int id, int data)
+{
+ char send_string[128];
+ malloc_tsetdword(send_string,'\0',128);
+
+ sprintf(send_string,"PRIVMSG %s : ", irc_nick);
+ irc_send(send_string);
+ add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0);
+ return 0;
+}
+
+void irc_send_sub(int fd, char transmit[4096])
+{
+ sprintf(transmit,"%s%c",transmit,10);
+ WFIFOHEAD(fd,strlen(transmit));
+ memcpy(WFIFOP(fd,0),transmit, strlen(transmit));
+ WFIFOSET(fd,strlen(transmit));
+}
+
+void irc_send(char *buf)
+{
+ char transmit[4096];
+
+ if(!irc_si || !session[irc_si->fd])
+ return;
+
+ malloc_tsetdword(transmit,'\0',4096);
+
+ sprintf(transmit,buf);
+ irc_send_sub(irc_si->fd,transmit);
+}
+
+void irc_parse_sub(int fd, char *incoming_string)
+{
+ char source[256];
+ char command[256];
+ char target[256];
+ char message[8192];
+ char send_string[8192];
+ char *source_nick=NULL;
+ char *source_ident=NULL;
+ char *source_host=NULL;
+ char *state_mgr=NULL;
+
+ char cmd1[256];
+ char cmd2[256];
+ char cmdname[256];
+ char cmdargs[256];
+
+ int users=0;
+ int i=0;
+
+ struct map_session_data **allsd;
+
+ malloc_tsetdword(source,'\0',256);
+ malloc_tsetdword(command,'\0',256);
+ malloc_tsetdword(target,'\0',256);
+ malloc_tsetdword(message,'\0',8192);
+ malloc_tsetdword(send_string,'\0',8192);
+
+ malloc_tsetdword(cmd1,'\0',256);
+ malloc_tsetdword(cmd2,'\0',256);
+ malloc_tsetdword(cmdname,'\0',256);
+ malloc_tsetdword(cmdargs,'\0',256);
+
+ sscanf(incoming_string, ":%255s %255s %255s :%4095[^\r\n]", source, command, target, message);
+ if (source != NULL) {
+ if (strstr(source,"!") != NULL) {
+ source_nick = strtok_r(source,"!",&state_mgr);
+ source_ident = strtok_r(NULL,"@",&state_mgr);
+ source_host = strtok_r(NULL,"%%",&state_mgr);
+ }
+ }
+ if (irc_si->state == 0){
+ sprintf(send_string, "NICK %s", irc_nick);
+ irc_send(send_string);
+ sprintf(send_string, "USER eABot 8 * : eABot");
+ irc_send(send_string);
+ irc_si->state = 1;
+ }
+ else if (irc_si->state == 1){
+ if(!strcmp(command,"001")){
+ ShowStatus("IRC: Connected to IRC.\n");
+ sprintf(send_string, "PRIVMSG nickserv :identify %s", irc_password);
+ irc_send(send_string);
+ sprintf(send_string, "JOIN %s %s", irc_channel, irc_channel_pass);
+ irc_send(send_string);
+ sprintf(send_string,"NAMES %s",irc_channel);
+ irc_send(send_string);
+ irc_si->state = 2;
+ }
+ else if(!strcmp(command,"433")){
+ ShowError("IRC: Nickname %s is already taken, IRC Client unable to connect.\n", irc_nick);
+ sprintf(send_string, "QUIT");
+ irc_send(send_string);
+ if(session[fd])
+ session[fd]->eof=1;
+ }
+ }
+ else if (irc_si->state == 2){
+ if(!strcmp(source, "PING")){
+ sprintf(send_string, "PONG %s", command);
+ irc_send(send_string);
+ }
+
+ else if((strcmpi(target,irc_channel)==0)||(strcmpi(target,irc_channel+1)==0)) {
+
+ // Broadcast [Zido] (Work in Progress)
+ if((strcmpi(command,"privmsg")==0)&&(sscanf(message,"@%255s %255[^\r\n]",cmdname,cmdargs)>0)&&(target[0]=='#')) {
+ if(strcmpi(cmdname,"kami")==0) {
+ if(get_access(source_nick)<ACCESS_OP)
+ sprintf(send_string,"NOTICE %s :Access Denied",source_nick);
+ else {
+ sprintf(send_string,"%s: %s",source_nick,cmdargs);
+ intif_GMmessage(send_string,strlen(send_string)+1,0);
+ sprintf(send_string,"NOTICE %s :Message Sent",source_nick);
+ }
+ irc_send(send_string);
+ // Number of users online [Zido]
+ } else if(strcmpi(cmdname,"users")==0) {
+ map_getallusers(&users);
+ sprintf(send_string,"PRIVMSG %s :Users Online: %d",irc_channel,users);
+ irc_send(send_string);
+ // List all users online [Zido]
+ } else if(strcmpi(cmdname,"who")==0) {
+ allsd=map_getallusers(&users);
+ if(users>0) {
+ sprintf(send_string,"NOTICE %s :%d Users Online",source_nick,users);
+ irc_send(send_string);
+ for(i=0;i<users;i++) {
+ sprintf(send_string,"NOTICE %s :Name: \"%s\"",source_nick,allsd[i]->status.name);
+ irc_send(send_string);
+ }
+ } else {
+ sprintf(send_string,"NOTICE %s :No Users Online",source_nick);
+ irc_send(send_string);
+ }
+ }
+ }
+
+ // Refresh Names [Zido]
+ else if((strcmpi(command,"join")==0)||(strcmpi(command,"part")==0)||(strcmpi(command,"mode")==0)||(strcmpi(command,"nick")==0)) {
+ ShowInfo("IRC: Refreshing User List");
+ irc_rmnames();
+ printf("...");
+ sprintf(send_string,"NAMES %s",irc_channel);
+ printf("...");
+ irc_send(send_string);
+ printf("Done\n");
+ }
+
+ // Autojoin on kick [Zido]
+ else if((strcmpi(command,"kick")==0)&&(irc_autojoin==1)) {
+ sprintf(send_string, "JOIN %s %s", target, irc_channel_pass);
+ irc_send(send_string);
+ }
+ }
+
+ // Names Reply [Zido]
+ else if((strcmpi(command,"353")==0)) {
+ ShowInfo("IRC: NAMES recieved\n");
+ parse_names_packet(incoming_string);
+ }
+ }
+
+ return;
+}
+
+int send_to_parser(int fd, char *input,char key[2])
+{
+ char *temp_string=NULL;
+ char *state_mgr=NULL;
+ int total_loops=0;
+
+ temp_string = strtok_r(input,key,&state_mgr);
+ while (temp_string != NULL){
+ total_loops = total_loops+1;
+ irc_parse_sub(fd,temp_string);
+ temp_string = strtok_r(NULL,key,&state_mgr);
+ }
+ return total_loops;
+}
+
+void do_final_irc(void)
+{
+
+}
+
+void do_init_irc(void)
+{
+ if(!use_irc)
+ return;
+ if (irc_ip_str[strlen(irc_ip_str)-1] == '\n')
+ irc_ip_str[strlen(irc_ip_str)-1] = '\0';
+ irc_ip = resolve_hostbyname(irc_ip_str, NULL, irc_ip_str);
+ if (!irc_ip)
+ {
+ ShowError("Unable to resolve %s! Cannot connect to IRC server, disabling irc_bot.\n", irc_ip_str);
+ use_irc = 0;
+ return;
+ }
+
+ irc_connect_timer(0, 0, 0, 0);
+
+ add_timer_func_list(irc_connect_timer, "irc_connect_timer");
+ add_timer_func_list(irc_keepalive_timer, "irc_keepalive_timer");
+ add_timer(gettick() + 30000, irc_keepalive_timer, 0, 0);
+}
+
+//NAMES Packet(353) parser [Zido]
+int parse_names_packet(char *str) {
+ char *tok;
+ char source[256];
+ char numeric[10];
+ char target[256];
+ char channel[256];
+ char names[1024];
+
+ malloc_tsetdword(source,'\0',256);
+ malloc_tsetword(numeric,'\0',10);
+ malloc_tsetdword(target,'\0',256);
+ malloc_tsetdword(channel,'\0',256);
+ malloc_tsetdword(names,'\0',1024);
+
+ tok=strtok(str,"\r\n");
+ sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names);
+ if(strcmpi(numeric,"353")==0)
+ parse_names(names);
+
+ while((tok=strtok(NULL,"\r\n"))!=NULL) {
+ sscanf(tok,":%255s %10s %255s %*1[=@] %255s :%1023[^\r\n]",source,numeric,target,channel,names);
+ if(strcmpi(numeric,"353")==0)
+ parse_names(names);
+ }
+
+ return 0;
+}
+
+//User access level prefix parser [Zido]
+int parse_names(char *str) {
+ char *tok;
+ if (str == NULL) return 0; //Nothing to parse!
+ tok=strtok(str," ");
+ switch(tok[0]) {
+ case '~':
+ set_access(tok+1,ACCESS_OWNER);
+ break;
+ case '&':
+ set_access(tok+1,ACCESS_SOP);
+ break;
+ case '@':
+ set_access(tok+1,ACCESS_OP);
+ break;
+ case '%':
+ set_access(tok+1,ACCESS_HOP);
+ break;
+ case '+':
+ set_access(tok+1,ACCESS_VOICE);
+ break;
+ default:
+ set_access(tok,ACCESS_NORM);
+ break;
+ }
+
+ while((tok=strtok(NULL," "))!=NULL) {
+ switch(tok[0]) {
+ case '~':
+ set_access(tok+1,ACCESS_OWNER);
+ break;
+ case '&':
+ set_access(tok+1,ACCESS_SOP);
+ break;
+ case '@':
+ set_access(tok+1,ACCESS_OP);
+ break;
+ case '%':
+ set_access(tok+1,ACCESS_HOP);
+ break;
+ case '+':
+ set_access(tok+1,ACCESS_VOICE);
+ break;
+ default:
+ set_access(tok,ACCESS_NORM);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+//Store user's access level [Zido]
+int set_access(char *nick,int newlevel) {
+ int i=0;
+
+ for(i=0;i<=MAX_CHANNEL_USERS;i++) {
+ if(strcmpi(cd.user[i].name,nick)==0) {
+ cd.user[i].level=newlevel;
+ return 1;
+ }
+ }
+
+ strcpy(cd.user[last_cd_user].name,nick);
+ cd.user[last_cd_user].level=newlevel;
+ last_cd_user++;
+
+ return 0;
+}
+
+//Returns users access level [Zido]
+int get_access(char *nick) {
+ int i=0;
+
+ for(i=0;i<=MAX_CHANNEL_USERS;i++) {
+ if(strcmpi(cd.user[i].name,nick)==0) {
+ return (cd.user[i].level);
+ }
+ }
+
+ return -1;
+}
+
+int irc_rmnames() {
+ int i=0;
+
+ for(i=0;i<=MAX_CHANNEL_USERS;i++) {
+ //malloc_tsetdword(cd.user[i].name,'\0',256);
+ cd.user[i].level=0;
+ }
+
+ last_cd_user=0;
+
+
+ return 0;
+}
+
+int irc_read_conf(char *file) {
+ FILE *fp=NULL;
+ char w1[256];
+ char w2[256];
+ char path[256];
+ char row[1024];
+
+ malloc_tsetdword(w1,'\0',256);
+ malloc_tsetdword(w2,'\0',256);
+ malloc_tsetdword(path,'\0',256);
+ malloc_tsetdword(row,'\0',256);
+
+ sprintf(path,"conf/%s",file);
+
+ if(!(fp=fopen(path,"r"))) {
+ ShowError("Cannot find file: %s\n",path);
+ return 0;
+ }
+
+ while(fgets(row,1023,fp)!=NULL) {
+ if(row[0]=='/'&&row[1]=='/')
+ continue;
+ sscanf(row,"%[^:]: %255[^\r\n]",w1,w2);
+ if(strcmpi(w1,"use_irc")==0) {
+ if(strcmpi(w2,"on")==0)
+ use_irc=1;
+ else
+ use_irc=0;
+ }
+ else if(strcmpi(w1,"irc_server")==0)
+ strcpy(irc_ip_str,w2);
+ else if(strcmpi(w1,"irc_port")==0)
+ irc_port=atoi(w2);
+ else if(strcmpi(w1,"irc_autojoin")==0)
+ irc_autojoin=atoi(w2);
+ else if(strcmpi(w1,"irc_channel")==0)
+ strcpy(irc_channel,w2);
+ else if(strcmpi(w1,"irc_channel_pass")==0)
+ strcpy(irc_channel_pass,w2);
+ else if(strcmpi(w1,"irc_trade_channel")==0)
+ strcpy(irc_trade_channel,w2);
+ else if(strcmpi(w1,"irc_nick")==0)
+ strcpy(irc_nick,w2);
+ else if(strcmpi(w1,"irc_pass")==0) {
+ if(strcmpi(w2,"0")!=0)
+ strcpy(irc_password,w2);
+ }
+ }
+
+ ShowInfo("IRC Config read successfully\n");
+
+ return 1;
+}
diff --git a/src/map/irc.h b/src/map/irc.h
index 5c321605b..2297f013b 100644
--- a/src/map/irc.h
+++ b/src/map/irc.h
@@ -1,55 +1,55 @@
-#include "map.h"
-
-// IRC .conf file [Zido]
-#define IRC_CONF "irc_athena.conf"
-
-// IRC Access levels [Zido]
-#define ACCESS_OWNER 5
-#define ACCESS_SOP 4
-#define ACCESS_OP 3
-#define ACCESS_HOP 2
-#define ACCESS_VOICE 1
-#define ACCESS_NORM 0
-
-#define MAX_CHANNEL_USERS 500
-
-extern short use_irc;
-
-extern short irc_announce_flag;
-extern short irc_announce_mvp_flag;
-extern short irc_announce_shop_flag;
-extern short irc_announce_jobchange_flag;
-
-void irc_announce(char *buf);
-void irc_announce_jobchange(struct map_session_data *sd);
-void irc_announce_shop(struct map_session_data *sd,int flag);
-void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md);
-
-int irc_parse(int fd);
-void do_final_irc(void);
-void do_init_irc(void);
-void irc_send(char *buf);
-void irc_parse_sub(int fd, char *incoming_string);
-int send_to_parser(int fd, char *input,char key[2]);
-struct IRC_Session_Info {
- int state;
- int fd;
- char username[30];
- char password[33];
-};
-
-typedef struct IRC_Session_Info IRC_SI;
-
-struct channel_data {
- struct {
- char name[256];
- int level;
- }user[MAX_CHANNEL_USERS];
-};
-
-int parse_names_packet(char *str); // [Zido]
-int parse_names(char *str); // [Zido]
-int set_access(char *nick,int level); // [Zido]
-int get_access(char *nick); // [Zido]
-int irc_rmnames(void); // [Zido]
-int irc_read_conf(char *file); // [Zido]
+#include "map.h"
+
+// IRC .conf file [Zido]
+#define IRC_CONF "irc_athena.conf"
+
+// IRC Access levels [Zido]
+#define ACCESS_OWNER 5
+#define ACCESS_SOP 4
+#define ACCESS_OP 3
+#define ACCESS_HOP 2
+#define ACCESS_VOICE 1
+#define ACCESS_NORM 0
+
+#define MAX_CHANNEL_USERS 500
+
+extern short use_irc;
+
+extern short irc_announce_flag;
+extern short irc_announce_mvp_flag;
+extern short irc_announce_shop_flag;
+extern short irc_announce_jobchange_flag;
+
+void irc_announce(char *buf);
+void irc_announce_jobchange(struct map_session_data *sd);
+void irc_announce_shop(struct map_session_data *sd,int flag);
+void irc_announce_mvp(struct map_session_data *sd, struct mob_data *md);
+
+int irc_parse(int fd);
+void do_final_irc(void);
+void do_init_irc(void);
+void irc_send(char *buf);
+void irc_parse_sub(int fd, char *incoming_string);
+int send_to_parser(int fd, char *input,char key[2]);
+struct IRC_Session_Info {
+ int state;
+ int fd;
+ char username[30];
+ char password[33];
+};
+
+typedef struct IRC_Session_Info IRC_SI;
+
+struct channel_data {
+ struct {
+ char name[256];
+ int level;
+ }user[MAX_CHANNEL_USERS];
+};
+
+int parse_names_packet(char *str); // [Zido]
+int parse_names(char *str); // [Zido]
+int set_access(char *nick,int level); // [Zido]
+int get_access(char *nick); // [Zido]
+int irc_rmnames(void); // [Zido]
+int irc_read_conf(char *file); // [Zido]
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
index 62d40fe51..b6626fa41 100644
--- a/src/map/itemdb.c
+++ b/src/map/itemdb.c
@@ -1,1277 +1,1277 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "../common/grfio.h"
-#include "../common/strlib.h"
-#include "map.h"
-#include "battle.h"
-#include "itemdb.h"
-#include "script.h"
-#include "pc.h"
-
-// ** ITEMDB_OVERRIDE_NAME_VERBOSE **
-// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します.
-//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1
-
-static struct dbt* item_db;
-
-static struct item_group itemgroup_db[MAX_ITEMGROUP];
-
-struct item_data dummy_item; //This is the default dummy item used for non-existant items. [Skotlex]
-
-/*==========================================
- * 名前で検索用
- *------------------------------------------
- */
-// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name)
-int itemdb_searchname_sub(DBKey key,void *data,va_list ap)
-{
- struct item_data *item=(struct item_data *)data,**dst;
- char *str;
- str=va_arg(ap,char *);
- dst=va_arg(ap,struct item_data **);
- if(item == &dummy_item) return 0;
- if( strcmpi(item->name,str)==0 ) //by lupus
- *dst=item;
- return 0;
-}
-
-/*==========================================
- * 名前で検索用
- *------------------------------------------
- */
-int itemdb_searchjname_sub(int key,void *data,va_list ap)
-{
- struct item_data *item=(struct item_data *)data,**dst;
- char *str;
- str=va_arg(ap,char *);
- dst=va_arg(ap,struct item_data **);
- if(item == &dummy_item) return 0;
- if( strcmpi(item->jname,str)==0 )
- *dst=item;
- return 0;
-}
-
-/*==========================================
- * 名前で検索
- *------------------------------------------
- */
-struct item_data* itemdb_searchname(const char *str)
-{
- struct item_data *item=NULL;
- item_db->foreach(item_db,itemdb_searchname_sub,str,&item);
- return item;
-}
-
-static int itemdb_searchname_array_sub(DBKey key,void * data,va_list ap)
-{
- struct item_data *item=(struct item_data *)data;
- char *str;
- str=va_arg(ap,char *);
- if (item == &dummy_item)
- return 1; //Invalid item.
- if(stristr(item->jname,str))
- return 0;
- if(stristr(item->name,str))
- return 0;
- return strcmpi(item->jname,str);
-}
-
-/*==========================================
- * Founds up to N matches. Returns number of matches [Skotlex]
- *------------------------------------------
- */
-int itemdb_searchname_array(struct item_data** data, int size, const char *str)
-{
- return item_db->getall(item_db,(void**)data,size,itemdb_searchname_array_sub,str);
-}
-
-
-/*==========================================
- * 箱系アイテム検索
- *------------------------------------------
- */
-int itemdb_searchrandomid(int group)
-{
- if(group<1 || group>=MAX_ITEMGROUP) {
- if (battle_config.error_log)
- ShowError("itemdb_searchrandomid: Invalid group id %d\n", group);
- return UNKNOWN_ITEM_ID;
- }
- if (itemgroup_db[group].qty)
- return itemgroup_db[group].nameid[rand()%itemgroup_db[group].qty];
-
- if (battle_config.error_log)
- ShowError("itemdb_searchrandomid: No item entries for group id %d\n", group);
- return UNKNOWN_ITEM_ID;
-}
-
-/*==========================================
- * Calculates total item-group related bonuses for the given item. [Skotlex]
- *------------------------------------------
- */
-int itemdb_group_bonus(struct map_session_data *sd, int itemid)
-{
- int bonus = 0, i, j;
- for (i=0; i < MAX_ITEMGROUP; i++) {
- if (!sd->itemgrouphealrate[i])
- continue;
- for (j=0; j < itemgroup_db[i].qty; j++) {
- if (itemgroup_db[i].nameid[j] == itemid)
- {
- bonus += sd->itemgrouphealrate[i];
- continue;
- }
- }
- }
- return bonus;
-}
-
-/*==========================================
- * DBの存在確認
- *------------------------------------------
- */
-struct item_data* itemdb_exists(int nameid)
-{
- struct item_data* id;
- if (!nameid) return NULL;
- id = idb_get(item_db,nameid);
- //Adjust nameid in case it's used outside. [Skotlex]
- if (id == &dummy_item)
- dummy_item.nameid = nameid;
- return id;
-}
-
-/*==========================================
- * Converts the jobid from the format in itemdb
- * to the format used by the map server. [Skotlex]
- *------------------------------------------
- */
-static void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask)
-{
- int i;
- bclass[0]= bclass[1]= bclass[2]= 0;
- //Base classes
- if (jobmask & 1<<JOB_NOVICE)
- { //Both Novice/Super-Novice are counted with the same ID
- bclass[0] |= 1<<MAPID_NOVICE;
- bclass[1] |= 1<<MAPID_NOVICE;
- }
- for (i = JOB_NOVICE+1; i <= JOB_THIEF; i++)
- {
- if (jobmask & 1<<i)
- bclass[0] |= 1<<(MAPID_NOVICE+i);
- }
- //2-1 classes
- if (jobmask & 1<<JOB_KNIGHT)
- bclass[1] |= 1<<MAPID_SWORDMAN;
- if (jobmask & 1<<JOB_PRIEST)
- bclass[1] |= 1<<MAPID_ACOLYTE;
- if (jobmask & 1<<JOB_WIZARD)
- bclass[1] |= 1<<MAPID_MAGE;
- if (jobmask & 1<<JOB_BLACKSMITH)
- bclass[1] |= 1<<MAPID_MERCHANT;
- if (jobmask & 1<<JOB_HUNTER)
- bclass[1] |= 1<<MAPID_ARCHER;
- if (jobmask & 1<<JOB_ASSASSIN)
- bclass[1] |= 1<<MAPID_THIEF;
- //2-2 classes
- if (jobmask & 1<<JOB_CRUSADER)
- bclass[2] |= 1<<MAPID_SWORDMAN;
- if (jobmask & 1<<JOB_MONK)
- bclass[2] |= 1<<MAPID_ACOLYTE;
- if (jobmask & 1<<JOB_SAGE)
- bclass[2] |= 1<<MAPID_MAGE;
- if (jobmask & 1<<JOB_ALCHEMIST)
- bclass[2] |= 1<<MAPID_MERCHANT;
- if (jobmask & 1<<JOB_BARD)
- bclass[2] |= 1<<MAPID_ARCHER;
-// Bard/Dancer share the same slot now.
-// if (jobmask & 1<<JOB_DANCER)
-// bclass[2] |= 1<<MAPID_ARCHER;
- if (jobmask & 1<<JOB_ROGUE)
- bclass[2] |= 1<<MAPID_THIEF;
- //Special classes that don't fit above.
- if (jobmask & 1<<21) //Taekwon boy
- bclass[0] |= 1<<MAPID_TAEKWON;
- if (jobmask & 1<<22) //Star Gladiator
- bclass[1] |= 1<<MAPID_TAEKWON;
- if (jobmask & 1<<23) //Soul Linker
- bclass[2] |= 1<<MAPID_TAEKWON;
- if (jobmask & 1<<JOB_GUNSLINGER)
- bclass[0] |= 1<<MAPID_GUNSLINGER;
- if (jobmask & 1<<JOB_NINJA)
- bclass[0] |= 1<<MAPID_NINJA;
-}
-
-static void create_dummy_data(void) {
- malloc_set(&dummy_item, 0, sizeof(struct item_data));
- dummy_item.nameid=500;
- dummy_item.weight=1;
- dummy_item.value_sell = 1;
- dummy_item.type=3; //Etc item
- strncpy(dummy_item.name,"UNKNOWN_ITEM",ITEM_NAME_LENGTH-1);
- strncpy(dummy_item.jname,"UNKNOWN_ITEM",ITEM_NAME_LENGTH-1);
- dummy_item.view_id = UNKNOWN_ITEM_ID;
-}
-
-static void* create_item_data(DBKey key, va_list args) {
- struct item_data *id;
- id=(struct item_data *)aCalloc(1,sizeof(struct item_data));
- id->nameid = key.i;
- id->weight=1;
- id->type=IT_ETC;
- return id;
-}
-
-/*==========================================
- * Loads (and creates if not found) an item from the db.
- *------------------------------------------
- */
-struct item_data* itemdb_load(int nameid)
-{
- struct item_data *id = idb_ensure(item_db,nameid,create_item_data);
- if (id == &dummy_item)
- { //Remove dummy_item, replace by real data.
- DBKey key;
- key.i = nameid;
- idb_remove(item_db,nameid);
- id = create_item_data(key, NULL);
- idb_put(item_db,nameid,id);
- }
- return id;
-}
-
-static void* return_dummy_data(DBKey key, va_list args) {
- if (battle_config.error_log)
- ShowWarning("itemdb_search: Item ID %d does not exists in the item_db. Using dummy data.\n", key.i);
- dummy_item.nameid = key.i;
- return &dummy_item;
-}
-
-/*==========================================
- * Loads an item from the db. If not found, it will return the dummy item.
- *------------------------------------------
- */
-struct item_data* itemdb_search(int nameid)
-{
- return idb_ensure(item_db,nameid,return_dummy_data);
-}
-
-/*==========================================
- * Returns if given item is a player-equippable piece.
- *------------------------------------------
- */
-int itemdb_isequip(int nameid)
-{
- int type=itemdb_type(nameid);
- switch (type) {
- case IT_WEAPON:
- case IT_ARMOR:
- case IT_AMMO:
- return 1;
- default:
- return 0;
- }
-}
-
-/*==========================================
- * Alternate version of itemdb_isequip
- *------------------------------------------
- */
-int itemdb_isequip2(struct item_data *data)
-{
- nullpo_retr(0, data);
- switch(data->type) {
- case IT_WEAPON:
- case IT_ARMOR:
- case IT_AMMO:
- return 1;
- default:
- return 0;
- }
-}
-
-/*==========================================
-* Returns if given item's type is stackable.
-*------------------------------------------
-*/
-int itemdb_isstackable(int nameid)
-{
- int type=itemdb_type(nameid);
- switch(type) {
- case IT_WEAPON:
- case IT_ARMOR:
- case IT_PETEGG:
- case IT_PETARMOR:
- return 0;
- default:
- return 1;
- }
-}
-
-/*==========================================
-* Alternate version of itemdb_isstackable
-*------------------------------------------
-*/
-int itemdb_isstackable2(struct item_data *data)
-{
- nullpo_retr(0, data);
- switch(data->type) {
- case IT_WEAPON:
- case IT_ARMOR:
- case IT_PETEGG:
- case IT_PETARMOR:
- return 0;
- default:
- return 1;
- }
-}
-
-
-/*==========================================
- * Trade Restriction functions [Skotlex]
- *------------------------------------------
- */
-int itemdb_isdropable_sub(struct item_data *item, int gmlv, int unused)
-{
- return (item && (!(item->flag.trade_restriction&1) || gmlv >= item->gm_lv_trade_override));
-}
-
-int itemdb_cantrade_sub(struct item_data* item, int gmlv, int gmlv2)
-{
- return (item && (!(item->flag.trade_restriction&2) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
-}
-
-int itemdb_canpartnertrade_sub(struct item_data* item, int gmlv, int gmlv2)
-{
- return (item && (item->flag.trade_restriction&4 || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
-}
-
-int itemdb_cansell_sub(struct item_data* item, int gmlv, int unused)
-{
- return (item && (!(item->flag.trade_restriction&8) || gmlv >= item->gm_lv_trade_override));
-}
-
-int itemdb_cancartstore_sub(struct item_data* item, int gmlv, int unused)
-{
- return (item && (!(item->flag.trade_restriction&16) || gmlv >= item->gm_lv_trade_override));
-}
-
-int itemdb_canstore_sub(struct item_data* item, int gmlv, int unused)
-{
- return (item && (!(item->flag.trade_restriction&32) || gmlv >= item->gm_lv_trade_override));
-}
-
-int itemdb_canguildstore_sub(struct item_data* item, int gmlv, int unused)
-{
- return (item && (!(item->flag.trade_restriction&64) || gmlv >= item->gm_lv_trade_override));
-}
-
-int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int))
-{
- struct item_data* item_data = itemdb_search(item->nameid);
- int i;
-
- if (!func(item_data, gmlv, gmlv2))
- return 0;
-
- if(item_data->slot == 0 || itemdb_isspecial(item->card[0]))
- return 1;
-
- for(i = 0; i < item_data->slot; i++) {
- if (!item->card[i]) continue;
- if (!func(itemdb_search(item->card[i]), gmlv, gmlv2))
- return 0;
- }
- return 1;
-}
-
-/*==========================================
- * Specifies if item-type should drop unidentified.
- *------------------------------------------
- */
-int itemdb_isidentified(int nameid)
-{
- int type=itemdb_type(nameid);
- switch (type) {
- case IT_WEAPON:
- case IT_ARMOR:
- case IT_PETARMOR:
- return 0;
- default:
- return 1;
- }
-}
-
-/*==========================================
- * アイテム使用可能フラグのオーバーライド
- *------------------------------------------
- */
-static int itemdb_read_itemavail (void)
-{
- FILE *fp;
- int nameid, j, k, ln = 0;
- char line[1024], *str[10], *p;
- struct item_data *id;
-
- sprintf(line, "%s/item_avail.txt", db_path);
- if ((fp = fopen(line,"r")) == NULL) {
- ShowError("can't read %s\n", line);
- return -1;
- }
-
- while (fgets(line, sizeof(line) - 1, fp)) {
- if (line[0] == '/' && line[1] == '/')
- continue;
- malloc_tsetdword(str, 0, sizeof(str));
- for (j = 0, p = line; j < 2 && p; j++) {
- str[j] = p;
- p = strchr(p, ',');
- if(p) *p++ = 0;
- }
-
- if (j < 2 || str[0] == NULL ||
- (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid)))
- continue;
-
- k = atoi(str[1]);
- if (k > 0) {
- id->flag.available = 1;
- id->view_id = k;
- } else
- id->flag.available = 0;
- ln++;
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_avail.txt");
-
- return 0;
-}
-
-/*==========================================
- * read item group data
- *------------------------------------------
- */
-static void itemdb_read_itemgroup_sub(const char* filename)
-{
- FILE *fp;
- char line[1024];
- int ln=0;
- int groupid,j,k,nameid;
- char *str[3],*p;
- char w1[1024], w2[1024];
-
- if( (fp=fopen(filename,"r"))==NULL ){
- ShowError("can't read %s\n", line);
- return;
- }
-
- while(fgets(line,1020,fp)){
- ln++;
- if(line[0]=='/' && line[1]=='/')
- continue;
- if(strstr(line,"import")) {
- if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2 &&
- strcmpi(w1, "import") == 0) {
- itemdb_read_itemgroup_sub(w2);
- continue;
- }
- }
- malloc_tsetdword(str,0,sizeof(str));
- for(j=0,p=line;j<3 && p;j++){
- str[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if(str[0]==NULL)
- continue;
- if (j<3) {
- if (j>1) //Or else it barks on blank lines...
- ShowWarning("itemdb_read_itemgroup: Insufficient fields for entry at %s:%d\n", filename, ln);
- continue;
- }
- groupid = atoi(str[0]);
- if (groupid < 0 || groupid >= MAX_ITEMGROUP) {
- ShowWarning("itemdb_read_itemgroup: Invalid group %d in %s:%d\n", groupid, filename, ln);
- continue;
- }
- nameid = atoi(str[1]);
- if (!itemdb_exists(nameid)) {
- ShowWarning("itemdb_read_itemgroup: Non-existant item %d in %s:%d\n", nameid, filename, ln);
- continue;
- }
- k = atoi(str[2]);
- if (itemgroup_db[groupid].qty+k > MAX_RANDITEM) {
- ShowWarning("itemdb_read_itemgroup: Group %d is full (%d entries) in %s:%d\n", groupid, MAX_RANDITEM, filename, ln);
- continue;
- }
- for(j=0;j<k;j++)
- itemgroup_db[groupid].nameid[itemgroup_db[groupid].qty++] = nameid;
- }
- fclose(fp);
- return;
-}
-
-static void itemdb_read_itemgroup(void)
-{
- char path[256];
- int i;
- const char* groups[] = {
- "Blue Box",
- "Violet Box",
- "Card Album",
- "Gift Box",
- "Scroll Box",
- "Finding Ore",
- "Cookie Bag",
- "Potion",
- "Herbs",
- "Fruits",
- "Meat",
- "Candy",
- "Juice",
- "Fish",
- "Boxes",
- "Gemstone",
- "Jellopy",
- "Ore",
- "Food",
- "Recovery",
- "Minerals",
- "Taming",
- "Scrolls",
- "Quivers",
- "Masks",
- "Accesory",
- "Jewels",
- "Gift Box 1",
- "Gift Box 2",
- "Gift Box 3",
- "Gift Box 4",
- "Egg Boy",
- "Egg Girl",
- "Gift Box China",
- "Lotto Box",
- };
- malloc_tsetdword(&itemgroup_db, 0, sizeof(itemgroup_db));
- snprintf(path, 255, "%s/item_group_db.txt", db_path);
- itemdb_read_itemgroup_sub(path);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","item_group_db.txt");
- if (battle_config.etc_log) {
- for (i = 1; i < MAX_ITEMGROUP; i++)
- ShowInfo("Group %s: %d entries.\n", groups[i-1], itemgroup_db[i].qty);
- }
- return;
-}
-/*==========================================
- * アイテムの名前テーブルを読み込む
- *------------------------------------------
- */
-static int itemdb_read_itemnametable(void)
-{
- char *buf,*p;
- int s;
-
- buf=(char *) grfio_reads("data\\idnum2itemdisplaynametable.txt",&s);
-
- if(buf==NULL)
- return -1;
-
- buf[s]=0;
- for(p=buf;p-buf<s;){
- int nameid;
- char buf2[64]; //Why 64? What's this for, other than holding an item's name? [Skotlex]
-
- if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
-
-#ifdef ITEMDB_OVERRIDE_NAME_VERBOSE
- if( itemdb_exists(nameid) &&
- strncmp(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH)!=0 ){
- ShowNotice("[override] %d %s => %s\n",nameid
- ,itemdb_search(nameid)->jname,buf2);
- }
-#endif
-
- strncpy(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH-1);
- }
-
- p=strchr(p,10);
- if(!p) break;
- p++;
- }
- aFree(buf);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\idnum2itemdisplaynametable.txt");
-
- return 0;
-}
-
-/*==========================================
- * カードイラストのリソース名前テーブルを読み込む
- *------------------------------------------
- */
-static int itemdb_read_cardillustnametable(void)
-{
- char *buf,*p;
- int s;
-
- buf=(char *) grfio_reads("data\\num2cardillustnametable.txt",&s);
-
- if(buf==NULL)
- return -1;
-
- buf[s]=0;
- for(p=buf;p-buf<s;){
- int nameid;
- char buf2[64];
-
- if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
- strcat(buf2,".bmp");
- memcpy(itemdb_search(nameid)->cardillustname,buf2,64);
- }
-
- p=strchr(p,10);
- if(!p) break;
- p++;
- }
- aFree(buf);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\num2cardillustnametable.txt");
-
- return 0;
-}
-
-//
-// 初期化
-//
-/*==========================================
- *
- *------------------------------------------
- */
-static int itemdb_read_itemslottable(void)
-{
- char *buf, *p;
- int s;
-
- buf = (char *)grfio_reads("data\\itemslottable.txt", &s);
- if (buf == NULL)
- return -1;
- buf[s] = 0;
- for (p = buf; p - buf < s; ) {
- int nameid, equip;
- struct item_data* item;
- sscanf(p, "%d#%d#", &nameid, &equip);
- item = itemdb_search(nameid);
- if (equip && item && itemdb_isequip2(item))
- item->equip = equip;
- p = strchr(p, 10);
- if(!p) break;
- p++;
- p=strchr(p, 10);
- if(!p) break;
- p++;
- }
- aFree(buf);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\itemslottable.txt");
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-static int itemdb_read_itemslotcounttable(void)
-{
- char *buf, *p;
- int s;
-
- buf = (char *)grfio_reads("data\\itemslotcounttable.txt", &s);
- if (buf == NULL)
- return -1;
- buf[s] = 0;
- for (p = buf; p - buf < s;){
- int nameid, slot;
- sscanf(p, "%d#%d#", &nameid, &slot);
- if (slot > MAX_SLOTS)
- {
- ShowWarning("itemdb_read_itemslotcounttable: Item %d specifies %d slots, but the server only supports up to %d\n", nameid, slot, MAX_SLOTS);
- slot = MAX_SLOTS;
- }
- itemdb_slot(nameid) = slot;
- p = strchr(p,10);
- if(!p) break;
- p++;
- p = strchr(p,10);
- if(!p) break;
- p++;
- }
- aFree(buf);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", "data\\itemslotcounttable.txt");
-
- return 0;
-}
-
-/*==========================================
- * 装備制限ファイル読み出し
- *------------------------------------------
- */
-static int itemdb_read_noequip(void)
-{
- FILE *fp;
- char line[1024];
- int ln=0;
- int nameid,j;
- char *str[32],*p;
- struct item_data *id;
-
- sprintf(line, "%s/item_noequip.txt", db_path);
- if( (fp=fopen(line,"r"))==NULL ){
- ShowError("can't read %s\n", line);
- return -1;
- }
- while(fgets(line,1020,fp)){
- if(line[0]=='/' && line[1]=='/')
- continue;
- malloc_tsetdword(str,0,sizeof(str));
- for(j=0,p=line;j<2 && p;j++){
- str[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if(str[0]==NULL)
- continue;
-
- nameid=atoi(str[0]);
- if(nameid<=0 || !(id=itemdb_exists(nameid)))
- continue;
-
- id->flag.no_equip=atoi(str[1]);
-
- ln++;
-
- }
- fclose(fp);
- if (ln > 0) {
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_noequip.txt");
- }
- return 0;
-}
-
-/*==========================================
- * Reads item trade restrictions [Skotlex]
- *------------------------------------------
- */
-static int itemdb_read_itemtrade(void)
-{
- FILE *fp;
- int nameid, j, flag, gmlv, ln = 0;
- char line[1024], *str[10], *p;
- struct item_data *id;
-
- sprintf(line, "%s/item_trade.txt", db_path);
- if ((fp = fopen(line,"r")) == NULL) {
- ShowError("can't read %s\n", line);
- return -1;
- }
-
- while (fgets(line, sizeof(line) - 1, fp)) {
- if (line[0] == '/' && line[1] == '/')
- continue;
- malloc_tsetdword(str, 0, sizeof(str));
- for (j = 0, p = line; j < 3 && p; j++) {
- str[j] = p;
- p = strchr(p, ',');
- if(p) *p++ = 0;
- }
-
- if (j < 3 || str[0] == NULL ||
- (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid)))
- continue;
-
- flag = atoi(str[1]);
- gmlv = atoi(str[2]);
-
- if (flag > 0 && flag < 128 && gmlv > 0) { //Check range
- id->flag.trade_restriction = flag;
- id->gm_lv_trade_override = gmlv;
- ln++;
- }
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_trade.txt");
-
- return 0;
-}
-
-/*======================================
- * Applies gender restrictions according to settings. [Skotlex]
- *======================================
- */
-static int itemdb_gendercheck(struct item_data *id)
-{
- if (id->nameid == WEDDING_RING_M) //Grom Ring
- return 1;
- if (id->nameid == WEDDING_RING_F) //Bride Ring
- return 0;
- if (id->look == W_MUSICAL && id->type == IT_WEAPON) //Musical instruments are always male-only
- return 1;
- if (id->look == W_WHIP && id->type == IT_WEAPON) //Whips are always female-only
- return 0;
-
- return (battle_config.ignore_items_gender?2:id->sex);
-}
-#ifndef TXT_ONLY
-
-/*======================================
-* SQL
-*===================================
-*/
-static int itemdb_read_sqldb(void)
-{
- unsigned short nameid;
- struct item_data *id;
- char script[65535 + 2 + 1]; // Maximum length of MySQL TEXT type (65535) + 2 bytes for curly brackets + 1 byte for terminator
- char *item_db_name[] = { item_db_db, item_db2_db };
- long unsigned int ln = 0;
- int i;
-
- // ----------
-
- for (i = 0; i < 2; i++) {
- sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]);
-
- // Execute the query; if the query execution succeeded...
- if (mysql_query(&mmysql_handle, tmp_sql) == 0) {
- sql_res = mysql_store_result(&mmysql_handle);
-
- // If the storage of the query result succeeded...
- if (sql_res) {
- // Parse each row in the query result into sql_row
- while ((sql_row = mysql_fetch_row(sql_res)))
- { /*Table structure is:
- 00 id
- 01 name_english
- 02 name_japanese
- 03 type
- 04 price_buy
- 05 price_sell
- 06 weight
- 07 attack
- 08 defence
- 09 range
- 10 slots
- 11 equip_jobs
- 12 equip_upper
- 13 equip_genders
- 14 equip_locations
- 15 weapon_level
- 16 equip_level
- 17 refineable
- 18 view
- 19 script
- 20 equip_script
- 21 unequip_script
- */
- nameid = atoi(sql_row[0]);
-
- // If the identifier is not within the valid range, process the next row
- if (nameid == 0)
- continue;
-
- ln++;
-
- // ----------
- id = itemdb_load(nameid);
-
- strncpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1);
- strncpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1);
-
- id->type = atoi(sql_row[3]);
- if (id->type == IT_DELAYCONSUME)
- { //Items that are consumed upon target confirmation
- //(yggdrasil leaf, spells & pet lures) [Skotlex]
- id->type = IT_USABLE;
- id->flag.delay_consume=1;
- }
-
- // If price_buy is not NULL and price_sell is not NULL...
- if ((sql_row[4] != NULL) && (sql_row[5] != NULL)) {
- id->value_buy = atoi(sql_row[4]);
- id->value_sell = atoi(sql_row[5]);
- }
- // If price_buy is not NULL and price_sell is NULL...
- else if ((sql_row[4] != NULL) && (sql_row[5] == NULL)) {
- id->value_buy = atoi(sql_row[4]);
- id->value_sell = atoi(sql_row[4]) / 2;
- }
- // If price_buy is NULL and price_sell is not NULL...
- else if ((sql_row[4] == NULL) && (sql_row[5] != NULL)) {
- id->value_buy = atoi(sql_row[5]) * 2;
- id->value_sell = atoi(sql_row[5]);
- }
- // If price_buy is NULL and price_sell is NULL...
- if ((sql_row[4] == NULL) && (sql_row[5] == NULL)) {
- id->value_buy = 0;
- id->value_sell = 0;
- }
-
- id->weight = atoi(sql_row[6]);
- id->atk = (sql_row[7] != NULL) ? atoi(sql_row[7]) : 0;
- id->def = (sql_row[8] != NULL) ? atoi(sql_row[8]) : 0;
- id->range = (sql_row[9] != NULL) ? atoi(sql_row[9]) : 0;
- id->slot = (sql_row[10] != NULL) ? atoi(sql_row[10]) : 0;
- if (id->slot > MAX_SLOTS)
- {
- ShowWarning("itemdb_read_sqldb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
- id->slot = MAX_SLOTS;
- }
- itemdb_jobid2mapid(id->class_base, (sql_row[11] != NULL) ? (unsigned int)strtoul(sql_row[11], NULL, 0) : 0);
- id->class_upper= (sql_row[12] != NULL) ? atoi(sql_row[12]) : 0;
- id->sex = (sql_row[13] != NULL) ? atoi(sql_row[13]) : 0;
- id->equip = (sql_row[14] != NULL) ? atoi(sql_row[14]) : 0;
- if (!id->equip && itemdb_isequip2(id))
- {
- ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname);
- id->type = 3;
- }
- id->wlv = (sql_row[15] != NULL) ? atoi(sql_row[15]) : 0;
- id->elv = (sql_row[16] != NULL) ? atoi(sql_row[16]) : 0;
- id->flag.no_refine = (sql_row[17] == NULL || atoi(sql_row[17]) == 1)?0:1;
- id->look = (sql_row[18] != NULL) ? atoi(sql_row[18]) : 0;
- id->view_id = 0;
- id->sex = itemdb_gendercheck(id); //Apply gender filtering.
-
- // ----------
-
- if (id->script)
- script_free_code(id->script);
- if (sql_row[19] != NULL) {
- if (sql_row[19][0] == '{')
- id->script = parse_script((unsigned char *) sql_row[19],item_db_name[i], 0);
- else {
- sprintf(script, "{%s}", sql_row[19]);
- id->script = parse_script((unsigned char *) script, item_db_name[i], 0);
- }
- } else id->script = NULL;
-
- if (id->equip_script)
- script_free_code(id->equip_script);
- if (sql_row[20] != NULL) {
- if (sql_row[20][0] == '{')
- id->equip_script = parse_script((unsigned char *) sql_row[20], item_db_name[i], 0);
- else {
- sprintf(script, "{%s}", sql_row[20]);
- id->equip_script = parse_script((unsigned char *) script, item_db_name[i], 0);
- }
- } else id->equip_script = NULL;
-
- if (id->unequip_script)
- script_free_code(id->unequip_script);
- if (sql_row[21] != NULL) {
- if (sql_row[21][0] == '{')
- id->unequip_script = parse_script((unsigned char *) sql_row[21],item_db_name[i], 0);
- else {
- sprintf(script, "{%s}", sql_row[21]);
- id->unequip_script = parse_script((unsigned char *) script, item_db_name[i], 0);
- }
- } else id->unequip_script = NULL;
-
- // ----------
-
- id->flag.available = 1;
- id->flag.value_notdc = 0;
- id->flag.value_notoc = 0;
- }
-
- ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]);
- ln = 0;
- } else {
- ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
-
- // Free the query result
- mysql_free_result(sql_res);
- } else {
- ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
-
- return 0;
-}
-#endif /* not TXT_ONLY */
-
-/*==========================================
- * アイテムデータベースの読み込み
- *------------------------------------------
- */
-static int itemdb_readdb(void)
-{
- FILE *fp;
- char line[1024];
- int ln=0,lines=0;
- int nameid,j;
- char *str[32],*p,*np;
- struct item_data *id;
- int i=0;
- char *filename[]={ "item_db.txt","item_db2.txt" };
-
- for(i=0;i<2;i++){
- sprintf(line, "%s/%s", db_path, filename[i]);
- fp=fopen(line,"r");
- if(fp==NULL){
- if(i>0)
- continue;
- ShowFatalError("can't read %s\n",line);
- exit(1);
- }
-
- lines=0;
- while(fgets(line,1020,fp)){
- lines++;
- if(line[0]=='/' && line[1]=='/')
- continue;
- malloc_tsetdword(str,0,sizeof(str));
- for(j=0,np=p=line;j<19 && p;j++){
- str[j]=p;
- p=strchr(p,',');
- if(p){ *p++=0; np=p; }
- }
- if(str[0]==NULL)
- continue;
-
- nameid=atoi(str[0]);
- if(nameid<=0)
- continue;
- if (j < 19)
- { //Crash-fix on broken item lines. [Skotlex]
- ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid);
- continue;
- }
- ln++;
-
- //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View
- id=itemdb_load(nameid);
- strncpy(id->name, str[1], ITEM_NAME_LENGTH-1);
- strncpy(id->jname, str[2], ITEM_NAME_LENGTH-1);
- id->type=atoi(str[3]);
- if (id->type == IT_DELAYCONSUME)
- { //Items that are consumed upon target confirmation
- //(yggdrasil leaf, spells & pet lures) [Skotlex]
- id->type = IT_USABLE;
- id->flag.delay_consume=1;
- }
-
- {
- int buy = atoi(str[4]), sell = atoi(str[5]);
- // if buying price > selling price * 2 consider it valid and don't change it [celest]
- if (buy && sell && buy > sell*2){
- id->value_buy = buy;
- id->value_sell = sell;
- } else {
- // buy≠sell*2 は item_value_db.txt で指定してください。
- if (sell) { // sell値を優先とする
- id->value_buy = sell*2;
- id->value_sell = sell;
- } else {
- id->value_buy = buy;
- id->value_sell = buy/2;
- }
- }
- // check for bad prices that can possibly cause exploits
- if (id->value_buy*75/100 < id->value_sell*124/100) {
- ShowWarning ("Item %s [%d] buying:%d < selling:%d\n",
- id->name, id->nameid, id->value_buy*75/100, id->value_sell*124/100);
- }
- }
- id->weight=atoi(str[6]);
- id->atk=atoi(str[7]);
- id->def=atoi(str[8]);
- id->range=atoi(str[9]);
- id->slot=atoi(str[10]);
- if (id->slot > MAX_SLOTS)
- {
- ShowWarning("itemdb_readdb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
- id->slot = MAX_SLOTS;
- }
- itemdb_jobid2mapid(id->class_base, (unsigned int)strtoul(str[11],NULL,0));
- id->class_upper = atoi(str[12]);
- id->sex = atoi(str[13]);
- if(id->equip != atoi(str[14])){
- id->equip=atoi(str[14]);
- }
- if (!id->equip && itemdb_isequip2(id))
- {
- ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname);
- id->type = 3;
- }
- id->wlv=atoi(str[15]);
- id->elv=atoi(str[16]);
- id->flag.no_refine = atoi(str[17])?0:1; //If the refine column is 1, no_refine is 0
- id->look=atoi(str[18]);
- id->flag.available=1;
- id->flag.value_notdc=0;
- id->flag.value_notoc=0;
- id->view_id=0;
- id->sex = itemdb_gendercheck(id); //Apply gender filtering.
-
- if (id->script) {
- script_free_code(id->script);
- id->script=NULL;
- }
- if (id->equip_script) {
- script_free_code(id->equip_script);
- id->equip_script=NULL;
- }
- if (id->unequip_script) {
- script_free_code(id->unequip_script);
- id->unequip_script=NULL;
- }
-
- if((p=strchr(np,'{'))==NULL)
- continue;
-
- str[19] = p; //Script
- np = strchr(p,'}');
-
- while (np && np[1] && np[1] != ',')
- np = strchr(np+1,'}'); //Jump close brackets until the next field is found.
- if (!np || !np[1]) {
- //Couldn't find the end of the script field.
- id->script = parse_script((unsigned char *) str[19],filename[i],lines);
- continue;
- }
- np[1] = '\0'; //Set end of script
- id->script = parse_script((unsigned char *) str[19],filename[i],lines);
- np+=2; //Skip to next field
-
- if(!np || (p=strchr(np,'{'))==NULL)
- continue;
-
- str[20] = p; //Equip Script
- np = strchr(p,'}');
-
- while (np && np[1] && np[1] != ',')
- np = strchr(np+1,'}'); //Jump close brackets until the next field is found.
- if (!np || !np[1]) {
- //Couldn't find the end of the script field.
- id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines);
- continue;
- }
-
- np[1] = '\0'; //Set end of script
- id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines);
- np+=2; //Skip comma, to next field
-
- if(!np || (p=strchr(np,'{'))==NULL)
- continue;
- //Unequip script, last column.
- id->unequip_script = parse_script((unsigned char *) p,filename[i],lines);
- }
- fclose(fp);
- if (ln > 0) {
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]);
- }
- ln=0; // reset to 0
- }
- return 0;
-}
-
-/*====================================
- * Removed item_value_db, don't re-add
- *------------------------------------
- */
-static void itemdb_read(void)
-{
-#ifndef TXT_ONLY
- if (db_use_sqldbs)
- itemdb_read_sqldb();
- else
-#endif
- itemdb_readdb();
-
- itemdb_read_itemgroup();
- itemdb_read_itemavail();
- itemdb_read_noequip();
- itemdb_read_itemtrade();
- if (battle_config.cardillust_read_grffile)
- itemdb_read_cardillustnametable();
- if (battle_config.item_equip_override_grffile)
- itemdb_read_itemslottable();
- if (battle_config.item_slots_override_grffile)
- itemdb_read_itemslotcounttable();
- if (battle_config.item_name_override_grffile)
- itemdb_read_itemnametable();
-}
-
-/*==========================================
- * Initialize / Finalize
- *------------------------------------------
- */
-static int itemdb_final_sub (DBKey key,void *data,va_list ap)
-{
- int flag;
- struct item_data *id = (struct item_data *)data;
-
- flag = va_arg(ap, int);
- if (id->script)
- {
- script_free_code(id->script);
- id->script = NULL;
- }
- if (id->equip_script)
- {
- script_free_code(id->equip_script);
- id->equip_script = NULL;
- }
- if (id->unequip_script)
- {
- script_free_code(id->unequip_script);
- id->unequip_script = NULL;
- }
- // Whether to clear the item data (exception: do not clear the dummy item data
- if (flag && id != &dummy_item)
- aFree(id);
-
- return 0;
-}
-
-void itemdb_reload(void)
-{
- //Just read, the function takes care of freeing scripts.
- itemdb_read();
-}
-
-void do_final_itemdb(void)
-{
- item_db->destroy(item_db, itemdb_final_sub, 1);
- if (dummy_item.script) {
- script_free_code(dummy_item.script);
- dummy_item.script = NULL;
- }
- if (dummy_item.equip_script) {
- script_free_code(dummy_item.equip_script);
- dummy_item.equip_script = NULL;
- }
- if (dummy_item.unequip_script) {
- script_free_code(dummy_item.unequip_script);
- dummy_item.unequip_script = NULL;
- }
-}
-
-int do_init_itemdb(void)
-{
- item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
- create_dummy_data(); //Dummy data item.
- itemdb_read();
-
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "../common/grfio.h"
+#include "../common/strlib.h"
+#include "map.h"
+#include "battle.h"
+#include "itemdb.h"
+#include "script.h"
+#include "pc.h"
+
+// ** ITEMDB_OVERRIDE_NAME_VERBOSE **
+// 定義すると、itemdb.txtとgrfで名前が異なる場合、表示します.
+//#define ITEMDB_OVERRIDE_NAME_VERBOSE 1
+
+static struct dbt* item_db;
+
+static struct item_group itemgroup_db[MAX_ITEMGROUP];
+
+struct item_data dummy_item; //This is the default dummy item used for non-existant items. [Skotlex]
+
+/*==========================================
+ * 名前で検索用
+ *------------------------------------------
+ */
+// name = item alias, so we should find items aliases first. if not found then look for "jname" (full name)
+int itemdb_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct item_data *item=(struct item_data *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct item_data **);
+ if(item == &dummy_item) return 0;
+ if( strcmpi(item->name,str)==0 ) //by lupus
+ *dst=item;
+ return 0;
+}
+
+/*==========================================
+ * 名前で検索用
+ *------------------------------------------
+ */
+int itemdb_searchjname_sub(int key,void *data,va_list ap)
+{
+ struct item_data *item=(struct item_data *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct item_data **);
+ if(item == &dummy_item) return 0;
+ if( strcmpi(item->jname,str)==0 )
+ *dst=item;
+ return 0;
+}
+
+/*==========================================
+ * 名前で検索
+ *------------------------------------------
+ */
+struct item_data* itemdb_searchname(const char *str)
+{
+ struct item_data *item=NULL;
+ item_db->foreach(item_db,itemdb_searchname_sub,str,&item);
+ return item;
+}
+
+static int itemdb_searchname_array_sub(DBKey key,void * data,va_list ap)
+{
+ struct item_data *item=(struct item_data *)data;
+ char *str;
+ str=va_arg(ap,char *);
+ if (item == &dummy_item)
+ return 1; //Invalid item.
+ if(stristr(item->jname,str))
+ return 0;
+ if(stristr(item->name,str))
+ return 0;
+ return strcmpi(item->jname,str);
+}
+
+/*==========================================
+ * Founds up to N matches. Returns number of matches [Skotlex]
+ *------------------------------------------
+ */
+int itemdb_searchname_array(struct item_data** data, int size, const char *str)
+{
+ return item_db->getall(item_db,(void**)data,size,itemdb_searchname_array_sub,str);
+}
+
+
+/*==========================================
+ * 箱系アイテム検索
+ *------------------------------------------
+ */
+int itemdb_searchrandomid(int group)
+{
+ if(group<1 || group>=MAX_ITEMGROUP) {
+ if (battle_config.error_log)
+ ShowError("itemdb_searchrandomid: Invalid group id %d\n", group);
+ return UNKNOWN_ITEM_ID;
+ }
+ if (itemgroup_db[group].qty)
+ return itemgroup_db[group].nameid[rand()%itemgroup_db[group].qty];
+
+ if (battle_config.error_log)
+ ShowError("itemdb_searchrandomid: No item entries for group id %d\n", group);
+ return UNKNOWN_ITEM_ID;
+}
+
+/*==========================================
+ * Calculates total item-group related bonuses for the given item. [Skotlex]
+ *------------------------------------------
+ */
+int itemdb_group_bonus(struct map_session_data *sd, int itemid)
+{
+ int bonus = 0, i, j;
+ for (i=0; i < MAX_ITEMGROUP; i++) {
+ if (!sd->itemgrouphealrate[i])
+ continue;
+ for (j=0; j < itemgroup_db[i].qty; j++) {
+ if (itemgroup_db[i].nameid[j] == itemid)
+ {
+ bonus += sd->itemgrouphealrate[i];
+ continue;
+ }
+ }
+ }
+ return bonus;
+}
+
+/*==========================================
+ * DBの存在確認
+ *------------------------------------------
+ */
+struct item_data* itemdb_exists(int nameid)
+{
+ struct item_data* id;
+ if (!nameid) return NULL;
+ id = idb_get(item_db,nameid);
+ //Adjust nameid in case it's used outside. [Skotlex]
+ if (id == &dummy_item)
+ dummy_item.nameid = nameid;
+ return id;
+}
+
+/*==========================================
+ * Converts the jobid from the format in itemdb
+ * to the format used by the map server. [Skotlex]
+ *------------------------------------------
+ */
+static void itemdb_jobid2mapid(unsigned int *bclass, unsigned int jobmask)
+{
+ int i;
+ bclass[0]= bclass[1]= bclass[2]= 0;
+ //Base classes
+ if (jobmask & 1<<JOB_NOVICE)
+ { //Both Novice/Super-Novice are counted with the same ID
+ bclass[0] |= 1<<MAPID_NOVICE;
+ bclass[1] |= 1<<MAPID_NOVICE;
+ }
+ for (i = JOB_NOVICE+1; i <= JOB_THIEF; i++)
+ {
+ if (jobmask & 1<<i)
+ bclass[0] |= 1<<(MAPID_NOVICE+i);
+ }
+ //2-1 classes
+ if (jobmask & 1<<JOB_KNIGHT)
+ bclass[1] |= 1<<MAPID_SWORDMAN;
+ if (jobmask & 1<<JOB_PRIEST)
+ bclass[1] |= 1<<MAPID_ACOLYTE;
+ if (jobmask & 1<<JOB_WIZARD)
+ bclass[1] |= 1<<MAPID_MAGE;
+ if (jobmask & 1<<JOB_BLACKSMITH)
+ bclass[1] |= 1<<MAPID_MERCHANT;
+ if (jobmask & 1<<JOB_HUNTER)
+ bclass[1] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_ASSASSIN)
+ bclass[1] |= 1<<MAPID_THIEF;
+ //2-2 classes
+ if (jobmask & 1<<JOB_CRUSADER)
+ bclass[2] |= 1<<MAPID_SWORDMAN;
+ if (jobmask & 1<<JOB_MONK)
+ bclass[2] |= 1<<MAPID_ACOLYTE;
+ if (jobmask & 1<<JOB_SAGE)
+ bclass[2] |= 1<<MAPID_MAGE;
+ if (jobmask & 1<<JOB_ALCHEMIST)
+ bclass[2] |= 1<<MAPID_MERCHANT;
+ if (jobmask & 1<<JOB_BARD)
+ bclass[2] |= 1<<MAPID_ARCHER;
+// Bard/Dancer share the same slot now.
+// if (jobmask & 1<<JOB_DANCER)
+// bclass[2] |= 1<<MAPID_ARCHER;
+ if (jobmask & 1<<JOB_ROGUE)
+ bclass[2] |= 1<<MAPID_THIEF;
+ //Special classes that don't fit above.
+ if (jobmask & 1<<21) //Taekwon boy
+ bclass[0] |= 1<<MAPID_TAEKWON;
+ if (jobmask & 1<<22) //Star Gladiator
+ bclass[1] |= 1<<MAPID_TAEKWON;
+ if (jobmask & 1<<23) //Soul Linker
+ bclass[2] |= 1<<MAPID_TAEKWON;
+ if (jobmask & 1<<JOB_GUNSLINGER)
+ bclass[0] |= 1<<MAPID_GUNSLINGER;
+ if (jobmask & 1<<JOB_NINJA)
+ bclass[0] |= 1<<MAPID_NINJA;
+}
+
+static void create_dummy_data(void) {
+ malloc_set(&dummy_item, 0, sizeof(struct item_data));
+ dummy_item.nameid=500;
+ dummy_item.weight=1;
+ dummy_item.value_sell = 1;
+ dummy_item.type=3; //Etc item
+ strncpy(dummy_item.name,"UNKNOWN_ITEM",ITEM_NAME_LENGTH-1);
+ strncpy(dummy_item.jname,"UNKNOWN_ITEM",ITEM_NAME_LENGTH-1);
+ dummy_item.view_id = UNKNOWN_ITEM_ID;
+}
+
+static void* create_item_data(DBKey key, va_list args) {
+ struct item_data *id;
+ id=(struct item_data *)aCalloc(1,sizeof(struct item_data));
+ id->nameid = key.i;
+ id->weight=1;
+ id->type=IT_ETC;
+ return id;
+}
+
+/*==========================================
+ * Loads (and creates if not found) an item from the db.
+ *------------------------------------------
+ */
+struct item_data* itemdb_load(int nameid)
+{
+ struct item_data *id = idb_ensure(item_db,nameid,create_item_data);
+ if (id == &dummy_item)
+ { //Remove dummy_item, replace by real data.
+ DBKey key;
+ key.i = nameid;
+ idb_remove(item_db,nameid);
+ id = create_item_data(key, NULL);
+ idb_put(item_db,nameid,id);
+ }
+ return id;
+}
+
+static void* return_dummy_data(DBKey key, va_list args) {
+ if (battle_config.error_log)
+ ShowWarning("itemdb_search: Item ID %d does not exists in the item_db. Using dummy data.\n", key.i);
+ dummy_item.nameid = key.i;
+ return &dummy_item;
+}
+
+/*==========================================
+ * Loads an item from the db. If not found, it will return the dummy item.
+ *------------------------------------------
+ */
+struct item_data* itemdb_search(int nameid)
+{
+ return idb_ensure(item_db,nameid,return_dummy_data);
+}
+
+/*==========================================
+ * Returns if given item is a player-equippable piece.
+ *------------------------------------------
+ */
+int itemdb_isequip(int nameid)
+{
+ int type=itemdb_type(nameid);
+ switch (type) {
+ case IT_WEAPON:
+ case IT_ARMOR:
+ case IT_AMMO:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*==========================================
+ * Alternate version of itemdb_isequip
+ *------------------------------------------
+ */
+int itemdb_isequip2(struct item_data *data)
+{
+ nullpo_retr(0, data);
+ switch(data->type) {
+ case IT_WEAPON:
+ case IT_ARMOR:
+ case IT_AMMO:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*==========================================
+* Returns if given item's type is stackable.
+*------------------------------------------
+*/
+int itemdb_isstackable(int nameid)
+{
+ int type=itemdb_type(nameid);
+ switch(type) {
+ case IT_WEAPON:
+ case IT_ARMOR:
+ case IT_PETEGG:
+ case IT_PETARMOR:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+/*==========================================
+* Alternate version of itemdb_isstackable
+*------------------------------------------
+*/
+int itemdb_isstackable2(struct item_data *data)
+{
+ nullpo_retr(0, data);
+ switch(data->type) {
+ case IT_WEAPON:
+ case IT_ARMOR:
+ case IT_PETEGG:
+ case IT_PETARMOR:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+
+/*==========================================
+ * Trade Restriction functions [Skotlex]
+ *------------------------------------------
+ */
+int itemdb_isdropable_sub(struct item_data *item, int gmlv, int unused)
+{
+ return (item && (!(item->flag.trade_restriction&1) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_cantrade_sub(struct item_data* item, int gmlv, int gmlv2)
+{
+ return (item && (!(item->flag.trade_restriction&2) || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+}
+
+int itemdb_canpartnertrade_sub(struct item_data* item, int gmlv, int gmlv2)
+{
+ return (item && (item->flag.trade_restriction&4 || gmlv >= item->gm_lv_trade_override || gmlv2 >= item->gm_lv_trade_override));
+}
+
+int itemdb_cansell_sub(struct item_data* item, int gmlv, int unused)
+{
+ return (item && (!(item->flag.trade_restriction&8) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_cancartstore_sub(struct item_data* item, int gmlv, int unused)
+{
+ return (item && (!(item->flag.trade_restriction&16) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_canstore_sub(struct item_data* item, int gmlv, int unused)
+{
+ return (item && (!(item->flag.trade_restriction&32) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_canguildstore_sub(struct item_data* item, int gmlv, int unused)
+{
+ return (item && (!(item->flag.trade_restriction&64) || gmlv >= item->gm_lv_trade_override));
+}
+
+int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int))
+{
+ struct item_data* item_data = itemdb_search(item->nameid);
+ int i;
+
+ if (!func(item_data, gmlv, gmlv2))
+ return 0;
+
+ if(item_data->slot == 0 || itemdb_isspecial(item->card[0]))
+ return 1;
+
+ for(i = 0; i < item_data->slot; i++) {
+ if (!item->card[i]) continue;
+ if (!func(itemdb_search(item->card[i]), gmlv, gmlv2))
+ return 0;
+ }
+ return 1;
+}
+
+/*==========================================
+ * Specifies if item-type should drop unidentified.
+ *------------------------------------------
+ */
+int itemdb_isidentified(int nameid)
+{
+ int type=itemdb_type(nameid);
+ switch (type) {
+ case IT_WEAPON:
+ case IT_ARMOR:
+ case IT_PETARMOR:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+/*==========================================
+ * アイテム使用可能フラグのオーバーライド
+ *------------------------------------------
+ */
+static int itemdb_read_itemavail (void)
+{
+ FILE *fp;
+ int nameid, j, k, ln = 0;
+ char line[1024], *str[10], *p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_avail.txt", db_path);
+ if ((fp = fopen(line,"r")) == NULL) {
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ malloc_tsetdword(str, 0, sizeof(str));
+ for (j = 0, p = line; j < 2 && p; j++) {
+ str[j] = p;
+ p = strchr(p, ',');
+ if(p) *p++ = 0;
+ }
+
+ if (j < 2 || str[0] == NULL ||
+ (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid)))
+ continue;
+
+ k = atoi(str[1]);
+ if (k > 0) {
+ id->flag.available = 1;
+ id->view_id = k;
+ } else
+ id->flag.available = 0;
+ ln++;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_avail.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * read item group data
+ *------------------------------------------
+ */
+static void itemdb_read_itemgroup_sub(const char* filename)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int groupid,j,k,nameid;
+ char *str[3],*p;
+ char w1[1024], w2[1024];
+
+ if( (fp=fopen(filename,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return;
+ }
+
+ while(fgets(line,1020,fp)){
+ ln++;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ if(strstr(line,"import")) {
+ if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2 &&
+ strcmpi(w1, "import") == 0) {
+ itemdb_read_itemgroup_sub(w2);
+ continue;
+ }
+ }
+ malloc_tsetdword(str,0,sizeof(str));
+ for(j=0,p=line;j<3 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+ if (j<3) {
+ if (j>1) //Or else it barks on blank lines...
+ ShowWarning("itemdb_read_itemgroup: Insufficient fields for entry at %s:%d\n", filename, ln);
+ continue;
+ }
+ groupid = atoi(str[0]);
+ if (groupid < 0 || groupid >= MAX_ITEMGROUP) {
+ ShowWarning("itemdb_read_itemgroup: Invalid group %d in %s:%d\n", groupid, filename, ln);
+ continue;
+ }
+ nameid = atoi(str[1]);
+ if (!itemdb_exists(nameid)) {
+ ShowWarning("itemdb_read_itemgroup: Non-existant item %d in %s:%d\n", nameid, filename, ln);
+ continue;
+ }
+ k = atoi(str[2]);
+ if (itemgroup_db[groupid].qty+k > MAX_RANDITEM) {
+ ShowWarning("itemdb_read_itemgroup: Group %d is full (%d entries) in %s:%d\n", groupid, MAX_RANDITEM, filename, ln);
+ continue;
+ }
+ for(j=0;j<k;j++)
+ itemgroup_db[groupid].nameid[itemgroup_db[groupid].qty++] = nameid;
+ }
+ fclose(fp);
+ return;
+}
+
+static void itemdb_read_itemgroup(void)
+{
+ char path[256];
+ int i;
+ const char* groups[] = {
+ "Blue Box",
+ "Violet Box",
+ "Card Album",
+ "Gift Box",
+ "Scroll Box",
+ "Finding Ore",
+ "Cookie Bag",
+ "Potion",
+ "Herbs",
+ "Fruits",
+ "Meat",
+ "Candy",
+ "Juice",
+ "Fish",
+ "Boxes",
+ "Gemstone",
+ "Jellopy",
+ "Ore",
+ "Food",
+ "Recovery",
+ "Minerals",
+ "Taming",
+ "Scrolls",
+ "Quivers",
+ "Masks",
+ "Accesory",
+ "Jewels",
+ "Gift Box 1",
+ "Gift Box 2",
+ "Gift Box 3",
+ "Gift Box 4",
+ "Egg Boy",
+ "Egg Girl",
+ "Gift Box China",
+ "Lotto Box",
+ };
+ malloc_tsetdword(&itemgroup_db, 0, sizeof(itemgroup_db));
+ snprintf(path, 255, "%s/item_group_db.txt", db_path);
+ itemdb_read_itemgroup_sub(path);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","item_group_db.txt");
+ if (battle_config.etc_log) {
+ for (i = 1; i < MAX_ITEMGROUP; i++)
+ ShowInfo("Group %s: %d entries.\n", groups[i-1], itemgroup_db[i].qty);
+ }
+ return;
+}
+/*==========================================
+ * アイテムの名前テーブルを読み込む
+ *------------------------------------------
+ */
+static int itemdb_read_itemnametable(void)
+{
+ char *buf,*p;
+ int s;
+
+ buf=(char *) grfio_reads("data\\idnum2itemdisplaynametable.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ int nameid;
+ char buf2[64]; //Why 64? What's this for, other than holding an item's name? [Skotlex]
+
+ if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
+
+#ifdef ITEMDB_OVERRIDE_NAME_VERBOSE
+ if( itemdb_exists(nameid) &&
+ strncmp(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH)!=0 ){
+ ShowNotice("[override] %d %s => %s\n",nameid
+ ,itemdb_search(nameid)->jname,buf2);
+ }
+#endif
+
+ strncpy(itemdb_search(nameid)->jname,buf2,ITEM_NAME_LENGTH-1);
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\idnum2itemdisplaynametable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * カードイラストのリソース名前テーブルを読み込む
+ *------------------------------------------
+ */
+static int itemdb_read_cardillustnametable(void)
+{
+ char *buf,*p;
+ int s;
+
+ buf=(char *) grfio_reads("data\\num2cardillustnametable.txt",&s);
+
+ if(buf==NULL)
+ return -1;
+
+ buf[s]=0;
+ for(p=buf;p-buf<s;){
+ int nameid;
+ char buf2[64];
+
+ if( sscanf(p,"%d#%[^#]#",&nameid,buf2)==2 ){
+ strcat(buf2,".bmp");
+ memcpy(itemdb_search(nameid)->cardillustname,buf2,64);
+ }
+
+ p=strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\num2cardillustnametable.txt");
+
+ return 0;
+}
+
+//
+// 初期化
+//
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int itemdb_read_itemslottable(void)
+{
+ char *buf, *p;
+ int s;
+
+ buf = (char *)grfio_reads("data\\itemslottable.txt", &s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+ for (p = buf; p - buf < s; ) {
+ int nameid, equip;
+ struct item_data* item;
+ sscanf(p, "%d#%d#", &nameid, &equip);
+ item = itemdb_search(nameid);
+ if (equip && item && itemdb_isequip2(item))
+ item->equip = equip;
+ p = strchr(p, 10);
+ if(!p) break;
+ p++;
+ p=strchr(p, 10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\itemslottable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+static int itemdb_read_itemslotcounttable(void)
+{
+ char *buf, *p;
+ int s;
+
+ buf = (char *)grfio_reads("data\\itemslotcounttable.txt", &s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+ for (p = buf; p - buf < s;){
+ int nameid, slot;
+ sscanf(p, "%d#%d#", &nameid, &slot);
+ if (slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_read_itemslotcounttable: Item %d specifies %d slots, but the server only supports up to %d\n", nameid, slot, MAX_SLOTS);
+ slot = MAX_SLOTS;
+ }
+ itemdb_slot(nameid) = slot;
+ p = strchr(p,10);
+ if(!p) break;
+ p++;
+ p = strchr(p,10);
+ if(!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n", "data\\itemslotcounttable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ * 装備制限ファイル読み出し
+ *------------------------------------------
+ */
+static int itemdb_read_noequip(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0;
+ int nameid,j;
+ char *str[32],*p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_noequip.txt", db_path);
+ if( (fp=fopen(line,"r"))==NULL ){
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+ while(fgets(line,1020,fp)){
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ malloc_tsetdword(str,0,sizeof(str));
+ for(j=0,p=line;j<2 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p) *p++=0;
+ }
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<=0 || !(id=itemdb_exists(nameid)))
+ continue;
+
+ id->flag.no_equip=atoi(str[1]);
+
+ ln++;
+
+ }
+ fclose(fp);
+ if (ln > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"item_noequip.txt");
+ }
+ return 0;
+}
+
+/*==========================================
+ * Reads item trade restrictions [Skotlex]
+ *------------------------------------------
+ */
+static int itemdb_read_itemtrade(void)
+{
+ FILE *fp;
+ int nameid, j, flag, gmlv, ln = 0;
+ char line[1024], *str[10], *p;
+ struct item_data *id;
+
+ sprintf(line, "%s/item_trade.txt", db_path);
+ if ((fp = fopen(line,"r")) == NULL) {
+ ShowError("can't read %s\n", line);
+ return -1;
+ }
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+ malloc_tsetdword(str, 0, sizeof(str));
+ for (j = 0, p = line; j < 3 && p; j++) {
+ str[j] = p;
+ p = strchr(p, ',');
+ if(p) *p++ = 0;
+ }
+
+ if (j < 3 || str[0] == NULL ||
+ (nameid = atoi(str[0])) < 0 || !(id = itemdb_exists(nameid)))
+ continue;
+
+ flag = atoi(str[1]);
+ gmlv = atoi(str[2]);
+
+ if (flag > 0 && flag < 128 && gmlv > 0) { //Check range
+ id->flag.trade_restriction = flag;
+ id->gm_lv_trade_override = gmlv;
+ ln++;
+ }
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, "item_trade.txt");
+
+ return 0;
+}
+
+/*======================================
+ * Applies gender restrictions according to settings. [Skotlex]
+ *======================================
+ */
+static int itemdb_gendercheck(struct item_data *id)
+{
+ if (id->nameid == WEDDING_RING_M) //Grom Ring
+ return 1;
+ if (id->nameid == WEDDING_RING_F) //Bride Ring
+ return 0;
+ if (id->look == W_MUSICAL && id->type == IT_WEAPON) //Musical instruments are always male-only
+ return 1;
+ if (id->look == W_WHIP && id->type == IT_WEAPON) //Whips are always female-only
+ return 0;
+
+ return (battle_config.ignore_items_gender?2:id->sex);
+}
+#ifndef TXT_ONLY
+
+/*======================================
+* SQL
+*===================================
+*/
+static int itemdb_read_sqldb(void)
+{
+ unsigned short nameid;
+ struct item_data *id;
+ char script[65535 + 2 + 1]; // Maximum length of MySQL TEXT type (65535) + 2 bytes for curly brackets + 1 byte for terminator
+ char *item_db_name[] = { item_db_db, item_db2_db };
+ long unsigned int ln = 0;
+ int i;
+
+ // ----------
+
+ for (i = 0; i < 2; i++) {
+ sprintf(tmp_sql, "SELECT * FROM `%s`", item_db_name[i]);
+
+ // Execute the query; if the query execution succeeded...
+ if (mysql_query(&mmysql_handle, tmp_sql) == 0) {
+ sql_res = mysql_store_result(&mmysql_handle);
+
+ // If the storage of the query result succeeded...
+ if (sql_res) {
+ // Parse each row in the query result into sql_row
+ while ((sql_row = mysql_fetch_row(sql_res)))
+ { /*Table structure is:
+ 00 id
+ 01 name_english
+ 02 name_japanese
+ 03 type
+ 04 price_buy
+ 05 price_sell
+ 06 weight
+ 07 attack
+ 08 defence
+ 09 range
+ 10 slots
+ 11 equip_jobs
+ 12 equip_upper
+ 13 equip_genders
+ 14 equip_locations
+ 15 weapon_level
+ 16 equip_level
+ 17 refineable
+ 18 view
+ 19 script
+ 20 equip_script
+ 21 unequip_script
+ */
+ nameid = atoi(sql_row[0]);
+
+ // If the identifier is not within the valid range, process the next row
+ if (nameid == 0)
+ continue;
+
+ ln++;
+
+ // ----------
+ id = itemdb_load(nameid);
+
+ strncpy(id->name, sql_row[1], ITEM_NAME_LENGTH-1);
+ strncpy(id->jname, sql_row[2], ITEM_NAME_LENGTH-1);
+
+ id->type = atoi(sql_row[3]);
+ if (id->type == IT_DELAYCONSUME)
+ { //Items that are consumed upon target confirmation
+ //(yggdrasil leaf, spells & pet lures) [Skotlex]
+ id->type = IT_USABLE;
+ id->flag.delay_consume=1;
+ }
+
+ // If price_buy is not NULL and price_sell is not NULL...
+ if ((sql_row[4] != NULL) && (sql_row[5] != NULL)) {
+ id->value_buy = atoi(sql_row[4]);
+ id->value_sell = atoi(sql_row[5]);
+ }
+ // If price_buy is not NULL and price_sell is NULL...
+ else if ((sql_row[4] != NULL) && (sql_row[5] == NULL)) {
+ id->value_buy = atoi(sql_row[4]);
+ id->value_sell = atoi(sql_row[4]) / 2;
+ }
+ // If price_buy is NULL and price_sell is not NULL...
+ else if ((sql_row[4] == NULL) && (sql_row[5] != NULL)) {
+ id->value_buy = atoi(sql_row[5]) * 2;
+ id->value_sell = atoi(sql_row[5]);
+ }
+ // If price_buy is NULL and price_sell is NULL...
+ if ((sql_row[4] == NULL) && (sql_row[5] == NULL)) {
+ id->value_buy = 0;
+ id->value_sell = 0;
+ }
+
+ id->weight = atoi(sql_row[6]);
+ id->atk = (sql_row[7] != NULL) ? atoi(sql_row[7]) : 0;
+ id->def = (sql_row[8] != NULL) ? atoi(sql_row[8]) : 0;
+ id->range = (sql_row[9] != NULL) ? atoi(sql_row[9]) : 0;
+ id->slot = (sql_row[10] != NULL) ? atoi(sql_row[10]) : 0;
+ if (id->slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_read_sqldb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
+ id->slot = MAX_SLOTS;
+ }
+ itemdb_jobid2mapid(id->class_base, (sql_row[11] != NULL) ? (unsigned int)strtoul(sql_row[11], NULL, 0) : 0);
+ id->class_upper= (sql_row[12] != NULL) ? atoi(sql_row[12]) : 0;
+ id->sex = (sql_row[13] != NULL) ? atoi(sql_row[13]) : 0;
+ id->equip = (sql_row[14] != NULL) ? atoi(sql_row[14]) : 0;
+ if (!id->equip && itemdb_isequip2(id))
+ {
+ ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname);
+ id->type = 3;
+ }
+ id->wlv = (sql_row[15] != NULL) ? atoi(sql_row[15]) : 0;
+ id->elv = (sql_row[16] != NULL) ? atoi(sql_row[16]) : 0;
+ id->flag.no_refine = (sql_row[17] == NULL || atoi(sql_row[17]) == 1)?0:1;
+ id->look = (sql_row[18] != NULL) ? atoi(sql_row[18]) : 0;
+ id->view_id = 0;
+ id->sex = itemdb_gendercheck(id); //Apply gender filtering.
+
+ // ----------
+
+ if (id->script)
+ script_free_code(id->script);
+ if (sql_row[19] != NULL) {
+ if (sql_row[19][0] == '{')
+ id->script = parse_script((unsigned char *) sql_row[19],item_db_name[i], 0);
+ else {
+ sprintf(script, "{%s}", sql_row[19]);
+ id->script = parse_script((unsigned char *) script, item_db_name[i], 0);
+ }
+ } else id->script = NULL;
+
+ if (id->equip_script)
+ script_free_code(id->equip_script);
+ if (sql_row[20] != NULL) {
+ if (sql_row[20][0] == '{')
+ id->equip_script = parse_script((unsigned char *) sql_row[20], item_db_name[i], 0);
+ else {
+ sprintf(script, "{%s}", sql_row[20]);
+ id->equip_script = parse_script((unsigned char *) script, item_db_name[i], 0);
+ }
+ } else id->equip_script = NULL;
+
+ if (id->unequip_script)
+ script_free_code(id->unequip_script);
+ if (sql_row[21] != NULL) {
+ if (sql_row[21][0] == '{')
+ id->unequip_script = parse_script((unsigned char *) sql_row[21],item_db_name[i], 0);
+ else {
+ sprintf(script, "{%s}", sql_row[21]);
+ id->unequip_script = parse_script((unsigned char *) script, item_db_name[i], 0);
+ }
+ } else id->unequip_script = NULL;
+
+ // ----------
+
+ id->flag.available = 1;
+ id->flag.value_notdc = 0;
+ id->flag.value_notoc = 0;
+ }
+
+ ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, item_db_name[i]);
+ ln = 0;
+ } else {
+ ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ // Free the query result
+ mysql_free_result(sql_res);
+ } else {
+ ShowSQL("DB error (%s) - %s\n",item_db_name[i], mysql_error(&mmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ return 0;
+}
+#endif /* not TXT_ONLY */
+
+/*==========================================
+ * アイテムデータベースの読み込み
+ *------------------------------------------
+ */
+static int itemdb_readdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int ln=0,lines=0;
+ int nameid,j;
+ char *str[32],*p,*np;
+ struct item_data *id;
+ int i=0;
+ char *filename[]={ "item_db.txt","item_db2.txt" };
+
+ for(i=0;i<2;i++){
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(i>0)
+ continue;
+ ShowFatalError("can't read %s\n",line);
+ exit(1);
+ }
+
+ lines=0;
+ while(fgets(line,1020,fp)){
+ lines++;
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+ malloc_tsetdword(str,0,sizeof(str));
+ for(j=0,np=p=line;j<19 && p;j++){
+ str[j]=p;
+ p=strchr(p,',');
+ if(p){ *p++=0; np=p; }
+ }
+ if(str[0]==NULL)
+ continue;
+
+ nameid=atoi(str[0]);
+ if(nameid<=0)
+ continue;
+ if (j < 19)
+ { //Crash-fix on broken item lines. [Skotlex]
+ ShowWarning("Reading %s: Insufficient fields for item with id %d, skipping.\n", filename[i], nameid);
+ continue;
+ }
+ ln++;
+
+ //ID,Name,Jname,Type,Price,Sell,Weight,ATK,DEF,Range,Slot,Job,Job Upper,Gender,Loc,wLV,eLV,refineable,View
+ id=itemdb_load(nameid);
+ strncpy(id->name, str[1], ITEM_NAME_LENGTH-1);
+ strncpy(id->jname, str[2], ITEM_NAME_LENGTH-1);
+ id->type=atoi(str[3]);
+ if (id->type == IT_DELAYCONSUME)
+ { //Items that are consumed upon target confirmation
+ //(yggdrasil leaf, spells & pet lures) [Skotlex]
+ id->type = IT_USABLE;
+ id->flag.delay_consume=1;
+ }
+
+ {
+ int buy = atoi(str[4]), sell = atoi(str[5]);
+ // if buying price > selling price * 2 consider it valid and don't change it [celest]
+ if (buy && sell && buy > sell*2){
+ id->value_buy = buy;
+ id->value_sell = sell;
+ } else {
+ // buy≠sell*2 は item_value_db.txt で指定してください。
+ if (sell) { // sell値を優先とする
+ id->value_buy = sell*2;
+ id->value_sell = sell;
+ } else {
+ id->value_buy = buy;
+ id->value_sell = buy/2;
+ }
+ }
+ // check for bad prices that can possibly cause exploits
+ if (id->value_buy*75/100 < id->value_sell*124/100) {
+ ShowWarning ("Item %s [%d] buying:%d < selling:%d\n",
+ id->name, id->nameid, id->value_buy*75/100, id->value_sell*124/100);
+ }
+ }
+ id->weight=atoi(str[6]);
+ id->atk=atoi(str[7]);
+ id->def=atoi(str[8]);
+ id->range=atoi(str[9]);
+ id->slot=atoi(str[10]);
+ if (id->slot > MAX_SLOTS)
+ {
+ ShowWarning("itemdb_readdb: Item %d (%s) specifies %d slots, but the server only supports up to %d\n", nameid, id->jname, id->slot, MAX_SLOTS);
+ id->slot = MAX_SLOTS;
+ }
+ itemdb_jobid2mapid(id->class_base, (unsigned int)strtoul(str[11],NULL,0));
+ id->class_upper = atoi(str[12]);
+ id->sex = atoi(str[13]);
+ if(id->equip != atoi(str[14])){
+ id->equip=atoi(str[14]);
+ }
+ if (!id->equip && itemdb_isequip2(id))
+ {
+ ShowWarning("Item %d (%s) is an equipment with no equip-field! Making it an etc item.\n", nameid, id->jname);
+ id->type = 3;
+ }
+ id->wlv=atoi(str[15]);
+ id->elv=atoi(str[16]);
+ id->flag.no_refine = atoi(str[17])?0:1; //If the refine column is 1, no_refine is 0
+ id->look=atoi(str[18]);
+ id->flag.available=1;
+ id->flag.value_notdc=0;
+ id->flag.value_notoc=0;
+ id->view_id=0;
+ id->sex = itemdb_gendercheck(id); //Apply gender filtering.
+
+ if (id->script) {
+ script_free_code(id->script);
+ id->script=NULL;
+ }
+ if (id->equip_script) {
+ script_free_code(id->equip_script);
+ id->equip_script=NULL;
+ }
+ if (id->unequip_script) {
+ script_free_code(id->unequip_script);
+ id->unequip_script=NULL;
+ }
+
+ if((p=strchr(np,'{'))==NULL)
+ continue;
+
+ str[19] = p; //Script
+ np = strchr(p,'}');
+
+ while (np && np[1] && np[1] != ',')
+ np = strchr(np+1,'}'); //Jump close brackets until the next field is found.
+ if (!np || !np[1]) {
+ //Couldn't find the end of the script field.
+ id->script = parse_script((unsigned char *) str[19],filename[i],lines);
+ continue;
+ }
+ np[1] = '\0'; //Set end of script
+ id->script = parse_script((unsigned char *) str[19],filename[i],lines);
+ np+=2; //Skip to next field
+
+ if(!np || (p=strchr(np,'{'))==NULL)
+ continue;
+
+ str[20] = p; //Equip Script
+ np = strchr(p,'}');
+
+ while (np && np[1] && np[1] != ',')
+ np = strchr(np+1,'}'); //Jump close brackets until the next field is found.
+ if (!np || !np[1]) {
+ //Couldn't find the end of the script field.
+ id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines);
+ continue;
+ }
+
+ np[1] = '\0'; //Set end of script
+ id->equip_script = parse_script((unsigned char *) str[20],filename[i],lines);
+ np+=2; //Skip comma, to next field
+
+ if(!np || (p=strchr(np,'{'))==NULL)
+ continue;
+ //Unequip script, last column.
+ id->unequip_script = parse_script((unsigned char *) p,filename[i],lines);
+ }
+ fclose(fp);
+ if (ln > 0) {
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,filename[i]);
+ }
+ ln=0; // reset to 0
+ }
+ return 0;
+}
+
+/*====================================
+ * Removed item_value_db, don't re-add
+ *------------------------------------
+ */
+static void itemdb_read(void)
+{
+#ifndef TXT_ONLY
+ if (db_use_sqldbs)
+ itemdb_read_sqldb();
+ else
+#endif
+ itemdb_readdb();
+
+ itemdb_read_itemgroup();
+ itemdb_read_itemavail();
+ itemdb_read_noequip();
+ itemdb_read_itemtrade();
+ if (battle_config.cardillust_read_grffile)
+ itemdb_read_cardillustnametable();
+ if (battle_config.item_equip_override_grffile)
+ itemdb_read_itemslottable();
+ if (battle_config.item_slots_override_grffile)
+ itemdb_read_itemslotcounttable();
+ if (battle_config.item_name_override_grffile)
+ itemdb_read_itemnametable();
+}
+
+/*==========================================
+ * Initialize / Finalize
+ *------------------------------------------
+ */
+static int itemdb_final_sub (DBKey key,void *data,va_list ap)
+{
+ int flag;
+ struct item_data *id = (struct item_data *)data;
+
+ flag = va_arg(ap, int);
+ if (id->script)
+ {
+ script_free_code(id->script);
+ id->script = NULL;
+ }
+ if (id->equip_script)
+ {
+ script_free_code(id->equip_script);
+ id->equip_script = NULL;
+ }
+ if (id->unequip_script)
+ {
+ script_free_code(id->unequip_script);
+ id->unequip_script = NULL;
+ }
+ // Whether to clear the item data (exception: do not clear the dummy item data
+ if (flag && id != &dummy_item)
+ aFree(id);
+
+ return 0;
+}
+
+void itemdb_reload(void)
+{
+ //Just read, the function takes care of freeing scripts.
+ itemdb_read();
+}
+
+void do_final_itemdb(void)
+{
+ item_db->destroy(item_db, itemdb_final_sub, 1);
+ if (dummy_item.script) {
+ script_free_code(dummy_item.script);
+ dummy_item.script = NULL;
+ }
+ if (dummy_item.equip_script) {
+ script_free_code(dummy_item.equip_script);
+ dummy_item.equip_script = NULL;
+ }
+ if (dummy_item.unequip_script) {
+ script_free_code(dummy_item.unequip_script);
+ dummy_item.unequip_script = NULL;
+ }
+}
+
+int do_init_itemdb(void)
+{
+ item_db = db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
+ create_dummy_data(); //Dummy data item.
+ itemdb_read();
+
+ return 0;
+}
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
index b141abbfc..f1e97227c 100644
--- a/src/map/itemdb.h
+++ b/src/map/itemdb.h
@@ -1,148 +1,148 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _ITEMDB_H_
-#define _ITEMDB_H_
-
-#include "map.h"
-#define MAX_RANDITEM 10000
-
-enum {
- IT_HEALING = 0,
- IT_UNKNOWN, //1
- IT_USABLE, //2
- IT_ETC, //3
- IT_WEAPON, //4
- IT_ARMOR, //5
- IT_CARD, //6
- IT_PETEGG, //7
- IT_PETARMOR,//8
- IT_UNKNOWN2,//9
- IT_AMMO, //10
- IT_DELAYCONSUME,//11
- IT_MAX
-} item_types;
-
-#define CARD0_FORGE 0x00FF
-#define CARD0_CREATE 0x00FE
-#define CARD0_PET ((short)0xFF00)
-
-//Marks if the card0 given is "special" (non-item id used to mark pets/created items. [Skotlex]
-#define itemdb_isspecial(i) (i == CARD0_FORGE || i == CARD0_CREATE || i == CARD0_PET)
-
-//Use apple for unknown items.
-#define UNKNOWN_ITEM_ID 512
-
-struct item_data {
- int nameid;
- char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH];
- char prefix[NAME_LENGTH],suffix[NAME_LENGTH];
- char cardillustname[64];
- //Do not add stuff between value_buy and wlv (see how getiteminfo works)
- int value_buy;
- int value_sell;
- int type;
- int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus]
- int sex;
- int equip;
- int weight;
- int atk;
- int def;
- int range;
- int slot;
- int look;
- int elv;
- int wlv;
-//Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command
-// some script commands should be revised as well...
- unsigned int class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2)
- unsigned class_upper : 3; //Specifies if the upper-type can equip it (1: normal, 2: upper, 3: baby)
- struct {
- unsigned short chance;
- int id;
- } mob[MAX_SEARCH]; //Holds the mobs that have the highest drop rate for this item. [Skotlex]
- struct script_code *script; //Default script for everything.
- struct script_code *equip_script; //Script executed once when equipping.
- struct script_code *unequip_script;//Script executed once when unequipping.
- struct {
- unsigned available : 1;
- unsigned value_notdc : 1;
- unsigned value_notoc : 1;
- short no_equip;
- unsigned no_use : 1;
- unsigned no_refine : 1; // [celest]
- unsigned delay_consume : 1; // Signifies items that are not consumed inmediately upon double-click [Skotlex]
- unsigned trade_restriction : 7; //Item restrictions mask [Skotlex]
- unsigned autoequip: 1;
- } flag;
- short gm_lv_trade_override; //GM-level to override trade_restriction
- int view_id;
-};
-
-struct item_group {
- int nameid[MAX_RANDITEM];
- int qty; //Counts amount of items in the group.
-};
-
-struct item_data* itemdb_searchname(const char *name);
-int itemdb_searchname_array(struct item_data** data, int size, const char *str);
-struct item_data* itemdb_load(int nameid);
-struct item_data* itemdb_search(int nameid);
-struct item_data* itemdb_exists(int nameid);
-#define itemdb_type(n) itemdb_search(n)->type
-#define itemdb_atk(n) itemdb_search(n)->atk
-#define itemdb_def(n) itemdb_search(n)->def
-#define itemdb_look(n) itemdb_search(n)->look
-#define itemdb_weight(n) itemdb_search(n)->weight
-#define itemdb_equip(n) itemdb_search(n)->equip
-#define itemdb_usescript(n) itemdb_search(n)->script
-#define itemdb_equipscript(n) itemdb_search(n)->script
-#define itemdb_wlv(n) itemdb_search(n)->wlv
-#define itemdb_range(n) itemdb_search(n)->range
-#define itemdb_slot(n) itemdb_search(n)->slot
-#define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available)
-#define itemdb_viewid(n) (itemdb_search(n)->view_id)
-#define itemdb_autoequip(n) (itemdb_search(n)->flag.autoequip)
-int itemdb_group_bonus(struct map_session_data *sd, int itemid);
-
-int itemdb_searchrandomid(int flags);
-
-#define itemdb_value_buy(n) itemdb_search(n)->value_buy
-#define itemdb_value_sell(n) itemdb_search(n)->value_sell
-#define itemdb_value_notdc(n) itemdb_search(n)->flag.value_notdc
-#define itemdb_value_notoc(n) itemdb_search(n)->flag.value_notoc
-#define itemdb_canrefine(n) itemdb_search(n)->flag.no_refine
-//Item trade restrictions [Skotlex]
-int itemdb_isdropable_sub(struct item_data *, int, int);
-int itemdb_cantrade_sub(struct item_data*, int, int);
-int itemdb_canpartnertrade_sub(struct item_data*, int, int);
-int itemdb_cansell_sub(struct item_data*,int, int);
-int itemdb_cancartstore_sub(struct item_data*, int, int);
-int itemdb_canstore_sub(struct item_data*, int, int);
-int itemdb_canguildstore_sub(struct item_data*, int, int);
-int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int));
-#define itemdb_isdropable(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_isdropable_sub)
-#define itemdb_cantrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_cantrade_sub)
-#define itemdb_canpartnertrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_canpartnertrade_sub)
-#define itemdb_cansell(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cansell_sub)
-#define itemdb_cancartstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cancartstore_sub)
-#define itemdb_canstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_canstore_sub)
-#define itemdb_canguildstore(item, gmlv) itemdb_isrestricted(item , gmlv, 0, itemdb_canguildstore_sub)
-
-int itemdb_isequip(int);
-int itemdb_isequip2(struct item_data *);
-int itemdb_isidentified(int);
-int itemdb_isstackable(int);
-int itemdb_isstackable2(struct item_data *);
-
-// itemdb_equipマクロとitemdb_equippointとの違いは
-// 前者が鯖側dbで定義された値そのものを返すのに対し
-// 後者はsessiondataを考慮した鞍側での装備可能場所
-// すべての組み合わせを返す
-
-void itemdb_reload(void);
-
-void do_final_itemdb(void);
-int do_init_itemdb(void);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ITEMDB_H_
+#define _ITEMDB_H_
+
+#include "map.h"
+#define MAX_RANDITEM 10000
+
+enum {
+ IT_HEALING = 0,
+ IT_UNKNOWN, //1
+ IT_USABLE, //2
+ IT_ETC, //3
+ IT_WEAPON, //4
+ IT_ARMOR, //5
+ IT_CARD, //6
+ IT_PETEGG, //7
+ IT_PETARMOR,//8
+ IT_UNKNOWN2,//9
+ IT_AMMO, //10
+ IT_DELAYCONSUME,//11
+ IT_MAX
+} item_types;
+
+#define CARD0_FORGE 0x00FF
+#define CARD0_CREATE 0x00FE
+#define CARD0_PET ((short)0xFF00)
+
+//Marks if the card0 given is "special" (non-item id used to mark pets/created items. [Skotlex]
+#define itemdb_isspecial(i) (i == CARD0_FORGE || i == CARD0_CREATE || i == CARD0_PET)
+
+//Use apple for unknown items.
+#define UNKNOWN_ITEM_ID 512
+
+struct item_data {
+ int nameid;
+ char name[ITEM_NAME_LENGTH],jname[ITEM_NAME_LENGTH];
+ char prefix[NAME_LENGTH],suffix[NAME_LENGTH];
+ char cardillustname[64];
+ //Do not add stuff between value_buy and wlv (see how getiteminfo works)
+ int value_buy;
+ int value_sell;
+ int type;
+ int maxchance; //For logs, for external game info, for scripts: Max drop chance of this item (e.g. 0.01% , etc.. if it = 0, then monsters don't drop it) [Lupus]
+ int sex;
+ int equip;
+ int weight;
+ int atk;
+ int def;
+ int range;
+ int slot;
+ int look;
+ int elv;
+ int wlv;
+//Lupus: I rearranged order of these fields due to compatibility with ITEMINFO script command
+// some script commands should be revised as well...
+ unsigned int class_base[3]; //Specifies if the base can wear this item (split in 3 indexes per type: 1-1, 2-1, 2-2)
+ unsigned class_upper : 3; //Specifies if the upper-type can equip it (1: normal, 2: upper, 3: baby)
+ struct {
+ unsigned short chance;
+ int id;
+ } mob[MAX_SEARCH]; //Holds the mobs that have the highest drop rate for this item. [Skotlex]
+ struct script_code *script; //Default script for everything.
+ struct script_code *equip_script; //Script executed once when equipping.
+ struct script_code *unequip_script;//Script executed once when unequipping.
+ struct {
+ unsigned available : 1;
+ unsigned value_notdc : 1;
+ unsigned value_notoc : 1;
+ short no_equip;
+ unsigned no_use : 1;
+ unsigned no_refine : 1; // [celest]
+ unsigned delay_consume : 1; // Signifies items that are not consumed inmediately upon double-click [Skotlex]
+ unsigned trade_restriction : 7; //Item restrictions mask [Skotlex]
+ unsigned autoequip: 1;
+ } flag;
+ short gm_lv_trade_override; //GM-level to override trade_restriction
+ int view_id;
+};
+
+struct item_group {
+ int nameid[MAX_RANDITEM];
+ int qty; //Counts amount of items in the group.
+};
+
+struct item_data* itemdb_searchname(const char *name);
+int itemdb_searchname_array(struct item_data** data, int size, const char *str);
+struct item_data* itemdb_load(int nameid);
+struct item_data* itemdb_search(int nameid);
+struct item_data* itemdb_exists(int nameid);
+#define itemdb_type(n) itemdb_search(n)->type
+#define itemdb_atk(n) itemdb_search(n)->atk
+#define itemdb_def(n) itemdb_search(n)->def
+#define itemdb_look(n) itemdb_search(n)->look
+#define itemdb_weight(n) itemdb_search(n)->weight
+#define itemdb_equip(n) itemdb_search(n)->equip
+#define itemdb_usescript(n) itemdb_search(n)->script
+#define itemdb_equipscript(n) itemdb_search(n)->script
+#define itemdb_wlv(n) itemdb_search(n)->wlv
+#define itemdb_range(n) itemdb_search(n)->range
+#define itemdb_slot(n) itemdb_search(n)->slot
+#define itemdb_available(n) (itemdb_exists(n) && itemdb_search(n)->flag.available)
+#define itemdb_viewid(n) (itemdb_search(n)->view_id)
+#define itemdb_autoequip(n) (itemdb_search(n)->flag.autoequip)
+int itemdb_group_bonus(struct map_session_data *sd, int itemid);
+
+int itemdb_searchrandomid(int flags);
+
+#define itemdb_value_buy(n) itemdb_search(n)->value_buy
+#define itemdb_value_sell(n) itemdb_search(n)->value_sell
+#define itemdb_value_notdc(n) itemdb_search(n)->flag.value_notdc
+#define itemdb_value_notoc(n) itemdb_search(n)->flag.value_notoc
+#define itemdb_canrefine(n) itemdb_search(n)->flag.no_refine
+//Item trade restrictions [Skotlex]
+int itemdb_isdropable_sub(struct item_data *, int, int);
+int itemdb_cantrade_sub(struct item_data*, int, int);
+int itemdb_canpartnertrade_sub(struct item_data*, int, int);
+int itemdb_cansell_sub(struct item_data*,int, int);
+int itemdb_cancartstore_sub(struct item_data*, int, int);
+int itemdb_canstore_sub(struct item_data*, int, int);
+int itemdb_canguildstore_sub(struct item_data*, int, int);
+int itemdb_isrestricted(struct item* item, int gmlv, int gmlv2, int (*func)(struct item_data*, int, int));
+#define itemdb_isdropable(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_isdropable_sub)
+#define itemdb_cantrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_cantrade_sub)
+#define itemdb_canpartnertrade(item, gmlv, gmlv2) itemdb_isrestricted(item, gmlv, gmlv2, itemdb_canpartnertrade_sub)
+#define itemdb_cansell(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cansell_sub)
+#define itemdb_cancartstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_cancartstore_sub)
+#define itemdb_canstore(item, gmlv) itemdb_isrestricted(item, gmlv, 0, itemdb_canstore_sub)
+#define itemdb_canguildstore(item, gmlv) itemdb_isrestricted(item , gmlv, 0, itemdb_canguildstore_sub)
+
+int itemdb_isequip(int);
+int itemdb_isequip2(struct item_data *);
+int itemdb_isidentified(int);
+int itemdb_isstackable(int);
+int itemdb_isstackable2(struct item_data *);
+
+// itemdb_equipマクロとitemdb_equippointとの違いは
+// 前者が鯖側dbで定義された値そのものを返すのに対し
+// 後者はsessiondataを考慮した鞍側での装備可能場所
+// すべての組み合わせを返す
+
+void itemdb_reload(void);
+
+void do_final_itemdb(void);
+int do_init_itemdb(void);
+
+#endif
diff --git a/src/map/log.c b/src/map/log.c
index 0d6f010f2..209b81d32 100644
--- a/src/map/log.c
+++ b/src/map/log.c
@@ -1,532 +1,532 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-// Logging functions by Azndragon & Codemaster
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "../common/strlib.h"
-#include "../common/nullpo.h"
-#include "../common/showmsg.h"
-#include "../common/malloc.h"
-#include "itemdb.h"
-#include "map.h"
-#include "log.h"
-
-#ifndef SQL_DEBUG
-
-#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
-
-#else
-
-#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
-
-#endif
-
-struct Log_Config log_config;
-
-char timestring[255];
-time_t curtime;
-
-//FILTER OPTIONS
-//0 = Don't log
-//1 = Log any item
-//Bits: ||
-//2 - Healing items (0)
-//3 - Etc Items(3) + Arrows (10)
-//4 - Usable Items(2) + Scrolls,Lures(11)
-//5 - Weapon(4)
-//6 - Shields,Armor,Headgears,Accessories,etc(5)
-//7 - Cards(6)
-//8 - Pet Accessories(8) + Eggs(7) (well, monsters don't drop 'em but we'll use the same system for ALL logs)
-//9 - Log expensive items ( >= price_log)
-//10 - Log big amount of items ( >= amount_log)
-//11 - Log refined items (if their refine >= refine_log )
-//12 - Log rare items (if their drop chance <= rare_log )
-
-//check if this item should be logged according the settings
-int should_log_item(int filter, int nameid, int amount) {
- struct item_data *item_data;
- if ((item_data= itemdb_exists(nameid)) == NULL) return 0;
- if ((filter&1) || // Filter = 1, we log any item
- (filter&2 && item_data->type == IT_HEALING ) ||
- (filter&4 && (item_data->type == IT_ETC || item_data->type == IT_AMMO) ) ||
- (filter&8 && item_data->type == IT_USABLE ) ||
- (filter&16 && item_data->type == IT_WEAPON ) ||
- (filter&32 && item_data->type == IT_ARMOR ) ||
- (filter&64 && item_data->type == IT_CARD ) ||
- (filter&128 && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) ||
- (filter&256 && item_data->value_buy >= log_config.price_items_log ) || //expensive items
- (filter&512 && abs(amount) >= log_config.amount_items_log ) || //big amount of items
- (filter&2048 && ((item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium
- ) return item_data->nameid;
-
- return 0;
-}
-
-int log_branch(struct map_session_data *sd)
-{
-#ifndef TXT_ONLY
- char t_name[NAME_LENGTH*2];
-#endif
- FILE *logfp;
-
- if(!log_config.enable_logs)
- return 0;
- nullpo_retr(0, sd);
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0)
- {
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%s')",
- log_config.log_branch_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex));
- if(mysql_query(&logmysql_handle, tmp_sql))
- {
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
- if((logfp=fopen(log_config.log_branch,"a+")) == NULL)
- return 0;
- time(&curtime);
- strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
- fprintf(logfp,"%s - %s[%d:%d]\t%s%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex), RETCODE);
- fclose(logfp);
- return 1;
-}
-
-
-int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm)
-{
- FILE *logfp;
- char *mapname;
-
- nullpo_retr(0, sd);
- //Should we log this item? [Lupus]
- if (!should_log_item(log_config.filter,nameid, amount))
- return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
-
- mapname = (char*)mapindex_id2name(sd->mapindex);
-
- if(mapname==NULL)
- mapname="";
-
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0)
- {
- if (itm==NULL) {
- //We log common item
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
- log_config.log_pick_db, sd->status.char_id, type, nameid, amount, mapname);
- } else {
- //We log Extended item
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
- log_config.log_pick_db, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
- }
-
- if(mysql_query(&logmysql_handle, tmp_sql))
- {
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
- if((logfp=fopen(log_config.log_pick,"a+")) == NULL)
- return 0;
- time(&curtime);
- strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
-
- if (itm==NULL) {
- //We log common item
- fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s",
- timestring, sd->status.char_id, type, nameid, amount, mapname, RETCODE);
-
- } else {
- //We log Extended item
- fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s",
- timestring, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE);
- }
- fclose(logfp);
- return 1; //Logged
-}
-
-//Mob picked item
-int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm)
-{
- FILE *logfp;
- char *mapname;
-
- nullpo_retr(0, md);
- //Should we log this item? [Lupus]
- if (!should_log_item(log_config.filter,nameid, amount))
- return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
-
- //either PLAYER or MOB (here we get map name and objects ID)
- mapname = map[md->bl.m].name;
- if(mapname==NULL)
- mapname="";
-
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0)
- {
- if (itm==NULL) {
- //We log common item
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
- log_config.log_pick_db, md->class_, type, nameid, amount, mapname);
- } else {
- //We log Extended item
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
- log_config.log_pick_db, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
- }
-
- if(mysql_query(&logmysql_handle, tmp_sql))
- {
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
- if((logfp=fopen(log_config.log_pick,"a+")) == NULL)
- return 0;
- time(&curtime);
- strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
-
- if (itm==NULL) {
- //We log common item
- fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s",
- timestring, md->class_, type, nameid, amount, mapname, RETCODE);
-
- } else {
- //We log Extended item
- fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s",
- timestring, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE);
- }
- fclose(logfp);
- return 1; //Logged
-}
-
-int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount)
-{
-// FILE *logfp;
- if(!log_config.enable_logs || (log_config.zeny!=1 && abs(amount)<log_config.zeny))
- return 0;
-
- nullpo_retr(0, sd);
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0)
- {
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')",
- log_config.log_zeny_db, sd->status.char_id, src_sd->status.char_id, type, amount, mapindex_id2name(sd->mapindex));
- if(mysql_query(&logmysql_handle, tmp_sql))
- {
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
-// if((logfp=fopen(log_config.log_zeny,"a+")) == NULL)
-// return 0;
-// time(&curtime);
-// strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
-// fprintf(logfp,"%s - %s[%d]\t%s[%d]\t%d\t%s", timestring, sd->status.name, sd->status.account_id, target_sd->status.name, target_sd->status.account_id, sd->deal.zeny, RETCODE);
-// fclose(logfp);
-// return 1;
- return 0;
-}
-
-int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp)
-{
- FILE *logfp;
-
- if(!log_config.enable_logs)
- return 0;
- nullpo_retr(0, sd);
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0)
- {
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex));
- if(mysql_query(&logmysql_handle, tmp_sql))
- {
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
- if((logfp=fopen(log_config.log_mvpdrop,"a+")) == NULL)
- return 0;
- time(&curtime);
- strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
- fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], RETCODE);
- fclose(logfp);
- return 0;
-}
-
-
-int log_atcommand(struct map_session_data *sd, const char *message)
-{
- FILE *logfp;
-#ifndef TXT_ONLY
- char t_name[NAME_LENGTH*2];
- char t_msg[MESSAGE_SIZE*2+1]; //These are the contents of an @ call, so there shouldn't be overflow danger here?
-#endif
-
- if(!log_config.enable_logs)
- return 0;
- nullpo_retr(0, sd);
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0)
- {
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
- log_config.log_gm_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
- if(mysql_query(&logmysql_handle, tmp_sql))
- {
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
- if((logfp=fopen(log_config.log_gm,"a+")) == NULL)
- return 0;
- time(&curtime);
- strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
- fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
- fclose(logfp);
- return 1;
-}
-
-int log_npc(struct map_session_data *sd, const char *message)
-{ //[Lupus]
- FILE *logfp;
-#ifndef TXT_ONLY
- char t_name[NAME_LENGTH*2];
- char t_msg[255+1]; //it's 255 chars MAX.
-#endif
-
- if(!log_config.enable_logs)
- return 0;
- nullpo_retr(0, sd);
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0)
- {
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
- log_config.log_npc_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
- if(mysql_query(&logmysql_handle, tmp_sql))
- {
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
- if((logfp=fopen(log_config.log_npc,"a+")) == NULL)
- return 0;
- time(&curtime);
- strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
- fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
- fclose(logfp);
- return 1;
-}
-
-//ChatLogging
-// Log CHAT (currently only: Party, Guild, Whisper)
-// LOGGING FILTERS [Lupus]
-//=============================================================
-//0 = Don't log at all
-//1 = Log any chat messages
-//Advanced Filter Bits: ||
-//2 - Log Whisper messages
-//3 - Log Party messages
-//4 - Log Guild messages
-//5 - Log Common messages (not implemented)
-//6 - Don't log when WOE is on
-//Example:
-//log_chat: 1 = logs ANY messages
-//log_chat: 6 = logs both Whisper & Party messages
-//log_chat: 8 = logs only Guild messages
-//log_chat: 18 = logs only Whisper, when WOE is off
-
-int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message){
- FILE *logfp;
-#ifndef TXT_ONLY
- char t_charname[NAME_LENGTH*2];
- char t_msg[MESSAGE_SIZE*2+1]; //Chat line fully escaped, with an extra space just in case.
-#endif
-
- //Check ON/OFF
- if(log_config.chat <= 0)
- return 0; //Deactivated
-
-#ifndef TXT_ONLY
- if(log_config.sql_logs > 0){
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%s', '%s')",
- log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y, jstrescapecpy(t_charname, dst_charname), jstrescapecpy(t_msg, message));
-
- if(mysql_query(&logmysql_handle, tmp_sql)){
- ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- return 1;
- }
-#endif
- if((logfp = fopen(log_config.log_chat, "a+")) == NULL)
- return 0;
- time(&curtime);
- strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
- //DATE - type,type_id,src_charid,src_accountid,src_map,src_x,src_y,dst_charname,message
- fprintf(logfp, "%s - %s,%d,%d,%d,%s,%d,%d,%s,%s%s",
- timestring, type, type_id, src_charid, src_accid, map, x, y, dst_charname, message, RETCODE);
- fclose(logfp);
- return 1;
-}
-
-
-void log_set_defaults(void)
-{
- malloc_set(&log_config, 0, sizeof(log_config));
-
- //LOG FILTER Default values
- log_config.refine_items_log = 5; //log refined items, with refine >= +7
- log_config.rare_items_log = 100; //log rare items. drop chance <= 1%
- log_config.price_items_log = 1000; //1000z
- log_config.amount_items_log = 100;
-}
-
-int log_config_read(char *cfgName)
-{
- static int count = 0;
- char line[1024], w1[1024], w2[1024];
- FILE *fp;
-
- if ((count++) == 0)
- log_set_defaults();
-
- if((fp = fopen(cfgName, "r")) == NULL)
- {
- ShowError("Log configuration file not found at: %s\n", cfgName);
- return 1;
- }
-
- while(fgets(line, sizeof(line) -1, fp))
- {
- if(line[0] == '/' && line[1] == '/')
- continue;
-
- if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2)
- {
- if(strcmpi(w1,"enable_logs") == 0) {
- log_config.enable_logs = (atoi(w2));
- if (log_config.enable_logs&1) //Log everything.
- log_config.enable_logs=0xFFFFFFFF;
- } else if(strcmpi(w1,"sql_logs") == 0) {
- log_config.sql_logs = (atoi(w2));
-//start of common filter settings
- } else if(strcmpi(w1,"rare_items_log") == 0) {
- log_config.rare_items_log = (atoi(w2));
- } else if(strcmpi(w1,"refine_items_log") == 0) {
- log_config.refine_items_log = (atoi(w2));
- } else if(strcmpi(w1,"price_items_log") == 0) {
- log_config.price_items_log = (atoi(w2));
- } else if(strcmpi(w1,"amount_items_log") == 0) {
- log_config.amount_items_log = (atoi(w2));
-//end of common filter settings
- } else if(strcmpi(w1,"log_branch") == 0) {
- log_config.branch = (atoi(w2));
- } else if(strcmpi(w1,"log_filter") == 0) {
- log_config.filter = (atoi(w2));
- } else if(strcmpi(w1,"log_zeny") == 0) {
- log_config.zeny = (atoi(w2));
- } else if(strcmpi(w1,"log_gm") == 0) {
- log_config.gm = (atoi(w2));
- } else if(strcmpi(w1,"log_npc") == 0) {
- log_config.npc = (atoi(w2));
- } else if(strcmpi(w1, "log_chat") == 0) {
- log_config.chat = (atoi(w2));
- } else if(strcmpi(w1,"log_mvpdrop") == 0) {
- log_config.mvpdrop = (atoi(w2));
- }
-
-#ifndef TXT_ONLY
- else if(strcmpi(w1, "log_branch_db") == 0) {
- strcpy(log_config.log_branch_db, w2);
- if(log_config.branch == 1)
- ShowNotice("Logging Dead Branch Usage to table `%s`\n", w2);
- } else if(strcmpi(w1, "log_pick_db") == 0) {
- strcpy(log_config.log_pick_db, w2);
- if(log_config.filter)
- ShowNotice("Logging Item Picks to table `%s`\n", w2);
- } else if(strcmpi(w1, "log_zeny_db") == 0) {
- strcpy(log_config.log_zeny_db, w2);
- if(log_config.zeny == 1)
- ShowNotice("Logging Zeny to table `%s`\n", w2);
- } else if(strcmpi(w1, "log_mvpdrop_db") == 0) {
- strcpy(log_config.log_mvpdrop_db, w2);
- if(log_config.mvpdrop == 1)
- ShowNotice("Logging MVP Drops to table `%s`\n", w2);
- } else if(strcmpi(w1, "log_gm_db") == 0) {
- strcpy(log_config.log_gm_db, w2);
- if(log_config.gm > 0)
- ShowNotice("Logging GM Level %d Commands to table `%s`\n", log_config.gm, w2);
- } else if(strcmpi(w1, "log_npc_db") == 0) {
- strcpy(log_config.log_npc_db, w2);
- if(log_config.npc > 0)
- ShowNotice("Logging NPC 'logmes' to table `%s`\n", w2);
- } else if(strcmpi(w1, "log_chat_db") == 0) {
- strcpy(log_config.log_chat_db, w2);
- if(log_config.chat > 0)
- ShowNotice("Logging CHAT to table `%s`\n", w2);
- }
-#endif
-
- else if(strcmpi(w1, "log_branch_file") == 0) {
- strcpy(log_config.log_branch, w2);
- if(log_config.branch > 0 && log_config.sql_logs < 1)
- ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2);
- } else if(strcmpi(w1, "log_pick_file") == 0) {
- strcpy(log_config.log_pick, w2);
- if(log_config.filter > 0 && log_config.sql_logs < 1)
- ShowNotice("Logging Item Picks to file `%s`.txt\n", w2);
- } else if(strcmpi(w1, "log_zeny_file") == 0) {
- strcpy(log_config.log_zeny, w2);
- if(log_config.zeny > 0 && log_config.sql_logs < 1)
- ShowNotice("Logging Zeny to file `%s`.txt\n", w2);
- } else if(strcmpi(w1, "log_mvpdrop_file") == 0) {
- strcpy(log_config.log_mvpdrop, w2);
- if(log_config.mvpdrop > 0 && log_config.sql_logs < 1)
- ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2);
- } else if(strcmpi(w1, "log_gm_file") == 0) {
- strcpy(log_config.log_gm, w2);
- if(log_config.gm > 0 && log_config.sql_logs < 1)
- ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2);
- } else if(strcmpi(w1, "log_npc_file") == 0) {
- strcpy(log_config.log_npc, w2);
- if(log_config.npc > 0 && log_config.sql_logs < 1)
- ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2);
- } else if(strcmpi(w1, "log_chat_file") == 0) {
- strcpy(log_config.log_chat, w2);
- if(log_config.chat > 0 && log_config.sql_logs < 1)
- ShowNotice("Logging CHAT to file `%s`.txt\n", w2);
- //support the import command, just like any other config
- } else if(strcmpi(w1,"import") == 0) {
- log_config_read(w2);
- }
- }
- }
-
- fclose(fp);
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// Logging functions by Azndragon & Codemaster
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/strlib.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+#include "../common/malloc.h"
+#include "itemdb.h"
+#include "map.h"
+#include "log.h"
+
+#ifndef SQL_DEBUG
+
+#define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y)) //supports ' in names and runs faster [Kevin]
+
+#else
+
+#define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+#endif
+
+struct Log_Config log_config;
+
+char timestring[255];
+time_t curtime;
+
+//FILTER OPTIONS
+//0 = Don't log
+//1 = Log any item
+//Bits: ||
+//2 - Healing items (0)
+//3 - Etc Items(3) + Arrows (10)
+//4 - Usable Items(2) + Scrolls,Lures(11)
+//5 - Weapon(4)
+//6 - Shields,Armor,Headgears,Accessories,etc(5)
+//7 - Cards(6)
+//8 - Pet Accessories(8) + Eggs(7) (well, monsters don't drop 'em but we'll use the same system for ALL logs)
+//9 - Log expensive items ( >= price_log)
+//10 - Log big amount of items ( >= amount_log)
+//11 - Log refined items (if their refine >= refine_log )
+//12 - Log rare items (if their drop chance <= rare_log )
+
+//check if this item should be logged according the settings
+int should_log_item(int filter, int nameid, int amount) {
+ struct item_data *item_data;
+ if ((item_data= itemdb_exists(nameid)) == NULL) return 0;
+ if ((filter&1) || // Filter = 1, we log any item
+ (filter&2 && item_data->type == IT_HEALING ) ||
+ (filter&4 && (item_data->type == IT_ETC || item_data->type == IT_AMMO) ) ||
+ (filter&8 && item_data->type == IT_USABLE ) ||
+ (filter&16 && item_data->type == IT_WEAPON ) ||
+ (filter&32 && item_data->type == IT_ARMOR ) ||
+ (filter&64 && item_data->type == IT_CARD ) ||
+ (filter&128 && (item_data->type == IT_PETEGG || item_data->type == IT_PETARMOR) ) ||
+ (filter&256 && item_data->value_buy >= log_config.price_items_log ) || //expensive items
+ (filter&512 && abs(amount) >= log_config.amount_items_log ) || //big amount of items
+ (filter&2048 && ((item_data->maxchance <= log_config.rare_items_log) || item_data->nameid == 714) ) //Rare items or Emperium
+ ) return item_data->nameid;
+
+ return 0;
+}
+
+int log_branch(struct map_session_data *sd)
+{
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+#endif
+ FILE *logfp;
+
+ if(!log_config.enable_logs)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`branch_date`, `account_id`, `char_id`, `char_name`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%s')",
+ log_config.log_branch_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+ if((logfp=fopen(log_config.log_branch,"a+")) == NULL)
+ return 0;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%s%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, mapindex_id2name(sd->mapindex), RETCODE);
+ fclose(logfp);
+ return 1;
+}
+
+
+int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm)
+{
+ FILE *logfp;
+ char *mapname;
+
+ nullpo_retr(0, sd);
+ //Should we log this item? [Lupus]
+ if (!should_log_item(log_config.filter,nameid, amount))
+ return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
+
+ mapname = (char*)mapindex_id2name(sd->mapindex);
+
+ if(mapname==NULL)
+ mapname="";
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ if (itm==NULL) {
+ //We log common item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
+ log_config.log_pick_db, sd->status.char_id, type, nameid, amount, mapname);
+ } else {
+ //We log Extended item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
+ log_config.log_pick_db, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
+ }
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+ if((logfp=fopen(log_config.log_pick,"a+")) == NULL)
+ return 0;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+
+ if (itm==NULL) {
+ //We log common item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s",
+ timestring, sd->status.char_id, type, nameid, amount, mapname, RETCODE);
+
+ } else {
+ //We log Extended item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s",
+ timestring, sd->status.char_id, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE);
+ }
+ fclose(logfp);
+ return 1; //Logged
+}
+
+//Mob picked item
+int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm)
+{
+ FILE *logfp;
+ char *mapname;
+
+ nullpo_retr(0, md);
+ //Should we log this item? [Lupus]
+ if (!should_log_item(log_config.filter,nameid, amount))
+ return 0; //we skip logging this items set - they doesn't met our logging conditions [Lupus]
+
+ //either PLAYER or MOB (here we get map name and objects ID)
+ mapname = map[md->bl.m].name;
+ if(mapname==NULL)
+ mapname="";
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ if (itm==NULL) {
+ //We log common item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%s')",
+ log_config.log_pick_db, md->class_, type, nameid, amount, mapname);
+ } else {
+ //We log Extended item
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `type`, `nameid`, `amount`, `refine`, `card0`, `card1`, `card2`, `card3`, `map`) VALUES (NOW(), '%d', '%s', '%d', '%d', '%d', '%d', '%d', '%d', '%d', '%s')",
+ log_config.log_pick_db, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname);
+ }
+
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+ if((logfp=fopen(log_config.log_pick,"a+")) == NULL)
+ return 0;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+
+ if (itm==NULL) {
+ //We log common item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%s%s",
+ timestring, md->class_, type, nameid, amount, mapname, RETCODE);
+
+ } else {
+ //We log Extended item
+ fprintf(logfp,"%s - %d\t%s\t%d,%d,%d,%d,%d,%d,%d,%s%s",
+ timestring, md->class_, type, itm->nameid, amount, itm->refine, itm->card[0], itm->card[1], itm->card[2], itm->card[3], mapname, RETCODE);
+ }
+ fclose(logfp);
+ return 1; //Logged
+}
+
+int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount)
+{
+// FILE *logfp;
+ if(!log_config.enable_logs || (log_config.zeny!=1 && abs(amount)<log_config.zeny))
+ return 0;
+
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `char_id`, `src_id`, `type`, `amount`, `map`) VALUES (NOW(), '%d', '%d', '%s', '%d', '%s')",
+ log_config.log_zeny_db, sd->status.char_id, src_sd->status.char_id, type, amount, mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+// if((logfp=fopen(log_config.log_zeny,"a+")) == NULL)
+// return 0;
+// time(&curtime);
+// strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+// fprintf(logfp,"%s - %s[%d]\t%s[%d]\t%d\t%s", timestring, sd->status.name, sd->status.account_id, target_sd->status.name, target_sd->status.account_id, sd->deal.zeny, RETCODE);
+// fclose(logfp);
+// return 1;
+ return 0;
+}
+
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp)
+{
+ FILE *logfp;
+
+ if(!log_config.enable_logs)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`mvp_date`, `kill_char_id`, `monster_id`, `prize`, `mvpexp`, `map`) VALUES (NOW(), '%d', '%d', '%d', '%d', '%s') ", log_config.log_mvpdrop_db, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], mapindex_id2name(sd->mapindex));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+ if((logfp=fopen(log_config.log_mvpdrop,"a+")) == NULL)
+ return 0;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d:%d]\t%d\t%d,%d%s", timestring, sd->status.name, sd->status.account_id, sd->status.char_id, monster_id, log_mvp[0], log_mvp[1], RETCODE);
+ fclose(logfp);
+ return 0;
+}
+
+
+int log_atcommand(struct map_session_data *sd, const char *message)
+{
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+ char t_msg[MESSAGE_SIZE*2+1]; //These are the contents of an @ call, so there shouldn't be overflow danger here?
+#endif
+
+ if(!log_config.enable_logs)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`atcommand_date`, `account_id`, `char_id`, `char_name`, `map`, `command`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
+ log_config.log_gm_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+ if((logfp=fopen(log_config.log_gm,"a+")) == NULL)
+ return 0;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
+ fclose(logfp);
+ return 1;
+}
+
+int log_npc(struct map_session_data *sd, const char *message)
+{ //[Lupus]
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_name[NAME_LENGTH*2];
+ char t_msg[255+1]; //it's 255 chars MAX.
+#endif
+
+ if(!log_config.enable_logs)
+ return 0;
+ nullpo_retr(0, sd);
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0)
+ {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`npc_date`, `account_id`, `char_id`, `char_name`, `map`, `mes`) VALUES(NOW(), '%d', '%d', '%s', '%s', '%s') ",
+ log_config.log_npc_db, sd->status.account_id, sd->status.char_id, jstrescapecpy(t_name, sd->status.name), mapindex_id2name(sd->mapindex), jstrescapecpy(t_msg, (char *)message));
+ if(mysql_query(&logmysql_handle, tmp_sql))
+ {
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+ if((logfp=fopen(log_config.log_npc,"a+")) == NULL)
+ return 0;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ fprintf(logfp,"%s - %s[%d]: %s%s",timestring,sd->status.name,sd->status.account_id,message,RETCODE);
+ fclose(logfp);
+ return 1;
+}
+
+//ChatLogging
+// Log CHAT (currently only: Party, Guild, Whisper)
+// LOGGING FILTERS [Lupus]
+//=============================================================
+//0 = Don't log at all
+//1 = Log any chat messages
+//Advanced Filter Bits: ||
+//2 - Log Whisper messages
+//3 - Log Party messages
+//4 - Log Guild messages
+//5 - Log Common messages (not implemented)
+//6 - Don't log when WOE is on
+//Example:
+//log_chat: 1 = logs ANY messages
+//log_chat: 6 = logs both Whisper & Party messages
+//log_chat: 8 = logs only Guild messages
+//log_chat: 18 = logs only Whisper, when WOE is off
+
+int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message){
+ FILE *logfp;
+#ifndef TXT_ONLY
+ char t_charname[NAME_LENGTH*2];
+ char t_msg[MESSAGE_SIZE*2+1]; //Chat line fully escaped, with an extra space just in case.
+#endif
+
+ //Check ON/OFF
+ if(log_config.chat <= 0)
+ return 0; //Deactivated
+
+#ifndef TXT_ONLY
+ if(log_config.sql_logs > 0){
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`time`, `type`, `type_id`, `src_charid`, `src_accountid`, `src_map`, `src_map_x`, `src_map_y`, `dst_charname`, `message`) VALUES (NOW(), '%s', '%d', '%d', '%d', '%s', '%d', '%d', '%s', '%s')",
+ log_config.log_chat_db, type, type_id, src_charid, src_accid, map, x, y, jstrescapecpy(t_charname, dst_charname), jstrescapecpy(t_msg, message));
+
+ if(mysql_query(&logmysql_handle, tmp_sql)){
+ ShowSQL("DB error - %s\n",mysql_error(&logmysql_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ return 1;
+ }
+#endif
+ if((logfp = fopen(log_config.log_chat, "a+")) == NULL)
+ return 0;
+ time(&curtime);
+ strftime(timestring, 254, "%m/%d/%Y %H:%M:%S", localtime(&curtime));
+ //DATE - type,type_id,src_charid,src_accountid,src_map,src_x,src_y,dst_charname,message
+ fprintf(logfp, "%s - %s,%d,%d,%d,%s,%d,%d,%s,%s%s",
+ timestring, type, type_id, src_charid, src_accid, map, x, y, dst_charname, message, RETCODE);
+ fclose(logfp);
+ return 1;
+}
+
+
+void log_set_defaults(void)
+{
+ malloc_set(&log_config, 0, sizeof(log_config));
+
+ //LOG FILTER Default values
+ log_config.refine_items_log = 5; //log refined items, with refine >= +7
+ log_config.rare_items_log = 100; //log rare items. drop chance <= 1%
+ log_config.price_items_log = 1000; //1000z
+ log_config.amount_items_log = 100;
+}
+
+int log_config_read(char *cfgName)
+{
+ static int count = 0;
+ char line[1024], w1[1024], w2[1024];
+ FILE *fp;
+
+ if ((count++) == 0)
+ log_set_defaults();
+
+ if((fp = fopen(cfgName, "r")) == NULL)
+ {
+ ShowError("Log configuration file not found at: %s\n", cfgName);
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line) -1, fp))
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ if(sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2)
+ {
+ if(strcmpi(w1,"enable_logs") == 0) {
+ log_config.enable_logs = (atoi(w2));
+ if (log_config.enable_logs&1) //Log everything.
+ log_config.enable_logs=0xFFFFFFFF;
+ } else if(strcmpi(w1,"sql_logs") == 0) {
+ log_config.sql_logs = (atoi(w2));
+//start of common filter settings
+ } else if(strcmpi(w1,"rare_items_log") == 0) {
+ log_config.rare_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"refine_items_log") == 0) {
+ log_config.refine_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"price_items_log") == 0) {
+ log_config.price_items_log = (atoi(w2));
+ } else if(strcmpi(w1,"amount_items_log") == 0) {
+ log_config.amount_items_log = (atoi(w2));
+//end of common filter settings
+ } else if(strcmpi(w1,"log_branch") == 0) {
+ log_config.branch = (atoi(w2));
+ } else if(strcmpi(w1,"log_filter") == 0) {
+ log_config.filter = (atoi(w2));
+ } else if(strcmpi(w1,"log_zeny") == 0) {
+ log_config.zeny = (atoi(w2));
+ } else if(strcmpi(w1,"log_gm") == 0) {
+ log_config.gm = (atoi(w2));
+ } else if(strcmpi(w1,"log_npc") == 0) {
+ log_config.npc = (atoi(w2));
+ } else if(strcmpi(w1, "log_chat") == 0) {
+ log_config.chat = (atoi(w2));
+ } else if(strcmpi(w1,"log_mvpdrop") == 0) {
+ log_config.mvpdrop = (atoi(w2));
+ }
+
+#ifndef TXT_ONLY
+ else if(strcmpi(w1, "log_branch_db") == 0) {
+ strcpy(log_config.log_branch_db, w2);
+ if(log_config.branch == 1)
+ ShowNotice("Logging Dead Branch Usage to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_pick_db") == 0) {
+ strcpy(log_config.log_pick_db, w2);
+ if(log_config.filter)
+ ShowNotice("Logging Item Picks to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_zeny_db") == 0) {
+ strcpy(log_config.log_zeny_db, w2);
+ if(log_config.zeny == 1)
+ ShowNotice("Logging Zeny to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_mvpdrop_db") == 0) {
+ strcpy(log_config.log_mvpdrop_db, w2);
+ if(log_config.mvpdrop == 1)
+ ShowNotice("Logging MVP Drops to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_gm_db") == 0) {
+ strcpy(log_config.log_gm_db, w2);
+ if(log_config.gm > 0)
+ ShowNotice("Logging GM Level %d Commands to table `%s`\n", log_config.gm, w2);
+ } else if(strcmpi(w1, "log_npc_db") == 0) {
+ strcpy(log_config.log_npc_db, w2);
+ if(log_config.npc > 0)
+ ShowNotice("Logging NPC 'logmes' to table `%s`\n", w2);
+ } else if(strcmpi(w1, "log_chat_db") == 0) {
+ strcpy(log_config.log_chat_db, w2);
+ if(log_config.chat > 0)
+ ShowNotice("Logging CHAT to table `%s`\n", w2);
+ }
+#endif
+
+ else if(strcmpi(w1, "log_branch_file") == 0) {
+ strcpy(log_config.log_branch, w2);
+ if(log_config.branch > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Dead Branch Usage to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_pick_file") == 0) {
+ strcpy(log_config.log_pick, w2);
+ if(log_config.filter > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Item Picks to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_zeny_file") == 0) {
+ strcpy(log_config.log_zeny, w2);
+ if(log_config.zeny > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging Zeny to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_mvpdrop_file") == 0) {
+ strcpy(log_config.log_mvpdrop, w2);
+ if(log_config.mvpdrop > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging MVP Drops to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_gm_file") == 0) {
+ strcpy(log_config.log_gm, w2);
+ if(log_config.gm > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging GM Level %d Commands to file `%s`.txt\n", log_config.gm, w2);
+ } else if(strcmpi(w1, "log_npc_file") == 0) {
+ strcpy(log_config.log_npc, w2);
+ if(log_config.npc > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging NPC 'logmes' to file `%s`.txt\n", w2);
+ } else if(strcmpi(w1, "log_chat_file") == 0) {
+ strcpy(log_config.log_chat, w2);
+ if(log_config.chat > 0 && log_config.sql_logs < 1)
+ ShowNotice("Logging CHAT to file `%s`.txt\n", w2);
+ //support the import command, just like any other config
+ } else if(strcmpi(w1,"import") == 0) {
+ log_config_read(w2);
+ }
+ }
+ }
+
+ fclose(fp);
+ return 0;
+}
diff --git a/src/map/log.h b/src/map/log.h
index 32f3853ef..1cc2247db 100644
--- a/src/map/log.h
+++ b/src/map/log.h
@@ -1,43 +1,43 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _LOG_H_
-#define _LOG_H_
-
-#include "map.h"
-
-#ifndef TXT_ONLY
-
-extern char db_server_logdb[32];
-
-#endif //NOT TXT_ONLY
-
-//New logs
-int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm);
-int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm);
-int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount);
-
-int log_npc(struct map_session_data *sd, const char *message);
-int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message);
-int log_atcommand(struct map_session_data *sd, const char *message);
-
-//Old, but useful logs
-int log_branch(struct map_session_data *sd);
-int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp);
-
-int log_config_read(char *cfgName);
-
-int should_log_item(int filter, int nameid, int amount); //log filter check
-
-extern struct Log_Config {
- int enable_logs, filter;
- int sql_logs;
- int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter
- int branch, drop, mvpdrop, zeny, gm, npc, chat;
- char log_branch[32], log_pick[32], log_zeny[32], log_mvpdrop[32], log_gm[32], log_npc[32], log_chat[32];
- char log_branch_db[32], log_pick_db[32], log_zeny_db[32], log_mvpdrop_db[32], log_gm_db[32], log_npc_db[32], log_chat_db[32];
- int uptime;
- char log_uptime[32];
-} log_config;
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+#include "map.h"
+
+#ifndef TXT_ONLY
+
+extern char db_server_logdb[32];
+
+#endif //NOT TXT_ONLY
+
+//New logs
+int log_pick_pc(struct map_session_data *sd, const char *type, int nameid, int amount, struct item *itm);
+int log_pick_mob(struct mob_data *md, const char *type, int nameid, int amount, struct item *itm);
+int log_zeny(struct map_session_data *sd, char *type, struct map_session_data *src_sd, int amount);
+
+int log_npc(struct map_session_data *sd, const char *message);
+int log_chat(char *type, int type_id, int src_charid, int src_accid, char *map, int x, int y, char *dst_charname, char *message);
+int log_atcommand(struct map_session_data *sd, const char *message);
+
+//Old, but useful logs
+int log_branch(struct map_session_data *sd);
+int log_mvpdrop(struct map_session_data *sd, int monster_id, int *log_mvp);
+
+int log_config_read(char *cfgName);
+
+int should_log_item(int filter, int nameid, int amount); //log filter check
+
+extern struct Log_Config {
+ int enable_logs, filter;
+ int sql_logs;
+ int rare_items_log,refine_items_log,price_items_log,amount_items_log; //for filter
+ int branch, drop, mvpdrop, zeny, gm, npc, chat;
+ char log_branch[32], log_pick[32], log_zeny[32], log_mvpdrop[32], log_gm[32], log_npc[32], log_chat[32];
+ char log_branch_db[32], log_pick_db[32], log_zeny_db[32], log_mvpdrop_db[32], log_gm_db[32], log_npc_db[32], log_chat_db[32];
+ int uptime;
+ char log_uptime[32];
+} log_config;
+
+#endif
diff --git a/src/map/mail.c b/src/map/mail.c
index 316bc9c18..cd1f63b1e 100644
--- a/src/map/mail.c
+++ b/src/map/mail.c
@@ -1,356 +1,356 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef TXT_ONLY
-// Mail System for eAthena SQL
-// Created by Valaris
-// moved all strings to msg_athena.conf [Lupus]
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../common/strlib.h"
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/showmsg.h"
-
-#include "map.h"
-#include "clif.h"
-#include "chrif.h"
-#include "intif.h"
-#include "atcommand.h"
-#include "pc.h"
-#include "mail.h"
-
-#ifndef TXT_ONLY
- #ifndef SQL_DEBUG
-
- #define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y))
-
- #else
-
- #define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
-
- #endif
-#endif
-
-int MAIL_CHECK_TIME = 120000;
-int mail_timer;
-//extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
-
-int mail_check(struct map_session_data *sd,int type)
-{
- int i = 0, new_ = 0, priority = 0;
- char message[80];
-
- nullpo_retr (0, sd);
-
- sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`read_flag`,`priority`,`check_flag` "
- "FROM `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id`", mail_db, sd->status.account_id);
-
- if (mysql_query(&mail_handle, tmp_sql)) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
-
- mail_res = mysql_store_result(&mail_handle);
- if(mail_res) {
- if (mysql_num_rows(mail_res) == 0) {
- //clif_displaymessage(sd->fd,"You have no messages.");
- clif_displaymessage(sd->fd, msg_txt(516));
-
- mysql_free_result(mail_res);
- return 0;
- }
-
- while ((mail_row = mysql_fetch_row(mail_res))) {
- i++;
- if(!atoi(mail_row[5])) {
- sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
- if(mysql_query(&mail_handle, tmp_sql) ) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
-
- if(!atoi(mail_row[3])) {
- new_++;
- if(atoi(mail_row[4]))
- priority++;
- if(type==2 || type==3) {
- if(atoi(mail_row[4])) {
- snprintf(message, 80, msg_txt(511), i, mail_row[2]);
- clif_displaymessage(sd->fd, message);
- } else {
- //sprintf(message, "%d - From : %s (New)", i, mail_row[2]);
- snprintf(message, 80, msg_txt(512), i, mail_row[2]);
- clif_displaymessage(sd->fd, message);
- }
- }
- } else if(type==2){
- snprintf(message, 80, msg_txt(513), i, mail_row[2]);
- clif_displaymessage(sd->fd, message);
- }
- }
-
- mysql_free_result(mail_res);
- } else {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
-
- if(i>0 && new_>0 && type==1) {
- //sprintf(message, "You have %d new messages.", new_);
- sprintf(message, msg_txt(514), new_);
-
- clif_displaymessage(sd->fd, jstrescape(message));
- }
- if(i>0 && new_>0 && priority>0 && type==1) {
- //sprintf(message, "You have %d unread priority messages.", priority);
- sprintf(message, msg_txt(515), priority);
- clif_displaymessage(sd->fd, jstrescape(message));
- }
- if(!new_) {
- //clif_displaymessage(sd->fd, "You have no new messages.");
- clif_displaymessage(sd->fd, msg_txt(516));
- }
-
- return 0;
-}
-
-int mail_read(struct map_session_data *sd, int message_id)
-{
-
- char message[80];
-
- nullpo_retr (0, sd);
-
- sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`message`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
-
- if (mysql_query(&mail_handle, tmp_sql)) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
-
- mail_res = mysql_store_result(&mail_handle);
- if(mail_res) {
- if (mysql_num_rows(mail_res) == 0) {
- mysql_free_result(mail_res);
- //clif_displaymessage(sd->fd, "Message not found.");
- clif_displaymessage(sd->fd, msg_txt(517));
- return 0;
- }
-
- if ((mail_row = mysql_fetch_row(mail_res))) {
- if(!atoi(mail_row[6])) {
- sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
- if(mysql_query(&mail_handle, tmp_sql) ) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
-
- //sprintf(message, "Reading message from %s", mail_row[2]);
- sprintf(message, msg_txt(518), mail_row[2]);
- clif_displaymessage(sd->fd, jstrescape(message));
-
- sprintf(message, "%s", mail_row[3]);
- clif_displaymessage(sd->fd, jstrescape(message));
-
- sprintf(tmp_sql,"UPDATE `%s` SET `read_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
- if(mysql_query(&mail_handle, tmp_sql) ) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
- }
-
- mysql_free_result(mail_res);
-
- } else {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
-
- return 0;
-}
-
-int mail_delete(struct map_session_data *sd, int message_id)
-{
- nullpo_retr (0, sd);
-
- sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
-
- if (mysql_query(&mail_handle, tmp_sql)) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
-
- mail_res = mysql_store_result(&mail_handle);
- if(mail_res) {
- if (mysql_num_rows(mail_res) == 0) {
- mysql_free_result(mail_res);
- //clif_displaymessage(sd->fd, "Message not found.");
- clif_displaymessage(sd->fd, msg_txt(517));
- return 0;
- }
-
- if ((mail_row = mysql_fetch_row(mail_res))) {
- if(!atoi(mail_row[2]) && atoi(mail_row[3])) {
- mysql_free_result(mail_res);
- //clif_displaymessage(sd->fd,"Cannot delete unread priority mail.");
- clif_displaymessage(sd->fd,msg_txt(519));
-
- return 0;
- }
- if(!atoi(mail_row[4])) {
- mysql_free_result(mail_res);
- //clif_displaymessage(sd->fd,"You have recieved new mail, use @listmail before deleting.");
- clif_displaymessage(sd->fd,msg_txt(520));
- return 0;
- }
- sprintf(tmp_sql,"DELETE FROM `%s` WHERE `message_id` = \"%d\"", mail_db, atoi(mail_row[0]));
- if(mysql_query(&mail_handle, tmp_sql) ) {
- mysql_free_result(mail_res);
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- //else clif_displaymessage(sd->fd,"Message deleted.");
- else clif_displaymessage(sd->fd,msg_txt(521));
- }
-
- mysql_free_result(mail_res);
-
- } else {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
-
- return 0;
-}
-
-int mail_send(struct map_session_data *sd, char *name, char *message, int flag)
-{
- nullpo_retr (0, sd);
-
- if(pc_isGM(sd) < 80 && sd->mail_counter > 0) {
- //clif_displaymessage(sd->fd,"You must wait 10 minutes before sending another message");
- clif_displaymessage(sd->fd,msg_txt(522));
- return 0;
- }
-
- if(strcmp(name,"*")==0) {
- if(pc_isGM(sd) < 80) {
- //clif_displaymessage(sd->fd, "Access Denied.");
- clif_displaymessage(sd->fd, msg_txt(523));
- return 0;
- }
- else
- sprintf(tmp_sql,"SELECT DISTINCT `account_id` FROM `%s` WHERE `account_id` <> '%d' ORDER BY `account_id`", char_db, sd->status.account_id);
- }
- else
- sprintf(tmp_sql,"SELECT `account_id`,`name` FROM `%s` WHERE `name` = \"%s\"", char_db, jstrescape(name));
-
- if (mysql_query(&mail_handle, tmp_sql)) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
-
- mail_res = mysql_store_result(&mail_handle);
- if(mail_res) {
- if (mysql_num_rows(mail_res) == 0) {
- mysql_free_result(mail_res);
- //clif_displaymessage(sd->fd,"Character does not exist.");
- clif_displaymessage(sd->fd,msg_txt(524));
- return 0;
- }
-
- while ((mail_row = mysql_fetch_row(mail_res))) {
- if(strcmp(name,"*")==0) {
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`from_account_id`,`from_char_name`,`message`,`priority`)"
- " VALUES ('%d', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), sd->status.account_id, sd->status.name, jstrescape(message), flag);
- }
- else {
- sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`to_char_name`,`from_account_id`,`from_char_name`,`message`,`priority`)"
- " VALUES ('%d', '%s', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), mail_row[1], sd->status.account_id, sd->status.name, jstrescape(message), flag);
- if(pc_isGM(sd) < 80)
- sd->mail_counter=5;
- }
-
- if(mysql_query(&mail_handle, tmp_sql) ) {
- mysql_free_result(mail_res);
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- return 0;
- }
- }
- }
-
- //clif_displaymessage(sd->fd,"Mail has been sent.");
- clif_displaymessage(sd->fd,msg_txt(525));
-
- return 0;
-}
-
-static int mail_check_timer_sub(struct map_session_data *sd, va_list va)
-{
- int id = va_arg(va, int);
- if(pc_isGM(sd) < 80 && sd->mail_counter > 0)
- sd->mail_counter--;
- if(sd->status.account_id==id)
- clif_displaymessage(sd->fd, msg_txt(526)); //you got new email.
- return 0;
-}
-
-int mail_check_timer(int tid,unsigned int tick,int id,int data)
-{
- if(mail_timer != tid)
- return 0;
-
- sprintf(tmp_sql,"SELECT DISTINCT `to_account_id` FROM `%s` WHERE `read_flag` = '0' AND `check_flag` = '0'", mail_db);
-
- if (mysql_query(&mail_handle, tmp_sql)) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
- return 0;
- }
-
- mail_res = mysql_store_result(&mail_handle);
-
- if (mail_res) {
- if (mysql_num_rows(mail_res) == 0) {
- mysql_free_result(mail_res);
- mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
- return 0;
- }
-
- while ((mail_row = mysql_fetch_row(mail_res)))
- clif_foreachclient(mail_check_timer_sub, atoi(mail_row[0]));
- }
-
- sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `check_flag`= '0' ", mail_db);
- if(mysql_query(&mail_handle, tmp_sql) ) {
- ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
- ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
- }
-
- mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
- return 0;
-}
-
-int do_init_mail(void)
-{
- add_timer_func_list(mail_check_timer,"mail_check_timer");
- mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
- return 0;
-}
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef TXT_ONLY
+// Mail System for eAthena SQL
+// Created by Valaris
+// moved all strings to msg_athena.conf [Lupus]
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/strlib.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "atcommand.h"
+#include "pc.h"
+#include "mail.h"
+
+#ifndef TXT_ONLY
+ #ifndef SQL_DEBUG
+
+ #define mysql_query(_x, _y) mysql_real_query(_x, _y, strlen(_y))
+
+ #else
+
+ #define mysql_query(_x, _y) debug_mysql_query(__FILE__, __LINE__, _x, _y)
+
+ #endif
+#endif
+
+int MAIL_CHECK_TIME = 120000;
+int mail_timer;
+//extern char *msg_table[1000]; // Server messages (0-499 reserved for GM commands, 500-999 reserved for others)
+
+int mail_check(struct map_session_data *sd,int type)
+{
+ int i = 0, new_ = 0, priority = 0;
+ char message[80];
+
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`read_flag`,`priority`,`check_flag` "
+ "FROM `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id`", mail_db, sd->status.account_id);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ //clif_displaymessage(sd->fd,"You have no messages.");
+ clif_displaymessage(sd->fd, msg_txt(516));
+
+ mysql_free_result(mail_res);
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ i++;
+ if(!atoi(mail_row[5])) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ if(!atoi(mail_row[3])) {
+ new_++;
+ if(atoi(mail_row[4]))
+ priority++;
+ if(type==2 || type==3) {
+ if(atoi(mail_row[4])) {
+ snprintf(message, 80, msg_txt(511), i, mail_row[2]);
+ clif_displaymessage(sd->fd, message);
+ } else {
+ //sprintf(message, "%d - From : %s (New)", i, mail_row[2]);
+ snprintf(message, 80, msg_txt(512), i, mail_row[2]);
+ clif_displaymessage(sd->fd, message);
+ }
+ }
+ } else if(type==2){
+ snprintf(message, 80, msg_txt(513), i, mail_row[2]);
+ clif_displaymessage(sd->fd, message);
+ }
+ }
+
+ mysql_free_result(mail_res);
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ if(i>0 && new_>0 && type==1) {
+ //sprintf(message, "You have %d new messages.", new_);
+ sprintf(message, msg_txt(514), new_);
+
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ if(i>0 && new_>0 && priority>0 && type==1) {
+ //sprintf(message, "You have %d unread priority messages.", priority);
+ sprintf(message, msg_txt(515), priority);
+ clif_displaymessage(sd->fd, jstrescape(message));
+ }
+ if(!new_) {
+ //clif_displaymessage(sd->fd, "You have no new messages.");
+ clif_displaymessage(sd->fd, msg_txt(516));
+ }
+
+ return 0;
+}
+
+int mail_read(struct map_session_data *sd, int message_id)
+{
+
+ char message[80];
+
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`from_char_name`,`message`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd, "Message not found.");
+ clif_displaymessage(sd->fd, msg_txt(517));
+ return 0;
+ }
+
+ if ((mail_row = mysql_fetch_row(mail_res))) {
+ if(!atoi(mail_row[6])) {
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ //sprintf(message, "Reading message from %s", mail_row[2]);
+ sprintf(message, msg_txt(518), mail_row[2]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+
+ sprintf(message, "%s", mail_row[3]);
+ clif_displaymessage(sd->fd, jstrescape(message));
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `read_flag`='1' WHERE `message_id`= \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+ }
+
+ mysql_free_result(mail_res);
+
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+int mail_delete(struct map_session_data *sd, int message_id)
+{
+ nullpo_retr (0, sd);
+
+ sprintf(tmp_sql,"SELECT `message_id`,`to_account_id`,`read_flag`,`priority`,`check_flag` from `%s` WHERE `to_account_id` = \"%d\" ORDER by `message_id` LIMIT %d, 1",mail_db,sd->status.account_id,message_id-1);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd, "Message not found.");
+ clif_displaymessage(sd->fd, msg_txt(517));
+ return 0;
+ }
+
+ if ((mail_row = mysql_fetch_row(mail_res))) {
+ if(!atoi(mail_row[2]) && atoi(mail_row[3])) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"Cannot delete unread priority mail.");
+ clif_displaymessage(sd->fd,msg_txt(519));
+
+ return 0;
+ }
+ if(!atoi(mail_row[4])) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"You have recieved new mail, use @listmail before deleting.");
+ clif_displaymessage(sd->fd,msg_txt(520));
+ return 0;
+ }
+ sprintf(tmp_sql,"DELETE FROM `%s` WHERE `message_id` = \"%d\"", mail_db, atoi(mail_row[0]));
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ mysql_free_result(mail_res);
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ //else clif_displaymessage(sd->fd,"Message deleted.");
+ else clif_displaymessage(sd->fd,msg_txt(521));
+ }
+
+ mysql_free_result(mail_res);
+
+ } else {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ return 0;
+}
+
+int mail_send(struct map_session_data *sd, char *name, char *message, int flag)
+{
+ nullpo_retr (0, sd);
+
+ if(pc_isGM(sd) < 80 && sd->mail_counter > 0) {
+ //clif_displaymessage(sd->fd,"You must wait 10 minutes before sending another message");
+ clif_displaymessage(sd->fd,msg_txt(522));
+ return 0;
+ }
+
+ if(strcmp(name,"*")==0) {
+ if(pc_isGM(sd) < 80) {
+ //clif_displaymessage(sd->fd, "Access Denied.");
+ clif_displaymessage(sd->fd, msg_txt(523));
+ return 0;
+ }
+ else
+ sprintf(tmp_sql,"SELECT DISTINCT `account_id` FROM `%s` WHERE `account_id` <> '%d' ORDER BY `account_id`", char_db, sd->status.account_id);
+ }
+ else
+ sprintf(tmp_sql,"SELECT `account_id`,`name` FROM `%s` WHERE `name` = \"%s\"", char_db, jstrescape(name));
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+ if(mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ //clif_displaymessage(sd->fd,"Character does not exist.");
+ clif_displaymessage(sd->fd,msg_txt(524));
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res))) {
+ if(strcmp(name,"*")==0) {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`from_account_id`,`from_char_name`,`message`,`priority`)"
+ " VALUES ('%d', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), sd->status.account_id, sd->status.name, jstrescape(message), flag);
+ }
+ else {
+ sprintf(tmp_sql, "INSERT DELAYED INTO `%s` (`to_account_id`,`to_char_name`,`from_account_id`,`from_char_name`,`message`,`priority`)"
+ " VALUES ('%d', '%s', '%d', '%s', '%s', '%d')",mail_db, atoi(mail_row[0]), mail_row[1], sd->status.account_id, sd->status.name, jstrescape(message), flag);
+ if(pc_isGM(sd) < 80)
+ sd->mail_counter=5;
+ }
+
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ mysql_free_result(mail_res);
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ return 0;
+ }
+ }
+ }
+
+ //clif_displaymessage(sd->fd,"Mail has been sent.");
+ clif_displaymessage(sd->fd,msg_txt(525));
+
+ return 0;
+}
+
+static int mail_check_timer_sub(struct map_session_data *sd, va_list va)
+{
+ int id = va_arg(va, int);
+ if(pc_isGM(sd) < 80 && sd->mail_counter > 0)
+ sd->mail_counter--;
+ if(sd->status.account_id==id)
+ clif_displaymessage(sd->fd, msg_txt(526)); //you got new email.
+ return 0;
+}
+
+int mail_check_timer(int tid,unsigned int tick,int id,int data)
+{
+ if(mail_timer != tid)
+ return 0;
+
+ sprintf(tmp_sql,"SELECT DISTINCT `to_account_id` FROM `%s` WHERE `read_flag` = '0' AND `check_flag` = '0'", mail_db);
+
+ if (mysql_query(&mail_handle, tmp_sql)) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+ }
+
+ mail_res = mysql_store_result(&mail_handle);
+
+ if (mail_res) {
+ if (mysql_num_rows(mail_res) == 0) {
+ mysql_free_result(mail_res);
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+ }
+
+ while ((mail_row = mysql_fetch_row(mail_res)))
+ clif_foreachclient(mail_check_timer_sub, atoi(mail_row[0]));
+ }
+
+ sprintf(tmp_sql,"UPDATE `%s` SET `check_flag`='1' WHERE `check_flag`= '0' ", mail_db);
+ if(mysql_query(&mail_handle, tmp_sql) ) {
+ ShowSQL("DB error - %s\n",mysql_error(&mail_handle));
+ ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql);
+ }
+
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+}
+
+int do_init_mail(void)
+{
+ add_timer_func_list(mail_check_timer,"mail_check_timer");
+ mail_timer=add_timer(gettick()+MAIL_CHECK_TIME,mail_check_timer,0,0);
+ return 0;
+}
+
+#endif
diff --git a/src/map/mail.h b/src/map/mail.h
index 2460de238..d6f865f78 100644
--- a/src/map/mail.h
+++ b/src/map/mail.h
@@ -1,12 +1,12 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-// Mail System for eAthena
-// Created by Valaris
-
-int mail_check(struct map_session_data *sd, int type);
-int mail_read(struct map_session_data *sd, int message_id);
-int mail_delete(struct map_session_data *sd, int message_id);
-int mail_send(struct map_session_data *sd, char *name, char *message, int flag);
-
-int do_init_mail(void);
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+// Mail System for eAthena
+// Created by Valaris
+
+int mail_check(struct map_session_data *sd, int type);
+int mail_read(struct map_session_data *sd, int message_id);
+int mail_delete(struct map_session_data *sd, int message_id);
+int mail_send(struct map_session_data *sd, char *name, char *message, int flag);
+
+int do_init_mail(void);
diff --git a/src/map/map.h b/src/map/map.h
index 0cb147e83..2f61a07d5 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -1,1491 +1,1491 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _MAP_H_
-#define _MAP_H_
-
-#include <stdarg.h>
-#include "../common/mmo.h"
-#include "../common/mapindex.h"
-#include "../common/db.h"
-
-//Uncomment to enable the Cell Stack Limit mod.
-//It's only config is the battle_config cell_stack_limit.
-//Only chars affected are those defined in BL_CHAR (mobs and players currently)
-//#define CELL_NOSTACK
-
-//Uncomment to enable circular area checks.
-//By default, all range checks in Aegis are of Square shapes, so a weapon range
-// of 10 allows you to attack from anywhere within a 21x21 area.
-//Enabling this changes such checks to circular checks, which is more realistic,
-// but is not the official behaviour.
-//#define CIRCULAR_AREA
-
-#define MAX_PC_CLASS 4050
-#define PC_CLASS_BASE 0
-#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001)
-#define PC_CLASS_BASE3 (PC_CLASS_BASE2 + 22)
-#define MAX_NPC_PER_MAP 512
-#define BLOCK_SIZE 8
-#define AREA_SIZE battle_config.area_size
-#define LIFETIME_FLOORITEM 60
-#define DAMAGELOG_SIZE 30
-#define LOOTITEM_SIZE 10
-#define MAX_STATUSCHANGE 250
-//Quick defines to know which are the min-max common ailments. [Skotlex]
-//Because of the way the headers are included.. these must be replaced for actual values.
-//Remember to update as needed! Min is SC_STONE and max is SC_DPOISON currently.
-#define SC_COMMON_MIN 0
-#define SC_COMMON_MAX 10
-
-#define MAX_SKILL_LEVEL 100
-#define MAX_SKILLUNITGROUP 25
-#define MAX_SKILLUNITGROUPTICKSET 25
-#define MAX_SKILLTIMERSKILL 15
-#define MAX_MOBSKILL 50
-#define MAX_MOB_LIST_PER_MAP 128
-#define MAX_EVENTQUEUE 2
-#define MAX_EVENTTIMER 32
-#define NATURAL_HEAL_INTERVAL 500
-#define MAX_FLOORITEM 500000
-#define MAX_LEVEL 1000
-#define MAX_WALKPATH 32
-#define MAX_DROP_PER_MAP 48
-#define MAX_IGNORE_LIST 80
-#define MAX_VENDING 12
-#define MOBID_EMPERIUM 1288
-
-#define MAX_PC_BONUS 10
-//Designed for search functions, species max number of matches to display.
-#define MAX_SEARCH 5
-#define MAX_DUEL 1024
-
-#define map_id2index(id) map[(id)].index
-
-//The following system marks a different job ID system used by the map server,
-//which makes a lot more sense than the normal one. [Skotlex]
-//
-//These marks the "level" of the job.
-#define JOBL_2_1 0x100 //256
-#define JOBL_2_2 0x200 //512
-#define JOBL_2 0x300
-
-#define JOBL_UPPER 0x1000 //4096
-#define JOBL_BABY 0x2000 //8192
-
-//for filtering and quick checking.
-#define MAPID_UPPERMASK 0x0fff
-#define MAPID_BASEMASK 0x00ff
-//First Jobs
-//Note the oddity of the novice:
-//Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too...
-enum {
- MAPID_NOVICE = 0x0,
- MAPID_SWORDMAN,
- MAPID_MAGE,
- MAPID_ARCHER,
- MAPID_ACOLYTE,
- MAPID_MERCHANT,
- MAPID_THIEF,
- MAPID_TAEKWON,
- MAPID_WEDDING,
- MAPID_GUNSLINGER,
- MAPID_NINJA,
- MAPID_XMAS, // [Valaris]
-//2_1 classes
- MAPID_SUPER_NOVICE = JOBL_2_1|0x0,
- MAPID_KNIGHT,
- MAPID_WIZARD,
- MAPID_HUNTER,
- MAPID_PRIEST,
- MAPID_BLACKSMITH,
- MAPID_ASSASSIN,
- MAPID_STAR_GLADIATOR,
-//2_2 classes
- MAPID_CRUSADER = JOBL_2_2|0x1,
- MAPID_SAGE,
- MAPID_BARDDANCER,
- MAPID_MONK,
- MAPID_ALCHEMIST,
- MAPID_ROGUE,
- MAPID_SOUL_LINKER,
-//1-1, advanced
- MAPID_NOVICE_HIGH = JOBL_UPPER|0x0,
- MAPID_SWORDMAN_HIGH,
- MAPID_MAGE_HIGH,
- MAPID_ARCHER_HIGH,
- MAPID_ACOLYTE_HIGH,
- MAPID_MERCHANT_HIGH,
- MAPID_THIEF_HIGH,
-//2_1 advanced
- MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|0x1,
- MAPID_HIGH_WIZARD,
- MAPID_SNIPER,
- MAPID_HIGH_PRIEST,
- MAPID_WHITESMITH,
- MAPID_ASSASSIN_CROSS,
-//2_2 advanced
- MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|0x1,
- MAPID_PROFESSOR,
- MAPID_CLOWNGYPSY,
- MAPID_CHAMPION,
- MAPID_CREATOR,
- MAPID_STALKER,
-//1-1 baby
- MAPID_BABY = JOBL_BABY|0x0,
- MAPID_BABY_SWORDMAN,
- MAPID_BABY_MAGE,
- MAPID_BABY_ARCHER,
- MAPID_BABY_ACOLYTE,
- MAPID_BABY_MERCHANT,
- MAPID_BABY_THIEF,
- MAPID_BABY_TAEKWON,
-//2_1 baby
- MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|0x0,
- MAPID_BABY_KNIGHT,
- MAPID_BABY_WIZARD,
- MAPID_BABY_HUNTER,
- MAPID_BABY_PRIEST,
- MAPID_BABY_BLACKSMITH,
- MAPID_BABY_ASSASSIN,
- MAPID_BABY_STAR_GLADIATOR,
-//2_2 baby
- MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|0x1,
- MAPID_BABY_SAGE,
- MAPID_BABY_BARDDANCER,
- MAPID_BABY_MONK,
- MAPID_BABY_ALCHEMIST,
- MAPID_BABY_ROGUE,
- MAPID_BABY_SOUL_LINKER,
-};
-
-//Don't change this, as the client seems to always send/receive 80 characters as it currently is. [Skotlex]
-#define MESSAGE_SIZE 80
-
-#define DEFAULT_AUTOSAVE_INTERVAL 5*60*1000
-
-//Specifies maps where players may hit each other
-#define map_flag_vs(m) (map[m].flag.pvp || map[m].flag.gvg_dungeon || map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
-//Specifies maps that have special GvG/WoE restrictions
-#define map_flag_gvg(m) (map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
-//Specifies if the map is tagged as GvG/WoE (regardless of agit_flag status)
-#define map_flag_gvg2(m) (map[m].flag.gvg || map[m].flag.gvg_castle)
-//Caps values to min/max
-#define cap_value(a, min, max) (a>=max?max:a<=min?min:a)
-
-//This stackable implementation does not means a BL can be more than one type at a time, but it's
-//meant to make it easier to check for multiple types at a time on invocations such as
-// map_foreach* calls [Skotlex]
-enum {
- BL_NUL = 0x000,
- BL_PC = 0x001,
- BL_MOB = 0x002,
- BL_PET = 0x004,
- BL_HOM = 0x008, //[blackhole89]
- BL_ITEM = 0x010,
- BL_SKILL = 0x020,
- BL_NPC = 0x040,
- BL_CHAT = 0x080,
-};
-
-//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet.
-#define BL_CHAR (BL_PC|BL_MOB|BL_HOM)
-#define BL_ALL 0xfff
-
-enum { WARP, SHOP, SCRIPT, MONS };
-
-enum {
- RC_FORMLESS=0,
- RC_UNDEAD,
- RC_BRUTE,
- RC_PLANT,
- RC_INSECT,
- RC_FISH,
- RC_DEMON,
- RC_DEMIHUMAN,
- RC_ANGEL,
- RC_DRAGON,
- RC_BOSS,
- RC_NONBOSS,
- RC_MAX
-};
-
-enum {
- ELE_NEUTRAL=0,
- ELE_WATER,
- ELE_EARTH,
- ELE_FIRE,
- ELE_WIND,
- ELE_POISON,
- ELE_HOLY,
- ELE_DARK,
- ELE_GHOST,
- ELE_UNDEAD,
- ELE_MAX
-};
-
-enum {
- IG_BLUEBOX=1,
- IG_VIOLETBOX, //2
- IG_CARDALBUM, //3
- IG_GIFTBOX, //4
- IG_SCROLLBOX, //5
- IG_FINDINGORE, //6
- IG_COOKIEBAG, //7
- IG_POTION, //8
- IG_HERBS, //9
- IG_FRUITS, //10
- IG_MEAT, //11
- IG_CANDY, //12
- IG_JUICE, //13
- IG_FISH, //14
- IG_BOXES, //15
- IG_GEMSTONE, //16
- IG_JELLOPY, //17
- IG_ORE, //18
- IG_FOOD, //19
- IG_RECOVERY, //20
- IG_MINERALS, //21
- IG_TAMING, //22
- IG_SCROLLS, //23
- IG_QUIVERS, //24
- IG_MASKS, //25
- IG_ACCESORY, //26
- IG_JEWELS, //27
- IG_GIFTBOX_1, //28
- IG_GIFTBOX_2, //29
- IG_GIFTBOX_3, //30
- IG_GIFTBOX_4, //31
- IG_EGGBOY, //32
- IG_EGGGIRL, //33
- IG_GIFTBOXCHINA, //34
- IG_LOTTOBOX, //35
- MAX_ITEMGROUP,
-} item_group_list;
-
-enum {
- ATF_SELF=0x01,
- ATF_TARGET=0x02,
- ATF_SHORT=0x04,
- ATF_LONG=0x08
-} auto_trigger_flag;
-
-struct block_list {
- struct block_list *next,*prev;
- int id;
- short m,x,y;
- unsigned char type;
- unsigned char subtype;
-};
-
-struct walkpath_data {
- unsigned char path_len,path_pos,path_half;
- unsigned char path[MAX_WALKPATH];
-};
-struct shootpath_data {
- int rx,ry,len;
- int x[MAX_WALKPATH];
- int y[MAX_WALKPATH];
-};
-
-struct skill_timerskill {
- int timer;
- int src_id;
- int target_id;
- int map;
- short x,y;
- short skill_id,skill_lv;
- int type;
- int flag;
-};
-
-struct skill_unit_group;
-struct skill_unit {
- struct block_list bl;
-
- struct skill_unit_group *group;
-
- int limit;
- int val1,val2;
- short alive,range;
-};
-
-struct skill_unit_group {
- int src_id;
- int party_id;
- int guild_id;
- int map;
- int target_flag; //Holds BCT_* flag for battle_check_target
- int bl_flag; //Holds BL_* flag for map_foreachin* functions
- unsigned int tick;
- int limit,interval;
-
- int skill_id,skill_lv;
- int val1,val2,val3;
- char *valstr;
- int unit_id;
- int group_id;
- int unit_count,alive_count;
- struct skill_unit *unit;
- struct {
- unsigned ammo_consume : 1;
- unsigned magic_power : 1;
- unsigned into_abyss : 1;
- unsigned song_dance : 2; //0x1 Song/Dance, 0x2 Ensemble
- } state;
-};
-struct skill_unit_group_tickset {
- unsigned int tick;
- int id;
-};
-
-struct unit_data {
- struct block_list *bl;
- struct walkpath_data walkpath;
- struct skill_timerskill *skilltimerskill[MAX_SKILLTIMERSKILL];
- struct skill_unit_group *skillunit[MAX_SKILLUNITGROUP];
- struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET];
- short attacktarget_lv;
- short to_x,to_y;
- short skillx,skilly;
- short skillid,skilllv;
- int skilltarget;
- int skilltimer;
- int target;
- int attacktimer;
- int walktimer;
- int chaserange;
- unsigned int attackabletime;
- unsigned int canact_tick;
- unsigned int canmove_tick;
- unsigned char dir;
- unsigned char walk_count;
- struct {
- unsigned change_walk_target : 1 ;
- unsigned skillcastcancel : 1 ;
- unsigned attack_continue : 1 ;
- unsigned walk_easy : 1 ;
- unsigned running : 1;
- } state;
-};
-
-//Basic damage info of a weapon
-//Required because players have two of these, one in status_data and another
-//for their left hand weapon.
-struct weapon_atk {
- unsigned short atk, atk2;
- unsigned short range;
- unsigned char ele;
-};
-
-//For holding basic status (which can be modified by status changes)
-struct status_data {
- unsigned int
- hp, sp,
- max_hp, max_sp;
- unsigned short
- str, agi, vit, int_, dex, luk,
- batk,
- matk_min, matk_max,
- speed,
- amotion, adelay, dmotion,
- mode;
- short
- hit, flee, cri, flee2,
- def2, mdef2,
- aspd_rate;
- unsigned char
- def_ele, ele_lv,
- size, race;
- signed char
- def, mdef;
- struct weapon_atk rhw, *lhw; //Right Hand/Left Hand Weapon. Only players have a lhw (hence it's a pointer)
-};
-
-struct script_reg {
- int index;
- int data;
-};
-struct script_regstr {
- int index;
- char data[256];
-};
-
-struct status_change_entry {
- int timer;
- int val1,val2,val3,val4;
-};
-
-struct status_change {
- struct status_change_entry data[MAX_STATUSCHANGE];
- short count;
- unsigned short opt1,opt2;
- unsigned int opt3, option; //Note that older packet versions use short here.
-};
-
-struct vending {
- short index;
- unsigned short amount;
- unsigned int value;
-};
-
-struct weapon_data {
- int atkmods[3];
- // all the variables except atkmods get zero'ed in each call of status_calc_pc
- // NOTE: if you want to add a non-zeroed variable, you need to update the malloc_set call
- // in status_calc_pc as well! All the following are automatically zero'ed. [Skotlex]
- int overrefine;
- int star;
- int ignore_def_ele;
- int ignore_def_race;
- int def_ratio_atk_ele;
- int def_ratio_atk_race;
- int addele[ELE_MAX];
- int addrace[RC_MAX];
- int addrace2[RC_MAX];
- int addsize[3];
-
- short ignore_def_mob;
- struct drain_data {
- short rate;
- short per;
- short value;
- unsigned type:1;
- } hp_drain[RC_MAX], sp_drain[RC_MAX];
-
- short add_damage_classid[MAX_PC_BONUS];
- int add_damage_classrate[MAX_PC_BONUS];
- int add_damage_class_count;
-};
-
-struct view_data {
- unsigned short
- class_,
- weapon,
- shield, //Or left-hand weapon.
- head_top,
- head_mid,
- head_bottom,
- hair_style,
- hair_color,
- cloth_color;
- char sex;
- unsigned dead_sit : 2;
-};
-
-//Additional regen data that only players have.
-struct regen_data_sub {
- unsigned short
- hp,sp;
-
- //tick accumulation before healing.
- struct {
- unsigned int hp,sp;
- } tick;
-
- //Regen rates (where every 1 means +100% regen)
- struct {
- unsigned char hp,sp;
- } rate;
-};
-
-struct regen_data {
-
- unsigned short flag; //Marks what stuff you may heal or not.
- unsigned short
- hp,sp,shp,ssp;
-
- //tick accumulation before healing.
- struct {
- unsigned int hp,sp,shp,ssp;
- } tick;
-
- //Regen rates (where every 1 means +100% regen)
- struct {
- unsigned char
- hp,sp,shp,ssp;
- } rate;
-
- struct {
- unsigned walk:1; //Can you regen even when walking?
- unsigned gc:1; //Tags when you should have double regen due to GVG castle
- unsigned overweight :2; //overweight state (1: 50%, 2: 90%)
- unsigned block :2; //Block regen flag (1: Hp, 2: Sp)
- } state;
-
- //skill-regen, sitting-skill-regen (since not all chars with regen need it)
- struct regen_data_sub *sregen, *ssregen;
-};
-
-struct party_member_data {
- struct map_session_data *sd;
- unsigned int hp; //For HP,x,y refreshing.
- unsigned short x, y;
-};
-
-struct party_data {
- struct party party;
- struct party_member_data data[MAX_PARTY];
- unsigned char itemc; //For item distribution.
- struct {
- unsigned monk : 1; //There's at least one monk in party?
- unsigned sg : 1; //There's at least one Star Gladiator in party?
- unsigned snovice :1; //There's a Super Novice
- unsigned tk : 1; //There's a taekwon
- } state;
-};
-
-struct npc_data;
-struct pet_db;
-struct homunculus_db; //[orn]
-struct item_data;
-struct square;
-
-struct map_session_data {
- struct block_list bl;
- struct unit_data ud;
- struct view_data vd;
- struct status_data base_status, battle_status;
- struct weapon_atk base_lhw, battle_lhw; //Left-hand weapon atk data.
- struct status_change sc;
- struct regen_data regen;
- struct regen_data_sub sregen, ssregen;
- //NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in
- //status_calc_pc, while special_state is recalculated in each call. [Skotlex]
- struct {
- unsigned auth : 1;
- unsigned menu_or_input : 1;
- unsigned dead_sit : 2;
- unsigned waitingdisconnect : 1;
- unsigned lr_flag : 2;
- unsigned connect_new : 1;
- unsigned arrow_atk : 1;
- unsigned skill_flag : 1;
- unsigned gangsterparadise : 1;
- unsigned rest : 1;
- unsigned storage_flag : 2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex]
- unsigned snovice_flag : 4;
- // originally by Qamera, adapted by celest
- unsigned event_death : 1;
- unsigned event_kill_pc : 1;
- unsigned event_disconnect : 1;
- unsigned event_kill_mob : 1;
- unsigned event_baselvup : 1;
- unsigned event_joblvup : 1;
- unsigned event_loadmap : 1;
- // Abracadabra bugfix by Aru
- unsigned abra_flag : 1;
- unsigned autotrade : 1; //By Fantik
- unsigned reg_dirty : 3; //By Skotlex (marks whether registry variables have been saved or not yet)
- unsigned showdelay :1;
- unsigned showexp :1;
- unsigned showzeny :1;
- unsigned mainchat :1; //[LuzZza]
- unsigned noask :1; // [LuzZza]
- unsigned trading :1; //[Skotlex] is 1 only after a trade has started.
- unsigned deal_locked :2; //1: Clicked on OK. 2: Clicked on TRADE
- unsigned party_sent :1;
- unsigned guild_sent :1;
- unsigned monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo]
- unsigned size :2; // for tiny/large types
- unsigned night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex]
- unsigned finalsave :1; //Signals whether the final save for the char was done or not yet. Meant to prevent exploits and the like. [Skotlex]
- unsigned blockedmove :1;
- unsigned using_fake_npc :1;
- unsigned rewarp :1; //Signals that a player should warp as soon as he is done loading a map. [Skotlex]
- unsigned killer : 1;
- unsigned killable : 1;
- unsigned doridori : 1;
- unsigned ignoreAll : 1;
- unsigned short autoloot;
- struct guild *gmaster_flag;
- } state;
- struct {
- unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
- unsigned restart_full_recover : 1;
- unsigned no_castcancel : 1;
- unsigned no_castcancel2 : 1;
- unsigned no_sizefix : 1;
- unsigned no_gemstone : 1;
- unsigned intravision : 1; // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG]
- unsigned perfect_hiding : 1; // [Valaris]
- } special_state;
- int login_id1, login_id2;
- unsigned short class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex]
-
- int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
- struct mmo_charstatus status;
- struct registry save_reg;
-
- struct item_data *inventory_data[MAX_INVENTORY];
- short equip_index[11];
- unsigned int weight,max_weight;
- int cart_weight,cart_max_weight,cart_num,cart_max_num;
- int fd;
- unsigned short mapindex;
- unsigned short prev_speed,prev_adelay;
- unsigned char head_dir; //0: Look forward. 1: Look right, 2: Look left.
- unsigned int client_tick;
- int npc_id,areanpc_id,npc_shopid;
- int npc_item_flag; //Marks the npc_id with which you can use items during interactions with said npc (see script command enable_itemuse)
- int npc_menu, max_menu;
- int npc_amount;
- struct script_state *st;
- char npc_str[256];
- int npc_timer_id; //For player attached npc timers. [Skotlex]
- unsigned int chatID;
- time_t idletime;
-
- struct{
- char name[NAME_LENGTH];
- } ignore[MAX_IGNORE_LIST];
-
- int followtimer; // [MouseJstr]
- int followtarget;
-
- time_t emotionlasttime; // to limit flood with emotion packets
-
- short skillitem,skillitemlv;
- short skillid_old,skilllv_old;
- short skillid_dance,skilllv_dance;
- char blockskill[MAX_SKILL]; // [celest]
- int cloneskill_id;
- int menuskill_id, menuskill_lv;
-
- int invincible_timer;
- unsigned int canlog_tick;
- unsigned int canuseitem_tick; // [Skotlex]
- unsigned int cantalk_tick;
-
- short weapontype1,weapontype2;
- short disguise; // [Valaris]
-
- struct weapon_data right_weapon, left_weapon;
-
- // here start arrays to be globally zeroed at the beginning of status_calc_pc()
- int param_bonus[6],param_equip[6]; //Stores card/equipment bonuses.
- int subele[ELE_MAX];
- int subrace[RC_MAX];
- int subrace2[RC_MAX];
- int subsize[3];
- int reseff[SC_COMMON_MAX-SC_COMMON_MIN+1];
- int weapon_coma_ele[ELE_MAX];
- int weapon_coma_race[RC_MAX];
- int weapon_atk[16];
- int weapon_atk_rate[16];
- int arrow_addele[ELE_MAX];
- int arrow_addrace[RC_MAX];
- int arrow_addsize[3];
- int magic_addele[ELE_MAX];
- int magic_addrace[RC_MAX];
- int magic_addsize[3];
- int critaddrace[RC_MAX];
- int expaddrace[RC_MAX];
- int itemgrouphealrate[MAX_ITEMGROUP];
- short sp_gain_race[RC_MAX];
- // zeroed arrays end here.
- // zeroed structures start here
- struct s_autospell{
- short id, lv, rate, card_id;
- } autospell[MAX_PC_BONUS], autospell2[MAX_PC_BONUS];
- struct s_addeffect{
- short id, rate, arrow_rate;
- unsigned char flag;
- } addeff[MAX_PC_BONUS], addeff2[MAX_PC_BONUS];
- struct { //skillatk raises bonus dmg% of skills, skillblown increases bonus blewcount for some skills.
- short id, val;
- } skillatk[MAX_PC_BONUS], skillblown[MAX_PC_BONUS];
- struct {
- short class_, rate;
- } add_def[MAX_PC_BONUS], add_mdef[MAX_PC_BONUS],
- add_dmg[MAX_PC_BONUS], add_mdmg[MAX_PC_BONUS];
- struct s_add_drop {
- short id, group;
- int race, rate;
- } add_drop[MAX_PC_BONUS];
- struct {
- int nameid;
- int rate;
- } itemhealrate[MAX_PC_BONUS];
- // zeroed structures end here
- // zeroed vars start here.
- int arrow_atk,arrow_ele,arrow_cri,arrow_hit;
- int nsshealhp,nsshealsp;
- int critical_def,double_rate;
- int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex]
- int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate;
- int ignore_mdef_ele;
- int ignore_mdef_race;
- int perfect_hit;
- int perfect_hit_add;
- int get_zeny_rate;
- int get_zeny_num; //Added Get Zeny Rate [Skotlex]
- int double_add_rate;
- int short_weapon_damage_return,long_weapon_damage_return;
- int magic_damage_return; // AppleGirl Was Here
- int random_attack_increase_add,random_attack_increase_per; // [Valaris]
- int break_weapon_rate,break_armor_rate;
- int crit_atk_rate;
- int hp_loss_rate;
- int sp_loss_rate;
- int classchange; // [Valaris]
- int speed_add_rate, aspd_add_rate;
- unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex]
-
- short splash_range, splash_add_range;
- short add_steal_rate;
- short hp_loss_value;
- short sp_loss_value;
- short hp_loss_type;
- short sp_gain_value, hp_gain_value;
- short sp_vanish_rate;
- short sp_vanish_per;
- short add_drop_count;
- unsigned short unbreakable; // chance to prevent ANY equipment breaking [celest]
- unsigned short unbreakable_equip; //100% break resistance on certain equipment
- unsigned short unstripable_equip;
- short add_def_count,add_mdef_count;
- short add_dmg_count,add_mdmg_count;
-
- // zeroed vars end here.
-
- int castrate,delayrate,hprate,sprate,dsprate;
- int atk_rate;
- int speed_rate,hprecov_rate,sprecov_rate;
- int matk_rate;
- int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate;
-
- int hp_loss_tick;
- int sp_loss_tick;
-
- int itemid;
- short itemindex; //Used item's index in sd->inventory [Skotlex]
-
- short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo]
-
- short spiritball, spiritball_old;
- int spirit_timer[MAX_SKILL_LEVEL];
-
- unsigned char potion_success_counter; //Potion successes in row counter
- unsigned char mission_count; //Stores the bounty kill count for TK_MISSION
- short mission_mobid; //Stores the target mob_id for TK_MISSION
- int die_counter; //Total number of times you've died
- int devotion[5]; //Stores the char IDs of chars devoted to.
- int reg_num; //Number of registries (type numeric)
- int regstr_num; //Number of registries (type string)
-
- struct script_reg *reg;
- struct script_regstr *regstr;
-
- int trade_partner;
- struct {
- struct {
- int index, amount;
- } item[10];
- int zeny, weight;
- } deal;
-
- int party_invite,party_invite_account;
-
- 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]
- int guildspy; // [Syrus22]
- int partyspy; // [Syrus22]
-
- int vender_id;
- int vend_num;
- char message[MESSAGE_SIZE];
- struct vending vending[MAX_VENDING];
-
- struct pet_data *pd;
- struct homun_data *hd; // [blackhole89]
-
- struct{
- int m; //-1 - none, other: map index corresponding to map name.
- unsigned short index; //map index
- }feel_map[3];// 0 - Sun; 1 - Moon; 2 - Stars
- short hate_mob[3];
-
- unsigned int pvp_timer;
- short pvp_point;
- unsigned short pvp_rank, pvp_lastusers;
- unsigned short pvp_won, pvp_lost;
-
- char eventqueue[MAX_EVENTQUEUE][50];
- int eventtimer[MAX_EVENTTIMER];
- unsigned short eventcount; // [celest]
-
- unsigned char change_level; // [celest]
-
- char fakename[NAME_LENGTH]; // fake names [Valaris]
-
-#ifndef TXT_ONLY
- int mail_counter; // mail counter for mail system [Valaris]
-#endif
-
- int duel_group; // duel vars [LuzZza]
- int duel_invite;
-
- char away_message[128]; // [LuzZza]
-
-};
-
-struct {
- int members_count;
- int invites_count;
- int max_players_limit;
-} duel_list[MAX_DUEL];
-
-int duel_count;
-
-struct npc_timerevent_list {
- int timer,pos;
-};
-struct npc_label_list {
- char name[NAME_LENGTH];
- int pos;
-};
-struct npc_item_list {
- unsigned int nameid,value;
-};
-struct npc_data {
- struct block_list bl;
- struct unit_data ud; //Because they need to be able to move....
- struct view_data *vd;
- struct status_change sc; //They can't have status changes, but.. they want the visual opt values.
- short n;
- short class_;
- short speed;
- unsigned char name[NAME_LENGTH];
- unsigned char exname[NAME_LENGTH];
- int chat_id;
- unsigned int next_walktime;
-
- char eventqueue[MAX_EVENTQUEUE][50];
- int eventtimer[MAX_EVENTTIMER];
- short arenaflag;
-
- void *chatdb;
- struct npc_data *master_nd;
-
- union {
- struct {
- struct script_code *script;
- short xs,ys;
- int guild_id;
- int timer,timerid,timeramount,rid;
- unsigned int timertick;
- struct npc_timerevent_list *timer_event;
- int label_list_num;
- struct npc_label_list *label_list;
- int src_id;
- } scr;
- struct npc_item_list shop_item[1];
- struct {
- short xs,ys;
- short x,y;
- unsigned short mapindex;
- } warp;
- } u;
- //Do NOT place anything afterwards... shop data NPC will override any variables from here and on! [Skotlex]
-};
-
-//For quick linking to a guardian's info. [Skotlex]
-struct guardian_data {
- int number; //0-MAX_GUARDIANS-1 = Guardians. MAX_GUARDIANS = Emperium.
- int guild_id;
- int emblem_id;
- int guardup_lv; //Level of GD_GUARDUP skill.
- char guild_name[NAME_LENGTH];
- struct guild_castle* castle;
-};
-
-// Mob List Held in memory for Dynamic Mobs [Wizputer]
-// Expanded to specify all mob-related spawn data by [Skotlex]
-struct spawn_data {
- short class_; //Class, used because a mob can change it's class
- unsigned short m,x,y; //Spawn information (map, point, spawn-area around point)
- signed short xs,ys;
- unsigned short num; //Number of mobs using this structure.
- unsigned int level; //Custom level.
- unsigned int delay1,delay2; //Min delay before respawning after spawn/death
- struct {
- unsigned size :2; //Holds if mob has to be tiny/large
- unsigned ai :2; //Holds if mob is special ai.
- } state;
- char name[NAME_LENGTH],eventname[50]; //Name/event
-};
-
-
-struct mob_data {
- struct block_list bl;
- struct unit_data ud;
- struct view_data *vd;
- struct status_data status, *base_status; //Second one is in case of leveling up mobs, or tiny/large mobs.
- struct status_change sc;
- struct mob_db *db; //For quick data access (saves doing mob_db(md->class_) all the time) [Skotlex]
- char name[NAME_LENGTH];
- struct {
- unsigned size : 2; //Small/Big monsters.
- unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex]
- unsigned ai : 2; //Special ai for summoned monsters.
- //0: Normal mob.
- //1: Standard summon, attacks mobs.
- //2: Alchemist Marine Sphere
- //3: Alchemist Summon Flora
- } special_state; //Special mob information that does not needs to be zero'ed on mob respawn.
- struct {
- unsigned skillstate : 8;
- unsigned aggressive : 1; //Signals whether the mob AI is in aggressive mode or reactive mode. [Skotlex]
- unsigned char steal_flag; //number of steal tries (to prevent steal exploit on mobs with few items) [Lupus]
- unsigned steal_coin_flag : 1;
- unsigned soul_change_flag : 1; // Celest
- unsigned alchemist: 1;
- unsigned no_random_walk: 1;
- unsigned killer: 1;
- int provoke_flag; // Celest
- } state;
- struct guardian_data* guardian_data;
- struct {
- int id;
- int dmg;
- unsigned flag : 1; //0: Normal. 1: Homunc exp
- } dmglog[DAMAGELOG_SIZE];
- struct spawn_data *spawn; //Spawn data.
- struct item *lootitem;
- short spawn_n; //Spawn data index on the map server.
- short class_;
- short attacked_count;
- unsigned char attacked_players;
- unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex]
- int level;
- int target_id,attacked_id;
- unsigned int next_walktime;
- unsigned int last_deadtime,last_spawntime,last_thinktime,last_linktime;
- short move_fail_count;
- short lootitem_count;
- short min_chase;
-
- int deletetimer;
- int master_id,master_dist;
-
- struct npc_data *nd;
- unsigned short callback_flag;
-
- short skillidx;
- unsigned int skilldelay[MAX_MOBSKILL];
- char npc_event[50];
-};
-
-/* [blackhole89] */
-struct homun_data {
- struct block_list bl;
- struct unit_data ud;
- struct view_data *vd;
- struct status_data base_status, battle_status;
- struct status_change sc;
- struct regen_data regen;
- struct homunculus_db *homunculusDB; //[orn]
- struct s_homunculus homunculus ; //[orn]
-
- struct map_session_data *master; //pointer back to its master
- int hungry_timer; //[orn]
- unsigned int exp_next;
- char blockskill[MAX_SKILL]; // [orn]
-};
-
-struct pet_data {
- struct block_list bl;
- struct unit_data ud;
- struct view_data vd;
- struct s_pet pet;
- struct status_data status;
- struct mob_db *db;
- struct pet_db *petDB;
- int pet_hungry_timer;
- int target_id;
- short n;
- struct {
- unsigned skillbonus : 1;
- } state;
- int move_fail_count;
- unsigned int next_walktime,last_thinktime;
- short rate_fix; //Support rate as modified by intimacy (1000 = 100%) [Skotlex]
-
- struct pet_recovery { //Stat recovery
- unsigned short type; //Status Change id
- unsigned short delay; //How long before curing (secs).
- int timer;
- } *recovery; //[Valaris] / Reimplemented by [Skotlex]
-
- struct pet_bonus {
- unsigned short type; //bStr, bVit?
- unsigned short val; //Qty
- unsigned short duration; //in secs
- unsigned short delay; //Time before recasting (secs)
- int timer;
- } *bonus; //[Valaris] / Reimplemented by [Skotlex]
-
- struct pet_skill_attack { //Attack Skill
- unsigned short id;
- unsigned short lv;
- unsigned short div_; //0 = Normal skill. >0 = Fixed damage (lv), fixed div_.
- unsigned short rate; //Base chance of skill ocurrance (10 = 10% of attacks)
- unsigned short bonusrate; //How being 100% loyal affects cast rate (10 = At 1000 intimacy->rate+10%
- } *a_skill; //[Skotlex]
-
- struct pet_skill_support { //Support Skill
- unsigned short id;
- unsigned short lv;
- unsigned short hp; //Max HP% for skill to trigger (50 -> 50% for Magnificat)
- unsigned short sp; //Max SP% for skill to trigger (100 = no check)
- unsigned short delay; //Time (secs) between being able to recast.
- int timer;
- } *s_skill; //[Skotlex]
-
- struct pet_loot {
- struct item *item;
- unsigned short count;
- unsigned short weight;
- unsigned short max;
- } *loot; //[Valaris] / Rewritten by [Skotlex]
-
- struct map_session_data *msd;
-};
-
-enum { ATK_LUCKY=1,ATK_FLEE,ATK_DEF}; // 囲まれペナルティ計算用
-
-struct map_data {
- char name[MAP_NAME_LENGTH];
- unsigned short index; //Index is the map index used by the mapindex* functions.
- unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う
- unsigned char *cell; //Contains temporary cell data that is set/unset on tiles.
-#ifdef CELL_NOSTACK
- unsigned char *cell_bl; //Holds amount of bls in any given cell.
-#endif
- char *alias; // [MouseJstr]
- struct block_list **block;
- struct block_list **block_mob;
- int *block_count,*block_mob_count;
- int m;
- short xs,ys;
- short bxs,bys;
- int water_height;
- int npc_num;
- int users;
- struct {
- unsigned alias : 1;
- unsigned nomemo : 1;
- unsigned noteleport : 1;
- unsigned noreturn : 1;
- unsigned monster_noteleport : 1;
- unsigned nosave : 1;
- unsigned nobranch : 1;
- unsigned noexppenalty : 1;
- unsigned pvp : 1;
- unsigned pvp_noparty : 1;
- unsigned pvp_noguild : 1;
- unsigned pvp_nightmaredrop :1;
- unsigned pvp_nocalcrank : 1;
- unsigned gvg_castle : 1;
- unsigned gvg : 1; // Now it identifies gvg versus maps that are active 24/7
- unsigned gvg_dungeon : 1; // Celest
- unsigned gvg_noparty : 1;
- unsigned nozenypenalty : 1;
- unsigned notrade : 1;
- unsigned noskill : 1;
- unsigned nowarp : 1;
- unsigned nowarpto : 1;
- unsigned noicewall : 1; // [Valaris]
- unsigned snow : 1; // [Valaris]
- unsigned clouds : 1;
- unsigned clouds2 : 1; // [Valaris]
- unsigned fog : 1; // [Valaris]
- unsigned fireworks : 1;
- unsigned sakura : 1; // [Valaris]
- unsigned leaves : 1; // [Valaris]
- unsigned rain : 1; // [Valaris]
- unsigned indoors : 1; // celest
- unsigned nogo : 1; // [Valaris]
- unsigned nobaseexp : 1; // [Lorky] added by Lupus
- unsigned nojobexp : 1; // [Lorky]
- unsigned nomobloot : 1; // [Lorky]
- unsigned nomvploot : 1; // [Lorky]
- unsigned nightenabled :1; //For night display. [Skotlex]
- unsigned restricted : 1; // [Komurka]
- unsigned nodrop : 1;
- unsigned novending : 1;
- unsigned loadevent : 1;
- unsigned nochat :1;
- unsigned partylock :1;
- unsigned guildlock :1;
- } flag;
- struct point save;
- struct npc_data *npc[MAX_NPC_PER_MAP];
- struct {
- int drop_id;
- int drop_type;
- int drop_per;
- } drop_list[MAX_DROP_PER_MAP];
-
- struct spawn_data *moblist[MAX_MOB_LIST_PER_MAP]; // [Wizputer]
- int mob_delete_timer; // [Skotlex]
- int zone; // [Komurka]
- int jexp; // map experience multiplicator
- int bexp; // map experience multiplicator
- int nocommand; //Blocks @/# commands for non-gms. [Skotlex]
-};
-
-struct map_data_other_server {
- char name[MAP_NAME_LENGTH];
- unsigned short index; //Index is the map index used by the mapindex* functions.
- unsigned char *gat; // NULL固定にして判断
- unsigned long ip;
- unsigned int port;
-};
-
-struct flooritem_data {
- struct block_list bl;
- unsigned char subx,suby;
- int cleartimer;
- int first_get_id,second_get_id,third_get_id;
- unsigned int first_get_tick,second_get_tick,third_get_tick;
- struct item item_data;
-};
-
-enum {
- SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7
- SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15
- SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23
- SP_WEIGHT,SP_MAXWEIGHT,SP_1a,SP_1b,SP_1c,SP_1d,SP_1e,SP_1f, // 24-31
- SP_USTR,SP_UAGI,SP_UVIT,SP_UINT,SP_UDEX,SP_ULUK,SP_26,SP_27, // 32-39
- SP_28,SP_ATK1,SP_ATK2,SP_MATK1,SP_MATK2,SP_DEF1,SP_DEF2,SP_MDEF1, // 40-47
- SP_MDEF2,SP_HIT,SP_FLEE1,SP_FLEE2,SP_CRITICAL,SP_ASPD,SP_36,SP_JOBLEVEL, // 48-55
- SP_UPPER,SP_PARTNER,SP_CART,SP_FAME,SP_UNBREAKABLE, //56-60
- SP_CARTINFO=99, // 99
-
- SP_BASEJOB=119, // 100+19 - celest
- SP_BASECLASS=120, //Hmm.. why 100+19? I just use the next one... [Skotlex]
-
- // original 1000-
- SP_ATTACKRANGE=1000, SP_ATKELE,SP_DEFELE, // 1000-1002
- SP_CASTRATE, SP_MAXHPRATE, SP_MAXSPRATE, SP_SPRATE, // 1003-1006
- SP_ADDELE, SP_ADDRACE, SP_ADDSIZE, SP_SUBELE, SP_SUBRACE, // 1007-1011
- SP_ADDEFF, SP_RESEFF, // 1012-1013
- SP_BASE_ATK,SP_ASPD_RATE,SP_HP_RECOV_RATE,SP_SP_RECOV_RATE,SP_SPEED_RATE, // 1014-1018
- SP_CRITICAL_DEF,SP_NEAR_ATK_DEF,SP_LONG_ATK_DEF, // 1019-1021
- SP_DOUBLE_RATE, SP_DOUBLE_ADD_RATE, SP_MATK, SP_MATK_RATE, // 1022-1025
- SP_IGNORE_DEF_ELE,SP_IGNORE_DEF_RACE, // 1026-1027
- SP_ATK_RATE,SP_SPEED_ADDRATE,SP_ASPD_ADDRATE, // 1028-1030
- SP_MAGIC_ATK_DEF,SP_MISC_ATK_DEF, // 1031-1032
- SP_IGNORE_MDEF_ELE,SP_IGNORE_MDEF_RACE, // 1033-1034
- SP_MAGIC_ADDELE,SP_MAGIC_ADDRACE,SP_MAGIC_ADDSIZE, // 1035-1037
- SP_PERFECT_HIT_RATE,SP_PERFECT_HIT_ADD_RATE,SP_CRITICAL_RATE,SP_GET_ZENY_NUM,SP_ADD_GET_ZENY_NUM, // 1038-1042
- SP_ADD_DAMAGE_CLASS,SP_ADD_MAGIC_DAMAGE_CLASS,SP_ADD_DEF_CLASS,SP_ADD_MDEF_CLASS, // 1043-1046
- SP_ADD_MONSTER_DROP_ITEM,SP_DEF_RATIO_ATK_ELE,SP_DEF_RATIO_ATK_RACE,SP_ADD_SPEED, // 1047-1050
- SP_HIT_RATE,SP_FLEE_RATE,SP_FLEE2_RATE,SP_DEF_RATE,SP_DEF2_RATE,SP_MDEF_RATE,SP_MDEF2_RATE, // 1051-1057
- SP_SPLASH_RANGE,SP_SPLASH_ADD_RANGE,SP_AUTOSPELL,SP_HP_DRAIN_RATE,SP_SP_DRAIN_RATE, // 1058-1062
- SP_SHORT_WEAPON_DAMAGE_RETURN,SP_LONG_WEAPON_DAMAGE_RETURN,SP_WEAPON_COMA_ELE,SP_WEAPON_COMA_RACE, // 1063-1066
- SP_ADDEFF2,SP_BREAK_WEAPON_RATE,SP_BREAK_ARMOR_RATE,SP_ADD_STEAL_RATE, // 1067-1070
- SP_MAGIC_DAMAGE_RETURN,SP_RANDOM_ATTACK_INCREASE,SP_ALL_STATS,SP_AGI_VIT,SP_AGI_DEX_STR,SP_PERFECT_HIDE, // 1071-1076
- SP_FREE,SP_CLASSCHANGE, // 1077-1078
- SP_HP_DRAIN_VALUE,SP_SP_DRAIN_VALUE, // 1079-1080
- SP_WEAPON_ATK,SP_WEAPON_ATK_RATE, // 1081-1082
- SP_DELAYRATE,SP_HP_DRAIN_RATE_RACE,SP_SP_DRAIN_RATE_RACE, // 1083-1085
-
- SP_RESTART_FULL_RECOVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005
- SP_NO_CASTCANCEL2,SP_NO_MISC_DAMAGE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR, SP_UNBREAKABLE_HELM, // 2006-2010
- SP_UNBREAKABLE_SHIELD, SP_LONG_ATK_RATE, // 2011-2012
-
- SP_CRIT_ATK_RATE, SP_CRITICAL_ADDRACE, SP_NO_REGEN, SP_ADDEFF_WHENHIT, SP_AUTOSPELL_WHENHIT, // 2013-2017
- SP_SKILL_ATK, SP_UNSTRIPABLE, SP_ADD_DAMAGE_BY_CLASS, // 2018-2020
- SP_SP_GAIN_VALUE, SP_IGNORE_DEF_MOB, SP_HP_LOSS_RATE, SP_ADDRACE2, SP_HP_GAIN_VALUE, // 2021-2025
- SP_SUBSIZE, SP_HP_DRAIN_VALUE_RACE, SP_ADD_ITEM_HEAL_RATE, SP_SP_DRAIN_VALUE_RACE, SP_EXP_ADDRACE, // 2026-2030
- SP_SP_GAIN_RACE, SP_SUBRACE2, SP_ADDEFF_WHENHIT_SHORT, // 2031-2033
- SP_UNSTRIPABLE_WEAPON,SP_UNSTRIPABLE_ARMOR,SP_UNSTRIPABLE_HELM,SP_UNSTRIPABLE_SHIELD, // 2034-2037
- SP_INTRAVISION, SP_ADD_MONSTER_DROP_ITEMGROUP, SP_SP_LOSS_RATE, // 2038-2040
- SP_ADD_SKILL_BLOW, SP_SP_VANISH_RATE //2041
- //Before adding another, note that
- //1077 (SP_FREE, previously disguise),
- //are available!
-};
-
-enum {
- LOOK_BASE,LOOK_HAIR,LOOK_WEAPON,LOOK_HEAD_BOTTOM,LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HAIR_COLOR,LOOK_CLOTHES_COLOR,LOOK_SHIELD,LOOK_SHOES
-};
-
-// CELLs for non-permanent cell-based effects (Pneuma, Basilica, Npcs, etc)
-#define CELL_NPC 0x1
-#define CELL_REGEN 0x2
-#define CELL_PNEUMA 0x4
-#define CELL_SAFETYWALL 0x8
-#define CELL_LANDPROTECTOR 0x10
-#define CELL_BASILICA 0x20
-#define CELL_ICEWALL 0x80
-/*
- * map_getcell()で使用されるフラグ
- */
-typedef enum {
- CELL_CHKWALL=0, // 壁(セルタイプ1)
- CELL_CHKWATER, // 水場(セルタイプ3)
- CELL_CHKGROUND, // 地面障害物(セルタイプ5)
- CELL_CHKPASS, // 通過可能(セルタイプ1,5以外)
- CELL_CHKREACH, // Same as PASS, but ignores the cell-stacking mod.
- CELL_CHKNOPASS, // 通過不可(セルタイプ1,5)
- CELL_CHKNOREACH, // Same as NOPASS, but ignores the cell-stacking mod.
- CELL_GETTYPE, // セルタイプを返す
- CELL_GETCELLTYPE,
- CELL_CHKNPC=0x10, // タッチタイプのNPC(セルタイプ0x80フラグ)
- CELL_CHKREGEN, // cells that improve regeneration
- CELL_CHKPNEUMA,
- CELL_CHKSAFETYWALL,
- CELL_CHKBASILICA, // バジリカ(セルタイプ0x40フラグ)
- CELL_CHKLANDPROTECTOR,
- CELL_CHKICEWALL,
- CELL_CHKSTACK,
-} cell_t;
-// map_setcell()で使用されるフラグ
-enum {
- CELL_SETNPC=0x10, // タッチタイプのNPCをセット
- CELL_CLRNPC,
- CELL_SETBASILICA, // バジリカをセット
- CELL_CLRBASILICA, // バジリカをクリア
- CELL_SETREGEN, // set regen cell
- CELL_SETLANDPROTECTOR, //Set/Clear Magnetic Earth
- CELL_CLRLANDPROTECTOR,
- CELL_SETPNEUMA,
- CELL_CLRPNEUMA,
- CELL_SETSAFETYWALL,
- CELL_CLRSAFETYWALL,
- CELL_SETICEWALL,
- CELL_CLRICEWALL,
-};
-
-struct chat_data {
- struct block_list bl;
-
- unsigned char pass[8]; /* password */
- unsigned char title[61]; /* room title MAX 60 */
- unsigned char limit; /* join limit */
- unsigned char trigger;
- unsigned char users; /* current users */
- unsigned char pub; /* room attribute */
- struct map_session_data *usersd[20];
- struct block_list *owner_;
- struct block_list **owner;
- char npc_event[50];
-};
-
-extern struct map_data map[];
-extern int map_num;
-extern int autosave_interval;
-extern int minsave_interval;
-extern int save_settings;
-extern int agit_flag;
-extern int night_flag; // 0=day, 1=night [Yor]
-extern int enable_spy; //Determines if @spy commands are active.
-extern char db_path[256];
-
-// gat?ヨァ
-int map_getcell(int,int,int,cell_t);
-int map_getcellp(struct map_data*,int,int,cell_t);
-void map_setcell(int,int,int,int);
-extern int map_read_flag; // 0: grfォユォ。ォ、ォ・1: ォュォ罩テォキォ・2: ォュォ罩テォキォ・?)
-enum {
- READ_FROM_GAT, READ_FROM_AFM,
- READ_FROM_BITMAP, CREATE_BITMAP,
- READ_FROM_BITMAP_COMPRESSED, CREATE_BITMAP_COMPRESSED
-};
-
-extern char motd_txt[];
-extern char help_txt[];
-extern char help2_txt[];
-extern char charhelp_txt[];
-
-extern char talkie_mes[];
-
-extern char wisp_server_name[];
-
-// 鯖全体情報
-void map_setusers(int);
-int map_getusers(void);
-// block削除関連
-int map_freeblock(struct block_list *bl);
-int map_freeblock_lock(void);
-//int map_freeblock_unlock(void);
-int map_freeblock_unlock_sub (char *file, int lineno);
-#define map_freeblock_unlock() map_freeblock_unlock_sub (__FILE__, __LINE__)
-// block関連
-int map_addblock_sub(struct block_list *, int);
-int map_delblock_sub(struct block_list *, int);
-#define map_addblock(bl) map_addblock_sub(bl,1)
-#define map_delblock(bl) map_delblock_sub(bl,1)
-int map_moveblock(struct block_list *, int, int, unsigned int);
-int map_foreachinrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...);
-int map_foreachinshootrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...);
-int map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...);
-// -- moonsoul (added map_foreachincell)
-int map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...);
-int map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...);
-int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...); // Celest
-int map_foreachinmap(int (*)(struct block_list*,va_list),int,int,...);
-int map_countnearpc(int,int,int);
-//block関連に追加
-int map_count_oncell(int m,int x,int y,int type);
-struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int x,int y,int skill_id,struct skill_unit *);
-// 一時的object関連
-int map_addobject(struct block_list *);
-int map_delobject(int);
-int map_delobjectnofree(int id);
-void map_foreachobject(int (*)(struct block_list*,va_list),int,...);
-int map_search_freecell(struct block_list *src, int m, short *x, short *y, int rx, int ry, int flag);
-//
-int map_quit(struct map_session_data *);
-void map_quit_ack(struct map_session_data *);
-// npc
-int map_addnpc(int,struct npc_data *);
-
-// 床アイテム関連
-int map_clearflooritem_timer(int,unsigned int,int,int);
-int map_removemobs_timer(int,unsigned int,int,int);
-#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1)
-int map_addflooritem(struct item *,int,int,int,int,struct map_session_data *,struct map_session_data *,struct map_session_data *,int);
-
-// キャラid=>キャラ名 変換関連
-void map_addchariddb(int charid,char *name);
-void map_delchariddb(int charid);
-int map_reqchariddb(struct map_session_data * sd,int charid);
-char * map_charid2nick(int);
-struct map_session_data * map_charid2sd(int);
-
-struct map_session_data * map_id2sd(int);
-struct block_list * map_id2bl(int);
-int map_mapindex2mapid(unsigned short mapindex);
-int map_mapname2mapid(char*);
-int map_mapname2ipport(unsigned short,int*,int*);
-int map_setipport(unsigned short map,unsigned long ip,int port);
-int map_eraseipport(unsigned short map,unsigned long ip,int port);
-int map_eraseallipport(void);
-void map_addiddb(struct block_list *);
-void map_deliddb(struct block_list *bl);
-struct map_session_data** map_getallusers(int *users);
-void map_foreachpc(int (*func)(DBKey,void*,va_list),...);
-int map_foreachiddb(int (*)(DBKey,void*,va_list),...);
-void map_addnickdb(struct map_session_data *);
-struct map_session_data * map_nick2sd(char*);
-int compare_item(struct item *a, struct item *b);
-
-// その他
-int map_check_dir(int s_dir,int t_dir);
-int map_calc_dir( struct block_list *src,int x,int y);
-int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex]
-
-// Water functions...
-//
-int map_setwaterheight(int m, char *mapname, int height);
-int map_waterheight(char *mapname);
-
-// path.cより
-int path_search_real(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int flag,cell_t flag2);
-#define path_search(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKNOPASS)
-#define path_search2(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKWALL)
-
-int path_search_long_real(struct shootpath_data *spd,int m,int x0,int y0,int x1,int y1,cell_t flag);
-#define path_search_long(spd,m,x0,y0,x1,y1) path_search_long_real(spd,m,x0,y0,x1,y1,CELL_CHKWALL)
-
-int path_blownpos(int m,int x0,int y0,int dx,int dy,int count);
-
-// distance related functions [Skotlex]
-#define check_distance_bl(bl1, bl2, distance) check_distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y, distance)
-#define check_distance_blxy(bl, x1, y1, distance) check_distance((bl)->x-(x1), (bl)->y-(y1), distance)
-#define check_distance_xy(x0, y0, x1, y1, distance) check_distance((x0)-(x1), (y0)-(y1), distance)
-int check_distance(int dx, int dy, int distance);
-
-#define distance_bl(bl1, bl2) distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y)
-#define distance_blxy(bl, x1, y1) distance((bl)->x-(x1), (bl)->y-(y1))
-#define distance_xy(x0, y0, x1, y1) distance((x0)-(x1), (y0)-(y1))
-unsigned int distance(int dx, int dy);
-
-int cleanup_sub(struct block_list *bl, va_list ap);
-
-void map_helpscreen(int flag); // [Valaris]
-int map_delmap(char *mapname);
-
-int map_addmobtolist(unsigned short m, struct spawn_data *spawn); // [Wizputer]
-void map_spawnmobs(int); // [Wizputer]
-void map_removemobs(int); // [Wizputer]
-void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex]
-
-//Added for own save method
-int charsql_db_init(int method);
-
-extern char *INTER_CONF_NAME;
-extern char *LOG_CONF_NAME;
-extern char *MAP_CONF_NAME;
-extern char *BATTLE_CONF_FILENAME;
-extern char *ATCOMMAND_CONF_FILENAME;
-extern char *CHARCOMMAND_CONF_FILENAME;
-extern char *SCRIPT_CONF_NAME;
-extern char *MSG_CONF_NAME;
-extern char *GRF_PATH_FILENAME;
-
-
-extern int charsave_method; //needed ..
-extern char *map_server_dns;
-
-#ifndef TXT_ONLY
-
-// MySQL
-#ifdef __WIN32
-#include <my_global.h>
-#include <my_sys.h>
-#endif
-#include <mysql.h>
-
-extern char tmp_sql[65535];
-
-extern int db_use_sqldbs;
-extern MYSQL mmysql_handle;
-extern MYSQL_RES* sql_res ;
-extern MYSQL_ROW sql_row ;
-
-extern MYSQL charsql_handle;
-extern MYSQL_RES* charsql_res;
-extern MYSQL_ROW charsql_row;
-
-extern MYSQL logmysql_handle;
-extern MYSQL_RES* logsql_res ;
-extern MYSQL_ROW logsql_row ;
-
-extern int mail_server_enable;
-extern MYSQL mail_handle;
-extern MYSQL_RES* mail_res ;
-extern MYSQL_ROW mail_row ;
-
-extern char item_db_db[32];
-extern char item_db2_db[32];
-extern char mob_db_db[32];
-extern char mob_db2_db[32];
-extern char char_db[32];
-extern char mail_db[32];
-
-#endif /* not TXT_ONLY */
-//Useful typedefs from jA [Skotlex]
-typedef struct map_session_data TBL_PC;
-typedef struct npc_data TBL_NPC;
-typedef struct mob_data TBL_MOB;
-typedef struct flooritem_data TBL_ITEM;
-typedef struct chat_data TBL_CHAT;
-typedef struct skill_unit TBL_SKILL;
-typedef struct pet_data TBL_PET;
-typedef struct homun_data TBL_HOM;
-
-#define BL_CAST(type_, bl , dest) \
- (((bl) == NULL || (bl)->type != type_) ? ((dest) = NULL, 0) : ((dest) = (T ## type_ *)(bl), 1))
-
-
-extern int lowest_gm_level;
-extern char main_chat_nick[16];
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MAP_H_
+#define _MAP_H_
+
+#include <stdarg.h>
+#include "../common/mmo.h"
+#include "../common/mapindex.h"
+#include "../common/db.h"
+
+//Uncomment to enable the Cell Stack Limit mod.
+//It's only config is the battle_config cell_stack_limit.
+//Only chars affected are those defined in BL_CHAR (mobs and players currently)
+//#define CELL_NOSTACK
+
+//Uncomment to enable circular area checks.
+//By default, all range checks in Aegis are of Square shapes, so a weapon range
+// of 10 allows you to attack from anywhere within a 21x21 area.
+//Enabling this changes such checks to circular checks, which is more realistic,
+// but is not the official behaviour.
+//#define CIRCULAR_AREA
+
+#define MAX_PC_CLASS 4050
+#define PC_CLASS_BASE 0
+#define PC_CLASS_BASE2 (PC_CLASS_BASE + 4001)
+#define PC_CLASS_BASE3 (PC_CLASS_BASE2 + 22)
+#define MAX_NPC_PER_MAP 512
+#define BLOCK_SIZE 8
+#define AREA_SIZE battle_config.area_size
+#define LIFETIME_FLOORITEM 60
+#define DAMAGELOG_SIZE 30
+#define LOOTITEM_SIZE 10
+#define MAX_STATUSCHANGE 250
+//Quick defines to know which are the min-max common ailments. [Skotlex]
+//Because of the way the headers are included.. these must be replaced for actual values.
+//Remember to update as needed! Min is SC_STONE and max is SC_DPOISON currently.
+#define SC_COMMON_MIN 0
+#define SC_COMMON_MAX 10
+
+#define MAX_SKILL_LEVEL 100
+#define MAX_SKILLUNITGROUP 25
+#define MAX_SKILLUNITGROUPTICKSET 25
+#define MAX_SKILLTIMERSKILL 15
+#define MAX_MOBSKILL 50
+#define MAX_MOB_LIST_PER_MAP 128
+#define MAX_EVENTQUEUE 2
+#define MAX_EVENTTIMER 32
+#define NATURAL_HEAL_INTERVAL 500
+#define MAX_FLOORITEM 500000
+#define MAX_LEVEL 1000
+#define MAX_WALKPATH 32
+#define MAX_DROP_PER_MAP 48
+#define MAX_IGNORE_LIST 80
+#define MAX_VENDING 12
+#define MOBID_EMPERIUM 1288
+
+#define MAX_PC_BONUS 10
+//Designed for search functions, species max number of matches to display.
+#define MAX_SEARCH 5
+#define MAX_DUEL 1024
+
+#define map_id2index(id) map[(id)].index
+
+//The following system marks a different job ID system used by the map server,
+//which makes a lot more sense than the normal one. [Skotlex]
+//
+//These marks the "level" of the job.
+#define JOBL_2_1 0x100 //256
+#define JOBL_2_2 0x200 //512
+#define JOBL_2 0x300
+
+#define JOBL_UPPER 0x1000 //4096
+#define JOBL_BABY 0x2000 //8192
+
+//for filtering and quick checking.
+#define MAPID_UPPERMASK 0x0fff
+#define MAPID_BASEMASK 0x00ff
+//First Jobs
+//Note the oddity of the novice:
+//Super Novices are considered the 2-1 version of the novice! Novices are considered a first class type, too...
+enum {
+ MAPID_NOVICE = 0x0,
+ MAPID_SWORDMAN,
+ MAPID_MAGE,
+ MAPID_ARCHER,
+ MAPID_ACOLYTE,
+ MAPID_MERCHANT,
+ MAPID_THIEF,
+ MAPID_TAEKWON,
+ MAPID_WEDDING,
+ MAPID_GUNSLINGER,
+ MAPID_NINJA,
+ MAPID_XMAS, // [Valaris]
+//2_1 classes
+ MAPID_SUPER_NOVICE = JOBL_2_1|0x0,
+ MAPID_KNIGHT,
+ MAPID_WIZARD,
+ MAPID_HUNTER,
+ MAPID_PRIEST,
+ MAPID_BLACKSMITH,
+ MAPID_ASSASSIN,
+ MAPID_STAR_GLADIATOR,
+//2_2 classes
+ MAPID_CRUSADER = JOBL_2_2|0x1,
+ MAPID_SAGE,
+ MAPID_BARDDANCER,
+ MAPID_MONK,
+ MAPID_ALCHEMIST,
+ MAPID_ROGUE,
+ MAPID_SOUL_LINKER,
+//1-1, advanced
+ MAPID_NOVICE_HIGH = JOBL_UPPER|0x0,
+ MAPID_SWORDMAN_HIGH,
+ MAPID_MAGE_HIGH,
+ MAPID_ARCHER_HIGH,
+ MAPID_ACOLYTE_HIGH,
+ MAPID_MERCHANT_HIGH,
+ MAPID_THIEF_HIGH,
+//2_1 advanced
+ MAPID_LORD_KNIGHT = JOBL_UPPER|JOBL_2_1|0x1,
+ MAPID_HIGH_WIZARD,
+ MAPID_SNIPER,
+ MAPID_HIGH_PRIEST,
+ MAPID_WHITESMITH,
+ MAPID_ASSASSIN_CROSS,
+//2_2 advanced
+ MAPID_PALADIN = JOBL_UPPER|JOBL_2_2|0x1,
+ MAPID_PROFESSOR,
+ MAPID_CLOWNGYPSY,
+ MAPID_CHAMPION,
+ MAPID_CREATOR,
+ MAPID_STALKER,
+//1-1 baby
+ MAPID_BABY = JOBL_BABY|0x0,
+ MAPID_BABY_SWORDMAN,
+ MAPID_BABY_MAGE,
+ MAPID_BABY_ARCHER,
+ MAPID_BABY_ACOLYTE,
+ MAPID_BABY_MERCHANT,
+ MAPID_BABY_THIEF,
+ MAPID_BABY_TAEKWON,
+//2_1 baby
+ MAPID_SUPER_BABY = JOBL_BABY|JOBL_2_1|0x0,
+ MAPID_BABY_KNIGHT,
+ MAPID_BABY_WIZARD,
+ MAPID_BABY_HUNTER,
+ MAPID_BABY_PRIEST,
+ MAPID_BABY_BLACKSMITH,
+ MAPID_BABY_ASSASSIN,
+ MAPID_BABY_STAR_GLADIATOR,
+//2_2 baby
+ MAPID_BABY_CRUSADER = JOBL_BABY|JOBL_2_2|0x1,
+ MAPID_BABY_SAGE,
+ MAPID_BABY_BARDDANCER,
+ MAPID_BABY_MONK,
+ MAPID_BABY_ALCHEMIST,
+ MAPID_BABY_ROGUE,
+ MAPID_BABY_SOUL_LINKER,
+};
+
+//Don't change this, as the client seems to always send/receive 80 characters as it currently is. [Skotlex]
+#define MESSAGE_SIZE 80
+
+#define DEFAULT_AUTOSAVE_INTERVAL 5*60*1000
+
+//Specifies maps where players may hit each other
+#define map_flag_vs(m) (map[m].flag.pvp || map[m].flag.gvg_dungeon || map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
+//Specifies maps that have special GvG/WoE restrictions
+#define map_flag_gvg(m) (map[m].flag.gvg || (agit_flag && map[m].flag.gvg_castle))
+//Specifies if the map is tagged as GvG/WoE (regardless of agit_flag status)
+#define map_flag_gvg2(m) (map[m].flag.gvg || map[m].flag.gvg_castle)
+//Caps values to min/max
+#define cap_value(a, min, max) (a>=max?max:a<=min?min:a)
+
+//This stackable implementation does not means a BL can be more than one type at a time, but it's
+//meant to make it easier to check for multiple types at a time on invocations such as
+// map_foreach* calls [Skotlex]
+enum {
+ BL_NUL = 0x000,
+ BL_PC = 0x001,
+ BL_MOB = 0x002,
+ BL_PET = 0x004,
+ BL_HOM = 0x008, //[blackhole89]
+ BL_ITEM = 0x010,
+ BL_SKILL = 0x020,
+ BL_NPC = 0x040,
+ BL_CHAT = 0x080,
+};
+
+//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet.
+#define BL_CHAR (BL_PC|BL_MOB|BL_HOM)
+#define BL_ALL 0xfff
+
+enum { WARP, SHOP, SCRIPT, MONS };
+
+enum {
+ RC_FORMLESS=0,
+ RC_UNDEAD,
+ RC_BRUTE,
+ RC_PLANT,
+ RC_INSECT,
+ RC_FISH,
+ RC_DEMON,
+ RC_DEMIHUMAN,
+ RC_ANGEL,
+ RC_DRAGON,
+ RC_BOSS,
+ RC_NONBOSS,
+ RC_MAX
+};
+
+enum {
+ ELE_NEUTRAL=0,
+ ELE_WATER,
+ ELE_EARTH,
+ ELE_FIRE,
+ ELE_WIND,
+ ELE_POISON,
+ ELE_HOLY,
+ ELE_DARK,
+ ELE_GHOST,
+ ELE_UNDEAD,
+ ELE_MAX
+};
+
+enum {
+ IG_BLUEBOX=1,
+ IG_VIOLETBOX, //2
+ IG_CARDALBUM, //3
+ IG_GIFTBOX, //4
+ IG_SCROLLBOX, //5
+ IG_FINDINGORE, //6
+ IG_COOKIEBAG, //7
+ IG_POTION, //8
+ IG_HERBS, //9
+ IG_FRUITS, //10
+ IG_MEAT, //11
+ IG_CANDY, //12
+ IG_JUICE, //13
+ IG_FISH, //14
+ IG_BOXES, //15
+ IG_GEMSTONE, //16
+ IG_JELLOPY, //17
+ IG_ORE, //18
+ IG_FOOD, //19
+ IG_RECOVERY, //20
+ IG_MINERALS, //21
+ IG_TAMING, //22
+ IG_SCROLLS, //23
+ IG_QUIVERS, //24
+ IG_MASKS, //25
+ IG_ACCESORY, //26
+ IG_JEWELS, //27
+ IG_GIFTBOX_1, //28
+ IG_GIFTBOX_2, //29
+ IG_GIFTBOX_3, //30
+ IG_GIFTBOX_4, //31
+ IG_EGGBOY, //32
+ IG_EGGGIRL, //33
+ IG_GIFTBOXCHINA, //34
+ IG_LOTTOBOX, //35
+ MAX_ITEMGROUP,
+} item_group_list;
+
+enum {
+ ATF_SELF=0x01,
+ ATF_TARGET=0x02,
+ ATF_SHORT=0x04,
+ ATF_LONG=0x08
+} auto_trigger_flag;
+
+struct block_list {
+ struct block_list *next,*prev;
+ int id;
+ short m,x,y;
+ unsigned char type;
+ unsigned char subtype;
+};
+
+struct walkpath_data {
+ unsigned char path_len,path_pos,path_half;
+ unsigned char path[MAX_WALKPATH];
+};
+struct shootpath_data {
+ int rx,ry,len;
+ int x[MAX_WALKPATH];
+ int y[MAX_WALKPATH];
+};
+
+struct skill_timerskill {
+ int timer;
+ int src_id;
+ int target_id;
+ int map;
+ short x,y;
+ short skill_id,skill_lv;
+ int type;
+ int flag;
+};
+
+struct skill_unit_group;
+struct skill_unit {
+ struct block_list bl;
+
+ struct skill_unit_group *group;
+
+ int limit;
+ int val1,val2;
+ short alive,range;
+};
+
+struct skill_unit_group {
+ int src_id;
+ int party_id;
+ int guild_id;
+ int map;
+ int target_flag; //Holds BCT_* flag for battle_check_target
+ int bl_flag; //Holds BL_* flag for map_foreachin* functions
+ unsigned int tick;
+ int limit,interval;
+
+ int skill_id,skill_lv;
+ int val1,val2,val3;
+ char *valstr;
+ int unit_id;
+ int group_id;
+ int unit_count,alive_count;
+ struct skill_unit *unit;
+ struct {
+ unsigned ammo_consume : 1;
+ unsigned magic_power : 1;
+ unsigned into_abyss : 1;
+ unsigned song_dance : 2; //0x1 Song/Dance, 0x2 Ensemble
+ } state;
+};
+struct skill_unit_group_tickset {
+ unsigned int tick;
+ int id;
+};
+
+struct unit_data {
+ struct block_list *bl;
+ struct walkpath_data walkpath;
+ struct skill_timerskill *skilltimerskill[MAX_SKILLTIMERSKILL];
+ struct skill_unit_group *skillunit[MAX_SKILLUNITGROUP];
+ struct skill_unit_group_tickset skillunittick[MAX_SKILLUNITGROUPTICKSET];
+ short attacktarget_lv;
+ short to_x,to_y;
+ short skillx,skilly;
+ short skillid,skilllv;
+ int skilltarget;
+ int skilltimer;
+ int target;
+ int attacktimer;
+ int walktimer;
+ int chaserange;
+ unsigned int attackabletime;
+ unsigned int canact_tick;
+ unsigned int canmove_tick;
+ unsigned char dir;
+ unsigned char walk_count;
+ struct {
+ unsigned change_walk_target : 1 ;
+ unsigned skillcastcancel : 1 ;
+ unsigned attack_continue : 1 ;
+ unsigned walk_easy : 1 ;
+ unsigned running : 1;
+ } state;
+};
+
+//Basic damage info of a weapon
+//Required because players have two of these, one in status_data and another
+//for their left hand weapon.
+struct weapon_atk {
+ unsigned short atk, atk2;
+ unsigned short range;
+ unsigned char ele;
+};
+
+//For holding basic status (which can be modified by status changes)
+struct status_data {
+ unsigned int
+ hp, sp,
+ max_hp, max_sp;
+ unsigned short
+ str, agi, vit, int_, dex, luk,
+ batk,
+ matk_min, matk_max,
+ speed,
+ amotion, adelay, dmotion,
+ mode;
+ short
+ hit, flee, cri, flee2,
+ def2, mdef2,
+ aspd_rate;
+ unsigned char
+ def_ele, ele_lv,
+ size, race;
+ signed char
+ def, mdef;
+ struct weapon_atk rhw, *lhw; //Right Hand/Left Hand Weapon. Only players have a lhw (hence it's a pointer)
+};
+
+struct script_reg {
+ int index;
+ int data;
+};
+struct script_regstr {
+ int index;
+ char data[256];
+};
+
+struct status_change_entry {
+ int timer;
+ int val1,val2,val3,val4;
+};
+
+struct status_change {
+ struct status_change_entry data[MAX_STATUSCHANGE];
+ short count;
+ unsigned short opt1,opt2;
+ unsigned int opt3, option; //Note that older packet versions use short here.
+};
+
+struct vending {
+ short index;
+ unsigned short amount;
+ unsigned int value;
+};
+
+struct weapon_data {
+ int atkmods[3];
+ // all the variables except atkmods get zero'ed in each call of status_calc_pc
+ // NOTE: if you want to add a non-zeroed variable, you need to update the malloc_set call
+ // in status_calc_pc as well! All the following are automatically zero'ed. [Skotlex]
+ int overrefine;
+ int star;
+ int ignore_def_ele;
+ int ignore_def_race;
+ int def_ratio_atk_ele;
+ int def_ratio_atk_race;
+ int addele[ELE_MAX];
+ int addrace[RC_MAX];
+ int addrace2[RC_MAX];
+ int addsize[3];
+
+ short ignore_def_mob;
+ struct drain_data {
+ short rate;
+ short per;
+ short value;
+ unsigned type:1;
+ } hp_drain[RC_MAX], sp_drain[RC_MAX];
+
+ short add_damage_classid[MAX_PC_BONUS];
+ int add_damage_classrate[MAX_PC_BONUS];
+ int add_damage_class_count;
+};
+
+struct view_data {
+ unsigned short
+ class_,
+ weapon,
+ shield, //Or left-hand weapon.
+ head_top,
+ head_mid,
+ head_bottom,
+ hair_style,
+ hair_color,
+ cloth_color;
+ char sex;
+ unsigned dead_sit : 2;
+};
+
+//Additional regen data that only players have.
+struct regen_data_sub {
+ unsigned short
+ hp,sp;
+
+ //tick accumulation before healing.
+ struct {
+ unsigned int hp,sp;
+ } tick;
+
+ //Regen rates (where every 1 means +100% regen)
+ struct {
+ unsigned char hp,sp;
+ } rate;
+};
+
+struct regen_data {
+
+ unsigned short flag; //Marks what stuff you may heal or not.
+ unsigned short
+ hp,sp,shp,ssp;
+
+ //tick accumulation before healing.
+ struct {
+ unsigned int hp,sp,shp,ssp;
+ } tick;
+
+ //Regen rates (where every 1 means +100% regen)
+ struct {
+ unsigned char
+ hp,sp,shp,ssp;
+ } rate;
+
+ struct {
+ unsigned walk:1; //Can you regen even when walking?
+ unsigned gc:1; //Tags when you should have double regen due to GVG castle
+ unsigned overweight :2; //overweight state (1: 50%, 2: 90%)
+ unsigned block :2; //Block regen flag (1: Hp, 2: Sp)
+ } state;
+
+ //skill-regen, sitting-skill-regen (since not all chars with regen need it)
+ struct regen_data_sub *sregen, *ssregen;
+};
+
+struct party_member_data {
+ struct map_session_data *sd;
+ unsigned int hp; //For HP,x,y refreshing.
+ unsigned short x, y;
+};
+
+struct party_data {
+ struct party party;
+ struct party_member_data data[MAX_PARTY];
+ unsigned char itemc; //For item distribution.
+ struct {
+ unsigned monk : 1; //There's at least one monk in party?
+ unsigned sg : 1; //There's at least one Star Gladiator in party?
+ unsigned snovice :1; //There's a Super Novice
+ unsigned tk : 1; //There's a taekwon
+ } state;
+};
+
+struct npc_data;
+struct pet_db;
+struct homunculus_db; //[orn]
+struct item_data;
+struct square;
+
+struct map_session_data {
+ struct block_list bl;
+ struct unit_data ud;
+ struct view_data vd;
+ struct status_data base_status, battle_status;
+ struct weapon_atk base_lhw, battle_lhw; //Left-hand weapon atk data.
+ struct status_change sc;
+ struct regen_data regen;
+ struct regen_data_sub sregen, ssregen;
+ //NOTE: When deciding to add a flag to state or special_state, take into consideration that state is preserved in
+ //status_calc_pc, while special_state is recalculated in each call. [Skotlex]
+ struct {
+ unsigned auth : 1;
+ unsigned menu_or_input : 1;
+ unsigned dead_sit : 2;
+ unsigned waitingdisconnect : 1;
+ unsigned lr_flag : 2;
+ unsigned connect_new : 1;
+ unsigned arrow_atk : 1;
+ unsigned skill_flag : 1;
+ unsigned gangsterparadise : 1;
+ unsigned rest : 1;
+ unsigned storage_flag : 2; //0: closed, 1: Normal Storage open, 2: guild storage open [Skotlex]
+ unsigned snovice_flag : 4;
+ // originally by Qamera, adapted by celest
+ unsigned event_death : 1;
+ unsigned event_kill_pc : 1;
+ unsigned event_disconnect : 1;
+ unsigned event_kill_mob : 1;
+ unsigned event_baselvup : 1;
+ unsigned event_joblvup : 1;
+ unsigned event_loadmap : 1;
+ // Abracadabra bugfix by Aru
+ unsigned abra_flag : 1;
+ unsigned autotrade : 1; //By Fantik
+ unsigned reg_dirty : 3; //By Skotlex (marks whether registry variables have been saved or not yet)
+ unsigned showdelay :1;
+ unsigned showexp :1;
+ unsigned showzeny :1;
+ unsigned mainchat :1; //[LuzZza]
+ unsigned noask :1; // [LuzZza]
+ unsigned trading :1; //[Skotlex] is 1 only after a trade has started.
+ unsigned deal_locked :2; //1: Clicked on OK. 2: Clicked on TRADE
+ unsigned party_sent :1;
+ unsigned guild_sent :1;
+ unsigned monster_ignore :1; // for monsters to ignore a character [Valaris] [zzo]
+ unsigned size :2; // for tiny/large types
+ unsigned night :1; //Holds whether or not the player currently has the SI_NIGHT effect on. [Skotlex]
+ unsigned finalsave :1; //Signals whether the final save for the char was done or not yet. Meant to prevent exploits and the like. [Skotlex]
+ unsigned blockedmove :1;
+ unsigned using_fake_npc :1;
+ unsigned rewarp :1; //Signals that a player should warp as soon as he is done loading a map. [Skotlex]
+ unsigned killer : 1;
+ unsigned killable : 1;
+ unsigned doridori : 1;
+ unsigned ignoreAll : 1;
+ unsigned short autoloot;
+ struct guild *gmaster_flag;
+ } state;
+ struct {
+ unsigned char no_weapon_damage, no_magic_damage, no_misc_damage;
+ unsigned restart_full_recover : 1;
+ unsigned no_castcancel : 1;
+ unsigned no_castcancel2 : 1;
+ unsigned no_sizefix : 1;
+ unsigned no_gemstone : 1;
+ unsigned intravision : 1; // Maya Purple Card effect allowing to see Hiding/Cloaking people [DracoRPG]
+ unsigned perfect_hiding : 1; // [Valaris]
+ } special_state;
+ int login_id1, login_id2;
+ unsigned short class_; //This is the internal job ID used by the map server to simplify comparisons/queries/etc. [Skotlex]
+
+ int packet_ver; // 5: old, 6: 7july04, 7: 13july04, 8: 26july04, 9: 9aug04/16aug04/17aug04, 10: 6sept04, 11: 21sept04, 12: 18oct04, 13: 25oct04 ... 18
+ struct mmo_charstatus status;
+ struct registry save_reg;
+
+ struct item_data *inventory_data[MAX_INVENTORY];
+ short equip_index[11];
+ unsigned int weight,max_weight;
+ int cart_weight,cart_max_weight,cart_num,cart_max_num;
+ int fd;
+ unsigned short mapindex;
+ unsigned short prev_speed,prev_adelay;
+ unsigned char head_dir; //0: Look forward. 1: Look right, 2: Look left.
+ unsigned int client_tick;
+ int npc_id,areanpc_id,npc_shopid;
+ int npc_item_flag; //Marks the npc_id with which you can use items during interactions with said npc (see script command enable_itemuse)
+ int npc_menu, max_menu;
+ int npc_amount;
+ struct script_state *st;
+ char npc_str[256];
+ int npc_timer_id; //For player attached npc timers. [Skotlex]
+ unsigned int chatID;
+ time_t idletime;
+
+ struct{
+ char name[NAME_LENGTH];
+ } ignore[MAX_IGNORE_LIST];
+
+ int followtimer; // [MouseJstr]
+ int followtarget;
+
+ time_t emotionlasttime; // to limit flood with emotion packets
+
+ short skillitem,skillitemlv;
+ short skillid_old,skilllv_old;
+ short skillid_dance,skilllv_dance;
+ char blockskill[MAX_SKILL]; // [celest]
+ int cloneskill_id;
+ int menuskill_id, menuskill_lv;
+
+ int invincible_timer;
+ unsigned int canlog_tick;
+ unsigned int canuseitem_tick; // [Skotlex]
+ unsigned int cantalk_tick;
+
+ short weapontype1,weapontype2;
+ short disguise; // [Valaris]
+
+ struct weapon_data right_weapon, left_weapon;
+
+ // here start arrays to be globally zeroed at the beginning of status_calc_pc()
+ int param_bonus[6],param_equip[6]; //Stores card/equipment bonuses.
+ int subele[ELE_MAX];
+ int subrace[RC_MAX];
+ int subrace2[RC_MAX];
+ int subsize[3];
+ int reseff[SC_COMMON_MAX-SC_COMMON_MIN+1];
+ int weapon_coma_ele[ELE_MAX];
+ int weapon_coma_race[RC_MAX];
+ int weapon_atk[16];
+ int weapon_atk_rate[16];
+ int arrow_addele[ELE_MAX];
+ int arrow_addrace[RC_MAX];
+ int arrow_addsize[3];
+ int magic_addele[ELE_MAX];
+ int magic_addrace[RC_MAX];
+ int magic_addsize[3];
+ int critaddrace[RC_MAX];
+ int expaddrace[RC_MAX];
+ int itemgrouphealrate[MAX_ITEMGROUP];
+ short sp_gain_race[RC_MAX];
+ // zeroed arrays end here.
+ // zeroed structures start here
+ struct s_autospell{
+ short id, lv, rate, card_id;
+ } autospell[MAX_PC_BONUS], autospell2[MAX_PC_BONUS];
+ struct s_addeffect{
+ short id, rate, arrow_rate;
+ unsigned char flag;
+ } addeff[MAX_PC_BONUS], addeff2[MAX_PC_BONUS];
+ struct { //skillatk raises bonus dmg% of skills, skillblown increases bonus blewcount for some skills.
+ short id, val;
+ } skillatk[MAX_PC_BONUS], skillblown[MAX_PC_BONUS];
+ struct {
+ short class_, rate;
+ } add_def[MAX_PC_BONUS], add_mdef[MAX_PC_BONUS],
+ add_dmg[MAX_PC_BONUS], add_mdmg[MAX_PC_BONUS];
+ struct s_add_drop {
+ short id, group;
+ int race, rate;
+ } add_drop[MAX_PC_BONUS];
+ struct {
+ int nameid;
+ int rate;
+ } itemhealrate[MAX_PC_BONUS];
+ // zeroed structures end here
+ // zeroed vars start here.
+ int arrow_atk,arrow_ele,arrow_cri,arrow_hit;
+ int nsshealhp,nsshealsp;
+ int critical_def,double_rate;
+ int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex]
+ int near_attack_def_rate,long_attack_def_rate,magic_def_rate,misc_def_rate;
+ int ignore_mdef_ele;
+ int ignore_mdef_race;
+ int perfect_hit;
+ int perfect_hit_add;
+ int get_zeny_rate;
+ int get_zeny_num; //Added Get Zeny Rate [Skotlex]
+ int double_add_rate;
+ int short_weapon_damage_return,long_weapon_damage_return;
+ int magic_damage_return; // AppleGirl Was Here
+ int random_attack_increase_add,random_attack_increase_per; // [Valaris]
+ int break_weapon_rate,break_armor_rate;
+ int crit_atk_rate;
+ int hp_loss_rate;
+ int sp_loss_rate;
+ int classchange; // [Valaris]
+ int speed_add_rate, aspd_add_rate;
+ unsigned int setitem_hash, setitem_hash2; //Split in 2 because shift operations only work on int ranges. [Skotlex]
+
+ short splash_range, splash_add_range;
+ short add_steal_rate;
+ short hp_loss_value;
+ short sp_loss_value;
+ short hp_loss_type;
+ short sp_gain_value, hp_gain_value;
+ short sp_vanish_rate;
+ short sp_vanish_per;
+ short add_drop_count;
+ unsigned short unbreakable; // chance to prevent ANY equipment breaking [celest]
+ unsigned short unbreakable_equip; //100% break resistance on certain equipment
+ unsigned short unstripable_equip;
+ short add_def_count,add_mdef_count;
+ short add_dmg_count,add_mdmg_count;
+
+ // zeroed vars end here.
+
+ int castrate,delayrate,hprate,sprate,dsprate;
+ int atk_rate;
+ int speed_rate,hprecov_rate,sprecov_rate;
+ int matk_rate;
+ int critical_rate,hit_rate,flee_rate,flee2_rate,def_rate,def2_rate,mdef_rate,mdef2_rate;
+
+ int hp_loss_tick;
+ int sp_loss_tick;
+
+ int itemid;
+ short itemindex; //Used item's index in sd->inventory [Skotlex]
+
+ short catch_target_class; // pet catching, stores a pet class to catch (short now) [zzo]
+
+ short spiritball, spiritball_old;
+ int spirit_timer[MAX_SKILL_LEVEL];
+
+ unsigned char potion_success_counter; //Potion successes in row counter
+ unsigned char mission_count; //Stores the bounty kill count for TK_MISSION
+ short mission_mobid; //Stores the target mob_id for TK_MISSION
+ int die_counter; //Total number of times you've died
+ int devotion[5]; //Stores the char IDs of chars devoted to.
+ int reg_num; //Number of registries (type numeric)
+ int regstr_num; //Number of registries (type string)
+
+ struct script_reg *reg;
+ struct script_regstr *regstr;
+
+ int trade_partner;
+ struct {
+ struct {
+ int index, amount;
+ } item[10];
+ int zeny, weight;
+ } deal;
+
+ int party_invite,party_invite_account;
+
+ 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]
+ int guildspy; // [Syrus22]
+ int partyspy; // [Syrus22]
+
+ int vender_id;
+ int vend_num;
+ char message[MESSAGE_SIZE];
+ struct vending vending[MAX_VENDING];
+
+ struct pet_data *pd;
+ struct homun_data *hd; // [blackhole89]
+
+ struct{
+ int m; //-1 - none, other: map index corresponding to map name.
+ unsigned short index; //map index
+ }feel_map[3];// 0 - Sun; 1 - Moon; 2 - Stars
+ short hate_mob[3];
+
+ unsigned int pvp_timer;
+ short pvp_point;
+ unsigned short pvp_rank, pvp_lastusers;
+ unsigned short pvp_won, pvp_lost;
+
+ char eventqueue[MAX_EVENTQUEUE][50];
+ int eventtimer[MAX_EVENTTIMER];
+ unsigned short eventcount; // [celest]
+
+ unsigned char change_level; // [celest]
+
+ char fakename[NAME_LENGTH]; // fake names [Valaris]
+
+#ifndef TXT_ONLY
+ int mail_counter; // mail counter for mail system [Valaris]
+#endif
+
+ int duel_group; // duel vars [LuzZza]
+ int duel_invite;
+
+ char away_message[128]; // [LuzZza]
+
+};
+
+struct {
+ int members_count;
+ int invites_count;
+ int max_players_limit;
+} duel_list[MAX_DUEL];
+
+int duel_count;
+
+struct npc_timerevent_list {
+ int timer,pos;
+};
+struct npc_label_list {
+ char name[NAME_LENGTH];
+ int pos;
+};
+struct npc_item_list {
+ unsigned int nameid,value;
+};
+struct npc_data {
+ struct block_list bl;
+ struct unit_data ud; //Because they need to be able to move....
+ struct view_data *vd;
+ struct status_change sc; //They can't have status changes, but.. they want the visual opt values.
+ short n;
+ short class_;
+ short speed;
+ unsigned char name[NAME_LENGTH];
+ unsigned char exname[NAME_LENGTH];
+ int chat_id;
+ unsigned int next_walktime;
+
+ char eventqueue[MAX_EVENTQUEUE][50];
+ int eventtimer[MAX_EVENTTIMER];
+ short arenaflag;
+
+ void *chatdb;
+ struct npc_data *master_nd;
+
+ union {
+ struct {
+ struct script_code *script;
+ short xs,ys;
+ int guild_id;
+ int timer,timerid,timeramount,rid;
+ unsigned int timertick;
+ struct npc_timerevent_list *timer_event;
+ int label_list_num;
+ struct npc_label_list *label_list;
+ int src_id;
+ } scr;
+ struct npc_item_list shop_item[1];
+ struct {
+ short xs,ys;
+ short x,y;
+ unsigned short mapindex;
+ } warp;
+ } u;
+ //Do NOT place anything afterwards... shop data NPC will override any variables from here and on! [Skotlex]
+};
+
+//For quick linking to a guardian's info. [Skotlex]
+struct guardian_data {
+ int number; //0-MAX_GUARDIANS-1 = Guardians. MAX_GUARDIANS = Emperium.
+ int guild_id;
+ int emblem_id;
+ int guardup_lv; //Level of GD_GUARDUP skill.
+ char guild_name[NAME_LENGTH];
+ struct guild_castle* castle;
+};
+
+// Mob List Held in memory for Dynamic Mobs [Wizputer]
+// Expanded to specify all mob-related spawn data by [Skotlex]
+struct spawn_data {
+ short class_; //Class, used because a mob can change it's class
+ unsigned short m,x,y; //Spawn information (map, point, spawn-area around point)
+ signed short xs,ys;
+ unsigned short num; //Number of mobs using this structure.
+ unsigned int level; //Custom level.
+ unsigned int delay1,delay2; //Min delay before respawning after spawn/death
+ struct {
+ unsigned size :2; //Holds if mob has to be tiny/large
+ unsigned ai :2; //Holds if mob is special ai.
+ } state;
+ char name[NAME_LENGTH],eventname[50]; //Name/event
+};
+
+
+struct mob_data {
+ struct block_list bl;
+ struct unit_data ud;
+ struct view_data *vd;
+ struct status_data status, *base_status; //Second one is in case of leveling up mobs, or tiny/large mobs.
+ struct status_change sc;
+ struct mob_db *db; //For quick data access (saves doing mob_db(md->class_) all the time) [Skotlex]
+ char name[NAME_LENGTH];
+ struct {
+ unsigned size : 2; //Small/Big monsters.
+ unsigned cached : 1; //Cached mobs for dynamic mob unloading [Skotlex]
+ unsigned ai : 2; //Special ai for summoned monsters.
+ //0: Normal mob.
+ //1: Standard summon, attacks mobs.
+ //2: Alchemist Marine Sphere
+ //3: Alchemist Summon Flora
+ } special_state; //Special mob information that does not needs to be zero'ed on mob respawn.
+ struct {
+ unsigned skillstate : 8;
+ unsigned aggressive : 1; //Signals whether the mob AI is in aggressive mode or reactive mode. [Skotlex]
+ unsigned char steal_flag; //number of steal tries (to prevent steal exploit on mobs with few items) [Lupus]
+ unsigned steal_coin_flag : 1;
+ unsigned soul_change_flag : 1; // Celest
+ unsigned alchemist: 1;
+ unsigned no_random_walk: 1;
+ unsigned killer: 1;
+ int provoke_flag; // Celest
+ } state;
+ struct guardian_data* guardian_data;
+ struct {
+ int id;
+ int dmg;
+ unsigned flag : 1; //0: Normal. 1: Homunc exp
+ } dmglog[DAMAGELOG_SIZE];
+ struct spawn_data *spawn; //Spawn data.
+ struct item *lootitem;
+ short spawn_n; //Spawn data index on the map server.
+ short class_;
+ short attacked_count;
+ unsigned char attacked_players;
+ unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex]
+ int level;
+ int target_id,attacked_id;
+ unsigned int next_walktime;
+ unsigned int last_deadtime,last_spawntime,last_thinktime,last_linktime;
+ short move_fail_count;
+ short lootitem_count;
+ short min_chase;
+
+ int deletetimer;
+ int master_id,master_dist;
+
+ struct npc_data *nd;
+ unsigned short callback_flag;
+
+ short skillidx;
+ unsigned int skilldelay[MAX_MOBSKILL];
+ char npc_event[50];
+};
+
+/* [blackhole89] */
+struct homun_data {
+ struct block_list bl;
+ struct unit_data ud;
+ struct view_data *vd;
+ struct status_data base_status, battle_status;
+ struct status_change sc;
+ struct regen_data regen;
+ struct homunculus_db *homunculusDB; //[orn]
+ struct s_homunculus homunculus ; //[orn]
+
+ struct map_session_data *master; //pointer back to its master
+ int hungry_timer; //[orn]
+ unsigned int exp_next;
+ char blockskill[MAX_SKILL]; // [orn]
+};
+
+struct pet_data {
+ struct block_list bl;
+ struct unit_data ud;
+ struct view_data vd;
+ struct s_pet pet;
+ struct status_data status;
+ struct mob_db *db;
+ struct pet_db *petDB;
+ int pet_hungry_timer;
+ int target_id;
+ short n;
+ struct {
+ unsigned skillbonus : 1;
+ } state;
+ int move_fail_count;
+ unsigned int next_walktime,last_thinktime;
+ short rate_fix; //Support rate as modified by intimacy (1000 = 100%) [Skotlex]
+
+ struct pet_recovery { //Stat recovery
+ unsigned short type; //Status Change id
+ unsigned short delay; //How long before curing (secs).
+ int timer;
+ } *recovery; //[Valaris] / Reimplemented by [Skotlex]
+
+ struct pet_bonus {
+ unsigned short type; //bStr, bVit?
+ unsigned short val; //Qty
+ unsigned short duration; //in secs
+ unsigned short delay; //Time before recasting (secs)
+ int timer;
+ } *bonus; //[Valaris] / Reimplemented by [Skotlex]
+
+ struct pet_skill_attack { //Attack Skill
+ unsigned short id;
+ unsigned short lv;
+ unsigned short div_; //0 = Normal skill. >0 = Fixed damage (lv), fixed div_.
+ unsigned short rate; //Base chance of skill ocurrance (10 = 10% of attacks)
+ unsigned short bonusrate; //How being 100% loyal affects cast rate (10 = At 1000 intimacy->rate+10%
+ } *a_skill; //[Skotlex]
+
+ struct pet_skill_support { //Support Skill
+ unsigned short id;
+ unsigned short lv;
+ unsigned short hp; //Max HP% for skill to trigger (50 -> 50% for Magnificat)
+ unsigned short sp; //Max SP% for skill to trigger (100 = no check)
+ unsigned short delay; //Time (secs) between being able to recast.
+ int timer;
+ } *s_skill; //[Skotlex]
+
+ struct pet_loot {
+ struct item *item;
+ unsigned short count;
+ unsigned short weight;
+ unsigned short max;
+ } *loot; //[Valaris] / Rewritten by [Skotlex]
+
+ struct map_session_data *msd;
+};
+
+enum { ATK_LUCKY=1,ATK_FLEE,ATK_DEF}; // 囲まれペナルティ計算用
+
+struct map_data {
+ char name[MAP_NAME_LENGTH];
+ unsigned short index; //Index is the map index used by the mapindex* functions.
+ unsigned char *gat; // NULLなら下のmap_data_other_serverとして扱う
+ unsigned char *cell; //Contains temporary cell data that is set/unset on tiles.
+#ifdef CELL_NOSTACK
+ unsigned char *cell_bl; //Holds amount of bls in any given cell.
+#endif
+ char *alias; // [MouseJstr]
+ struct block_list **block;
+ struct block_list **block_mob;
+ int *block_count,*block_mob_count;
+ int m;
+ short xs,ys;
+ short bxs,bys;
+ int water_height;
+ int npc_num;
+ int users;
+ struct {
+ unsigned alias : 1;
+ unsigned nomemo : 1;
+ unsigned noteleport : 1;
+ unsigned noreturn : 1;
+ unsigned monster_noteleport : 1;
+ unsigned nosave : 1;
+ unsigned nobranch : 1;
+ unsigned noexppenalty : 1;
+ unsigned pvp : 1;
+ unsigned pvp_noparty : 1;
+ unsigned pvp_noguild : 1;
+ unsigned pvp_nightmaredrop :1;
+ unsigned pvp_nocalcrank : 1;
+ unsigned gvg_castle : 1;
+ unsigned gvg : 1; // Now it identifies gvg versus maps that are active 24/7
+ unsigned gvg_dungeon : 1; // Celest
+ unsigned gvg_noparty : 1;
+ unsigned nozenypenalty : 1;
+ unsigned notrade : 1;
+ unsigned noskill : 1;
+ unsigned nowarp : 1;
+ unsigned nowarpto : 1;
+ unsigned noicewall : 1; // [Valaris]
+ unsigned snow : 1; // [Valaris]
+ unsigned clouds : 1;
+ unsigned clouds2 : 1; // [Valaris]
+ unsigned fog : 1; // [Valaris]
+ unsigned fireworks : 1;
+ unsigned sakura : 1; // [Valaris]
+ unsigned leaves : 1; // [Valaris]
+ unsigned rain : 1; // [Valaris]
+ unsigned indoors : 1; // celest
+ unsigned nogo : 1; // [Valaris]
+ unsigned nobaseexp : 1; // [Lorky] added by Lupus
+ unsigned nojobexp : 1; // [Lorky]
+ unsigned nomobloot : 1; // [Lorky]
+ unsigned nomvploot : 1; // [Lorky]
+ unsigned nightenabled :1; //For night display. [Skotlex]
+ unsigned restricted : 1; // [Komurka]
+ unsigned nodrop : 1;
+ unsigned novending : 1;
+ unsigned loadevent : 1;
+ unsigned nochat :1;
+ unsigned partylock :1;
+ unsigned guildlock :1;
+ } flag;
+ struct point save;
+ struct npc_data *npc[MAX_NPC_PER_MAP];
+ struct {
+ int drop_id;
+ int drop_type;
+ int drop_per;
+ } drop_list[MAX_DROP_PER_MAP];
+
+ struct spawn_data *moblist[MAX_MOB_LIST_PER_MAP]; // [Wizputer]
+ int mob_delete_timer; // [Skotlex]
+ int zone; // [Komurka]
+ int jexp; // map experience multiplicator
+ int bexp; // map experience multiplicator
+ int nocommand; //Blocks @/# commands for non-gms. [Skotlex]
+};
+
+struct map_data_other_server {
+ char name[MAP_NAME_LENGTH];
+ unsigned short index; //Index is the map index used by the mapindex* functions.
+ unsigned char *gat; // NULL固定にして判断
+ unsigned long ip;
+ unsigned int port;
+};
+
+struct flooritem_data {
+ struct block_list bl;
+ unsigned char subx,suby;
+ int cleartimer;
+ int first_get_id,second_get_id,third_get_id;
+ unsigned int first_get_tick,second_get_tick,third_get_tick;
+ struct item item_data;
+};
+
+enum {
+ SP_SPEED,SP_BASEEXP,SP_JOBEXP,SP_KARMA,SP_MANNER,SP_HP,SP_MAXHP,SP_SP, // 0-7
+ SP_MAXSP,SP_STATUSPOINT,SP_0a,SP_BASELEVEL,SP_SKILLPOINT,SP_STR,SP_AGI,SP_VIT, // 8-15
+ SP_INT,SP_DEX,SP_LUK,SP_CLASS,SP_ZENY,SP_SEX,SP_NEXTBASEEXP,SP_NEXTJOBEXP, // 16-23
+ SP_WEIGHT,SP_MAXWEIGHT,SP_1a,SP_1b,SP_1c,SP_1d,SP_1e,SP_1f, // 24-31
+ SP_USTR,SP_UAGI,SP_UVIT,SP_UINT,SP_UDEX,SP_ULUK,SP_26,SP_27, // 32-39
+ SP_28,SP_ATK1,SP_ATK2,SP_MATK1,SP_MATK2,SP_DEF1,SP_DEF2,SP_MDEF1, // 40-47
+ SP_MDEF2,SP_HIT,SP_FLEE1,SP_FLEE2,SP_CRITICAL,SP_ASPD,SP_36,SP_JOBLEVEL, // 48-55
+ SP_UPPER,SP_PARTNER,SP_CART,SP_FAME,SP_UNBREAKABLE, //56-60
+ SP_CARTINFO=99, // 99
+
+ SP_BASEJOB=119, // 100+19 - celest
+ SP_BASECLASS=120, //Hmm.. why 100+19? I just use the next one... [Skotlex]
+
+ // original 1000-
+ SP_ATTACKRANGE=1000, SP_ATKELE,SP_DEFELE, // 1000-1002
+ SP_CASTRATE, SP_MAXHPRATE, SP_MAXSPRATE, SP_SPRATE, // 1003-1006
+ SP_ADDELE, SP_ADDRACE, SP_ADDSIZE, SP_SUBELE, SP_SUBRACE, // 1007-1011
+ SP_ADDEFF, SP_RESEFF, // 1012-1013
+ SP_BASE_ATK,SP_ASPD_RATE,SP_HP_RECOV_RATE,SP_SP_RECOV_RATE,SP_SPEED_RATE, // 1014-1018
+ SP_CRITICAL_DEF,SP_NEAR_ATK_DEF,SP_LONG_ATK_DEF, // 1019-1021
+ SP_DOUBLE_RATE, SP_DOUBLE_ADD_RATE, SP_MATK, SP_MATK_RATE, // 1022-1025
+ SP_IGNORE_DEF_ELE,SP_IGNORE_DEF_RACE, // 1026-1027
+ SP_ATK_RATE,SP_SPEED_ADDRATE,SP_ASPD_ADDRATE, // 1028-1030
+ SP_MAGIC_ATK_DEF,SP_MISC_ATK_DEF, // 1031-1032
+ SP_IGNORE_MDEF_ELE,SP_IGNORE_MDEF_RACE, // 1033-1034
+ SP_MAGIC_ADDELE,SP_MAGIC_ADDRACE,SP_MAGIC_ADDSIZE, // 1035-1037
+ SP_PERFECT_HIT_RATE,SP_PERFECT_HIT_ADD_RATE,SP_CRITICAL_RATE,SP_GET_ZENY_NUM,SP_ADD_GET_ZENY_NUM, // 1038-1042
+ SP_ADD_DAMAGE_CLASS,SP_ADD_MAGIC_DAMAGE_CLASS,SP_ADD_DEF_CLASS,SP_ADD_MDEF_CLASS, // 1043-1046
+ SP_ADD_MONSTER_DROP_ITEM,SP_DEF_RATIO_ATK_ELE,SP_DEF_RATIO_ATK_RACE,SP_ADD_SPEED, // 1047-1050
+ SP_HIT_RATE,SP_FLEE_RATE,SP_FLEE2_RATE,SP_DEF_RATE,SP_DEF2_RATE,SP_MDEF_RATE,SP_MDEF2_RATE, // 1051-1057
+ SP_SPLASH_RANGE,SP_SPLASH_ADD_RANGE,SP_AUTOSPELL,SP_HP_DRAIN_RATE,SP_SP_DRAIN_RATE, // 1058-1062
+ SP_SHORT_WEAPON_DAMAGE_RETURN,SP_LONG_WEAPON_DAMAGE_RETURN,SP_WEAPON_COMA_ELE,SP_WEAPON_COMA_RACE, // 1063-1066
+ SP_ADDEFF2,SP_BREAK_WEAPON_RATE,SP_BREAK_ARMOR_RATE,SP_ADD_STEAL_RATE, // 1067-1070
+ SP_MAGIC_DAMAGE_RETURN,SP_RANDOM_ATTACK_INCREASE,SP_ALL_STATS,SP_AGI_VIT,SP_AGI_DEX_STR,SP_PERFECT_HIDE, // 1071-1076
+ SP_FREE,SP_CLASSCHANGE, // 1077-1078
+ SP_HP_DRAIN_VALUE,SP_SP_DRAIN_VALUE, // 1079-1080
+ SP_WEAPON_ATK,SP_WEAPON_ATK_RATE, // 1081-1082
+ SP_DELAYRATE,SP_HP_DRAIN_RATE_RACE,SP_SP_DRAIN_RATE_RACE, // 1083-1085
+
+ SP_RESTART_FULL_RECOVER=2000,SP_NO_CASTCANCEL,SP_NO_SIZEFIX,SP_NO_MAGIC_DAMAGE,SP_NO_WEAPON_DAMAGE,SP_NO_GEMSTONE, // 2000-2005
+ SP_NO_CASTCANCEL2,SP_NO_MISC_DAMAGE,SP_UNBREAKABLE_WEAPON,SP_UNBREAKABLE_ARMOR, SP_UNBREAKABLE_HELM, // 2006-2010
+ SP_UNBREAKABLE_SHIELD, SP_LONG_ATK_RATE, // 2011-2012
+
+ SP_CRIT_ATK_RATE, SP_CRITICAL_ADDRACE, SP_NO_REGEN, SP_ADDEFF_WHENHIT, SP_AUTOSPELL_WHENHIT, // 2013-2017
+ SP_SKILL_ATK, SP_UNSTRIPABLE, SP_ADD_DAMAGE_BY_CLASS, // 2018-2020
+ SP_SP_GAIN_VALUE, SP_IGNORE_DEF_MOB, SP_HP_LOSS_RATE, SP_ADDRACE2, SP_HP_GAIN_VALUE, // 2021-2025
+ SP_SUBSIZE, SP_HP_DRAIN_VALUE_RACE, SP_ADD_ITEM_HEAL_RATE, SP_SP_DRAIN_VALUE_RACE, SP_EXP_ADDRACE, // 2026-2030
+ SP_SP_GAIN_RACE, SP_SUBRACE2, SP_ADDEFF_WHENHIT_SHORT, // 2031-2033
+ SP_UNSTRIPABLE_WEAPON,SP_UNSTRIPABLE_ARMOR,SP_UNSTRIPABLE_HELM,SP_UNSTRIPABLE_SHIELD, // 2034-2037
+ SP_INTRAVISION, SP_ADD_MONSTER_DROP_ITEMGROUP, SP_SP_LOSS_RATE, // 2038-2040
+ SP_ADD_SKILL_BLOW, SP_SP_VANISH_RATE //2041
+ //Before adding another, note that
+ //1077 (SP_FREE, previously disguise),
+ //are available!
+};
+
+enum {
+ LOOK_BASE,LOOK_HAIR,LOOK_WEAPON,LOOK_HEAD_BOTTOM,LOOK_HEAD_TOP,LOOK_HEAD_MID,LOOK_HAIR_COLOR,LOOK_CLOTHES_COLOR,LOOK_SHIELD,LOOK_SHOES
+};
+
+// CELLs for non-permanent cell-based effects (Pneuma, Basilica, Npcs, etc)
+#define CELL_NPC 0x1
+#define CELL_REGEN 0x2
+#define CELL_PNEUMA 0x4
+#define CELL_SAFETYWALL 0x8
+#define CELL_LANDPROTECTOR 0x10
+#define CELL_BASILICA 0x20
+#define CELL_ICEWALL 0x80
+/*
+ * map_getcell()で使用されるフラグ
+ */
+typedef enum {
+ CELL_CHKWALL=0, // 壁(セルタイプ1)
+ CELL_CHKWATER, // 水場(セルタイプ3)
+ CELL_CHKGROUND, // 地面障害物(セルタイプ5)
+ CELL_CHKPASS, // 通過可能(セルタイプ1,5以外)
+ CELL_CHKREACH, // Same as PASS, but ignores the cell-stacking mod.
+ CELL_CHKNOPASS, // 通過不可(セルタイプ1,5)
+ CELL_CHKNOREACH, // Same as NOPASS, but ignores the cell-stacking mod.
+ CELL_GETTYPE, // セルタイプを返す
+ CELL_GETCELLTYPE,
+ CELL_CHKNPC=0x10, // タッチタイプのNPC(セルタイプ0x80フラグ)
+ CELL_CHKREGEN, // cells that improve regeneration
+ CELL_CHKPNEUMA,
+ CELL_CHKSAFETYWALL,
+ CELL_CHKBASILICA, // バジリカ(セルタイプ0x40フラグ)
+ CELL_CHKLANDPROTECTOR,
+ CELL_CHKICEWALL,
+ CELL_CHKSTACK,
+} cell_t;
+// map_setcell()で使用されるフラグ
+enum {
+ CELL_SETNPC=0x10, // タッチタイプのNPCをセット
+ CELL_CLRNPC,
+ CELL_SETBASILICA, // バジリカをセット
+ CELL_CLRBASILICA, // バジリカをクリア
+ CELL_SETREGEN, // set regen cell
+ CELL_SETLANDPROTECTOR, //Set/Clear Magnetic Earth
+ CELL_CLRLANDPROTECTOR,
+ CELL_SETPNEUMA,
+ CELL_CLRPNEUMA,
+ CELL_SETSAFETYWALL,
+ CELL_CLRSAFETYWALL,
+ CELL_SETICEWALL,
+ CELL_CLRICEWALL,
+};
+
+struct chat_data {
+ struct block_list bl;
+
+ unsigned char pass[8]; /* password */
+ unsigned char title[61]; /* room title MAX 60 */
+ unsigned char limit; /* join limit */
+ unsigned char trigger;
+ unsigned char users; /* current users */
+ unsigned char pub; /* room attribute */
+ struct map_session_data *usersd[20];
+ struct block_list *owner_;
+ struct block_list **owner;
+ char npc_event[50];
+};
+
+extern struct map_data map[];
+extern int map_num;
+extern int autosave_interval;
+extern int minsave_interval;
+extern int save_settings;
+extern int agit_flag;
+extern int night_flag; // 0=day, 1=night [Yor]
+extern int enable_spy; //Determines if @spy commands are active.
+extern char db_path[256];
+
+// gat?ヨァ
+int map_getcell(int,int,int,cell_t);
+int map_getcellp(struct map_data*,int,int,cell_t);
+void map_setcell(int,int,int,int);
+extern int map_read_flag; // 0: grfォユォ。ォ、ォ・1: ォュォ罩テォキォ・2: ォュォ罩テォキォ・?)
+enum {
+ READ_FROM_GAT, READ_FROM_AFM,
+ READ_FROM_BITMAP, CREATE_BITMAP,
+ READ_FROM_BITMAP_COMPRESSED, CREATE_BITMAP_COMPRESSED
+};
+
+extern char motd_txt[];
+extern char help_txt[];
+extern char help2_txt[];
+extern char charhelp_txt[];
+
+extern char talkie_mes[];
+
+extern char wisp_server_name[];
+
+// 鯖全体情報
+void map_setusers(int);
+int map_getusers(void);
+// block削除関連
+int map_freeblock(struct block_list *bl);
+int map_freeblock_lock(void);
+//int map_freeblock_unlock(void);
+int map_freeblock_unlock_sub (char *file, int lineno);
+#define map_freeblock_unlock() map_freeblock_unlock_sub (__FILE__, __LINE__)
+// block関連
+int map_addblock_sub(struct block_list *, int);
+int map_delblock_sub(struct block_list *, int);
+#define map_addblock(bl) map_addblock_sub(bl,1)
+#define map_delblock(bl) map_delblock_sub(bl,1)
+int map_moveblock(struct block_list *, int, int, unsigned int);
+int map_foreachinrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...);
+int map_foreachinshootrange(int (*)(struct block_list*,va_list),struct block_list *,int,int,...);
+int map_foreachinarea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,...);
+// -- moonsoul (added map_foreachincell)
+int map_foreachincell(int (*)(struct block_list*,va_list),int,int,int,int,...);
+int map_foreachinmovearea(int (*)(struct block_list*,va_list),int,int,int,int,int,int,int,int,...);
+int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int type,...); // Celest
+int map_foreachinmap(int (*)(struct block_list*,va_list),int,int,...);
+int map_countnearpc(int,int,int);
+//block関連に追加
+int map_count_oncell(int m,int x,int y,int type);
+struct skill_unit *map_find_skill_unit_oncell(struct block_list *,int x,int y,int skill_id,struct skill_unit *);
+// 一時的object関連
+int map_addobject(struct block_list *);
+int map_delobject(int);
+int map_delobjectnofree(int id);
+void map_foreachobject(int (*)(struct block_list*,va_list),int,...);
+int map_search_freecell(struct block_list *src, int m, short *x, short *y, int rx, int ry, int flag);
+//
+int map_quit(struct map_session_data *);
+void map_quit_ack(struct map_session_data *);
+// npc
+int map_addnpc(int,struct npc_data *);
+
+// 床アイテム関連
+int map_clearflooritem_timer(int,unsigned int,int,int);
+int map_removemobs_timer(int,unsigned int,int,int);
+#define map_clearflooritem(id) map_clearflooritem_timer(0,0,id,1)
+int map_addflooritem(struct item *,int,int,int,int,struct map_session_data *,struct map_session_data *,struct map_session_data *,int);
+
+// キャラid=>キャラ名 変換関連
+void map_addchariddb(int charid,char *name);
+void map_delchariddb(int charid);
+int map_reqchariddb(struct map_session_data * sd,int charid);
+char * map_charid2nick(int);
+struct map_session_data * map_charid2sd(int);
+
+struct map_session_data * map_id2sd(int);
+struct block_list * map_id2bl(int);
+int map_mapindex2mapid(unsigned short mapindex);
+int map_mapname2mapid(char*);
+int map_mapname2ipport(unsigned short,int*,int*);
+int map_setipport(unsigned short map,unsigned long ip,int port);
+int map_eraseipport(unsigned short map,unsigned long ip,int port);
+int map_eraseallipport(void);
+void map_addiddb(struct block_list *);
+void map_deliddb(struct block_list *bl);
+struct map_session_data** map_getallusers(int *users);
+void map_foreachpc(int (*func)(DBKey,void*,va_list),...);
+int map_foreachiddb(int (*)(DBKey,void*,va_list),...);
+void map_addnickdb(struct map_session_data *);
+struct map_session_data * map_nick2sd(char*);
+int compare_item(struct item *a, struct item *b);
+
+// その他
+int map_check_dir(int s_dir,int t_dir);
+int map_calc_dir( struct block_list *src,int x,int y);
+int map_random_dir(struct block_list *bl, short *x, short *y); // [Skotlex]
+
+// Water functions...
+//
+int map_setwaterheight(int m, char *mapname, int height);
+int map_waterheight(char *mapname);
+
+// path.cより
+int path_search_real(struct walkpath_data *wpd,int m,int x0,int y0,int x1,int y1,int flag,cell_t flag2);
+#define path_search(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKNOPASS)
+#define path_search2(wpd,m,x0,y0,x1,y1,flag) path_search_real(wpd,m,x0,y0,x1,y1,flag,CELL_CHKWALL)
+
+int path_search_long_real(struct shootpath_data *spd,int m,int x0,int y0,int x1,int y1,cell_t flag);
+#define path_search_long(spd,m,x0,y0,x1,y1) path_search_long_real(spd,m,x0,y0,x1,y1,CELL_CHKWALL)
+
+int path_blownpos(int m,int x0,int y0,int dx,int dy,int count);
+
+// distance related functions [Skotlex]
+#define check_distance_bl(bl1, bl2, distance) check_distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y, distance)
+#define check_distance_blxy(bl, x1, y1, distance) check_distance((bl)->x-(x1), (bl)->y-(y1), distance)
+#define check_distance_xy(x0, y0, x1, y1, distance) check_distance((x0)-(x1), (y0)-(y1), distance)
+int check_distance(int dx, int dy, int distance);
+
+#define distance_bl(bl1, bl2) distance((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y)
+#define distance_blxy(bl, x1, y1) distance((bl)->x-(x1), (bl)->y-(y1))
+#define distance_xy(x0, y0, x1, y1) distance((x0)-(x1), (y0)-(y1))
+unsigned int distance(int dx, int dy);
+
+int cleanup_sub(struct block_list *bl, va_list ap);
+
+void map_helpscreen(int flag); // [Valaris]
+int map_delmap(char *mapname);
+
+int map_addmobtolist(unsigned short m, struct spawn_data *spawn); // [Wizputer]
+void map_spawnmobs(int); // [Wizputer]
+void map_removemobs(int); // [Wizputer]
+void do_reconnect_map(void); //Invoked on map-char reconnection [Skotlex]
+
+//Added for own save method
+int charsql_db_init(int method);
+
+extern char *INTER_CONF_NAME;
+extern char *LOG_CONF_NAME;
+extern char *MAP_CONF_NAME;
+extern char *BATTLE_CONF_FILENAME;
+extern char *ATCOMMAND_CONF_FILENAME;
+extern char *CHARCOMMAND_CONF_FILENAME;
+extern char *SCRIPT_CONF_NAME;
+extern char *MSG_CONF_NAME;
+extern char *GRF_PATH_FILENAME;
+
+
+extern int charsave_method; //needed ..
+extern char *map_server_dns;
+
+#ifndef TXT_ONLY
+
+// MySQL
+#ifdef __WIN32
+#include <my_global.h>
+#include <my_sys.h>
+#endif
+#include <mysql.h>
+
+extern char tmp_sql[65535];
+
+extern int db_use_sqldbs;
+extern MYSQL mmysql_handle;
+extern MYSQL_RES* sql_res ;
+extern MYSQL_ROW sql_row ;
+
+extern MYSQL charsql_handle;
+extern MYSQL_RES* charsql_res;
+extern MYSQL_ROW charsql_row;
+
+extern MYSQL logmysql_handle;
+extern MYSQL_RES* logsql_res ;
+extern MYSQL_ROW logsql_row ;
+
+extern int mail_server_enable;
+extern MYSQL mail_handle;
+extern MYSQL_RES* mail_res ;
+extern MYSQL_ROW mail_row ;
+
+extern char item_db_db[32];
+extern char item_db2_db[32];
+extern char mob_db_db[32];
+extern char mob_db2_db[32];
+extern char char_db[32];
+extern char mail_db[32];
+
+#endif /* not TXT_ONLY */
+//Useful typedefs from jA [Skotlex]
+typedef struct map_session_data TBL_PC;
+typedef struct npc_data TBL_NPC;
+typedef struct mob_data TBL_MOB;
+typedef struct flooritem_data TBL_ITEM;
+typedef struct chat_data TBL_CHAT;
+typedef struct skill_unit TBL_SKILL;
+typedef struct pet_data TBL_PET;
+typedef struct homun_data TBL_HOM;
+
+#define BL_CAST(type_, bl , dest) \
+ (((bl) == NULL || (bl)->type != type_) ? ((dest) = NULL, 0) : ((dest) = (T ## type_ *)(bl), 1))
+
+
+extern int lowest_gm_level;
+extern char main_chat_nick[16];
+
+#endif
diff --git a/src/map/mercenary.c b/src/map/mercenary.c
index f83d3673c..3ae9f8ffb 100644
--- a/src/map/mercenary.c
+++ b/src/map/mercenary.c
@@ -1,957 +1,957 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-#include <limits.h>
-
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/mmo.h"
-#include "../common/showmsg.h"
-
-#include "log.h"
-#include "clif.h"
-#include "chrif.h"
-#include "intif.h"
-#include "itemdb.h"
-#include "map.h"
-#include "pc.h"
-#include "status.h"
-#include "skill.h"
-#include "mob.h"
-#include "pet.h"
-#include "battle.h"
-#include "party.h"
-#include "guild.h"
-#include "atcommand.h"
-#include "script.h"
-#include "npc.h"
-#include "trade.h"
-#include "unit.h"
-
-#include "mercenary.h"
-#include "charsave.h"
-
-//Better equiprobability than rand()% [orn]
-#define rand(a, b) a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0))
-
-struct homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; //[orn]
-struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE];
-
-static int merc_hom_hungry(int tid,unsigned int tick,int id,int data);
-
-static unsigned int hexptbl[MAX_LEVEL];
-
-void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp)
-{
- clif_hominfo(hd->master,hd,0);
-}
-
-int merc_hom_dead(struct homun_data *hd, struct block_list *src)
-{
- //There's no intimacy penalties on death (from Tharis)
- struct map_session_data *sd = hd->master;
-
- clif_emotion(&hd->bl, 16) ; //wah
-
- //Delete timers when dead.
- merc_hom_hungry_timer_delete(hd);
- hd->homunculus.hp = 0;
-
- if (!sd) //unit remove map will invoke unit free
- return 3;
-
- clif_hominfo(sd,hd,0); // Send dead flag
- clif_emotion(&sd->bl, 28) ; //sob
- //Remove from map (if it has no intimacy, it is auto-removed from memory)
- return 3;
-}
-
-//Vaporize a character's homun. If flag, HP needs to be 80% or above.
-int merc_hom_vaporize(struct map_session_data *sd, int flag)
-{
- struct homun_data *hd;
-
- nullpo_retr(0, sd);
-
- hd = sd->hd;
- if (!hd || hd->homunculus.vaporize)
- return 0;
-
- if (status_isdead(&hd->bl))
- return 0; //Can't vaporize a dead homun.
-
- if (flag && hd->battle_status.hp < (hd->battle_status.max_hp*80/100))
- return 0;
-
- hd->regen.state.block = 3; //Block regen while vaporized.
- //Delete timers when vaporized.
- merc_hom_hungry_timer_delete(hd);
- hd->homunculus.vaporize = 1;
- clif_hominfo(sd, sd->hd, 0);
- merc_save(hd);
- return unit_remove_map(&hd->bl, 0);
-}
-
-//delete a homunculus, completely "killing it".
-//Emote is the emotion the master should use, send negative to disable.
-int merc_hom_delete(struct homun_data *hd, int emote)
-{
- struct map_session_data *sd;
- nullpo_retr(0, hd);
- sd = hd->master;
-
- if (!sd)
- return unit_free(&hd->bl,1);
-
- if (emote >= 0)
- clif_emotion(&sd->bl, emote);
-
- //This makes it be deleted right away.
- hd->homunculus.intimacy = 0;
- // Send homunculus_dead to client
- hd->homunculus.hp = 0;
- clif_hominfo(sd, hd, 0);
- return unit_remove_map(&hd->bl,0);
-}
-
-int merc_hom_calc_skilltree(struct homun_data *hd)
-{
- int i,id=0 ;
- int j,f=1;
- int c=0;
-
- nullpo_retr(0, hd);
- c = hd->homunculus.class_ - HM_CLASS_BASE;
-
- for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++)
- {
- if(hd->homunculus.hskill[id-HM_SKILLBASE-1].id)
- continue; //Skill already known.
- if(!battle_config.skillfree)
- {
- for(j=0;j<5;j++)
- {
- if( hskill_tree[c][i].need[j].id &&
- merc_hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv)
- {
- f=0;
- break;
- }
- }
- }
- if (f)
- hd->homunculus.hskill[id-HM_SKILLBASE-1].id = id ;
- }
- return 0;
-}
-
-int merc_hom_checkskill(struct homun_data *hd,int skill_id)
-{
- int i = skill_id - HM_SKILLBASE - 1;
- if(!hd)
- return 0;
-
- if(hd->homunculus.hskill[i].id == skill_id)
- return (hd->homunculus.hskill[i].lv);
-
- return 0;
-}
-
-int merc_skill_tree_get_max(int id, int b_class){
- int i, skillid;
- b_class -= HM_CLASS_BASE;
- for(i=0;(skillid=hskill_tree[b_class][i].id)>0;i++)
- if (id == skillid)
- return hskill_tree[b_class][i].max;
- return skill_get_max(id);
-}
-
-void merc_hom_skillup(struct homun_data *hd,int skillnum)
-{
- int i = 0 ;
- nullpo_retv(hd);
-
- if(!hd->homunculus.vaporize)
- {
- i = skillnum - HM_SKILLBASE - 1 ;
- if( hd->homunculus.skillpts > 0 &&
- hd->homunculus.hskill[i].id &&
- hd->homunculus.hskill[i].flag == 0 && //Don't allow raising while you have granted skills. [Skotlex]
- hd->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->homunculus.class_)
- )
- {
- hd->homunculus.hskill[i].lv++ ;
- hd->homunculus.skillpts-- ;
- status_calc_homunculus(hd,0) ;
- if (hd->master) {
- clif_homskillup(hd->master, skillnum);
- clif_hominfo(hd->master,hd,0);
- clif_homskillinfoblock(hd->master);
- }
- }
- }
-}
-
-int merc_hom_levelup(struct homun_data *hd)
-{
- int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ;
- int growth_max_hp, growth_max_sp ;
- char output[256] ;
-
- if (hd->homunculus.level == MAX_LEVEL || !hd->exp_next || hd->homunculus.exp < hd->exp_next)
- return 0 ;
-
- hd->homunculus.level++ ;
- if (!(hd->homunculus.level % 3))
- hd->homunculus.skillpts++ ; //1 skillpoint each 3 base level
-
- hd->homunculus.exp -= hd->exp_next ;
- hd->exp_next = hexptbl[hd->homunculus.level - 1] ;
-
- if ( hd->homunculusDB->gmaxHP <= hd->homunculusDB->gminHP )
- growth_max_hp = hd->homunculusDB->gminHP ;
- else
- growth_max_hp = rand(hd->homunculusDB->gminHP, hd->homunculusDB->gmaxHP) ;
- if ( hd->homunculusDB->gmaxSP <= hd->homunculusDB->gminSP )
- growth_max_sp = hd->homunculusDB->gminSP ;
- else
- growth_max_sp = rand(hd->homunculusDB->gminSP, hd->homunculusDB->gmaxSP) ;
- if ( hd->homunculusDB->gmaxSTR <= hd->homunculusDB->gminSTR )
- growth_str = hd->homunculusDB->gminSTR ;
- else
- growth_str = rand(hd->homunculusDB->gminSTR, hd->homunculusDB->gmaxSTR) ;
- if ( hd->homunculusDB->gmaxAGI <= hd->homunculusDB->gminAGI )
- growth_agi = hd->homunculusDB->gminAGI ;
- else
- growth_agi = rand(hd->homunculusDB->gminAGI, hd->homunculusDB->gmaxAGI) ;
- if ( hd->homunculusDB->gmaxVIT <= hd->homunculusDB->gminVIT )
- growth_vit = hd->homunculusDB->gminVIT ;
- else
- growth_vit = rand(hd->homunculusDB->gminVIT, hd->homunculusDB->gmaxVIT) ;
- if ( hd->homunculusDB->gmaxDEX <= hd->homunculusDB->gminDEX )
- growth_dex = hd->homunculusDB->gminDEX ;
- else
- growth_dex = rand(hd->homunculusDB->gminDEX, hd->homunculusDB->gmaxDEX) ;
- if ( hd->homunculusDB->gmaxINT <= hd->homunculusDB->gminINT )
- growth_int = hd->homunculusDB->gminINT ;
- else
- growth_int = rand(hd->homunculusDB->gminINT, hd->homunculusDB->gmaxINT) ;
- if ( hd->homunculusDB->gmaxLUK <= hd->homunculusDB->gminLUK )
- growth_luk = hd->homunculusDB->gminLUK ;
- else
- growth_luk = rand(hd->homunculusDB->gminLUK, hd->homunculusDB->gmaxLUK) ;
-
- hd->homunculus.max_hp += growth_max_hp;
- hd->homunculus.max_sp += growth_max_sp;
- hd->homunculus.str += growth_str ;
- hd->homunculus.agi += growth_agi ;
- hd->homunculus.vit += growth_vit ;
- hd->homunculus.dex += growth_dex ;
- hd->homunculus.int_ += growth_int ;
- hd->homunculus.luk += growth_luk ;
-
- if ( battle_config.homunculus_show_growth ) {
- sprintf(output,
- "Growth : hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ", growth_max_hp, growth_max_sp, growth_str/(float)10, growth_agi/(float)10, growth_vit/(float)10, growth_int/(float)10, growth_dex/(float)10, growth_luk/(float)10 ) ;
- clif_disp_onlyself(hd->master,output,strlen(output));
- }
- return 1 ;
-}
-
-int merc_hom_change_class(struct homun_data *hd, short class_)
-{
- int i;
- i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
- if(i < 0)
- return 0;
- hd->homunculusDB = &homunculus_db[i];
- hd->homunculus.class_ = class_;
- status_set_viewdata(&hd->bl, class_);
- merc_hom_calc_skilltree(hd);
- return 1;
-}
-
-int merc_hom_evolution(struct homun_data *hd)
-{
- struct map_session_data *sd;
- nullpo_retr(0, hd);
-
- if(!hd->homunculusDB->evo_class)
- {
- clif_emotion(&hd->bl, 4) ; //swt
- return 0 ;
- }
- sd = hd->master;
- if (!sd)
- return 0;
-
- merc_hom_vaporize(sd, 0);
-
- if (!merc_hom_change_class(hd, hd->homunculusDB->evo_class)) {
- ShowError("merc_hom_evolution: Can't evoluate homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class);
- return 0;
- }
- hd->homunculus.intimacy = 500;
- merc_call_homunculus(sd);
- clif_emotion(&sd->bl, 21); //no1
- clif_misceffect2(&hd->bl,568);
- return 1 ;
-}
-
-int merc_hom_gainexp(struct homun_data *hd,int exp)
-{
- if(hd->homunculus.vaporize)
- return 1;
-
- if( hd->exp_next == 0 ) {
- hd->homunculus.exp = 0 ;
- return 0;
- }
-
- hd->homunculus.exp += exp;
-
- if(hd->homunculus.exp < hd->exp_next) {
- clif_hominfo(hd->master,hd,0);
- return 0;
- }
-
- //levelup
- do
- {
- merc_hom_levelup(hd) ;
- }
- while(hd->homunculus.exp > hd->exp_next && hd->exp_next != 0 );
-
- if( hd->exp_next == 0 )
- hd->homunculus.exp = 0 ;
-
- clif_misceffect2(&hd->bl,568);
- status_calc_homunculus(hd,0);
- status_percent_heal(&hd->bl, 100, 100);
- return 0;
-}
-
-// Return the new value
-int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value)
-{
- if (battle_config.homunculus_friendly_rate != 100)
- value = (value * battle_config.homunculus_friendly_rate) / 100;
-
- if (hd->homunculus.intimacy + value <= 100000)
- hd->homunculus.intimacy += value;
- else
- hd->homunculus.intimacy = 100000;
- return hd->homunculus.intimacy;
-}
-
-// Return 0 if decrease fails or intimacy became 0 else the new value
-int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value)
-{
- if (hd->homunculus.intimacy >= value)
- hd->homunculus.intimacy -= value;
- else
- hd->homunculus.intimacy = 0;
-
- return hd->homunculus.intimacy;
-}
-
-void merc_hom_heal(struct homun_data *hd,int hp,int sp)
-{
- clif_hominfo(hd->master,hd,0);
-}
-
-void merc_save(struct homun_data *hd)
-{
- // copy data that must be saved in homunculus struct ( hp / sp )
- TBL_PC * sd = hd->master;
- //Do not check for max_hp/max_sp caps as current could be higher to max due
- //to status changes/skills (they will be capped as needed upon stat
- //calculation on login)
- hd->homunculus.hp = hd->battle_status.hp;
- hd->homunculus.sp = hd->battle_status.sp;
- intif_homunculus_requestsave(sd->status.account_id, &hd->homunculus) ;
-}
-
-int merc_menu(struct map_session_data *sd,int menunum)
-{
- nullpo_retr(0, sd);
- if (sd->hd == NULL)
- return 1;
-
- switch(menunum) {
- case 0:
- break;
- case 1:
- merc_hom_food(sd, sd->hd);
- break;
- case 2:
- merc_hom_delete(sd->hd, -1);
- break;
- default:
- ShowError("merc_menu : unknown menu choice : %d\n", menunum) ;
- break;
- }
- return 0;
-}
-
-int merc_hom_food(struct map_session_data *sd, struct homun_data *hd)
-{
- int i, foodID, emotion;
-
- if(hd->homunculus.vaporize)
- return 1 ;
-
- foodID = hd->homunculusDB->foodID;
- i = pc_search_inventory(sd,foodID);
- if(i < 0) {
- clif_hom_food(sd,foodID,0);
- return 1;
- }
- pc_delitem(sd,i,1,0);
-
- if ( hd->homunculus.hunger >= 91 ) {
- merc_hom_decrease_intimacy(hd, 50);
- emotion = 16;
- } else if ( hd->homunculus.hunger >= 76 ) {
- merc_hom_decrease_intimacy(hd, 5);
- emotion = 19;
- } else if ( hd->homunculus.hunger >= 26 ) {
- merc_hom_increase_intimacy(hd, 75);
- emotion = 2;
- } else if ( hd->homunculus.hunger >= 11 ) {
- merc_hom_increase_intimacy(hd, 100);
- emotion = 2;
- } else {
- merc_hom_increase_intimacy(hd, 50);
- emotion = 2;
- }
-
- hd->homunculus.hunger += 10; //dunno increase value for each food
- if(hd->homunculus.hunger > 100)
- hd->homunculus.hunger = 100;
-
- clif_emotion(&hd->bl,emotion) ;
- clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
- clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
- clif_hom_food(sd,foodID,1);
-
- // Too much food :/
- if(hd->homunculus.intimacy == 0)
- return merc_hom_delete(sd->hd, 23); //omg
-
- return 0;
-}
-
-static int merc_hom_hungry(int tid,unsigned int tick,int id,int data)
-{
- struct map_session_data *sd;
- struct homun_data *hd;
-
- sd=map_id2sd(id);
- if(!sd)
- return 1;
-
- if(!sd->status.hom_id || !(hd=sd->hd))
- return 1;
-
- if(hd->hungry_timer != tid){
- if(battle_config.error_log)
- ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid);
- return 0;
- }
-
- hd->hungry_timer = -1;
-
- hd->homunculus.hunger-- ;
- if(hd->homunculus.hunger <= 10) {
- clif_emotion(&hd->bl, 6) ; //an
- } else if(hd->homunculus.hunger == 25) {
- clif_emotion(&hd->bl, 20) ; //hmm
- } else if(hd->homunculus.hunger == 75) {
- clif_emotion(&hd->bl, 33) ; //ok
- }
-
- if(hd->homunculus.hunger < 0) {
- hd->homunculus.hunger = 0;
- // Delete the homunculus if intimacy <= 100
- if ( !merc_hom_decrease_intimacy(hd, 100) )
- return merc_hom_delete(hd, 23); //omg
- clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
- }
-
- clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
- hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator
- return 0;
-}
-
-int merc_hom_hungry_timer_delete(struct homun_data *hd)
-{
- nullpo_retr(0, hd);
- if(hd->hungry_timer != -1) {
- delete_timer(hd->hungry_timer,merc_hom_hungry);
- hd->hungry_timer = -1;
- }
- return 1;
-}
-
-int search_homunculusDB_index(int key,int type)
-{
- int i;
-
- for(i=0;i<MAX_HOMUNCULUS_CLASS;i++) {
- if(homunculus_db[i].class_ <= 0)
- continue;
- switch(type) {
- case HOMUNCULUS_CLASS:
- if(homunculus_db[i].class_ == key)
- return i;
- break;
- case HOMUNCULUS_FOOD:
- if(homunculus_db[i].foodID == key)
- return i;
- break;
- default:
- return -1;
- }
- }
- return -1;
-}
-
-// Create homunc structure
-int merc_hom_alloc(struct map_session_data *sd, struct s_homunculus *hom)
-{
- struct homun_data *hd;
- int i = 0;
- short x,y;
-
- nullpo_retr(1, sd);
-
- Assert((sd->status.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd);
-
- i = search_homunculusDB_index(hom->class_,HOMUNCULUS_CLASS);
- if(i < 0) {
- ShowError("merc_hom_alloc: unknown homunculus class [%d]", hom->class_);
- sd->status.hom_id = 0;
- intif_homunculus_requestdelete(hom->hom_id);
- return 1;
- }
- sd->hd = hd = aCalloc(1,sizeof(struct homun_data));
- hd->bl.subtype = MONS;
- hd->bl.type = BL_HOM;
- hd->bl.id = npc_get_new_npc_id();
-
- hd->master = sd;
- hd->homunculusDB = &homunculus_db[i];
- memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus));
- hd->exp_next = hexptbl[hd->homunculus.level - 1];
-
- status_set_viewdata(&hd->bl, hd->homunculus.class_);
- status_change_init(&hd->bl);
- unit_dataset(&hd->bl);
- hd->ud.dir = sd->ud.dir;
-
- // Find a random valid pos around the player
- hd->bl.m = sd->bl.m;
- hd->bl.x = sd->bl.x;
- hd->bl.y = sd->bl.y;
- x = sd->bl.x + 1;
- y = sd->bl.y + 1;
- map_random_dir(&hd->bl, &x, &y);
- hd->bl.x = x;
- hd->bl.y = y;
-
- map_addiddb(&hd->bl);
- status_calc_homunculus(hd,1);
-
- hd->hungry_timer = -1;
- return 0;
-}
-
-void merc_hom_init_timers(struct homun_data * hd)
-{
- if (hd->hungry_timer == -1)
- hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,hd->master->bl.id,0);
- hd->regen.state.block = 0; //Restore HP/SP block.
-}
-
-int merc_call_homunculus(struct map_session_data *sd)
-{
- struct homun_data *hd;
-
- if (!sd->status.hom_id) //Create a new homun.
- return merc_create_homunculus_request(sd, HM_CLASS_BASE + rand(0, 7)) ;
-
- // If homunc not yet loaded, load it
- if (!sd->hd)
- return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
-
- hd = sd->hd;
-
- if (!hd->homunculus.vaporize)
- return 0; //Can't use this if homun wasn't vaporized.
-
- merc_hom_init_timers(hd);
- hd->homunculus.vaporize = 0;
- if (hd->bl.prev == NULL)
- { //Spawn him
- hd->bl.x = sd->bl.x;
- hd->bl.y = sd->bl.y;
- hd->bl.m = sd->bl.m;
- map_addblock(&hd->bl);
- clif_spawn(&hd->bl);
- clif_send_homdata(sd,SP_ACK,0);
- clif_hominfo(sd,hd,1);
- clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
- clif_homskillinfoblock(sd);
- merc_save(hd);
- } else
- //Warp him to master.
- unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,0);
- return 1;
-}
-
-// Recv homunculus data from char server
-int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag)
-{
- struct map_session_data *sd;
- struct homun_data *hd;
-
- sd = map_id2sd(account_id);
- if(!sd)
- return 0;
- if (sd->status.char_id != sh->char_id)
- {
- if (sd->status.hom_id == sh->hom_id)
- sh->char_id = sd->status.char_id; //Correct char id.
- else
- return 0;
- }
- if(!flag) { // Failed to load
- sd->status.hom_id = 0;
- return 0;
- }
-
- if (!sd->status.hom_id) //Hom just created.
- sd->status.hom_id = sh->hom_id;
- if (sd->hd) //uh? Overwrite the data.
- memcpy(&sd->hd->homunculus, sh, sizeof(struct s_homunculus));
- else
- merc_hom_alloc(sd, sh);
-
- hd = sd->hd;
- if(hd->homunculus.hp && !hd->homunculus.vaporize &&
- hd->bl.prev == NULL && sd->bl.prev != NULL)
- {
- map_addblock(&hd->bl);
- clif_spawn(&hd->bl);
- clif_hominfo(sd,hd,1);
- clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
- clif_homskillinfoblock(sd);
- clif_hominfo(sd,hd,0);
- clif_send_homdata(sd,SP_ACK,0);
- merc_hom_init_timers(hd);
- }
- return 1;
-}
-
-// Ask homunculus creation to char server
-int merc_create_homunculus_request(struct map_session_data *sd, int class_)
-{
- struct s_homunculus homun;
- int i;
-
- nullpo_retr(1, sd);
-
- i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
- if(i < 0) return 0;
-
- memset(&homun, 0, sizeof(struct s_homunculus));
- //Initial data
- strncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1);
- homun.class_ = class_;
- homun.level = 1;
-// FIXME: Commented value is what the map-server had as initial value,
-// Uncommented value is what the char-server was overwriting it with
-// So which one is correct?
-// homun.hunger = 50;
- homun.hunger = 32;
-// homun.intimacy = 500;
- homun.intimacy = 21;
- homun.char_id = sd->status.char_id;
-
- homun.hp = 10 ;
- homun.max_hp = homunculus_db[i].basemaxHP;
- homun.max_sp = homunculus_db[i].basemaxSP;
- homun.str = homunculus_db[i].baseSTR * 10;
- homun.agi = homunculus_db[i].baseAGI * 10;
- homun.vit = homunculus_db[i].baseVIT * 10;
- homun.int_ = homunculus_db[i].baseINT * 10;
- homun.dex = homunculus_db[i].baseDEX * 10;
- homun.luk = homunculus_db[i].baseLUK * 10;
-
- // Request homunculus creation
- intif_homunculus_create(sd->status.account_id, &homun);
- return 1;
-}
-
-int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y)
-{
- struct homun_data *hd;
- nullpo_retr(0, sd);
- if (!sd->status.hom_id)
- return 0;
-
- if (!sd->hd) //Load homun data;
- return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
-
- hd = sd->hd;
-
- if (hd->homunculus.vaporize)
- return 0;
-
- if (!status_isdead(&hd->bl))
- return 0;
-
- merc_hom_init_timers(hd);
-
- if (!hd->bl.prev)
- { //Add it back to the map.
- hd->bl.m = sd->bl.m;
- hd->bl.x = x;
- hd->bl.y = y;
- map_addblock(&hd->bl);
- clif_spawn(&hd->bl);
- }
- status_revive(&hd->bl, per, 0);
- return 1;
-}
-
-void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp)
-{
- struct map_session_data *sd = hd->master;
- hd->homunculus.hp = hd->battle_status.hp;
- if (!sd)
- return;
- clif_send_homdata(sd,SP_ACK,0);
- clif_hominfo(sd,hd,1);
- clif_hominfo(sd,hd,0);
- clif_homskillinfoblock(sd);
-}
-
-int read_homunculusdb(void)
-{
- FILE *fp;
- char line[1024], *p;
- int i, k, classid;
- int j = 0;
- char *filename[]={"homunculus_db.txt","homunculus_db2.txt"};
- char *str[36];
-
- malloc_set(homunculus_db,0,sizeof(homunculus_db));
- for(i = 0; i<2; i++)
- {
- sprintf(line, "%s/%s", db_path, filename[i]);
- fp = fopen(line,"r");
- if(!fp){
- if(i != 0)
- continue;
- ShowError("read_homunculusdb : can't read %s\n", line);
- return -1;
- }
-
- while(fgets(line,sizeof(line)-1,fp) && j < MAX_HOMUNCULUS_CLASS)
- {
- if(line[0] == '/' && line[1] == '/')
- continue;
-
- k = 0;
- p = strtok (line,",");
- while (p != NULL && k < 36)
- {
- str[k++] = p;
- p = strtok (NULL, ",");
- }
-
- classid = atoi(str[0]);
- if (k != 36 || classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
- {
- ShowError("read_homunculusdb : Error reading %s", filename[i]);
- continue;
- }
-
- //Class,Homunculus,HP,SP,ATK,MATK,HIT,CRI,DEF,MDEF,FLEE,ASPD,STR,AGI,VIT,INT,DEX,LUK
- homunculus_db[j].class_ = classid;
- strncpy(homunculus_db[j].name,str[1],NAME_LENGTH-1);
- homunculus_db[j].basemaxHP = atoi(str[2]);
- homunculus_db[j].basemaxSP = atoi(str[3]);
- homunculus_db[j].baseSTR = atoi(str[4]);
- homunculus_db[j].baseAGI = atoi(str[5]);
- homunculus_db[j].baseVIT = atoi(str[6]);
- homunculus_db[j].baseINT = atoi(str[7]);
- homunculus_db[j].baseDEX = atoi(str[8]);
- homunculus_db[j].baseLUK = atoi(str[9]);
- homunculus_db[j].baseIntimacy = atoi(str[10]);
- homunculus_db[j].baseHungry = atoi(str[11]);
- homunculus_db[j].hungryDelay = atoi(str[12]);
- homunculus_db[j].foodID = atoi(str[13]);
- homunculus_db[j].gminHP = atoi(str[14]);
- homunculus_db[j].gmaxHP = atoi(str[15]);
- homunculus_db[j].gminSP = atoi(str[16]);
- homunculus_db[j].gmaxSP = atoi(str[17]);
- homunculus_db[j].gminSTR = atoi(str[18]);
- homunculus_db[j].gmaxSTR = atoi(str[19]);
- homunculus_db[j].gminAGI = atoi(str[20]);
- homunculus_db[j].gmaxAGI = atoi(str[21]);
- homunculus_db[j].gminVIT = atoi(str[22]);
- homunculus_db[j].gmaxVIT = atoi(str[23]);
- homunculus_db[j].gminINT = atoi(str[24]);
- homunculus_db[j].gmaxINT = atoi(str[25]);
- homunculus_db[j].gminDEX = atoi(str[26]);
- homunculus_db[j].gmaxDEX = atoi(str[27]);
- homunculus_db[j].gminLUK = atoi(str[28]);
- homunculus_db[j].gmaxLUK = atoi(str[29]);
- homunculus_db[j].evo_class = atoi(str[30]);
- homunculus_db[j].baseASPD = atoi(str[31]);
- homunculus_db[j].size = atoi(str[32]);
- homunculus_db[j].race = atoi(str[33]);
- homunculus_db[j].element = atoi(str[34]);
- homunculus_db[j].accessID = atoi(str[35]);
- j++;
- }
- if (j > MAX_HOMUNCULUS_CLASS)
- ShowWarning("read_homunculusdb: Reached max number of homunculus [%d]. Remaining homunculus were not read.\n ", MAX_HOMUNCULUS_CLASS);
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' homunculus in '"CL_WHITE"db/%s"CL_RESET"'.\n",j,filename[i]);
- }
- return 0;
-}
-
-int read_homunculus_skilldb(void)
-{
- FILE *fp;
- char line[1024], *p;
- int k, classid;
- int j = 0;
- char *split[15];
-
- malloc_tsetdword(hskill_tree,0,sizeof(hskill_tree));
- sprintf(line, "%s/homun_skill_tree.txt", db_path);
- fp=fopen(line,"r");
- if(fp==NULL){
- ShowError("can't read %s\n", line);
- return 1;
- }
-
- while(fgets(line, sizeof(line)-1, fp))
- {
- int minJobLevelPresent = 0;
-
- if(line[0]=='/' && line[1]=='/')
- continue;
-
- k = 0;
- p = strtok(line,",");
- while (p != NULL && k < 15)
- {
- split[k++] = p;
- p = strtok(NULL, ",");
- }
-
- if(k < 13)
- continue;
-
- if (k == 14)
- minJobLevelPresent = 1; // MinJobLvl has been added
-
- // check for bounds [celest]
- classid = atoi(split[0]) - HM_CLASS_BASE;
- if ( classid >= MAX_HOMUNCULUS_CLASS )
- continue;
-
- k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
- // Search an empty line or a line with the same skill_id (stored in j)
- for(j = 0; j < MAX_SKILL_TREE && hskill_tree[classid][j].id && hskill_tree[classid][j].id != k; j++);
-
- if (j == MAX_SKILL_TREE)
- {
- ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", k, classid);
- continue;
- }
-
- hskill_tree[classid][j].id=k;
- hskill_tree[classid][j].max=atoi(split[2]);
- if (minJobLevelPresent)
- hskill_tree[classid][j].joblv=atoi(split[3]);
-
- for(k=0;k<5;k++){
- hskill_tree[classid][j].need[k].id=atoi(split[3+k*2+minJobLevelPresent]);
- hskill_tree[classid][j].need[k].lv=atoi(split[3+k*2+minJobLevelPresent+1]);
- }
- }
-
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt");
- return 0;
-}
-
-void read_homunculus_expdb(void)
-{
- FILE *fp;
- char line[1024];
- int i, j=0;
- char *filename[]={"exp_homun.txt","exp_homun2.txt"};
-
- malloc_tsetdword(hexptbl,0,sizeof(hexptbl));
- for(i=0; i<2; i++){
- sprintf(line, "%s/%s", db_path, filename[i]);
- fp=fopen(line,"r");
- if(fp == NULL){
- if(i != 0)
- continue;
- ShowError("can't read %s\n",line);
- return;
- }
- while(fgets(line,sizeof(line)-1,fp) && j < MAX_LEVEL)
- {
- if(line[0] == '/' && line[1] == '/')
- continue;
-
- hexptbl[j] = strtoul(line, NULL, 10);
- if (!hexptbl[j++])
- break;
- }
- if (hexptbl[MAX_LEVEL - 1]) // Last permitted level have to be 0!
- {
- ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_LEVEL);
- hexptbl[MAX_LEVEL - 1] = 0;
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s"CL_RESET"'.\n", j, filename[i]);
- }
-}
-
-void merc_reload(void)
-{
- read_homunculusdb();
- read_homunculus_expdb();
-}
-
-void merc_skill_reload(void)
-{
- read_homunculus_skilldb();
-}
-
-int do_init_merc(void)
-{
- read_homunculusdb();
- read_homunculus_expdb();
- read_homunculus_skilldb();
- // Add homunc timer function to timer func list [Toms]
- add_timer_func_list(merc_hom_hungry, "merc_hom_hungry");
- return 0;
-}
-
-int do_final_merc(void);
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+#include <limits.h>
+
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "script.h"
+#include "npc.h"
+#include "trade.h"
+#include "unit.h"
+
+#include "mercenary.h"
+#include "charsave.h"
+
+//Better equiprobability than rand()% [orn]
+#define rand(a, b) a+(int) ((float)(b-a+1)*rand()/(RAND_MAX+1.0))
+
+struct homunculus_db homunculus_db[MAX_HOMUNCULUS_CLASS]; //[orn]
+struct skill_tree_entry hskill_tree[MAX_HOMUNCULUS_CLASS][MAX_SKILL_TREE];
+
+static int merc_hom_hungry(int tid,unsigned int tick,int id,int data);
+
+static unsigned int hexptbl[MAX_LEVEL];
+
+void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp)
+{
+ clif_hominfo(hd->master,hd,0);
+}
+
+int merc_hom_dead(struct homun_data *hd, struct block_list *src)
+{
+ //There's no intimacy penalties on death (from Tharis)
+ struct map_session_data *sd = hd->master;
+
+ clif_emotion(&hd->bl, 16) ; //wah
+
+ //Delete timers when dead.
+ merc_hom_hungry_timer_delete(hd);
+ hd->homunculus.hp = 0;
+
+ if (!sd) //unit remove map will invoke unit free
+ return 3;
+
+ clif_hominfo(sd,hd,0); // Send dead flag
+ clif_emotion(&sd->bl, 28) ; //sob
+ //Remove from map (if it has no intimacy, it is auto-removed from memory)
+ return 3;
+}
+
+//Vaporize a character's homun. If flag, HP needs to be 80% or above.
+int merc_hom_vaporize(struct map_session_data *sd, int flag)
+{
+ struct homun_data *hd;
+
+ nullpo_retr(0, sd);
+
+ hd = sd->hd;
+ if (!hd || hd->homunculus.vaporize)
+ return 0;
+
+ if (status_isdead(&hd->bl))
+ return 0; //Can't vaporize a dead homun.
+
+ if (flag && hd->battle_status.hp < (hd->battle_status.max_hp*80/100))
+ return 0;
+
+ hd->regen.state.block = 3; //Block regen while vaporized.
+ //Delete timers when vaporized.
+ merc_hom_hungry_timer_delete(hd);
+ hd->homunculus.vaporize = 1;
+ clif_hominfo(sd, sd->hd, 0);
+ merc_save(hd);
+ return unit_remove_map(&hd->bl, 0);
+}
+
+//delete a homunculus, completely "killing it".
+//Emote is the emotion the master should use, send negative to disable.
+int merc_hom_delete(struct homun_data *hd, int emote)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, hd);
+ sd = hd->master;
+
+ if (!sd)
+ return unit_free(&hd->bl,1);
+
+ if (emote >= 0)
+ clif_emotion(&sd->bl, emote);
+
+ //This makes it be deleted right away.
+ hd->homunculus.intimacy = 0;
+ // Send homunculus_dead to client
+ hd->homunculus.hp = 0;
+ clif_hominfo(sd, hd, 0);
+ return unit_remove_map(&hd->bl,0);
+}
+
+int merc_hom_calc_skilltree(struct homun_data *hd)
+{
+ int i,id=0 ;
+ int j,f=1;
+ int c=0;
+
+ nullpo_retr(0, hd);
+ c = hd->homunculus.class_ - HM_CLASS_BASE;
+
+ for(i=0;i < MAX_SKILL_TREE && (id = hskill_tree[c][i].id) > 0;i++)
+ {
+ if(hd->homunculus.hskill[id-HM_SKILLBASE-1].id)
+ continue; //Skill already known.
+ if(!battle_config.skillfree)
+ {
+ for(j=0;j<5;j++)
+ {
+ if( hskill_tree[c][i].need[j].id &&
+ merc_hom_checkskill(hd,hskill_tree[c][i].need[j].id) < hskill_tree[c][i].need[j].lv)
+ {
+ f=0;
+ break;
+ }
+ }
+ }
+ if (f)
+ hd->homunculus.hskill[id-HM_SKILLBASE-1].id = id ;
+ }
+ return 0;
+}
+
+int merc_hom_checkskill(struct homun_data *hd,int skill_id)
+{
+ int i = skill_id - HM_SKILLBASE - 1;
+ if(!hd)
+ return 0;
+
+ if(hd->homunculus.hskill[i].id == skill_id)
+ return (hd->homunculus.hskill[i].lv);
+
+ return 0;
+}
+
+int merc_skill_tree_get_max(int id, int b_class){
+ int i, skillid;
+ b_class -= HM_CLASS_BASE;
+ for(i=0;(skillid=hskill_tree[b_class][i].id)>0;i++)
+ if (id == skillid)
+ return hskill_tree[b_class][i].max;
+ return skill_get_max(id);
+}
+
+void merc_hom_skillup(struct homun_data *hd,int skillnum)
+{
+ int i = 0 ;
+ nullpo_retv(hd);
+
+ if(!hd->homunculus.vaporize)
+ {
+ i = skillnum - HM_SKILLBASE - 1 ;
+ if( hd->homunculus.skillpts > 0 &&
+ hd->homunculus.hskill[i].id &&
+ hd->homunculus.hskill[i].flag == 0 && //Don't allow raising while you have granted skills. [Skotlex]
+ hd->homunculus.hskill[i].lv < merc_skill_tree_get_max(skillnum, hd->homunculus.class_)
+ )
+ {
+ hd->homunculus.hskill[i].lv++ ;
+ hd->homunculus.skillpts-- ;
+ status_calc_homunculus(hd,0) ;
+ if (hd->master) {
+ clif_homskillup(hd->master, skillnum);
+ clif_hominfo(hd->master,hd,0);
+ clif_homskillinfoblock(hd->master);
+ }
+ }
+ }
+}
+
+int merc_hom_levelup(struct homun_data *hd)
+{
+ int growth_str, growth_agi, growth_vit, growth_int, growth_dex, growth_luk ;
+ int growth_max_hp, growth_max_sp ;
+ char output[256] ;
+
+ if (hd->homunculus.level == MAX_LEVEL || !hd->exp_next || hd->homunculus.exp < hd->exp_next)
+ return 0 ;
+
+ hd->homunculus.level++ ;
+ if (!(hd->homunculus.level % 3))
+ hd->homunculus.skillpts++ ; //1 skillpoint each 3 base level
+
+ hd->homunculus.exp -= hd->exp_next ;
+ hd->exp_next = hexptbl[hd->homunculus.level - 1] ;
+
+ if ( hd->homunculusDB->gmaxHP <= hd->homunculusDB->gminHP )
+ growth_max_hp = hd->homunculusDB->gminHP ;
+ else
+ growth_max_hp = rand(hd->homunculusDB->gminHP, hd->homunculusDB->gmaxHP) ;
+ if ( hd->homunculusDB->gmaxSP <= hd->homunculusDB->gminSP )
+ growth_max_sp = hd->homunculusDB->gminSP ;
+ else
+ growth_max_sp = rand(hd->homunculusDB->gminSP, hd->homunculusDB->gmaxSP) ;
+ if ( hd->homunculusDB->gmaxSTR <= hd->homunculusDB->gminSTR )
+ growth_str = hd->homunculusDB->gminSTR ;
+ else
+ growth_str = rand(hd->homunculusDB->gminSTR, hd->homunculusDB->gmaxSTR) ;
+ if ( hd->homunculusDB->gmaxAGI <= hd->homunculusDB->gminAGI )
+ growth_agi = hd->homunculusDB->gminAGI ;
+ else
+ growth_agi = rand(hd->homunculusDB->gminAGI, hd->homunculusDB->gmaxAGI) ;
+ if ( hd->homunculusDB->gmaxVIT <= hd->homunculusDB->gminVIT )
+ growth_vit = hd->homunculusDB->gminVIT ;
+ else
+ growth_vit = rand(hd->homunculusDB->gminVIT, hd->homunculusDB->gmaxVIT) ;
+ if ( hd->homunculusDB->gmaxDEX <= hd->homunculusDB->gminDEX )
+ growth_dex = hd->homunculusDB->gminDEX ;
+ else
+ growth_dex = rand(hd->homunculusDB->gminDEX, hd->homunculusDB->gmaxDEX) ;
+ if ( hd->homunculusDB->gmaxINT <= hd->homunculusDB->gminINT )
+ growth_int = hd->homunculusDB->gminINT ;
+ else
+ growth_int = rand(hd->homunculusDB->gminINT, hd->homunculusDB->gmaxINT) ;
+ if ( hd->homunculusDB->gmaxLUK <= hd->homunculusDB->gminLUK )
+ growth_luk = hd->homunculusDB->gminLUK ;
+ else
+ growth_luk = rand(hd->homunculusDB->gminLUK, hd->homunculusDB->gmaxLUK) ;
+
+ hd->homunculus.max_hp += growth_max_hp;
+ hd->homunculus.max_sp += growth_max_sp;
+ hd->homunculus.str += growth_str ;
+ hd->homunculus.agi += growth_agi ;
+ hd->homunculus.vit += growth_vit ;
+ hd->homunculus.dex += growth_dex ;
+ hd->homunculus.int_ += growth_int ;
+ hd->homunculus.luk += growth_luk ;
+
+ if ( battle_config.homunculus_show_growth ) {
+ sprintf(output,
+ "Growth : hp:%d sp:%d str(%.2f) agi(%.2f) vit(%.2f) int(%.2f) dex(%.2f) luk(%.2f) ", growth_max_hp, growth_max_sp, growth_str/(float)10, growth_agi/(float)10, growth_vit/(float)10, growth_int/(float)10, growth_dex/(float)10, growth_luk/(float)10 ) ;
+ clif_disp_onlyself(hd->master,output,strlen(output));
+ }
+ return 1 ;
+}
+
+int merc_hom_change_class(struct homun_data *hd, short class_)
+{
+ int i;
+ i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
+ if(i < 0)
+ return 0;
+ hd->homunculusDB = &homunculus_db[i];
+ hd->homunculus.class_ = class_;
+ status_set_viewdata(&hd->bl, class_);
+ merc_hom_calc_skilltree(hd);
+ return 1;
+}
+
+int merc_hom_evolution(struct homun_data *hd)
+{
+ struct map_session_data *sd;
+ nullpo_retr(0, hd);
+
+ if(!hd->homunculusDB->evo_class)
+ {
+ clif_emotion(&hd->bl, 4) ; //swt
+ return 0 ;
+ }
+ sd = hd->master;
+ if (!sd)
+ return 0;
+
+ merc_hom_vaporize(sd, 0);
+
+ if (!merc_hom_change_class(hd, hd->homunculusDB->evo_class)) {
+ ShowError("merc_hom_evolution: Can't evoluate homunc from %d to %d", hd->homunculus.class_, hd->homunculusDB->evo_class);
+ return 0;
+ }
+ hd->homunculus.intimacy = 500;
+ merc_call_homunculus(sd);
+ clif_emotion(&sd->bl, 21); //no1
+ clif_misceffect2(&hd->bl,568);
+ return 1 ;
+}
+
+int merc_hom_gainexp(struct homun_data *hd,int exp)
+{
+ if(hd->homunculus.vaporize)
+ return 1;
+
+ if( hd->exp_next == 0 ) {
+ hd->homunculus.exp = 0 ;
+ return 0;
+ }
+
+ hd->homunculus.exp += exp;
+
+ if(hd->homunculus.exp < hd->exp_next) {
+ clif_hominfo(hd->master,hd,0);
+ return 0;
+ }
+
+ //levelup
+ do
+ {
+ merc_hom_levelup(hd) ;
+ }
+ while(hd->homunculus.exp > hd->exp_next && hd->exp_next != 0 );
+
+ if( hd->exp_next == 0 )
+ hd->homunculus.exp = 0 ;
+
+ clif_misceffect2(&hd->bl,568);
+ status_calc_homunculus(hd,0);
+ status_percent_heal(&hd->bl, 100, 100);
+ return 0;
+}
+
+// Return the new value
+int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value)
+{
+ if (battle_config.homunculus_friendly_rate != 100)
+ value = (value * battle_config.homunculus_friendly_rate) / 100;
+
+ if (hd->homunculus.intimacy + value <= 100000)
+ hd->homunculus.intimacy += value;
+ else
+ hd->homunculus.intimacy = 100000;
+ return hd->homunculus.intimacy;
+}
+
+// Return 0 if decrease fails or intimacy became 0 else the new value
+int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value)
+{
+ if (hd->homunculus.intimacy >= value)
+ hd->homunculus.intimacy -= value;
+ else
+ hd->homunculus.intimacy = 0;
+
+ return hd->homunculus.intimacy;
+}
+
+void merc_hom_heal(struct homun_data *hd,int hp,int sp)
+{
+ clif_hominfo(hd->master,hd,0);
+}
+
+void merc_save(struct homun_data *hd)
+{
+ // copy data that must be saved in homunculus struct ( hp / sp )
+ TBL_PC * sd = hd->master;
+ //Do not check for max_hp/max_sp caps as current could be higher to max due
+ //to status changes/skills (they will be capped as needed upon stat
+ //calculation on login)
+ hd->homunculus.hp = hd->battle_status.hp;
+ hd->homunculus.sp = hd->battle_status.sp;
+ intif_homunculus_requestsave(sd->status.account_id, &hd->homunculus) ;
+}
+
+int merc_menu(struct map_session_data *sd,int menunum)
+{
+ nullpo_retr(0, sd);
+ if (sd->hd == NULL)
+ return 1;
+
+ switch(menunum) {
+ case 0:
+ break;
+ case 1:
+ merc_hom_food(sd, sd->hd);
+ break;
+ case 2:
+ merc_hom_delete(sd->hd, -1);
+ break;
+ default:
+ ShowError("merc_menu : unknown menu choice : %d\n", menunum) ;
+ break;
+ }
+ return 0;
+}
+
+int merc_hom_food(struct map_session_data *sd, struct homun_data *hd)
+{
+ int i, foodID, emotion;
+
+ if(hd->homunculus.vaporize)
+ return 1 ;
+
+ foodID = hd->homunculusDB->foodID;
+ i = pc_search_inventory(sd,foodID);
+ if(i < 0) {
+ clif_hom_food(sd,foodID,0);
+ return 1;
+ }
+ pc_delitem(sd,i,1,0);
+
+ if ( hd->homunculus.hunger >= 91 ) {
+ merc_hom_decrease_intimacy(hd, 50);
+ emotion = 16;
+ } else if ( hd->homunculus.hunger >= 76 ) {
+ merc_hom_decrease_intimacy(hd, 5);
+ emotion = 19;
+ } else if ( hd->homunculus.hunger >= 26 ) {
+ merc_hom_increase_intimacy(hd, 75);
+ emotion = 2;
+ } else if ( hd->homunculus.hunger >= 11 ) {
+ merc_hom_increase_intimacy(hd, 100);
+ emotion = 2;
+ } else {
+ merc_hom_increase_intimacy(hd, 50);
+ emotion = 2;
+ }
+
+ hd->homunculus.hunger += 10; //dunno increase value for each food
+ if(hd->homunculus.hunger > 100)
+ hd->homunculus.hunger = 100;
+
+ clif_emotion(&hd->bl,emotion) ;
+ clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
+ clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
+ clif_hom_food(sd,foodID,1);
+
+ // Too much food :/
+ if(hd->homunculus.intimacy == 0)
+ return merc_hom_delete(sd->hd, 23); //omg
+
+ return 0;
+}
+
+static int merc_hom_hungry(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ struct homun_data *hd;
+
+ sd=map_id2sd(id);
+ if(!sd)
+ return 1;
+
+ if(!sd->status.hom_id || !(hd=sd->hd))
+ return 1;
+
+ if(hd->hungry_timer != tid){
+ if(battle_config.error_log)
+ ShowError("merc_hom_hungry_timer %d != %d\n",hd->hungry_timer,tid);
+ return 0;
+ }
+
+ hd->hungry_timer = -1;
+
+ hd->homunculus.hunger-- ;
+ if(hd->homunculus.hunger <= 10) {
+ clif_emotion(&hd->bl, 6) ; //an
+ } else if(hd->homunculus.hunger == 25) {
+ clif_emotion(&hd->bl, 20) ; //hmm
+ } else if(hd->homunculus.hunger == 75) {
+ clif_emotion(&hd->bl, 33) ; //ok
+ }
+
+ if(hd->homunculus.hunger < 0) {
+ hd->homunculus.hunger = 0;
+ // Delete the homunculus if intimacy <= 100
+ if ( !merc_hom_decrease_intimacy(hd, 100) )
+ return merc_hom_delete(hd, 23); //omg
+ clif_send_homdata(sd,SP_INTIMATE,hd->homunculus.intimacy / 100);
+ }
+
+ clif_send_homdata(sd,SP_HUNGRY,hd->homunculus.hunger);
+ hd->hungry_timer = add_timer(tick+hd->homunculusDB->hungryDelay,merc_hom_hungry,sd->bl.id,0); //simple Fix albator
+ return 0;
+}
+
+int merc_hom_hungry_timer_delete(struct homun_data *hd)
+{
+ nullpo_retr(0, hd);
+ if(hd->hungry_timer != -1) {
+ delete_timer(hd->hungry_timer,merc_hom_hungry);
+ hd->hungry_timer = -1;
+ }
+ return 1;
+}
+
+int search_homunculusDB_index(int key,int type)
+{
+ int i;
+
+ for(i=0;i<MAX_HOMUNCULUS_CLASS;i++) {
+ if(homunculus_db[i].class_ <= 0)
+ continue;
+ switch(type) {
+ case HOMUNCULUS_CLASS:
+ if(homunculus_db[i].class_ == key)
+ return i;
+ break;
+ case HOMUNCULUS_FOOD:
+ if(homunculus_db[i].foodID == key)
+ return i;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+}
+
+// Create homunc structure
+int merc_hom_alloc(struct map_session_data *sd, struct s_homunculus *hom)
+{
+ struct homun_data *hd;
+ int i = 0;
+ short x,y;
+
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.hom_id == 0 || sd->hd == 0) || sd->hd->master == sd);
+
+ i = search_homunculusDB_index(hom->class_,HOMUNCULUS_CLASS);
+ if(i < 0) {
+ ShowError("merc_hom_alloc: unknown homunculus class [%d]", hom->class_);
+ sd->status.hom_id = 0;
+ intif_homunculus_requestdelete(hom->hom_id);
+ return 1;
+ }
+ sd->hd = hd = aCalloc(1,sizeof(struct homun_data));
+ hd->bl.subtype = MONS;
+ hd->bl.type = BL_HOM;
+ hd->bl.id = npc_get_new_npc_id();
+
+ hd->master = sd;
+ hd->homunculusDB = &homunculus_db[i];
+ memcpy(&hd->homunculus, hom, sizeof(struct s_homunculus));
+ hd->exp_next = hexptbl[hd->homunculus.level - 1];
+
+ status_set_viewdata(&hd->bl, hd->homunculus.class_);
+ status_change_init(&hd->bl);
+ unit_dataset(&hd->bl);
+ hd->ud.dir = sd->ud.dir;
+
+ // Find a random valid pos around the player
+ hd->bl.m = sd->bl.m;
+ hd->bl.x = sd->bl.x;
+ hd->bl.y = sd->bl.y;
+ x = sd->bl.x + 1;
+ y = sd->bl.y + 1;
+ map_random_dir(&hd->bl, &x, &y);
+ hd->bl.x = x;
+ hd->bl.y = y;
+
+ map_addiddb(&hd->bl);
+ status_calc_homunculus(hd,1);
+
+ hd->hungry_timer = -1;
+ return 0;
+}
+
+void merc_hom_init_timers(struct homun_data * hd)
+{
+ if (hd->hungry_timer == -1)
+ hd->hungry_timer = add_timer(gettick()+hd->homunculusDB->hungryDelay,merc_hom_hungry,hd->master->bl.id,0);
+ hd->regen.state.block = 0; //Restore HP/SP block.
+}
+
+int merc_call_homunculus(struct map_session_data *sd)
+{
+ struct homun_data *hd;
+
+ if (!sd->status.hom_id) //Create a new homun.
+ return merc_create_homunculus_request(sd, HM_CLASS_BASE + rand(0, 7)) ;
+
+ // If homunc not yet loaded, load it
+ if (!sd->hd)
+ return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
+
+ hd = sd->hd;
+
+ if (!hd->homunculus.vaporize)
+ return 0; //Can't use this if homun wasn't vaporized.
+
+ merc_hom_init_timers(hd);
+ hd->homunculus.vaporize = 0;
+ if (hd->bl.prev == NULL)
+ { //Spawn him
+ hd->bl.x = sd->bl.x;
+ hd->bl.y = sd->bl.y;
+ hd->bl.m = sd->bl.m;
+ map_addblock(&hd->bl);
+ clif_spawn(&hd->bl);
+ clif_send_homdata(sd,SP_ACK,0);
+ clif_hominfo(sd,hd,1);
+ clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+ clif_homskillinfoblock(sd);
+ merc_save(hd);
+ } else
+ //Warp him to master.
+ unit_warp(&hd->bl,sd->bl.m, sd->bl.x, sd->bl.y,0);
+ return 1;
+}
+
+// Recv homunculus data from char server
+int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag)
+{
+ struct map_session_data *sd;
+ struct homun_data *hd;
+
+ sd = map_id2sd(account_id);
+ if(!sd)
+ return 0;
+ if (sd->status.char_id != sh->char_id)
+ {
+ if (sd->status.hom_id == sh->hom_id)
+ sh->char_id = sd->status.char_id; //Correct char id.
+ else
+ return 0;
+ }
+ if(!flag) { // Failed to load
+ sd->status.hom_id = 0;
+ return 0;
+ }
+
+ if (!sd->status.hom_id) //Hom just created.
+ sd->status.hom_id = sh->hom_id;
+ if (sd->hd) //uh? Overwrite the data.
+ memcpy(&sd->hd->homunculus, sh, sizeof(struct s_homunculus));
+ else
+ merc_hom_alloc(sd, sh);
+
+ hd = sd->hd;
+ if(hd->homunculus.hp && !hd->homunculus.vaporize &&
+ hd->bl.prev == NULL && sd->bl.prev != NULL)
+ {
+ map_addblock(&hd->bl);
+ clif_spawn(&hd->bl);
+ clif_hominfo(sd,hd,1);
+ clif_hominfo(sd,hd,0); // send this x2. dunno why, but kRO does that [blackhole89]
+ clif_homskillinfoblock(sd);
+ clif_hominfo(sd,hd,0);
+ clif_send_homdata(sd,SP_ACK,0);
+ merc_hom_init_timers(hd);
+ }
+ return 1;
+}
+
+// Ask homunculus creation to char server
+int merc_create_homunculus_request(struct map_session_data *sd, int class_)
+{
+ struct s_homunculus homun;
+ int i;
+
+ nullpo_retr(1, sd);
+
+ i = search_homunculusDB_index(class_,HOMUNCULUS_CLASS);
+ if(i < 0) return 0;
+
+ memset(&homun, 0, sizeof(struct s_homunculus));
+ //Initial data
+ strncpy(homun.name, homunculus_db[i].name, NAME_LENGTH-1);
+ homun.class_ = class_;
+ homun.level = 1;
+// FIXME: Commented value is what the map-server had as initial value,
+// Uncommented value is what the char-server was overwriting it with
+// So which one is correct?
+// homun.hunger = 50;
+ homun.hunger = 32;
+// homun.intimacy = 500;
+ homun.intimacy = 21;
+ homun.char_id = sd->status.char_id;
+
+ homun.hp = 10 ;
+ homun.max_hp = homunculus_db[i].basemaxHP;
+ homun.max_sp = homunculus_db[i].basemaxSP;
+ homun.str = homunculus_db[i].baseSTR * 10;
+ homun.agi = homunculus_db[i].baseAGI * 10;
+ homun.vit = homunculus_db[i].baseVIT * 10;
+ homun.int_ = homunculus_db[i].baseINT * 10;
+ homun.dex = homunculus_db[i].baseDEX * 10;
+ homun.luk = homunculus_db[i].baseLUK * 10;
+
+ // Request homunculus creation
+ intif_homunculus_create(sd->status.account_id, &homun);
+ return 1;
+}
+
+int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y)
+{
+ struct homun_data *hd;
+ nullpo_retr(0, sd);
+ if (!sd->status.hom_id)
+ return 0;
+
+ if (!sd->hd) //Load homun data;
+ return intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
+
+ hd = sd->hd;
+
+ if (hd->homunculus.vaporize)
+ return 0;
+
+ if (!status_isdead(&hd->bl))
+ return 0;
+
+ merc_hom_init_timers(hd);
+
+ if (!hd->bl.prev)
+ { //Add it back to the map.
+ hd->bl.m = sd->bl.m;
+ hd->bl.x = x;
+ hd->bl.y = y;
+ map_addblock(&hd->bl);
+ clif_spawn(&hd->bl);
+ }
+ status_revive(&hd->bl, per, 0);
+ return 1;
+}
+
+void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp)
+{
+ struct map_session_data *sd = hd->master;
+ hd->homunculus.hp = hd->battle_status.hp;
+ if (!sd)
+ return;
+ clif_send_homdata(sd,SP_ACK,0);
+ clif_hominfo(sd,hd,1);
+ clif_hominfo(sd,hd,0);
+ clif_homskillinfoblock(sd);
+}
+
+int read_homunculusdb(void)
+{
+ FILE *fp;
+ char line[1024], *p;
+ int i, k, classid;
+ int j = 0;
+ char *filename[]={"homunculus_db.txt","homunculus_db2.txt"};
+ char *str[36];
+
+ malloc_set(homunculus_db,0,sizeof(homunculus_db));
+ for(i = 0; i<2; i++)
+ {
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp = fopen(line,"r");
+ if(!fp){
+ if(i != 0)
+ continue;
+ ShowError("read_homunculusdb : can't read %s\n", line);
+ return -1;
+ }
+
+ while(fgets(line,sizeof(line)-1,fp) && j < MAX_HOMUNCULUS_CLASS)
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ k = 0;
+ p = strtok (line,",");
+ while (p != NULL && k < 36)
+ {
+ str[k++] = p;
+ p = strtok (NULL, ",");
+ }
+
+ classid = atoi(str[0]);
+ if (k != 36 || classid < HM_CLASS_BASE || classid > HM_CLASS_MAX)
+ {
+ ShowError("read_homunculusdb : Error reading %s", filename[i]);
+ continue;
+ }
+
+ //Class,Homunculus,HP,SP,ATK,MATK,HIT,CRI,DEF,MDEF,FLEE,ASPD,STR,AGI,VIT,INT,DEX,LUK
+ homunculus_db[j].class_ = classid;
+ strncpy(homunculus_db[j].name,str[1],NAME_LENGTH-1);
+ homunculus_db[j].basemaxHP = atoi(str[2]);
+ homunculus_db[j].basemaxSP = atoi(str[3]);
+ homunculus_db[j].baseSTR = atoi(str[4]);
+ homunculus_db[j].baseAGI = atoi(str[5]);
+ homunculus_db[j].baseVIT = atoi(str[6]);
+ homunculus_db[j].baseINT = atoi(str[7]);
+ homunculus_db[j].baseDEX = atoi(str[8]);
+ homunculus_db[j].baseLUK = atoi(str[9]);
+ homunculus_db[j].baseIntimacy = atoi(str[10]);
+ homunculus_db[j].baseHungry = atoi(str[11]);
+ homunculus_db[j].hungryDelay = atoi(str[12]);
+ homunculus_db[j].foodID = atoi(str[13]);
+ homunculus_db[j].gminHP = atoi(str[14]);
+ homunculus_db[j].gmaxHP = atoi(str[15]);
+ homunculus_db[j].gminSP = atoi(str[16]);
+ homunculus_db[j].gmaxSP = atoi(str[17]);
+ homunculus_db[j].gminSTR = atoi(str[18]);
+ homunculus_db[j].gmaxSTR = atoi(str[19]);
+ homunculus_db[j].gminAGI = atoi(str[20]);
+ homunculus_db[j].gmaxAGI = atoi(str[21]);
+ homunculus_db[j].gminVIT = atoi(str[22]);
+ homunculus_db[j].gmaxVIT = atoi(str[23]);
+ homunculus_db[j].gminINT = atoi(str[24]);
+ homunculus_db[j].gmaxINT = atoi(str[25]);
+ homunculus_db[j].gminDEX = atoi(str[26]);
+ homunculus_db[j].gmaxDEX = atoi(str[27]);
+ homunculus_db[j].gminLUK = atoi(str[28]);
+ homunculus_db[j].gmaxLUK = atoi(str[29]);
+ homunculus_db[j].evo_class = atoi(str[30]);
+ homunculus_db[j].baseASPD = atoi(str[31]);
+ homunculus_db[j].size = atoi(str[32]);
+ homunculus_db[j].race = atoi(str[33]);
+ homunculus_db[j].element = atoi(str[34]);
+ homunculus_db[j].accessID = atoi(str[35]);
+ j++;
+ }
+ if (j > MAX_HOMUNCULUS_CLASS)
+ ShowWarning("read_homunculusdb: Reached max number of homunculus [%d]. Remaining homunculus were not read.\n ", MAX_HOMUNCULUS_CLASS);
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' homunculus in '"CL_WHITE"db/%s"CL_RESET"'.\n",j,filename[i]);
+ }
+ return 0;
+}
+
+int read_homunculus_skilldb(void)
+{
+ FILE *fp;
+ char line[1024], *p;
+ int k, classid;
+ int j = 0;
+ char *split[15];
+
+ malloc_tsetdword(hskill_tree,0,sizeof(hskill_tree));
+ sprintf(line, "%s/homun_skill_tree.txt", db_path);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ ShowError("can't read %s\n", line);
+ return 1;
+ }
+
+ while(fgets(line, sizeof(line)-1, fp))
+ {
+ int minJobLevelPresent = 0;
+
+ if(line[0]=='/' && line[1]=='/')
+ continue;
+
+ k = 0;
+ p = strtok(line,",");
+ while (p != NULL && k < 15)
+ {
+ split[k++] = p;
+ p = strtok(NULL, ",");
+ }
+
+ if(k < 13)
+ continue;
+
+ if (k == 14)
+ minJobLevelPresent = 1; // MinJobLvl has been added
+
+ // check for bounds [celest]
+ classid = atoi(split[0]) - HM_CLASS_BASE;
+ if ( classid >= MAX_HOMUNCULUS_CLASS )
+ continue;
+
+ k = atoi(split[1]); //This is to avoid adding two lines for the same skill. [Skotlex]
+ // Search an empty line or a line with the same skill_id (stored in j)
+ for(j = 0; j < MAX_SKILL_TREE && hskill_tree[classid][j].id && hskill_tree[classid][j].id != k; j++);
+
+ if (j == MAX_SKILL_TREE)
+ {
+ ShowWarning("Unable to load skill %d into homunculus %d's tree. Maximum number of skills per class has been reached.\n", k, classid);
+ continue;
+ }
+
+ hskill_tree[classid][j].id=k;
+ hskill_tree[classid][j].max=atoi(split[2]);
+ if (minJobLevelPresent)
+ hskill_tree[classid][j].joblv=atoi(split[3]);
+
+ for(k=0;k<5;k++){
+ hskill_tree[classid][j].need[k].id=atoi(split[3+k*2+minJobLevelPresent]);
+ hskill_tree[classid][j].need[k].lv=atoi(split[3+k*2+minJobLevelPresent+1]);
+ }
+ }
+
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","homun_skill_tree.txt");
+ return 0;
+}
+
+void read_homunculus_expdb(void)
+{
+ FILE *fp;
+ char line[1024];
+ int i, j=0;
+ char *filename[]={"exp_homun.txt","exp_homun2.txt"};
+
+ malloc_tsetdword(hexptbl,0,sizeof(hexptbl));
+ for(i=0; i<2; i++){
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp=fopen(line,"r");
+ if(fp == NULL){
+ if(i != 0)
+ continue;
+ ShowError("can't read %s\n",line);
+ return;
+ }
+ while(fgets(line,sizeof(line)-1,fp) && j < MAX_LEVEL)
+ {
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ hexptbl[j] = strtoul(line, NULL, 10);
+ if (!hexptbl[j++])
+ break;
+ }
+ if (hexptbl[MAX_LEVEL - 1]) // Last permitted level have to be 0!
+ {
+ ShowWarning("read_hexptbl: Reached max level in exp_homun [%d]. Remaining lines were not read.\n ", MAX_LEVEL);
+ hexptbl[MAX_LEVEL - 1] = 0;
+ }
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' levels in '"CL_WHITE"%s"CL_RESET"'.\n", j, filename[i]);
+ }
+}
+
+void merc_reload(void)
+{
+ read_homunculusdb();
+ read_homunculus_expdb();
+}
+
+void merc_skill_reload(void)
+{
+ read_homunculus_skilldb();
+}
+
+int do_init_merc(void)
+{
+ read_homunculusdb();
+ read_homunculus_expdb();
+ read_homunculus_skilldb();
+ // Add homunc timer function to timer func list [Toms]
+ add_timer_func_list(merc_hom_hungry, "merc_hom_hungry");
+ return 0;
+}
+
+int do_final_merc(void);
diff --git a/src/map/mercenary.h b/src/map/mercenary.h
index 73f791c68..e5ba86cfe 100644
--- a/src/map/mercenary.h
+++ b/src/map/mercenary.h
@@ -1,79 +1,79 @@
-// Homunculus and future Mercenary system code go here [Celest]
-// implemented by [orn]
-struct homunculus_db {
- int class_ ;
- char name[NAME_LENGTH];
- int basemaxHP ;
- int basemaxSP ;
- int baseSTR ;
- int baseAGI ;
- int baseVIT ;
- int baseINT ;
- int baseDEX ;
- int baseLUK ;
- int foodID ;
- int baseIntimacy ;
- short baseHungry ;
- long hungryDelay ;
- int gminHP ;
- int gmaxHP ;
- int gminSP ;
- int gmaxSP ;
- int gminSTR ;
- int gmaxSTR ;
- int gminAGI ;
- int gmaxAGI ;
- int gminVIT ;
- int gmaxVIT ;
- int gminINT ;
- int gmaxINT ;
- int gminDEX ;
- int gmaxDEX ;
- int gminLUK ;
- int gmaxLUK ;
- int evo_class ;
- int baseASPD ;
- unsigned char element, race, size;
- int accessID ;
-};
-extern struct homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS];
-enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD };
-enum {
- SP_ACK = 0x00,
- SP_INTIMATE = 0x100,
- SP_HUNGRY = 0x200
-};
-// merc_is_hom_alive(struct homun_data *)
-#define merc_is_hom_active(x) (x && x->homunculus.vaporize != 1 && x->battle_status.hp > 0)
-int do_init_merc(void);
-int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator
-void merc_load_sub(struct homun_data *hd, struct map_session_data *sd);
-void merc_load_exptables(void);
-char *merc_hom_skill_get_name(int id);
-void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp);
-int merc_hom_dead(struct homun_data *hd, struct block_list *src);
-void merc_hom_skillup(struct homun_data *hd,int skillnum);
-int merc_hom_calc_skilltree(struct homun_data *hd) ;
-int merc_hom_checkskill(struct homun_data *hd,int skill_id) ;
-int merc_hom_gainexp(struct homun_data *hd,int exp) ;
-int merc_hom_levelup(struct homun_data *hd) ;
-int merc_hom_evolution(struct homun_data *hd) ;
-void merc_hom_heal(struct homun_data *hd,int hp,int sp);
-int merc_hom_vaporize(struct map_session_data *sd, int flag);
-int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y);
-void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp);
-void merc_save(struct homun_data *hd);
-int merc_call_homunculus(struct map_session_data *sd);
-int merc_create_homunculus_request(struct map_session_data *sd, int class_);
-int search_homunculusDB_index(int key,int type);
-int merc_menu(struct map_session_data *sd,int menunum);
-int merc_hom_food(struct map_session_data *sd, struct homun_data *hd);
-int merc_hom_hungry_timer_delete(struct homun_data *hd);
-#define merc_stop_walking(hd, type) { if((hd)->ud.walktimer != -1) unit_stop_walking(&(hd)->bl, type); }
-#define merc_stop_attack(hd) { if((hd)->ud.attacktimer != -1) unit_stop_attack(&(hd)->bl); hd->ud.target = 0; }
-int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value);
-int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value);
-int merc_skill_tree_get_max(int id, int b_class);
-void merc_hom_init_timers(struct homun_data * hd);
-void merc_skill_reload(void);
-void merc_reload(void);
+// Homunculus and future Mercenary system code go here [Celest]
+// implemented by [orn]
+struct homunculus_db {
+ int class_ ;
+ char name[NAME_LENGTH];
+ int basemaxHP ;
+ int basemaxSP ;
+ int baseSTR ;
+ int baseAGI ;
+ int baseVIT ;
+ int baseINT ;
+ int baseDEX ;
+ int baseLUK ;
+ int foodID ;
+ int baseIntimacy ;
+ short baseHungry ;
+ long hungryDelay ;
+ int gminHP ;
+ int gmaxHP ;
+ int gminSP ;
+ int gmaxSP ;
+ int gminSTR ;
+ int gmaxSTR ;
+ int gminAGI ;
+ int gmaxAGI ;
+ int gminVIT ;
+ int gmaxVIT ;
+ int gminINT ;
+ int gmaxINT ;
+ int gminDEX ;
+ int gmaxDEX ;
+ int gminLUK ;
+ int gmaxLUK ;
+ int evo_class ;
+ int baseASPD ;
+ unsigned char element, race, size;
+ int accessID ;
+};
+extern struct homunculus_db homuncumlus_db[MAX_HOMUNCULUS_CLASS];
+enum { HOMUNCULUS_CLASS, HOMUNCULUS_FOOD };
+enum {
+ SP_ACK = 0x00,
+ SP_INTIMATE = 0x100,
+ SP_HUNGRY = 0x200
+};
+// merc_is_hom_alive(struct homun_data *)
+#define merc_is_hom_active(x) (x && x->homunculus.vaporize != 1 && x->battle_status.hp > 0)
+int do_init_merc(void);
+int merc_hom_recv_data(int account_id, struct s_homunculus *sh, int flag); //albator
+void merc_load_sub(struct homun_data *hd, struct map_session_data *sd);
+void merc_load_exptables(void);
+char *merc_hom_skill_get_name(int id);
+void merc_damage(struct homun_data *hd,struct block_list *src,int hp,int sp);
+int merc_hom_dead(struct homun_data *hd, struct block_list *src);
+void merc_hom_skillup(struct homun_data *hd,int skillnum);
+int merc_hom_calc_skilltree(struct homun_data *hd) ;
+int merc_hom_checkskill(struct homun_data *hd,int skill_id) ;
+int merc_hom_gainexp(struct homun_data *hd,int exp) ;
+int merc_hom_levelup(struct homun_data *hd) ;
+int merc_hom_evolution(struct homun_data *hd) ;
+void merc_hom_heal(struct homun_data *hd,int hp,int sp);
+int merc_hom_vaporize(struct map_session_data *sd, int flag);
+int merc_resurrect_homunculus(struct map_session_data *sd, unsigned char per, short x, short y);
+void merc_hom_revive(struct homun_data *hd, unsigned int hp, unsigned int sp);
+void merc_save(struct homun_data *hd);
+int merc_call_homunculus(struct map_session_data *sd);
+int merc_create_homunculus_request(struct map_session_data *sd, int class_);
+int search_homunculusDB_index(int key,int type);
+int merc_menu(struct map_session_data *sd,int menunum);
+int merc_hom_food(struct map_session_data *sd, struct homun_data *hd);
+int merc_hom_hungry_timer_delete(struct homun_data *hd);
+#define merc_stop_walking(hd, type) { if((hd)->ud.walktimer != -1) unit_stop_walking(&(hd)->bl, type); }
+#define merc_stop_attack(hd) { if((hd)->ud.attacktimer != -1) unit_stop_attack(&(hd)->bl); hd->ud.target = 0; }
+int merc_hom_increase_intimacy(struct homun_data * hd, unsigned int value);
+int merc_hom_decrease_intimacy(struct homun_data * hd, unsigned int value);
+int merc_skill_tree_get_max(int id, int b_class);
+void merc_hom_init_timers(struct homun_data * hd);
+void merc_skill_reload(void);
+void merc_reload(void);
diff --git a/src/map/mob.h b/src/map/mob.h
index 7dedf948b..03d35b076 100644
--- a/src/map/mob.h
+++ b/src/map/mob.h
@@ -1,207 +1,207 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _MOB_H_
-#define _MOB_H_
-
-#include "unit.h"
-#include "map.h"
-
-#define MAX_RANDOMMONSTER 3
-#define MAX_MOB_RACE_DB 6
- /* Change this to increase the table size in your mob_db to accomodate
- a larger mob database. Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes.
- */
-#define MAX_MOB_DB 10000
-
-//The number of drops all mobs have and the max drop-slot that the steal skill
-//will attempt to steal from.
-#define MAX_MOB_DROP 10
-#define MAX_STEAL_DROP 7
-
-//Min time before mobs do a check to call nearby friends for help (or for slaves to support their master)
-#define MIN_MOBLINKTIME 1000
-
-//Distance that slaves should keep from their master.
-#define MOB_SLAVEDISTANCE 2
-
-// These define the range of available IDs for clones. [Valaris]
-#define MOB_CLONE_START 9001
-#define MOB_CLONE_END 10000
-
-// Scripted Mob AI Constants
-#define CALLBACK_NPCCLICK 0x100
-#define CALLBACK_ATTACK 0x80
-#define CALLBACK_DETECT 0x40
-#define CALLBACK_DEAD 0x20
-#define CALLBACK_ASSIST 0x10
-#define CALLBACK_KILL 0x08
-#define CALLBACK_UNLOCK 0x04
-#define CALLBACK_WALKACK 0x02
-#define CALLBACK_WARPACK 0x01
-
-int mob_script_callback(struct mob_data *md, struct block_list *target, short action_type);
-
-struct mob_skill {
- short state;
- short skill_id,skill_lv;
- short permillage;
- int casttime,delay;
- short cancel;
- short cond1,cond2;
- short target;
- int val[5];
- short emotion;
-};
-
-struct mob_db {
- char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH];
- unsigned int base_exp,job_exp;
- unsigned int mexp,mexpper;
- unsigned int min_thinktime; //Min think time, Recharge Time as aegis calls it.
- int range2,range3;
- short race2; // celest
- unsigned short lv;
- struct { int nameid,p; } dropitem[MAX_MOB_DROP];
- struct { int nameid,p; } mvpitem[3];
- struct status_data status;
- struct view_data vd;
- short option;
- int summonper[MAX_RANDOMMONSTER];
- int maxskill;
- struct mob_skill skill[MAX_MOBSKILL];
-};
-
-enum {
- MST_TARGET = 0,
- MST_SELF,
- MST_FRIEND,
- MST_MASTER,
- MST_AROUND5,
- MST_AROUND6,
- MST_AROUND7,
- MST_AROUND8,
- MST_AROUND1,
- MST_AROUND2,
- MST_AROUND3,
- MST_AROUND4,
- MST_AROUND = MST_AROUND4,
-
- MSC_ALWAYS = 0x0000,
- MSC_MYHPLTMAXRATE,
- MSC_MYHPINRATE,
- MSC_FRIENDHPLTMAXRATE,
- MSC_FRIENDHPINRATE,
- MSC_MYSTATUSON,
- MSC_MYSTATUSOFF,
- MSC_FRIENDSTATUSON,
- MSC_FRIENDSTATUSOFF,
- MSC_ATTACKPCGT,
- MSC_ATTACKPCGE,
- MSC_SLAVELT,
- MSC_SLAVELE,
- MSC_CLOSEDATTACKED,
- MSC_LONGRANGEATTACKED,
- MSC_AFTERSKILL,
- MSC_SKILLUSED ,
- MSC_CASTTARGETED,
- MSC_RUDEATTACKED,
- MSC_MASTERHPLTMAXRATE,
- MSC_MASTERATTACKED,
- MSC_ALCHEMIST,
- MSC_SPAWN,
-};
-
-//Mob skill states.
-enum {
- MSS_ANY = -1,
- MSS_IDLE,
- MSS_WALK,
- MSS_LOOT,
- MSS_DEAD,
- MSS_BERSERK, //Aggressive mob attacking
- MSS_ANGRY, //Mob retaliating from being attacked.
- MSS_RUSH, //Mob following a player after being attacked.
- MSS_FOLLOW, //Mob following a player without being attacked.
- MSS_ANYTARGET,
-};
-
-
-/*==========================================
- * The structure object for item drop with delay
- * Since it is only two being able to pass [ int ] a timer function
- * Data is put in and passed to this structure object.
- *------------------------------------------
- */
-struct item_drop {
- struct item item_data;
- struct item_drop *next;
-};
-
-struct item_drop_list {
- int m,x,y;
- struct map_session_data *first_sd,*second_sd,*third_sd;
- struct item_drop *item;
-};
-
-struct mob_db* mob_db(int class_);
-int mobdb_searchname(const char *str);
-int mobdb_searchname_array(struct mob_db** data, int size, const char *str);
-int mobdb_checkid(const int id);
-struct view_data* mob_get_viewdata(int class_);
-struct mob_data *mob_once_spawn_sub(struct block_list *bl, int m,
- short x, short y, const char *mobname, int class_, const char *event);
-int mob_once_spawn(struct map_session_data *sd,char *mapname,
- short x,short y,const char *mobname,int class_,int amount,const char *event);
-int mob_once_spawn_area(struct map_session_data *sd,char *mapname,
- int x0,int y0,int x1,int y1,
- const char *mobname,int class_,int amount,const char *event);
-
-int mob_spawn_guardian(struct map_session_data *sd,char *mapname, // Spawning Guardians [Valaris]
- int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian); // Spawning Guardians [Valaris]
-int mob_guardian_guildchange(struct block_list *bl,va_list ap); //Change Guardian's ownership. [Skotlex]
-
-int mob_randomwalk(struct mob_data *md,int tick);
-
-int mob_target(struct mob_data *md,struct block_list *bl,int dist);
-int mob_unlocktarget(struct mob_data *md,int tick);
-struct mob_data* mob_spawn_dataset(struct spawn_data *data);
-int mob_spawn(struct mob_data *md);
-int mob_setdelayspawn(struct mob_data *md);
-int mob_parse_dataset(struct spawn_data *data);
-void mob_damage(struct mob_data *md, struct block_list *src, int damage);
-int mob_dead(struct mob_data *md, struct block_list *src, int type);
-void mob_revive(struct mob_data *md, unsigned int hp);
-void mob_heal(struct mob_data *md,unsigned int heal);
-
-#define mob_stop_walking(md, type) { if (md->ud.walktimer != -1) unit_stop_walking(&md->bl, type); }
-#define mob_stop_attack(md) { if (md->ud.attacktimer != -1) unit_stop_attack(&md->bl); }
-
-int do_init_mob(void);
-int do_final_mob(void);
-
-int mob_timer_delete(int tid, unsigned int tick, int id, int data);
-int mob_deleteslave(struct mob_data *md);
-
-int mob_random_class (int *value, size_t count);
-int mob_get_random_id(int type, int flag, int lv);
-int mob_class_change(struct mob_data *md,int class_);
-int mob_warpslave(struct block_list *bl, int range);
-int mob_linksearch(struct block_list *bl,va_list ap);
-
-int mobskill_use(struct mob_data *md,unsigned int tick,int event);
-int mobskill_event(struct mob_data *md,struct block_list *src,unsigned int tick, int flag);
-int mobskill_castend_id( int tid, unsigned int tick, int id,int data );
-int mobskill_castend_pos( int tid, unsigned int tick, int id,int data );
-int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id);
-int mob_countslave(struct block_list *bl);
-int mob_convertslave(struct mob_data *md);
-
-int mob_is_clone(int class_);
-
-int mob_clone_spawn(struct map_session_data *sd, int m, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration);
-int mob_clone_delete(int class_);
-
-void mob_reload(void);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _MOB_H_
+#define _MOB_H_
+
+#include "unit.h"
+#include "map.h"
+
+#define MAX_RANDOMMONSTER 3
+#define MAX_MOB_RACE_DB 6
+ /* Change this to increase the table size in your mob_db to accomodate
+ a larger mob database. Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes.
+ */
+#define MAX_MOB_DB 10000
+
+//The number of drops all mobs have and the max drop-slot that the steal skill
+//will attempt to steal from.
+#define MAX_MOB_DROP 10
+#define MAX_STEAL_DROP 7
+
+//Min time before mobs do a check to call nearby friends for help (or for slaves to support their master)
+#define MIN_MOBLINKTIME 1000
+
+//Distance that slaves should keep from their master.
+#define MOB_SLAVEDISTANCE 2
+
+// These define the range of available IDs for clones. [Valaris]
+#define MOB_CLONE_START 9001
+#define MOB_CLONE_END 10000
+
+// Scripted Mob AI Constants
+#define CALLBACK_NPCCLICK 0x100
+#define CALLBACK_ATTACK 0x80
+#define CALLBACK_DETECT 0x40
+#define CALLBACK_DEAD 0x20
+#define CALLBACK_ASSIST 0x10
+#define CALLBACK_KILL 0x08
+#define CALLBACK_UNLOCK 0x04
+#define CALLBACK_WALKACK 0x02
+#define CALLBACK_WARPACK 0x01
+
+int mob_script_callback(struct mob_data *md, struct block_list *target, short action_type);
+
+struct mob_skill {
+ short state;
+ short skill_id,skill_lv;
+ short permillage;
+ int casttime,delay;
+ short cancel;
+ short cond1,cond2;
+ short target;
+ int val[5];
+ short emotion;
+};
+
+struct mob_db {
+ char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH];
+ unsigned int base_exp,job_exp;
+ unsigned int mexp,mexpper;
+ unsigned int min_thinktime; //Min think time, Recharge Time as aegis calls it.
+ int range2,range3;
+ short race2; // celest
+ unsigned short lv;
+ struct { int nameid,p; } dropitem[MAX_MOB_DROP];
+ struct { int nameid,p; } mvpitem[3];
+ struct status_data status;
+ struct view_data vd;
+ short option;
+ int summonper[MAX_RANDOMMONSTER];
+ int maxskill;
+ struct mob_skill skill[MAX_MOBSKILL];
+};
+
+enum {
+ MST_TARGET = 0,
+ MST_SELF,
+ MST_FRIEND,
+ MST_MASTER,
+ MST_AROUND5,
+ MST_AROUND6,
+ MST_AROUND7,
+ MST_AROUND8,
+ MST_AROUND1,
+ MST_AROUND2,
+ MST_AROUND3,
+ MST_AROUND4,
+ MST_AROUND = MST_AROUND4,
+
+ MSC_ALWAYS = 0x0000,
+ MSC_MYHPLTMAXRATE,
+ MSC_MYHPINRATE,
+ MSC_FRIENDHPLTMAXRATE,
+ MSC_FRIENDHPINRATE,
+ MSC_MYSTATUSON,
+ MSC_MYSTATUSOFF,
+ MSC_FRIENDSTATUSON,
+ MSC_FRIENDSTATUSOFF,
+ MSC_ATTACKPCGT,
+ MSC_ATTACKPCGE,
+ MSC_SLAVELT,
+ MSC_SLAVELE,
+ MSC_CLOSEDATTACKED,
+ MSC_LONGRANGEATTACKED,
+ MSC_AFTERSKILL,
+ MSC_SKILLUSED ,
+ MSC_CASTTARGETED,
+ MSC_RUDEATTACKED,
+ MSC_MASTERHPLTMAXRATE,
+ MSC_MASTERATTACKED,
+ MSC_ALCHEMIST,
+ MSC_SPAWN,
+};
+
+//Mob skill states.
+enum {
+ MSS_ANY = -1,
+ MSS_IDLE,
+ MSS_WALK,
+ MSS_LOOT,
+ MSS_DEAD,
+ MSS_BERSERK, //Aggressive mob attacking
+ MSS_ANGRY, //Mob retaliating from being attacked.
+ MSS_RUSH, //Mob following a player after being attacked.
+ MSS_FOLLOW, //Mob following a player without being attacked.
+ MSS_ANYTARGET,
+};
+
+
+/*==========================================
+ * The structure object for item drop with delay
+ * Since it is only two being able to pass [ int ] a timer function
+ * Data is put in and passed to this structure object.
+ *------------------------------------------
+ */
+struct item_drop {
+ struct item item_data;
+ struct item_drop *next;
+};
+
+struct item_drop_list {
+ int m,x,y;
+ struct map_session_data *first_sd,*second_sd,*third_sd;
+ struct item_drop *item;
+};
+
+struct mob_db* mob_db(int class_);
+int mobdb_searchname(const char *str);
+int mobdb_searchname_array(struct mob_db** data, int size, const char *str);
+int mobdb_checkid(const int id);
+struct view_data* mob_get_viewdata(int class_);
+struct mob_data *mob_once_spawn_sub(struct block_list *bl, int m,
+ short x, short y, const char *mobname, int class_, const char *event);
+int mob_once_spawn(struct map_session_data *sd,char *mapname,
+ short x,short y,const char *mobname,int class_,int amount,const char *event);
+int mob_once_spawn_area(struct map_session_data *sd,char *mapname,
+ int x0,int y0,int x1,int y1,
+ const char *mobname,int class_,int amount,const char *event);
+
+int mob_spawn_guardian(struct map_session_data *sd,char *mapname, // Spawning Guardians [Valaris]
+ int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian); // Spawning Guardians [Valaris]
+int mob_guardian_guildchange(struct block_list *bl,va_list ap); //Change Guardian's ownership. [Skotlex]
+
+int mob_randomwalk(struct mob_data *md,int tick);
+
+int mob_target(struct mob_data *md,struct block_list *bl,int dist);
+int mob_unlocktarget(struct mob_data *md,int tick);
+struct mob_data* mob_spawn_dataset(struct spawn_data *data);
+int mob_spawn(struct mob_data *md);
+int mob_setdelayspawn(struct mob_data *md);
+int mob_parse_dataset(struct spawn_data *data);
+void mob_damage(struct mob_data *md, struct block_list *src, int damage);
+int mob_dead(struct mob_data *md, struct block_list *src, int type);
+void mob_revive(struct mob_data *md, unsigned int hp);
+void mob_heal(struct mob_data *md,unsigned int heal);
+
+#define mob_stop_walking(md, type) { if (md->ud.walktimer != -1) unit_stop_walking(&md->bl, type); }
+#define mob_stop_attack(md) { if (md->ud.attacktimer != -1) unit_stop_attack(&md->bl); }
+
+int do_init_mob(void);
+int do_final_mob(void);
+
+int mob_timer_delete(int tid, unsigned int tick, int id, int data);
+int mob_deleteslave(struct mob_data *md);
+
+int mob_random_class (int *value, size_t count);
+int mob_get_random_id(int type, int flag, int lv);
+int mob_class_change(struct mob_data *md,int class_);
+int mob_warpslave(struct block_list *bl, int range);
+int mob_linksearch(struct block_list *bl,va_list ap);
+
+int mobskill_use(struct mob_data *md,unsigned int tick,int event);
+int mobskill_event(struct mob_data *md,struct block_list *src,unsigned int tick, int flag);
+int mobskill_castend_id( int tid, unsigned int tick, int id,int data );
+int mobskill_castend_pos( int tid, unsigned int tick, int id,int data );
+int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id);
+int mob_countslave(struct block_list *bl);
+int mob_convertslave(struct mob_data *md);
+
+int mob_is_clone(int class_);
+
+int mob_clone_spawn(struct map_session_data *sd, int m, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration);
+int mob_clone_delete(int class_);
+
+void mob_reload(void);
+
+#endif
diff --git a/src/map/npc.c b/src/map/npc.c
index fbedef5da..46fd4c4ab 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -1,3084 +1,3084 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-#include <limits.h>
-
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/grfio.h"
-#include "../common/showmsg.h"
-#include "../common/ers.h"
-#include "../common/db.h"
-#include "map.h"
-#include "log.h"
-#include "npc.h"
-#include "clif.h"
-#include "intif.h"
-#include "pc.h"
-#include "status.h"
-#include "itemdb.h"
-#include "script.h"
-#include "mob.h"
-#include "pet.h"
-#include "battle.h"
-#include "skill.h"
-#include "unit.h"
-
-#ifdef _WIN32
-#undef isspace
-#define isspace(x) (x == ' ' || x == '\t')
-#endif
-
-struct npc_src_list {
- struct npc_src_list * next;
-// struct npc_src_list * prev; //[Shinomori]
- char name[4];
-};
-
-static struct npc_src_list *npc_src_first=NULL;
-static struct npc_src_list *npc_src_last=NULL;
-static int npc_id=START_NPC_NUM;
-static int npc_warp=0;
-static int npc_shop=0;
-static int npc_script=0;
-static int npc_mob=0;
-static int npc_delay_mob=0;
-static int npc_cache_mob=0;
-char *current_file = NULL;
-int npc_get_new_npc_id(void){ return npc_id++; }
-
-static struct dbt *ev_db;
-static struct dbt *npcname_db;
-
-struct event_data {
- struct npc_data *nd;
- int pos;
-};
-static struct tm ev_tm_b; // 時計イベント用
-
-static struct eri *timer_event_ers; //For the npc timer data. [Skotlex]
-
-//For holding the view data of npc classes. [Skotlex]
-static struct view_data npc_viewdb[MAX_NPC_CLASS];
-
-static struct
-{ //Holds pointers to the commonly executed scripts for speedup. [Skotlex]
- struct npc_data *nd;
- struct event_data *event[UCHAR_MAX];
- unsigned char *event_name[UCHAR_MAX];
- unsigned char event_count;
-} script_event[NPCE_MAX];
-
-struct view_data* npc_get_viewdata(int class_)
-{ //Returns the viewdata for normal npc classes.
- if (class_ == INVISIBLE_CLASS)
- return &npc_viewdb[0];
- if (npcdb_checkid(class_) || class_ == WARP_CLASS)
- return &npc_viewdb[class_];
- return NULL;
-}
-/*==========================================
- * NPCの無効化/有効化
- * npc_enable
- * npc_enable_sub 有効時にOnTouchイベントを実行
- *------------------------------------------
- */
-int npc_enable_sub( struct block_list *bl, va_list ap )
-{
- struct map_session_data *sd;
- struct npc_data *nd;
- //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
-
- nullpo_retr(0, bl);
- nullpo_retr(0, ap);
- nullpo_retr(0, nd=va_arg(ap,struct npc_data *));
- if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){
- char name[50]; // need 24 + 9 for the "::OnTouch"
-
- if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている
- return 1;
-
- if(sd->areanpc_id==nd->bl.id)
- return 1;
- sd->areanpc_id=nd->bl.id;
-
- snprintf(name, 50, "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance]
- npc_event(sd,name,0);
- }
- //aFree(name);
- return 0;
-}
-int npc_enable(const char *name,int flag)
-{
- struct npc_data *nd= strdb_get(npcname_db,(unsigned char*)name);
- if (nd==NULL)
- return 0;
-
- if (flag&1)
- nd->sc.option&=~OPTION_INVISIBLE;
- else if (flag&2)
- nd->sc.option&=~OPTION_HIDE;
- else if (flag&4)
- nd->sc.option|= OPTION_HIDE;
- else //Can't change the view_data to invisible class because the view_data for all npcs is shared! [Skotlex]
- nd->sc.option|= OPTION_INVISIBLE;
-
- if (nd->class_ == WARP_CLASS)
- { //Client won't display option changes for warp portals [Toms]
- if (nd->sc.option&(OPTION_HIDE|OPTION_INVISIBLE))
- clif_clearchar(&nd->bl, 0);
- else
- clif_spawn(&nd->bl);
- } else
- clif_changeoption(&nd->bl);
-
- if(flag&3 && (nd->u.scr.xs > 0 || nd->u.scr.ys >0))
- map_foreachinarea( npc_enable_sub,nd->bl.m,nd->bl.x-nd->u.scr.xs,nd->bl.y-nd->u.scr.ys,nd->bl.x+nd->u.scr.xs,nd->bl.y+nd->u.scr.ys,BL_PC,nd);
-
- return 0;
-}
-
-/*==========================================
- * NPCを名前で探す
- *------------------------------------------
- */
-struct npc_data* npc_name2id(const char *name)
-{
- return (struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
-}
-
-/*==========================================
- * イベントキューのイベント処理
- *------------------------------------------
- */
-int npc_event_dequeue(struct map_session_data *sd)
-{
- nullpo_retr(0, sd);
-
- if (!sd->eventqueue[0][0])
- return 0; //Nothing to dequeue
-
- if (!pc_addeventtimer(sd,100,sd->eventqueue[0]))
- { //Failed to dequeue, couldn't set a timer.
- ShowWarning("npc_event_dequeue: event timer is full !\n");
- return 0;
- }
- //Event dequeued successfully, shift other elements.
- sd->npc_id=0; //FIXME: Shouldn't dequeueing fail when you have an npc_id set?
- memmove(sd->eventqueue[0], sd->eventqueue[1], (MAX_EVENTQUEUE-1)*sizeof(sd->eventqueue[0]));
- sd->eventqueue[MAX_EVENTQUEUE-1][0]=0;
- return 1;
-}
-
-/*==========================================
- * イベントの遅延実行
- *------------------------------------------
- */
-int npc_event_timer(int tid,unsigned int tick,int id,int data)
-{
- unsigned char *eventname = (unsigned char *)data;
- struct event_data *ev = strdb_get(ev_db,eventname);
- struct npc_data *nd;
- struct map_session_data *sd=map_id2sd(id);
- size_t i;
-
- if((ev==NULL || (nd=ev->nd)==NULL))
- {
- if(battle_config.error_log)
- ShowWarning("npc_event: event not found [%s]\n",eventname);
- }
- else
- {
- for(i=0;i<MAX_EVENTTIMER;i++) {
- if( nd->eventtimer[i]==tid ) {
- nd->eventtimer[i]=-1;
- npc_event(sd,eventname,0); // sd NULL check is within
- break;
- }
- }
- if(i==MAX_EVENTTIMER && battle_config.error_log)
- ShowWarning("npc_event_timer: event timer not found [%s]!\n",eventname);
- }
-
- aFree(eventname);
- return 0;
-}
-
-int npc_timer_event(const unsigned char *eventname) // Added by RoVeRT
-{
- struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
- struct npc_data *nd;
-// int xs,ys;
-
- if((ev==NULL || (nd=ev->nd)==NULL)){
- ShowWarning("npc_timer_event: event not found [%s]\n",eventname);
- return 0;
- }
-
- run_script(nd->u.scr.script,ev->pos,nd->bl.id,nd->bl.id);
-
- return 0;
-}
-/*
-int npc_timer_sub_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
-{
- char *p=(char *)key;
- struct event_data *ev=(struct event_data *)data;
- int *c=va_arg(ap,int *);
- int tick=0,ctick=gettick();
- char temp[10];
- char event[100];
-
- if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){
- sscanf(&p[9],"%s",temp);
- tick=atoi(temp);
-
- strcpy( event, ev->nd->name);
- strcat( event, p);
-
- if (ctick >= ev->nd->lastaction && ctick - ev->nd->timer >= tick) {
- npc_timer_event(event);
- ev->nd->lastaction = ctick;
- }
- }
- return 0;
-}
-
-int npc_timer_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
-{
- struct npc_data *nd=(struct npc_data*)data;
-
- if(nd->timer == -1)
- return 0;
-
- sv_db->foreach(ev_db,npc_timer_sub_sub,&nd->bl.id);
-
- return 0;
-}
-
-int npc_timer(int tid,unsigned int tick,int id,int data) // Added by RoVeRT
-{
- npcname_db->foreach(npcname_db,npc_timer_sub);
-
- aFree((void*)data);
- return 0;
-}*/
-/*==========================================
- * イベント用ラベルのエクスポート
- * npc_parse_script->strdb_foreachから呼ばれる
- *------------------------------------------
- */
-int npc_event_export(char *lname,void *data,va_list ap)
-{
- int pos=(int)data;
- struct npc_data *nd=va_arg(ap,struct npc_data *);
-
- if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) {
- struct event_data *ev;
- unsigned char buf[51];
- char *p=strchr(lname,':');
- // エクスポートされる
- ev=(struct event_data *) aMalloc(sizeof(struct event_data));
- if (ev==NULL) {
- ShowFatalError("npc_event_export: out of memory !\n");
- exit(1);
- }else if (p==NULL || (p-lname)>NAME_LENGTH) {
- ShowFatalError("npc_event_export: label name error !\n");
- exit(1);
- }else{
- ev->nd=nd;
- ev->pos=pos;
- *p='\0';
- sprintf(buf,"%s::%s",nd->exname,lname);
- *p=':';
- strdb_put(ev_db,buf,ev);
- }
- }
- return 0;
-}
-
-int npc_event_sub(struct map_session_data *, struct event_data *, const unsigned char *); //[Lance]
-/*==========================================
- * 全てのNPCのOn*イベント実行
- *------------------------------------------
- */
-int npc_event_doall_sub(DBKey key,void *data,va_list ap)
-{
- unsigned char*p = key.str;
- struct event_data *ev;
- int *c;
- int rid;
- unsigned char *name;
-
- ev=(struct event_data *)data;
- c=va_arg(ap,int *);
- name=va_arg(ap,unsigned char *);
- rid=va_arg(ap, int);
-
- if( (p=strchr(p,':')) && p && strcmpi(name,p)==0 ){
- if(rid)
- npc_event_sub(((struct map_session_data *)map_id2bl(rid)),ev,key.str);
- else
- run_script(ev->nd->u.scr.script,ev->pos,rid,ev->nd->bl.id);
- (*c)++;
- }
-
- return 0;
-}
-int npc_event_doall(const unsigned char *name)
-{
- int c=0;
- unsigned char buf[64]="::";
-
- strncpy(buf+2,name,62);
- ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,0);
- return c;
-}
-int npc_event_doall_id(const unsigned char *name, int rid)
-{
- int c=0;
- unsigned char buf[64]="::";
-
- strncpy(buf+2,name,62);
- ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,rid);
- return c;
-}
-
-int npc_event_do_sub(DBKey key,void *data,va_list ap)
-{
- unsigned char *p = key.str;
- struct event_data *ev;
- int *c;
- const unsigned char *name;
-
- nullpo_retr(0, ev=(struct event_data *)data);
- nullpo_retr(0, ap);
- nullpo_retr(0, c=va_arg(ap,int *));
-
- name=va_arg(ap,const unsigned char *);
-
- if (p && strcmpi(name,p)==0 ) {
- run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
- (*c)++;
- }
-
- return 0;
-}
-int npc_event_do(const unsigned char *name)
-{
- int c=0;
-
- if (*name==':' && name[1]==':') {
- return npc_event_doall(name+2);
- }
-
- ev_db->foreach(ev_db,npc_event_do_sub,&c,name);
- return c;
-}
-
-/*==========================================
- * 時計イベント実行
- *------------------------------------------
- */
-int npc_event_do_clock(int tid,unsigned int tick,int id,int data)
-{
- time_t timer;
- struct tm *t;
- char buf[64];
- char *day="";
- int c=0;
-
- time(&timer);
- t=localtime(&timer);
-
- switch (t->tm_wday) {
- case 0: day = "Sun"; break;
- case 1: day = "Mon"; break;
- case 2: day = "Tue"; break;
- case 3: day = "Wed"; break;
- case 4: day = "Thu"; break;
- case 5: day = "Fri"; break;
- case 6: day = "Sat"; break;
- }
-
- if (t->tm_min != ev_tm_b.tm_min ) {
- sprintf(buf,"OnMinute%02d",t->tm_min);
- c+=npc_event_doall(buf);
- sprintf(buf,"OnClock%02d%02d",t->tm_hour,t->tm_min);
- c+=npc_event_doall(buf);
- sprintf(buf,"On%s%02d%02d",day,t->tm_hour,t->tm_min);
- c+=npc_event_doall(buf);
- }
- if (t->tm_hour!= ev_tm_b.tm_hour) {
- sprintf(buf,"OnHour%02d",t->tm_hour);
- c+=npc_event_doall(buf);
- }
- if (t->tm_mday!= ev_tm_b.tm_mday) {
- sprintf(buf,"OnDay%02d%02d",t->tm_mon+1,t->tm_mday);
- c+=npc_event_doall(buf);
- }
- memcpy(&ev_tm_b,t,sizeof(ev_tm_b));
- return c;
-}
-/*==========================================
- * OnInitイベント実行(&時計イベント開始)
- *------------------------------------------
- */
-int npc_event_do_oninit(void)
-{
-// int c = npc_event_doall("OnInit");
- ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
- CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
-
- add_timer_interval(gettick()+100,
- npc_event_do_clock,0,0,1000);
-
- return 0;
-}
-/*==========================================
- * OnTimer NPC event - by RoVeRT
- *------------------------------------------
- */
-int npc_addeventtimer(struct npc_data *nd,int tick,const char *name)
-{
- int i;
- unsigned char *evname;
-
- for(i=0;i<MAX_EVENTTIMER;i++)
- if( nd->eventtimer[i]==-1 )
- break;
- if(i<MAX_EVENTTIMER){
- if (!strdb_get(ev_db,(unsigned char*)name)) {
- if (battle_config.error_log)
- ShowError("npc_addeventimer: Event %s does not exists.\n", name);
- return 1; //Event does not exists!
- }
- evname =(unsigned char *) aMallocA(NAME_LENGTH*sizeof(char));
- if(evname==NULL){
- ShowFatalError("npc_addeventtimer: out of memory !\n");exit(1);
- }
- memcpy(evname,name,NAME_LENGTH-1);
- evname[NAME_LENGTH-1] = '\0';
- nd->eventtimer[i]=add_timer(gettick()+tick,
- npc_event_timer,nd->bl.id,(int)evname);
- }else
- ShowWarning("npc_addtimer: event timer is full !\n");
-
- return 0;
-}
-
-int npc_deleventtimer(struct npc_data *nd,const unsigned char *name)
-{
- int i;
- for(i=0;i<MAX_EVENTTIMER;i++)
- if( nd->eventtimer[i]!=-1 && strcmp(
- (unsigned char *)(get_timer(nd->eventtimer[i])->data), name)==0 ){
- delete_timer(nd->eventtimer[i],npc_event_timer);
- nd->eventtimer[i]=-1;
- break;
- }
-
- return 0;
-}
-
-int npc_cleareventtimer(struct npc_data *nd)
-{
- int i;
- for(i=0;i<MAX_EVENTTIMER;i++)
- if( nd->eventtimer[i]!=-1 ){
- delete_timer(nd->eventtimer[i],npc_event_timer);
- nd->eventtimer[i]=-1;
- }
-
- return 0;
-}
-
-int npc_do_ontimer_sub(DBKey key,void *data,va_list ap)
-{
- unsigned char *p = key.str;
- struct event_data *ev = (struct event_data *)data;
- int *c = va_arg(ap,int *);
-// struct map_session_data *sd=va_arg(ap,struct map_session_data *);
- int option = va_arg(ap,int);
- int tick = 0;
- char temp[10];
- char event[50];
-
- if(ev->nd->bl.id == (int)*c && (p = strchr(p,':')) && strnicmp("::OnTimer",p,8) == 0){
- sscanf(&p[9], "%s", temp);
- tick = atoi(temp);
-
- strcpy(event, ev->nd->name);
- strcat(event, p);
-
- if (option!=0) {
- npc_addeventtimer(ev->nd, tick, event);
- } else {
- npc_deleventtimer(ev->nd, event);
- }
- }
- return 0;
-}
-int npc_do_ontimer(int npc_id, int option)
-{
- ev_db->foreach(ev_db, npc_do_ontimer_sub, &npc_id, option);
- return 0;
-}
-/*==========================================
- * タイマーイベント用ラベルの取り込み
- * npc_parse_script->strdb_foreachから呼ばれる
- *------------------------------------------
- */
-int npc_timerevent_import(char *lname,void *data,va_list ap)
-{
- int pos=(int)data;
- struct npc_data *nd=va_arg(ap,struct npc_data *);
- int t=0,i=0;
-
- if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':') {
- // タイマーイベント
- struct npc_timerevent_list *te=nd->u.scr.timer_event;
- int j,i=nd->u.scr.timeramount;
- if(te==NULL) te=(struct npc_timerevent_list*)aMallocA(sizeof(struct npc_timerevent_list));
- else te= (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) );
- if(te==NULL){
- ShowFatalError("npc_timerevent_import: out of memory !\n");
- exit(1);
- }
- for(j=0;j<i;j++){
- if(te[j].timer>t){
- memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j));
- break;
- }
- }
- te[j].timer=t;
- te[j].pos=pos;
- nd->u.scr.timer_event=te;
- nd->u.scr.timeramount++;
- }
- return 0;
-}
-struct timer_event_data {
- int rid; //Attached player for this timer.
- int next; //timer index (starts with 0, then goes up to nd->u.scr.timeramount
- int time; //holds total time elapsed for the script since time 0 (whenthe timers started)
- unsigned int otick; //Holds tick value at which timer sequence was started (that is, it stores the tick value for which T= 0
-};
-
-/*==========================================
- * タイマーイベント実行
- *------------------------------------------
- */
-int npc_timerevent(int tid,unsigned int tick,int id,int data)
-{
- int next,t,old_rid,old_timer;
- unsigned int old_tick;
- struct npc_data* nd=(struct npc_data *)map_id2bl(id);
- struct npc_timerevent_list *te;
- struct timer_event_data *ted = (struct timer_event_data*)data;
- struct map_session_data *sd=NULL;
-
- if( nd==NULL ){
- ShowError("npc_timerevent: NPC not found??\n");
- return 0;
- }
- if (ted->rid) {
- sd = map_id2sd(ted->rid);
- if (!sd) {
- if(battle_config.error_log)
- ShowError("npc_timerevent: Attached player not found.\n");
- ers_free(timer_event_ers, ted);
- return 0;
- }
- }
- old_rid = nd->u.scr.rid; //To restore it later.
- nd->u.scr.rid = sd?sd->bl.id:0;
-
- old_tick = nd->u.scr.timertick;
- nd->u.scr.timertick=ted->otick;
- te=nd->u.scr.timer_event+ ted->next;
-
- old_timer = nd->u.scr.timer;
- t = nd->u.scr.timer=ted->time;
- ted->next++;
-
- if( nd->u.scr.timeramount> ted->next){
- next= nd->u.scr.timer_event[ ted->next ].timer
- - nd->u.scr.timer_event[ ted->next-1 ].timer;
- ted->time+=next;
- if (sd)
- sd->npc_timer_id = add_timer(tick+next,npc_timerevent,id,(int)ted);
- else
- nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,(int)ted);
- } else {
- if (sd)
- sd->npc_timer_id = -1;
- else
- nd->u.scr.timerid = -1;
- ers_free(timer_event_ers, ted);
- }
- run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id);
- //Restore previous data, only if this timer is a player-attached one.
- if (sd) {
- nd->u.scr.rid = old_rid;
- nd->u.scr.timer = old_timer;
- nd->u.scr.timertick = old_tick;
- }
- return 0;
-}
-/*==========================================
- * タイマーイベント開始
- *------------------------------------------
- */
-int npc_timerevent_start(struct npc_data *nd, int rid)
-{
- int j,n, next;
- struct map_session_data *sd=NULL; //Player to whom script is attached.
- struct timer_event_data *ted;
-
- nullpo_retr(0, nd);
-
- n=nd->u.scr.timeramount;
- if( n==0 )
- return 0;
-
- for(j=0;j<n;j++){
- if( nd->u.scr.timer_event[j].timer > nd->u.scr.timer )
- break;
- }
- if(j>=n) // check if there is a timer to use !!BEFORE!! you write stuff to the structures [Shinomori]
- return 0;
- if (nd->u.scr.rid > 0) {
- //Try to attach timer to this player.
- sd = map_id2sd(nd->u.scr.rid);
- if (!sd) {
- if(battle_config.error_log)
- ShowError("npc_timerevent_start: Attached player not found!\n");
- return 1;
- }
- }
- //Check if timer is already started.
- if (sd) {
- if (sd->npc_timer_id != -1)
- return 0;
- } else if (nd->u.scr.timerid != -1)
- return 0;
-
- ted = ers_alloc(timer_event_ers, struct timer_event_data);
- ted->next = j;
- nd->u.scr.timertick=ted->otick=gettick();
-
- //Attach only the player if attachplayerrid was used.
- ted->rid = sd?sd->bl.id:0;
-
-// Do not store it to make way to two types of timers: globals and personals.
-// if (rid >= 0) nd->u.scr.rid=rid; // changed to: attaching to given rid by default [Shinomori]
- // if rid is less than 0 leave it unchanged [celest]
-
- next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer;
- ted->time = nd->u.scr.timer_event[j].timer;
- if (sd)
- sd->npc_timer_id = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted);
- else
- nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted);
- return 0;
-}
-/*==========================================
- * タイマーイベント終了
- *------------------------------------------
- */
-int npc_timerevent_stop(struct npc_data *nd)
-{
- struct map_session_data *sd =NULL;
- struct TimerData *td = NULL;
- int *tid;
- nullpo_retr(0, nd);
- if (nd->u.scr.rid) {
- sd = map_id2sd(nd->u.scr.rid);
- if (!sd) {
- if(battle_config.error_log)
- ShowError("npc_timerevent_stop: Attached player not found!\n");
- return 1;
- }
- }
-
- tid = sd?&sd->npc_timer_id:&nd->u.scr.timerid;
-
- if (*tid == -1) //Nothing to stop
- return 0;
- td = get_timer(*tid);
- if (td && td->data)
- ers_free(timer_event_ers, (struct event_timer_data*)td->data);
- delete_timer(*tid,npc_timerevent);
- *tid = -1;
- //Set the timer tick to the time that has passed since the beginning of the timers and now.
- nd->u.scr.timer = DIFF_TICK(gettick(),nd->u.scr.timertick);
-// nd->u.scr.rid = 0; //Eh? why detach?
- return 0;
-}
-/*==========================================
- * Aborts a running npc timer that is attached to a player.
- *------------------------------------------
- */
-void npc_timerevent_quit(struct map_session_data *sd) {
- struct TimerData *td;
- if (sd->npc_timer_id == -1)
- return;
- td = get_timer(sd->npc_timer_id);
- if (!td) {
- sd->npc_timer_id = -1;
- return; //??
- }
- delete_timer(sd->npc_timer_id,npc_timerevent);
- sd->npc_timer_id = -1;
- ers_free(timer_event_ers, (struct event_timer_data*)td->data);
-}
-
-/*==========================================
- * タイマー値の所得
- *------------------------------------------
- */
-int npc_gettimerevent_tick(struct npc_data *nd)
-{
- int tick;
- nullpo_retr(0, nd);
-
- tick=nd->u.scr.timer;
- if (nd->u.scr.timertick)
- tick+=DIFF_TICK(gettick(), nd->u.scr.timertick);
- return tick;
-}
-/*==========================================
- * タイマー値の設定
- *------------------------------------------
- */
-int npc_settimerevent_tick(struct npc_data *nd,int newtimer)
-{
- int flag;
- struct map_session_data *sd=NULL;
-
- nullpo_retr(0, nd);
-
- if (nd->u.scr.rid) {
- sd = map_id2sd(nd->u.scr.rid);
- if (!sd) {
- if(battle_config.error_log)
- ShowError("npc_settimerevent_tick: Attached player not found!\n");
- return 1;
- }
- flag= sd->npc_timer_id != -1 ;
- } else
- flag= nd->u.scr.timerid != -1 ;
-
- if(flag)
- npc_timerevent_stop(nd);
- nd->u.scr.timer=newtimer;
- if(flag)
- npc_timerevent_start(nd, -1);
- return 0;
-}
-
-int npc_event_sub(struct map_session_data *sd, struct event_data *ev, const unsigned char *eventname){
-
- if ( sd->npc_id!=0) {
- //Enqueue the event trigger.
- int i;
- for(i=0;i<MAX_EVENTQUEUE && sd->eventqueue[i][0];i++);
-
- if (i==MAX_EVENTQUEUE) {
- if (battle_config.error_log)
- ShowWarning("npc_event: event queue is full !\n");
- }else //Event enqueued.
- memcpy(sd->eventqueue[i],eventname,50);
- return 1;
- }
- if (ev->nd->sc.option&OPTION_INVISIBLE) {
- //Disabled npc, shouldn't trigger event.
- npc_event_dequeue(sd);
- return 2;
- }
- run_script(ev->nd->u.scr.script,ev->pos,sd->bl.id,ev->nd->bl.id);
- return 0;
-}
-
-/*==========================================
- * イベント型のNPC処理
- *------------------------------------------
- */
-int npc_event (struct map_session_data *sd, const unsigned char *eventname, int mob_kill)
-{
- struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
- struct npc_data *nd;
- int xs,ys;
- unsigned char mobevent[100];
-
- if (sd == NULL) {
- nullpo_info(NLP_MARK);
- return 0;
- }
-
- if (ev == NULL && eventname && strcmp(((eventname)+strlen(eventname)-9),"::OnTouch") == 0)
- return 1;
-
- if (ev == NULL || (nd = ev->nd) == NULL) {
- if (mob_kill) {
- strcpy( mobevent, eventname);
- strcat( mobevent, "::OnMyMobDead");
- ev = strdb_get(ev_db, mobevent);
- if (ev == NULL || (nd = ev->nd) == NULL) {
- if (strnicmp(eventname, "GM_MONSTER",10) != 0)
- ShowError("npc_event: (mob_kill) event not found [%s]\n", mobevent);
- return 0;
- }
- } else {
- if (battle_config.error_log)
- ShowError("npc_event: event not found [%s]\n", eventname);
- return 0;
- }
- }
-
- xs=nd->u.scr.xs;
- ys=nd->u.scr.ys;
- if (xs>=0 && ys>=0 && (strcmp(((eventname)+strlen(eventname)-6),"Global") != 0) )
- {
- if (nd->bl.m >= 0) { //Non-invisible npc
- if (nd->bl.m != sd->bl.m )
- return 1;
- if ( xs>0 && (sd->bl.x<nd->bl.x-xs/2 || nd->bl.x+xs/2<sd->bl.x) )
- return 1;
- if ( ys>0 && (sd->bl.y<nd->bl.y-ys/2 || nd->bl.y+ys/2<sd->bl.y) )
- return 1;
- }
- }
-
- return npc_event_sub(sd,ev,eventname);
-}
-
-
-int npc_command_sub(DBKey key,void *data,va_list ap)
-{
- unsigned char *p = key.str;
- struct event_data *ev=(struct event_data *)data;
- unsigned char *npcname=va_arg(ap,char *);
- char *command=va_arg(ap,char *);
- unsigned char temp[100];
-
- if(strcmp(ev->nd->name,npcname)==0 && (p=strchr(p,':')) && p && strnicmp("::OnCommand",p,10)==0 ){
- sscanf(&p[11],"%s",temp);
-
- if (strcmp(command,temp)==0)
- run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
- }
-
- return 0;
-}
-
-int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command)
-{
- ev_db->foreach(ev_db,npc_command_sub,npcname,command);
-
- return 0;
-}
-/*==========================================
- * 接触型のNPC処理
- *------------------------------------------
- */
-int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y)
-{
- int i,f=1;
- int xs,ys;
-
- nullpo_retr(1, sd);
-
- if(sd->npc_id)
- return 1;
-
- for(i=0;i<map[m].npc_num;i++) {
- if (map[m].npc[i]->sc.option&OPTION_INVISIBLE) { // 無効化されている
- f=0;
- continue;
- }
-
- switch(map[m].npc[i]->bl.subtype) {
- case WARP:
- xs=map[m].npc[i]->u.warp.xs;
- ys=map[m].npc[i]->u.warp.ys;
- break;
- case SCRIPT:
- xs=map[m].npc[i]->u.scr.xs;
- ys=map[m].npc[i]->u.scr.ys;
- break;
- default:
- continue;
- }
- if (x >= map[m].npc[i]->bl.x-xs/2 && x < map[m].npc[i]->bl.x-xs/2+xs &&
- y >= map[m].npc[i]->bl.y-ys/2 && y < map[m].npc[i]->bl.y-ys/2+ys)
- break;
- }
- if (i==map[m].npc_num) {
- if (f) {
- if (battle_config.error_log)
- ShowError("npc_touch_areanpc : some bug \n");
- }
- return 1;
- }
- switch(map[m].npc[i]->bl.subtype) {
- case WARP:
- // hidden chars cannot use warps -- is it the same for scripts too?
- if (sd->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) ||
- (!battle_config.duel_allow_teleport && sd->duel_group)) // duel rstrct [LuzZza]
- break;
- pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0);
- break;
- case SCRIPT:
- {
- //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
- char name[50]; // need 24 max + 9 for "::OnTouch"
-
- if(sd->areanpc_id == map[m].npc[i]->bl.id)
- return 1;
- sd->areanpc_id = map[m].npc[i]->bl.id;
-
- sprintf(name,"%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance]
-
- if( npc_event(sd,name,0)>0 ) {
- pc_stop_walking(sd,1); //Make it stop walking!
- npc_click(sd,map[m].npc[i]);
- }
- //aFree(name);
- break;
- }
- }
- return 0;
-}
-
-int npc_touch_areanpc2(struct block_list *bl)
-{
- int i,m=bl->m;
- int xs,ys;
-
- for(i=0;i<map[m].npc_num;i++) {
- if (map[m].npc[i]->sc.option&OPTION_INVISIBLE)
- continue;
-
- if (map[m].npc[i]->bl.subtype!=WARP)
- continue;
-
- xs=map[m].npc[i]->u.warp.xs;
- ys=map[m].npc[i]->u.warp.ys;
-
- if (bl->x >= map[m].npc[i]->bl.x-xs/2 && bl->x < map[m].npc[i]->bl.x-xs/2+xs &&
- bl->y >= map[m].npc[i]->bl.y-ys/2 && bl->y < map[m].npc[i]->bl.y-ys/2+ys)
- break;
- }
- if (i==map[m].npc_num)
- return 0;
-
- xs = map_mapindex2mapid(map[m].npc[i]->u.warp.mapindex);
- if (xs < 0) // Can't warp object between map servers...
- return 0;
-
- if (unit_warp(bl, xs, map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0))
- return 0; //Failed to warp.
-
- return 1;
-}
-
-/*==========================================
- * 近くかどうかの判定
- *------------------------------------------
- */
-int npc_checknear2(struct map_session_data *sd,struct block_list *bl)
-{
- nullpo_retr(1, sd);
- if(bl == NULL) return 1;
-
- if(sd->state.using_fake_npc && sd->npc_id == bl->id)
- return 0;
-
- if (status_get_class(bl)<0) //Class-less npc, enable click from anywhere.
- return 0;
-
- if (bl->m!=sd->bl.m ||
- bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
- bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)
- return 1;
-
- return 0;
-}
-
-TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl)
-{
- struct npc_data *nd;
-
- nullpo_retr(NULL, sd);
- if(bl == NULL) return NULL;
- if(bl->type != BL_NPC) return NULL;
- nd = (TBL_NPC*)bl;
-
- if(sd->state.using_fake_npc && sd->npc_id == bl->id)
- return nd;
-
- if (nd->class_<0) //Class-less npc, enable click from anywhere.
- return nd;
-
- if (bl->m!=sd->bl.m ||
- bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
- bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)
- return NULL;
-
- return nd;
-}
-
-/*==========================================
- * NPCのオープンチャット発言
- *------------------------------------------
- */
-int npc_globalmessage(const char *name,char *mes)
-{
- struct npc_data *nd=(struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
- char temp[100];
-
- if (!nd)
- return 0;
-
- snprintf(temp, sizeof temp ,"%s : %s",name,mes);
- clif_GlobalMessage(&nd->bl,temp);
-
- return 0;
-}
-
-/*==========================================
- * クリック時のNPC処理
- *------------------------------------------
- */
-int npc_click(struct map_session_data *sd,struct npc_data *nd)
-{
- nullpo_retr(1, sd);
-
- if (sd->npc_id != 0) {
- if (battle_config.error_log)
- ShowError("npc_click: npc_id != 0\n");
- return 1;
- }
-
- if(!nd) return 1;
- if ((nd = npc_checknear(sd,&nd->bl)) == NULL)
- return 1;
- //Hidden/Disabled npc.
- if (nd->class_ < 0 || nd->sc.option&OPTION_INVISIBLE)
- return 1;
-
- switch(nd->bl.subtype) {
- case SHOP:
- clif_npcbuysell(sd,nd->bl.id);
- npc_event_dequeue(sd);
- break;
- case SCRIPT:
- run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id);
- break;
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int npc_scriptcont(struct map_session_data *sd,int id)
-{
- nullpo_retr(1, sd);
-
- if (id!=sd->npc_id){
- ShowWarning("npc_scriptcont: sd->npc_id (%d) is not id (%d).\n", sd->npc_id, id);
- return 1;
- }
-
- if(id != fake_nd->bl.id) { // Not item script
- if ((npc_checknear(sd,map_id2bl(id))) == NULL){
- ShowWarning("npc_scriptcont: failed npc_checknear test.\n");
- return 1;
- }
- }
- run_script_main(sd->st);
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int npc_buysellsel(struct map_session_data *sd,int id,int type)
-{
- struct npc_data *nd;
-
- nullpo_retr(1, sd);
-
- if ((nd = npc_checknear(sd,map_id2bl(id))) == NULL)
- return 1;
-
- if (nd->bl.subtype!=SHOP) {
- if (battle_config.error_log)
- ShowError("no such shop npc : %d\n",id);
- if (sd->npc_id == id)
- sd->npc_id=0;
- return 1;
- }
- if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている
- return 1;
-
- sd->npc_shopid=id;
- if (type==0) {
- clif_buylist(sd,nd);
- } else {
- clif_selllist(sd);
- }
- return 0;
-}
-
-//npc_buylist for script-controlled shops.
-static int npc_buylist_sub(
- struct map_session_data *sd,int n,
- unsigned short *item_list, struct npc_data *nd)
-{
- unsigned char npc_ev[51];
- int i;
- int regkey = add_str("@bought_nameid");
- int regkey2 = add_str("@bought_quantity");
- sprintf(npc_ev, "%s::OnBuyItem", nd->exname);
- for(i=0;i<n;i++){
- pc_setreg(sd,regkey+(i<<24),(int)item_list[i*2+1]);
- pc_setreg(sd,regkey2+(i<<24),(int)item_list[i*2]);
- }
- npc_event(sd, npc_ev, 0);
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int npc_buylist(struct map_session_data *sd,int n,unsigned short *item_list)
-{
- struct npc_data *nd;
- double z;
- int i,j,w,skill,itemamount=0,new_=0;
-
- nullpo_retr(3, sd);
- nullpo_retr(3, item_list);
-
- if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL)
- return 3;
-
- if (nd->master_nd) //Script-based shops.
- return npc_buylist_sub(sd,n,item_list,nd->master_nd);
-
- if (nd->bl.subtype!=SHOP)
- return 3;
-
- for(i=0,w=0,z=0;i<n;i++) {
- for(j=0;nd->u.shop_item[j].nameid;j++) {
- if (nd->u.shop_item[j].nameid==item_list[i*2+1] || //Normal items
- itemdb_viewid(nd->u.shop_item[j].nameid)==item_list[i*2+1]) //item_avail replacement
- break;
- }
- if (nd->u.shop_item[j].nameid==0)
- return 3;
-
- if (!itemdb_isstackable(nd->u.shop_item[j].nameid) && item_list[i*2] > 1)
- { //Exploit? You can't buy more than 1 of equipment types o.O
- ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %d!\n",
- sd->status.name, sd->status.account_id, sd->status.char_id, item_list[i*2], nd->u.shop_item[j].nameid);
- item_list[i*2] = 1;
- }
- if (itemdb_value_notdc(nd->u.shop_item[j].nameid))
- z+=(double)nd->u.shop_item[j].value * item_list[i*2];
- else
- z+=(double)pc_modifybuyvalue(sd,nd->u.shop_item[j].value) * item_list[i*2];
- itemamount+=item_list[i*2];
-
- switch(pc_checkadditem(sd,item_list[i*2+1],item_list[i*2])) {
- case ADDITEM_EXIST:
- break;
- case ADDITEM_NEW:
- new_++;
- break;
- case ADDITEM_OVERAMOUNT:
- return 2;
- }
-
- w+=itemdb_weight(item_list[i*2+1]) * item_list[i*2];
- }
- if (z > (double)sd->status.zeny)
- return 1; // zeny不足
- if (w+sd->weight > sd->max_weight)
- return 2; // 重量超過
- if (pc_inventoryblank(sd)<new_)
- return 3; // 種類数超過
-
- //Logs (S)hopping Zeny [Lupus]
- if(log_config.zeny > 0 )
- log_zeny(sd, "S", sd, -(int)z);
- //Logs
-
- pc_payzeny(sd,(int)z);
- for(i=0;i<n;i++) {
- struct item item_tmp;
-
- malloc_set(&item_tmp,0,sizeof(item_tmp));
- item_tmp.nameid = item_list[i*2+1];
- item_tmp.identify = 1; // npc販売アイテムは鑑定済み
-
- pc_additem(sd,&item_tmp,item_list[i*2]);
-
- //Logs items, Bought in NPC (S)hop [Lupus]
- if(log_config.enable_logs&0x20)
- log_pick_pc(sd, "S", item_tmp.nameid, item_list[i*2], NULL);
- //Logs
- }
-
- //商人経験値
- if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) {
- if (sd->status.skill[MC_DISCOUNT].flag != 0)
- skill = sd->status.skill[MC_DISCOUNT].flag - 2;
- if (skill > 0) {
- z = z * (double)skill * (double)battle_config.shop_exp/10000.;
- if (z < 1)
- z = 1;
- pc_gainexp(sd,NULL,0,(int)z);
- }
- }
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-int npc_selllist(struct map_session_data *sd,int n,unsigned short *item_list)
-{
- double z;
- int i,skill,itemamount=0;
- struct npc_data *nd;
-
- nullpo_retr(1, sd);
- nullpo_retr(1, item_list);
-
- if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL)
- return 1;
- nd = nd->master_nd; //For OnSell triggers.
-
- for(i=0,z=0;i<n;i++) {
- int nameid, idx, qty;
- idx = item_list[i*2]-2;
- qty = item_list[i*2+1];
-
- if (idx <0 || idx >=MAX_INVENTORY || qty < 0)
- break;
-
- nameid=sd->status.inventory[idx].nameid;
- if (nameid == 0 || !sd->inventory_data[idx] ||
- sd->status.inventory[idx].amount < qty)
- break;
-
- if (sd->inventory_data[idx]->flag.value_notoc)
- z+=(double)qty*sd->inventory_data[idx]->value_sell;
- else
- z+=(double)qty*pc_modifysellvalue(sd,sd->inventory_data[idx]->value_sell);
-
- if(sd->inventory_data[idx]->type==7 && sd->status.inventory[idx].card[0] == (short)0xff00)
- {
- if(search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0)
- intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1],sd->status.inventory[idx].card[2]));
- }
-
- if(log_config.enable_logs&0x20) //Logs items, Sold to NPC (S)hop [Lupus]
- log_pick_pc(sd, "S", nameid, -qty, &sd->status.inventory[idx]);
-
- if(nd) {
- pc_setreg(sd,add_str("@sold_nameid")+(i<<24),(int)sd->status.inventory[idx].nameid);
- pc_setreg(sd,add_str("@sold_quantity")+(i<<24),qty);
- }
- itemamount+=qty;
- pc_delitem(sd,idx,qty,0);
- }
-
- if (z > MAX_ZENY) z = MAX_ZENY;
-
- if(log_config.zeny) //Logs (S)hopping Zeny [Lupus]
- log_zeny(sd, "S", sd, (int)z);
-
- pc_getzeny(sd,(int)z);
-
- if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) {
- if (sd->status.skill[MC_OVERCHARGE].flag != 0)
- skill = sd->status.skill[MC_OVERCHARGE].flag - 2;
- if (skill > 0) {
- z = z * (double)skill * (double)battle_config.shop_exp/10000.;
- if (z < 1)
- z = 1;
- pc_gainexp(sd,NULL,0,(int)z);
- }
- }
-
- if(nd) {
- unsigned char npc_ev[51];
- sprintf(npc_ev, "%s::OnSellItem", nd->exname);
- npc_event(sd, npc_ev, 0);
- }
-
- if (i<n) {
- //Error/Exploit... of some sort. If we return 1, the client will not mark
- //any item as deleted even though a few were sold. In such a case, we
- //have no recourse but to kick them out so their inventory will refresh
- //correctly on relog. [Skotlex]
- if (i) clif_setwaitclose(sd->fd);
- return 1;
- }
- return 0;
-}
-
-int npc_remove_map (struct npc_data *nd)
-{
- int m,i;
- nullpo_retr(1, nd);
-
- if(nd->bl.prev == NULL || nd->bl.m < 0)
- return 1; //Not assigned to a map.
- m = nd->bl.m;
-#ifdef PCRE_SUPPORT
- npc_chat_finalize(nd);
-#endif
- clif_clearchar_area(&nd->bl,2);
- strdb_remove(npcname_db, (nd->bl.subtype < SCRIPT) ? nd->name : nd->exname);
- //Remove corresponding NPC CELLs
- if (nd->bl.subtype == WARP) {
- int j, xs, ys, x, y;
- x = nd->bl.x;
- y = nd->bl.y;
- xs = nd->u.warp.xs;
- ys = nd->u.warp.ys;
-
- for (i = 0; i < ys; i++) {
- for (j = 0; j < xs; j++) {
- if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNPC))
- map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_CLRNPC);
- }
- }
- }
- map_delblock(&nd->bl);
- map_deliddb(&nd->bl);
- //Remove npc from map[].npc list. [Skotlex]
- for(i=0;i<map[m].npc_num && map[m].npc[i] != nd;i++);
- if (i >= map[m].npc_num) return 2; //failed to find it?
-
- map[m].npc_num--;
- for(; i<map[m].npc_num; i++)
- map[m].npc[i]=map[m].npc[i+1];
- return 0;
-}
-
-static int npc_unload_ev(DBKey key,void *data,va_list ap) {
- struct event_data *ev=(struct event_data *)data;
- unsigned char *npcname=va_arg(ap,unsigned char *);
-
- if(strcmp(ev->nd->exname,npcname)==0){
- db_remove(ev_db, key);
- return 1;
- }
- return 0;
-}
-
-static int npc_unload_dup_sub(DBKey key,void * data,va_list ap)
-{
- struct npc_data *nd = (struct npc_data *)data;
- int src_id;
-
- if(nd->bl.type!=BL_NPC || nd->bl.subtype != SCRIPT)
- return 0;
-
- src_id=va_arg(ap,int);
- if (nd->u.scr.src_id == src_id)
- npc_unload(nd);
- return 0;
-}
-//Removes all npcs that are duplicates of the passed one. [Skotlex]
-void npc_unload_duplicates (struct npc_data *nd)
-{
- map_foreachiddb(npc_unload_dup_sub,nd->bl.id);
-}
-
-int npc_unload (struct npc_data *nd)
-{
- nullpo_ret(nd);
-
- npc_remove_map (nd);
- map_deliddb(&nd->bl);
-
- if (nd->chat_id) {
- struct chat_data *cd = (struct chat_data*)map_id2bl(nd->chat_id);
- if (cd) aFree (cd);
- cd = NULL;
- }
- if (nd->bl.subtype == SCRIPT) {
- ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related.
- if (nd->u.scr.timerid != -1) {
- struct TimerData *td = NULL;
- td = get_timer(nd->u.scr.timerid);
- if (td && td->data)
- ers_free(timer_event_ers, (struct event_timer_data*)td->data);
- delete_timer(nd->u.scr.timerid, npc_timerevent);
- }
- npc_cleareventtimer (nd);
- if (nd->u.scr.timer_event)
- aFree(nd->u.scr.timer_event);
- if (nd->u.scr.src_id == 0) {
- if(nd->u.scr.script) {
- script_free_code(nd->u.scr.script);
- nd->u.scr.script = NULL;
- }
- if (nd->u.scr.label_list) {
- aFree(nd->u.scr.label_list);
- nd->u.scr.label_list = NULL;
- }
- }
- }
- aFree(nd);
-
- return 0;
-}
-
-//
-// 初期化関係
-//
-
-/*==========================================
- * 読み込むnpcファイルのクリア
- *------------------------------------------
- */
-void npc_clearsrcfile (void)
-{
- struct npc_src_list *p = npc_src_first, *p2;
-
- while (p) {
- p2 = p;
- p = p->next;
- aFree(p2);
- }
- npc_src_first = NULL;
- npc_src_last = NULL;
-}
-/*==========================================
- * 読み込むnpcファイルの追加
- *------------------------------------------
- */
-void npc_addsrcfile (char *name)
-{
- struct npc_src_list *nsl;
-
- if (strcmpi(name, "clear") == 0) {
- npc_clearsrcfile();
- return;
- }
-
- // prevent multiple insert of source files
- nsl = npc_src_first;
- while (nsl)
- { // found the file, no need to insert it again
- if (0 == strcmp(name, nsl->name))
- return;
- nsl = nsl->next;
- }
-
- nsl = (struct npc_src_list *) aMalloc (sizeof(*nsl) + strlen(name));
- nsl->next = NULL;
- strncpy(nsl->name, name, strlen(name) + 1);
- if (npc_src_first == NULL)
- npc_src_first = nsl;
- if (npc_src_last)
- npc_src_last->next = nsl;
- npc_src_last = nsl;
-}
-/*==========================================
- * 読み込むnpcファイルの削除
- *------------------------------------------
- */
-void npc_delsrcfile (char *name)
-{
- struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first;
-
- if (strcmpi(name, "all") == 0) {
- npc_clearsrcfile();
- return;
- }
-
- while (p) {
- if (strcmp(p->name, name) == 0) {
- *lp = p->next;
- if (npc_src_last == p)
- npc_src_last = pp;
- aFree(p);
- break;
- }
- lp = &p->next;
- pp = p;
- p = p->next;
- }
-}
-
-/*==========================================
- * warp行解析
- *------------------------------------------
- */
-int npc_parse_warp (char *w1,char *w2,char *w3,char *w4)
-{
- int x, y, xs, ys, to_x, to_y, m;
- int i, j;
- char mapname[MAP_NAME_LENGTH], to_mapname[MAP_NAME_LENGTH];
- struct npc_data *nd;
-
- // 引数の個数チェック
- if (sscanf(w1, "%15[^,],%d,%d", mapname, &x, &y) != 3 ||
- sscanf(w4, "%d,%d,%15[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5) {
- ShowError("bad warp line : %s\n", w3);
- return 1;
- }
-
- m = map_mapname2mapid(mapname);
- i = mapindex_name2id(to_mapname);
- if (!i) {
- ShowError("bad warp line (destination map not found): %s\n", w3);
- return 1;
- }
-
- nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data));
-
- nd->bl.id = npc_get_new_npc_id();
- nd->n = map_addnpc(m, nd);
- nd->bl.prev = nd->bl.next = NULL;
- nd->bl.m = m;
- nd->bl.x = x;
- nd->bl.y = y;
- memcpy(nd->name, w3, NAME_LENGTH-1);
- memcpy(nd->exname, w3, NAME_LENGTH-1);
-
- if (!battle_config.warp_point_debug)
- nd->class_ = WARP_CLASS;
- else
- nd->class_ = WARP_DEBUG_CLASS;
- nd->speed = 200;
-
- nd->u.warp.mapindex = (short)i;
- xs += 2;
- ys += 2;
- nd->u.warp.x = to_x;
- nd->u.warp.y = to_y;
- nd->u.warp.xs = xs;
- nd->u.warp.ys = ys;
-
- for (i = 0; i < ys; i++) {
- for (j = 0; j < xs; j++) {
- if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNOPASS))
- continue;
- map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_SETNPC);
- }
- }
-
- npc_warp++;
- nd->bl.type = BL_NPC;
- nd->bl.subtype = WARP;
- map_addblock(&nd->bl);
- status_set_viewdata(&nd->bl, nd->class_);
- status_change_init(&nd->bl);
- unit_dataset(&nd->bl);
- clif_spawn(&nd->bl);
- strdb_put(npcname_db, nd->name, nd);
-
- return 0;
-}
-
-/*==========================================
- * shop行解析
- *------------------------------------------
- */
-static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4)
-{
- #define MAX_SHOPITEM 100
- char *p;
- int x, y, dir, m, pos = 0;
- char mapname[MAP_NAME_LENGTH];
- struct npc_data *nd;
-
- if (strcmp(w1, "-") == 0) {
- x = 0; y = 0; dir = 0; m = -1;
- } else {
- // 引数の個数チェック
- if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
- strchr(w4, ',') == NULL) {
- ShowError("bad shop line : %s\n", w3);
- return 1;
- }
- m = map_mapname2mapid(mapname);
- }
-
- nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) +
- sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1));
- p = strchr(w4, ',');
-
- while (p && pos < MAX_SHOPITEM) {
- int nameid, value;
- struct item_data *id;
- p++;
- if (sscanf(p, "%d:%d", &nameid, &value) != 2)
- break;
- nd->u.shop_item[pos].nameid = nameid;
- id = itemdb_search(nameid);
- if (value < 0)
- value = id->value_buy;
- nd->u.shop_item[pos].value = value;
- // check for bad prices that can possibly cause exploits
- if (value/124. < id->value_sell/75.) { //Clened up formula to prevent overflows.
- printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex]
- if (value < id->value_sell)
- ShowWarning ("Item %s [%d] buying price (%d) is less than selling price (%d) at %s\n",
- id->name, id->nameid, value, id->value_sell, current_file);
- else
- ShowWarning ("Item %s [%d] discounted buying price (%d) is less than overcharged selling price (%d) at %s\n",
- id->name, id->nameid, value/100*75, id->value_sell/100*124, current_file);
- }
- //for logs filters, atcommands and iteminfo script command
- if (id->maxchance<=0)
- id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop
-
- pos++;
- p = strchr(p, ',');
- }
- if (pos == 0) {
- aFree(nd);
- return 1;
- }
- nd->u.shop_item[pos++].nameid = 0;
-
- nd->bl.prev = nd->bl.next = NULL;
- nd->bl.m = m;
- nd->bl.x = x;
- nd->bl.y = y;
- nd->bl.id = npc_get_new_npc_id();
- memcpy(nd->name, w3, NAME_LENGTH-1);
- nd->name[NAME_LENGTH-1] = '\0';
- nd->class_ = m==-1?-1:atoi(w4);
- nd->speed = 200;
-
- nd = (struct npc_data *)aRealloc(nd,
- sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos);
-
- npc_shop++;
- nd->bl.type = BL_NPC;
- nd->bl.subtype = SHOP;
- if (m >= 0) {
- nd->n = map_addnpc(m,nd);
- map_addblock(&nd->bl);
- status_set_viewdata(&nd->bl, nd->class_);
- status_change_init(&nd->bl);
- unit_dataset(&nd->bl);
- nd->ud.dir = dir;
- clif_spawn(&nd->bl);
- } else
- // we skip map_addnpc, but still add it to the list of ID's
- map_addiddb(&nd->bl);
- strdb_put(npcname_db, nd->name,nd);
-
- return 0;
-}
-
-/*==========================================
- * NPCのラベルデータコンバート
- *------------------------------------------
- */
-int npc_convertlabel_db (DBKey key, void *data, va_list ap)
-{
- unsigned char *lname = key.str;
- int pos = (int)data;
- struct npc_data *nd;
- struct npc_label_list *lst;
- int num;
- char *p;
- char c;
-
- nullpo_retr(0, ap);
- nullpo_retr(0, nd = va_arg(ap,struct npc_data *));
-
- lst = nd->u.scr.label_list;
- num = nd->u.scr.label_list_num;
- if (!lst) {
- lst = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list));
- num = 0;
- } else
- lst = (struct npc_label_list *) aRealloc (lst, sizeof(struct npc_label_list)*(num+1));
-
- // In case of labels not terminated with ':', for user defined function support
- p = lname;
- while(isalnum(*(unsigned char*)p) || *p == '_') { p++; }
- c = *p;
- *p='\0';
-
- // here we check if the label fit into the buffer
- if (strlen(lname) > 23) {
- ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, current_file);
- exit(1);
- }
- memcpy(lst[num].name, lname, strlen(lname)+1); //including EOS
-
- *p = c;
- lst[num].pos = pos;
- nd->u.scr.label_list = lst;
- nd->u.scr.label_list_num = num+1;
-
- return 0;
-}
-
-/*==========================================
- * script行解析
- *------------------------------------------
- */
-static void npc_parse_script_line(unsigned char *p,int *curly_count,int line) {
- int i = strlen((char *)p),j;
- int string_flag = 0;
- static int comment_flag = 0;
- for(j = 0; j < i ; j++) {
- if(comment_flag) {
- if(p[j] == '*' && p[j+1] == '/') {
- // マルチラインコメント終了
- j++;
- (*curly_count)--;
- comment_flag = 0;
- }
- } else if(string_flag) {
- if(p[j] == '"') {
- string_flag = 0;
- } else if(p[j] == '\\' && p[j-1]<=0x7e) {
- // エスケープ
- j++;
- }
- } else {
- if(p[j] == '"') {
- string_flag = 1;
- } else if(p[j] == '}') {
- if(*curly_count == 0) {
- break;
- } else {
- (*curly_count)--;
- }
- } else if(p[j] == '{') {
- (*curly_count)++;
- } else if(p[j] == '/' && p[j+1] == '/') {
- // コメント
- break;
- } else if(p[j] == '/' && p[j+1] == '*') {
- // マルチラインコメント
- j++;
- (*curly_count)++;
- comment_flag = 1;
- }
- }
- }
- if(string_flag) {
- printf("Missing '\"' at file %s line %d\n",current_file,line);
- exit(1);
- }
-}
-
-// Like npc_parse_script, except it's sole use is to skip the contents of a script. [Skotlex]
-static int npc_skip_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines)
-{
- unsigned char *srcbuf = NULL;
- int srcsize = 65536;
- int startline = 0;
- unsigned char line[1024];
- int curly_count = 0;
-
- srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char));
- if (strchr(first_line, '{')) {
- strcpy((char *)srcbuf, strchr(first_line, '{'));
- startline = *lines;
- } else
- srcbuf[0] = 0;
- npc_parse_script_line(srcbuf,&curly_count,*lines);
- while (curly_count > 0) {
- fgets ((char *)line, 1020, fp);
- (*lines)++;
- npc_parse_script_line(line,&curly_count,*lines);
- if (feof(fp))
- break;
- if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
- srcsize += 65536;
- srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
- malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536);
- }
- if (srcbuf[0] != '{') {
- if (strchr((char *) line,'{')) {
- strcpy((char *) srcbuf, strchr((const char *) line, '{'));
- startline = *lines;
- }
- } else
- strcat((char *) srcbuf, (const char *) line);
- }
- if(curly_count > 0)
- ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
- aFree(srcbuf);
- return 0;
-}
-
-static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines,const char* file)
-{
- int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov
- char mapname[MAP_NAME_LENGTH];
- unsigned char *srcbuf = NULL;
- struct script_code *script;
- int srcsize = 65536;
- int startline = 0;
- unsigned char line[1024];
- int i;
- struct npc_data *nd, *dnd;
- struct dbt *label_db;
- char *p;
- struct npc_label_list *label_dup = NULL;
- int label_dupnum = 0;
- int src_id = 0;
-
- if (strcmp(w1, "-") == 0) {
- x = 0; y = 0; m = -1;
- } else {
- // 引数の個数チェック
- if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
- (strcmp(w2, "script") == 0 && strchr(w4,',') == NULL)) {
- ShowError("bad script line (in file %s): %s\n", file, w3);
- return 1;
- }
- m = map_mapname2mapid(mapname);
- }
-
- if (strcmp(w2, "script") == 0){
- // parsing script with curly
- int curly_count = 0;
- srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char));
- if (strchr(first_line, '{')) {
- strcpy((char *)srcbuf, strchr(first_line, '{'));
- startline = *lines;
- } else
- srcbuf[0] = 0;
- npc_parse_script_line(srcbuf,&curly_count,*lines);
- while (curly_count > 0) {
- fgets ((char *)line, 1020, fp);
- (*lines)++;
- npc_parse_script_line(line,&curly_count,*lines);
- if (feof(fp))
- break;
- if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
- srcsize += 65536;
- srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
- malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536);
- }
- if (srcbuf[0] != '{') {
- if (strchr((char *) line,'{')) {
- strcpy((char *) srcbuf, strchr((const char *) line, '{'));
- startline = *lines;
- }
- } else
- strcat((char *) srcbuf, (const char *) line);
- }
- if(curly_count > 0) {
- ShowError("Missing right curly at file %s, line %d\n",file, *lines);
- script = NULL;
- } else {
- // printf("Ok line %d\n",*lines);
- script = parse_script((unsigned char *) srcbuf, file, startline);
- }
- if (script == NULL) {
- // script parse error?
- aFree(srcbuf);
- return 1;
- }
- } else {
- // duplicateする
- char srcname[128];
- struct npc_data *dnd;
- if (sscanf(w2, "duplicate(%[^)])", srcname) != 1) {
- ShowError("bad duplicate name (in %s)! : %s", file, w2);
- return 0;
- }
- if ((dnd = npc_name2id(srcname)) == NULL) {
- ShowError("bad duplicate name (in %s)! (not exist) : %s\n", file, srcname);
- return 0;
- }
- script = dnd->u.scr.script;
- label_dup = dnd->u.scr.label_list;
- label_dupnum = dnd->u.scr.label_list_num;
- src_id = dnd->bl.id;
-
- }// end of スクリプト解析
-
- nd = (struct npc_data *)aCalloc(1, sizeof(struct npc_data));
-
- if (sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3) {
- // 接触型NPC
- int i, j;
-
- if (xs >= 0) xs = xs * 2 + 1;
- if (ys >= 0) ys = ys * 2 + 1;
-
- if (m >= 0) {
- for (i = 0; i < ys; i++) {
- for (j = 0; j < xs; j++) {
- if (map_getcell(m, x - xs/2 + j, y - ys/2 + i, CELL_CHKNOPASS))
- continue;
- map_setcell(m, x - xs/2 + j, y - ys/2 + i, CELL_SETNPC);
- }
- }
- }
- nd->u.scr.xs = xs;
- nd->u.scr.ys = ys;
- } else {
- // クリック型NPC
- class_ = atoi(w4);
- nd->u.scr.xs = 0;
- nd->u.scr.ys = 0;
- }
-
- while ((p = strchr(w3,':'))) {
- if (p[1] == ':') break;
- }
- if (p) {
- *p = 0;
- memcpy(nd->name, w3, NAME_LENGTH-1);
- memcpy(nd->exname, p+2, NAME_LENGTH-1);
- } else {
- memcpy(nd->name, w3, NAME_LENGTH-1);
- memcpy(nd->exname, w3, NAME_LENGTH-1);
- }
-
- if((dnd = npc_name2id(nd->exname))){
- if(battle_config.etc_log)
- ShowInfo("npc_parse_script: Overriding NPC '%s::%s' to '%s::%d'.. in file '%s' (Duplicated System Name - Lazy scripters >_>) \n",nd->name,nd->exname,nd->name,npc_script,file);
- sprintf(nd->exname, "%d", npc_script);
- }
-
- nd->bl.prev = nd->bl.next = NULL;
- nd->bl.m = m;
- nd->bl.x = x;
- nd->bl.y = y;
- nd->bl.id = npc_get_new_npc_id();
- nd->class_ = class_;
- nd->speed = 200;
- nd->u.scr.script = script;
- nd->u.scr.src_id = src_id;
-
- npc_script++;
- nd->bl.type = BL_NPC;
- nd->bl.subtype = SCRIPT;
-
- for (i = 0; i < MAX_EVENTTIMER; i++)
- nd->eventtimer[i] = -1;
- if (m >= 0) {
- nd->n = map_addnpc(m, nd);
- status_change_init(&nd->bl);
- unit_dataset(&nd->bl);
- nd->ud.dir = dir;
- map_addblock(&nd->bl);
- // Unused. You can always use xxx::OnXXXX events. Have this removed to improve perfomance.
- /*if (evflag) { // イベント型
- struct event_data *ev = (struct event_data *)aCalloc(1, sizeof(struct event_data));
- ev->nd = nd;
- ev->pos = 0;
- strdb_put(ev_db, nd->exname, ev);
- } else {
- clif_spawn(&nd->bl);
- }*/
- if (class_ >= 0){
- status_set_viewdata(&nd->bl, nd->class_);
- clif_spawn(&nd->bl);
- }
- } else {
- // we skip map_addnpc, but still add it to the list of ID's
- map_addiddb(&nd->bl);
- }
- strdb_put(npcname_db, nd->exname, nd);
-
- //-----------------------------------------
- // ラベルデータの準備
- if (srcbuf){
- // script本体がある場合の処理
- // ラベルデータのコンバート
- label_db = script_get_label_db();
- label_db->foreach(label_db, npc_convertlabel_db, nd);
-
- // もう使わないのでバッファ解放
- aFree(srcbuf);
- } else {
- // duplicate
- nd->u.scr.label_list = label_dup; // ラベルデータ共有
- nd->u.scr.label_list_num = label_dupnum;
- }
-
- //-----------------------------------------
- // イベント用ラベルデータのエクスポート
- for (i = 0; i < nd->u.scr.label_list_num; i++){
- char *lname = nd->u.scr.label_list[i].name;
- int pos = nd->u.scr.label_list[i].pos;
-
- if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) {
- // this check is useless here because the buffer is only 24 chars
- // and already overwritten if this is here is reached
- // I leave the check anyway but place it correctly to npc_convertlabel_db
- if (strlen(lname)>NAME_LENGTH-1) {
- ShowError("npc_parse_script: label name longer than %d chars! '%s' (%s)\n", NAME_LENGTH-1, lname, file);
- exit(1);
- } else {
- struct event_data *ev;
- unsigned char buf[51];
- // 51 comes from: 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS
- sprintf(buf,"%s::%s",nd->exname,lname);
-
- // remember the label is max 50 chars + eos; see the strdb_init below
- // generate the data and insert it
- ev=(struct event_data *)aMalloc(sizeof(struct event_data));
- ev->nd=nd;
- ev->pos=pos;
- if (strdb_put(ev_db,buf,ev) != NULL) //There was already another event of the same name?
- ShowWarning("npc_parse_script : duplicate event %s (%s)\n",buf, file);
- }
- }
- }
-
- //-----------------------------------------
- // ラベルデータからタイマーイベント取り込み
- for (i = 0; i < nd->u.scr.label_list_num; i++){
- int t = 0, k = 0;
- char *lname = nd->u.scr.label_list[i].name;
- int pos = nd->u.scr.label_list[i].pos;
- if (sscanf(lname, "OnTimer%d%n", &t, &k) == 1 && lname[k] == '\0') {
- // タイマーイベント
- struct npc_timerevent_list *te = nd->u.scr.timer_event;
- int j, k = nd->u.scr.timeramount;
- if (te == NULL)
- te = (struct npc_timerevent_list *)aMallocA(sizeof(struct npc_timerevent_list));
- else
- te = (struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) );
- for (j = 0; j < k; j++){
- if (te[j].timer > t){
- memmove(te+j+1, te+j, sizeof(struct npc_timerevent_list)*(k-j));
- break;
- }
- }
- te[j].timer = t;
- te[j].pos = pos;
- nd->u.scr.timer_event = te;
- nd->u.scr.timeramount++;
- }
- }
- nd->u.scr.timerid = -1;
-
- return 0;
-}
-
-/*==========================================
- * function行解析
- *------------------------------------------
- */
-static int npc_parse_function (char *w1, char *w2, char *w3, char *w4, char *first_line, FILE *fp, int *lines,const char* file)
-{
- unsigned char *srcbuf, *p;
- struct script_code *script;
- struct script_code *oldscript;
- int srcsize = 65536;
- int startline = 0;
- char line[1024];
- int curly_count = 0;
- struct dbt *user_db;
-
- // スクリプトの解析
- srcbuf = (unsigned char *) aMallocA (srcsize*sizeof(char));
- if (strchr(first_line,'{')) {
- strcpy(srcbuf, strchr(first_line,'{'));
- startline = *lines;
- } else
- srcbuf[0] = 0;
- npc_parse_script_line(srcbuf,&curly_count,*lines);
-
- while (curly_count > 0) {
- fgets(line, sizeof(line) - 1, fp);
- (*lines)++;
- npc_parse_script_line(line,&curly_count,*lines);
- if (feof(fp))
- break;
- if (strlen(srcbuf)+strlen(line)+1 >= (unsigned int)srcsize) {
- srcsize += 65536;
- srcbuf = (char *)aRealloc(srcbuf, srcsize);
- malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536);
- }
- if (srcbuf[0]!='{') {
- if (strchr(line,'{')) {
- strcpy(srcbuf, strchr(line,'{'));
- startline = *lines;
- }
- } else
- strcat(srcbuf,line);
- }
- if(curly_count > 0) {
- ShowError("Missing right curly at file %s, line %d\n",file, *lines);
- script = NULL;
- } else {
- script = parse_script(srcbuf, file, startline);
- }
- if (script == NULL) {
- // script parse error?
- aFree(srcbuf);
- return 1;
- }
-
- p = (char *) aMallocA (50*sizeof(char));
- strncpy(p, w3, 50);
-
- user_db = script_get_userfunc_db();
- oldscript = (struct script_code *)strdb_get(user_db, p);
- if(oldscript != NULL) {
- printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex]
- ShowInfo("parse_function: Overwriting user function [%s] (%s:%d)\n", p, file, *lines);
- script_free_code(oldscript);
- user_db->remove(user_db,str2key(p));
- strdb_put(user_db, p, script);
- } else
- strdb_put(user_db, p, script);
-
- // もう使わないのでバッファ解放
- aFree(srcbuf);
-
-// printf("function %s => %p\n",p,script);
-
- return 0;
-}
-
-
-/*==========================================
- * Parse Mob 1 - Parse mob list into each map
- * Parse Mob 2 - Actually Spawns Mob
- * [Wizputer]
- * If cached =1, it is a dynamic cached mob
- * index points to the index in the mob_list of the map_data cache.
- * -1 indicates that it is not stored on the map.
- *------------------------------------------
- */
-int npc_parse_mob2 (struct spawn_data *mob, int index)
-{
- int i;
- struct mob_data *md;
-
- for (i = 0; i < mob->num; i++) {
- md = mob_spawn_dataset(mob);
- md->spawn = mob;
- md->spawn_n = index;
- md->special_state.cached = (index>=0); //If mob is cached on map, it is dynamically removed
- mob_spawn(md);
- }
-
- return 1;
-}
-
-int npc_parse_mob (char *w1, char *w2, char *w3, char *w4)
-{
- int level, num, class_, mode, x,y,xs,ys;
- char mapname[MAP_NAME_LENGTH];
- char mobname[NAME_LENGTH];
- struct spawn_data mob, *data;
-
- malloc_set(&mob, 0, sizeof(struct spawn_data));
-
- // 引数の個数チェック
- if (sscanf(w1, "%15[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3 ||
- sscanf(w4, "%d,%d,%u,%u,%49[^\r\n]", &class_, &num, &mob.delay1, &mob.delay2, mob.eventname) < 2 ) {
- ShowError("bad monster line : %s %s %s (file %s)\n", w1, w3, w4, current_file);
- return 1;
- }
- if (!mapindex_name2id(mapname)) {
- ShowError("wrong map name : %s %s (file %s)\n", w1,w3, current_file);
- return 1;
- }
- mode = map_mapname2mapid(mapname);
- if (mode < 0) //Not loaded on this map-server instance.
- return 1;
- mob.m = (unsigned short)mode;
-
- if (x < 0 || map[mob.m].xs <= x || y < 0 || map[mob.m].ys <= y) {
- ShowError("Out of range spawn coordinates: %s (%d,%d), map size is (%d,%d) - %s %s (file %s)\n", map[mob.m].name, x, y, map[mob.m].xs-1, map[mob.m].ys-1, w1,w3, current_file);
- return 1;
- }
-
- // check monster ID if exists!
- if (mobdb_checkid(class_)==0) {
- ShowError("bad monster ID : %s %s (file %s)\n", w3, w4, current_file);
- return 1;
- }
-
- if (num < 1 || num>1000 ) {
- ShowError("wrong number of monsters : %s %s (file %s)\n", w3, w4, current_file);
- return 1;
- }
-
- mob.num = (unsigned short)num;
- mob.class_ = (short) class_;
- mob.x = (unsigned short)x;
- mob.y = (unsigned short)y;
- mob.xs = (signed short)xs;
- mob.ys = (signed short)ys;
-
- if (mob.num > 1 && battle_config.mob_count_rate != 100) {
- if ((mob.num = mob.num * battle_config.mob_count_rate / 100) < 1)
- mob.num = 1;
- }
-
- if (battle_config.force_random_spawn || (mob.x == 0 && mob.y == 0))
- { //Force a random spawn anywhere on the map.
- mob.x = mob.y = 0;
- mob.xs = mob.ys = -1;
- }
-
- //Apply the spawn delay fix [Skotlex]
- mode = mob_db(class_)->status.mode;
- if (mode & MD_BOSS) { //Bosses
- if (battle_config.boss_spawn_delay != 100)
- {
- mob.delay1 = mob.delay1*battle_config.boss_spawn_delay/100;
- mob.delay2 = mob.delay2*battle_config.boss_spawn_delay/100;
- }
- } else if (mode&MD_PLANT) { //Plants
- if (battle_config.plant_spawn_delay != 100)
- {
- mob.delay1 = mob.delay1*battle_config.plant_spawn_delay/100;
- mob.delay2 = mob.delay2*battle_config.plant_spawn_delay/100;
- }
- } else if (battle_config.mob_spawn_delay != 100)
- { //Normal mobs
- mob.delay1 = mob.delay1*battle_config.mob_spawn_delay/100;
- mob.delay2 = mob.delay2*battle_config.mob_spawn_delay/100;
- }
-
- // parse MOB_NAME,[MOB LEVEL]
- if (sscanf(w3, "%23[^,],%d", mobname, &level) > 1)
- mob.level = level;
-
- if(mob.delay1>0xfffffff || mob.delay2>0xfffffff) {
- ShowError("wrong monsters spawn delays : %s %s (file %s)\n", w3, w4, current_file);
- return 1;
- }
-
- //Use db names instead of the spawn file ones.
- if(battle_config.override_mob_names==1)
- strcpy(mob.name,"--en--");
- else if (battle_config.override_mob_names==2)
- strcpy(mob.name,"--ja--");
- else
- strncpy(mob.name, mobname, NAME_LENGTH-1);
-
- if (!mob_parse_dataset(&mob)) //Verify dataset.
- return 1;
-
- //Now that all has been validated. We allocate the actual memory
- //that the re-spawn data will use.
- data = aMalloc(sizeof(struct spawn_data));
- memcpy(data, &mob, sizeof(struct spawn_data));
-
- if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) {
- npc_parse_mob2(data,-1);
- npc_delay_mob += mob.num;
- } else {
- int index = map_addmobtolist(data->m, data);
- if( index >= 0 ) {
- // check if target map has players
- // (usually shouldn't occur when map server is just starting,
- // but not the case when we do @reloadscript
- if (map[mob.m].users > 0)
- npc_parse_mob2(data,index);
- npc_cache_mob += mob.num;
- } else {
- // mobcache is full
- // create them as delayed with one second
- mob.delay1 = 1000;
- mob.delay2 = 1000;
- npc_parse_mob2(data,-1);
- npc_delay_mob += mob.num;
- }
- }
-
- npc_mob++;
-
- return 0;
-}
-
-/*==========================================
- * マップフラグ行の解析
- *------------------------------------------
- */
-static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4)
-{
- int m;
- char mapname[MAP_NAME_LENGTH];
- int state = 1;
-
- // 引数の個数チェック
- if (sscanf(w1, "%15[^,]",mapname) != 1)
- return 1;
-
- m = map_mapname2mapid(mapname);
- if (m < 0)
- return 1;
- if (w4 && strcmpi(w4, "off") == 0)
- state = 0; //Disable mapflag rather than enable it. [Skotlex]
-
-//マップフラグ
- if (strcmpi(w3, "nosave") == 0) {
- char savemap[MAP_NAME_LENGTH];
- int savex, savey;
- if (state == 0)
- ; //Map flag disabled.
- else if (strcmp(w4, "SavePoint") == 0) {
- map[m].save.map = 0;
- map[m].save.x = -1;
- map[m].save.y = -1;
- } else if (sscanf(w4, "%15[^,],%d,%d", savemap, &savex, &savey) == 3) {
- map[m].save.map = mapindex_name2id(savemap);
- map[m].save.x = savex;
- map[m].save.y = savey;
- if (!map[m].save.map) {
- ShowWarning("Specified save point map '%s' for mapflag 'nosave' not found (file %s), using 'SavePoint'.\n",savemap,current_file);
- map[m].save.x = -1;
- map[m].save.y = -1;
- }
- }
- map[m].flag.nosave = state;
- }
- else if (strcmpi(w3,"nomemo")==0) {
- map[m].flag.nomemo=state;
- }
- else if (strcmpi(w3,"noteleport")==0) {
- map[m].flag.noteleport=state;
- }
- else if (strcmpi(w3,"nowarp")==0) {
- map[m].flag.nowarp=state;
- }
- else if (strcmpi(w3,"nowarpto")==0) {
- map[m].flag.nowarpto=state;
- }
- else if (strcmpi(w3,"noreturn")==0) {
- map[m].flag.noreturn=state;
- }
- else if (strcmpi(w3,"monster_noteleport")==0) {
- map[m].flag.monster_noteleport=state;
- }
- else if (strcmpi(w3,"nobranch")==0) {
- map[m].flag.nobranch=state;
- }
- else if (strcmpi(w3,"nopenalty")==0) {
- map[m].flag.noexppenalty=state;
- map[m].flag.nozenypenalty=state;
- }
- else if (strcmpi(w3,"pvp")==0) {
- map[m].flag.pvp=state;
- if (state) {
- map[m].flag.gvg=0;
- map[m].flag.gvg=0;
- map[m].flag.gvg_dungeon=0;
- map[m].flag.gvg_castle=0;
- }
- }
- else if (strcmpi(w3,"pvp_noparty")==0) {
- map[m].flag.pvp_noparty=state;
- }
- else if (strcmpi(w3,"pvp_noguild")==0) {
- map[m].flag.pvp_noguild=state;
- }
- else if (strcmpi(w3, "pvp_nightmaredrop") == 0) {
- char drop_arg1[16], drop_arg2[16];
- int drop_id = 0, drop_type = 0, drop_per = 0;
- if (sscanf(w4, "%[^,],%[^,],%d", drop_arg1, drop_arg2, &drop_per) == 3) {
- int i;
- if (strcmp(drop_arg1, "random") == 0)
- drop_id = -1;
- else if (itemdb_exists((drop_id = atoi(drop_arg1))) == NULL)
- drop_id = 0;
- if (strcmp(drop_arg2, "inventory") == 0)
- drop_type = 1;
- else if (strcmp(drop_arg2,"equip") == 0)
- drop_type = 2;
- else if (strcmp(drop_arg2,"all") == 0)
- drop_type = 3;
-
- if (drop_id != 0){
- for (i = 0; i < MAX_DROP_PER_MAP; i++) {
- if (map[m].drop_list[i].drop_id == 0){
- map[m].drop_list[i].drop_id = drop_id;
- map[m].drop_list[i].drop_type = drop_type;
- map[m].drop_list[i].drop_per = drop_per;
- break;
- }
- }
- map[m].flag.pvp_nightmaredrop = 1;
- }
- } else if (!state) //Disable
- map[m].flag.pvp_nightmaredrop = 0;
- }
- else if (strcmpi(w3,"pvp_nocalcrank")==0) {
- map[m].flag.pvp_nocalcrank=state;
- }
- else if (strcmpi(w3,"gvg")==0) {
- map[m].flag.gvg=state;
- if (state) map[m].flag.pvp=0;
- }
- else if (strcmpi(w3,"gvg_noparty")==0) {
- map[m].flag.gvg_noparty=state;
- }
- else if (strcmpi(w3,"gvg_dungeon")==0) {
- map[m].flag.gvg_dungeon=state;
- if (state) map[m].flag.pvp=0;
- }
- else if (strcmpi(w3,"gvg_castle")==0) {
- map[m].flag.gvg_castle=state;
- if (state) map[m].flag.pvp=0;
- }
- else if (strcmpi(w3,"noexppenalty")==0) {
- map[m].flag.noexppenalty=state;
- }
- else if (strcmpi(w3,"nozenypenalty")==0) {
- map[m].flag.nozenypenalty=state;
- }
- else if (strcmpi(w3,"notrade")==0) {
- map[m].flag.notrade=state;
- }
- else if (strcmpi(w3,"novending")==0) {
- map[m].flag.novending=state;
- }
- else if (strcmpi(w3,"nodrop")==0) {
- map[m].flag.nodrop=state;
- }
- else if (strcmpi(w3,"noskill")==0) {
- map[m].flag.noskill=state;
- }
- else if (strcmpi(w3,"noicewall")==0) { // noicewall [Valaris]
- map[m].flag.noicewall=state;
- }
- else if (strcmpi(w3,"snow")==0) { // snow [Valaris]
- map[m].flag.snow=state;
- }
- else if (strcmpi(w3,"clouds")==0) {
- map[m].flag.clouds=state;
- }
- else if (strcmpi(w3,"clouds2")==0) { // clouds2 [Valaris]
- map[m].flag.clouds2=state;
- }
- else if (strcmpi(w3,"fog")==0) { // fog [Valaris]
- map[m].flag.fog=state;
- }
- else if (strcmpi(w3,"fireworks")==0) {
- map[m].flag.fireworks=state;
- }
- else if (strcmpi(w3,"sakura")==0) { // sakura [Valaris]
- map[m].flag.sakura=state;
- }
- else if (strcmpi(w3,"leaves")==0) { // leaves [Valaris]
- map[m].flag.leaves=state;
- }
- else if (strcmpi(w3,"rain")==0) { // rain [Valaris]
- map[m].flag.rain=state;
- }
- else if (strcmpi(w3,"indoors")==0) { // celest
- map[m].flag.indoors=state;
- }
- else if (strcmpi(w3,"nightenabled")==0) { // Skotlex
- map[m].flag.nightenabled=state;
- }
- else if (strcmpi(w3,"nogo")==0) { // celest
- map[m].flag.nogo=state;
- }
- else if (strcmpi(w3,"noexp")==0) { // Lorky
- map[m].flag.nobaseexp=state;
- map[m].flag.nojobexp=state;
- }
- else if (strcmpi(w3,"nobaseexp")==0) { // Lorky
- map[m].flag.nobaseexp=state;
- }
- else if (strcmpi(w3,"nojobexp")==0) { // Lorky
- map[m].flag.nojobexp=state;
- }
- else if (strcmpi(w3,"noloot")==0) { // Lorky
- map[m].flag.nomobloot=state;
- map[m].flag.nomvploot=state;
- }
- else if (strcmpi(w3,"nomobloot")==0) { // Lorky
- map[m].flag.nomobloot=state;
- }
- else if (strcmpi(w3,"nomvploot")==0) { // Lorky
- map[m].flag.nomvploot=state;
- }
- else if (strcmpi(w3,"nocommand")==0) { // Skotlex
- if (state) {
- if (sscanf(w4, "%d", &state) == 1)
- map[m].nocommand =state;
- else //No level specified, block everyone.
- map[m].nocommand =100;
- } else
- map[m].nocommand=0;
- }
- else if (strcmpi(w3,"restricted")==0) { // Komurka
- if (state) {
- map[m].flag.restricted=1;
- sscanf(w4, "%d", &state);
- map[m].zone |= 1<<(state+1);
- } else {
- map[m].flag.restricted=0;
- map[m].zone = 0;
- }
- }
- else if (strcmpi(w3,"jexp")==0) {
- map[m].jexp = (state) ? atoi(w4) : 100;
- if( map[m].jexp < 0 ) map[m].jexp = 100;
- map[m].flag.nojobexp = (map[m].jexp==0)?1:0;
- }
- else if (strcmpi(w3,"bexp")==0) {
- map[m].bexp = (state) ? atoi(w4) : 100;
- if( map[m].bexp < 0 ) map[m].bexp = 100;
- map[m].flag.nobaseexp = (map[m].bexp==0)?1:0;
- }
- else if (strcmpi(w3,"loadevent")==0) { // Skotlex
- map[m].flag.loadevent=state;
- }
- else if (strcmpi(w3,"nochat")==0) { // Skotlex
- map[m].flag.nochat=state;
- }
- else if (strcmpi(w3,"partylock")==0) { // Skotlex
- map[m].flag.partylock=state;
- }
- else if (strcmpi(w3,"guildlock")==0) { // Skotlex
- map[m].flag.guildlock=state;
- }
-
- return 0;
-}
-
-/*==========================================
- * Setting up map cells
- *------------------------------------------
- */
-static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4)
-{
- int m, cell, x, y, x0, y0, x1, y1;
- char type[24], mapname[MAP_NAME_LENGTH];
-
- if (sscanf(w1, "%15[^,]", mapname) != 1)
- return 1;
-
- m = map_mapname2mapid(mapname);
- if (m < 0)
- return 1;
-
- if (sscanf(w3, "%23[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 4) {
- ShowError("Bad setcell line : %s\n",w3);
- return 1;
- }
- cell = strtol(type, (char **)NULL, 0);
- //printf ("0x%x %d %d %d %d\n", cell, x0, y0, x1, y1);
-
- if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
- if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
-
- for (x = x0; x <= x1; x++) {
- for (y = y0; y <= y1; y++) {
- map_setcell(m, x, y, cell);
- //printf ("setcell 0x%x %d %d %d\n", cell, m, x, y);
- }
- }
-
- return 0;
-}
-
-void npc_parsesrcfile (char *name)
-{
- int m, lines = 0;
- char line[2048];
-
- FILE *fp = fopen (name,"r");
- if (fp == NULL) {
- ShowError ("File not found : %s\n", name);
- return;
- }
- current_file = name;
-
- while (fgets(line, sizeof(line) - 1, fp)) {
- char w1[2048], w2[2048], w3[2048], w4[2048], mapname[2048];
- int i, w4pos, count;
- lines++;
-
- if (line[0] == '/' && line[1] == '/')
- continue;
-
- if (!sscanf(line, " %n", &i) && i == strlen(line)) // just whitespace
- continue;
-
- // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認
- w1[0] = w2[0] = w3[0] = w4[0] = '\0'; //It's best to initialize values
- //to prevent passing previously parsed values to the parsers when not all
- //fields are specified. [Skotlex]
- if ((count = sscanf(line, "%[^\t\r\n]\t%[^\t\r\n]\t%[^\t\r\n]\t%n%[^\r\n]", w1, w2, w3, &w4pos, w4)) < 3)
- {
- if ((count = sscanf(line, "%s %s %[^\t]\t %n%[^\n]", w1, w2, w3, &w4pos, w4)) == 4 ||
- (count = sscanf(line, "%s %s %s %n%[^\n]\n", w1, w2, w3, &w4pos, w4)) >= 3)
- {
- ShowWarning("\r");
- ShowWarning("Incorrect separator syntax in file '%s', line '%i'. Use tabs instead of spaces!\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
- } else {
- ShowError("\r"); //Erase the npc spinner.
- ShowError("Could not parse file '%s', line '%i'.\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
- continue;
- }
- }
-
- // マップの存在確認
- if (strcmp(w1,"-") !=0 && strcmpi(w1,"function") != 0 ){
- sscanf(w1,"%[^,]",mapname);
- if (!mapindex_name2id(mapname)) { //Incorrect map
- ShowError("Invalid map '%s' in line %d, file %s\n", mapname, lines, current_file);
- if (strcmpi(w2,"script") == 0 && count > 3) //we must skip the script info...
- npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
- continue;
- }
- if ((m = map_mapname2mapid(mapname)) < 0) {
- // "mapname" is not assigned to this server
- // we must skip the script info...
- if (strcmpi(w2,"script") == 0 && count > 3)
- npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
- continue;
- }
- }
- if (strcmpi(w2,"warp") == 0 && count > 3) {
- npc_parse_warp(w1,w2,w3,w4);
- } else if (strcmpi(w2,"shop") == 0 && count > 3) {
- npc_parse_shop(w1,w2,w3,w4);
- } else if (strcmpi(w2,"script") == 0 && count > 3) {
- if (strcmpi(w1,"function") == 0) {
- npc_parse_function(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
- } else {
- npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
- }
- } else if ((i = 0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3) {
- npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
- } else if (strcmpi(w2,"monster") == 0 && count > 3) {
- npc_parse_mob(w1,w2,w3,w4);
- } else if (strcmpi(w2,"mapflag") == 0 && count >= 3) {
- npc_parse_mapflag(w1,w2,w3,w4);
- } else if (strcmpi(w2,"setcell") == 0 && count >= 3) {
- npc_parse_mapcell(w1,w2,w3,w4);
- } else {
- ShowError("Probably TAB is missing in file '%s', line '%i':\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
- }
- }
- fclose(fp);
-
- return;
-}
-
-int npc_script_event(TBL_PC* sd, int type) {
- int i;
- if (type < 0 || type >= NPCE_MAX)
- return 0;
- if (!sd) {
- if (battle_config.error_log)
- ShowError("npc_script_event: NULL sd. Event Type %d\n", type);
- return 0;
- }
- if (script_event[type].nd) {
- TBL_NPC *nd = script_event[type].nd;
- run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id);
- return 1;
- } else if (script_event[type].event_count) {
- for (i = 0; i<script_event[type].event_count; i++) {
- npc_event_sub(sd,script_event[type].event[i],script_event[type].event_name[i]);
- }
- return i;
- }
- return 0;
-}
-
-static int npc_read_event_script_sub(DBKey key,void *data,va_list ap)
-{
- unsigned char *p = key.str;
- unsigned char *name = va_arg(ap,unsigned char *);
- struct event_data **event_buf = va_arg(ap,struct event_data**);
- unsigned char **event_name = va_arg(ap,unsigned char **);
- unsigned char *count = va_arg(ap,char *);;
-
- if (*count >= UCHAR_MAX) return 0;
-
- if((p=strchr(p,':')) && p && strcmpi(name,p)==0 )
- {
- event_buf[*count] = (struct event_data *)data;
- event_name[*count] = key.str;
- (*count)++;
- return 1;
- }
- return 0;
-}
-
-static void npc_read_event_script(void)
-{
- int i;
- unsigned char buf[64]="::";
- struct {
- char *name;
- char *event_name;
- } config[] = {
- {"Login Event",script_config.login_event_name},
- {"Logout Event",script_config.logout_event_name},
- {"Load Map Event",script_config.loadmap_event_name},
- {"Base LV Up Event",script_config.baselvup_event_name},
- {"Job LV Up Event",script_config.joblvup_event_name},
- {"Die Event",script_config.die_event_name},
- {"Kill PC Event",script_config.kill_pc_event_name},
- {"Kill NPC Event",script_config.kill_mob_event_name},
- };
-
- for (i = 0; i < NPCE_MAX; i++) {
- if (script_event[i].nd)
- script_event[i].nd = NULL;
- if (script_event[i].event_count)
- script_event[i].event_count = 0;
- if (!script_config.event_script_type) {
- //Use a single NPC as event source.
- script_event[i].nd = npc_name2id(config[i].event_name);
- } else {
- //Use an array of Events
- strncpy(buf+2,config[i].event_name,62);
- ev_db->foreach(ev_db,npc_read_event_script_sub,buf,
- &script_event[i].event,
- &script_event[i].event_name,
- &script_event[i].event_count);
- }
- }
- if (battle_config.etc_log) {
- //Print summary.
- for (i = 0; i < NPCE_MAX; i++) {
- if(!script_config.event_script_type) {
- if (script_event[i].nd)
- ShowInfo("%s: Using NPC named '%s'.\n", config[i].name, config[i].event_name);
- else
- ShowInfo("%s: No NPC found with name '%s'.\n", config[i].name, config[i].event_name);
- } else
- ShowInfo("%s: %d '%s' events.\n", config[i].name, script_event[i].event_count, config[i].event_name);
- }
- }
-}
-static int npc_read_indoors (void)
-{
- char *buf, *p;
- int s, m;
-
- buf = (char *)grfio_reads("data\\indoorrswtable.txt",&s);
- if (buf == NULL)
- return -1;
- buf[s] = 0;
-
- for (p = buf; p - buf < s; ) {
- char map_name[64];
- if (sscanf(p, "%15[^#]#", map_name) == 1) {
- size_t pos = strlen(map_name) - 4; // replace '.xxx' extension
- memcpy(map_name+pos,".gat",4); // with '.gat'
- if ((m = map_mapname2mapid(map_name)) >= 0)
- map[m].flag.indoors = 1;
- }
-
- p = strchr(p, 10);
- if (!p) break;
- p++;
- }
- aFree(buf);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\indoorrswtable.txt");
-
- return 0;
-}
-
-/*==========================================
- *
- *------------------------------------------
- */
-
-static int npc_cleanup_sub (struct block_list *bl, va_list ap) {
- nullpo_retr(0, bl);
-
- switch(bl->type) {
- case BL_NPC:
- npc_unload((struct npc_data *)bl);
- break;
- case BL_MOB:
- //This is used only on reloading npcs, so let's not free spawn-once mobs. [Skotlex]
- if (((TBL_MOB*)bl)->spawn)
- unit_free(bl,0);
- break;
- }
-
- return 0;
-}
-
-static int npc_cleanup_dbsub(DBKey key,void * data,va_list ap) {
- return npc_cleanup_sub((struct block_list*)data, 0);
-}
-
-int npc_reload (void)
-{
- struct npc_src_list *nsl;
- int m, i;
- time_t last_time = time(0);
- int busy = 0, npc_new_min = npc_id;
- char c = '-';
-
- //Remove all npcs/mobs. [Skotlex]
- map_foreachiddb(npc_cleanup_dbsub);
- for (m = 0; m < map_num; m++) {
- if(battle_config.dynamic_mobs) { //dynamic check by [random]
- for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++)
- if (map[m].moblist[i]) aFree(map[m].moblist[i]);
- malloc_set (map[m].moblist, 0, sizeof(map[m].moblist));
- }
- if (map[m].npc_num > 0 && battle_config.error_log)
- ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map[m].npc_num, map[m].name);
- }
-
- // anything else we should cleanup?
- // Reloading npc's now
- ev_db->clear(ev_db,NULL);
- npcname_db->clear(npcname_db,NULL);
- npc_warp = npc_shop = npc_script = 0;
- npc_mob = npc_cache_mob = npc_delay_mob = 0;
-
- for (nsl = npc_src_first; nsl; nsl = nsl->next) {
- npc_parsesrcfile(nsl->name);
- if (script_config.verbose_mode) {
- printf("\r");
- ShowStatus("Loading NPCs... %-53s", nsl->name);
- } else {
- if (last_time != time(0)) {
- printf("\r");
- ShowStatus("Loading NPCs... Working: ");
- last_time = time(0);
- switch(busy) {
- case 0: c='\\'; busy++; break;
- case 1: c='|'; busy++; break;
- case 2: c='/'; busy++; break;
- case 3: c='-'; busy=0;
- }
- printf("[%c]",c);
- }
- }
- fflush(stdout);
- }
- printf("\r");
- ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
- CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
- CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
- CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
- CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
- CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
- CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
- npc_id - npc_new_min, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
-
- //Re-read the NPC Script Events cache.
- npc_read_event_script();
-
- //Execute the OnInit event for freshly loaded npcs. [Skotlex]
- ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
- CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
- // Execute rest of the startup events if connected to char-server. [Lance]
- if(!CheckForCharServer()){
- ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit"));
- ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
- ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce"));
- }
- return 0;
-}
-
-/*==========================================
- * 終了
- *------------------------------------------
- */
-int do_final_npc(void)
-{
- int i;
- struct block_list *bl;
-
- for (i = START_NPC_NUM; i < npc_id; i++){
- if ((bl = map_id2bl(i))){
- if (bl->type == BL_NPC)
- npc_unload((struct npc_data *)bl);
- else if (bl->type&(BL_MOB|BL_PET))
- unit_free(bl, 0);
- }
- }
-
- ev_db->destroy(ev_db, NULL);
- //There is no free function for npcname_db because at this point there shouldn't be any npcs left!
- //So if there is anything remaining, let the memory manager catch it and report it.
- npcname_db->destroy(npcname_db, NULL);
- ers_destroy(timer_event_ers);
- npc_clearsrcfile();
-
- return 0;
-}
-
-static void npc_debug_warps_sub(struct npc_data *nd)
-{
- int m;
- if (nd->bl.type != BL_NPC || nd->bl.subtype != WARP || nd->bl.m < 0)
- return;
-
- m = map_mapindex2mapid(nd->u.warp.mapindex);
- if (m < 0) return; //Warps to another map, nothing to do about it.
-
- if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNPC)) {
- ShowWarning("Warp %s at %s(%d,%d) warps directly on top of an area npc at %s(%d,%d)\n",
- nd->name,
- map[nd->bl.m].name, nd->bl.x, nd->bl.y,
- map[m].name, nd->u.warp.x, nd->u.warp.y
- );
- }
- if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNOPASS)) {
- ShowWarning("Warp %s at %s(%d,%d) warps to a non-walkable tile at %s(%d,%d)\n",
- nd->name,
- map[nd->bl.m].name, nd->bl.x, nd->bl.y,
- map[m].name, nd->u.warp.x, nd->u.warp.y
- );
- }
-}
-
-static void npc_debug_warps(void)
-{
- int m, i;
- for (m = 0; m < map_num; m++)
- for (i = 0; i < map[m].npc_num; i++)
- npc_debug_warps_sub(map[m].npc[i]);
-}
-
-/*==========================================
- * npc初期化
- *------------------------------------------
- */
-int do_init_npc(void)
-{
- struct npc_src_list *nsl;
- time_t last_time = time(0);
- int busy, i;
- char c = '-';
-
- //Stock view data for normal npcs.
- malloc_set(&npc_viewdb, 0, sizeof(npc_viewdb));
- npc_viewdb[0].class_ = INVISIBLE_CLASS; //Invisible class is stored here.
- for (busy = 1; busy < MAX_NPC_CLASS; busy++)
- npc_viewdb[busy].class_ = busy;
- busy = 0;
- // indoorrswtable.txt and etcinfo.txt [Celest]
- if (battle_config.indoors_override_grffile)
- npc_read_indoors();
-
- // comparing only the first 24 chars of labels that are 50 chars long isn't that nice
- // will cause "duplicated" labels where actually no dup is...
- ev_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,51);
- npcname_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_BASE,NAME_LENGTH);
-
- malloc_set(&ev_tm_b, -1, sizeof(ev_tm_b));
- timer_event_ers = ers_new((uint32)sizeof(struct timer_event_data));
-
- for (nsl = npc_src_first; nsl; nsl = nsl->next) {
- npc_parsesrcfile(nsl->name);
- current_file = NULL;
- printf("\r");
- if (script_config.verbose_mode)
- ShowStatus ("Loading NPCs... %-53s", nsl->name);
- else {
- ShowStatus("Loading NPCs... Working: ");
- if (last_time != time(0)) {
- last_time = time(0);
- switch(busy) {
- case 0: c='\\'; busy++; break;
- case 1: c='|'; busy++; break;
- case 2: c='/'; busy++; break;
- case 3: c='-'; busy=0;
- }
- }
- printf("[%c]",c);
- }
- fflush(stdout);
- }
- printf("\r");
- ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
- CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
- CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
- CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
- CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
- CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
- CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
- npc_id - START_NPC_NUM, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
-
- malloc_set(script_event, 0, sizeof(script_event));
- npc_read_event_script();
- //Debug function to locate all endless loop warps.
- if (battle_config.warp_point_debug)
- npc_debug_warps();
-
- add_timer_func_list(npc_event_timer,"npc_event_timer");
- add_timer_func_list(npc_event_do_clock,"npc_event_do_clock");
- add_timer_func_list(npc_timerevent,"npc_timerevent");
-
- // Init dummy NPC
- fake_nd = (struct npc_data *)aCalloc(sizeof(struct npc_data),1);
- fake_nd->bl.prev = fake_nd->bl.next = NULL;
- fake_nd->bl.m = -1;
- fake_nd->bl.x = 0;
- fake_nd->bl.y = 0;
- fake_nd->bl.id = npc_get_new_npc_id();
- fake_nd->class_ = -1;
- fake_nd->speed = 200;
- fake_nd->u.scr.script = NULL;
- fake_nd->u.scr.src_id = 0;
- fake_nd->chatdb = NULL;
- for (i = 0; i < MAX_EVENTTIMER; i++)
- fake_nd->eventtimer[i] = -1;
- strcpy(fake_nd->name,"FAKE_NPC");
- memcpy(fake_nd->exname, fake_nd->name, 9);
-
- npc_script++;
- fake_nd->bl.type = BL_NPC;
- fake_nd->bl.subtype = SCRIPT;
-
- strdb_put(npcname_db, fake_nd->exname, fake_nd);
- fake_nd->u.scr.timerid = -1;
- map_addiddb(&fake_nd->bl);
- // End of initialization
-
- return 0;
-}
-// [Lance]
-int npc_changename(const char *name, const char *newname, short look){
- struct npc_data *nd= (struct npc_data *) strdb_remove(npcname_db,(unsigned char*)name);
- if (nd==NULL)
- return 0;
- npc_enable(name,0);
- strcpy(nd->name,newname);
- nd->class_ = look;
- npc_enable(newname,1);
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <limits.h>
+
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/grfio.h"
+#include "../common/showmsg.h"
+#include "../common/ers.h"
+#include "../common/db.h"
+#include "map.h"
+#include "log.h"
+#include "npc.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "status.h"
+#include "itemdb.h"
+#include "script.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "skill.h"
+#include "unit.h"
+
+#ifdef _WIN32
+#undef isspace
+#define isspace(x) (x == ' ' || x == '\t')
+#endif
+
+struct npc_src_list {
+ struct npc_src_list * next;
+// struct npc_src_list * prev; //[Shinomori]
+ char name[4];
+};
+
+static struct npc_src_list *npc_src_first=NULL;
+static struct npc_src_list *npc_src_last=NULL;
+static int npc_id=START_NPC_NUM;
+static int npc_warp=0;
+static int npc_shop=0;
+static int npc_script=0;
+static int npc_mob=0;
+static int npc_delay_mob=0;
+static int npc_cache_mob=0;
+char *current_file = NULL;
+int npc_get_new_npc_id(void){ return npc_id++; }
+
+static struct dbt *ev_db;
+static struct dbt *npcname_db;
+
+struct event_data {
+ struct npc_data *nd;
+ int pos;
+};
+static struct tm ev_tm_b; // 時計イベント用
+
+static struct eri *timer_event_ers; //For the npc timer data. [Skotlex]
+
+//For holding the view data of npc classes. [Skotlex]
+static struct view_data npc_viewdb[MAX_NPC_CLASS];
+
+static struct
+{ //Holds pointers to the commonly executed scripts for speedup. [Skotlex]
+ struct npc_data *nd;
+ struct event_data *event[UCHAR_MAX];
+ unsigned char *event_name[UCHAR_MAX];
+ unsigned char event_count;
+} script_event[NPCE_MAX];
+
+struct view_data* npc_get_viewdata(int class_)
+{ //Returns the viewdata for normal npc classes.
+ if (class_ == INVISIBLE_CLASS)
+ return &npc_viewdb[0];
+ if (npcdb_checkid(class_) || class_ == WARP_CLASS)
+ return &npc_viewdb[class_];
+ return NULL;
+}
+/*==========================================
+ * NPCの無効化/有効化
+ * npc_enable
+ * npc_enable_sub 有効時にOnTouchイベントを実行
+ *------------------------------------------
+ */
+int npc_enable_sub( struct block_list *bl, va_list ap )
+{
+ struct map_session_data *sd;
+ struct npc_data *nd;
+ //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
+
+ nullpo_retr(0, bl);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd=va_arg(ap,struct npc_data *));
+ if(bl->type == BL_PC && (sd=(struct map_session_data *)bl)){
+ char name[50]; // need 24 + 9 for the "::OnTouch"
+
+ if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている
+ return 1;
+
+ if(sd->areanpc_id==nd->bl.id)
+ return 1;
+ sd->areanpc_id=nd->bl.id;
+
+ snprintf(name, 50, "%s::OnTouch", nd->exname); // exname to be specific. exname is the unique identifier for script events. [Lance]
+ npc_event(sd,name,0);
+ }
+ //aFree(name);
+ return 0;
+}
+int npc_enable(const char *name,int flag)
+{
+ struct npc_data *nd= strdb_get(npcname_db,(unsigned char*)name);
+ if (nd==NULL)
+ return 0;
+
+ if (flag&1)
+ nd->sc.option&=~OPTION_INVISIBLE;
+ else if (flag&2)
+ nd->sc.option&=~OPTION_HIDE;
+ else if (flag&4)
+ nd->sc.option|= OPTION_HIDE;
+ else //Can't change the view_data to invisible class because the view_data for all npcs is shared! [Skotlex]
+ nd->sc.option|= OPTION_INVISIBLE;
+
+ if (nd->class_ == WARP_CLASS)
+ { //Client won't display option changes for warp portals [Toms]
+ if (nd->sc.option&(OPTION_HIDE|OPTION_INVISIBLE))
+ clif_clearchar(&nd->bl, 0);
+ else
+ clif_spawn(&nd->bl);
+ } else
+ clif_changeoption(&nd->bl);
+
+ if(flag&3 && (nd->u.scr.xs > 0 || nd->u.scr.ys >0))
+ map_foreachinarea( npc_enable_sub,nd->bl.m,nd->bl.x-nd->u.scr.xs,nd->bl.y-nd->u.scr.ys,nd->bl.x+nd->u.scr.xs,nd->bl.y+nd->u.scr.ys,BL_PC,nd);
+
+ return 0;
+}
+
+/*==========================================
+ * NPCを名前で探す
+ *------------------------------------------
+ */
+struct npc_data* npc_name2id(const char *name)
+{
+ return (struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
+}
+
+/*==========================================
+ * イベントキューのイベント処理
+ *------------------------------------------
+ */
+int npc_event_dequeue(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ if (!sd->eventqueue[0][0])
+ return 0; //Nothing to dequeue
+
+ if (!pc_addeventtimer(sd,100,sd->eventqueue[0]))
+ { //Failed to dequeue, couldn't set a timer.
+ ShowWarning("npc_event_dequeue: event timer is full !\n");
+ return 0;
+ }
+ //Event dequeued successfully, shift other elements.
+ sd->npc_id=0; //FIXME: Shouldn't dequeueing fail when you have an npc_id set?
+ memmove(sd->eventqueue[0], sd->eventqueue[1], (MAX_EVENTQUEUE-1)*sizeof(sd->eventqueue[0]));
+ sd->eventqueue[MAX_EVENTQUEUE-1][0]=0;
+ return 1;
+}
+
+/*==========================================
+ * イベントの遅延実行
+ *------------------------------------------
+ */
+int npc_event_timer(int tid,unsigned int tick,int id,int data)
+{
+ unsigned char *eventname = (unsigned char *)data;
+ struct event_data *ev = strdb_get(ev_db,eventname);
+ struct npc_data *nd;
+ struct map_session_data *sd=map_id2sd(id);
+ size_t i;
+
+ if((ev==NULL || (nd=ev->nd)==NULL))
+ {
+ if(battle_config.error_log)
+ ShowWarning("npc_event: event not found [%s]\n",eventname);
+ }
+ else
+ {
+ for(i=0;i<MAX_EVENTTIMER;i++) {
+ if( nd->eventtimer[i]==tid ) {
+ nd->eventtimer[i]=-1;
+ npc_event(sd,eventname,0); // sd NULL check is within
+ break;
+ }
+ }
+ if(i==MAX_EVENTTIMER && battle_config.error_log)
+ ShowWarning("npc_event_timer: event timer not found [%s]!\n",eventname);
+ }
+
+ aFree(eventname);
+ return 0;
+}
+
+int npc_timer_event(const unsigned char *eventname) // Added by RoVeRT
+{
+ struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
+ struct npc_data *nd;
+// int xs,ys;
+
+ if((ev==NULL || (nd=ev->nd)==NULL)){
+ ShowWarning("npc_timer_event: event not found [%s]\n",eventname);
+ return 0;
+ }
+
+ run_script(nd->u.scr.script,ev->pos,nd->bl.id,nd->bl.id);
+
+ return 0;
+}
+/*
+int npc_timer_sub_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
+{
+ char *p=(char *)key;
+ struct event_data *ev=(struct event_data *)data;
+ int *c=va_arg(ap,int *);
+ int tick=0,ctick=gettick();
+ char temp[10];
+ char event[100];
+
+ if(ev->nd->bl.id==(int)*c && (p=strchr(p,':')) && p && strncasecmp("::OnTimer",p,8)==0 ){
+ sscanf(&p[9],"%s",temp);
+ tick=atoi(temp);
+
+ strcpy( event, ev->nd->name);
+ strcat( event, p);
+
+ if (ctick >= ev->nd->lastaction && ctick - ev->nd->timer >= tick) {
+ npc_timer_event(event);
+ ev->nd->lastaction = ctick;
+ }
+ }
+ return 0;
+}
+
+int npc_timer_sub(DBKey key,void *data,va_list ap) // Added by RoVeRT
+{
+ struct npc_data *nd=(struct npc_data*)data;
+
+ if(nd->timer == -1)
+ return 0;
+
+ sv_db->foreach(ev_db,npc_timer_sub_sub,&nd->bl.id);
+
+ return 0;
+}
+
+int npc_timer(int tid,unsigned int tick,int id,int data) // Added by RoVeRT
+{
+ npcname_db->foreach(npcname_db,npc_timer_sub);
+
+ aFree((void*)data);
+ return 0;
+}*/
+/*==========================================
+ * イベント用ラベルのエクスポート
+ * npc_parse_script->strdb_foreachから呼ばれる
+ *------------------------------------------
+ */
+int npc_event_export(char *lname,void *data,va_list ap)
+{
+ int pos=(int)data;
+ struct npc_data *nd=va_arg(ap,struct npc_data *);
+
+ if ((lname[0]=='O' || lname[0]=='o')&&(lname[1]=='N' || lname[1]=='n')) {
+ struct event_data *ev;
+ unsigned char buf[51];
+ char *p=strchr(lname,':');
+ // エクスポートされる
+ ev=(struct event_data *) aMalloc(sizeof(struct event_data));
+ if (ev==NULL) {
+ ShowFatalError("npc_event_export: out of memory !\n");
+ exit(1);
+ }else if (p==NULL || (p-lname)>NAME_LENGTH) {
+ ShowFatalError("npc_event_export: label name error !\n");
+ exit(1);
+ }else{
+ ev->nd=nd;
+ ev->pos=pos;
+ *p='\0';
+ sprintf(buf,"%s::%s",nd->exname,lname);
+ *p=':';
+ strdb_put(ev_db,buf,ev);
+ }
+ }
+ return 0;
+}
+
+int npc_event_sub(struct map_session_data *, struct event_data *, const unsigned char *); //[Lance]
+/*==========================================
+ * 全てのNPCのOn*イベント実行
+ *------------------------------------------
+ */
+int npc_event_doall_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char*p = key.str;
+ struct event_data *ev;
+ int *c;
+ int rid;
+ unsigned char *name;
+
+ ev=(struct event_data *)data;
+ c=va_arg(ap,int *);
+ name=va_arg(ap,unsigned char *);
+ rid=va_arg(ap, int);
+
+ if( (p=strchr(p,':')) && p && strcmpi(name,p)==0 ){
+ if(rid)
+ npc_event_sub(((struct map_session_data *)map_id2bl(rid)),ev,key.str);
+ else
+ run_script(ev->nd->u.scr.script,ev->pos,rid,ev->nd->bl.id);
+ (*c)++;
+ }
+
+ return 0;
+}
+int npc_event_doall(const unsigned char *name)
+{
+ int c=0;
+ unsigned char buf[64]="::";
+
+ strncpy(buf+2,name,62);
+ ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,0);
+ return c;
+}
+int npc_event_doall_id(const unsigned char *name, int rid)
+{
+ int c=0;
+ unsigned char buf[64]="::";
+
+ strncpy(buf+2,name,62);
+ ev_db->foreach(ev_db,npc_event_doall_sub,&c,buf,rid);
+ return c;
+}
+
+int npc_event_do_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev;
+ int *c;
+ const unsigned char *name;
+
+ nullpo_retr(0, ev=(struct event_data *)data);
+ nullpo_retr(0, ap);
+ nullpo_retr(0, c=va_arg(ap,int *));
+
+ name=va_arg(ap,const unsigned char *);
+
+ if (p && strcmpi(name,p)==0 ) {
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ (*c)++;
+ }
+
+ return 0;
+}
+int npc_event_do(const unsigned char *name)
+{
+ int c=0;
+
+ if (*name==':' && name[1]==':') {
+ return npc_event_doall(name+2);
+ }
+
+ ev_db->foreach(ev_db,npc_event_do_sub,&c,name);
+ return c;
+}
+
+/*==========================================
+ * 時計イベント実行
+ *------------------------------------------
+ */
+int npc_event_do_clock(int tid,unsigned int tick,int id,int data)
+{
+ time_t timer;
+ struct tm *t;
+ char buf[64];
+ char *day="";
+ int c=0;
+
+ time(&timer);
+ t=localtime(&timer);
+
+ switch (t->tm_wday) {
+ case 0: day = "Sun"; break;
+ case 1: day = "Mon"; break;
+ case 2: day = "Tue"; break;
+ case 3: day = "Wed"; break;
+ case 4: day = "Thu"; break;
+ case 5: day = "Fri"; break;
+ case 6: day = "Sat"; break;
+ }
+
+ if (t->tm_min != ev_tm_b.tm_min ) {
+ sprintf(buf,"OnMinute%02d",t->tm_min);
+ c+=npc_event_doall(buf);
+ sprintf(buf,"OnClock%02d%02d",t->tm_hour,t->tm_min);
+ c+=npc_event_doall(buf);
+ sprintf(buf,"On%s%02d%02d",day,t->tm_hour,t->tm_min);
+ c+=npc_event_doall(buf);
+ }
+ if (t->tm_hour!= ev_tm_b.tm_hour) {
+ sprintf(buf,"OnHour%02d",t->tm_hour);
+ c+=npc_event_doall(buf);
+ }
+ if (t->tm_mday!= ev_tm_b.tm_mday) {
+ sprintf(buf,"OnDay%02d%02d",t->tm_mon+1,t->tm_mday);
+ c+=npc_event_doall(buf);
+ }
+ memcpy(&ev_tm_b,t,sizeof(ev_tm_b));
+ return c;
+}
+/*==========================================
+ * OnInitイベント実行(&時計イベント開始)
+ *------------------------------------------
+ */
+int npc_event_do_oninit(void)
+{
+// int c = npc_event_doall("OnInit");
+ ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
+ CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
+
+ add_timer_interval(gettick()+100,
+ npc_event_do_clock,0,0,1000);
+
+ return 0;
+}
+/*==========================================
+ * OnTimer NPC event - by RoVeRT
+ *------------------------------------------
+ */
+int npc_addeventtimer(struct npc_data *nd,int tick,const char *name)
+{
+ int i;
+ unsigned char *evname;
+
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]==-1 )
+ break;
+ if(i<MAX_EVENTTIMER){
+ if (!strdb_get(ev_db,(unsigned char*)name)) {
+ if (battle_config.error_log)
+ ShowError("npc_addeventimer: Event %s does not exists.\n", name);
+ return 1; //Event does not exists!
+ }
+ evname =(unsigned char *) aMallocA(NAME_LENGTH*sizeof(char));
+ if(evname==NULL){
+ ShowFatalError("npc_addeventtimer: out of memory !\n");exit(1);
+ }
+ memcpy(evname,name,NAME_LENGTH-1);
+ evname[NAME_LENGTH-1] = '\0';
+ nd->eventtimer[i]=add_timer(gettick()+tick,
+ npc_event_timer,nd->bl.id,(int)evname);
+ }else
+ ShowWarning("npc_addtimer: event timer is full !\n");
+
+ return 0;
+}
+
+int npc_deleventtimer(struct npc_data *nd,const unsigned char *name)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]!=-1 && strcmp(
+ (unsigned char *)(get_timer(nd->eventtimer[i])->data), name)==0 ){
+ delete_timer(nd->eventtimer[i],npc_event_timer);
+ nd->eventtimer[i]=-1;
+ break;
+ }
+
+ return 0;
+}
+
+int npc_cleareventtimer(struct npc_data *nd)
+{
+ int i;
+ for(i=0;i<MAX_EVENTTIMER;i++)
+ if( nd->eventtimer[i]!=-1 ){
+ delete_timer(nd->eventtimer[i],npc_event_timer);
+ nd->eventtimer[i]=-1;
+ }
+
+ return 0;
+}
+
+int npc_do_ontimer_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev = (struct event_data *)data;
+ int *c = va_arg(ap,int *);
+// struct map_session_data *sd=va_arg(ap,struct map_session_data *);
+ int option = va_arg(ap,int);
+ int tick = 0;
+ char temp[10];
+ char event[50];
+
+ if(ev->nd->bl.id == (int)*c && (p = strchr(p,':')) && strnicmp("::OnTimer",p,8) == 0){
+ sscanf(&p[9], "%s", temp);
+ tick = atoi(temp);
+
+ strcpy(event, ev->nd->name);
+ strcat(event, p);
+
+ if (option!=0) {
+ npc_addeventtimer(ev->nd, tick, event);
+ } else {
+ npc_deleventtimer(ev->nd, event);
+ }
+ }
+ return 0;
+}
+int npc_do_ontimer(int npc_id, int option)
+{
+ ev_db->foreach(ev_db, npc_do_ontimer_sub, &npc_id, option);
+ return 0;
+}
+/*==========================================
+ * タイマーイベント用ラベルの取り込み
+ * npc_parse_script->strdb_foreachから呼ばれる
+ *------------------------------------------
+ */
+int npc_timerevent_import(char *lname,void *data,va_list ap)
+{
+ int pos=(int)data;
+ struct npc_data *nd=va_arg(ap,struct npc_data *);
+ int t=0,i=0;
+
+ if(sscanf(lname,"OnTimer%d%n",&t,&i)==1 && lname[i]==':') {
+ // タイマーイベント
+ struct npc_timerevent_list *te=nd->u.scr.timer_event;
+ int j,i=nd->u.scr.timeramount;
+ if(te==NULL) te=(struct npc_timerevent_list*)aMallocA(sizeof(struct npc_timerevent_list));
+ else te= (struct npc_timerevent_list*)aRealloc( te, sizeof(struct npc_timerevent_list) * (i+1) );
+ if(te==NULL){
+ ShowFatalError("npc_timerevent_import: out of memory !\n");
+ exit(1);
+ }
+ for(j=0;j<i;j++){
+ if(te[j].timer>t){
+ memmove(te+j+1,te+j,sizeof(struct npc_timerevent_list)*(i-j));
+ break;
+ }
+ }
+ te[j].timer=t;
+ te[j].pos=pos;
+ nd->u.scr.timer_event=te;
+ nd->u.scr.timeramount++;
+ }
+ return 0;
+}
+struct timer_event_data {
+ int rid; //Attached player for this timer.
+ int next; //timer index (starts with 0, then goes up to nd->u.scr.timeramount
+ int time; //holds total time elapsed for the script since time 0 (whenthe timers started)
+ unsigned int otick; //Holds tick value at which timer sequence was started (that is, it stores the tick value for which T= 0
+};
+
+/*==========================================
+ * タイマーイベント実行
+ *------------------------------------------
+ */
+int npc_timerevent(int tid,unsigned int tick,int id,int data)
+{
+ int next,t,old_rid,old_timer;
+ unsigned int old_tick;
+ struct npc_data* nd=(struct npc_data *)map_id2bl(id);
+ struct npc_timerevent_list *te;
+ struct timer_event_data *ted = (struct timer_event_data*)data;
+ struct map_session_data *sd=NULL;
+
+ if( nd==NULL ){
+ ShowError("npc_timerevent: NPC not found??\n");
+ return 0;
+ }
+ if (ted->rid) {
+ sd = map_id2sd(ted->rid);
+ if (!sd) {
+ if(battle_config.error_log)
+ ShowError("npc_timerevent: Attached player not found.\n");
+ ers_free(timer_event_ers, ted);
+ return 0;
+ }
+ }
+ old_rid = nd->u.scr.rid; //To restore it later.
+ nd->u.scr.rid = sd?sd->bl.id:0;
+
+ old_tick = nd->u.scr.timertick;
+ nd->u.scr.timertick=ted->otick;
+ te=nd->u.scr.timer_event+ ted->next;
+
+ old_timer = nd->u.scr.timer;
+ t = nd->u.scr.timer=ted->time;
+ ted->next++;
+
+ if( nd->u.scr.timeramount> ted->next){
+ next= nd->u.scr.timer_event[ ted->next ].timer
+ - nd->u.scr.timer_event[ ted->next-1 ].timer;
+ ted->time+=next;
+ if (sd)
+ sd->npc_timer_id = add_timer(tick+next,npc_timerevent,id,(int)ted);
+ else
+ nd->u.scr.timerid = add_timer(tick+next,npc_timerevent,id,(int)ted);
+ } else {
+ if (sd)
+ sd->npc_timer_id = -1;
+ else
+ nd->u.scr.timerid = -1;
+ ers_free(timer_event_ers, ted);
+ }
+ run_script(nd->u.scr.script,te->pos,nd->u.scr.rid,nd->bl.id);
+ //Restore previous data, only if this timer is a player-attached one.
+ if (sd) {
+ nd->u.scr.rid = old_rid;
+ nd->u.scr.timer = old_timer;
+ nd->u.scr.timertick = old_tick;
+ }
+ return 0;
+}
+/*==========================================
+ * タイマーイベント開始
+ *------------------------------------------
+ */
+int npc_timerevent_start(struct npc_data *nd, int rid)
+{
+ int j,n, next;
+ struct map_session_data *sd=NULL; //Player to whom script is attached.
+ struct timer_event_data *ted;
+
+ nullpo_retr(0, nd);
+
+ n=nd->u.scr.timeramount;
+ if( n==0 )
+ return 0;
+
+ for(j=0;j<n;j++){
+ if( nd->u.scr.timer_event[j].timer > nd->u.scr.timer )
+ break;
+ }
+ if(j>=n) // check if there is a timer to use !!BEFORE!! you write stuff to the structures [Shinomori]
+ return 0;
+ if (nd->u.scr.rid > 0) {
+ //Try to attach timer to this player.
+ sd = map_id2sd(nd->u.scr.rid);
+ if (!sd) {
+ if(battle_config.error_log)
+ ShowError("npc_timerevent_start: Attached player not found!\n");
+ return 1;
+ }
+ }
+ //Check if timer is already started.
+ if (sd) {
+ if (sd->npc_timer_id != -1)
+ return 0;
+ } else if (nd->u.scr.timerid != -1)
+ return 0;
+
+ ted = ers_alloc(timer_event_ers, struct timer_event_data);
+ ted->next = j;
+ nd->u.scr.timertick=ted->otick=gettick();
+
+ //Attach only the player if attachplayerrid was used.
+ ted->rid = sd?sd->bl.id:0;
+
+// Do not store it to make way to two types of timers: globals and personals.
+// if (rid >= 0) nd->u.scr.rid=rid; // changed to: attaching to given rid by default [Shinomori]
+ // if rid is less than 0 leave it unchanged [celest]
+
+ next = nd->u.scr.timer_event[j].timer - nd->u.scr.timer;
+ ted->time = nd->u.scr.timer_event[j].timer;
+ if (sd)
+ sd->npc_timer_id = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted);
+ else
+ nd->u.scr.timerid = add_timer(gettick()+next,npc_timerevent,nd->bl.id,(int)ted);
+ return 0;
+}
+/*==========================================
+ * タイマーイベント終了
+ *------------------------------------------
+ */
+int npc_timerevent_stop(struct npc_data *nd)
+{
+ struct map_session_data *sd =NULL;
+ struct TimerData *td = NULL;
+ int *tid;
+ nullpo_retr(0, nd);
+ if (nd->u.scr.rid) {
+ sd = map_id2sd(nd->u.scr.rid);
+ if (!sd) {
+ if(battle_config.error_log)
+ ShowError("npc_timerevent_stop: Attached player not found!\n");
+ return 1;
+ }
+ }
+
+ tid = sd?&sd->npc_timer_id:&nd->u.scr.timerid;
+
+ if (*tid == -1) //Nothing to stop
+ return 0;
+ td = get_timer(*tid);
+ if (td && td->data)
+ ers_free(timer_event_ers, (struct event_timer_data*)td->data);
+ delete_timer(*tid,npc_timerevent);
+ *tid = -1;
+ //Set the timer tick to the time that has passed since the beginning of the timers and now.
+ nd->u.scr.timer = DIFF_TICK(gettick(),nd->u.scr.timertick);
+// nd->u.scr.rid = 0; //Eh? why detach?
+ return 0;
+}
+/*==========================================
+ * Aborts a running npc timer that is attached to a player.
+ *------------------------------------------
+ */
+void npc_timerevent_quit(struct map_session_data *sd) {
+ struct TimerData *td;
+ if (sd->npc_timer_id == -1)
+ return;
+ td = get_timer(sd->npc_timer_id);
+ if (!td) {
+ sd->npc_timer_id = -1;
+ return; //??
+ }
+ delete_timer(sd->npc_timer_id,npc_timerevent);
+ sd->npc_timer_id = -1;
+ ers_free(timer_event_ers, (struct event_timer_data*)td->data);
+}
+
+/*==========================================
+ * タイマー値の所得
+ *------------------------------------------
+ */
+int npc_gettimerevent_tick(struct npc_data *nd)
+{
+ int tick;
+ nullpo_retr(0, nd);
+
+ tick=nd->u.scr.timer;
+ if (nd->u.scr.timertick)
+ tick+=DIFF_TICK(gettick(), nd->u.scr.timertick);
+ return tick;
+}
+/*==========================================
+ * タイマー値の設定
+ *------------------------------------------
+ */
+int npc_settimerevent_tick(struct npc_data *nd,int newtimer)
+{
+ int flag;
+ struct map_session_data *sd=NULL;
+
+ nullpo_retr(0, nd);
+
+ if (nd->u.scr.rid) {
+ sd = map_id2sd(nd->u.scr.rid);
+ if (!sd) {
+ if(battle_config.error_log)
+ ShowError("npc_settimerevent_tick: Attached player not found!\n");
+ return 1;
+ }
+ flag= sd->npc_timer_id != -1 ;
+ } else
+ flag= nd->u.scr.timerid != -1 ;
+
+ if(flag)
+ npc_timerevent_stop(nd);
+ nd->u.scr.timer=newtimer;
+ if(flag)
+ npc_timerevent_start(nd, -1);
+ return 0;
+}
+
+int npc_event_sub(struct map_session_data *sd, struct event_data *ev, const unsigned char *eventname){
+
+ if ( sd->npc_id!=0) {
+ //Enqueue the event trigger.
+ int i;
+ for(i=0;i<MAX_EVENTQUEUE && sd->eventqueue[i][0];i++);
+
+ if (i==MAX_EVENTQUEUE) {
+ if (battle_config.error_log)
+ ShowWarning("npc_event: event queue is full !\n");
+ }else //Event enqueued.
+ memcpy(sd->eventqueue[i],eventname,50);
+ return 1;
+ }
+ if (ev->nd->sc.option&OPTION_INVISIBLE) {
+ //Disabled npc, shouldn't trigger event.
+ npc_event_dequeue(sd);
+ return 2;
+ }
+ run_script(ev->nd->u.scr.script,ev->pos,sd->bl.id,ev->nd->bl.id);
+ return 0;
+}
+
+/*==========================================
+ * イベント型のNPC処理
+ *------------------------------------------
+ */
+int npc_event (struct map_session_data *sd, const unsigned char *eventname, int mob_kill)
+{
+ struct event_data *ev=strdb_get(ev_db,(unsigned char*)eventname);
+ struct npc_data *nd;
+ int xs,ys;
+ unsigned char mobevent[100];
+
+ if (sd == NULL) {
+ nullpo_info(NLP_MARK);
+ return 0;
+ }
+
+ if (ev == NULL && eventname && strcmp(((eventname)+strlen(eventname)-9),"::OnTouch") == 0)
+ return 1;
+
+ if (ev == NULL || (nd = ev->nd) == NULL) {
+ if (mob_kill) {
+ strcpy( mobevent, eventname);
+ strcat( mobevent, "::OnMyMobDead");
+ ev = strdb_get(ev_db, mobevent);
+ if (ev == NULL || (nd = ev->nd) == NULL) {
+ if (strnicmp(eventname, "GM_MONSTER",10) != 0)
+ ShowError("npc_event: (mob_kill) event not found [%s]\n", mobevent);
+ return 0;
+ }
+ } else {
+ if (battle_config.error_log)
+ ShowError("npc_event: event not found [%s]\n", eventname);
+ return 0;
+ }
+ }
+
+ xs=nd->u.scr.xs;
+ ys=nd->u.scr.ys;
+ if (xs>=0 && ys>=0 && (strcmp(((eventname)+strlen(eventname)-6),"Global") != 0) )
+ {
+ if (nd->bl.m >= 0) { //Non-invisible npc
+ if (nd->bl.m != sd->bl.m )
+ return 1;
+ if ( xs>0 && (sd->bl.x<nd->bl.x-xs/2 || nd->bl.x+xs/2<sd->bl.x) )
+ return 1;
+ if ( ys>0 && (sd->bl.y<nd->bl.y-ys/2 || nd->bl.y+ys/2<sd->bl.y) )
+ return 1;
+ }
+ }
+
+ return npc_event_sub(sd,ev,eventname);
+}
+
+
+int npc_command_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ struct event_data *ev=(struct event_data *)data;
+ unsigned char *npcname=va_arg(ap,char *);
+ char *command=va_arg(ap,char *);
+ unsigned char temp[100];
+
+ if(strcmp(ev->nd->name,npcname)==0 && (p=strchr(p,':')) && p && strnicmp("::OnCommand",p,10)==0 ){
+ sscanf(&p[11],"%s",temp);
+
+ if (strcmp(command,temp)==0)
+ run_script(ev->nd->u.scr.script,ev->pos,0,ev->nd->bl.id);
+ }
+
+ return 0;
+}
+
+int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command)
+{
+ ev_db->foreach(ev_db,npc_command_sub,npcname,command);
+
+ return 0;
+}
+/*==========================================
+ * 接触型のNPC処理
+ *------------------------------------------
+ */
+int npc_touch_areanpc(struct map_session_data *sd,int m,int x,int y)
+{
+ int i,f=1;
+ int xs,ys;
+
+ nullpo_retr(1, sd);
+
+ if(sd->npc_id)
+ return 1;
+
+ for(i=0;i<map[m].npc_num;i++) {
+ if (map[m].npc[i]->sc.option&OPTION_INVISIBLE) { // 無効化されている
+ f=0;
+ continue;
+ }
+
+ switch(map[m].npc[i]->bl.subtype) {
+ case WARP:
+ xs=map[m].npc[i]->u.warp.xs;
+ ys=map[m].npc[i]->u.warp.ys;
+ break;
+ case SCRIPT:
+ xs=map[m].npc[i]->u.scr.xs;
+ ys=map[m].npc[i]->u.scr.ys;
+ break;
+ default:
+ continue;
+ }
+ if (x >= map[m].npc[i]->bl.x-xs/2 && x < map[m].npc[i]->bl.x-xs/2+xs &&
+ y >= map[m].npc[i]->bl.y-ys/2 && y < map[m].npc[i]->bl.y-ys/2+ys)
+ break;
+ }
+ if (i==map[m].npc_num) {
+ if (f) {
+ if (battle_config.error_log)
+ ShowError("npc_touch_areanpc : some bug \n");
+ }
+ return 1;
+ }
+ switch(map[m].npc[i]->bl.subtype) {
+ case WARP:
+ // hidden chars cannot use warps -- is it the same for scripts too?
+ if (sd->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) ||
+ (!battle_config.duel_allow_teleport && sd->duel_group)) // duel rstrct [LuzZza]
+ break;
+ pc_setpos(sd,map[m].npc[i]->u.warp.mapindex,map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0);
+ break;
+ case SCRIPT:
+ {
+ //char *name=(char *)aCallocA(50,sizeof(char)); // fixed [Shinomori]
+ char name[50]; // need 24 max + 9 for "::OnTouch"
+
+ if(sd->areanpc_id == map[m].npc[i]->bl.id)
+ return 1;
+ sd->areanpc_id = map[m].npc[i]->bl.id;
+
+ sprintf(name,"%s::OnTouch", map[m].npc[i]->exname); // It goes here too. exname being the unique identifier. [Lance]
+
+ if( npc_event(sd,name,0)>0 ) {
+ pc_stop_walking(sd,1); //Make it stop walking!
+ npc_click(sd,map[m].npc[i]);
+ }
+ //aFree(name);
+ break;
+ }
+ }
+ return 0;
+}
+
+int npc_touch_areanpc2(struct block_list *bl)
+{
+ int i,m=bl->m;
+ int xs,ys;
+
+ for(i=0;i<map[m].npc_num;i++) {
+ if (map[m].npc[i]->sc.option&OPTION_INVISIBLE)
+ continue;
+
+ if (map[m].npc[i]->bl.subtype!=WARP)
+ continue;
+
+ xs=map[m].npc[i]->u.warp.xs;
+ ys=map[m].npc[i]->u.warp.ys;
+
+ if (bl->x >= map[m].npc[i]->bl.x-xs/2 && bl->x < map[m].npc[i]->bl.x-xs/2+xs &&
+ bl->y >= map[m].npc[i]->bl.y-ys/2 && bl->y < map[m].npc[i]->bl.y-ys/2+ys)
+ break;
+ }
+ if (i==map[m].npc_num)
+ return 0;
+
+ xs = map_mapindex2mapid(map[m].npc[i]->u.warp.mapindex);
+ if (xs < 0) // Can't warp object between map servers...
+ return 0;
+
+ if (unit_warp(bl, xs, map[m].npc[i]->u.warp.x,map[m].npc[i]->u.warp.y,0))
+ return 0; //Failed to warp.
+
+ return 1;
+}
+
+/*==========================================
+ * 近くかどうかの判定
+ *------------------------------------------
+ */
+int npc_checknear2(struct map_session_data *sd,struct block_list *bl)
+{
+ nullpo_retr(1, sd);
+ if(bl == NULL) return 1;
+
+ if(sd->state.using_fake_npc && sd->npc_id == bl->id)
+ return 0;
+
+ if (status_get_class(bl)<0) //Class-less npc, enable click from anywhere.
+ return 0;
+
+ if (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)
+ return 1;
+
+ return 0;
+}
+
+TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(NULL, sd);
+ if(bl == NULL) return NULL;
+ if(bl->type != BL_NPC) return NULL;
+ nd = (TBL_NPC*)bl;
+
+ if(sd->state.using_fake_npc && sd->npc_id == bl->id)
+ return nd;
+
+ if (nd->class_<0) //Class-less npc, enable click from anywhere.
+ return nd;
+
+ if (bl->m!=sd->bl.m ||
+ bl->x<sd->bl.x-AREA_SIZE-1 || bl->x>sd->bl.x+AREA_SIZE+1 ||
+ bl->y<sd->bl.y-AREA_SIZE-1 || bl->y>sd->bl.y+AREA_SIZE+1)
+ return NULL;
+
+ return nd;
+}
+
+/*==========================================
+ * NPCのオープンチャット発言
+ *------------------------------------------
+ */
+int npc_globalmessage(const char *name,char *mes)
+{
+ struct npc_data *nd=(struct npc_data *) strdb_get(npcname_db,(unsigned char*)name);
+ char temp[100];
+
+ if (!nd)
+ return 0;
+
+ snprintf(temp, sizeof temp ,"%s : %s",name,mes);
+ clif_GlobalMessage(&nd->bl,temp);
+
+ return 0;
+}
+
+/*==========================================
+ * クリック時のNPC処理
+ *------------------------------------------
+ */
+int npc_click(struct map_session_data *sd,struct npc_data *nd)
+{
+ nullpo_retr(1, sd);
+
+ if (sd->npc_id != 0) {
+ if (battle_config.error_log)
+ ShowError("npc_click: npc_id != 0\n");
+ return 1;
+ }
+
+ if(!nd) return 1;
+ if ((nd = npc_checknear(sd,&nd->bl)) == NULL)
+ return 1;
+ //Hidden/Disabled npc.
+ if (nd->class_ < 0 || nd->sc.option&OPTION_INVISIBLE)
+ return 1;
+
+ switch(nd->bl.subtype) {
+ case SHOP:
+ clif_npcbuysell(sd,nd->bl.id);
+ npc_event_dequeue(sd);
+ break;
+ case SCRIPT:
+ run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id);
+ break;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_scriptcont(struct map_session_data *sd,int id)
+{
+ nullpo_retr(1, sd);
+
+ if (id!=sd->npc_id){
+ ShowWarning("npc_scriptcont: sd->npc_id (%d) is not id (%d).\n", sd->npc_id, id);
+ return 1;
+ }
+
+ if(id != fake_nd->bl.id) { // Not item script
+ if ((npc_checknear(sd,map_id2bl(id))) == NULL){
+ ShowWarning("npc_scriptcont: failed npc_checknear test.\n");
+ return 1;
+ }
+ }
+ run_script_main(sd->st);
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_buysellsel(struct map_session_data *sd,int id,int type)
+{
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+
+ if ((nd = npc_checknear(sd,map_id2bl(id))) == NULL)
+ return 1;
+
+ if (nd->bl.subtype!=SHOP) {
+ if (battle_config.error_log)
+ ShowError("no such shop npc : %d\n",id);
+ if (sd->npc_id == id)
+ sd->npc_id=0;
+ return 1;
+ }
+ if (nd->sc.option&OPTION_INVISIBLE) // 無効化されている
+ return 1;
+
+ sd->npc_shopid=id;
+ if (type==0) {
+ clif_buylist(sd,nd);
+ } else {
+ clif_selllist(sd);
+ }
+ return 0;
+}
+
+//npc_buylist for script-controlled shops.
+static int npc_buylist_sub(
+ struct map_session_data *sd,int n,
+ unsigned short *item_list, struct npc_data *nd)
+{
+ unsigned char npc_ev[51];
+ int i;
+ int regkey = add_str("@bought_nameid");
+ int regkey2 = add_str("@bought_quantity");
+ sprintf(npc_ev, "%s::OnBuyItem", nd->exname);
+ for(i=0;i<n;i++){
+ pc_setreg(sd,regkey+(i<<24),(int)item_list[i*2+1]);
+ pc_setreg(sd,regkey2+(i<<24),(int)item_list[i*2]);
+ }
+ npc_event(sd, npc_ev, 0);
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_buylist(struct map_session_data *sd,int n,unsigned short *item_list)
+{
+ struct npc_data *nd;
+ double z;
+ int i,j,w,skill,itemamount=0,new_=0;
+
+ nullpo_retr(3, sd);
+ nullpo_retr(3, item_list);
+
+ if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL)
+ return 3;
+
+ if (nd->master_nd) //Script-based shops.
+ return npc_buylist_sub(sd,n,item_list,nd->master_nd);
+
+ if (nd->bl.subtype!=SHOP)
+ return 3;
+
+ for(i=0,w=0,z=0;i<n;i++) {
+ for(j=0;nd->u.shop_item[j].nameid;j++) {
+ if (nd->u.shop_item[j].nameid==item_list[i*2+1] || //Normal items
+ itemdb_viewid(nd->u.shop_item[j].nameid)==item_list[i*2+1]) //item_avail replacement
+ break;
+ }
+ if (nd->u.shop_item[j].nameid==0)
+ return 3;
+
+ if (!itemdb_isstackable(nd->u.shop_item[j].nameid) && item_list[i*2] > 1)
+ { //Exploit? You can't buy more than 1 of equipment types o.O
+ ShowWarning("Player %s (%d:%d) sent a hexed packet trying to buy %d of nonstackable item %d!\n",
+ sd->status.name, sd->status.account_id, sd->status.char_id, item_list[i*2], nd->u.shop_item[j].nameid);
+ item_list[i*2] = 1;
+ }
+ if (itemdb_value_notdc(nd->u.shop_item[j].nameid))
+ z+=(double)nd->u.shop_item[j].value * item_list[i*2];
+ else
+ z+=(double)pc_modifybuyvalue(sd,nd->u.shop_item[j].value) * item_list[i*2];
+ itemamount+=item_list[i*2];
+
+ switch(pc_checkadditem(sd,item_list[i*2+1],item_list[i*2])) {
+ case ADDITEM_EXIST:
+ break;
+ case ADDITEM_NEW:
+ new_++;
+ break;
+ case ADDITEM_OVERAMOUNT:
+ return 2;
+ }
+
+ w+=itemdb_weight(item_list[i*2+1]) * item_list[i*2];
+ }
+ if (z > (double)sd->status.zeny)
+ return 1; // zeny不足
+ if (w+sd->weight > sd->max_weight)
+ return 2; // 重量超過
+ if (pc_inventoryblank(sd)<new_)
+ return 3; // 種類数超過
+
+ //Logs (S)hopping Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(sd, "S", sd, -(int)z);
+ //Logs
+
+ pc_payzeny(sd,(int)z);
+ for(i=0;i<n;i++) {
+ struct item item_tmp;
+
+ malloc_set(&item_tmp,0,sizeof(item_tmp));
+ item_tmp.nameid = item_list[i*2+1];
+ item_tmp.identify = 1; // npc販売アイテムは鑑定済み
+
+ pc_additem(sd,&item_tmp,item_list[i*2]);
+
+ //Logs items, Bought in NPC (S)hop [Lupus]
+ if(log_config.enable_logs&0x20)
+ log_pick_pc(sd, "S", item_tmp.nameid, item_list[i*2], NULL);
+ //Logs
+ }
+
+ //商人経験値
+ if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_DISCOUNT)) > 0) {
+ if (sd->status.skill[MC_DISCOUNT].flag != 0)
+ skill = sd->status.skill[MC_DISCOUNT].flag - 2;
+ if (skill > 0) {
+ z = z * (double)skill * (double)battle_config.shop_exp/10000.;
+ if (z < 1)
+ z = 1;
+ pc_gainexp(sd,NULL,0,(int)z);
+ }
+ }
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+int npc_selllist(struct map_session_data *sd,int n,unsigned short *item_list)
+{
+ double z;
+ int i,skill,itemamount=0;
+ struct npc_data *nd;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, item_list);
+
+ if ((nd = npc_checknear(sd,map_id2bl(sd->npc_shopid))) == NULL)
+ return 1;
+ nd = nd->master_nd; //For OnSell triggers.
+
+ for(i=0,z=0;i<n;i++) {
+ int nameid, idx, qty;
+ idx = item_list[i*2]-2;
+ qty = item_list[i*2+1];
+
+ if (idx <0 || idx >=MAX_INVENTORY || qty < 0)
+ break;
+
+ nameid=sd->status.inventory[idx].nameid;
+ if (nameid == 0 || !sd->inventory_data[idx] ||
+ sd->status.inventory[idx].amount < qty)
+ break;
+
+ if (sd->inventory_data[idx]->flag.value_notoc)
+ z+=(double)qty*sd->inventory_data[idx]->value_sell;
+ else
+ z+=(double)qty*pc_modifysellvalue(sd,sd->inventory_data[idx]->value_sell);
+
+ if(sd->inventory_data[idx]->type==7 && sd->status.inventory[idx].card[0] == (short)0xff00)
+ {
+ if(search_petDB_index(sd->status.inventory[idx].nameid, PET_EGG) >= 0)
+ intif_delete_petdata(MakeDWord(sd->status.inventory[idx].card[1],sd->status.inventory[idx].card[2]));
+ }
+
+ if(log_config.enable_logs&0x20) //Logs items, Sold to NPC (S)hop [Lupus]
+ log_pick_pc(sd, "S", nameid, -qty, &sd->status.inventory[idx]);
+
+ if(nd) {
+ pc_setreg(sd,add_str("@sold_nameid")+(i<<24),(int)sd->status.inventory[idx].nameid);
+ pc_setreg(sd,add_str("@sold_quantity")+(i<<24),qty);
+ }
+ itemamount+=qty;
+ pc_delitem(sd,idx,qty,0);
+ }
+
+ if (z > MAX_ZENY) z = MAX_ZENY;
+
+ if(log_config.zeny) //Logs (S)hopping Zeny [Lupus]
+ log_zeny(sd, "S", sd, (int)z);
+
+ pc_getzeny(sd,(int)z);
+
+ if (battle_config.shop_exp > 0 && z > 0 && (skill = pc_checkskill(sd,MC_OVERCHARGE)) > 0) {
+ if (sd->status.skill[MC_OVERCHARGE].flag != 0)
+ skill = sd->status.skill[MC_OVERCHARGE].flag - 2;
+ if (skill > 0) {
+ z = z * (double)skill * (double)battle_config.shop_exp/10000.;
+ if (z < 1)
+ z = 1;
+ pc_gainexp(sd,NULL,0,(int)z);
+ }
+ }
+
+ if(nd) {
+ unsigned char npc_ev[51];
+ sprintf(npc_ev, "%s::OnSellItem", nd->exname);
+ npc_event(sd, npc_ev, 0);
+ }
+
+ if (i<n) {
+ //Error/Exploit... of some sort. If we return 1, the client will not mark
+ //any item as deleted even though a few were sold. In such a case, we
+ //have no recourse but to kick them out so their inventory will refresh
+ //correctly on relog. [Skotlex]
+ if (i) clif_setwaitclose(sd->fd);
+ return 1;
+ }
+ return 0;
+}
+
+int npc_remove_map (struct npc_data *nd)
+{
+ int m,i;
+ nullpo_retr(1, nd);
+
+ if(nd->bl.prev == NULL || nd->bl.m < 0)
+ return 1; //Not assigned to a map.
+ m = nd->bl.m;
+#ifdef PCRE_SUPPORT
+ npc_chat_finalize(nd);
+#endif
+ clif_clearchar_area(&nd->bl,2);
+ strdb_remove(npcname_db, (nd->bl.subtype < SCRIPT) ? nd->name : nd->exname);
+ //Remove corresponding NPC CELLs
+ if (nd->bl.subtype == WARP) {
+ int j, xs, ys, x, y;
+ x = nd->bl.x;
+ y = nd->bl.y;
+ xs = nd->u.warp.xs;
+ ys = nd->u.warp.ys;
+
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNPC))
+ map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_CLRNPC);
+ }
+ }
+ }
+ map_delblock(&nd->bl);
+ map_deliddb(&nd->bl);
+ //Remove npc from map[].npc list. [Skotlex]
+ for(i=0;i<map[m].npc_num && map[m].npc[i] != nd;i++);
+ if (i >= map[m].npc_num) return 2; //failed to find it?
+
+ map[m].npc_num--;
+ for(; i<map[m].npc_num; i++)
+ map[m].npc[i]=map[m].npc[i+1];
+ return 0;
+}
+
+static int npc_unload_ev(DBKey key,void *data,va_list ap) {
+ struct event_data *ev=(struct event_data *)data;
+ unsigned char *npcname=va_arg(ap,unsigned char *);
+
+ if(strcmp(ev->nd->exname,npcname)==0){
+ db_remove(ev_db, key);
+ return 1;
+ }
+ return 0;
+}
+
+static int npc_unload_dup_sub(DBKey key,void * data,va_list ap)
+{
+ struct npc_data *nd = (struct npc_data *)data;
+ int src_id;
+
+ if(nd->bl.type!=BL_NPC || nd->bl.subtype != SCRIPT)
+ return 0;
+
+ src_id=va_arg(ap,int);
+ if (nd->u.scr.src_id == src_id)
+ npc_unload(nd);
+ return 0;
+}
+//Removes all npcs that are duplicates of the passed one. [Skotlex]
+void npc_unload_duplicates (struct npc_data *nd)
+{
+ map_foreachiddb(npc_unload_dup_sub,nd->bl.id);
+}
+
+int npc_unload (struct npc_data *nd)
+{
+ nullpo_ret(nd);
+
+ npc_remove_map (nd);
+ map_deliddb(&nd->bl);
+
+ if (nd->chat_id) {
+ struct chat_data *cd = (struct chat_data*)map_id2bl(nd->chat_id);
+ if (cd) aFree (cd);
+ cd = NULL;
+ }
+ if (nd->bl.subtype == SCRIPT) {
+ ev_db->foreach(ev_db,npc_unload_ev,nd->exname); //Clean up all events related.
+ if (nd->u.scr.timerid != -1) {
+ struct TimerData *td = NULL;
+ td = get_timer(nd->u.scr.timerid);
+ if (td && td->data)
+ ers_free(timer_event_ers, (struct event_timer_data*)td->data);
+ delete_timer(nd->u.scr.timerid, npc_timerevent);
+ }
+ npc_cleareventtimer (nd);
+ if (nd->u.scr.timer_event)
+ aFree(nd->u.scr.timer_event);
+ if (nd->u.scr.src_id == 0) {
+ if(nd->u.scr.script) {
+ script_free_code(nd->u.scr.script);
+ nd->u.scr.script = NULL;
+ }
+ if (nd->u.scr.label_list) {
+ aFree(nd->u.scr.label_list);
+ nd->u.scr.label_list = NULL;
+ }
+ }
+ }
+ aFree(nd);
+
+ return 0;
+}
+
+//
+// 初期化関係
+//
+
+/*==========================================
+ * 読み込むnpcファイルのクリア
+ *------------------------------------------
+ */
+void npc_clearsrcfile (void)
+{
+ struct npc_src_list *p = npc_src_first, *p2;
+
+ while (p) {
+ p2 = p;
+ p = p->next;
+ aFree(p2);
+ }
+ npc_src_first = NULL;
+ npc_src_last = NULL;
+}
+/*==========================================
+ * 読み込むnpcファイルの追加
+ *------------------------------------------
+ */
+void npc_addsrcfile (char *name)
+{
+ struct npc_src_list *nsl;
+
+ if (strcmpi(name, "clear") == 0) {
+ npc_clearsrcfile();
+ return;
+ }
+
+ // prevent multiple insert of source files
+ nsl = npc_src_first;
+ while (nsl)
+ { // found the file, no need to insert it again
+ if (0 == strcmp(name, nsl->name))
+ return;
+ nsl = nsl->next;
+ }
+
+ nsl = (struct npc_src_list *) aMalloc (sizeof(*nsl) + strlen(name));
+ nsl->next = NULL;
+ strncpy(nsl->name, name, strlen(name) + 1);
+ if (npc_src_first == NULL)
+ npc_src_first = nsl;
+ if (npc_src_last)
+ npc_src_last->next = nsl;
+ npc_src_last = nsl;
+}
+/*==========================================
+ * 読み込むnpcファイルの削除
+ *------------------------------------------
+ */
+void npc_delsrcfile (char *name)
+{
+ struct npc_src_list *p = npc_src_first, *pp = NULL, **lp = &npc_src_first;
+
+ if (strcmpi(name, "all") == 0) {
+ npc_clearsrcfile();
+ return;
+ }
+
+ while (p) {
+ if (strcmp(p->name, name) == 0) {
+ *lp = p->next;
+ if (npc_src_last == p)
+ npc_src_last = pp;
+ aFree(p);
+ break;
+ }
+ lp = &p->next;
+ pp = p;
+ p = p->next;
+ }
+}
+
+/*==========================================
+ * warp行解析
+ *------------------------------------------
+ */
+int npc_parse_warp (char *w1,char *w2,char *w3,char *w4)
+{
+ int x, y, xs, ys, to_x, to_y, m;
+ int i, j;
+ char mapname[MAP_NAME_LENGTH], to_mapname[MAP_NAME_LENGTH];
+ struct npc_data *nd;
+
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d", mapname, &x, &y) != 3 ||
+ sscanf(w4, "%d,%d,%15[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5) {
+ ShowError("bad warp line : %s\n", w3);
+ return 1;
+ }
+
+ m = map_mapname2mapid(mapname);
+ i = mapindex_name2id(to_mapname);
+ if (!i) {
+ ShowError("bad warp line (destination map not found): %s\n", w3);
+ return 1;
+ }
+
+ nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data));
+
+ nd->bl.id = npc_get_new_npc_id();
+ nd->n = map_addnpc(m, nd);
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, w3, NAME_LENGTH-1);
+
+ if (!battle_config.warp_point_debug)
+ nd->class_ = WARP_CLASS;
+ else
+ nd->class_ = WARP_DEBUG_CLASS;
+ nd->speed = 200;
+
+ nd->u.warp.mapindex = (short)i;
+ xs += 2;
+ ys += 2;
+ nd->u.warp.x = to_x;
+ nd->u.warp.y = to_y;
+ nd->u.warp.xs = xs;
+ nd->u.warp.ys = ys;
+
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x-xs/2+j, y-ys/2+i, CELL_CHKNOPASS))
+ continue;
+ map_setcell(m, x-xs/2+j, y-ys/2+i, CELL_SETNPC);
+ }
+ }
+
+ npc_warp++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = WARP;
+ map_addblock(&nd->bl);
+ status_set_viewdata(&nd->bl, nd->class_);
+ status_change_init(&nd->bl);
+ unit_dataset(&nd->bl);
+ clif_spawn(&nd->bl);
+ strdb_put(npcname_db, nd->name, nd);
+
+ return 0;
+}
+
+/*==========================================
+ * shop行解析
+ *------------------------------------------
+ */
+static int npc_parse_shop (char *w1, char *w2, char *w3, char *w4)
+{
+ #define MAX_SHOPITEM 100
+ char *p;
+ int x, y, dir, m, pos = 0;
+ char mapname[MAP_NAME_LENGTH];
+ struct npc_data *nd;
+
+ if (strcmp(w1, "-") == 0) {
+ x = 0; y = 0; dir = 0; m = -1;
+ } else {
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
+ strchr(w4, ',') == NULL) {
+ ShowError("bad shop line : %s\n", w3);
+ return 1;
+ }
+ m = map_mapname2mapid(mapname);
+ }
+
+ nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) +
+ sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1));
+ p = strchr(w4, ',');
+
+ while (p && pos < MAX_SHOPITEM) {
+ int nameid, value;
+ struct item_data *id;
+ p++;
+ if (sscanf(p, "%d:%d", &nameid, &value) != 2)
+ break;
+ nd->u.shop_item[pos].nameid = nameid;
+ id = itemdb_search(nameid);
+ if (value < 0)
+ value = id->value_buy;
+ nd->u.shop_item[pos].value = value;
+ // check for bad prices that can possibly cause exploits
+ if (value/124. < id->value_sell/75.) { //Clened up formula to prevent overflows.
+ printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex]
+ if (value < id->value_sell)
+ ShowWarning ("Item %s [%d] buying price (%d) is less than selling price (%d) at %s\n",
+ id->name, id->nameid, value, id->value_sell, current_file);
+ else
+ ShowWarning ("Item %s [%d] discounted buying price (%d) is less than overcharged selling price (%d) at %s\n",
+ id->name, id->nameid, value/100*75, id->value_sell/100*124, current_file);
+ }
+ //for logs filters, atcommands and iteminfo script command
+ if (id->maxchance<=0)
+ id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop
+
+ pos++;
+ p = strchr(p, ',');
+ }
+ if (pos == 0) {
+ aFree(nd);
+ return 1;
+ }
+ nd->u.shop_item[pos++].nameid = 0;
+
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ nd->bl.id = npc_get_new_npc_id();
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ nd->name[NAME_LENGTH-1] = '\0';
+ nd->class_ = m==-1?-1:atoi(w4);
+ nd->speed = 200;
+
+ nd = (struct npc_data *)aRealloc(nd,
+ sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos);
+
+ npc_shop++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = SHOP;
+ if (m >= 0) {
+ nd->n = map_addnpc(m,nd);
+ map_addblock(&nd->bl);
+ status_set_viewdata(&nd->bl, nd->class_);
+ status_change_init(&nd->bl);
+ unit_dataset(&nd->bl);
+ nd->ud.dir = dir;
+ clif_spawn(&nd->bl);
+ } else
+ // we skip map_addnpc, but still add it to the list of ID's
+ map_addiddb(&nd->bl);
+ strdb_put(npcname_db, nd->name,nd);
+
+ return 0;
+}
+
+/*==========================================
+ * NPCのラベルデータコンバート
+ *------------------------------------------
+ */
+int npc_convertlabel_db (DBKey key, void *data, va_list ap)
+{
+ unsigned char *lname = key.str;
+ int pos = (int)data;
+ struct npc_data *nd;
+ struct npc_label_list *lst;
+ int num;
+ char *p;
+ char c;
+
+ nullpo_retr(0, ap);
+ nullpo_retr(0, nd = va_arg(ap,struct npc_data *));
+
+ lst = nd->u.scr.label_list;
+ num = nd->u.scr.label_list_num;
+ if (!lst) {
+ lst = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list));
+ num = 0;
+ } else
+ lst = (struct npc_label_list *) aRealloc (lst, sizeof(struct npc_label_list)*(num+1));
+
+ // In case of labels not terminated with ':', for user defined function support
+ p = lname;
+ while(isalnum(*(unsigned char*)p) || *p == '_') { p++; }
+ c = *p;
+ *p='\0';
+
+ // here we check if the label fit into the buffer
+ if (strlen(lname) > 23) {
+ ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, current_file);
+ exit(1);
+ }
+ memcpy(lst[num].name, lname, strlen(lname)+1); //including EOS
+
+ *p = c;
+ lst[num].pos = pos;
+ nd->u.scr.label_list = lst;
+ nd->u.scr.label_list_num = num+1;
+
+ return 0;
+}
+
+/*==========================================
+ * script行解析
+ *------------------------------------------
+ */
+static void npc_parse_script_line(unsigned char *p,int *curly_count,int line) {
+ int i = strlen((char *)p),j;
+ int string_flag = 0;
+ static int comment_flag = 0;
+ for(j = 0; j < i ; j++) {
+ if(comment_flag) {
+ if(p[j] == '*' && p[j+1] == '/') {
+ // マルチラインコメント終了
+ j++;
+ (*curly_count)--;
+ comment_flag = 0;
+ }
+ } else if(string_flag) {
+ if(p[j] == '"') {
+ string_flag = 0;
+ } else if(p[j] == '\\' && p[j-1]<=0x7e) {
+ // エスケープ
+ j++;
+ }
+ } else {
+ if(p[j] == '"') {
+ string_flag = 1;
+ } else if(p[j] == '}') {
+ if(*curly_count == 0) {
+ break;
+ } else {
+ (*curly_count)--;
+ }
+ } else if(p[j] == '{') {
+ (*curly_count)++;
+ } else if(p[j] == '/' && p[j+1] == '/') {
+ // コメント
+ break;
+ } else if(p[j] == '/' && p[j+1] == '*') {
+ // マルチラインコメント
+ j++;
+ (*curly_count)++;
+ comment_flag = 1;
+ }
+ }
+ }
+ if(string_flag) {
+ printf("Missing '\"' at file %s line %d\n",current_file,line);
+ exit(1);
+ }
+}
+
+// Like npc_parse_script, except it's sole use is to skip the contents of a script. [Skotlex]
+static int npc_skip_script (char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines)
+{
+ unsigned char *srcbuf = NULL;
+ int srcsize = 65536;
+ int startline = 0;
+ unsigned char line[1024];
+ int curly_count = 0;
+
+ srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char));
+ if (strchr(first_line, '{')) {
+ strcpy((char *)srcbuf, strchr(first_line, '{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+ while (curly_count > 0) {
+ fgets ((char *)line, 1020, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
+ srcsize += 65536;
+ srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
+ malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0] != '{') {
+ if (strchr((char *) line,'{')) {
+ strcpy((char *) srcbuf, strchr((const char *) line, '{'));
+ startline = *lines;
+ }
+ } else
+ strcat((char *) srcbuf, (const char *) line);
+ }
+ if(curly_count > 0)
+ ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
+ aFree(srcbuf);
+ return 0;
+}
+
+static int npc_parse_script(char *w1,char *w2,char *w3,char *w4,char *first_line,FILE *fp,int *lines,const char* file)
+{
+ int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0; // [Valaris] thanks to fov
+ char mapname[MAP_NAME_LENGTH];
+ unsigned char *srcbuf = NULL;
+ struct script_code *script;
+ int srcsize = 65536;
+ int startline = 0;
+ unsigned char line[1024];
+ int i;
+ struct npc_data *nd, *dnd;
+ struct dbt *label_db;
+ char *p;
+ struct npc_label_list *label_dup = NULL;
+ int label_dupnum = 0;
+ int src_id = 0;
+
+ if (strcmp(w1, "-") == 0) {
+ x = 0; y = 0; m = -1;
+ } else {
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
+ (strcmp(w2, "script") == 0 && strchr(w4,',') == NULL)) {
+ ShowError("bad script line (in file %s): %s\n", file, w3);
+ return 1;
+ }
+ m = map_mapname2mapid(mapname);
+ }
+
+ if (strcmp(w2, "script") == 0){
+ // parsing script with curly
+ int curly_count = 0;
+ srcbuf = (unsigned char *)aMallocA(srcsize*sizeof(char));
+ if (strchr(first_line, '{')) {
+ strcpy((char *)srcbuf, strchr(first_line, '{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+ while (curly_count > 0) {
+ fgets ((char *)line, 1020, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen((char *)srcbuf) + strlen((char *)line) + 1 >= (size_t)srcsize) {
+ srcsize += 65536;
+ srcbuf = (unsigned char *)aRealloc(srcbuf, srcsize);
+ malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0] != '{') {
+ if (strchr((char *) line,'{')) {
+ strcpy((char *) srcbuf, strchr((const char *) line, '{'));
+ startline = *lines;
+ }
+ } else
+ strcat((char *) srcbuf, (const char *) line);
+ }
+ if(curly_count > 0) {
+ ShowError("Missing right curly at file %s, line %d\n",file, *lines);
+ script = NULL;
+ } else {
+ // printf("Ok line %d\n",*lines);
+ script = parse_script((unsigned char *) srcbuf, file, startline);
+ }
+ if (script == NULL) {
+ // script parse error?
+ aFree(srcbuf);
+ return 1;
+ }
+ } else {
+ // duplicateする
+ char srcname[128];
+ struct npc_data *dnd;
+ if (sscanf(w2, "duplicate(%[^)])", srcname) != 1) {
+ ShowError("bad duplicate name (in %s)! : %s", file, w2);
+ return 0;
+ }
+ if ((dnd = npc_name2id(srcname)) == NULL) {
+ ShowError("bad duplicate name (in %s)! (not exist) : %s\n", file, srcname);
+ return 0;
+ }
+ script = dnd->u.scr.script;
+ label_dup = dnd->u.scr.label_list;
+ label_dupnum = dnd->u.scr.label_list_num;
+ src_id = dnd->bl.id;
+
+ }// end of スクリプト解析
+
+ nd = (struct npc_data *)aCalloc(1, sizeof(struct npc_data));
+
+ if (sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3) {
+ // 接触型NPC
+ int i, j;
+
+ if (xs >= 0) xs = xs * 2 + 1;
+ if (ys >= 0) ys = ys * 2 + 1;
+
+ if (m >= 0) {
+ for (i = 0; i < ys; i++) {
+ for (j = 0; j < xs; j++) {
+ if (map_getcell(m, x - xs/2 + j, y - ys/2 + i, CELL_CHKNOPASS))
+ continue;
+ map_setcell(m, x - xs/2 + j, y - ys/2 + i, CELL_SETNPC);
+ }
+ }
+ }
+ nd->u.scr.xs = xs;
+ nd->u.scr.ys = ys;
+ } else {
+ // クリック型NPC
+ class_ = atoi(w4);
+ nd->u.scr.xs = 0;
+ nd->u.scr.ys = 0;
+ }
+
+ while ((p = strchr(w3,':'))) {
+ if (p[1] == ':') break;
+ }
+ if (p) {
+ *p = 0;
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, p+2, NAME_LENGTH-1);
+ } else {
+ memcpy(nd->name, w3, NAME_LENGTH-1);
+ memcpy(nd->exname, w3, NAME_LENGTH-1);
+ }
+
+ if((dnd = npc_name2id(nd->exname))){
+ if(battle_config.etc_log)
+ ShowInfo("npc_parse_script: Overriding NPC '%s::%s' to '%s::%d'.. in file '%s' (Duplicated System Name - Lazy scripters >_>) \n",nd->name,nd->exname,nd->name,npc_script,file);
+ sprintf(nd->exname, "%d", npc_script);
+ }
+
+ nd->bl.prev = nd->bl.next = NULL;
+ nd->bl.m = m;
+ nd->bl.x = x;
+ nd->bl.y = y;
+ nd->bl.id = npc_get_new_npc_id();
+ nd->class_ = class_;
+ nd->speed = 200;
+ nd->u.scr.script = script;
+ nd->u.scr.src_id = src_id;
+
+ npc_script++;
+ nd->bl.type = BL_NPC;
+ nd->bl.subtype = SCRIPT;
+
+ for (i = 0; i < MAX_EVENTTIMER; i++)
+ nd->eventtimer[i] = -1;
+ if (m >= 0) {
+ nd->n = map_addnpc(m, nd);
+ status_change_init(&nd->bl);
+ unit_dataset(&nd->bl);
+ nd->ud.dir = dir;
+ map_addblock(&nd->bl);
+ // Unused. You can always use xxx::OnXXXX events. Have this removed to improve perfomance.
+ /*if (evflag) { // イベント型
+ struct event_data *ev = (struct event_data *)aCalloc(1, sizeof(struct event_data));
+ ev->nd = nd;
+ ev->pos = 0;
+ strdb_put(ev_db, nd->exname, ev);
+ } else {
+ clif_spawn(&nd->bl);
+ }*/
+ if (class_ >= 0){
+ status_set_viewdata(&nd->bl, nd->class_);
+ clif_spawn(&nd->bl);
+ }
+ } else {
+ // we skip map_addnpc, but still add it to the list of ID's
+ map_addiddb(&nd->bl);
+ }
+ strdb_put(npcname_db, nd->exname, nd);
+
+ //-----------------------------------------
+ // ラベルデータの準備
+ if (srcbuf){
+ // script本体がある場合の処理
+ // ラベルデータのコンバート
+ label_db = script_get_label_db();
+ label_db->foreach(label_db, npc_convertlabel_db, nd);
+
+ // もう使わないのでバッファ解放
+ aFree(srcbuf);
+ } else {
+ // duplicate
+ nd->u.scr.label_list = label_dup; // ラベルデータ共有
+ nd->u.scr.label_list_num = label_dupnum;
+ }
+
+ //-----------------------------------------
+ // イベント用ラベルデータのエクスポート
+ for (i = 0; i < nd->u.scr.label_list_num; i++){
+ char *lname = nd->u.scr.label_list[i].name;
+ int pos = nd->u.scr.label_list[i].pos;
+
+ if ((lname[0] == 'O' || lname[0] == 'o') && (lname[1] == 'N' || lname[1] == 'n')) {
+ // this check is useless here because the buffer is only 24 chars
+ // and already overwritten if this is here is reached
+ // I leave the check anyway but place it correctly to npc_convertlabel_db
+ if (strlen(lname)>NAME_LENGTH-1) {
+ ShowError("npc_parse_script: label name longer than %d chars! '%s' (%s)\n", NAME_LENGTH-1, lname, file);
+ exit(1);
+ } else {
+ struct event_data *ev;
+ unsigned char buf[51];
+ // 51 comes from: 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS
+ sprintf(buf,"%s::%s",nd->exname,lname);
+
+ // remember the label is max 50 chars + eos; see the strdb_init below
+ // generate the data and insert it
+ ev=(struct event_data *)aMalloc(sizeof(struct event_data));
+ ev->nd=nd;
+ ev->pos=pos;
+ if (strdb_put(ev_db,buf,ev) != NULL) //There was already another event of the same name?
+ ShowWarning("npc_parse_script : duplicate event %s (%s)\n",buf, file);
+ }
+ }
+ }
+
+ //-----------------------------------------
+ // ラベルデータからタイマーイベント取り込み
+ for (i = 0; i < nd->u.scr.label_list_num; i++){
+ int t = 0, k = 0;
+ char *lname = nd->u.scr.label_list[i].name;
+ int pos = nd->u.scr.label_list[i].pos;
+ if (sscanf(lname, "OnTimer%d%n", &t, &k) == 1 && lname[k] == '\0') {
+ // タイマーイベント
+ struct npc_timerevent_list *te = nd->u.scr.timer_event;
+ int j, k = nd->u.scr.timeramount;
+ if (te == NULL)
+ te = (struct npc_timerevent_list *)aMallocA(sizeof(struct npc_timerevent_list));
+ else
+ te = (struct npc_timerevent_list *)aRealloc( te, sizeof(struct npc_timerevent_list) * (k+1) );
+ for (j = 0; j < k; j++){
+ if (te[j].timer > t){
+ memmove(te+j+1, te+j, sizeof(struct npc_timerevent_list)*(k-j));
+ break;
+ }
+ }
+ te[j].timer = t;
+ te[j].pos = pos;
+ nd->u.scr.timer_event = te;
+ nd->u.scr.timeramount++;
+ }
+ }
+ nd->u.scr.timerid = -1;
+
+ return 0;
+}
+
+/*==========================================
+ * function行解析
+ *------------------------------------------
+ */
+static int npc_parse_function (char *w1, char *w2, char *w3, char *w4, char *first_line, FILE *fp, int *lines,const char* file)
+{
+ unsigned char *srcbuf, *p;
+ struct script_code *script;
+ struct script_code *oldscript;
+ int srcsize = 65536;
+ int startline = 0;
+ char line[1024];
+ int curly_count = 0;
+ struct dbt *user_db;
+
+ // スクリプトの解析
+ srcbuf = (unsigned char *) aMallocA (srcsize*sizeof(char));
+ if (strchr(first_line,'{')) {
+ strcpy(srcbuf, strchr(first_line,'{'));
+ startline = *lines;
+ } else
+ srcbuf[0] = 0;
+ npc_parse_script_line(srcbuf,&curly_count,*lines);
+
+ while (curly_count > 0) {
+ fgets(line, sizeof(line) - 1, fp);
+ (*lines)++;
+ npc_parse_script_line(line,&curly_count,*lines);
+ if (feof(fp))
+ break;
+ if (strlen(srcbuf)+strlen(line)+1 >= (unsigned int)srcsize) {
+ srcsize += 65536;
+ srcbuf = (char *)aRealloc(srcbuf, srcsize);
+ malloc_tsetdword(srcbuf + srcsize - 65536, '\0', 65536);
+ }
+ if (srcbuf[0]!='{') {
+ if (strchr(line,'{')) {
+ strcpy(srcbuf, strchr(line,'{'));
+ startline = *lines;
+ }
+ } else
+ strcat(srcbuf,line);
+ }
+ if(curly_count > 0) {
+ ShowError("Missing right curly at file %s, line %d\n",file, *lines);
+ script = NULL;
+ } else {
+ script = parse_script(srcbuf, file, startline);
+ }
+ if (script == NULL) {
+ // script parse error?
+ aFree(srcbuf);
+ return 1;
+ }
+
+ p = (char *) aMallocA (50*sizeof(char));
+ strncpy(p, w3, 50);
+
+ user_db = script_get_userfunc_db();
+ oldscript = (struct script_code *)strdb_get(user_db, p);
+ if(oldscript != NULL) {
+ printf("\r"); //Carriage return to clear the 'loading..' line. [Skotlex]
+ ShowInfo("parse_function: Overwriting user function [%s] (%s:%d)\n", p, file, *lines);
+ script_free_code(oldscript);
+ user_db->remove(user_db,str2key(p));
+ strdb_put(user_db, p, script);
+ } else
+ strdb_put(user_db, p, script);
+
+ // もう使わないのでバッファ解放
+ aFree(srcbuf);
+
+// printf("function %s => %p\n",p,script);
+
+ return 0;
+}
+
+
+/*==========================================
+ * Parse Mob 1 - Parse mob list into each map
+ * Parse Mob 2 - Actually Spawns Mob
+ * [Wizputer]
+ * If cached =1, it is a dynamic cached mob
+ * index points to the index in the mob_list of the map_data cache.
+ * -1 indicates that it is not stored on the map.
+ *------------------------------------------
+ */
+int npc_parse_mob2 (struct spawn_data *mob, int index)
+{
+ int i;
+ struct mob_data *md;
+
+ for (i = 0; i < mob->num; i++) {
+ md = mob_spawn_dataset(mob);
+ md->spawn = mob;
+ md->spawn_n = index;
+ md->special_state.cached = (index>=0); //If mob is cached on map, it is dynamically removed
+ mob_spawn(md);
+ }
+
+ return 1;
+}
+
+int npc_parse_mob (char *w1, char *w2, char *w3, char *w4)
+{
+ int level, num, class_, mode, x,y,xs,ys;
+ char mapname[MAP_NAME_LENGTH];
+ char mobname[NAME_LENGTH];
+ struct spawn_data mob, *data;
+
+ malloc_set(&mob, 0, sizeof(struct spawn_data));
+
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3 ||
+ sscanf(w4, "%d,%d,%u,%u,%49[^\r\n]", &class_, &num, &mob.delay1, &mob.delay2, mob.eventname) < 2 ) {
+ ShowError("bad monster line : %s %s %s (file %s)\n", w1, w3, w4, current_file);
+ return 1;
+ }
+ if (!mapindex_name2id(mapname)) {
+ ShowError("wrong map name : %s %s (file %s)\n", w1,w3, current_file);
+ return 1;
+ }
+ mode = map_mapname2mapid(mapname);
+ if (mode < 0) //Not loaded on this map-server instance.
+ return 1;
+ mob.m = (unsigned short)mode;
+
+ if (x < 0 || map[mob.m].xs <= x || y < 0 || map[mob.m].ys <= y) {
+ ShowError("Out of range spawn coordinates: %s (%d,%d), map size is (%d,%d) - %s %s (file %s)\n", map[mob.m].name, x, y, map[mob.m].xs-1, map[mob.m].ys-1, w1,w3, current_file);
+ return 1;
+ }
+
+ // check monster ID if exists!
+ if (mobdb_checkid(class_)==0) {
+ ShowError("bad monster ID : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+
+ if (num < 1 || num>1000 ) {
+ ShowError("wrong number of monsters : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+
+ mob.num = (unsigned short)num;
+ mob.class_ = (short) class_;
+ mob.x = (unsigned short)x;
+ mob.y = (unsigned short)y;
+ mob.xs = (signed short)xs;
+ mob.ys = (signed short)ys;
+
+ if (mob.num > 1 && battle_config.mob_count_rate != 100) {
+ if ((mob.num = mob.num * battle_config.mob_count_rate / 100) < 1)
+ mob.num = 1;
+ }
+
+ if (battle_config.force_random_spawn || (mob.x == 0 && mob.y == 0))
+ { //Force a random spawn anywhere on the map.
+ mob.x = mob.y = 0;
+ mob.xs = mob.ys = -1;
+ }
+
+ //Apply the spawn delay fix [Skotlex]
+ mode = mob_db(class_)->status.mode;
+ if (mode & MD_BOSS) { //Bosses
+ if (battle_config.boss_spawn_delay != 100)
+ {
+ mob.delay1 = mob.delay1*battle_config.boss_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.boss_spawn_delay/100;
+ }
+ } else if (mode&MD_PLANT) { //Plants
+ if (battle_config.plant_spawn_delay != 100)
+ {
+ mob.delay1 = mob.delay1*battle_config.plant_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.plant_spawn_delay/100;
+ }
+ } else if (battle_config.mob_spawn_delay != 100)
+ { //Normal mobs
+ mob.delay1 = mob.delay1*battle_config.mob_spawn_delay/100;
+ mob.delay2 = mob.delay2*battle_config.mob_spawn_delay/100;
+ }
+
+ // parse MOB_NAME,[MOB LEVEL]
+ if (sscanf(w3, "%23[^,],%d", mobname, &level) > 1)
+ mob.level = level;
+
+ if(mob.delay1>0xfffffff || mob.delay2>0xfffffff) {
+ ShowError("wrong monsters spawn delays : %s %s (file %s)\n", w3, w4, current_file);
+ return 1;
+ }
+
+ //Use db names instead of the spawn file ones.
+ if(battle_config.override_mob_names==1)
+ strcpy(mob.name,"--en--");
+ else if (battle_config.override_mob_names==2)
+ strcpy(mob.name,"--ja--");
+ else
+ strncpy(mob.name, mobname, NAME_LENGTH-1);
+
+ if (!mob_parse_dataset(&mob)) //Verify dataset.
+ return 1;
+
+ //Now that all has been validated. We allocate the actual memory
+ //that the re-spawn data will use.
+ data = aMalloc(sizeof(struct spawn_data));
+ memcpy(data, &mob, sizeof(struct spawn_data));
+
+ if( !battle_config.dynamic_mobs || mob.delay1 || mob.delay2 ) {
+ npc_parse_mob2(data,-1);
+ npc_delay_mob += mob.num;
+ } else {
+ int index = map_addmobtolist(data->m, data);
+ if( index >= 0 ) {
+ // check if target map has players
+ // (usually shouldn't occur when map server is just starting,
+ // but not the case when we do @reloadscript
+ if (map[mob.m].users > 0)
+ npc_parse_mob2(data,index);
+ npc_cache_mob += mob.num;
+ } else {
+ // mobcache is full
+ // create them as delayed with one second
+ mob.delay1 = 1000;
+ mob.delay2 = 1000;
+ npc_parse_mob2(data,-1);
+ npc_delay_mob += mob.num;
+ }
+ }
+
+ npc_mob++;
+
+ return 0;
+}
+
+/*==========================================
+ * マップフラグ行の解析
+ *------------------------------------------
+ */
+static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4)
+{
+ int m;
+ char mapname[MAP_NAME_LENGTH];
+ int state = 1;
+
+ // 引数の個数チェック
+ if (sscanf(w1, "%15[^,]",mapname) != 1)
+ return 1;
+
+ m = map_mapname2mapid(mapname);
+ if (m < 0)
+ return 1;
+ if (w4 && strcmpi(w4, "off") == 0)
+ state = 0; //Disable mapflag rather than enable it. [Skotlex]
+
+//マップフラグ
+ if (strcmpi(w3, "nosave") == 0) {
+ char savemap[MAP_NAME_LENGTH];
+ int savex, savey;
+ if (state == 0)
+ ; //Map flag disabled.
+ else if (strcmp(w4, "SavePoint") == 0) {
+ map[m].save.map = 0;
+ map[m].save.x = -1;
+ map[m].save.y = -1;
+ } else if (sscanf(w4, "%15[^,],%d,%d", savemap, &savex, &savey) == 3) {
+ map[m].save.map = mapindex_name2id(savemap);
+ map[m].save.x = savex;
+ map[m].save.y = savey;
+ if (!map[m].save.map) {
+ ShowWarning("Specified save point map '%s' for mapflag 'nosave' not found (file %s), using 'SavePoint'.\n",savemap,current_file);
+ map[m].save.x = -1;
+ map[m].save.y = -1;
+ }
+ }
+ map[m].flag.nosave = state;
+ }
+ else if (strcmpi(w3,"nomemo")==0) {
+ map[m].flag.nomemo=state;
+ }
+ else if (strcmpi(w3,"noteleport")==0) {
+ map[m].flag.noteleport=state;
+ }
+ else if (strcmpi(w3,"nowarp")==0) {
+ map[m].flag.nowarp=state;
+ }
+ else if (strcmpi(w3,"nowarpto")==0) {
+ map[m].flag.nowarpto=state;
+ }
+ else if (strcmpi(w3,"noreturn")==0) {
+ map[m].flag.noreturn=state;
+ }
+ else if (strcmpi(w3,"monster_noteleport")==0) {
+ map[m].flag.monster_noteleport=state;
+ }
+ else if (strcmpi(w3,"nobranch")==0) {
+ map[m].flag.nobranch=state;
+ }
+ else if (strcmpi(w3,"nopenalty")==0) {
+ map[m].flag.noexppenalty=state;
+ map[m].flag.nozenypenalty=state;
+ }
+ else if (strcmpi(w3,"pvp")==0) {
+ map[m].flag.pvp=state;
+ if (state) {
+ map[m].flag.gvg=0;
+ map[m].flag.gvg=0;
+ map[m].flag.gvg_dungeon=0;
+ map[m].flag.gvg_castle=0;
+ }
+ }
+ else if (strcmpi(w3,"pvp_noparty")==0) {
+ map[m].flag.pvp_noparty=state;
+ }
+ else if (strcmpi(w3,"pvp_noguild")==0) {
+ map[m].flag.pvp_noguild=state;
+ }
+ else if (strcmpi(w3, "pvp_nightmaredrop") == 0) {
+ char drop_arg1[16], drop_arg2[16];
+ int drop_id = 0, drop_type = 0, drop_per = 0;
+ if (sscanf(w4, "%[^,],%[^,],%d", drop_arg1, drop_arg2, &drop_per) == 3) {
+ int i;
+ if (strcmp(drop_arg1, "random") == 0)
+ drop_id = -1;
+ else if (itemdb_exists((drop_id = atoi(drop_arg1))) == NULL)
+ drop_id = 0;
+ if (strcmp(drop_arg2, "inventory") == 0)
+ drop_type = 1;
+ else if (strcmp(drop_arg2,"equip") == 0)
+ drop_type = 2;
+ else if (strcmp(drop_arg2,"all") == 0)
+ drop_type = 3;
+
+ if (drop_id != 0){
+ for (i = 0; i < MAX_DROP_PER_MAP; i++) {
+ if (map[m].drop_list[i].drop_id == 0){
+ map[m].drop_list[i].drop_id = drop_id;
+ map[m].drop_list[i].drop_type = drop_type;
+ map[m].drop_list[i].drop_per = drop_per;
+ break;
+ }
+ }
+ map[m].flag.pvp_nightmaredrop = 1;
+ }
+ } else if (!state) //Disable
+ map[m].flag.pvp_nightmaredrop = 0;
+ }
+ else if (strcmpi(w3,"pvp_nocalcrank")==0) {
+ map[m].flag.pvp_nocalcrank=state;
+ }
+ else if (strcmpi(w3,"gvg")==0) {
+ map[m].flag.gvg=state;
+ if (state) map[m].flag.pvp=0;
+ }
+ else if (strcmpi(w3,"gvg_noparty")==0) {
+ map[m].flag.gvg_noparty=state;
+ }
+ else if (strcmpi(w3,"gvg_dungeon")==0) {
+ map[m].flag.gvg_dungeon=state;
+ if (state) map[m].flag.pvp=0;
+ }
+ else if (strcmpi(w3,"gvg_castle")==0) {
+ map[m].flag.gvg_castle=state;
+ if (state) map[m].flag.pvp=0;
+ }
+ else if (strcmpi(w3,"noexppenalty")==0) {
+ map[m].flag.noexppenalty=state;
+ }
+ else if (strcmpi(w3,"nozenypenalty")==0) {
+ map[m].flag.nozenypenalty=state;
+ }
+ else if (strcmpi(w3,"notrade")==0) {
+ map[m].flag.notrade=state;
+ }
+ else if (strcmpi(w3,"novending")==0) {
+ map[m].flag.novending=state;
+ }
+ else if (strcmpi(w3,"nodrop")==0) {
+ map[m].flag.nodrop=state;
+ }
+ else if (strcmpi(w3,"noskill")==0) {
+ map[m].flag.noskill=state;
+ }
+ else if (strcmpi(w3,"noicewall")==0) { // noicewall [Valaris]
+ map[m].flag.noicewall=state;
+ }
+ else if (strcmpi(w3,"snow")==0) { // snow [Valaris]
+ map[m].flag.snow=state;
+ }
+ else if (strcmpi(w3,"clouds")==0) {
+ map[m].flag.clouds=state;
+ }
+ else if (strcmpi(w3,"clouds2")==0) { // clouds2 [Valaris]
+ map[m].flag.clouds2=state;
+ }
+ else if (strcmpi(w3,"fog")==0) { // fog [Valaris]
+ map[m].flag.fog=state;
+ }
+ else if (strcmpi(w3,"fireworks")==0) {
+ map[m].flag.fireworks=state;
+ }
+ else if (strcmpi(w3,"sakura")==0) { // sakura [Valaris]
+ map[m].flag.sakura=state;
+ }
+ else if (strcmpi(w3,"leaves")==0) { // leaves [Valaris]
+ map[m].flag.leaves=state;
+ }
+ else if (strcmpi(w3,"rain")==0) { // rain [Valaris]
+ map[m].flag.rain=state;
+ }
+ else if (strcmpi(w3,"indoors")==0) { // celest
+ map[m].flag.indoors=state;
+ }
+ else if (strcmpi(w3,"nightenabled")==0) { // Skotlex
+ map[m].flag.nightenabled=state;
+ }
+ else if (strcmpi(w3,"nogo")==0) { // celest
+ map[m].flag.nogo=state;
+ }
+ else if (strcmpi(w3,"noexp")==0) { // Lorky
+ map[m].flag.nobaseexp=state;
+ map[m].flag.nojobexp=state;
+ }
+ else if (strcmpi(w3,"nobaseexp")==0) { // Lorky
+ map[m].flag.nobaseexp=state;
+ }
+ else if (strcmpi(w3,"nojobexp")==0) { // Lorky
+ map[m].flag.nojobexp=state;
+ }
+ else if (strcmpi(w3,"noloot")==0) { // Lorky
+ map[m].flag.nomobloot=state;
+ map[m].flag.nomvploot=state;
+ }
+ else if (strcmpi(w3,"nomobloot")==0) { // Lorky
+ map[m].flag.nomobloot=state;
+ }
+ else if (strcmpi(w3,"nomvploot")==0) { // Lorky
+ map[m].flag.nomvploot=state;
+ }
+ else if (strcmpi(w3,"nocommand")==0) { // Skotlex
+ if (state) {
+ if (sscanf(w4, "%d", &state) == 1)
+ map[m].nocommand =state;
+ else //No level specified, block everyone.
+ map[m].nocommand =100;
+ } else
+ map[m].nocommand=0;
+ }
+ else if (strcmpi(w3,"restricted")==0) { // Komurka
+ if (state) {
+ map[m].flag.restricted=1;
+ sscanf(w4, "%d", &state);
+ map[m].zone |= 1<<(state+1);
+ } else {
+ map[m].flag.restricted=0;
+ map[m].zone = 0;
+ }
+ }
+ else if (strcmpi(w3,"jexp")==0) {
+ map[m].jexp = (state) ? atoi(w4) : 100;
+ if( map[m].jexp < 0 ) map[m].jexp = 100;
+ map[m].flag.nojobexp = (map[m].jexp==0)?1:0;
+ }
+ else if (strcmpi(w3,"bexp")==0) {
+ map[m].bexp = (state) ? atoi(w4) : 100;
+ if( map[m].bexp < 0 ) map[m].bexp = 100;
+ map[m].flag.nobaseexp = (map[m].bexp==0)?1:0;
+ }
+ else if (strcmpi(w3,"loadevent")==0) { // Skotlex
+ map[m].flag.loadevent=state;
+ }
+ else if (strcmpi(w3,"nochat")==0) { // Skotlex
+ map[m].flag.nochat=state;
+ }
+ else if (strcmpi(w3,"partylock")==0) { // Skotlex
+ map[m].flag.partylock=state;
+ }
+ else if (strcmpi(w3,"guildlock")==0) { // Skotlex
+ map[m].flag.guildlock=state;
+ }
+
+ return 0;
+}
+
+/*==========================================
+ * Setting up map cells
+ *------------------------------------------
+ */
+static int npc_parse_mapcell (char *w1, char *w2, char *w3, char *w4)
+{
+ int m, cell, x, y, x0, y0, x1, y1;
+ char type[24], mapname[MAP_NAME_LENGTH];
+
+ if (sscanf(w1, "%15[^,]", mapname) != 1)
+ return 1;
+
+ m = map_mapname2mapid(mapname);
+ if (m < 0)
+ return 1;
+
+ if (sscanf(w3, "%23[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 4) {
+ ShowError("Bad setcell line : %s\n",w3);
+ return 1;
+ }
+ cell = strtol(type, (char **)NULL, 0);
+ //printf ("0x%x %d %d %d %d\n", cell, x0, y0, x1, y1);
+
+ if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
+ if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
+
+ for (x = x0; x <= x1; x++) {
+ for (y = y0; y <= y1; y++) {
+ map_setcell(m, x, y, cell);
+ //printf ("setcell 0x%x %d %d %d\n", cell, m, x, y);
+ }
+ }
+
+ return 0;
+}
+
+void npc_parsesrcfile (char *name)
+{
+ int m, lines = 0;
+ char line[2048];
+
+ FILE *fp = fopen (name,"r");
+ if (fp == NULL) {
+ ShowError ("File not found : %s\n", name);
+ return;
+ }
+ current_file = name;
+
+ while (fgets(line, sizeof(line) - 1, fp)) {
+ char w1[2048], w2[2048], w3[2048], w4[2048], mapname[2048];
+ int i, w4pos, count;
+ lines++;
+
+ if (line[0] == '/' && line[1] == '/')
+ continue;
+
+ if (!sscanf(line, " %n", &i) && i == strlen(line)) // just whitespace
+ continue;
+
+ // 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認
+ w1[0] = w2[0] = w3[0] = w4[0] = '\0'; //It's best to initialize values
+ //to prevent passing previously parsed values to the parsers when not all
+ //fields are specified. [Skotlex]
+ if ((count = sscanf(line, "%[^\t\r\n]\t%[^\t\r\n]\t%[^\t\r\n]\t%n%[^\r\n]", w1, w2, w3, &w4pos, w4)) < 3)
+ {
+ if ((count = sscanf(line, "%s %s %[^\t]\t %n%[^\n]", w1, w2, w3, &w4pos, w4)) == 4 ||
+ (count = sscanf(line, "%s %s %s %n%[^\n]\n", w1, w2, w3, &w4pos, w4)) >= 3)
+ {
+ ShowWarning("\r");
+ ShowWarning("Incorrect separator syntax in file '%s', line '%i'. Use tabs instead of spaces!\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
+ } else {
+ ShowError("\r"); //Erase the npc spinner.
+ ShowError("Could not parse file '%s', line '%i'.\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
+ continue;
+ }
+ }
+
+ // マップの存在確認
+ if (strcmp(w1,"-") !=0 && strcmpi(w1,"function") != 0 ){
+ sscanf(w1,"%[^,]",mapname);
+ if (!mapindex_name2id(mapname)) { //Incorrect map
+ ShowError("Invalid map '%s' in line %d, file %s\n", mapname, lines, current_file);
+ if (strcmpi(w2,"script") == 0 && count > 3) //we must skip the script info...
+ npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ continue;
+ }
+ if ((m = map_mapname2mapid(mapname)) < 0) {
+ // "mapname" is not assigned to this server
+ // we must skip the script info...
+ if (strcmpi(w2,"script") == 0 && count > 3)
+ npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+ continue;
+ }
+ }
+ if (strcmpi(w2,"warp") == 0 && count > 3) {
+ npc_parse_warp(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"shop") == 0 && count > 3) {
+ npc_parse_shop(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"script") == 0 && count > 3) {
+ if (strcmpi(w1,"function") == 0) {
+ npc_parse_function(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
+ } else {
+ npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
+ }
+ } else if ((i = 0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3) {
+ npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
+ } else if (strcmpi(w2,"monster") == 0 && count > 3) {
+ npc_parse_mob(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"mapflag") == 0 && count >= 3) {
+ npc_parse_mapflag(w1,w2,w3,w4);
+ } else if (strcmpi(w2,"setcell") == 0 && count >= 3) {
+ npc_parse_mapcell(w1,w2,w3,w4);
+ } else {
+ ShowError("Probably TAB is missing in file '%s', line '%i':\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
+ }
+ }
+ fclose(fp);
+
+ return;
+}
+
+int npc_script_event(TBL_PC* sd, int type) {
+ int i;
+ if (type < 0 || type >= NPCE_MAX)
+ return 0;
+ if (!sd) {
+ if (battle_config.error_log)
+ ShowError("npc_script_event: NULL sd. Event Type %d\n", type);
+ return 0;
+ }
+ if (script_event[type].nd) {
+ TBL_NPC *nd = script_event[type].nd;
+ run_script(nd->u.scr.script,0,sd->bl.id,nd->bl.id);
+ return 1;
+ } else if (script_event[type].event_count) {
+ for (i = 0; i<script_event[type].event_count; i++) {
+ npc_event_sub(sd,script_event[type].event[i],script_event[type].event_name[i]);
+ }
+ return i;
+ }
+ return 0;
+}
+
+static int npc_read_event_script_sub(DBKey key,void *data,va_list ap)
+{
+ unsigned char *p = key.str;
+ unsigned char *name = va_arg(ap,unsigned char *);
+ struct event_data **event_buf = va_arg(ap,struct event_data**);
+ unsigned char **event_name = va_arg(ap,unsigned char **);
+ unsigned char *count = va_arg(ap,char *);;
+
+ if (*count >= UCHAR_MAX) return 0;
+
+ if((p=strchr(p,':')) && p && strcmpi(name,p)==0 )
+ {
+ event_buf[*count] = (struct event_data *)data;
+ event_name[*count] = key.str;
+ (*count)++;
+ return 1;
+ }
+ return 0;
+}
+
+static void npc_read_event_script(void)
+{
+ int i;
+ unsigned char buf[64]="::";
+ struct {
+ char *name;
+ char *event_name;
+ } config[] = {
+ {"Login Event",script_config.login_event_name},
+ {"Logout Event",script_config.logout_event_name},
+ {"Load Map Event",script_config.loadmap_event_name},
+ {"Base LV Up Event",script_config.baselvup_event_name},
+ {"Job LV Up Event",script_config.joblvup_event_name},
+ {"Die Event",script_config.die_event_name},
+ {"Kill PC Event",script_config.kill_pc_event_name},
+ {"Kill NPC Event",script_config.kill_mob_event_name},
+ };
+
+ for (i = 0; i < NPCE_MAX; i++) {
+ if (script_event[i].nd)
+ script_event[i].nd = NULL;
+ if (script_event[i].event_count)
+ script_event[i].event_count = 0;
+ if (!script_config.event_script_type) {
+ //Use a single NPC as event source.
+ script_event[i].nd = npc_name2id(config[i].event_name);
+ } else {
+ //Use an array of Events
+ strncpy(buf+2,config[i].event_name,62);
+ ev_db->foreach(ev_db,npc_read_event_script_sub,buf,
+ &script_event[i].event,
+ &script_event[i].event_name,
+ &script_event[i].event_count);
+ }
+ }
+ if (battle_config.etc_log) {
+ //Print summary.
+ for (i = 0; i < NPCE_MAX; i++) {
+ if(!script_config.event_script_type) {
+ if (script_event[i].nd)
+ ShowInfo("%s: Using NPC named '%s'.\n", config[i].name, config[i].event_name);
+ else
+ ShowInfo("%s: No NPC found with name '%s'.\n", config[i].name, config[i].event_name);
+ } else
+ ShowInfo("%s: %d '%s' events.\n", config[i].name, script_event[i].event_count, config[i].event_name);
+ }
+ }
+}
+static int npc_read_indoors (void)
+{
+ char *buf, *p;
+ int s, m;
+
+ buf = (char *)grfio_reads("data\\indoorrswtable.txt",&s);
+ if (buf == NULL)
+ return -1;
+ buf[s] = 0;
+
+ for (p = buf; p - buf < s; ) {
+ char map_name[64];
+ if (sscanf(p, "%15[^#]#", map_name) == 1) {
+ size_t pos = strlen(map_name) - 4; // replace '.xxx' extension
+ memcpy(map_name+pos,".gat",4); // with '.gat'
+ if ((m = map_mapname2mapid(map_name)) >= 0)
+ map[m].flag.indoors = 1;
+ }
+
+ p = strchr(p, 10);
+ if (!p) break;
+ p++;
+ }
+ aFree(buf);
+ ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\indoorrswtable.txt");
+
+ return 0;
+}
+
+/*==========================================
+ *
+ *------------------------------------------
+ */
+
+static int npc_cleanup_sub (struct block_list *bl, va_list ap) {
+ nullpo_retr(0, bl);
+
+ switch(bl->type) {
+ case BL_NPC:
+ npc_unload((struct npc_data *)bl);
+ break;
+ case BL_MOB:
+ //This is used only on reloading npcs, so let's not free spawn-once mobs. [Skotlex]
+ if (((TBL_MOB*)bl)->spawn)
+ unit_free(bl,0);
+ break;
+ }
+
+ return 0;
+}
+
+static int npc_cleanup_dbsub(DBKey key,void * data,va_list ap) {
+ return npc_cleanup_sub((struct block_list*)data, 0);
+}
+
+int npc_reload (void)
+{
+ struct npc_src_list *nsl;
+ int m, i;
+ time_t last_time = time(0);
+ int busy = 0, npc_new_min = npc_id;
+ char c = '-';
+
+ //Remove all npcs/mobs. [Skotlex]
+ map_foreachiddb(npc_cleanup_dbsub);
+ for (m = 0; m < map_num; m++) {
+ if(battle_config.dynamic_mobs) { //dynamic check by [random]
+ for (i = 0; i < MAX_MOB_LIST_PER_MAP; i++)
+ if (map[m].moblist[i]) aFree(map[m].moblist[i]);
+ malloc_set (map[m].moblist, 0, sizeof(map[m].moblist));
+ }
+ if (map[m].npc_num > 0 && battle_config.error_log)
+ ShowWarning("npc_reload: %d npcs weren't removed at map %s!\n", map[m].npc_num, map[m].name);
+ }
+
+ // anything else we should cleanup?
+ // Reloading npc's now
+ ev_db->clear(ev_db,NULL);
+ npcname_db->clear(npcname_db,NULL);
+ npc_warp = npc_shop = npc_script = 0;
+ npc_mob = npc_cache_mob = npc_delay_mob = 0;
+
+ for (nsl = npc_src_first; nsl; nsl = nsl->next) {
+ npc_parsesrcfile(nsl->name);
+ if (script_config.verbose_mode) {
+ printf("\r");
+ ShowStatus("Loading NPCs... %-53s", nsl->name);
+ } else {
+ if (last_time != time(0)) {
+ printf("\r");
+ ShowStatus("Loading NPCs... Working: ");
+ last_time = time(0);
+ switch(busy) {
+ case 0: c='\\'; busy++; break;
+ case 1: c='|'; busy++; break;
+ case 2: c='/'; busy++; break;
+ case 3: c='-'; busy=0;
+ }
+ printf("[%c]",c);
+ }
+ }
+ fflush(stdout);
+ }
+ printf("\r");
+ ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
+ npc_id - npc_new_min, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
+
+ //Re-read the NPC Script Events cache.
+ npc_read_event_script();
+
+ //Execute the OnInit event for freshly loaded npcs. [Skotlex]
+ ShowStatus("Event '"CL_WHITE"OnInit"CL_RESET"' executed with '"
+ CL_WHITE"%d"CL_RESET"' NPCs.\n",npc_event_doall("OnInit"));
+ // Execute rest of the startup events if connected to char-server. [Lance]
+ if(!CheckForCharServer()){
+ ShowStatus("Event '"CL_WHITE"OnCharIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnCharIfInit"));
+ ShowStatus("Event '"CL_WHITE"OnInterIfInit"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInit"));
+ ShowStatus("Event '"CL_WHITE"OnInterIfInitOnce"CL_RESET"' executed with '"CL_WHITE"%d"CL_RESET"' NPCs.\n", npc_event_doall("OnInterIfInitOnce"));
+ }
+ return 0;
+}
+
+/*==========================================
+ * 終了
+ *------------------------------------------
+ */
+int do_final_npc(void)
+{
+ int i;
+ struct block_list *bl;
+
+ for (i = START_NPC_NUM; i < npc_id; i++){
+ if ((bl = map_id2bl(i))){
+ if (bl->type == BL_NPC)
+ npc_unload((struct npc_data *)bl);
+ else if (bl->type&(BL_MOB|BL_PET))
+ unit_free(bl, 0);
+ }
+ }
+
+ ev_db->destroy(ev_db, NULL);
+ //There is no free function for npcname_db because at this point there shouldn't be any npcs left!
+ //So if there is anything remaining, let the memory manager catch it and report it.
+ npcname_db->destroy(npcname_db, NULL);
+ ers_destroy(timer_event_ers);
+ npc_clearsrcfile();
+
+ return 0;
+}
+
+static void npc_debug_warps_sub(struct npc_data *nd)
+{
+ int m;
+ if (nd->bl.type != BL_NPC || nd->bl.subtype != WARP || nd->bl.m < 0)
+ return;
+
+ m = map_mapindex2mapid(nd->u.warp.mapindex);
+ if (m < 0) return; //Warps to another map, nothing to do about it.
+
+ if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNPC)) {
+ ShowWarning("Warp %s at %s(%d,%d) warps directly on top of an area npc at %s(%d,%d)\n",
+ nd->name,
+ map[nd->bl.m].name, nd->bl.x, nd->bl.y,
+ map[m].name, nd->u.warp.x, nd->u.warp.y
+ );
+ }
+ if (map_getcell(m, nd->u.warp.x, nd->u.warp.y, CELL_CHKNOPASS)) {
+ ShowWarning("Warp %s at %s(%d,%d) warps to a non-walkable tile at %s(%d,%d)\n",
+ nd->name,
+ map[nd->bl.m].name, nd->bl.x, nd->bl.y,
+ map[m].name, nd->u.warp.x, nd->u.warp.y
+ );
+ }
+}
+
+static void npc_debug_warps(void)
+{
+ int m, i;
+ for (m = 0; m < map_num; m++)
+ for (i = 0; i < map[m].npc_num; i++)
+ npc_debug_warps_sub(map[m].npc[i]);
+}
+
+/*==========================================
+ * npc初期化
+ *------------------------------------------
+ */
+int do_init_npc(void)
+{
+ struct npc_src_list *nsl;
+ time_t last_time = time(0);
+ int busy, i;
+ char c = '-';
+
+ //Stock view data for normal npcs.
+ malloc_set(&npc_viewdb, 0, sizeof(npc_viewdb));
+ npc_viewdb[0].class_ = INVISIBLE_CLASS; //Invisible class is stored here.
+ for (busy = 1; busy < MAX_NPC_CLASS; busy++)
+ npc_viewdb[busy].class_ = busy;
+ busy = 0;
+ // indoorrswtable.txt and etcinfo.txt [Celest]
+ if (battle_config.indoors_override_grffile)
+ npc_read_indoors();
+
+ // comparing only the first 24 chars of labels that are 50 chars long isn't that nice
+ // will cause "duplicated" labels where actually no dup is...
+ ev_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_RELEASE_DATA,51);
+ npcname_db = db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_BASE,NAME_LENGTH);
+
+ malloc_set(&ev_tm_b, -1, sizeof(ev_tm_b));
+ timer_event_ers = ers_new((uint32)sizeof(struct timer_event_data));
+
+ for (nsl = npc_src_first; nsl; nsl = nsl->next) {
+ npc_parsesrcfile(nsl->name);
+ current_file = NULL;
+ printf("\r");
+ if (script_config.verbose_mode)
+ ShowStatus ("Loading NPCs... %-53s", nsl->name);
+ else {
+ ShowStatus("Loading NPCs... Working: ");
+ if (last_time != time(0)) {
+ last_time = time(0);
+ switch(busy) {
+ case 0: c='\\'; busy++; break;
+ case 1: c='|'; busy++; break;
+ case 2: c='/'; busy++; break;
+ case 3: c='-'; busy=0;
+ }
+ }
+ printf("[%c]",c);
+ }
+ fflush(stdout);
+ }
+ printf("\r");
+ ShowInfo ("Done loading '"CL_WHITE"%d"CL_RESET"' NPCs:%30s\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Warps\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Shops\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Scripts\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Cached\n\t-'"
+ CL_WHITE"%d"CL_RESET"' Mobs Not Cached\n",
+ npc_id - START_NPC_NUM, "", npc_warp, npc_shop, npc_script, npc_mob, npc_cache_mob, npc_delay_mob);
+
+ malloc_set(script_event, 0, sizeof(script_event));
+ npc_read_event_script();
+ //Debug function to locate all endless loop warps.
+ if (battle_config.warp_point_debug)
+ npc_debug_warps();
+
+ add_timer_func_list(npc_event_timer,"npc_event_timer");
+ add_timer_func_list(npc_event_do_clock,"npc_event_do_clock");
+ add_timer_func_list(npc_timerevent,"npc_timerevent");
+
+ // Init dummy NPC
+ fake_nd = (struct npc_data *)aCalloc(sizeof(struct npc_data),1);
+ fake_nd->bl.prev = fake_nd->bl.next = NULL;
+ fake_nd->bl.m = -1;
+ fake_nd->bl.x = 0;
+ fake_nd->bl.y = 0;
+ fake_nd->bl.id = npc_get_new_npc_id();
+ fake_nd->class_ = -1;
+ fake_nd->speed = 200;
+ fake_nd->u.scr.script = NULL;
+ fake_nd->u.scr.src_id = 0;
+ fake_nd->chatdb = NULL;
+ for (i = 0; i < MAX_EVENTTIMER; i++)
+ fake_nd->eventtimer[i] = -1;
+ strcpy(fake_nd->name,"FAKE_NPC");
+ memcpy(fake_nd->exname, fake_nd->name, 9);
+
+ npc_script++;
+ fake_nd->bl.type = BL_NPC;
+ fake_nd->bl.subtype = SCRIPT;
+
+ strdb_put(npcname_db, fake_nd->exname, fake_nd);
+ fake_nd->u.scr.timerid = -1;
+ map_addiddb(&fake_nd->bl);
+ // End of initialization
+
+ return 0;
+}
+// [Lance]
+int npc_changename(const char *name, const char *newname, short look){
+ struct npc_data *nd= (struct npc_data *) strdb_remove(npcname_db,(unsigned char*)name);
+ if (nd==NULL)
+ return 0;
+ npc_enable(name,0);
+ strcpy(nd->name,newname);
+ nd->class_ = look;
+ npc_enable(newname,1);
+ return 0;
+}
diff --git a/src/map/npc.h b/src/map/npc.h
index 2ea4f7eb7..63772089c 100644
--- a/src/map/npc.h
+++ b/src/map/npc.h
@@ -1,90 +1,90 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _NPC_H_
-#define _NPC_H_
-
-#define START_NPC_NUM 110000000
-
-#define WARP_CLASS 45
-#define WARP_DEBUG_CLASS 722
-#define INVISIBLE_CLASS 32767
-
-#define MAX_NPC_CLASS 1000
-//Checks if a given id is a valid npc id. [Skotlex]
-//Since new npcs are added all the time, the max valid value is the one before the first mob (Scorpion = 1001)
-#define npcdb_checkid(id) ((id >= 46 && id <= 125) || id == 139 || (id >= 700 && id <= MAX_NPC_CLASS) || id == INVISIBLE_CLASS)
-
-#ifdef PCRE_SUPPORT
-void npc_chat_finalize(struct npc_data *nd);
-int mob_chat_sub(struct block_list *bl, va_list ap);
-#endif
-
-//Script NPC events.
-enum {
- NPCE_LOGIN,
- NPCE_LOGOUT,
- NPCE_LOADMAP,
- NPCE_BASELVUP,
- NPCE_JOBLVUP,
- NPCE_DIE,
- NPCE_KILLPC,
- NPCE_KILLNPC,
- NPCE_MAX
-};
-struct view_data* npc_get_viewdata(int class_);
-int npc_chat_sub(struct block_list *bl, va_list ap);
-int npc_event_dequeue(struct map_session_data *sd);
-int npc_event_timer(int tid,unsigned int tick,int id,int data);
-int npc_event(struct map_session_data *sd,const unsigned char *npcname,int);
-int npc_timer_event(const unsigned char *eventname); // Added by RoVeRT
-int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command);
-int npc_touch_areanpc(struct map_session_data *,int,int,int);
-int npc_touch_areanpc2(struct block_list *bl); // [Skotlex]
-int npc_click(struct map_session_data *sd,struct npc_data *nd);
-int npc_scriptcont(struct map_session_data *,int);
-TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl);
-int npc_checknear2(struct map_session_data *sd,struct block_list *bl);
-int npc_buysellsel(struct map_session_data *,int,int);
-int npc_buylist(struct map_session_data *,int,unsigned short *);
-int npc_selllist(struct map_session_data *,int,unsigned short *);
-int npc_parse_mob(char *w1,char *w2,char *w3,char *w4);
-int npc_parse_mob2 (struct spawn_data*, int index); // [Wizputer]
-int npc_parse_warp(char *w1,char *w2,char *w3,char *w4);
-int npc_globalmessage(const char *name,char *mes);
-
-int npc_enable(const char *name,int flag);
-int npc_changename(const char *name, const char *newname, short look); // [Lance]
-struct npc_data* npc_name2id(const char *name);
-
-int npc_get_new_npc_id(void);
-
-void npc_addsrcfile(char *);
-void npc_delsrcfile(char *);
-void npc_parsesrcfile(char *);
-int do_final_npc(void);
-int do_init_npc(void);
-int npc_event_do_oninit(void);
-int npc_do_ontimer(int,int);
-
-int npc_event_doall(const unsigned char *name);
-int npc_event_do(const unsigned char *name);
-int npc_event_doall_id(const unsigned char *name, int id);
-
-int npc_timerevent_start(struct npc_data *nd, int rid);
-int npc_timerevent_stop(struct npc_data *nd);
-void npc_timerevent_quit(struct map_session_data *sd);
-int npc_gettimerevent_tick(struct npc_data *nd);
-int npc_settimerevent_tick(struct npc_data *nd,int newtimer);
-int npc_remove_map(struct npc_data *nd);
-void npc_unload_duplicates (struct npc_data *nd);
-int npc_unload(struct npc_data *nd);
-int npc_reload(void);
-int npc_script_event(TBL_PC* sd, int type);
-
-extern char *current_file;
-
-struct npc_data *fake_nd;
-
-#endif
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _NPC_H_
+#define _NPC_H_
+
+#define START_NPC_NUM 110000000
+
+#define WARP_CLASS 45
+#define WARP_DEBUG_CLASS 722
+#define INVISIBLE_CLASS 32767
+
+#define MAX_NPC_CLASS 1000
+//Checks if a given id is a valid npc id. [Skotlex]
+//Since new npcs are added all the time, the max valid value is the one before the first mob (Scorpion = 1001)
+#define npcdb_checkid(id) ((id >= 46 && id <= 125) || id == 139 || (id >= 700 && id <= MAX_NPC_CLASS) || id == INVISIBLE_CLASS)
+
+#ifdef PCRE_SUPPORT
+void npc_chat_finalize(struct npc_data *nd);
+int mob_chat_sub(struct block_list *bl, va_list ap);
+#endif
+
+//Script NPC events.
+enum {
+ NPCE_LOGIN,
+ NPCE_LOGOUT,
+ NPCE_LOADMAP,
+ NPCE_BASELVUP,
+ NPCE_JOBLVUP,
+ NPCE_DIE,
+ NPCE_KILLPC,
+ NPCE_KILLNPC,
+ NPCE_MAX
+};
+struct view_data* npc_get_viewdata(int class_);
+int npc_chat_sub(struct block_list *bl, va_list ap);
+int npc_event_dequeue(struct map_session_data *sd);
+int npc_event_timer(int tid,unsigned int tick,int id,int data);
+int npc_event(struct map_session_data *sd,const unsigned char *npcname,int);
+int npc_timer_event(const unsigned char *eventname); // Added by RoVeRT
+int npc_command(struct map_session_data *sd,const unsigned char *npcname,char *command);
+int npc_touch_areanpc(struct map_session_data *,int,int,int);
+int npc_touch_areanpc2(struct block_list *bl); // [Skotlex]
+int npc_click(struct map_session_data *sd,struct npc_data *nd);
+int npc_scriptcont(struct map_session_data *,int);
+TBL_NPC *npc_checknear(struct map_session_data *sd,struct block_list *bl);
+int npc_checknear2(struct map_session_data *sd,struct block_list *bl);
+int npc_buysellsel(struct map_session_data *,int,int);
+int npc_buylist(struct map_session_data *,int,unsigned short *);
+int npc_selllist(struct map_session_data *,int,unsigned short *);
+int npc_parse_mob(char *w1,char *w2,char *w3,char *w4);
+int npc_parse_mob2 (struct spawn_data*, int index); // [Wizputer]
+int npc_parse_warp(char *w1,char *w2,char *w3,char *w4);
+int npc_globalmessage(const char *name,char *mes);
+
+int npc_enable(const char *name,int flag);
+int npc_changename(const char *name, const char *newname, short look); // [Lance]
+struct npc_data* npc_name2id(const char *name);
+
+int npc_get_new_npc_id(void);
+
+void npc_addsrcfile(char *);
+void npc_delsrcfile(char *);
+void npc_parsesrcfile(char *);
+int do_final_npc(void);
+int do_init_npc(void);
+int npc_event_do_oninit(void);
+int npc_do_ontimer(int,int);
+
+int npc_event_doall(const unsigned char *name);
+int npc_event_do(const unsigned char *name);
+int npc_event_doall_id(const unsigned char *name, int id);
+
+int npc_timerevent_start(struct npc_data *nd, int rid);
+int npc_timerevent_stop(struct npc_data *nd);
+void npc_timerevent_quit(struct map_session_data *sd);
+int npc_gettimerevent_tick(struct npc_data *nd);
+int npc_settimerevent_tick(struct npc_data *nd,int newtimer);
+int npc_remove_map(struct npc_data *nd);
+void npc_unload_duplicates (struct npc_data *nd);
+int npc_unload(struct npc_data *nd);
+int npc_reload(void);
+int npc_script_event(TBL_PC* sd, int type);
+
+extern char *current_file;
+
+struct npc_data *fake_nd;
+
+#endif
+
diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c
index 1e5da1277..f7c955257 100644
--- a/src/map/npc_chat.c
+++ b/src/map/npc_chat.c
@@ -1,517 +1,517 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifdef PCRE_SUPPORT
-
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-
-#include "../common/timer.h"
-#include "../common/malloc.h"
-#include "../common/version.h"
-#include "../common/nullpo.h"
-#include "../common/showmsg.h"
-
-#include "map.h"
-#include "status.h"
-#include "npc.h"
-#include "chat.h"
-#include "script.h"
-#include "battle.h"
-
-#include "pcre.h"
-
-/**
- * Written by MouseJstr in a vision... (2/21/2005)
- *
- * This allows you to make npc listen for spoken text (global
- * messages) and pattern match against that spoken text using perl
- * regular expressions.
- *
- * Please feel free to copy this code into your own personal ragnarok
- * servers or distributions but please leave my name. Also, please
- * wait until I've put it into the main eA branch which means I
- * believe it is ready for distribution.
- *
- * So, how do people use this?
- *
- * The first and most important function is defpattern
- *
- * defpattern 1, "[^:]+: (.*) loves (.*)", "label";
- *
- * this defines a new pattern in set 1 using perl syntax
- * (http://www.troubleshooters.com/codecorn/littperl/perlreg.htm)
- * and tells it to jump to the supplied label when the pattern
- * is matched.
- *
- * each of the matched Groups will result in a variable being
- * set ($p1$ through $p9$ with $p0$ being the entire string)
- * before the script gets executed.
- *
- * activatepset 1;
- *
- * This activates a set of patterns.. You can have many pattern
- * sets defined and many active all at once. This feature allows
- * you to set up "conversations" and ever changing expectations of
- * the pattern matcher
- *
- * deactivatepset 1;
- *
- * turns off a pattern set;
- *
- * deactivatepset -1;
- *
- * turns off ALL pattern sets;
- *
- * deletepset 1;
- *
- * deletes a pset
- */
-
-/* Structure containing all info associated with a single pattern
- block */
-
-struct pcrematch_entry {
- struct pcrematch_entry *next_;
- char *pattern_;
- pcre *pcre_;
- pcre_extra *pcre_extra_;
- char *label_;
-};
-
-/* A set of patterns that can be activated and deactived with a single
- command */
-
-struct pcrematch_set {
- struct pcrematch_set *next_, *prev_;
- struct pcrematch_entry *head_;
- int setid_;
-};
-
-/*
- * Entire data structure hung off a NPC
- *
- * The reason I have done it this way (a void * in npc_data and then
- * this) was to reduce the number of patches that needed to be applied
- * to a ragnarok distribution to bring this code online. I
- * also wanted people to be able to grab this one file to get updates
- * without having to do a large number of changes.
- */
-
-struct npc_parse {
- struct pcrematch_set *active_;
- struct pcrematch_set *inactive_;
-};
-
-
-/**
- * delete everythign associated with a entry
- *
- * This does NOT do the list management
- */
-
-void finalize_pcrematch_entry(struct pcrematch_entry *e) {
-//TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex]
-#ifndef _WIN32
- if (e->pcre_) {
- free(e->pcre_);
- e->pcre_ = NULL;
- }
-#endif
- if (e->pcre_extra_) {
- free(e->pcre_extra_);
- e->pcre_ = NULL;
- }
- aFree(e->pattern_);
- aFree(e->label_);
-}
-
-/**
- * Lookup (and possibly create) a new set of patterns by the set id
- */
-static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid)
-{
- struct pcrematch_set *pcreset;
- struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
- if (npcParse == NULL)
- nd->chatdb = npcParse = (struct npc_parse *)
- aCalloc(sizeof(struct npc_parse), 1);
-
- pcreset = npcParse->active_;
-
- while (pcreset != NULL) {
- if (pcreset->setid_ == setid)
- break;
- pcreset = pcreset->next_;
- }
- if (pcreset == NULL)
- pcreset = npcParse->inactive_;
-
- while (pcreset != NULL) {
- if (pcreset->setid_ == setid)
- break;
- pcreset = pcreset->next_;
- }
-
- if (pcreset == NULL) {
- pcreset = (struct pcrematch_set *)
- aCalloc(sizeof(struct pcrematch_set), 1);
- pcreset->next_ = npcParse->inactive_;
- if (pcreset->next_ != NULL)
- pcreset->next_->prev_ = pcreset;
- pcreset->prev_ = 0;
- npcParse->inactive_ = pcreset;
- pcreset->setid_ = setid;
- }
-
- return pcreset;
-}
-
-/**
- * activate a set of patterns.
- *
- * if the setid does not exist, this will silently return
- */
-
-static void activate_pcreset(struct npc_data *nd,int setid) {
- struct pcrematch_set *pcreset;
- struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
- if (npcParse == NULL)
- return; // Nothing to activate...
- pcreset = npcParse->inactive_;
- while (pcreset != NULL) {
- if (pcreset->setid_ == setid)
- break;
- pcreset = pcreset->next_;
- }
- if (pcreset == NULL)
- return; // not in inactive list
- if (pcreset->next_ != NULL)
- pcreset->next_->prev_ = pcreset->prev_;
- if (pcreset->prev_ != NULL)
- pcreset->prev_->next_ = pcreset->next_;
- else
- npcParse->inactive_ = pcreset->next_;
-
- pcreset->prev_ = NULL;
- pcreset->next_ = npcParse->active_;
- if (pcreset->next_ != NULL)
- pcreset->next_->prev_ = pcreset;
- npcParse->active_ = pcreset;
-}
-
-/**
- * deactivate a set of patterns.
- *
- * if the setid does not exist, this will silently return
- */
-
-static void deactivate_pcreset(struct npc_data *nd,int setid) {
- struct pcrematch_set *pcreset;
- struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
- if (npcParse == NULL)
- return; // Nothing to deactivate...
- if (setid == -1) {
- while(npcParse->active_ != NULL)
- deactivate_pcreset(nd, npcParse->active_->setid_);
- return;
- }
- pcreset = npcParse->active_;
- while (pcreset != NULL) {
- if (pcreset->setid_ == setid)
- break;
- pcreset = pcreset->next_;
- }
- if (pcreset == NULL)
- return; // not in active list
- if (pcreset->next_ != NULL)
- pcreset->next_->prev_ = pcreset->prev_;
- if (pcreset->prev_ != NULL)
- pcreset->prev_->next_ = pcreset->next_;
- else
- npcParse->active_ = pcreset->next_;
-
- pcreset->prev_ = NULL;
- pcreset->next_ = npcParse->inactive_;
- if (pcreset->next_ != NULL)
- pcreset->next_->prev_ = pcreset;
- npcParse->inactive_ = pcreset;
-}
-
-/**
- * delete a set of patterns.
- */
-static void delete_pcreset(struct npc_data *nd,int setid) {
- int active = 1;
- struct pcrematch_set *pcreset;
- struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
- if (npcParse == NULL)
- return; // Nothing to deactivate...
- pcreset = npcParse->active_;
- while (pcreset != NULL) {
- if (pcreset->setid_ == setid)
- break;
- pcreset = pcreset->next_;
- }
- if (pcreset == NULL) {
- active = 0;
- pcreset = npcParse->inactive_;
- while (pcreset != NULL) {
- if (pcreset->setid_ == setid)
- break;
- pcreset = pcreset->next_;
- }
- }
- if (pcreset == NULL)
- return;
-
- if (pcreset->next_ != NULL)
- pcreset->next_->prev_ = pcreset->prev_;
- if (pcreset->prev_ != NULL)
- pcreset->prev_->next_ = pcreset->next_;
-
- if(active)
- npcParse->active_ = pcreset->next_;
- else
- npcParse->inactive_ = pcreset->next_;
-
- pcreset->prev_ = NULL;
- pcreset->next_ = NULL;
-
- while (pcreset->head_) {
- struct pcrematch_entry *n = pcreset->head_->next_;
- finalize_pcrematch_entry(pcreset->head_);
- aFree(pcreset->head_); // Cleanin' the last ones.. [Lance]
- pcreset->head_ = n;
- }
-
- aFree(pcreset);
-}
-
-/**
- * create a new pattern entry
- */
-static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) {
- struct pcrematch_entry * e = (struct pcrematch_entry *)
- aCalloc(sizeof(struct pcrematch_entry), 1);
- struct pcrematch_entry * last = set->head_;
-
- // Normally we would have just stuck it at the end of the list but
- // this doesn't sink up with peoples usage pattern. They wanted
- // the items defined first to have a higher priority then the
- // items defined later.. as a result, we have to do some work up
- // front..
-
- /* if we are the first pattern, stick us at the end */
- if (last == NULL) {
- set->head_ = e;
- return e;
- }
-
- /* Look for the last entry */
- while (last->next_ != NULL)
- last = last->next_;
-
- last->next_ = e;
- e->next_ = NULL;
-
- return e;
-}
-
-/**
- * define/compile a new pattern
- */
-
-void npc_chat_def_pattern(struct npc_data *nd, int setid,
- const char *pattern, const char *label)
-{
- const char *err;
- int erroff;
-
- struct pcrematch_set * s = lookup_pcreset(nd, setid);
- struct pcrematch_entry *e = create_pcrematch_entry(s);
- e->pattern_ = aStrdup(pattern);
- e->label_ = aStrdup(label);
- e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL);
- e->pcre_extra_ = pcre_study(e->pcre_, 0, &err);
-}
-
-/**
- * Delete everything associated with a NPC concerning the pattern
- * matching code
- *
- * this could be more efficent but.. how often do you do this?
- */
-void npc_chat_finalize(struct npc_data *nd)
-{
- struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
- if (npcParse == NULL)
- return;
-
- while(npcParse->active_)
- delete_pcreset(nd, npcParse->active_->setid_);
-
- while(npcParse->inactive_)
- delete_pcreset(nd, npcParse->inactive_->setid_);
-
- // Additional cleaning up [Lance]
- aFree(npcParse);
-}
-
-/**
- * Handler called whenever a global message is spoken in a NPC's area
- */
-int npc_chat_sub(struct block_list *bl, va_list ap)
-{
- struct npc_data *nd = (struct npc_data *)bl;
- struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
- unsigned char *msg;
- int len, pos, i;
- struct map_session_data *sd;
- struct npc_label_list *lst;
- struct pcrematch_set *pcreset;
-
- // Not interested in anything you might have to say...
- if (npcParse == NULL || npcParse->active_ == NULL)
- return 0;
-
- msg = va_arg(ap,unsigned char*);
- len = va_arg(ap,int);
- sd = va_arg(ap,struct map_session_data *);
-
- // grab the active list
- pcreset = npcParse->active_;
-
- // interate across all active sets
- while (pcreset != NULL) {
- struct pcrematch_entry *e = pcreset->head_;
- // interate across all patterns in that set
- while (e != NULL) {
- int offsets[20];
- char buf[255];
- // perform pattern match
- int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0,
- 0, offsets, sizeof(offsets) / sizeof(offsets[0]));
- if (r >= 0) {
- // save out the matched strings
- switch (r) {
- case 10:
- memcpy(buf, &msg[offsets[18]], offsets[19]);
- buf[offsets[19]] = '\0';
- set_var(sd, "$p9$", buf);
- case 9:
- memcpy(buf, &msg[offsets[16]], offsets[17]);
- buf[offsets[17]] = '\0';
- set_var(sd, "$p8$", buf);
- case 8:
- memcpy(buf, &msg[offsets[14]], offsets[15]);
- buf[offsets[15]] = '\0';
- set_var(sd, "$p7$", buf);
- case 7:
- memcpy(buf, &msg[offsets[12]], offsets[13]);
- buf[offsets[13]] = '\0';
- set_var(sd, "$p6$", buf);
- case 6:
- memcpy(buf, &msg[offsets[10]], offsets[11]);
- buf[offsets[11]] = '\0';
- set_var(sd, "$p5$", buf);
- case 5:
- memcpy(buf, &msg[offsets[8]], offsets[9]);
- buf[offsets[9]] = '\0';
- set_var(sd, "$p4$", buf);
- case 4:
- memcpy(buf, &msg[offsets[6]], offsets[7]);
- buf[offsets[7]] = '\0';
- set_var(sd, "$p3$", buf);
- case 3:
- memcpy(buf, &msg[offsets[4]], offsets[5]);
- buf[offsets[5]] = '\0';
- set_var(sd, "$p2$", buf);
- case 2:
- memcpy(buf, &msg[offsets[2]], offsets[3]);
- buf[offsets[3]] = '\0';
- set_var(sd, "$p1$", buf);
- case 1:
- memcpy(buf, &msg[offsets[0]], offsets[1]);
- buf[offsets[1]] = '\0';
- set_var(sd, "$p0$", buf);
- }
-
- // find the target label.. this sucks..
- lst=nd->u.scr.label_list;
- pos = -1;
- for (i = 0; i < nd->u.scr.label_list_num; i++) {
- if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) {
- pos = lst[i].pos;
- break;
- }
- }
- if (pos == -1) {
- ShowWarning("Unable to find label: %s", e->label_);
- // unable to find label... do something..
- return 0;
- }
- // run the npc script
- run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id);
- return 0;
- }
- e = e->next_;
- }
- pcreset = pcreset->next_;
- }
-
- return 0;
-}
-
-int mob_chat_sub(struct block_list *bl, va_list ap){
- struct mob_data *md = (struct mob_data *)bl;
- if(md->nd){
- npc_chat_sub(&md->nd->bl, ap);
- }
- return 0;
-}
-
-// Various script builtins used to support these functions
-
-int buildin_defpattern(struct script_state *st) {
- int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
- char *pattern=conv_str(st,& (st->stack->stack_data[st->start+3]));
- char *label=conv_str(st,& (st->stack->stack_data[st->start+4]));
- struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
-
- npc_chat_def_pattern(nd, setid, pattern, label);
-
- return 0;
-}
-
-int buildin_activatepset(struct script_state *st) {
- int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
- struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
-
- activate_pcreset(nd, setid);
-
- return 0;
-}
-int buildin_deactivatepset(struct script_state *st) {
- int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
- struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
-
- deactivate_pcreset(nd, setid);
-
- return 0;
-}
-int buildin_deletepset(struct script_state *st) {
- int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
- struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
-
- delete_pcreset(nd, setid);
-
- return 0;
-}
-
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifdef PCRE_SUPPORT
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "../common/timer.h"
+#include "../common/malloc.h"
+#include "../common/version.h"
+#include "../common/nullpo.h"
+#include "../common/showmsg.h"
+
+#include "map.h"
+#include "status.h"
+#include "npc.h"
+#include "chat.h"
+#include "script.h"
+#include "battle.h"
+
+#include "pcre.h"
+
+/**
+ * Written by MouseJstr in a vision... (2/21/2005)
+ *
+ * This allows you to make npc listen for spoken text (global
+ * messages) and pattern match against that spoken text using perl
+ * regular expressions.
+ *
+ * Please feel free to copy this code into your own personal ragnarok
+ * servers or distributions but please leave my name. Also, please
+ * wait until I've put it into the main eA branch which means I
+ * believe it is ready for distribution.
+ *
+ * So, how do people use this?
+ *
+ * The first and most important function is defpattern
+ *
+ * defpattern 1, "[^:]+: (.*) loves (.*)", "label";
+ *
+ * this defines a new pattern in set 1 using perl syntax
+ * (http://www.troubleshooters.com/codecorn/littperl/perlreg.htm)
+ * and tells it to jump to the supplied label when the pattern
+ * is matched.
+ *
+ * each of the matched Groups will result in a variable being
+ * set ($p1$ through $p9$ with $p0$ being the entire string)
+ * before the script gets executed.
+ *
+ * activatepset 1;
+ *
+ * This activates a set of patterns.. You can have many pattern
+ * sets defined and many active all at once. This feature allows
+ * you to set up "conversations" and ever changing expectations of
+ * the pattern matcher
+ *
+ * deactivatepset 1;
+ *
+ * turns off a pattern set;
+ *
+ * deactivatepset -1;
+ *
+ * turns off ALL pattern sets;
+ *
+ * deletepset 1;
+ *
+ * deletes a pset
+ */
+
+/* Structure containing all info associated with a single pattern
+ block */
+
+struct pcrematch_entry {
+ struct pcrematch_entry *next_;
+ char *pattern_;
+ pcre *pcre_;
+ pcre_extra *pcre_extra_;
+ char *label_;
+};
+
+/* A set of patterns that can be activated and deactived with a single
+ command */
+
+struct pcrematch_set {
+ struct pcrematch_set *next_, *prev_;
+ struct pcrematch_entry *head_;
+ int setid_;
+};
+
+/*
+ * Entire data structure hung off a NPC
+ *
+ * The reason I have done it this way (a void * in npc_data and then
+ * this) was to reduce the number of patches that needed to be applied
+ * to a ragnarok distribution to bring this code online. I
+ * also wanted people to be able to grab this one file to get updates
+ * without having to do a large number of changes.
+ */
+
+struct npc_parse {
+ struct pcrematch_set *active_;
+ struct pcrematch_set *inactive_;
+};
+
+
+/**
+ * delete everythign associated with a entry
+ *
+ * This does NOT do the list management
+ */
+
+void finalize_pcrematch_entry(struct pcrematch_entry *e) {
+//TODO: For some odd reason this causes a already-free'd error under Windows, but not *nix! [Skotlex]
+#ifndef _WIN32
+ if (e->pcre_) {
+ free(e->pcre_);
+ e->pcre_ = NULL;
+ }
+#endif
+ if (e->pcre_extra_) {
+ free(e->pcre_extra_);
+ e->pcre_ = NULL;
+ }
+ aFree(e->pattern_);
+ aFree(e->label_);
+}
+
+/**
+ * Lookup (and possibly create) a new set of patterns by the set id
+ */
+static struct pcrematch_set * lookup_pcreset(struct npc_data *nd,int setid)
+{
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ nd->chatdb = npcParse = (struct npc_parse *)
+ aCalloc(sizeof(struct npc_parse), 1);
+
+ pcreset = npcParse->active_;
+
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ pcreset = npcParse->inactive_;
+
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+
+ if (pcreset == NULL) {
+ pcreset = (struct pcrematch_set *)
+ aCalloc(sizeof(struct pcrematch_set), 1);
+ pcreset->next_ = npcParse->inactive_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ pcreset->prev_ = 0;
+ npcParse->inactive_ = pcreset;
+ pcreset->setid_ = setid;
+ }
+
+ return pcreset;
+}
+
+/**
+ * activate a set of patterns.
+ *
+ * if the setid does not exist, this will silently return
+ */
+
+static void activate_pcreset(struct npc_data *nd,int setid) {
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to activate...
+ pcreset = npcParse->inactive_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ return; // not in inactive list
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+ else
+ npcParse->inactive_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = npcParse->active_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ npcParse->active_ = pcreset;
+}
+
+/**
+ * deactivate a set of patterns.
+ *
+ * if the setid does not exist, this will silently return
+ */
+
+static void deactivate_pcreset(struct npc_data *nd,int setid) {
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to deactivate...
+ if (setid == -1) {
+ while(npcParse->active_ != NULL)
+ deactivate_pcreset(nd, npcParse->active_->setid_);
+ return;
+ }
+ pcreset = npcParse->active_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL)
+ return; // not in active list
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+ else
+ npcParse->active_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = npcParse->inactive_;
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset;
+ npcParse->inactive_ = pcreset;
+}
+
+/**
+ * delete a set of patterns.
+ */
+static void delete_pcreset(struct npc_data *nd,int setid) {
+ int active = 1;
+ struct pcrematch_set *pcreset;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return; // Nothing to deactivate...
+ pcreset = npcParse->active_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ if (pcreset == NULL) {
+ active = 0;
+ pcreset = npcParse->inactive_;
+ while (pcreset != NULL) {
+ if (pcreset->setid_ == setid)
+ break;
+ pcreset = pcreset->next_;
+ }
+ }
+ if (pcreset == NULL)
+ return;
+
+ if (pcreset->next_ != NULL)
+ pcreset->next_->prev_ = pcreset->prev_;
+ if (pcreset->prev_ != NULL)
+ pcreset->prev_->next_ = pcreset->next_;
+
+ if(active)
+ npcParse->active_ = pcreset->next_;
+ else
+ npcParse->inactive_ = pcreset->next_;
+
+ pcreset->prev_ = NULL;
+ pcreset->next_ = NULL;
+
+ while (pcreset->head_) {
+ struct pcrematch_entry *n = pcreset->head_->next_;
+ finalize_pcrematch_entry(pcreset->head_);
+ aFree(pcreset->head_); // Cleanin' the last ones.. [Lance]
+ pcreset->head_ = n;
+ }
+
+ aFree(pcreset);
+}
+
+/**
+ * create a new pattern entry
+ */
+static struct pcrematch_entry *create_pcrematch_entry(struct pcrematch_set * set) {
+ struct pcrematch_entry * e = (struct pcrematch_entry *)
+ aCalloc(sizeof(struct pcrematch_entry), 1);
+ struct pcrematch_entry * last = set->head_;
+
+ // Normally we would have just stuck it at the end of the list but
+ // this doesn't sink up with peoples usage pattern. They wanted
+ // the items defined first to have a higher priority then the
+ // items defined later.. as a result, we have to do some work up
+ // front..
+
+ /* if we are the first pattern, stick us at the end */
+ if (last == NULL) {
+ set->head_ = e;
+ return e;
+ }
+
+ /* Look for the last entry */
+ while (last->next_ != NULL)
+ last = last->next_;
+
+ last->next_ = e;
+ e->next_ = NULL;
+
+ return e;
+}
+
+/**
+ * define/compile a new pattern
+ */
+
+void npc_chat_def_pattern(struct npc_data *nd, int setid,
+ const char *pattern, const char *label)
+{
+ const char *err;
+ int erroff;
+
+ struct pcrematch_set * s = lookup_pcreset(nd, setid);
+ struct pcrematch_entry *e = create_pcrematch_entry(s);
+ e->pattern_ = aStrdup(pattern);
+ e->label_ = aStrdup(label);
+ e->pcre_ = pcre_compile(pattern, PCRE_CASELESS, &err, &erroff, NULL);
+ e->pcre_extra_ = pcre_study(e->pcre_, 0, &err);
+}
+
+/**
+ * Delete everything associated with a NPC concerning the pattern
+ * matching code
+ *
+ * this could be more efficent but.. how often do you do this?
+ */
+void npc_chat_finalize(struct npc_data *nd)
+{
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ if (npcParse == NULL)
+ return;
+
+ while(npcParse->active_)
+ delete_pcreset(nd, npcParse->active_->setid_);
+
+ while(npcParse->inactive_)
+ delete_pcreset(nd, npcParse->inactive_->setid_);
+
+ // Additional cleaning up [Lance]
+ aFree(npcParse);
+}
+
+/**
+ * Handler called whenever a global message is spoken in a NPC's area
+ */
+int npc_chat_sub(struct block_list *bl, va_list ap)
+{
+ struct npc_data *nd = (struct npc_data *)bl;
+ struct npc_parse *npcParse = (struct npc_parse *) nd->chatdb;
+ unsigned char *msg;
+ int len, pos, i;
+ struct map_session_data *sd;
+ struct npc_label_list *lst;
+ struct pcrematch_set *pcreset;
+
+ // Not interested in anything you might have to say...
+ if (npcParse == NULL || npcParse->active_ == NULL)
+ return 0;
+
+ msg = va_arg(ap,unsigned char*);
+ len = va_arg(ap,int);
+ sd = va_arg(ap,struct map_session_data *);
+
+ // grab the active list
+ pcreset = npcParse->active_;
+
+ // interate across all active sets
+ while (pcreset != NULL) {
+ struct pcrematch_entry *e = pcreset->head_;
+ // interate across all patterns in that set
+ while (e != NULL) {
+ int offsets[20];
+ char buf[255];
+ // perform pattern match
+ int r = pcre_exec(e->pcre_, e->pcre_extra_, msg, len, 0,
+ 0, offsets, sizeof(offsets) / sizeof(offsets[0]));
+ if (r >= 0) {
+ // save out the matched strings
+ switch (r) {
+ case 10:
+ memcpy(buf, &msg[offsets[18]], offsets[19]);
+ buf[offsets[19]] = '\0';
+ set_var(sd, "$p9$", buf);
+ case 9:
+ memcpy(buf, &msg[offsets[16]], offsets[17]);
+ buf[offsets[17]] = '\0';
+ set_var(sd, "$p8$", buf);
+ case 8:
+ memcpy(buf, &msg[offsets[14]], offsets[15]);
+ buf[offsets[15]] = '\0';
+ set_var(sd, "$p7$", buf);
+ case 7:
+ memcpy(buf, &msg[offsets[12]], offsets[13]);
+ buf[offsets[13]] = '\0';
+ set_var(sd, "$p6$", buf);
+ case 6:
+ memcpy(buf, &msg[offsets[10]], offsets[11]);
+ buf[offsets[11]] = '\0';
+ set_var(sd, "$p5$", buf);
+ case 5:
+ memcpy(buf, &msg[offsets[8]], offsets[9]);
+ buf[offsets[9]] = '\0';
+ set_var(sd, "$p4$", buf);
+ case 4:
+ memcpy(buf, &msg[offsets[6]], offsets[7]);
+ buf[offsets[7]] = '\0';
+ set_var(sd, "$p3$", buf);
+ case 3:
+ memcpy(buf, &msg[offsets[4]], offsets[5]);
+ buf[offsets[5]] = '\0';
+ set_var(sd, "$p2$", buf);
+ case 2:
+ memcpy(buf, &msg[offsets[2]], offsets[3]);
+ buf[offsets[3]] = '\0';
+ set_var(sd, "$p1$", buf);
+ case 1:
+ memcpy(buf, &msg[offsets[0]], offsets[1]);
+ buf[offsets[1]] = '\0';
+ set_var(sd, "$p0$", buf);
+ }
+
+ // find the target label.. this sucks..
+ lst=nd->u.scr.label_list;
+ pos = -1;
+ for (i = 0; i < nd->u.scr.label_list_num; i++) {
+ if (strncmp(lst[i].name, e->label_, sizeof(lst[i].name)) == 0) {
+ pos = lst[i].pos;
+ break;
+ }
+ }
+ if (pos == -1) {
+ ShowWarning("Unable to find label: %s", e->label_);
+ // unable to find label... do something..
+ return 0;
+ }
+ // run the npc script
+ run_script(nd->u.scr.script,pos,sd->bl.id,nd->bl.id);
+ return 0;
+ }
+ e = e->next_;
+ }
+ pcreset = pcreset->next_;
+ }
+
+ return 0;
+}
+
+int mob_chat_sub(struct block_list *bl, va_list ap){
+ struct mob_data *md = (struct mob_data *)bl;
+ if(md->nd){
+ npc_chat_sub(&md->nd->bl, ap);
+ }
+ return 0;
+}
+
+// Various script builtins used to support these functions
+
+int buildin_defpattern(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ char *pattern=conv_str(st,& (st->stack->stack_data[st->start+3]));
+ char *label=conv_str(st,& (st->stack->stack_data[st->start+4]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ npc_chat_def_pattern(nd, setid, pattern, label);
+
+ return 0;
+}
+
+int buildin_activatepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ activate_pcreset(nd, setid);
+
+ return 0;
+}
+int buildin_deactivatepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ deactivate_pcreset(nd, setid);
+
+ return 0;
+}
+int buildin_deletepset(struct script_state *st) {
+ int setid=conv_num(st,& (st->stack->stack_data[st->start+2]));
+ struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid);
+
+ delete_pcreset(nd, setid);
+
+ return 0;
+}
+
+
+#endif
diff --git a/src/map/party.c b/src/map/party.c
index 6c97bd464..babc9129d 100644
--- a/src/map/party.c
+++ b/src/map/party.c
@@ -1,902 +1,902 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#include "../common/timer.h"
-#include "../common/socket.h"
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-
-#include "party.h"
-#include "pc.h"
-#include "map.h"
-#include "battle.h"
-#include "intif.h"
-#include "clif.h"
-#include "log.h"
-#include "skill.h"
-#include "status.h"
-
-static struct dbt* party_db;
-static struct party_data* party_cache = NULL; //party in cache for skipping consecutive lookups. [Skotlex]
-int party_share_level = 10;
-int party_send_xy_timer(int tid,unsigned int tick,int id,int data);
-
-/*==========================================
- * Fills the given party_member structure according to the sd provided.
- * Used when creating/adding people to a party. [Skotlex]
- *------------------------------------------
- */
-static void party_fill_member(struct party_member *member, struct map_session_data *sd) {
- member->account_id = sd->status.account_id;
- member->char_id = sd->status.char_id;
- memcpy(member->name,sd->status.name,NAME_LENGTH);
- member->class_ = sd->status.class_;
- member->map = sd->mapindex;
- member->lv = sd->status.base_level;
- member->online = 1;
- member->leader = 0;
-}
-
-/*==========================================
- * 終了
- *------------------------------------------
- */
-void do_final_party(void)
-{
- party_db->destroy(party_db,NULL);
-}
-// 初期化
-void do_init_party(void)
-{
- party_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
- add_timer_func_list(party_send_xy_timer,"party_send_xy_timer");
- add_timer_interval(gettick()+battle_config.party_update_interval,party_send_xy_timer,0,0,battle_config.party_update_interval);
-}
-
-// 検索
-struct party_data *party_search(int party_id)
-{
- if(!party_id) return NULL;
- if (party_cache && party_cache->party.party_id == party_id)
- return party_cache;
-
- party_cache = idb_get(party_db,party_id);
- return party_cache;
-}
-int party_searchname_sub(DBKey key,void *data,va_list ap)
-{
- struct party_data *p=(struct party_data *)data,**dst;
- char *str;
- str=va_arg(ap,char *);
- dst=va_arg(ap,struct party_data **);
- if(strncmpi(p->party.name,str,NAME_LENGTH)==0)
- *dst=p;
- return 0;
-}
-
-struct party_data* party_searchname(char *str)
-{
- struct party_data *p=NULL;
- party_db->foreach(party_db,party_searchname_sub,str,&p);
- return p;
-}
-
-int party_create(struct map_session_data *sd,char *name,int item,int item2)
-{
- struct party_member leader;
- nullpo_retr(0, sd);
-
- if(sd->status.party_id) {
- clif_party_created(sd,2);
- return 0;
- }
-
- party_fill_member(&leader, sd);
- leader.leader = 1;
-
- intif_create_party(&leader,name,item,item2);
- return 0;
-}
-
-
-int party_created(int account_id,int char_id,int fail,int party_id,char *name)
-{
- struct map_session_data *sd;
- struct party_data *p;
- sd=map_id2sd(account_id);
-
- nullpo_retr(0, sd);
- if (sd->status.char_id != char_id)
- return 0; //unlikely failure...
-
- if(fail){
- clif_party_created(sd,1);
- return 0;
- }
- sd->status.party_id=party_id;
- if(idb_get(party_db,party_id)!=NULL){
- ShowFatalError("party: id already exists!\n");
- exit(1);
- }
- p=(struct party_data *)aCalloc(1,sizeof(struct party_data));
- p->party.party_id=party_id;
- memcpy(p->party.name, name, NAME_LENGTH);
- idb_put(party_db,party_id,p);
- clif_party_created(sd,0); //Success message
- clif_charnameupdate(sd); //Update other people's display. [Skotlex]
- return 1;
-}
-
-int party_request_info(int party_id)
-{
- return intif_request_partyinfo(party_id);
-}
-
-int party_check_member(struct party *p)
-{
- int i, users;
- struct map_session_data *sd, **all_sd;
-
- nullpo_retr(0, p);
-
- all_sd = map_getallusers(&users);
-
- for(i=0;i<users;i++)
- {
- if((sd = all_sd[i]) && sd->status.party_id==p->party_id)
- {
- int j,f=1;
- for(j=0;j<MAX_PARTY;j++){
- if(p->member[j].account_id==sd->status.account_id &&
- p->member[j].char_id==sd->status.char_id)
- {
- f=0;
- break;
- }
- }
-
- if(f){
- sd->status.party_id=0;
- if(battle_config.error_log)
- ShowWarning("party: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
- }
- }
- }
- return 0;
-}
-
-int party_recv_noinfo(int party_id)
-{
- int i, users;
- struct map_session_data *sd, **all_sd;
-
- all_sd = map_getallusers(&users);
-
- for(i=0;i<users;i++){
- if((sd = all_sd[i]) && sd->status.party_id==party_id)
- sd->status.party_id=0;
- }
- return 0;
-}
-
-static void* create_party(DBKey key, va_list args) {
- struct party_data *p;
- p=(struct party_data *)aCalloc(1,sizeof(struct party_data));
- return p;
-}
-
-static void party_check_state(struct party_data *p)
-{
- int i;
- malloc_set(&p->state, 0, sizeof(p->state));
- for (i = 0; i < MAX_PARTY; i ++)
- {
- if (!p->party.member[i].online) continue; //Those not online shouldn't aport to skill usage and all that.
- switch (p->party.member[i].class_) {
- case JOB_MONK:
- case JOB_BABY_MONK:
- case JOB_CHAMPION:
- p->state.monk = 1;
- break;
- case JOB_STAR_GLADIATOR:
- p->state.sg = 1;
- break;
- case JOB_SUPER_NOVICE:
- case JOB_SUPER_BABY:
- p->state.snovice = 1;
- break;
- case JOB_TAEKWON:
- p->state.tk = 1;
- break;
- }
- }
-}
-
-int party_recv_info(struct party *sp)
-{
- struct map_session_data *sd;
- struct party_data *p;
- int i;
-
- nullpo_retr(0, sp);
-
- p= idb_ensure(party_db, sp->party_id, create_party);
- if (!p->party.party_id) //party just received.
- party_check_member(sp);
- memcpy(&p->party,sp,sizeof(struct party));
- malloc_set(&p->state, 0, sizeof(p->state));
- malloc_set(&p->data, 0, sizeof(p->data));
- for(i=0;i<MAX_PARTY;i++){
- if (!p->party.member[i].account_id)
- continue;
- sd = map_id2sd(p->party.member[i].account_id);
- if (sd && sd->status.party_id==p->party.party_id
- && sd->status.char_id == p->party.member[i].char_id
- && !sd->state.waitingdisconnect)
- p->data[i].sd = sd;
- }
- party_check_state(p);
- for(i=0;i<MAX_PARTY;i++){
- sd = p->data[i].sd;
- if(!sd || sd->state.party_sent)
- continue;
- clif_party_main_info(p,-1);
- clif_party_option(p,sd,0x100);
- clif_party_info(p,-1);
- sd->state.party_sent=1;
- }
-
- return 0;
-}
-
-int party_invite(struct map_session_data *sd,struct map_session_data *tsd)
-{
- struct party_data *p=party_search(sd->status.party_id);
- int i,flag=0;
-
- nullpo_retr(0, sd);
-
- if(tsd==NULL || p==NULL)
- return 0;
- if(!battle_config.invite_request_check) {
- if (tsd->guild_invite>0 || tsd->trade_partner) {
- clif_party_inviteack(sd,tsd->status.name,0);
- return 0;
- }
- }
- if( tsd->status.party_id>0 || tsd->party_invite>0 ){
- clif_party_inviteack(sd,tsd->status.name,0);
- return 0;
- }
- for(i=0;i<MAX_PARTY;i++){
- if(p->party.member[i].account_id == 0) //Room for a new member.
- flag = 1;
- /* By default Aegis BLOCKS more than one char from the same account on a party.
- * But eA does support it... so this check is left commented.
- if(p->party.member[i].account_id==tsd->status.account_id)
- {
- clif_party_inviteack(sd,tsd->status.name,4);
- return 0;
- }
- */
- }
- if (!flag) { //Full party.
- clif_party_inviteack(sd,tsd->status.name,3);
- return 0;
- }
-
- tsd->party_invite=sd->status.party_id;
- tsd->party_invite_account=sd->status.account_id;
-
- clif_party_invite(sd,tsd);
- return 1;
-}
-
-int party_reply_invite(struct map_session_data *sd,int account_id,int flag)
-{
- struct map_session_data *tsd= map_id2sd(account_id);
- struct party_member member;
- nullpo_retr(0, sd);
-
- if(flag==1){
- party_fill_member(&member, sd);
- intif_party_addmember(sd->party_invite, &member);
- return 0;
- }
- sd->party_invite=0;
- sd->party_invite_account=0;
- if(tsd==NULL)
- return 0;
- clif_party_inviteack(tsd,sd->status.name,1);
- return 1;
-}
-
-int party_member_added(int party_id,int account_id,int char_id, int flag)
-{
- struct map_session_data *sd = map_id2sd(account_id),*sd2;
- struct party_data *p = party_search(party_id);
- if(sd == NULL || sd->status.char_id != char_id){
- if (flag == 0) {
- if(battle_config.error_log)
- ShowError("party: member added error %d is not online\n",account_id);
- intif_party_leave(party_id,account_id,char_id);
- }
- return 0;
- }
- sd->party_invite=0;
- sd->party_invite_account=0;
-
- if (!p) {
- if(battle_config.error_log)
- ShowError("party_member_added: party %d not found.\n",party_id);
- intif_party_leave(party_id,account_id,char_id);
- return 0;
- }
-
- if(!flag) {
- sd->state.party_sent=0;
- sd->status.party_id=party_id;
- party_check_conflict(sd);
- clif_party_join_info(&p->party,sd);
- clif_party_hp(sd);
- clif_party_xy(sd);
- clif_charnameupdate(sd); //Update char name's display [Skotlex]
- }
-
- sd2=map_id2sd(sd->party_invite_account);
- if (sd2)
- clif_party_inviteack(sd2,sd->status.name,flag?2:1);
- return 0;
-}
-
-int party_removemember(struct map_session_data *sd,int account_id,char *name)
-{
- struct party_data *p;
- int i;
-
- nullpo_retr(0, sd);
-
- if( (p = party_search(sd->status.party_id)) == NULL )
- return 0;
-
- for(i=0;i<MAX_PARTY;i++){
- if(p->party.member[i].account_id==sd->status.account_id &&
- p->party.member[i].char_id==sd->status.char_id) {
- if(p->party.member[i].leader)
- break;
- return 0;
- }
- }
- if (i >= MAX_PARTY) //Request from someone not in party? o.O
- return 0;
-
- for(i=0;i<MAX_PARTY;i++){
- if(p->party.member[i].account_id==account_id &&
- strncmp(p->party.member[i].name,name,NAME_LENGTH)==0)
- {
- intif_party_leave(p->party.party_id,account_id,p->party.member[i].char_id);
- return 1;
- }
- }
- return 0;
-}
-
-int party_leave(struct map_session_data *sd)
-{
- struct party_data *p;
- int i;
-
- nullpo_retr(0, sd);
-
- if( (p = party_search(sd->status.party_id)) == NULL )
- return 0;
-
- for(i=0;i<MAX_PARTY;i++){
- if(p->party.member[i].account_id==sd->status.account_id &&
- p->party.member[i].char_id==sd->status.char_id){
- intif_party_leave(p->party.party_id,sd->status.account_id,sd->status.char_id);
- return 0;
- }
- }
- return 0;
-}
-
-int party_member_leaved(int party_id,int account_id,int char_id)
-{
- struct map_session_data *sd=map_id2sd(account_id);
- struct party_data *p=party_search(party_id);
- int i;
- if (sd && sd->status.char_id != char_id) //Wrong target
- sd = NULL;
- if(p!=NULL){
- for(i=0;i<MAX_PARTY;i++)
- if(p->party.member[i].account_id==account_id &&
- p->party.member[i].char_id==char_id){
- clif_party_leaved(p,sd,account_id,p->party.member[i].name,0x00);
- malloc_set(&p->party.member[i], 0, sizeof(p->party.member[0]));
- malloc_set(&p->data[i], 0, sizeof(p->data[0]));
- p->party.count--;
- party_check_state(p);
- break;
- }
- }
- if(sd!=NULL && sd->status.party_id==party_id){
- sd->status.party_id=0;
- sd->state.party_sent=0;
- clif_charnameupdate(sd); //Update name display [Skotlex]
- }
- return 0;
-}
-
-int party_broken(int party_id)
-{
- struct party_data *p;
- int i;
- if( (p=party_search(party_id))==NULL )
- return 0;
-
- for(i=0;i<MAX_PARTY;i++){
- if(p->data[i].sd!=NULL){
- clif_party_leaved(p,p->data[i].sd,
- p->party.member[i].account_id,p->party.member[i].name,0x10);
- p->data[i].sd->status.party_id=0;
- p->data[i].sd->state.party_sent=0;
- }
- }
- if (party_cache && party_cache->party.party_id == party_id)
- party_cache = NULL;
- idb_remove(party_db,party_id);
- return 0;
-}
-
-int party_changeoption(struct map_session_data *sd,int exp,int item)
-{
- nullpo_retr(0, sd);
-
- if( sd->status.party_id==0)
- return 0;
- intif_party_changeoption(sd->status.party_id,sd->status.account_id,exp,item);
- return 0;
-}
-
-int party_optionchanged(int party_id,int account_id,int exp,int item,int flag)
-{
- struct party_data *p;
- struct map_session_data *sd=map_id2sd(account_id);
- if( (p=party_search(party_id))==NULL)
- return 0;
-
- if(!(flag&0x01) && p->party.exp != exp) {
- p->party.exp=exp;
- clif_party_option(p,sd,flag); //This packet doesn't updates item info anymore...
- }
- if(!(flag&0x10) && p->party.item != item) {
- p->party.item=item;
- clif_party_main_info(p,-1);
- }
- if(flag&0x01) //Send denied message
- clif_party_option(p,sd,flag);
- return 0;
-}
-
-int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv)
-{
- struct party_data *p;
- int i;
- if( (p=party_search(party_id))==NULL)
- return 0;
- for(i=0;i<MAX_PARTY;i++){
- struct map_session_data *sd;
- struct party_member *m=&p->party.member[i];
- if(m->account_id==account_id && m->char_id==char_id){
- m->map = map;
- m->online=online;
- m->lv=lv;
- //Check if they still exist on this map server
- sd = map_id2sd(m->account_id);
- p->data[i].sd = (sd!=NULL && sd->status.party_id==p->party.party_id && sd->status.char_id == m->char_id && !sd->state.waitingdisconnect)?sd:NULL;
- break;
- }
- }
- if(i==MAX_PARTY){
- if(battle_config.error_log)
- ShowError("party: not found member %d/%d on %d[%s]",account_id,char_id,party_id,p->party.name);
- return 0;
- }
-
- clif_party_info(p,-1);
- return 0;
-}
-
-int party_send_movemap(struct map_session_data *sd)
-{
- int i;
- struct party_data *p;
-
- nullpo_retr(0, sd);
-
- if( sd->status.party_id==0 )
- return 0;
- intif_party_changemap(sd,1);
-
- p=party_search(sd->status.party_id);
- if (p && sd->fd) {
- //Send dots of other party members to this char. [Skotlex]
- for(i=0; i < MAX_PARTY; i++) {
- if (!p->data[i].sd || p->data[i].sd == sd ||
- p->data[i].sd->bl.m != sd->bl.m)
- continue;
- clif_party_xy_single(sd->fd, p->data[i].sd);
- }
-
- }
-
- if( sd->state.party_sent )
- return 0;
-
- party_check_conflict(sd);
-
- if(p){
- party_check_member(&p->party);
- if(sd->status.party_id==p->party.party_id){
- clif_party_main_info(p,sd->fd);
- clif_party_option(p,sd,0x100);
- clif_party_info(p,sd->fd);
- sd->state.party_sent=1;
- }
- }
-
- return 0;
-}
-
-int party_send_logout(struct map_session_data *sd)
-{
- struct party_data *p;
- int i;
-
- if(!sd->status.party_id)
- return 0;
-
- intif_party_changemap(sd,0);
- p=party_search(sd->status.party_id);
- if(!p) return 0;
-
- for(i=0;i<MAX_PARTY && p->data[i].sd != sd;i++);
- if (i < MAX_PARTY)
- malloc_set(&p->data[i], 0, sizeof(p->data[0]));
-
- return 1;
-}
-
-int party_send_message(struct map_session_data *sd,char *mes,int len)
-{
- if(sd->status.party_id==0)
- return 0;
- intif_party_message(sd->status.party_id,sd->status.account_id,mes,len);
- party_recv_message(sd->status.party_id,sd->status.account_id,mes,len);
- //Chat Logging support Type 'P'
- if(log_config.chat&1 //we log everything then
- || (log_config.chat&4 //if Party bit is on
- && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
- log_chat("P", sd->status.party_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
-
- return 0;
-}
-
-int party_recv_message(int party_id,int account_id,char *mes,int len)
-{
- struct party_data *p;
- if( (p=party_search(party_id))==NULL)
- return 0;
- clif_party_message(p,account_id,mes,len);
- return 0;
-}
-
-int party_check_conflict(struct map_session_data *sd)
-{
- nullpo_retr(0, sd);
-
- intif_party_checkconflict(sd->status.party_id,sd->status.account_id,sd->status.char_id);
- return 0;
-}
-
-int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv)
-{
- struct party_data *p;
- struct map_session_data *p_sd;
- int i;
-
- if(!party_id || (p=party_search(party_id))==NULL)
- return 0;
- switch(skillid) {
- case TK_COUNTER: //Increase Triple Attack rate of Monks.
- if (!p->state.monk) return 0;
- break;
- case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators
- if (!p->state.sg) return 0;
- break;
- case AM_TWILIGHT2: //Twilight Pharmacy, requires Super Novice
- return p->state.snovice;
- case AM_TWILIGHT3: //Twilight Pharmacy, Requires Taekwon
- return p->state.tk;
- default:
- return 0; //Unknown case?
- }
-
- for(i=0;i<MAX_PARTY;i++){
- if ((p_sd = p->data[i].sd) == NULL)
- continue;
- if (sd->bl.m != p_sd->bl.m)
- continue;
- switch(skillid) {
- case TK_COUNTER: //Increase Triple Attack rate of Monks.
- if((p_sd->class_&MAPID_UPPERMASK) == MAPID_MONK
- && pc_checkskill(p_sd,MO_TRIPLEATTACK)) {
- sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,MO_TRIPLEATTACK,
- 50+50*skilllv, //+100/150/200% rate
- 0,0,skill_get_time(SG_FRIEND, 1));
- }
- break;
- case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators
- if((p_sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR
- && sd->sc.data[SC_READYCOUNTER].timer != -1
- && pc_checkskill(p_sd,SG_FRIEND)) {
- sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,TK_COUNTER,
- 50+50*pc_checkskill(p_sd,SG_FRIEND), //+100/150/200% rate
- 0,0,skill_get_time(SG_FRIEND, 1));
- }
- break;
- }
- }
- return 0;
-}
-
-int party_send_xy_timer_sub(DBKey key,void *data,va_list ap)
-{
- struct party_data *p=(struct party_data *)data;
- struct map_session_data *sd;
- int i;
-
- nullpo_retr(0, p);
-
- for(i=0;i<MAX_PARTY;i++){
- if(!p->data[i].sd) continue;
- sd = p->data[i].sd;
- if (p->data[i].x != sd->bl.x || p->data[i].y != sd->bl.y)
- {
- clif_party_xy(sd);
- p->data[i].x = sd->bl.x;
- p->data[i].y = sd->bl.y;
- }
- if (battle_config.party_hp_mode &&
- p->data[i].hp != sd->battle_status.hp)
- {
- clif_party_hp(sd);
- p->data[i].hp = sd->battle_status.hp;
- }
- }
- return 0;
-}
-
-int party_send_xy_timer(int tid,unsigned int tick,int id,int data)
-{
- party_db->foreach(party_db,party_send_xy_timer_sub,tick);
- return 0;
-}
-
-int party_send_xy_clear(struct party_data *p)
-{
- int i;
-
- nullpo_retr(0, p);
-
- for(i=0;i<MAX_PARTY;i++){
- if(!p->data[i].sd) continue;
- p->data[i].hp = 0;
- p->data[i].x = 0;
- p->data[i].y = 0;
- }
- return 0;
-}
-
-// exp share and added zeny share [Valaris]
-int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny)
-{
- struct map_session_data* sd[MAX_PARTY];
- int i;
- unsigned short c;
-
- nullpo_retr(0, p);
-
- for (i = c = 0; i < MAX_PARTY; i++)
- if ((sd[c] = p->data[i].sd)!=NULL && sd[c]->bl.m == src->m && !pc_isdead(sd[c])) {
- if (battle_config.idle_no_share && (sd[c]->chatID || sd[c]->vender_id || (sd[c]->idletime < (last_tick - battle_config.idle_no_share))))
- continue;
- c++;
- }
- if (c < 1)
- return 0;
-
- base_exp/=c;
- job_exp/=c;
- zeny/=c;
-
- if (battle_config.party_even_share_bonus && c > 1) {
- unsigned short bonus =100 + battle_config.party_even_share_bonus*(c-1);
- if (base_exp) {
- if (base_exp/100 > UINT_MAX/bonus)
- base_exp= UINT_MAX; //Exp overflow
- else if (base_exp > 10000)
- base_exp = (base_exp/100)*bonus; //Calculation overflow protection
- else
- base_exp = base_exp*bonus/100;
- }
- if (job_exp) {
- if (job_exp/100 > UINT_MAX/bonus)
- job_exp = UINT_MAX;
- else if (job_exp > 10000)
- job_exp = (job_exp/100)*bonus;
- else
- job_exp = job_exp*bonus/100;
- }
- if (zeny) {
- if (zeny/100 > INT_MAX/bonus)
- zeny = INT_MAX;
- else if (zeny > 10000)
- zeny = (zeny/100)*bonus;
- else
- zeny = zeny*bonus/100;
- }
- }
-
- for (i = 0; i < c; i++)
- {
- pc_gainexp(sd[i], src, base_exp, job_exp);
- if (zeny) // zeny from mobs [Valaris]
- pc_getzeny(sd[i],zeny);
- }
- return 0;
-}
-
-//Does party loot. first holds the id of the player who has time priority to take the item.
-int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int first)
-{
- TBL_PC *target=NULL;
- int i;
- if (p && p->party.item&2 && (first || !(battle_config.party_share_type&1))) {
- //item distribution to party members.
- if (battle_config.party_share_type&2) { //Round Robin
- TBL_PC *psd;
- i = p->itemc;
- do {
- i++;
- if (i >= MAX_PARTY)
- i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc"
- if ((psd=p->data[i].sd)==NULL || sd->bl.m != psd->bl.m ||
- pc_isdead(psd) || (battle_config.idle_no_share && (
- psd->chatID || psd->vender_id || (psd->idletime < (last_tick - battle_config.idle_no_share)))
- ))
- continue;
-
- if (pc_additem(psd,item_data,item_data->amount))
- continue; //Chosen char can't pick up loot.
- //Successful pick.
- p->itemc = i;
- target = psd;
- break;
- } while (i != p->itemc);
- } else { //Random pick
- TBL_PC *psd[MAX_PARTY];
- int count=0;
- //Collect pick candidates
- for (i = 0; i < MAX_PARTY; i++) {
- if ((psd[count]=p->data[i].sd) && psd[count]->bl.m == sd->bl.m &&
- !pc_isdead(psd[count]) && (!battle_config.idle_no_share || (
- !psd[count]->chatID && !psd[count]->vender_id &&
- (psd[count]->idletime >= (last_tick - battle_config.idle_no_share)))
- ))
- count++;
- }
- while (count > 0) { //Pick a random member.
- i = rand()%count;
- if (pc_additem(psd[i],item_data,item_data->amount))
- { //Discard this receiver.
- psd[i] = psd[count-1];
- count--;
- } else { //Successful pick.
- target = psd[i];
- break;
- }
- }
- }
- }
- if (!target) { //Give it to the owner.
- target = sd;
- if ((i=pc_additem(sd,item_data,item_data->amount)))
- return i;
- }
-
- if(log_config.enable_logs&0x8) //Logs items, taken by (P)layers [Lupus]
- log_pick_pc(target, "P", item_data->nameid, item_data->amount, item_data);
- //Logs
- if(battle_config.party_show_share_picker && target != sd){
- char output[80];
- sprintf(output, "%s acquired the item.",target->status.name);
- clif_disp_onlyself(sd,output,strlen(output));
- }
- return 0;
-}
-
-int party_send_dot_remove(struct map_session_data *sd)
-{
- if (sd->status.party_id)
- clif_party_xy_remove(sd);
- return 0;
-}
-
-// To use for Taekwon's "Fighting Chant"
-// int c = 0;
-// party_foreachsamemap(party_sub_count, sd, 0, &c);
-int party_sub_count(struct block_list *bl, va_list ap)
-{
- struct map_session_data *sd = (TBL_PC *)bl;
-
- if (sd->state.autotrade)
- return 0;
-
- if (battle_config.idle_no_share && (sd->chatID || sd->vender_id || (sd->idletime < (last_tick - battle_config.idle_no_share))))
- return 0;
-
- return 1;
-}
-
-int party_foreachsamemap(int (*func)(struct block_list*,va_list),struct map_session_data *sd,int range,...)
-{
- struct party_data *p;
- va_list ap;
- int i;
- int x0,y0,x1,y1;
- struct block_list *list[MAX_PARTY];
- int blockcount=0;
- int total = 0; //Return value.
-
- nullpo_retr(0,sd);
-
- if((p=party_search(sd->status.party_id))==NULL)
- return 0;
-
- x0=sd->bl.x-range;
- y0=sd->bl.y-range;
- x1=sd->bl.x+range;
- y1=sd->bl.y+range;
-
- va_start(ap,range);
-
- for(i=0;i<MAX_PARTY;i++){
- struct map_session_data *psd=p->data[i].sd;
- if(!psd) continue;
- if(psd->bl.m!=sd->bl.m || !psd->bl.prev)
- continue;
- if(range &&
- (psd->bl.x<x0 || psd->bl.y<y0 ||
- psd->bl.x>x1 || psd->bl.y>y1 ) )
- continue;
- list[blockcount++]=&psd->bl;
- }
-
- map_freeblock_lock();
-
- for(i=0;i<blockcount;i++)
- total += func(list[i],ap);
-
- map_freeblock_unlock();
-
- va_end(ap);
- return total;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "../common/timer.h"
+#include "../common/socket.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "party.h"
+#include "pc.h"
+#include "map.h"
+#include "battle.h"
+#include "intif.h"
+#include "clif.h"
+#include "log.h"
+#include "skill.h"
+#include "status.h"
+
+static struct dbt* party_db;
+static struct party_data* party_cache = NULL; //party in cache for skipping consecutive lookups. [Skotlex]
+int party_share_level = 10;
+int party_send_xy_timer(int tid,unsigned int tick,int id,int data);
+
+/*==========================================
+ * Fills the given party_member structure according to the sd provided.
+ * Used when creating/adding people to a party. [Skotlex]
+ *------------------------------------------
+ */
+static void party_fill_member(struct party_member *member, struct map_session_data *sd) {
+ member->account_id = sd->status.account_id;
+ member->char_id = sd->status.char_id;
+ memcpy(member->name,sd->status.name,NAME_LENGTH);
+ member->class_ = sd->status.class_;
+ member->map = sd->mapindex;
+ member->lv = sd->status.base_level;
+ member->online = 1;
+ member->leader = 0;
+}
+
+/*==========================================
+ * 終了
+ *------------------------------------------
+ */
+void do_final_party(void)
+{
+ party_db->destroy(party_db,NULL);
+}
+// 初期化
+void do_init_party(void)
+{
+ party_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ add_timer_func_list(party_send_xy_timer,"party_send_xy_timer");
+ add_timer_interval(gettick()+battle_config.party_update_interval,party_send_xy_timer,0,0,battle_config.party_update_interval);
+}
+
+// 検索
+struct party_data *party_search(int party_id)
+{
+ if(!party_id) return NULL;
+ if (party_cache && party_cache->party.party_id == party_id)
+ return party_cache;
+
+ party_cache = idb_get(party_db,party_id);
+ return party_cache;
+}
+int party_searchname_sub(DBKey key,void *data,va_list ap)
+{
+ struct party_data *p=(struct party_data *)data,**dst;
+ char *str;
+ str=va_arg(ap,char *);
+ dst=va_arg(ap,struct party_data **);
+ if(strncmpi(p->party.name,str,NAME_LENGTH)==0)
+ *dst=p;
+ return 0;
+}
+
+struct party_data* party_searchname(char *str)
+{
+ struct party_data *p=NULL;
+ party_db->foreach(party_db,party_searchname_sub,str,&p);
+ return p;
+}
+
+int party_create(struct map_session_data *sd,char *name,int item,int item2)
+{
+ struct party_member leader;
+ nullpo_retr(0, sd);
+
+ if(sd->status.party_id) {
+ clif_party_created(sd,2);
+ return 0;
+ }
+
+ party_fill_member(&leader, sd);
+ leader.leader = 1;
+
+ intif_create_party(&leader,name,item,item2);
+ return 0;
+}
+
+
+int party_created(int account_id,int char_id,int fail,int party_id,char *name)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ sd=map_id2sd(account_id);
+
+ nullpo_retr(0, sd);
+ if (sd->status.char_id != char_id)
+ return 0; //unlikely failure...
+
+ if(fail){
+ clif_party_created(sd,1);
+ return 0;
+ }
+ sd->status.party_id=party_id;
+ if(idb_get(party_db,party_id)!=NULL){
+ ShowFatalError("party: id already exists!\n");
+ exit(1);
+ }
+ p=(struct party_data *)aCalloc(1,sizeof(struct party_data));
+ p->party.party_id=party_id;
+ memcpy(p->party.name, name, NAME_LENGTH);
+ idb_put(party_db,party_id,p);
+ clif_party_created(sd,0); //Success message
+ clif_charnameupdate(sd); //Update other people's display. [Skotlex]
+ return 1;
+}
+
+int party_request_info(int party_id)
+{
+ return intif_request_partyinfo(party_id);
+}
+
+int party_check_member(struct party *p)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ nullpo_retr(0, p);
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++)
+ {
+ if((sd = all_sd[i]) && sd->status.party_id==p->party_id)
+ {
+ int j,f=1;
+ for(j=0;j<MAX_PARTY;j++){
+ if(p->member[j].account_id==sd->status.account_id &&
+ p->member[j].char_id==sd->status.char_id)
+ {
+ f=0;
+ break;
+ }
+ }
+
+ if(f){
+ sd->status.party_id=0;
+ if(battle_config.error_log)
+ ShowWarning("party: check_member %d[%s] is not member\n",sd->status.account_id,sd->status.name);
+ }
+ }
+ }
+ return 0;
+}
+
+int party_recv_noinfo(int party_id)
+{
+ int i, users;
+ struct map_session_data *sd, **all_sd;
+
+ all_sd = map_getallusers(&users);
+
+ for(i=0;i<users;i++){
+ if((sd = all_sd[i]) && sd->status.party_id==party_id)
+ sd->status.party_id=0;
+ }
+ return 0;
+}
+
+static void* create_party(DBKey key, va_list args) {
+ struct party_data *p;
+ p=(struct party_data *)aCalloc(1,sizeof(struct party_data));
+ return p;
+}
+
+static void party_check_state(struct party_data *p)
+{
+ int i;
+ malloc_set(&p->state, 0, sizeof(p->state));
+ for (i = 0; i < MAX_PARTY; i ++)
+ {
+ if (!p->party.member[i].online) continue; //Those not online shouldn't aport to skill usage and all that.
+ switch (p->party.member[i].class_) {
+ case JOB_MONK:
+ case JOB_BABY_MONK:
+ case JOB_CHAMPION:
+ p->state.monk = 1;
+ break;
+ case JOB_STAR_GLADIATOR:
+ p->state.sg = 1;
+ break;
+ case JOB_SUPER_NOVICE:
+ case JOB_SUPER_BABY:
+ p->state.snovice = 1;
+ break;
+ case JOB_TAEKWON:
+ p->state.tk = 1;
+ break;
+ }
+ }
+}
+
+int party_recv_info(struct party *sp)
+{
+ struct map_session_data *sd;
+ struct party_data *p;
+ int i;
+
+ nullpo_retr(0, sp);
+
+ p= idb_ensure(party_db, sp->party_id, create_party);
+ if (!p->party.party_id) //party just received.
+ party_check_member(sp);
+ memcpy(&p->party,sp,sizeof(struct party));
+ malloc_set(&p->state, 0, sizeof(p->state));
+ malloc_set(&p->data, 0, sizeof(p->data));
+ for(i=0;i<MAX_PARTY;i++){
+ if (!p->party.member[i].account_id)
+ continue;
+ sd = map_id2sd(p->party.member[i].account_id);
+ if (sd && sd->status.party_id==p->party.party_id
+ && sd->status.char_id == p->party.member[i].char_id
+ && !sd->state.waitingdisconnect)
+ p->data[i].sd = sd;
+ }
+ party_check_state(p);
+ for(i=0;i<MAX_PARTY;i++){
+ sd = p->data[i].sd;
+ if(!sd || sd->state.party_sent)
+ continue;
+ clif_party_main_info(p,-1);
+ clif_party_option(p,sd,0x100);
+ clif_party_info(p,-1);
+ sd->state.party_sent=1;
+ }
+
+ return 0;
+}
+
+int party_invite(struct map_session_data *sd,struct map_session_data *tsd)
+{
+ struct party_data *p=party_search(sd->status.party_id);
+ int i,flag=0;
+
+ nullpo_retr(0, sd);
+
+ if(tsd==NULL || p==NULL)
+ return 0;
+ if(!battle_config.invite_request_check) {
+ if (tsd->guild_invite>0 || tsd->trade_partner) {
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ }
+ if( tsd->status.party_id>0 || tsd->party_invite>0 ){
+ clif_party_inviteack(sd,tsd->status.name,0);
+ return 0;
+ }
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->party.member[i].account_id == 0) //Room for a new member.
+ flag = 1;
+ /* By default Aegis BLOCKS more than one char from the same account on a party.
+ * But eA does support it... so this check is left commented.
+ if(p->party.member[i].account_id==tsd->status.account_id)
+ {
+ clif_party_inviteack(sd,tsd->status.name,4);
+ return 0;
+ }
+ */
+ }
+ if (!flag) { //Full party.
+ clif_party_inviteack(sd,tsd->status.name,3);
+ return 0;
+ }
+
+ tsd->party_invite=sd->status.party_id;
+ tsd->party_invite_account=sd->status.account_id;
+
+ clif_party_invite(sd,tsd);
+ return 1;
+}
+
+int party_reply_invite(struct map_session_data *sd,int account_id,int flag)
+{
+ struct map_session_data *tsd= map_id2sd(account_id);
+ struct party_member member;
+ nullpo_retr(0, sd);
+
+ if(flag==1){
+ party_fill_member(&member, sd);
+ intif_party_addmember(sd->party_invite, &member);
+ return 0;
+ }
+ sd->party_invite=0;
+ sd->party_invite_account=0;
+ if(tsd==NULL)
+ return 0;
+ clif_party_inviteack(tsd,sd->status.name,1);
+ return 1;
+}
+
+int party_member_added(int party_id,int account_id,int char_id, int flag)
+{
+ struct map_session_data *sd = map_id2sd(account_id),*sd2;
+ struct party_data *p = party_search(party_id);
+ if(sd == NULL || sd->status.char_id != char_id){
+ if (flag == 0) {
+ if(battle_config.error_log)
+ ShowError("party: member added error %d is not online\n",account_id);
+ intif_party_leave(party_id,account_id,char_id);
+ }
+ return 0;
+ }
+ sd->party_invite=0;
+ sd->party_invite_account=0;
+
+ if (!p) {
+ if(battle_config.error_log)
+ ShowError("party_member_added: party %d not found.\n",party_id);
+ intif_party_leave(party_id,account_id,char_id);
+ return 0;
+ }
+
+ if(!flag) {
+ sd->state.party_sent=0;
+ sd->status.party_id=party_id;
+ party_check_conflict(sd);
+ clif_party_join_info(&p->party,sd);
+ clif_party_hp(sd);
+ clif_party_xy(sd);
+ clif_charnameupdate(sd); //Update char name's display [Skotlex]
+ }
+
+ sd2=map_id2sd(sd->party_invite_account);
+ if (sd2)
+ clif_party_inviteack(sd2,sd->status.name,flag?2:1);
+ return 0;
+}
+
+int party_removemember(struct map_session_data *sd,int account_id,char *name)
+{
+ struct party_data *p;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (p = party_search(sd->status.party_id)) == NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->party.member[i].account_id==sd->status.account_id &&
+ p->party.member[i].char_id==sd->status.char_id) {
+ if(p->party.member[i].leader)
+ break;
+ return 0;
+ }
+ }
+ if (i >= MAX_PARTY) //Request from someone not in party? o.O
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->party.member[i].account_id==account_id &&
+ strncmp(p->party.member[i].name,name,NAME_LENGTH)==0)
+ {
+ intif_party_leave(p->party.party_id,account_id,p->party.member[i].char_id);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int party_leave(struct map_session_data *sd)
+{
+ struct party_data *p;
+ int i;
+
+ nullpo_retr(0, sd);
+
+ if( (p = party_search(sd->status.party_id)) == NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->party.member[i].account_id==sd->status.account_id &&
+ p->party.member[i].char_id==sd->status.char_id){
+ intif_party_leave(p->party.party_id,sd->status.account_id,sd->status.char_id);
+ return 0;
+ }
+ }
+ return 0;
+}
+
+int party_member_leaved(int party_id,int account_id,int char_id)
+{
+ struct map_session_data *sd=map_id2sd(account_id);
+ struct party_data *p=party_search(party_id);
+ int i;
+ if (sd && sd->status.char_id != char_id) //Wrong target
+ sd = NULL;
+ if(p!=NULL){
+ for(i=0;i<MAX_PARTY;i++)
+ if(p->party.member[i].account_id==account_id &&
+ p->party.member[i].char_id==char_id){
+ clif_party_leaved(p,sd,account_id,p->party.member[i].name,0x00);
+ malloc_set(&p->party.member[i], 0, sizeof(p->party.member[0]));
+ malloc_set(&p->data[i], 0, sizeof(p->data[0]));
+ p->party.count--;
+ party_check_state(p);
+ break;
+ }
+ }
+ if(sd!=NULL && sd->status.party_id==party_id){
+ sd->status.party_id=0;
+ sd->state.party_sent=0;
+ clif_charnameupdate(sd); //Update name display [Skotlex]
+ }
+ return 0;
+}
+
+int party_broken(int party_id)
+{
+ struct party_data *p;
+ int i;
+ if( (p=party_search(party_id))==NULL )
+ return 0;
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(p->data[i].sd!=NULL){
+ clif_party_leaved(p,p->data[i].sd,
+ p->party.member[i].account_id,p->party.member[i].name,0x10);
+ p->data[i].sd->status.party_id=0;
+ p->data[i].sd->state.party_sent=0;
+ }
+ }
+ if (party_cache && party_cache->party.party_id == party_id)
+ party_cache = NULL;
+ idb_remove(party_db,party_id);
+ return 0;
+}
+
+int party_changeoption(struct map_session_data *sd,int exp,int item)
+{
+ nullpo_retr(0, sd);
+
+ if( sd->status.party_id==0)
+ return 0;
+ intif_party_changeoption(sd->status.party_id,sd->status.account_id,exp,item);
+ return 0;
+}
+
+int party_optionchanged(int party_id,int account_id,int exp,int item,int flag)
+{
+ struct party_data *p;
+ struct map_session_data *sd=map_id2sd(account_id);
+ if( (p=party_search(party_id))==NULL)
+ return 0;
+
+ if(!(flag&0x01) && p->party.exp != exp) {
+ p->party.exp=exp;
+ clif_party_option(p,sd,flag); //This packet doesn't updates item info anymore...
+ }
+ if(!(flag&0x10) && p->party.item != item) {
+ p->party.item=item;
+ clif_party_main_info(p,-1);
+ }
+ if(flag&0x01) //Send denied message
+ clif_party_option(p,sd,flag);
+ return 0;
+}
+
+int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv)
+{
+ struct party_data *p;
+ int i;
+ if( (p=party_search(party_id))==NULL)
+ return 0;
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *sd;
+ struct party_member *m=&p->party.member[i];
+ if(m->account_id==account_id && m->char_id==char_id){
+ m->map = map;
+ m->online=online;
+ m->lv=lv;
+ //Check if they still exist on this map server
+ sd = map_id2sd(m->account_id);
+ p->data[i].sd = (sd!=NULL && sd->status.party_id==p->party.party_id && sd->status.char_id == m->char_id && !sd->state.waitingdisconnect)?sd:NULL;
+ break;
+ }
+ }
+ if(i==MAX_PARTY){
+ if(battle_config.error_log)
+ ShowError("party: not found member %d/%d on %d[%s]",account_id,char_id,party_id,p->party.name);
+ return 0;
+ }
+
+ clif_party_info(p,-1);
+ return 0;
+}
+
+int party_send_movemap(struct map_session_data *sd)
+{
+ int i;
+ struct party_data *p;
+
+ nullpo_retr(0, sd);
+
+ if( sd->status.party_id==0 )
+ return 0;
+ intif_party_changemap(sd,1);
+
+ p=party_search(sd->status.party_id);
+ if (p && sd->fd) {
+ //Send dots of other party members to this char. [Skotlex]
+ for(i=0; i < MAX_PARTY; i++) {
+ if (!p->data[i].sd || p->data[i].sd == sd ||
+ p->data[i].sd->bl.m != sd->bl.m)
+ continue;
+ clif_party_xy_single(sd->fd, p->data[i].sd);
+ }
+
+ }
+
+ if( sd->state.party_sent )
+ return 0;
+
+ party_check_conflict(sd);
+
+ if(p){
+ party_check_member(&p->party);
+ if(sd->status.party_id==p->party.party_id){
+ clif_party_main_info(p,sd->fd);
+ clif_party_option(p,sd,0x100);
+ clif_party_info(p,sd->fd);
+ sd->state.party_sent=1;
+ }
+ }
+
+ return 0;
+}
+
+int party_send_logout(struct map_session_data *sd)
+{
+ struct party_data *p;
+ int i;
+
+ if(!sd->status.party_id)
+ return 0;
+
+ intif_party_changemap(sd,0);
+ p=party_search(sd->status.party_id);
+ if(!p) return 0;
+
+ for(i=0;i<MAX_PARTY && p->data[i].sd != sd;i++);
+ if (i < MAX_PARTY)
+ malloc_set(&p->data[i], 0, sizeof(p->data[0]));
+
+ return 1;
+}
+
+int party_send_message(struct map_session_data *sd,char *mes,int len)
+{
+ if(sd->status.party_id==0)
+ return 0;
+ intif_party_message(sd->status.party_id,sd->status.account_id,mes,len);
+ party_recv_message(sd->status.party_id,sd->status.account_id,mes,len);
+ //Chat Logging support Type 'P'
+ if(log_config.chat&1 //we log everything then
+ || (log_config.chat&4 //if Party bit is on
+ && ( !agit_flag || !(log_config.chat&16) ))) //if WOE ONLY flag is off or AGIT is OFF
+ log_chat("P", sd->status.party_id, sd->status.char_id, sd->status.account_id, (char*)mapindex_id2name(sd->mapindex), sd->bl.x, sd->bl.y, NULL, mes);
+
+ return 0;
+}
+
+int party_recv_message(int party_id,int account_id,char *mes,int len)
+{
+ struct party_data *p;
+ if( (p=party_search(party_id))==NULL)
+ return 0;
+ clif_party_message(p,account_id,mes,len);
+ return 0;
+}
+
+int party_check_conflict(struct map_session_data *sd)
+{
+ nullpo_retr(0, sd);
+
+ intif_party_checkconflict(sd->status.party_id,sd->status.account_id,sd->status.char_id);
+ return 0;
+}
+
+int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv)
+{
+ struct party_data *p;
+ struct map_session_data *p_sd;
+ int i;
+
+ if(!party_id || (p=party_search(party_id))==NULL)
+ return 0;
+ switch(skillid) {
+ case TK_COUNTER: //Increase Triple Attack rate of Monks.
+ if (!p->state.monk) return 0;
+ break;
+ case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators
+ if (!p->state.sg) return 0;
+ break;
+ case AM_TWILIGHT2: //Twilight Pharmacy, requires Super Novice
+ return p->state.snovice;
+ case AM_TWILIGHT3: //Twilight Pharmacy, Requires Taekwon
+ return p->state.tk;
+ default:
+ return 0; //Unknown case?
+ }
+
+ for(i=0;i<MAX_PARTY;i++){
+ if ((p_sd = p->data[i].sd) == NULL)
+ continue;
+ if (sd->bl.m != p_sd->bl.m)
+ continue;
+ switch(skillid) {
+ case TK_COUNTER: //Increase Triple Attack rate of Monks.
+ if((p_sd->class_&MAPID_UPPERMASK) == MAPID_MONK
+ && pc_checkskill(p_sd,MO_TRIPLEATTACK)) {
+ sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,MO_TRIPLEATTACK,
+ 50+50*skilllv, //+100/150/200% rate
+ 0,0,skill_get_time(SG_FRIEND, 1));
+ }
+ break;
+ case MO_COMBOFINISH: //Increase Counter rate of Star Gladiators
+ if((p_sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR
+ && sd->sc.data[SC_READYCOUNTER].timer != -1
+ && pc_checkskill(p_sd,SG_FRIEND)) {
+ sc_start4(&p_sd->bl,SC_SKILLRATE_UP,100,TK_COUNTER,
+ 50+50*pc_checkskill(p_sd,SG_FRIEND), //+100/150/200% rate
+ 0,0,skill_get_time(SG_FRIEND, 1));
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+int party_send_xy_timer_sub(DBKey key,void *data,va_list ap)
+{
+ struct party_data *p=(struct party_data *)data;
+ struct map_session_data *sd;
+ int i;
+
+ nullpo_retr(0, p);
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(!p->data[i].sd) continue;
+ sd = p->data[i].sd;
+ if (p->data[i].x != sd->bl.x || p->data[i].y != sd->bl.y)
+ {
+ clif_party_xy(sd);
+ p->data[i].x = sd->bl.x;
+ p->data[i].y = sd->bl.y;
+ }
+ if (battle_config.party_hp_mode &&
+ p->data[i].hp != sd->battle_status.hp)
+ {
+ clif_party_hp(sd);
+ p->data[i].hp = sd->battle_status.hp;
+ }
+ }
+ return 0;
+}
+
+int party_send_xy_timer(int tid,unsigned int tick,int id,int data)
+{
+ party_db->foreach(party_db,party_send_xy_timer_sub,tick);
+ return 0;
+}
+
+int party_send_xy_clear(struct party_data *p)
+{
+ int i;
+
+ nullpo_retr(0, p);
+
+ for(i=0;i<MAX_PARTY;i++){
+ if(!p->data[i].sd) continue;
+ p->data[i].hp = 0;
+ p->data[i].x = 0;
+ p->data[i].y = 0;
+ }
+ return 0;
+}
+
+// exp share and added zeny share [Valaris]
+int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny)
+{
+ struct map_session_data* sd[MAX_PARTY];
+ int i;
+ unsigned short c;
+
+ nullpo_retr(0, p);
+
+ for (i = c = 0; i < MAX_PARTY; i++)
+ if ((sd[c] = p->data[i].sd)!=NULL && sd[c]->bl.m == src->m && !pc_isdead(sd[c])) {
+ if (battle_config.idle_no_share && (sd[c]->chatID || sd[c]->vender_id || (sd[c]->idletime < (last_tick - battle_config.idle_no_share))))
+ continue;
+ c++;
+ }
+ if (c < 1)
+ return 0;
+
+ base_exp/=c;
+ job_exp/=c;
+ zeny/=c;
+
+ if (battle_config.party_even_share_bonus && c > 1) {
+ unsigned short bonus =100 + battle_config.party_even_share_bonus*(c-1);
+ if (base_exp) {
+ if (base_exp/100 > UINT_MAX/bonus)
+ base_exp= UINT_MAX; //Exp overflow
+ else if (base_exp > 10000)
+ base_exp = (base_exp/100)*bonus; //Calculation overflow protection
+ else
+ base_exp = base_exp*bonus/100;
+ }
+ if (job_exp) {
+ if (job_exp/100 > UINT_MAX/bonus)
+ job_exp = UINT_MAX;
+ else if (job_exp > 10000)
+ job_exp = (job_exp/100)*bonus;
+ else
+ job_exp = job_exp*bonus/100;
+ }
+ if (zeny) {
+ if (zeny/100 > INT_MAX/bonus)
+ zeny = INT_MAX;
+ else if (zeny > 10000)
+ zeny = (zeny/100)*bonus;
+ else
+ zeny = zeny*bonus/100;
+ }
+ }
+
+ for (i = 0; i < c; i++)
+ {
+ pc_gainexp(sd[i], src, base_exp, job_exp);
+ if (zeny) // zeny from mobs [Valaris]
+ pc_getzeny(sd[i],zeny);
+ }
+ return 0;
+}
+
+//Does party loot. first holds the id of the player who has time priority to take the item.
+int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int first)
+{
+ TBL_PC *target=NULL;
+ int i;
+ if (p && p->party.item&2 && (first || !(battle_config.party_share_type&1))) {
+ //item distribution to party members.
+ if (battle_config.party_share_type&2) { //Round Robin
+ TBL_PC *psd;
+ i = p->itemc;
+ do {
+ i++;
+ if (i >= MAX_PARTY)
+ i = 0; // reset counter to 1st person in party so it'll stop when it reaches "itemc"
+ if ((psd=p->data[i].sd)==NULL || sd->bl.m != psd->bl.m ||
+ pc_isdead(psd) || (battle_config.idle_no_share && (
+ psd->chatID || psd->vender_id || (psd->idletime < (last_tick - battle_config.idle_no_share)))
+ ))
+ continue;
+
+ if (pc_additem(psd,item_data,item_data->amount))
+ continue; //Chosen char can't pick up loot.
+ //Successful pick.
+ p->itemc = i;
+ target = psd;
+ break;
+ } while (i != p->itemc);
+ } else { //Random pick
+ TBL_PC *psd[MAX_PARTY];
+ int count=0;
+ //Collect pick candidates
+ for (i = 0; i < MAX_PARTY; i++) {
+ if ((psd[count]=p->data[i].sd) && psd[count]->bl.m == sd->bl.m &&
+ !pc_isdead(psd[count]) && (!battle_config.idle_no_share || (
+ !psd[count]->chatID && !psd[count]->vender_id &&
+ (psd[count]->idletime >= (last_tick - battle_config.idle_no_share)))
+ ))
+ count++;
+ }
+ while (count > 0) { //Pick a random member.
+ i = rand()%count;
+ if (pc_additem(psd[i],item_data,item_data->amount))
+ { //Discard this receiver.
+ psd[i] = psd[count-1];
+ count--;
+ } else { //Successful pick.
+ target = psd[i];
+ break;
+ }
+ }
+ }
+ }
+ if (!target) { //Give it to the owner.
+ target = sd;
+ if ((i=pc_additem(sd,item_data,item_data->amount)))
+ return i;
+ }
+
+ if(log_config.enable_logs&0x8) //Logs items, taken by (P)layers [Lupus]
+ log_pick_pc(target, "P", item_data->nameid, item_data->amount, item_data);
+ //Logs
+ if(battle_config.party_show_share_picker && target != sd){
+ char output[80];
+ sprintf(output, "%s acquired the item.",target->status.name);
+ clif_disp_onlyself(sd,output,strlen(output));
+ }
+ return 0;
+}
+
+int party_send_dot_remove(struct map_session_data *sd)
+{
+ if (sd->status.party_id)
+ clif_party_xy_remove(sd);
+ return 0;
+}
+
+// To use for Taekwon's "Fighting Chant"
+// int c = 0;
+// party_foreachsamemap(party_sub_count, sd, 0, &c);
+int party_sub_count(struct block_list *bl, va_list ap)
+{
+ struct map_session_data *sd = (TBL_PC *)bl;
+
+ if (sd->state.autotrade)
+ return 0;
+
+ if (battle_config.idle_no_share && (sd->chatID || sd->vender_id || (sd->idletime < (last_tick - battle_config.idle_no_share))))
+ return 0;
+
+ return 1;
+}
+
+int party_foreachsamemap(int (*func)(struct block_list*,va_list),struct map_session_data *sd,int range,...)
+{
+ struct party_data *p;
+ va_list ap;
+ int i;
+ int x0,y0,x1,y1;
+ struct block_list *list[MAX_PARTY];
+ int blockcount=0;
+ int total = 0; //Return value.
+
+ nullpo_retr(0,sd);
+
+ if((p=party_search(sd->status.party_id))==NULL)
+ return 0;
+
+ x0=sd->bl.x-range;
+ y0=sd->bl.y-range;
+ x1=sd->bl.x+range;
+ y1=sd->bl.y+range;
+
+ va_start(ap,range);
+
+ for(i=0;i<MAX_PARTY;i++){
+ struct map_session_data *psd=p->data[i].sd;
+ if(!psd) continue;
+ if(psd->bl.m!=sd->bl.m || !psd->bl.prev)
+ continue;
+ if(range &&
+ (psd->bl.x<x0 || psd->bl.y<y0 ||
+ psd->bl.x>x1 || psd->bl.y>y1 ) )
+ continue;
+ list[blockcount++]=&psd->bl;
+ }
+
+ map_freeblock_lock();
+
+ for(i=0;i<blockcount;i++)
+ total += func(list[i],ap);
+
+ map_freeblock_unlock();
+
+ va_end(ap);
+ return total;
+}
diff --git a/src/map/party.h b/src/map/party.h
index a3e7cfaa0..92d78a4b6 100644
--- a/src/map/party.h
+++ b/src/map/party.h
@@ -1,49 +1,49 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _PARTY_H_
-#define _PARTY_H_
-
-#include <stdarg.h>
-#include "map.h"
-
-extern int party_share_level;
-struct party;
-struct map_session_data;
-struct block_list;
-
-void do_init_party(void);
-void do_final_party(void);
-struct party_data *party_search(int party_id);
-struct party_data *party_searchname(char *str);
-
-int party_create(struct map_session_data *sd,char *name, int item, int item2);
-int party_created(int account_id,int char_id,int fail,int party_id,char *name);
-int party_request_info(int party_id);
-int party_invite(struct map_session_data *sd,struct map_session_data *tsd);
-int party_member_added(int party_id,int account_id,int char_id,int flag);
-int party_leave(struct map_session_data *sd);
-int party_removemember(struct map_session_data *sd,int account_id,char *name);
-int party_member_leaved(int party_id,int account_id,int char_id);
-int party_reply_invite(struct map_session_data *sd,int account_id,int flag);
-int party_recv_noinfo(int party_id);
-int party_recv_info(struct party *sp);
-int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv);
-int party_broken(int party_id);
-int party_optionchanged(int party_id,int account_id,int exp,int item,int flag);
-int party_changeoption(struct map_session_data *sd,int exp,int item);
-int party_send_movemap(struct map_session_data *sd);
-int party_send_logout(struct map_session_data *sd);
-int party_send_message(struct map_session_data *sd,char *mes,int len);
-int party_recv_message(int party_id,int account_id,char *mes,int len);
-int party_check_conflict(struct map_session_data *sd);
-int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv);
-int party_send_xy_clear(struct party_data *p);
-int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny);
-int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int type);
-int party_send_dot_remove(struct map_session_data *sd);
-int party_sub_count(struct block_list *bl, va_list ap);
-int party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...);
-
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PARTY_H_
+#define _PARTY_H_
+
+#include <stdarg.h>
+#include "map.h"
+
+extern int party_share_level;
+struct party;
+struct map_session_data;
+struct block_list;
+
+void do_init_party(void);
+void do_final_party(void);
+struct party_data *party_search(int party_id);
+struct party_data *party_searchname(char *str);
+
+int party_create(struct map_session_data *sd,char *name, int item, int item2);
+int party_created(int account_id,int char_id,int fail,int party_id,char *name);
+int party_request_info(int party_id);
+int party_invite(struct map_session_data *sd,struct map_session_data *tsd);
+int party_member_added(int party_id,int account_id,int char_id,int flag);
+int party_leave(struct map_session_data *sd);
+int party_removemember(struct map_session_data *sd,int account_id,char *name);
+int party_member_leaved(int party_id,int account_id,int char_id);
+int party_reply_invite(struct map_session_data *sd,int account_id,int flag);
+int party_recv_noinfo(int party_id);
+int party_recv_info(struct party *sp);
+int party_recv_movemap(int party_id,int account_id,int char_id, unsigned short map,int online,int lv);
+int party_broken(int party_id);
+int party_optionchanged(int party_id,int account_id,int exp,int item,int flag);
+int party_changeoption(struct map_session_data *sd,int exp,int item);
+int party_send_movemap(struct map_session_data *sd);
+int party_send_logout(struct map_session_data *sd);
+int party_send_message(struct map_session_data *sd,char *mes,int len);
+int party_recv_message(int party_id,int account_id,char *mes,int len);
+int party_check_conflict(struct map_session_data *sd);
+int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv);
+int party_send_xy_clear(struct party_data *p);
+int party_exp_share(struct party_data *p,struct block_list *src,unsigned int base_exp,unsigned int job_exp,int zeny);
+int party_share_loot(struct party_data *p, TBL_PC *sd, struct item *item_data, int type);
+int party_send_dot_remove(struct map_session_data *sd);
+int party_sub_count(struct block_list *bl, va_list ap);
+int party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...);
+
+
+#endif
diff --git a/src/map/pc.h b/src/map/pc.h
index d4397db8f..9d9809263 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -1,318 +1,318 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _PC_H_
-#define _PC_H_
-
-#include "map.h"
-#include "unit.h"
-
-#define OPTION_MASK 0xd7b8
-#define CART_MASK 0x788
-
-//Update this max as necessary. 53 is the value needed for Super Baby currently
-#define MAX_SKILL_TREE 53
-
-enum {
- W_FIST, //Bare hands
- W_DAGGER, //1
- W_1HSWORD, //2
- W_2HSWORD, //3
- W_1HSPEAR, //4
- W_2HSPEAR, //5
- W_1HAXE, //6
- W_2HAXE, //7
- W_MACE, //8
- W_UNKNOWN, //View 9 seems unused anywhere
- W_STAFF, //10
- W_BOW, //11
- W_KNUCKLE, //12
- W_MUSICAL, //13
- W_WHIP, //14
- W_BOOK, //15
- W_KATAR, //16
- W_REVOLVER, //17
- W_RIFLE, //18
- W_SHOTGUN, //19
- W_GATLING, //20
- W_GRENADE, //21
- W_HUUMA, //22
- MAX_WEAPON_TYPE
-} weapon_type;
-
-enum {
- A_ARROW = 1,
- A_DAGGER, //2
- A_BULLET, //3
- A_SHELL, //4
- A_GRENADE, //5
- A_SHURIKEN, //6
- A_KUNAI //7
-} ammo_type;
-//Equip position constants
-enum {
- EQP_HEAD_LOW = 0x0001,
- EQP_HEAD_MID = 0x0200, //512
- EQP_HEAD_TOP = 0x0100, //256
- EQP_HAND_R = 0x0002,
- EQP_HAND_L = 0x0020, //32
- EQP_ARMOR = 0x0010, //16
- EQP_SHOES = 0x0040, //64
- EQP_GARMENT = 0x0004,
- EQP_ACC_L = 0x0008,
- EQP_ACC_R = 0x0080, //128
- EQP_AMMO = 0x8000, //32768
-} equip_pos_enum;
-
-#define EQP_WEAPON EQP_HAND_R
-#define EQP_SHIELD EQP_HAND_L
-#define EQP_ARMS (EQP_HAND_R|EQP_HAND_L)
-#define EQP_HELM (EQP_HEAD_LOW|EQP_HEAD_MID|EQP_HEAD_TOP)
-#define EQP_ACC (EQP_ACC_L|EQP_ACC_R)
-
-//Equip indexes constants. (eg: sd->equip_index[EQI_AMMO] returns the index
-//where the arrows are equipped)
-enum {
- EQI_ACC_L = 0,
- EQI_ACC_R,
- EQI_SHOES,
- EQI_GARMENT,
- EQI_HEAD_LOW,
- EQI_HEAD_MID,
- EQI_HEAD_TOP,
- EQI_ARMOR,
- EQI_HAND_L,
- EQI_HAND_R,
- EQI_AMMO,
- EQI_MAX
-} equip_index_enum;
-
-#define pc_setdead(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 1)
-#define pc_setsit(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 2)
-#define pc_isdead(sd) ((sd)->state.dead_sit == 1)
-#define pc_issit(sd) ((sd)->vd.dead_sit == 2)
-#define pc_setdir(sd,b,h) ((sd)->ud.dir = (b) ,(sd)->head_dir = (h) )
-#define pc_setchatid(sd,n) ((sd)->chatID = n)
-#define pc_ishiding(sd) ((sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK))
-#define pc_iscloaking(sd) (!((sd)->sc.option&OPTION_CHASEWALK) && ((sd)->sc.option&OPTION_CLOAK))
-#define pc_ischasewalk(sd) ((sd)->sc.option&OPTION_CHASEWALK)
-#define pc_iscarton(sd) ((sd)->sc.option&CART_MASK)
-#define pc_isfalcon(sd) ((sd)->sc.option&OPTION_FALCON)
-#define pc_isriding(sd) ((sd)->sc.option&OPTION_RIDING)
-#define pc_isinvisible(sd) ((sd)->sc.option&OPTION_INVISIBLE)
-#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight)
-#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9)
-#define pc_maxparameter(sd) ((sd->class_&JOBL_BABY) ? battle_config.max_baby_parameter : battle_config.max_parameter)
-
-#define pc_stop_attack(sd) { if (sd->ud.attacktimer!=-1) { unit_stop_attack(&sd->bl); sd->ud.target = 0; } }
-#define pc_stop_walking(sd, type) { if (sd->ud.walktimer!=-1) unit_stop_walking(&sd->bl, type); }
-
-//Weapon check considering dual wielding.
-#define pc_check_weapontype(sd, type) ((type)&((sd)->status.weapon < MAX_WEAPON_TYPE? \
- 1<<(sd)->status.weapon:(1<<(sd)->weapontype1)|(1<<(sd)->weapontype2)))
-//Checks if the given class value corresponds to a player class. [Skotlex]
-#define pcdb_checkid(class_) (class_ <= JOB_XMAS || (class_ >= JOB_NOVICE_HIGH && class_ <= JOB_SOUL_LINKER))
-
-int pc_isGM(struct map_session_data *sd);
-int pc_getrefinebonus(int lv,int type);
-int pc_can_give_items(int level); //[Lupus]
-
-int pc_setrestartvalue(struct map_session_data *sd,int type);
-int pc_makesavestatus(struct map_session_data *);
-int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int);
-int pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *);
-int pc_authfail(struct map_session_data *);
-int pc_reg_received(struct map_session_data *sd);
-
-int pc_isequip(struct map_session_data *sd,int n);
-int pc_equippoint(struct map_session_data *sd,int n);
-
-int pc_checkskill(struct map_session_data *sd,int skill_id);
-int pc_checkallowskill(struct map_session_data *sd);
-int pc_checkequip(struct map_session_data *sd,int pos);
-
-int pc_calc_skilltree(struct map_session_data *sd);
-int pc_calc_skilltree_normalize_job(struct map_session_data *sd);
-int pc_clean_skilltree(struct map_session_data *sd);
-
-#define pc_checkoverhp(sd) (sd->battle_status.hp == sd->battle_status.max_hp)
-#define pc_checkoversp(sd) (sd->battle_status.sp == sd->battle_status.max_sp)
-
-int pc_setpos(struct map_session_data*,unsigned short,int,int,int);
-int pc_setsavepoint(struct map_session_data*,short,int,int);
-int pc_randomwarp(struct map_session_data *sd,int type);
-int pc_memo(struct map_session_data *sd,int i);
-int pc_remove_map(struct map_session_data *sd,int clrtype);
-
-int pc_checkadditem(struct map_session_data*,int,int);
-int pc_inventoryblank(struct map_session_data*);
-int pc_search_inventory(struct map_session_data *sd,int item_id);
-int pc_payzeny(struct map_session_data*,int);
-int pc_additem(struct map_session_data*,struct item*,int);
-int pc_getzeny(struct map_session_data*,int);
-int pc_delitem(struct map_session_data*,int,int,int);
-
-int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount);
-int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type);
-int pc_putitemtocart(struct map_session_data *sd,int idx,int amount);
-int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount);
-int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount);
-
-int pc_takeitem(struct map_session_data*,struct flooritem_data*);
-int pc_dropitem(struct map_session_data*,int,int);
-
-int pc_checkweighticon(struct map_session_data *sd);
-
-int pc_bonus(struct map_session_data*,int,int);
-int pc_bonus2(struct map_session_data *sd,int,int,int);
-int pc_bonus3(struct map_session_data *sd,int,int,int,int);
-int pc_bonus4(struct map_session_data *sd,int,int,int,int,int);
-int pc_skill(struct map_session_data*,int,int,int);
-
-int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip);
-
-int pc_steal_item(struct map_session_data *sd,struct block_list *bl, int skilllv);
-int pc_steal_coin(struct map_session_data *sd,struct block_list *bl);
-
-int pc_modifybuyvalue(struct map_session_data*,int);
-int pc_modifysellvalue(struct map_session_data*,int);
-
-int pc_follow(struct map_session_data*, int); // [MouseJstr]
-int pc_stop_following(struct map_session_data*);
-
-unsigned int pc_maxbaselv(struct map_session_data *sd);
-unsigned int pc_maxjoblv(struct map_session_data *sd);
-int pc_checkbaselevelup(struct map_session_data *sd);
-int pc_checkjoblevelup(struct map_session_data *sd);
-int pc_gainexp(struct map_session_data*,struct block_list*,unsigned int,unsigned int);
-unsigned int pc_nextbaseexp(struct map_session_data *);
-unsigned int pc_nextjobexp(struct map_session_data *);
-int pc_need_status_point(struct map_session_data *,int);
-int pc_statusup(struct map_session_data*,int);
-int pc_statusup2(struct map_session_data*,int,int);
-int pc_skillup(struct map_session_data*,int);
-int pc_allskillup(struct map_session_data*);
-int pc_resetlvl(struct map_session_data*,int type);
-int pc_resetstate(struct map_session_data*);
-int pc_resetskill(struct map_session_data*, int);
-int pc_resetfeel(struct map_session_data*);
-int pc_resethate(struct map_session_data*);
-int pc_equipitem(struct map_session_data*,int,int);
-int pc_unequipitem(struct map_session_data*,int,int);
-int pc_checkitem(struct map_session_data*);
-int pc_useitem(struct map_session_data*,int);
-
-void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int hp, unsigned int sp);
-int pc_dead(struct map_session_data *sd,struct block_list *src);
-void pc_revive(struct map_session_data *sd,unsigned int hp, unsigned int sp);
-void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type);
-int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp);
-int pc_percentheal(struct map_session_data *sd,int,int);
-int pc_jobchange(struct map_session_data *,int, int);
-int pc_setoption(struct map_session_data *,int);
-int pc_setcart(struct map_session_data *sd,int type);
-int pc_setfalcon(struct map_session_data *sd);
-int pc_setriding(struct map_session_data *sd);
-int pc_changelook(struct map_session_data *,int,int);
-int pc_equiplookall(struct map_session_data *sd);
-
-int pc_readparam(struct map_session_data*,int);
-int pc_setparam(struct map_session_data*,int,int);
-int pc_readreg(struct map_session_data*,int);
-int pc_setreg(struct map_session_data*,int,int);
-char *pc_readregstr(struct map_session_data *sd,int reg);
-int pc_setregstr(struct map_session_data *sd,int reg,char *str);
-
-#define pc_readglobalreg(sd,reg) pc_readregistry(sd,reg,3)
-#define pc_setglobalreg(sd,reg,val) pc_setregistry(sd,reg,val,3)
-#define pc_readglobalreg_str(sd,reg) pc_readregistry_str(sd,reg,3)
-#define pc_setglobalreg_str(sd,reg,val) pc_setregistry_str(sd,reg,val,3)
-#define pc_readaccountreg(sd,reg) pc_readregistry(sd,reg,2)
-#define pc_setaccountreg(sd,reg,val) pc_setregistry(sd,reg,val,2)
-#define pc_readaccountregstr(sd,reg) pc_readregistry_str(sd,reg,2)
-#define pc_setaccountregstr(sd,reg,val) pc_setregistry_str(sd,reg,val,2)
-#define pc_readaccountreg2(sd,reg) pc_readregistry(sd,reg,1)
-#define pc_setaccountreg2(sd,reg,val) pc_setregistry(sd,reg,val,1)
-#define pc_readaccountreg2str(sd,reg) pc_readregistry_str(sd,reg,1)
-#define pc_setaccountreg2str(sd,reg,val) pc_setregistry_str(sd,reg,val,1)
-int pc_readregistry(struct map_session_data*,const char*,int);
-int pc_setregistry(struct map_session_data*,const char*,int,int);
-char *pc_readregistry_str(struct map_session_data*,char*,int);
-int pc_setregistry_str(struct map_session_data*,char*,char*,int);
-
-int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name);
-int pc_deleventtimer(struct map_session_data *sd,const char *name);
-int pc_cleareventtimer(struct map_session_data *sd);
-int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick);
-
-int pc_calc_pvprank(struct map_session_data *sd);
-int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data);
-
-int pc_ismarried(struct map_session_data *sd);
-int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd);
-int pc_divorce(struct map_session_data *sd);
-int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd,struct map_session_data *jasd);
-struct map_session_data *pc_get_partner(struct map_session_data *sd);
-struct map_session_data *pc_get_father(struct map_session_data *sd);
-struct map_session_data *pc_get_mother(struct map_session_data *sd);
-struct map_session_data *pc_get_child(struct map_session_data *sd);
-
-void pc_bleeding (struct map_session_data *sd, unsigned int diff_tick);
-
-int pc_set_gm_level(int account_id, int level);
-void pc_setstand(struct map_session_data *sd);
-int pc_candrop(struct map_session_data *sd,struct item *item);
-
-int pc_jobid2mapid(unsigned short b_class); // Skotlex
-int pc_mapid2jobid(unsigned short class_, int sex); // Skotlex
-
-char * job_name(int class_);
-
-struct skill_tree_entry {
- short id;
- unsigned char max;
- unsigned char joblv;
- struct {
- short id;
- unsigned char lv;
- } need[5];
-}; // Celest
-extern struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE];
-
-int pc_read_gm_account(int fd);
-int pc_setinvincibletimer(struct map_session_data *sd,int);
-int pc_delinvincibletimer(struct map_session_data *sd);
-int pc_addspiritball(struct map_session_data *sd,int,int);
-int pc_delspiritball(struct map_session_data *sd,int,int);
-void pc_addfame(struct map_session_data *sd,int count);
-unsigned char pc_famerank(int char_id, int job);
-int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl);
-
-extern struct fame_list smith_fame_list[MAX_FAME_LIST];
-extern struct fame_list chemist_fame_list[MAX_FAME_LIST];
-extern struct fame_list taekwon_fame_list[MAX_FAME_LIST];
-
-int pc_readdb(void);
-int do_init_pc(void);
-void do_final_pc(void);
-
-enum {ADDITEM_EXIST,ADDITEM_NEW,ADDITEM_OVERAMOUNT};
-
-// timer for night.day
-extern int day_timer_tid;
-extern int night_timer_tid;
-int map_day_timer(int,unsigned int,int,int); // by [yor]
-int map_night_timer(int,unsigned int,int,int); // by [yor]
-
-//Duel functions // [LuzZza]
-int duel_create(struct map_session_data* sd, const unsigned int maxpl);
-int duel_invite(const unsigned int did, struct map_session_data* sd, struct map_session_data* target_sd);
-int duel_accept(const unsigned int did, struct map_session_data* sd);
-int duel_reject(const unsigned int did, struct map_session_data* sd);
-int duel_leave(const unsigned int did, struct map_session_data* sd);
-int duel_showinfo(const unsigned int did, struct map_session_data* sd);
-int duel_checktime(struct map_session_data* sd);
-
-int pc_read_motd(void); // [Valaris]
-int pc_disguise(struct map_session_data *sd, int class_);
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PC_H_
+#define _PC_H_
+
+#include "map.h"
+#include "unit.h"
+
+#define OPTION_MASK 0xd7b8
+#define CART_MASK 0x788
+
+//Update this max as necessary. 53 is the value needed for Super Baby currently
+#define MAX_SKILL_TREE 53
+
+enum {
+ W_FIST, //Bare hands
+ W_DAGGER, //1
+ W_1HSWORD, //2
+ W_2HSWORD, //3
+ W_1HSPEAR, //4
+ W_2HSPEAR, //5
+ W_1HAXE, //6
+ W_2HAXE, //7
+ W_MACE, //8
+ W_UNKNOWN, //View 9 seems unused anywhere
+ W_STAFF, //10
+ W_BOW, //11
+ W_KNUCKLE, //12
+ W_MUSICAL, //13
+ W_WHIP, //14
+ W_BOOK, //15
+ W_KATAR, //16
+ W_REVOLVER, //17
+ W_RIFLE, //18
+ W_SHOTGUN, //19
+ W_GATLING, //20
+ W_GRENADE, //21
+ W_HUUMA, //22
+ MAX_WEAPON_TYPE
+} weapon_type;
+
+enum {
+ A_ARROW = 1,
+ A_DAGGER, //2
+ A_BULLET, //3
+ A_SHELL, //4
+ A_GRENADE, //5
+ A_SHURIKEN, //6
+ A_KUNAI //7
+} ammo_type;
+//Equip position constants
+enum {
+ EQP_HEAD_LOW = 0x0001,
+ EQP_HEAD_MID = 0x0200, //512
+ EQP_HEAD_TOP = 0x0100, //256
+ EQP_HAND_R = 0x0002,
+ EQP_HAND_L = 0x0020, //32
+ EQP_ARMOR = 0x0010, //16
+ EQP_SHOES = 0x0040, //64
+ EQP_GARMENT = 0x0004,
+ EQP_ACC_L = 0x0008,
+ EQP_ACC_R = 0x0080, //128
+ EQP_AMMO = 0x8000, //32768
+} equip_pos_enum;
+
+#define EQP_WEAPON EQP_HAND_R
+#define EQP_SHIELD EQP_HAND_L
+#define EQP_ARMS (EQP_HAND_R|EQP_HAND_L)
+#define EQP_HELM (EQP_HEAD_LOW|EQP_HEAD_MID|EQP_HEAD_TOP)
+#define EQP_ACC (EQP_ACC_L|EQP_ACC_R)
+
+//Equip indexes constants. (eg: sd->equip_index[EQI_AMMO] returns the index
+//where the arrows are equipped)
+enum {
+ EQI_ACC_L = 0,
+ EQI_ACC_R,
+ EQI_SHOES,
+ EQI_GARMENT,
+ EQI_HEAD_LOW,
+ EQI_HEAD_MID,
+ EQI_HEAD_TOP,
+ EQI_ARMOR,
+ EQI_HAND_L,
+ EQI_HAND_R,
+ EQI_AMMO,
+ EQI_MAX
+} equip_index_enum;
+
+#define pc_setdead(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 1)
+#define pc_setsit(sd) ((sd)->state.dead_sit = (sd)->vd.dead_sit = 2)
+#define pc_isdead(sd) ((sd)->state.dead_sit == 1)
+#define pc_issit(sd) ((sd)->vd.dead_sit == 2)
+#define pc_setdir(sd,b,h) ((sd)->ud.dir = (b) ,(sd)->head_dir = (h) )
+#define pc_setchatid(sd,n) ((sd)->chatID = n)
+#define pc_ishiding(sd) ((sd)->sc.option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK))
+#define pc_iscloaking(sd) (!((sd)->sc.option&OPTION_CHASEWALK) && ((sd)->sc.option&OPTION_CLOAK))
+#define pc_ischasewalk(sd) ((sd)->sc.option&OPTION_CHASEWALK)
+#define pc_iscarton(sd) ((sd)->sc.option&CART_MASK)
+#define pc_isfalcon(sd) ((sd)->sc.option&OPTION_FALCON)
+#define pc_isriding(sd) ((sd)->sc.option&OPTION_RIDING)
+#define pc_isinvisible(sd) ((sd)->sc.option&OPTION_INVISIBLE)
+#define pc_is50overweight(sd) (sd->weight*2 >= sd->max_weight)
+#define pc_is90overweight(sd) (sd->weight*10 >= sd->max_weight*9)
+#define pc_maxparameter(sd) ((sd->class_&JOBL_BABY) ? battle_config.max_baby_parameter : battle_config.max_parameter)
+
+#define pc_stop_attack(sd) { if (sd->ud.attacktimer!=-1) { unit_stop_attack(&sd->bl); sd->ud.target = 0; } }
+#define pc_stop_walking(sd, type) { if (sd->ud.walktimer!=-1) unit_stop_walking(&sd->bl, type); }
+
+//Weapon check considering dual wielding.
+#define pc_check_weapontype(sd, type) ((type)&((sd)->status.weapon < MAX_WEAPON_TYPE? \
+ 1<<(sd)->status.weapon:(1<<(sd)->weapontype1)|(1<<(sd)->weapontype2)))
+//Checks if the given class value corresponds to a player class. [Skotlex]
+#define pcdb_checkid(class_) (class_ <= JOB_XMAS || (class_ >= JOB_NOVICE_HIGH && class_ <= JOB_SOUL_LINKER))
+
+int pc_isGM(struct map_session_data *sd);
+int pc_getrefinebonus(int lv,int type);
+int pc_can_give_items(int level); //[Lupus]
+
+int pc_setrestartvalue(struct map_session_data *sd,int type);
+int pc_makesavestatus(struct map_session_data *);
+int pc_setnewpc(struct map_session_data*,int,int,int,unsigned int,int,int);
+int pc_authok(struct map_session_data*, int, time_t, struct mmo_charstatus *);
+int pc_authfail(struct map_session_data *);
+int pc_reg_received(struct map_session_data *sd);
+
+int pc_isequip(struct map_session_data *sd,int n);
+int pc_equippoint(struct map_session_data *sd,int n);
+
+int pc_checkskill(struct map_session_data *sd,int skill_id);
+int pc_checkallowskill(struct map_session_data *sd);
+int pc_checkequip(struct map_session_data *sd,int pos);
+
+int pc_calc_skilltree(struct map_session_data *sd);
+int pc_calc_skilltree_normalize_job(struct map_session_data *sd);
+int pc_clean_skilltree(struct map_session_data *sd);
+
+#define pc_checkoverhp(sd) (sd->battle_status.hp == sd->battle_status.max_hp)
+#define pc_checkoversp(sd) (sd->battle_status.sp == sd->battle_status.max_sp)
+
+int pc_setpos(struct map_session_data*,unsigned short,int,int,int);
+int pc_setsavepoint(struct map_session_data*,short,int,int);
+int pc_randomwarp(struct map_session_data *sd,int type);
+int pc_memo(struct map_session_data *sd,int i);
+int pc_remove_map(struct map_session_data *sd,int clrtype);
+
+int pc_checkadditem(struct map_session_data*,int,int);
+int pc_inventoryblank(struct map_session_data*);
+int pc_search_inventory(struct map_session_data *sd,int item_id);
+int pc_payzeny(struct map_session_data*,int);
+int pc_additem(struct map_session_data*,struct item*,int);
+int pc_getzeny(struct map_session_data*,int);
+int pc_delitem(struct map_session_data*,int,int,int);
+
+int pc_cart_additem(struct map_session_data *sd,struct item *item_data,int amount);
+int pc_cart_delitem(struct map_session_data *sd,int n,int amount,int type);
+int pc_putitemtocart(struct map_session_data *sd,int idx,int amount);
+int pc_getitemfromcart(struct map_session_data *sd,int idx,int amount);
+int pc_cartitem_amount(struct map_session_data *sd,int idx,int amount);
+
+int pc_takeitem(struct map_session_data*,struct flooritem_data*);
+int pc_dropitem(struct map_session_data*,int,int);
+
+int pc_checkweighticon(struct map_session_data *sd);
+
+int pc_bonus(struct map_session_data*,int,int);
+int pc_bonus2(struct map_session_data *sd,int,int,int);
+int pc_bonus3(struct map_session_data *sd,int,int,int,int);
+int pc_bonus4(struct map_session_data *sd,int,int,int,int,int);
+int pc_skill(struct map_session_data*,int,int,int);
+
+int pc_insert_card(struct map_session_data *sd,int idx_card,int idx_equip);
+
+int pc_steal_item(struct map_session_data *sd,struct block_list *bl, int skilllv);
+int pc_steal_coin(struct map_session_data *sd,struct block_list *bl);
+
+int pc_modifybuyvalue(struct map_session_data*,int);
+int pc_modifysellvalue(struct map_session_data*,int);
+
+int pc_follow(struct map_session_data*, int); // [MouseJstr]
+int pc_stop_following(struct map_session_data*);
+
+unsigned int pc_maxbaselv(struct map_session_data *sd);
+unsigned int pc_maxjoblv(struct map_session_data *sd);
+int pc_checkbaselevelup(struct map_session_data *sd);
+int pc_checkjoblevelup(struct map_session_data *sd);
+int pc_gainexp(struct map_session_data*,struct block_list*,unsigned int,unsigned int);
+unsigned int pc_nextbaseexp(struct map_session_data *);
+unsigned int pc_nextjobexp(struct map_session_data *);
+int pc_need_status_point(struct map_session_data *,int);
+int pc_statusup(struct map_session_data*,int);
+int pc_statusup2(struct map_session_data*,int,int);
+int pc_skillup(struct map_session_data*,int);
+int pc_allskillup(struct map_session_data*);
+int pc_resetlvl(struct map_session_data*,int type);
+int pc_resetstate(struct map_session_data*);
+int pc_resetskill(struct map_session_data*, int);
+int pc_resetfeel(struct map_session_data*);
+int pc_resethate(struct map_session_data*);
+int pc_equipitem(struct map_session_data*,int,int);
+int pc_unequipitem(struct map_session_data*,int,int);
+int pc_checkitem(struct map_session_data*);
+int pc_useitem(struct map_session_data*,int);
+
+void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int hp, unsigned int sp);
+int pc_dead(struct map_session_data *sd,struct block_list *src);
+void pc_revive(struct map_session_data *sd,unsigned int hp, unsigned int sp);
+void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type);
+int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp);
+int pc_percentheal(struct map_session_data *sd,int,int);
+int pc_jobchange(struct map_session_data *,int, int);
+int pc_setoption(struct map_session_data *,int);
+int pc_setcart(struct map_session_data *sd,int type);
+int pc_setfalcon(struct map_session_data *sd);
+int pc_setriding(struct map_session_data *sd);
+int pc_changelook(struct map_session_data *,int,int);
+int pc_equiplookall(struct map_session_data *sd);
+
+int pc_readparam(struct map_session_data*,int);
+int pc_setparam(struct map_session_data*,int,int);
+int pc_readreg(struct map_session_data*,int);
+int pc_setreg(struct map_session_data*,int,int);
+char *pc_readregstr(struct map_session_data *sd,int reg);
+int pc_setregstr(struct map_session_data *sd,int reg,char *str);
+
+#define pc_readglobalreg(sd,reg) pc_readregistry(sd,reg,3)
+#define pc_setglobalreg(sd,reg,val) pc_setregistry(sd,reg,val,3)
+#define pc_readglobalreg_str(sd,reg) pc_readregistry_str(sd,reg,3)
+#define pc_setglobalreg_str(sd,reg,val) pc_setregistry_str(sd,reg,val,3)
+#define pc_readaccountreg(sd,reg) pc_readregistry(sd,reg,2)
+#define pc_setaccountreg(sd,reg,val) pc_setregistry(sd,reg,val,2)
+#define pc_readaccountregstr(sd,reg) pc_readregistry_str(sd,reg,2)
+#define pc_setaccountregstr(sd,reg,val) pc_setregistry_str(sd,reg,val,2)
+#define pc_readaccountreg2(sd,reg) pc_readregistry(sd,reg,1)
+#define pc_setaccountreg2(sd,reg,val) pc_setregistry(sd,reg,val,1)
+#define pc_readaccountreg2str(sd,reg) pc_readregistry_str(sd,reg,1)
+#define pc_setaccountreg2str(sd,reg,val) pc_setregistry_str(sd,reg,val,1)
+int pc_readregistry(struct map_session_data*,const char*,int);
+int pc_setregistry(struct map_session_data*,const char*,int,int);
+char *pc_readregistry_str(struct map_session_data*,char*,int);
+int pc_setregistry_str(struct map_session_data*,char*,char*,int);
+
+int pc_addeventtimer(struct map_session_data *sd,int tick,const char *name);
+int pc_deleventtimer(struct map_session_data *sd,const char *name);
+int pc_cleareventtimer(struct map_session_data *sd);
+int pc_addeventtimercount(struct map_session_data *sd,const char *name,int tick);
+
+int pc_calc_pvprank(struct map_session_data *sd);
+int pc_calc_pvprank_timer(int tid,unsigned int tick,int id,int data);
+
+int pc_ismarried(struct map_session_data *sd);
+int pc_marriage(struct map_session_data *sd,struct map_session_data *dstsd);
+int pc_divorce(struct map_session_data *sd);
+int pc_adoption(struct map_session_data *sd,struct map_session_data *dstsd,struct map_session_data *jasd);
+struct map_session_data *pc_get_partner(struct map_session_data *sd);
+struct map_session_data *pc_get_father(struct map_session_data *sd);
+struct map_session_data *pc_get_mother(struct map_session_data *sd);
+struct map_session_data *pc_get_child(struct map_session_data *sd);
+
+void pc_bleeding (struct map_session_data *sd, unsigned int diff_tick);
+
+int pc_set_gm_level(int account_id, int level);
+void pc_setstand(struct map_session_data *sd);
+int pc_candrop(struct map_session_data *sd,struct item *item);
+
+int pc_jobid2mapid(unsigned short b_class); // Skotlex
+int pc_mapid2jobid(unsigned short class_, int sex); // Skotlex
+
+char * job_name(int class_);
+
+struct skill_tree_entry {
+ short id;
+ unsigned char max;
+ unsigned char joblv;
+ struct {
+ short id;
+ unsigned char lv;
+ } need[5];
+}; // Celest
+extern struct skill_tree_entry skill_tree[MAX_PC_CLASS][MAX_SKILL_TREE];
+
+int pc_read_gm_account(int fd);
+int pc_setinvincibletimer(struct map_session_data *sd,int);
+int pc_delinvincibletimer(struct map_session_data *sd);
+int pc_addspiritball(struct map_session_data *sd,int,int);
+int pc_delspiritball(struct map_session_data *sd,int,int);
+void pc_addfame(struct map_session_data *sd,int count);
+unsigned char pc_famerank(int char_id, int job);
+int pc_set_hate_mob(struct map_session_data *sd, int pos, struct block_list *bl);
+
+extern struct fame_list smith_fame_list[MAX_FAME_LIST];
+extern struct fame_list chemist_fame_list[MAX_FAME_LIST];
+extern struct fame_list taekwon_fame_list[MAX_FAME_LIST];
+
+int pc_readdb(void);
+int do_init_pc(void);
+void do_final_pc(void);
+
+enum {ADDITEM_EXIST,ADDITEM_NEW,ADDITEM_OVERAMOUNT};
+
+// timer for night.day
+extern int day_timer_tid;
+extern int night_timer_tid;
+int map_day_timer(int,unsigned int,int,int); // by [yor]
+int map_night_timer(int,unsigned int,int,int); // by [yor]
+
+//Duel functions // [LuzZza]
+int duel_create(struct map_session_data* sd, const unsigned int maxpl);
+int duel_invite(const unsigned int did, struct map_session_data* sd, struct map_session_data* target_sd);
+int duel_accept(const unsigned int did, struct map_session_data* sd);
+int duel_reject(const unsigned int did, struct map_session_data* sd);
+int duel_leave(const unsigned int did, struct map_session_data* sd);
+int duel_showinfo(const unsigned int did, struct map_session_data* sd);
+int duel_checktime(struct map_session_data* sd);
+
+int pc_read_motd(void); // [Valaris]
+int pc_disguise(struct map_session_data *sd, int class_);
+#endif
diff --git a/src/map/pcre.h b/src/map/pcre.h
index 244e55e0d..b2596a83d 100644
--- a/src/map/pcre.h
+++ b/src/map/pcre.h
@@ -1,258 +1,258 @@
-/*************************************************
-* Perl-Compatible Regular Expressions *
-*************************************************/
-
-/* In its original form, this is the .in file that is transformed by
-"configure" into pcre.h.
-
- Copyright (c) 1997-2005 University of Cambridge
-
------------------------------------------------------------------------------
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the University of Cambridge nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------------
-*/
-
-#ifndef _PCRE_H
-#define _PCRE_H
-
-/* The file pcre.h is build by "configure". Do not edit it; instead
-make changes to pcre.in. */
-
-#define PCRE_MAJOR 6
-#define PCRE_MINOR 3
-#define PCRE_DATE 15-Aug-2005
-
-/* Win32 uses DLL by default; it needs special stuff for exported functions. */
-
-#ifdef _WIN32
-# ifdef PCRE_DEFINITION
-# ifdef DLL_EXPORT
-# define PCRE_DATA_SCOPE __declspec(dllexport)
-# endif
-# else
-# ifndef PCRE_STATIC
-# define PCRE_DATA_SCOPE extern __declspec(dllimport)
-# endif
-# endif
-#endif
-
-/* For other operating systems, we use the standard "extern". */
-
-#ifndef PCRE_DATA_SCOPE
-# ifdef __cplusplus
-# define PCRE_DATA_SCOPE extern "C"
-# else
-# define PCRE_DATA_SCOPE extern
-# endif
-#endif
-
-/* Have to include stdlib.h in order to ensure that size_t is defined;
-it is needed here for malloc. */
-
-#include <stdlib.h>
-
-/* Allow for C++ users */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Options */
-
-#define PCRE_CASELESS 0x00000001
-#define PCRE_MULTILINE 0x00000002
-#define PCRE_DOTALL 0x00000004
-#define PCRE_EXTENDED 0x00000008
-#define PCRE_ANCHORED 0x00000010
-#define PCRE_DOLLAR_ENDONLY 0x00000020
-#define PCRE_EXTRA 0x00000040
-#define PCRE_NOTBOL 0x00000080
-#define PCRE_NOTEOL 0x00000100
-#define PCRE_UNGREEDY 0x00000200
-#define PCRE_NOTEMPTY 0x00000400
-#define PCRE_UTF8 0x00000800
-#define PCRE_NO_AUTO_CAPTURE 0x00001000
-#define PCRE_NO_UTF8_CHECK 0x00002000
-#define PCRE_AUTO_CALLOUT 0x00004000
-#define PCRE_PARTIAL 0x00008000
-#define PCRE_DFA_SHORTEST 0x00010000
-#define PCRE_DFA_RESTART 0x00020000
-#define PCRE_FIRSTLINE 0x00040000
-
-/* Exec-time and get/set-time error codes */
-
-#define PCRE_ERROR_NOMATCH (-1)
-#define PCRE_ERROR_NULL (-2)
-#define PCRE_ERROR_BADOPTION (-3)
-#define PCRE_ERROR_BADMAGIC (-4)
-#define PCRE_ERROR_UNKNOWN_NODE (-5)
-#define PCRE_ERROR_NOMEMORY (-6)
-#define PCRE_ERROR_NOSUBSTRING (-7)
-#define PCRE_ERROR_MATCHLIMIT (-8)
-#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
-#define PCRE_ERROR_BADUTF8 (-10)
-#define PCRE_ERROR_BADUTF8_OFFSET (-11)
-#define PCRE_ERROR_PARTIAL (-12)
-#define PCRE_ERROR_BADPARTIAL (-13)
-#define PCRE_ERROR_INTERNAL (-14)
-#define PCRE_ERROR_BADCOUNT (-15)
-#define PCRE_ERROR_DFA_UITEM (-16)
-#define PCRE_ERROR_DFA_UCOND (-17)
-#define PCRE_ERROR_DFA_UMLIMIT (-18)
-#define PCRE_ERROR_DFA_WSSIZE (-19)
-#define PCRE_ERROR_DFA_RECURSE (-20)
-
-/* Request types for pcre_fullinfo() */
-
-#define PCRE_INFO_OPTIONS 0
-#define PCRE_INFO_SIZE 1
-#define PCRE_INFO_CAPTURECOUNT 2
-#define PCRE_INFO_BACKREFMAX 3
-#define PCRE_INFO_FIRSTBYTE 4
-#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
-#define PCRE_INFO_FIRSTTABLE 5
-#define PCRE_INFO_LASTLITERAL 6
-#define PCRE_INFO_NAMEENTRYSIZE 7
-#define PCRE_INFO_NAMECOUNT 8
-#define PCRE_INFO_NAMETABLE 9
-#define PCRE_INFO_STUDYSIZE 10
-#define PCRE_INFO_DEFAULT_TABLES 11
-
-/* Request types for pcre_config() */
-
-#define PCRE_CONFIG_UTF8 0
-#define PCRE_CONFIG_NEWLINE 1
-#define PCRE_CONFIG_LINK_SIZE 2
-#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
-#define PCRE_CONFIG_MATCH_LIMIT 4
-#define PCRE_CONFIG_STACKRECURSE 5
-#define PCRE_CONFIG_UNICODE_PROPERTIES 6
-
-/* Bit flags for the pcre_extra structure */
-
-#define PCRE_EXTRA_STUDY_DATA 0x0001
-#define PCRE_EXTRA_MATCH_LIMIT 0x0002
-#define PCRE_EXTRA_CALLOUT_DATA 0x0004
-#define PCRE_EXTRA_TABLES 0x0008
-
-/* Types */
-
-struct real_pcre; /* declaration; the definition is private */
-typedef struct real_pcre pcre;
-
-/* The structure for passing additional data to pcre_exec(). This is defined in
-such as way as to be extensible. Always add new fields at the end, in order to
-remain compatible. */
-
-typedef struct pcre_extra {
- unsigned long int flags; /* Bits for which fields are set */
- void *study_data; /* Opaque data from pcre_study() */
- unsigned long int match_limit; /* Maximum number of calls to match() */
- void *callout_data; /* Data passed back in callouts */
- const unsigned char *tables; /* Pointer to character tables */
-} pcre_extra;
-
-/* The structure for passing out data via the pcre_callout_function. We use a
-structure so that new fields can be added on the end in future versions,
-without changing the API of the function, thereby allowing old clients to work
-without modification. */
-
-typedef struct pcre_callout_block {
- int version; /* Identifies version of block */
- /* ------------------------ Version 0 ------------------------------- */
- int callout_number; /* Number compiled into pattern */
- int *offset_vector; /* The offset vector */
- const char *subject; /* The subject being matched */
- int subject_length; /* The length of the subject */
- int start_match; /* Offset to start of this match attempt */
- int current_position; /* Where we currently are in the subject */
- int capture_top; /* Max current capture */
- int capture_last; /* Most recently closed capture */
- void *callout_data; /* Data passed in with the call */
- /* ------------------- Added for Version 1 -------------------------- */
- int pattern_position; /* Offset to next item in the pattern */
- int next_item_length; /* Length of next item in the pattern */
- /* ------------------------------------------------------------------ */
-} pcre_callout_block;
-
-/* Indirection for store get and free functions. These can be set to
-alternative malloc/free functions if required. Special ones are used in the
-non-recursive case for "frames". There is also an optional callout function
-that is triggered by the (?) regex item. For Virtual Pascal, these definitions
-have to take another form. */
-
-#ifndef VPCOMPAT
-PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
-PCRE_DATA_SCOPE void (*pcre_free)(void *);
-PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
-PCRE_DATA_SCOPE void (*pcre_stack_free)(void *);
-PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *);
-#else /* VPCOMPAT */
-PCRE_DATA_SCOPE void *pcre_malloc(size_t);
-PCRE_DATA_SCOPE void pcre_free(void *);
-PCRE_DATA_SCOPE void *pcre_stack_malloc(size_t);
-PCRE_DATA_SCOPE void pcre_stack_free(void *);
-PCRE_DATA_SCOPE int pcre_callout(pcre_callout_block *);
-#endif /* VPCOMPAT */
-
-/* Exported PCRE functions */
-
-PCRE_DATA_SCOPE pcre *pcre_compile(const char *, int, const char **, int *,
- const unsigned char *);
-PCRE_DATA_SCOPE pcre *pcre_compile2(const char *, int, int *, const char **,
- int *, const unsigned char *);
-PCRE_DATA_SCOPE int pcre_config(int, void *);
-PCRE_DATA_SCOPE int pcre_copy_named_substring(const pcre *, const char *,
- int *, int, const char *, char *, int);
-PCRE_DATA_SCOPE int pcre_copy_substring(const char *, int *, int, int, char *,
- int);
-PCRE_DATA_SCOPE int pcre_dfa_exec(const pcre *, const pcre_extra *,
- const char *, int, int, int, int *, int , int *, int);
-PCRE_DATA_SCOPE int pcre_exec(const pcre *, const pcre_extra *, const char *,
- int, int, int, int *, int);
-PCRE_DATA_SCOPE void pcre_free_substring(const char *);
-PCRE_DATA_SCOPE void pcre_free_substring_list(const char **);
-PCRE_DATA_SCOPE int pcre_fullinfo(const pcre *, const pcre_extra *, int,
- void *);
-PCRE_DATA_SCOPE int pcre_get_named_substring(const pcre *, const char *,
- int *, int, const char *, const char **);
-PCRE_DATA_SCOPE int pcre_get_stringnumber(const pcre *, const char *);
-PCRE_DATA_SCOPE int pcre_get_substring(const char *, int *, int, int,
- const char **);
-PCRE_DATA_SCOPE int pcre_get_substring_list(const char *, int *, int,
- const char ***);
-PCRE_DATA_SCOPE int pcre_info(const pcre *, int *, int *);
-PCRE_DATA_SCOPE const unsigned char *pcre_maketables(void);
-PCRE_DATA_SCOPE int pcre_refcount(pcre *, int);
-PCRE_DATA_SCOPE pcre_extra *pcre_study(const pcre *, int, const char **);
-PCRE_DATA_SCOPE const char *pcre_version(void);
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
-
-#endif /* End of pcre.h */
+/*************************************************
+* Perl-Compatible Regular Expressions *
+*************************************************/
+
+/* In its original form, this is the .in file that is transformed by
+"configure" into pcre.h.
+
+ Copyright (c) 1997-2005 University of Cambridge
+
+-----------------------------------------------------------------------------
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+-----------------------------------------------------------------------------
+*/
+
+#ifndef _PCRE_H
+#define _PCRE_H
+
+/* The file pcre.h is build by "configure". Do not edit it; instead
+make changes to pcre.in. */
+
+#define PCRE_MAJOR 6
+#define PCRE_MINOR 3
+#define PCRE_DATE 15-Aug-2005
+
+/* Win32 uses DLL by default; it needs special stuff for exported functions. */
+
+#ifdef _WIN32
+# ifdef PCRE_DEFINITION
+# ifdef DLL_EXPORT
+# define PCRE_DATA_SCOPE __declspec(dllexport)
+# endif
+# else
+# ifndef PCRE_STATIC
+# define PCRE_DATA_SCOPE extern __declspec(dllimport)
+# endif
+# endif
+#endif
+
+/* For other operating systems, we use the standard "extern". */
+
+#ifndef PCRE_DATA_SCOPE
+# ifdef __cplusplus
+# define PCRE_DATA_SCOPE extern "C"
+# else
+# define PCRE_DATA_SCOPE extern
+# endif
+#endif
+
+/* Have to include stdlib.h in order to ensure that size_t is defined;
+it is needed here for malloc. */
+
+#include <stdlib.h>
+
+/* Allow for C++ users */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Options */
+
+#define PCRE_CASELESS 0x00000001
+#define PCRE_MULTILINE 0x00000002
+#define PCRE_DOTALL 0x00000004
+#define PCRE_EXTENDED 0x00000008
+#define PCRE_ANCHORED 0x00000010
+#define PCRE_DOLLAR_ENDONLY 0x00000020
+#define PCRE_EXTRA 0x00000040
+#define PCRE_NOTBOL 0x00000080
+#define PCRE_NOTEOL 0x00000100
+#define PCRE_UNGREEDY 0x00000200
+#define PCRE_NOTEMPTY 0x00000400
+#define PCRE_UTF8 0x00000800
+#define PCRE_NO_AUTO_CAPTURE 0x00001000
+#define PCRE_NO_UTF8_CHECK 0x00002000
+#define PCRE_AUTO_CALLOUT 0x00004000
+#define PCRE_PARTIAL 0x00008000
+#define PCRE_DFA_SHORTEST 0x00010000
+#define PCRE_DFA_RESTART 0x00020000
+#define PCRE_FIRSTLINE 0x00040000
+
+/* Exec-time and get/set-time error codes */
+
+#define PCRE_ERROR_NOMATCH (-1)
+#define PCRE_ERROR_NULL (-2)
+#define PCRE_ERROR_BADOPTION (-3)
+#define PCRE_ERROR_BADMAGIC (-4)
+#define PCRE_ERROR_UNKNOWN_NODE (-5)
+#define PCRE_ERROR_NOMEMORY (-6)
+#define PCRE_ERROR_NOSUBSTRING (-7)
+#define PCRE_ERROR_MATCHLIMIT (-8)
+#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */
+#define PCRE_ERROR_BADUTF8 (-10)
+#define PCRE_ERROR_BADUTF8_OFFSET (-11)
+#define PCRE_ERROR_PARTIAL (-12)
+#define PCRE_ERROR_BADPARTIAL (-13)
+#define PCRE_ERROR_INTERNAL (-14)
+#define PCRE_ERROR_BADCOUNT (-15)
+#define PCRE_ERROR_DFA_UITEM (-16)
+#define PCRE_ERROR_DFA_UCOND (-17)
+#define PCRE_ERROR_DFA_UMLIMIT (-18)
+#define PCRE_ERROR_DFA_WSSIZE (-19)
+#define PCRE_ERROR_DFA_RECURSE (-20)
+
+/* Request types for pcre_fullinfo() */
+
+#define PCRE_INFO_OPTIONS 0
+#define PCRE_INFO_SIZE 1
+#define PCRE_INFO_CAPTURECOUNT 2
+#define PCRE_INFO_BACKREFMAX 3
+#define PCRE_INFO_FIRSTBYTE 4
+#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */
+#define PCRE_INFO_FIRSTTABLE 5
+#define PCRE_INFO_LASTLITERAL 6
+#define PCRE_INFO_NAMEENTRYSIZE 7
+#define PCRE_INFO_NAMECOUNT 8
+#define PCRE_INFO_NAMETABLE 9
+#define PCRE_INFO_STUDYSIZE 10
+#define PCRE_INFO_DEFAULT_TABLES 11
+
+/* Request types for pcre_config() */
+
+#define PCRE_CONFIG_UTF8 0
+#define PCRE_CONFIG_NEWLINE 1
+#define PCRE_CONFIG_LINK_SIZE 2
+#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3
+#define PCRE_CONFIG_MATCH_LIMIT 4
+#define PCRE_CONFIG_STACKRECURSE 5
+#define PCRE_CONFIG_UNICODE_PROPERTIES 6
+
+/* Bit flags for the pcre_extra structure */
+
+#define PCRE_EXTRA_STUDY_DATA 0x0001
+#define PCRE_EXTRA_MATCH_LIMIT 0x0002
+#define PCRE_EXTRA_CALLOUT_DATA 0x0004
+#define PCRE_EXTRA_TABLES 0x0008
+
+/* Types */
+
+struct real_pcre; /* declaration; the definition is private */
+typedef struct real_pcre pcre;
+
+/* The structure for passing additional data to pcre_exec(). This is defined in
+such as way as to be extensible. Always add new fields at the end, in order to
+remain compatible. */
+
+typedef struct pcre_extra {
+ unsigned long int flags; /* Bits for which fields are set */
+ void *study_data; /* Opaque data from pcre_study() */
+ unsigned long int match_limit; /* Maximum number of calls to match() */
+ void *callout_data; /* Data passed back in callouts */
+ const unsigned char *tables; /* Pointer to character tables */
+} pcre_extra;
+
+/* The structure for passing out data via the pcre_callout_function. We use a
+structure so that new fields can be added on the end in future versions,
+without changing the API of the function, thereby allowing old clients to work
+without modification. */
+
+typedef struct pcre_callout_block {
+ int version; /* Identifies version of block */
+ /* ------------------------ Version 0 ------------------------------- */
+ int callout_number; /* Number compiled into pattern */
+ int *offset_vector; /* The offset vector */
+ const char *subject; /* The subject being matched */
+ int subject_length; /* The length of the subject */
+ int start_match; /* Offset to start of this match attempt */
+ int current_position; /* Where we currently are in the subject */
+ int capture_top; /* Max current capture */
+ int capture_last; /* Most recently closed capture */
+ void *callout_data; /* Data passed in with the call */
+ /* ------------------- Added for Version 1 -------------------------- */
+ int pattern_position; /* Offset to next item in the pattern */
+ int next_item_length; /* Length of next item in the pattern */
+ /* ------------------------------------------------------------------ */
+} pcre_callout_block;
+
+/* Indirection for store get and free functions. These can be set to
+alternative malloc/free functions if required. Special ones are used in the
+non-recursive case for "frames". There is also an optional callout function
+that is triggered by the (?) regex item. For Virtual Pascal, these definitions
+have to take another form. */
+
+#ifndef VPCOMPAT
+PCRE_DATA_SCOPE void *(*pcre_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_free)(void *);
+PCRE_DATA_SCOPE void *(*pcre_stack_malloc)(size_t);
+PCRE_DATA_SCOPE void (*pcre_stack_free)(void *);
+PCRE_DATA_SCOPE int (*pcre_callout)(pcre_callout_block *);
+#else /* VPCOMPAT */
+PCRE_DATA_SCOPE void *pcre_malloc(size_t);
+PCRE_DATA_SCOPE void pcre_free(void *);
+PCRE_DATA_SCOPE void *pcre_stack_malloc(size_t);
+PCRE_DATA_SCOPE void pcre_stack_free(void *);
+PCRE_DATA_SCOPE int pcre_callout(pcre_callout_block *);
+#endif /* VPCOMPAT */
+
+/* Exported PCRE functions */
+
+PCRE_DATA_SCOPE pcre *pcre_compile(const char *, int, const char **, int *,
+ const unsigned char *);
+PCRE_DATA_SCOPE pcre *pcre_compile2(const char *, int, int *, const char **,
+ int *, const unsigned char *);
+PCRE_DATA_SCOPE int pcre_config(int, void *);
+PCRE_DATA_SCOPE int pcre_copy_named_substring(const pcre *, const char *,
+ int *, int, const char *, char *, int);
+PCRE_DATA_SCOPE int pcre_copy_substring(const char *, int *, int, int, char *,
+ int);
+PCRE_DATA_SCOPE int pcre_dfa_exec(const pcre *, const pcre_extra *,
+ const char *, int, int, int, int *, int , int *, int);
+PCRE_DATA_SCOPE int pcre_exec(const pcre *, const pcre_extra *, const char *,
+ int, int, int, int *, int);
+PCRE_DATA_SCOPE void pcre_free_substring(const char *);
+PCRE_DATA_SCOPE void pcre_free_substring_list(const char **);
+PCRE_DATA_SCOPE int pcre_fullinfo(const pcre *, const pcre_extra *, int,
+ void *);
+PCRE_DATA_SCOPE int pcre_get_named_substring(const pcre *, const char *,
+ int *, int, const char *, const char **);
+PCRE_DATA_SCOPE int pcre_get_stringnumber(const pcre *, const char *);
+PCRE_DATA_SCOPE int pcre_get_substring(const char *, int *, int, int,
+ const char **);
+PCRE_DATA_SCOPE int pcre_get_substring_list(const char *, int *, int,
+ const char ***);
+PCRE_DATA_SCOPE int pcre_info(const pcre *, int *, int *);
+PCRE_DATA_SCOPE const unsigned char *pcre_maketables(void);
+PCRE_DATA_SCOPE int pcre_refcount(pcre *, int);
+PCRE_DATA_SCOPE pcre_extra *pcre_study(const pcre *, int, const char **);
+PCRE_DATA_SCOPE const char *pcre_version(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* End of pcre.h */
diff --git a/src/map/pet.c b/src/map/pet.c
index 836c9fa97..4d153be8d 100644
--- a/src/map/pet.c
+++ b/src/map/pet.c
@@ -1,1400 +1,1400 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../common/db.h"
-#include "../common/timer.h"
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-#include "../common/ers.h"
-
-#include "pc.h"
-#include "status.h"
-#include "map.h"
-#include "intif.h"
-#include "clif.h"
-#include "chrif.h"
-#include "pet.h"
-#include "itemdb.h"
-#include "battle.h"
-#include "mob.h"
-#include "npc.h"
-#include "script.h"
-#include "skill.h"
-#include "unit.h"
-
-#define MIN_PETTHINKTIME 100
-
-struct pet_db pet_db[MAX_PET_DB];
-
-static struct eri *item_drop_ers; //For loot drops delay structures.
-static struct eri *item_drop_list_ers;
-
-int pet_hungry_val(struct pet_data *pd)
-{
- nullpo_retr(0, pd);
-
- if(pd->pet.hungry > 90)
- return 4;
- else if(pd->pet.hungry > 75)
- return 3;
- else if(pd->pet.hungry > 25)
- return 2;
- else if(pd->pet.hungry > 10)
- return 1;
- else
- return 0;
-}
-
-static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir)
-{
- int x,y,dx,dy;
- int i,k;
-
- nullpo_retr(0, pd);
-
- pd->ud.to_x = tx;
- pd->ud.to_y = ty;
-
- if(dir < 0 || dir >= 8)
- return 1;
-
- dx = -dirx[dir]*2;
- dy = -diry[dir]*2;
- x = tx + dx;
- y = ty + dy;
- if(!unit_can_reach_pos(&pd->bl,x,y,0)) {
- if(dx > 0) x--;
- else if(dx < 0) x++;
- if(dy > 0) y--;
- else if(dy < 0) y++;
- if(!unit_can_reach_pos(&pd->bl,x,y,0)) {
- for(i=0;i<12;i++) {
- k = rand()%8;
- dx = -dirx[k]*2;
- dy = -diry[k]*2;
- x = tx + dx;
- y = ty + dy;
- if(unit_can_reach_pos(&pd->bl,x,y,0))
- break;
- else {
- if(dx > 0) x--;
- else if(dx < 0) x++;
- if(dy > 0) y--;
- else if(dy < 0) y++;
- if(unit_can_reach_pos(&pd->bl,x,y,0))
- break;
- }
- }
- if(i>=12) {
- x = tx;
- y = ty;
- if(!unit_can_reach_pos(&pd->bl,x,y,0))
- return 1;
- }
- }
- }
- pd->ud.to_x = x;
- pd->ud.to_y = y;
- return 0;
-}
-
-int pet_create_egg(struct map_session_data *sd, int item_id)
-{
- int pet_id = search_petDB_index(item_id, PET_EGG);
- if (pet_id < 0) return 0; //No pet egg here.
- sd->catch_target_class = pet_db[pet_id].class_;
- intif_create_pet(sd->status.account_id, sd->status.char_id,
- (short)pet_db[pet_id].class_,
- (short)mob_db(pet_db[pet_id].class_)->lv,
- (short)pet_db[pet_id].EggID, 0,
- (short)pet_db[pet_id].intimate,
- 100, 0, 1, pet_db[pet_id].jname);
- return 1;
-}
-
-int pet_unlocktarget(struct pet_data *pd)
-{
- nullpo_retr(0, pd);
-
- pd->target_id=0;
- pet_stop_attack(pd);
- pet_stop_walking(pd,1);
- return 0;
-}
-
-/*==========================================
- * Pet Attack Skill [Skotlex]
- *------------------------------------------
- */
-int pet_attackskill(struct pet_data *pd, int target_id)
-{
- struct block_list *bl;
- int inf;
-
- if (!battle_config.pet_status_support || !pd->a_skill ||
- (battle_config.pet_equip_required && !pd->pet.equip))
- return 0;
-
- if (DIFF_TICK(pd->ud.canact_tick, gettick()) > 0)
- return 0;
-
- if (rand()%100 < (pd->a_skill->rate +pd->pet.intimate*pd->a_skill->bonusrate/1000))
- { //Skotlex: Use pet's skill
- bl=map_id2bl(target_id);
- if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL || status_isdead(bl) ||
- !check_distance_bl(&pd->bl, bl, pd->db->range3))
- return 0;
-
- inf = skill_get_inf(pd->a_skill->id);
- if (inf & INF_GROUND_SKILL)
- unit_skilluse_pos(&pd->bl, bl->x, bl->y, pd->a_skill->id, pd->a_skill->lv);
- else //Offensive self skill? Could be stuff like GX.
- unit_skilluse_id(&pd->bl,(inf&INF_SELF_SKILL?pd->bl.id:bl->id), pd->a_skill->id, pd->a_skill->lv);
- return 1; //Skill invoked.
- }
- return 0;
-}
-
-int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type)
-{
- struct pet_data *pd;
- int rate;
-
- pd = sd->pd;
-
- Assert((pd->msd == 0) || (pd->msd->pd == pd));
-
- if(bl == NULL || bl->type != BL_MOB || bl->prev == NULL ||
- pd->pet.intimate < battle_config.pet_support_min_friendly ||
- pd->pet.hungry < 1 ||
- pd->pet.class_ == status_get_class(bl))
- return 0;
-
- if(pd->bl.m != bl->m ||
- !check_distance_bl(&pd->bl, bl, pd->db->range2))
- return 0;
-
- if (!status_check_skilluse(&pd->bl, bl, 0, 0))
- return 0;
-
- if(!type) {
- rate = pd->petDB->attack_rate;
- rate = rate * pd->rate_fix/1000;
- if(pd->petDB->attack_rate > 0 && rate <= 0)
- rate = 1;
- } else {
- rate = pd->petDB->defence_attack_rate;
- rate = rate * pd->rate_fix/1000;
- if(pd->petDB->defence_attack_rate > 0 && rate <= 0)
- rate = 1;
- }
- if(rand()%10000 < rate)
- {
- if(pd->target_id == 0 || rand()%10000 < pd->petDB->change_target_rate)
- pd->target_id = bl->id;
- }
-
- return 0;
-}
-/*==========================================
- * Pet SC Check [Skotlex]
- *------------------------------------------
- */
-int pet_sc_check(struct map_session_data *sd, int type)
-{
- struct pet_data *pd;
-
- nullpo_retr(0, sd);
- pd = sd->pd;
-
- if (pd == NULL ||
- (battle_config.pet_equip_required && pd->pet.equip == 0) ||
- pd->recovery == NULL ||
- pd->recovery->timer != -1 ||
- pd->recovery->type != type)
- return 1;
-
- pd->recovery->timer = add_timer(gettick()+pd->recovery->delay*1000,pet_recovery_timer,sd->bl.id,0);
-
- return 0;
-}
-
-static int pet_hungry(int tid,unsigned int tick,int id,int data)
-{
- struct map_session_data *sd;
- struct pet_data *pd;
- int interval,t;
-
- sd=map_id2sd(id);
- if(!sd)
- return 1;
-
- if(!sd->status.pet_id || !sd->pd)
- return 1;
-
- pd = sd->pd;
- if(pd->pet_hungry_timer != tid){
- if(battle_config.error_log)
- ShowError("pet_hungry_timer %d != %d\n",pd->pet_hungry_timer,tid);
- return 0;
- }
- pd->pet_hungry_timer = -1;
-
- if (pd->pet.intimate <= 0)
- return 1; //You lost the pet already, the rest is irrelevant.
-
- pd->pet.hungry--;
- t = pd->pet.intimate;
- if(pd->pet.hungry < 0) {
- pet_stop_attack(pd);
- pd->pet.hungry = 0;
- pd->pet.intimate -= battle_config.pet_hungry_friendly_decrease;
- if(pd->pet.intimate <= 0) {
- pd->pet.intimate = 0;
- pd->status.speed = pd->db->status.speed;
- }
- status_calc_pet(pd, 0);
- clif_send_petdata(sd,1,pd->pet.intimate);
- }
- clif_send_petdata(sd,2,pd->pet.hungry);
-
- if(battle_config.pet_hungry_delay_rate != 100)
- interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
- else
- interval = pd->petDB->hungry_delay;
- if(interval <= 0)
- interval = 1;
- pd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0);
-
- return 0;
-}
-
-int search_petDB_index(int key,int type)
-{
- int i;
-
- for(i=0;i<MAX_PET_DB;i++) {
- if(pet_db[i].class_ <= 0)
- continue;
- switch(type) {
- case PET_CLASS:
- if(pet_db[i].class_ == key)
- return i;
- break;
- case PET_CATCH:
- if(pet_db[i].itemID == key)
- return i;
- break;
- case PET_EGG:
- if(pet_db[i].EggID == key)
- return i;
- break;
- case PET_EQUIP:
- if(pet_db[i].AcceID == key)
- return i;
- break;
- case PET_FOOD:
- if(pet_db[i].FoodID == key)
- return i;
- break;
- default:
- return -1;
- }
- }
- return -1;
-}
-
-int pet_hungry_timer_delete(struct pet_data *pd)
-{
- nullpo_retr(0, pd);
- if(pd->pet_hungry_timer != -1) {
- delete_timer(pd->pet_hungry_timer,pet_hungry);
- pd->pet_hungry_timer = -1;
- }
-
- return 1;
-}
-
-static int pet_performance(struct map_session_data *sd, struct pet_data *pd)
-{
- int val;
-
- if (pd->pet.intimate > 900)
- val = (pd->petDB->s_perfor > 0)? 4:3;
- else if(pd->pet.intimate > 750)
- val = 2;
- else
- val = 1;
-
- pet_stop_walking(pd,2000<<8);
- clif_pet_performance(&pd->bl,rand()%val + 1);
- pet_lootitem_drop(pd,NULL);
- return 1;
-}
-
-static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd)
-{
- struct item tmp_item;
- int flag;
-
- pet_lootitem_drop(pd,sd);
- malloc_set(&tmp_item,0,sizeof(tmp_item));
- tmp_item.nameid = pd->petDB->EggID;
- tmp_item.identify = 1;
- tmp_item.card[0] = CARD0_PET;
- tmp_item.card[1] = GetWord(pd->pet.pet_id,0);
- tmp_item.card[2] = GetWord(pd->pet.pet_id,1);
- tmp_item.card[3] = pd->pet.rename_flag;
- if((flag = pc_additem(sd,&tmp_item,1))) {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
- }
- pd->pet.incuvate = 1;
- //No need, pet is saved on unit_free below.
- //intif_save_petdata(sd->status.account_id,&pd->pet);
- if(pd->state.skillbonus) {
- pd->state.skillbonus = 0;
- status_calc_pc(sd,0);
- }
- unit_free(&pd->bl,0);
- sd->status.pet_id = 0;
-
- return 1;
-}
-
-int pet_data_init(struct map_session_data *sd, struct s_pet *pet)
-{
- struct pet_data *pd;
- int i=0,interval=0;
-
- nullpo_retr(1, sd);
-
- Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
-
- if(sd->status.account_id != pet->account_id || sd->status.char_id != pet->char_id) {
- sd->status.pet_id = 0;
- return 1;
- }
- if (sd->status.pet_id != pet->pet_id) {
- if (sd->status.pet_id) {
- //Wrong pet?? Set incuvate to no and send it back for saving.
- pet->incuvate = 1;
- intif_save_petdata(sd->status.account_id,pet);
- sd->status.pet_id = 0;
- return 1;
- }
- //The pet_id value was lost? odd... restore it.
- sd->status.pet_id = pet->pet_id;
- }
-
- i = search_petDB_index(pet->class_,PET_CLASS);
- if(i < 0) {
- sd->status.pet_id = 0;
- return 1;
- }
- sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data));
- pd->petDB = &pet_db[i];
- memcpy(&pd->pet, pet, sizeof(struct s_pet));
- pd->bl.m = sd->bl.m;
- pd->bl.x = sd->bl.x;
- pd->bl.y = sd->bl.y;
- pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir);
- pd->bl.x = pd->ud.to_x;
- pd->bl.y = pd->ud.to_y;
- pd->bl.id = npc_get_new_npc_id();
- pd->db = mob_db(pet->class_);
- pd->bl.subtype = MONS;
- pd->bl.type = BL_PET;
- pd->msd = sd;
- status_set_viewdata(&pd->bl, pet->class_);
- unit_dataset(&pd->bl);
- pd->ud.dir = sd->ud.dir;
- pd->last_thinktime = gettick();
-
- map_addiddb(&pd->bl);
-
- // initialise
- status_calc_pet(pd,1);
-
- pd->state.skillbonus = 0;
- if (battle_config.pet_status_support) //Skotlex
- run_script(pet_db[i].script,0,sd->bl.id,0);
-
- if(battle_config.pet_hungry_delay_rate != 100)
- interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
- else
- interval = pd->petDB->hungry_delay;
- if(interval <= 0)
- interval = 1;
- pd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0);
- return 0;
-}
-
-int pet_birth_process(struct map_session_data *sd, struct s_pet *pet)
-{
- nullpo_retr(1, sd);
-
- Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
-
- if(sd->status.pet_id && pet->incuvate == 1) {
- sd->status.pet_id = 0;
- return 1;
- }
-
- pet->incuvate = 0;
- pet->account_id = sd->status.account_id;
- pet->char_id = sd->status.char_id;
- sd->status.pet_id = pet->pet_id;
- if(pet_data_init(sd, pet)) {
- sd->status.pet_id = 0;
- return 1;
- }
-
- intif_save_petdata(sd->status.account_id,pet);
- if (save_settings&8)
- chrif_save(sd,0); //is it REALLY Needed to save the char for hatching a pet? [Skotlex]
-
- if(sd->bl.prev != NULL) {
- map_addblock(&sd->pd->bl);
- clif_spawn(&sd->pd->bl);
- clif_send_petdata(sd,0,0);
- clif_send_petdata(sd,5,battle_config.pet_hair_style);
- clif_pet_equip(sd->pd);
- clif_send_petstatus(sd);
- }
- Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
-
- return 0;
-}
-
-int pet_recv_petdata(int account_id,struct s_pet *p,int flag)
-{
- struct map_session_data *sd;
-
- sd = map_id2sd(account_id);
- if(sd == NULL)
- return 1;
- if(flag == 1) {
- sd->status.pet_id = 0;
- return 1;
- }
- if(p->incuvate == 1) {
- int i;
- //Delete egg from inventory. [Skotlex]
- for (i = 0; i < MAX_INVENTORY; i++) {
- if(sd->status.inventory[i].card[0] == CARD0_PET &&
- p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))
- break;
- }
- if(i >= MAX_INVENTORY) {
- if (battle_config.error_log)
- ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name);
- sd->status.pet_id = 0;
- return 1;
- }
- if (!pet_birth_process(sd,p)) //Pet hatched. Delete egg.
- pc_delitem(sd,i,1,0);
- } else {
- pet_data_init(sd,p);
- if(sd->pd && sd->bl.prev != NULL) {
- map_addblock(&sd->pd->bl);
- clif_spawn(&sd->pd->bl);
- clif_send_petdata(sd,0,0);
- clif_send_petdata(sd,5,battle_config.pet_hair_style);
- clif_pet_equip(sd->pd);
- clif_send_petstatus(sd);
- }
- }
-
- return 0;
-}
-
-int pet_select_egg(struct map_session_data *sd,short egg_index)
-{
- nullpo_retr(0, sd);
-
- if(egg_index < 0 || egg_index >= MAX_INVENTORY)
- return 0; //Forged packet!
-
- if(sd->status.inventory[egg_index].card[0] == CARD0_PET)
- intif_request_petdata(sd->status.account_id, sd->status.char_id, MakeDWord(sd->status.inventory[egg_index].card[1], sd->status.inventory[egg_index].card[2]) );
- else {
- if(battle_config.error_log)
- ShowError("wrong egg item inventory %d\n",egg_index);
- }
- return 0;
-}
-
-int pet_catch_process1(struct map_session_data *sd,int target_class)
-{
- nullpo_retr(0, sd);
-
- sd->catch_target_class = target_class;
- clif_catch_process(sd);
-
- return 0;
-}
-
-int pet_catch_process2(struct map_session_data *sd,int target_id)
-{
- struct mob_data *md;
- int i=0,pet_catch_rate=0;
-
- nullpo_retr(1, sd);
-
- md=(struct mob_data*)map_id2bl(target_id);
- if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL){
- //Abort capture.
- sd->catch_target_class = -1;
- sd->itemid = sd->itemindex = -1;
- return 1;
- }
-
- if (sd->menuskill_id != SA_TAMINGMONSTER)
- { //Exploit?
- clif_pet_rulet(sd,0);
- sd->catch_target_class = -1;
- return 1;
- }
-
- if (sd->menuskill_lv > 0)
- { //Consume the pet lure [Skotlex]
- i=pc_search_inventory(sd,sd->menuskill_lv);
- if (i < 0)
- { //they tried an exploit?
- clif_pet_rulet(sd,0);
- sd->catch_target_class = -1;
- return 1;
- }
- //Delete the item
- if (sd->itemid == sd->menuskill_lv)
- sd->itemid = sd->itemindex = -1;
- sd->menuskill_id = sd->menuskill_lv = 0;
- pc_delitem(sd,i,1,0);
- }
-
- i = search_petDB_index(md->class_,PET_CLASS);
- //catch_target_class == 0 is used for universal lures. [Skotlex]
- //for now universal lures do not include bosses.
- if (sd->catch_target_class == 0 && !(md->status.mode&MD_BOSS))
- sd->catch_target_class = md->class_;
- if(i < 0 || sd->catch_target_class != md->class_) {
- clif_emotion(&md->bl, 7); //mob will do /ag if wrong lure is used on them.
- clif_pet_rulet(sd,0);
- sd->catch_target_class = -1;
- return 1;
- }
-
- pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - md->status.hp*100/md->status.max_hp)/100;
- if(pet_catch_rate < 1) pet_catch_rate = 1;
- if(battle_config.pet_catch_rate != 100)
- pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100;
-
- if(rand()%10000 < pet_catch_rate) {
- unit_remove_map(&md->bl,0);
- status_kill(&md->bl);
- clif_pet_rulet(sd,1);
-// if(battle_config.etc_log)
-// printf("rulet success %d\n",target_id);
- intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv,
- pet_db[i].EggID,0,pet_db[i].intimate,100,0,1,pet_db[i].jname);
- }
- else
- {
- sd->catch_target_class = -1;
- clif_pet_rulet(sd,0);
- }
-
- return 0;
-}
-
-int pet_get_egg(int account_id,int pet_id,int flag)
-{ //This function is invoked when a new pet has been created, and at no other time!
- struct map_session_data *sd;
- struct item tmp_item;
- int i=0,ret=0;
-
- if(flag)
- return 0;
-
- sd = map_id2sd(account_id);
- if(sd == NULL)
- return 0;
-
- i = search_petDB_index(sd->catch_target_class,PET_CLASS);
- sd->catch_target_class = -1;
-
- if(i < 0) {
- intif_delete_petdata(pet_id);
- return 0;
- }
-
- malloc_set(&tmp_item,0,sizeof(tmp_item));
- tmp_item.nameid = pet_db[i].EggID;
- tmp_item.identify = 1;
- tmp_item.card[0] = CARD0_PET;
- tmp_item.card[1] = GetWord(pet_id,0);
- tmp_item.card[2] = GetWord(pet_id,1);
- tmp_item.card[3] = 0; //New pets are not named.
- if((ret = pc_additem(sd,&tmp_item,1))) {
- clif_additem(sd,0,0,ret);
- map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
- }
-
- return 1;
-}
-
-static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd);
-static int pet_food(struct map_session_data *sd, struct pet_data *pd);
-static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap);
-
-int pet_menu(struct map_session_data *sd,int menunum)
-{
- nullpo_retr(0, sd);
- if (sd->pd == NULL)
- return 1;
-
- //You lost the pet already.
- if(!sd->status.pet_id || sd->pd->pet.intimate <= 0)
- return 1;
-
- switch(menunum) {
- case 0:
- clif_send_petstatus(sd);
- break;
- case 1:
- pet_food(sd, sd->pd);
- break;
- case 2:
- pet_performance(sd, sd->pd);
- break;
- case 3:
- pet_return_egg(sd, sd->pd);
- break;
- case 4:
- pet_unequipitem(sd, sd->pd);
- break;
- }
- return 0;
-}
-
-int pet_change_name(struct map_session_data *sd,char *name, int flag) //flag 0 = check name, 1 = good name
-{
- int i;
- struct pet_data *pd;
- nullpo_retr(1, sd);
-
- pd = sd->pd;
- if((pd == NULL) || (pd->pet.rename_flag == 1 && !battle_config.pet_rename))
- return 1;
-
- for(i=0;i<NAME_LENGTH && name[i];i++){
- if( !(name[i]&0xe0) || name[i]==0x7f)
- return 1;
- }
-
- if (!flag)
- return intif_rename_pet(sd, name);
-
- pet_stop_walking(pd,1);
-
- memcpy(pd->pet.name, name, NAME_LENGTH-1);
-
- clif_charnameack (0,&pd->bl);
- pd->pet.rename_flag = 1;
- clif_pet_equip(pd);
- clif_send_petstatus(sd);
-
- return 0;
-}
-
-int pet_equipitem(struct map_session_data *sd,int index)
-{
- struct pet_data *pd;
- int nameid;
-
- nullpo_retr(1, sd);
- pd = sd->pd;
- if (!pd) return 1;
-
- nameid = sd->status.inventory[index].nameid;
-
- if(pd->petDB->AcceID == 0 || nameid != pd->petDB->AcceID || pd->pet.equip != 0) {
- clif_equipitemack(sd,0,0,0);
- return 1;
- }
-
- pc_delitem(sd,index,1,0);
- pd->pet.equip = nameid;
- status_set_viewdata(&pd->bl, pd->pet.class_); //Updates view_data.
- clif_pet_equip(pd);
- if (battle_config.pet_equip_required)
- { //Skotlex: start support timers if need
- unsigned int tick = gettick();
- if (pd->s_skill && pd->s_skill->timer == -1)
- {
- if (pd->s_skill->id)
- pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0);
- else
- pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0);
- }
- if (pd->bonus && pd->bonus->timer == -1)
- pd->bonus->timer=add_timer(tick+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
- }
-
- return 0;
-}
-
-static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd)
-{
- struct item tmp_item;
- int nameid,flag;
-
- if(pd->pet.equip == 0)
- return 1;
-
- nameid = pd->pet.equip;
- pd->pet.equip = 0;
- status_set_viewdata(&pd->bl, pd->pet.class_);
- clif_pet_equip(pd);
- malloc_set(&tmp_item,0,sizeof(tmp_item));
- tmp_item.nameid = nameid;
- tmp_item.identify = 1;
- if((flag = pc_additem(sd,&tmp_item,1))) {
- clif_additem(sd,0,0,flag);
- map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
- }
- if (battle_config.pet_equip_required)
- { //Skotlex: halt support timers if needed
- if(pd->state.skillbonus) {
- pd->state.skillbonus = 0;
- status_calc_pc(sd,0);
- }
- if (pd->s_skill && pd->s_skill->timer != -1)
- {
- if (pd->s_skill->id)
- delete_timer(pd->s_skill->timer, pet_skill_support_timer);
- else
- delete_timer(pd->s_skill->timer, pet_heal_timer);
- pd->s_skill->timer = -1;
- }
- if (pd->bonus && pd->bonus->timer != -1)
- {
- delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
- pd->bonus->timer = -1;
- }
- }
-
- return 0;
-}
-
-static int pet_food(struct map_session_data *sd, struct pet_data *pd)
-{
- int i,k;
-
- k=pd->petDB->FoodID;
- i=pc_search_inventory(sd,k);
- if(i < 0) {
- clif_pet_food(sd,k,0);
- return 1;
- }
- pc_delitem(sd,i,1,0);
-
- if(pd->pet.hungry > 90)
- pd->pet.intimate -= pd->petDB->r_full;
- else {
- if(battle_config.pet_friendly_rate != 100)
- k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100;
- else
- k = pd->petDB->r_hungry;
- if(pd->pet.hungry > 75) {
- k = k >> 1;
- if(k <= 0)
- k = 1;
- }
- pd->pet.intimate += k;
- }
- if(pd->pet.intimate <= 0) {
- pd->pet.intimate = 0;
- pet_stop_attack(pd);
- pd->status.speed = pd->db->status.speed;
- }
- else if(pd->pet.intimate > 1000)
- pd->pet.intimate = 1000;
- status_calc_pet(pd, 0);
- pd->pet.hungry += pd->petDB->fullness;
- if(pd->pet.hungry > 100)
- pd->pet.hungry = 100;
-
- clif_send_petdata(sd,2,pd->pet.hungry);
- clif_send_petdata(sd,1,pd->pet.intimate);
- clif_pet_food(sd,pd->petDB->FoodID,1);
-
- return 0;
-}
-
-static int pet_randomwalk(struct pet_data *pd,unsigned int tick)
-{
- const int retrycount=20;
-
- nullpo_retr(0, pd);
-
- Assert((pd->msd == 0) || (pd->msd->pd == pd));
-
- if(DIFF_TICK(pd->next_walktime,tick) < 0 && unit_can_move(&pd->bl)) {
- int i,x,y,c,d=12-pd->move_fail_count;
- if(d<5) d=5;
- for(i=0;i<retrycount;i++){
- int r=rand();
- x=pd->bl.x+r%(d*2+1)-d;
- y=pd->bl.y+r/(d*2+1)%(d*2+1)-d;
- if(map_getcell(pd->bl.m,x,y,CELL_CHKPASS) && unit_walktoxy(&pd->bl,x,y,0)){
- pd->move_fail_count=0;
- break;
- }
- if(i+1>=retrycount){
- pd->move_fail_count++;
- if(pd->move_fail_count>1000){
- if(battle_config.error_log)
- ShowWarning("PET cant move. hold position %d, class = %d\n",pd->bl.id,pd->pet.class_);
- pd->move_fail_count=0;
- pd->ud.canmove_tick = tick + 60000;
- return 0;
- }
- }
- }
- for(i=c=0;i<pd->ud.walkpath.path_len;i++){
- if(pd->ud.walkpath.path[i]&1)
- c+=pd->status.speed*14/10;
- else
- c+=pd->status.speed;
- }
- pd->next_walktime = tick+rand()%3000+3000+c;
-
- return 1;
- }
- return 0;
-}
-
-static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, unsigned int tick)
-{
- struct block_list *target = NULL;
-
- if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL)
- return 0;
-
- if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME)
- return 0;
- pd->last_thinktime=tick;
-
- if(pd->ud.attacktimer != -1 || pd->ud.skilltimer != -1 || pd->bl.m != sd->bl.m)
- return 0;
-
- if(pd->ud.walktimer != -1 && pd->ud.walkpath.path_pos <= 2)
- return 0; //No thinking when you just started to walk.
-
- if(pd->pet.intimate <= 0) {
- //Pet should just... well, random walk.
- pet_randomwalk(pd,tick);
- return 0;
- }
-
- if (!check_distance_bl(&sd->bl, &pd->bl, pd->db->range3)) {
- //Master too far, chase.
- if(pd->target_id)
- pet_unlocktarget(pd);
- if(pd->ud.walktimer != -1 && pd->ud.target == sd->bl.id)
- return 0; //Already walking to him
- if (DIFF_TICK(tick, pd->ud.canmove_tick) < 0)
- return 0; //Can't move yet.
- pd->status.speed = (sd->battle_status.speed>>1);
- if(pd->status.speed <= 0)
- pd->status.speed = 1;
- if (!unit_walktobl(&pd->bl, &sd->bl, 3, 0))
- pet_randomwalk(pd,tick);
- return 0;
- }
-
- //Return speed to normal.
- if (pd->status.speed != pd->petDB->speed) {
- if (pd->ud.walktimer != -1)
- return 0; //Wait until the pet finishes walking back to master.
- pd->status.speed = pd->petDB->speed;
- }
-
- if (pd->target_id) {
- target= map_id2bl(pd->target_id);
- if (!target || pd->bl.m != target->m || status_isdead(target) ||
- !check_distance_bl(&pd->bl, target, pd->db->range3))
- {
- target = NULL;
- pet_unlocktarget(pd);
- }
- }
-
- if(!target && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) {
- //Use half the pet's range of sight.
- map_foreachinrange(pet_ai_sub_hard_lootsearch,&pd->bl,
- pd->db->range2/2, BL_ITEM,pd,&target);
- }
-
- if (!target) {
- //Just walk around.
- if (check_distance_bl(&sd->bl, &pd->bl, 3))
- return 0; //Already next to master.
-
- if(pd->ud.walktimer != -1 && check_distance_blxy(&sd->bl, pd->ud.to_x,pd->ud.to_y, 3))
- return 0; //Already walking to him
-
- pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir);
- if(!unit_walktoxy(&pd->bl,pd->ud.to_x,pd->ud.to_y,0))
- pet_randomwalk(pd,tick);
-
- return 0;
- }
-
- if(pd->ud.target == target->id &&
- (pd->ud.attacktimer != -1 || pd->ud.walktimer != -1))
- return 0; //Target already locked.
-
- if (target->type != BL_ITEM)
- { //enemy targetted
- if(!battle_check_range(&pd->bl,target,pd->status.rhw.range))
- { //Chase
- if(!unit_walktobl(&pd->bl, target, pd->status.rhw.range, 2))
- pet_unlocktarget(pd); //Unreachable target.
- return 0;
- }
- //Continuous attack.
- unit_attack(&pd->bl, pd->target_id, 1);
- } else { //Item Targeted, attempt loot
- if (!check_distance_bl(&pd->bl, target, 1))
- { //Out of range
- if(!unit_walktobl(&pd->bl, target, 0, 1)) //Unreachable target.
- pet_unlocktarget(pd);
- return 0;
- } else{
- struct flooritem_data *fitem = (struct flooritem_data *)target;
- if(pd->loot->count < pd->loot->max){
- memcpy(&pd->loot->item[pd->loot->count++],&fitem->item_data,sizeof(pd->loot->item[0]));
- pd->loot->weight += itemdb_search(fitem->item_data.nameid)->weight*fitem->item_data.amount;
- map_clearflooritem(target->id);
- }
- //Target is unlocked regardless of whether it was picked or not.
- pet_unlocktarget(pd);
- }
- }
- return 0;
-}
-
-static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap)
-{
- unsigned int tick = va_arg(ap,unsigned int);
- if(sd->status.pet_id && sd->pd)
- pet_ai_sub_hard(sd->pd,sd,tick);
-
- return 0;
-}
-
-static int pet_ai_hard(int tid,unsigned int tick,int id,int data)
-{
- clif_foreachclient(pet_ai_sub_foreachclient,tick);
-
- return 0;
-}
-
-static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap)
-{
- struct pet_data* pd;
- struct flooritem_data *fitem = (struct flooritem_data *)bl;
- struct block_list **target;
- int sd_id =0;
-
- pd=va_arg(ap,struct pet_data *);
- target=va_arg(ap,struct block_list**);
-
- sd_id = fitem->first_get_id;
-
- if(sd_id && sd_id != pd->msd->bl.id)
- return 0;
-
- if(unit_can_reach_bl(&pd->bl,bl, pd->db->range2, 1, NULL, NULL) &&
- ((*target) == NULL || //New target closer than previous one.
- !check_distance_bl(&pd->bl, *target, distance_bl(&pd->bl, bl))))
- {
- (*target) = bl;
- pd->target_id = bl->id;
- return 1;
- }
-
- return 0;
-}
-
-static int pet_delay_item_drop(int tid,unsigned int tick,int id,int data)
-{
- struct item_drop_list *list;
- struct item_drop *ditem, *ditem_prev;
- list=(struct item_drop_list *)id;
- ditem = list->item;
- while (ditem) {
- map_addflooritem(&ditem->item_data,ditem->item_data.amount,
- list->m,list->x,list->y,
- list->first_sd,list->second_sd,list->third_sd,0);
- ditem_prev = ditem;
- ditem = ditem->next;
- ers_free(item_drop_ers, ditem_prev);
- }
- ers_free(item_drop_list_ers, list);
- return 0;
-}
-
-int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd)
-{
- int i,flag=0;
- struct item_drop_list *dlist;
- struct item_drop *ditem;
- struct item *it;
- if(!pd || !pd->loot || !pd->loot->count)
- return 0;
- dlist = ers_alloc(item_drop_list_ers, struct item_drop_list);
- dlist->m = pd->bl.m;
- dlist->x = pd->bl.x;
- dlist->y = pd->bl.y;
- dlist->first_sd = NULL;
- dlist->second_sd = NULL;
- dlist->third_sd = NULL;
- dlist->item = NULL;
-
- for(i=0;i<pd->loot->count;i++) {
- it = &pd->loot->item[i];
- if(sd){
- if((flag = pc_additem(sd,it,it->amount))){
- clif_additem(sd,0,0,flag);
- ditem = ers_alloc(item_drop_ers, struct item_drop);
- memcpy(&ditem->item_data, it, sizeof(struct item));
- ditem->next = dlist->item;
- dlist->item = ditem;
- }
- }
- else {
- ditem = ers_alloc(item_drop_ers, struct item_drop);
- memcpy(&ditem->item_data, it, sizeof(struct item));
- ditem->next = dlist->item;
- dlist->item = ditem;
- }
- }
- //The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori)
- malloc_set(pd->loot->item,0,pd->loot->max * sizeof(struct item));
- pd->loot->count = 0;
- pd->loot->weight = 0;
- pd->ud.canact_tick = gettick()+10000; // 10*1000msの間拾わない
-
- if (dlist->item)
- add_timer(gettick()+540,pet_delay_item_drop,(int)dlist,0);
- else
- ers_free(item_drop_list_ers, dlist);
- return 1;
-}
-
-/*==========================================
- * pet bonus giving skills [Valaris] / Rewritten by [Skotlex]
- *------------------------------------------
- */
-int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data)
-{
- struct map_session_data *sd=map_id2sd(id);
- struct pet_data *pd;
- int bonus;
- int timer = 0;
-
- if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL)
- return 1;
-
- pd=sd->pd;
-
- if(pd->bonus->timer != tid) {
- if(battle_config.error_log)
- {
- ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid);
- pd->bonus->timer = -1;
- }
- return 0;
- }
-
- // determine the time for the next timer
- if (pd->state.skillbonus && pd->bonus->delay > 0) {
- bonus = 0;
- timer = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again
- } else if (pd->pet.intimate) {
- bonus = 1;
- timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect
- } else { //Lost pet...
- pd->bonus->timer = -1;
- return 0;
- }
-
- if (pd->state.skillbonus != bonus) {
- pd->state.skillbonus = bonus;
- status_calc_pc(sd, 0);
- }
- // wait for the next timer
- pd->bonus->timer=add_timer(tick+timer,pet_skill_bonus_timer,sd->bl.id,0);
- return 0;
-}
-
-/*==========================================
- * pet recovery skills [Valaris] / Rewritten by [Skotlex]
- *------------------------------------------
- */
-int pet_recovery_timer(int tid,unsigned int tick,int id,int data)
-{
- struct map_session_data *sd=map_id2sd(id);
- struct pet_data *pd;
-
- if(sd==NULL || sd->pd == NULL || sd->pd->recovery == NULL)
- return 1;
-
- pd=sd->pd;
-
- if(pd->recovery->timer != tid) {
- if(battle_config.error_log)
- ShowError("pet_recovery_timer %d != %d\n",pd->recovery->timer,tid);
- return 0;
- }
-
- if(sd->sc.count && sd->sc.data[pd->recovery->type].timer != -1)
- { //Display a heal animation?
- //Detoxify is chosen for now.
- clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1);
- status_change_end(&sd->bl,pd->recovery->type,-1);
- clif_emotion(&pd->bl, 33);
- }
-
- pd->recovery->timer = -1;
-
- return 0;
-}
-
-int pet_heal_timer(int tid,unsigned int tick,int id,int data)
-{
- struct map_session_data *sd=map_id2sd(id);
- struct status_data *status;
- struct pet_data *pd;
- short rate = 100;
-
- if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL)
- return 1;
-
- pd=sd->pd;
-
- if(pd->s_skill->timer != tid) {
- if(battle_config.error_log)
- ShowError("pet_heal_timer %d != %d\n",pd->s_skill->timer,tid);
- return 0;
- }
-
- status = status_get_status_data(&sd->bl);
-
- if(pc_isdead(sd) ||
- (rate = status->sp*100/status->max_sp) > pd->s_skill->sp ||
- (rate = status->hp*100/status->max_hp) > pd->s_skill->hp ||
- (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect
- ) { //Wait (how long? 1 sec for every 10% of remaining)
- pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0);
- return 0;
- }
- pet_stop_attack(pd);
- pet_stop_walking(pd,1);
- clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1);
- status_heal(&sd->bl, pd->s_skill->lv,0, 0);
- pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
- return 0;
-}
-
-/*==========================================
- * pet support skills [Skotlex]
- *------------------------------------------
- */
-int pet_skill_support_timer(int tid,unsigned int tick,int id,int data)
-{
- struct map_session_data *sd=map_id2sd(id);
- struct pet_data *pd;
- struct status_data *status;
- short rate = 100;
- if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL)
- return 1;
-
- pd=sd->pd;
-
- if(pd->s_skill->timer != tid) {
- if(battle_config.error_log)
- ShowError("pet_skill_support_timer %d != %d\n",pd->s_skill->timer,tid);
- return 0;
- }
-
- status = status_get_status_data(&sd->bl);
-
- if (DIFF_TICK(pd->ud.canact_tick, tick) > 0)
- { //Wait until the pet can act again.
- pd->s_skill->timer=add_timer(pd->ud.canact_tick,pet_skill_support_timer,sd->bl.id,0);
- return 0;
- }
-
- if(pc_isdead(sd) ||
- (rate = status->sp*100/status->max_sp) > pd->s_skill->sp ||
- (rate = status->hp*100/status->max_hp) > pd->s_skill->hp ||
- (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect
- ) { //Wait (how long? 1 sec for every 10% of remaining)
- pd->s_skill->timer=add_timer(tick+(rate>10?rate:10)*100,pet_skill_support_timer,sd->bl.id,0);
- return 0;
- }
-
- pet_stop_attack(pd);
- pet_stop_walking(pd,1);
-
- if (skill_get_inf(pd->s_skill->id) & INF_GROUND_SKILL)
- unit_skilluse_pos(&pd->bl, sd->bl.x, sd->bl.y, pd->s_skill->id, pd->s_skill->lv);
- else
- unit_skilluse_id(&pd->bl, sd->bl.id, pd->s_skill->id, pd->s_skill->lv);
-
- pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
- return 0;
-}
-
-/*==========================================
- *ペットデータ読み込み
- *------------------------------------------
- */
-int read_petdb()
-{
- FILE *fp;
- char line[1024];
- int nameid,i,k;
- int j=0;
- int lines;
- char *filename[]={"pet_db.txt","pet_db2.txt"};
- char *str[32],*p,*np;
-
-
-//Remove any previous scripts in case reloaddb was invoked.
- for(j =0; j < MAX_PET_DB; j++)
- if (pet_db[j].script) {
- aFree(pet_db[j].script);
- pet_db[j].script = NULL;
- }
- j = 0;
- malloc_set(pet_db,0,sizeof(pet_db));
- for(i=0;i<2;i++){
- sprintf(line, "%s/%s", db_path, filename[i]);
- fp=fopen(line,"r");
- if(fp==NULL){
- if(i>0)
- continue;
- ShowError("can't read %s\n",line);
- return -1;
- }
- lines = 0;
- while(fgets(line,1020,fp) && j < MAX_PET_DB){
-
- lines++;
-
- if(line[0] == '/' && line[1] == '/')
- continue;
-
- for(k=0,p=line;k<20;k++){
- if((np=strchr(p,','))!=NULL){
- str[k]=p;
- *np=0;
- p=np+1;
- } else {
- str[k]=p;
- p+=strlen(p);
- }
- }
-
- nameid=atoi(str[0]);
- if(nameid<=0)
- continue;
-
- if (!mobdb_checkid(nameid)) {
- ShowWarning("pet_db reading: Invalid mob-class %d, pet not read.\n", nameid);
- continue;
- }
-
- pet_db[j].class_ = nameid;
- memcpy(pet_db[j].name,str[1],NAME_LENGTH-1);
- memcpy(pet_db[j].jname,str[2],NAME_LENGTH-1);
- pet_db[j].itemID=atoi(str[3]);
- pet_db[j].EggID=atoi(str[4]);
- pet_db[j].AcceID=atoi(str[5]);
- pet_db[j].FoodID=atoi(str[6]);
- pet_db[j].fullness=atoi(str[7]);
- pet_db[j].hungry_delay=atoi(str[8])*1000;
- pet_db[j].r_hungry=atoi(str[9]);
- if(pet_db[j].r_hungry <= 0)
- pet_db[j].r_hungry=1;
- pet_db[j].r_full=atoi(str[10]);
- pet_db[j].intimate=atoi(str[11]);
- pet_db[j].die=atoi(str[12]);
- pet_db[j].capture=atoi(str[13]);
- pet_db[j].speed=atoi(str[14]);
- pet_db[j].s_perfor=(char)atoi(str[15]);
- pet_db[j].talk_convert_class=atoi(str[16]);
- pet_db[j].attack_rate=atoi(str[17]);
- pet_db[j].defence_attack_rate=atoi(str[18]);
- pet_db[j].change_target_rate=atoi(str[19]);
- pet_db[j].script = NULL;
- if((np=strchr(p,'{'))==NULL)
- continue;
- pet_db[j].script = parse_script((unsigned char *) np, filename[i], lines);
- j++;
- }
- if (j >= MAX_PET_DB)
- ShowWarning("read_petdb: Reached max number of pets [%d]. Remaining pets were not read.\n ", MAX_PET_DB);
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' pets in '"CL_WHITE"%s"CL_RESET"'.\n",j,filename[i]);
- }
- return 0;
-}
-
-/*==========================================
- * スキル関係初期化処理
- *------------------------------------------
- */
-int do_init_pet(void)
-{
- malloc_set(pet_db,0,sizeof(pet_db));
- read_petdb();
-
- item_drop_ers = ers_new((uint32)sizeof(struct item_drop));
- item_drop_list_ers = ers_new((uint32)sizeof(struct item_drop_list));
-
- add_timer_func_list(pet_hungry,"pet_hungry");
- add_timer_func_list(pet_ai_hard,"pet_ai_hard");
- add_timer_func_list(pet_skill_bonus_timer,"pet_skill_bonus_timer"); // [Valaris]
- add_timer_func_list(pet_delay_item_drop,"pet_delay_item_drop");
- add_timer_func_list(pet_skill_support_timer, "pet_skill_support_timer"); // [Skotlex]
- add_timer_func_list(pet_recovery_timer,"pet_recovery_timer"); // [Valaris]
- add_timer_func_list(pet_heal_timer,"pet_heal_timer"); // [Valaris]
- add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME);
-
- return 0;
-}
-
-int do_final_pet(void) {
- int i;
- for(i = 0;i < MAX_PET_DB; i++) {
- if(pet_db[i].script) {
- script_free_code(pet_db[i].script);
- pet_db[i].script = NULL;
- }
- }
- ers_destroy(item_drop_ers);
- ers_destroy(item_drop_list_ers);
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/db.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+#include "../common/ers.h"
+
+#include "pc.h"
+#include "status.h"
+#include "map.h"
+#include "intif.h"
+#include "clif.h"
+#include "chrif.h"
+#include "pet.h"
+#include "itemdb.h"
+#include "battle.h"
+#include "mob.h"
+#include "npc.h"
+#include "script.h"
+#include "skill.h"
+#include "unit.h"
+
+#define MIN_PETTHINKTIME 100
+
+struct pet_db pet_db[MAX_PET_DB];
+
+static struct eri *item_drop_ers; //For loot drops delay structures.
+static struct eri *item_drop_list_ers;
+
+int pet_hungry_val(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ if(pd->pet.hungry > 90)
+ return 4;
+ else if(pd->pet.hungry > 75)
+ return 3;
+ else if(pd->pet.hungry > 25)
+ return 2;
+ else if(pd->pet.hungry > 10)
+ return 1;
+ else
+ return 0;
+}
+
+static int pet_calc_pos(struct pet_data *pd,int tx,int ty,int dir)
+{
+ int x,y,dx,dy;
+ int i,k;
+
+ nullpo_retr(0, pd);
+
+ pd->ud.to_x = tx;
+ pd->ud.to_y = ty;
+
+ if(dir < 0 || dir >= 8)
+ return 1;
+
+ dx = -dirx[dir]*2;
+ dy = -diry[dir]*2;
+ x = tx + dx;
+ y = ty + dy;
+ if(!unit_can_reach_pos(&pd->bl,x,y,0)) {
+ if(dx > 0) x--;
+ else if(dx < 0) x++;
+ if(dy > 0) y--;
+ else if(dy < 0) y++;
+ if(!unit_can_reach_pos(&pd->bl,x,y,0)) {
+ for(i=0;i<12;i++) {
+ k = rand()%8;
+ dx = -dirx[k]*2;
+ dy = -diry[k]*2;
+ x = tx + dx;
+ y = ty + dy;
+ if(unit_can_reach_pos(&pd->bl,x,y,0))
+ break;
+ else {
+ if(dx > 0) x--;
+ else if(dx < 0) x++;
+ if(dy > 0) y--;
+ else if(dy < 0) y++;
+ if(unit_can_reach_pos(&pd->bl,x,y,0))
+ break;
+ }
+ }
+ if(i>=12) {
+ x = tx;
+ y = ty;
+ if(!unit_can_reach_pos(&pd->bl,x,y,0))
+ return 1;
+ }
+ }
+ }
+ pd->ud.to_x = x;
+ pd->ud.to_y = y;
+ return 0;
+}
+
+int pet_create_egg(struct map_session_data *sd, int item_id)
+{
+ int pet_id = search_petDB_index(item_id, PET_EGG);
+ if (pet_id < 0) return 0; //No pet egg here.
+ sd->catch_target_class = pet_db[pet_id].class_;
+ intif_create_pet(sd->status.account_id, sd->status.char_id,
+ (short)pet_db[pet_id].class_,
+ (short)mob_db(pet_db[pet_id].class_)->lv,
+ (short)pet_db[pet_id].EggID, 0,
+ (short)pet_db[pet_id].intimate,
+ 100, 0, 1, pet_db[pet_id].jname);
+ return 1;
+}
+
+int pet_unlocktarget(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+
+ pd->target_id=0;
+ pet_stop_attack(pd);
+ pet_stop_walking(pd,1);
+ return 0;
+}
+
+/*==========================================
+ * Pet Attack Skill [Skotlex]
+ *------------------------------------------
+ */
+int pet_attackskill(struct pet_data *pd, int target_id)
+{
+ struct block_list *bl;
+ int inf;
+
+ if (!battle_config.pet_status_support || !pd->a_skill ||
+ (battle_config.pet_equip_required && !pd->pet.equip))
+ return 0;
+
+ if (DIFF_TICK(pd->ud.canact_tick, gettick()) > 0)
+ return 0;
+
+ if (rand()%100 < (pd->a_skill->rate +pd->pet.intimate*pd->a_skill->bonusrate/1000))
+ { //Skotlex: Use pet's skill
+ bl=map_id2bl(target_id);
+ if(bl == NULL || pd->bl.m != bl->m || bl->prev == NULL || status_isdead(bl) ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range3))
+ return 0;
+
+ inf = skill_get_inf(pd->a_skill->id);
+ if (inf & INF_GROUND_SKILL)
+ unit_skilluse_pos(&pd->bl, bl->x, bl->y, pd->a_skill->id, pd->a_skill->lv);
+ else //Offensive self skill? Could be stuff like GX.
+ unit_skilluse_id(&pd->bl,(inf&INF_SELF_SKILL?pd->bl.id:bl->id), pd->a_skill->id, pd->a_skill->lv);
+ return 1; //Skill invoked.
+ }
+ return 0;
+}
+
+int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type)
+{
+ struct pet_data *pd;
+ int rate;
+
+ pd = sd->pd;
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(bl == NULL || bl->type != BL_MOB || bl->prev == NULL ||
+ pd->pet.intimate < battle_config.pet_support_min_friendly ||
+ pd->pet.hungry < 1 ||
+ pd->pet.class_ == status_get_class(bl))
+ return 0;
+
+ if(pd->bl.m != bl->m ||
+ !check_distance_bl(&pd->bl, bl, pd->db->range2))
+ return 0;
+
+ if (!status_check_skilluse(&pd->bl, bl, 0, 0))
+ return 0;
+
+ if(!type) {
+ rate = pd->petDB->attack_rate;
+ rate = rate * pd->rate_fix/1000;
+ if(pd->petDB->attack_rate > 0 && rate <= 0)
+ rate = 1;
+ } else {
+ rate = pd->petDB->defence_attack_rate;
+ rate = rate * pd->rate_fix/1000;
+ if(pd->petDB->defence_attack_rate > 0 && rate <= 0)
+ rate = 1;
+ }
+ if(rand()%10000 < rate)
+ {
+ if(pd->target_id == 0 || rand()%10000 < pd->petDB->change_target_rate)
+ pd->target_id = bl->id;
+ }
+
+ return 0;
+}
+/*==========================================
+ * Pet SC Check [Skotlex]
+ *------------------------------------------
+ */
+int pet_sc_check(struct map_session_data *sd, int type)
+{
+ struct pet_data *pd;
+
+ nullpo_retr(0, sd);
+ pd = sd->pd;
+
+ if (pd == NULL ||
+ (battle_config.pet_equip_required && pd->pet.equip == 0) ||
+ pd->recovery == NULL ||
+ pd->recovery->timer != -1 ||
+ pd->recovery->type != type)
+ return 1;
+
+ pd->recovery->timer = add_timer(gettick()+pd->recovery->delay*1000,pet_recovery_timer,sd->bl.id,0);
+
+ return 0;
+}
+
+static int pet_hungry(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd;
+ struct pet_data *pd;
+ int interval,t;
+
+ sd=map_id2sd(id);
+ if(!sd)
+ return 1;
+
+ if(!sd->status.pet_id || !sd->pd)
+ return 1;
+
+ pd = sd->pd;
+ if(pd->pet_hungry_timer != tid){
+ if(battle_config.error_log)
+ ShowError("pet_hungry_timer %d != %d\n",pd->pet_hungry_timer,tid);
+ return 0;
+ }
+ pd->pet_hungry_timer = -1;
+
+ if (pd->pet.intimate <= 0)
+ return 1; //You lost the pet already, the rest is irrelevant.
+
+ pd->pet.hungry--;
+ t = pd->pet.intimate;
+ if(pd->pet.hungry < 0) {
+ pet_stop_attack(pd);
+ pd->pet.hungry = 0;
+ pd->pet.intimate -= battle_config.pet_hungry_friendly_decrease;
+ if(pd->pet.intimate <= 0) {
+ pd->pet.intimate = 0;
+ pd->status.speed = pd->db->status.speed;
+ }
+ status_calc_pet(pd, 0);
+ clif_send_petdata(sd,1,pd->pet.intimate);
+ }
+ clif_send_petdata(sd,2,pd->pet.hungry);
+
+ if(battle_config.pet_hungry_delay_rate != 100)
+ interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
+ else
+ interval = pd->petDB->hungry_delay;
+ if(interval <= 0)
+ interval = 1;
+ pd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0);
+
+ return 0;
+}
+
+int search_petDB_index(int key,int type)
+{
+ int i;
+
+ for(i=0;i<MAX_PET_DB;i++) {
+ if(pet_db[i].class_ <= 0)
+ continue;
+ switch(type) {
+ case PET_CLASS:
+ if(pet_db[i].class_ == key)
+ return i;
+ break;
+ case PET_CATCH:
+ if(pet_db[i].itemID == key)
+ return i;
+ break;
+ case PET_EGG:
+ if(pet_db[i].EggID == key)
+ return i;
+ break;
+ case PET_EQUIP:
+ if(pet_db[i].AcceID == key)
+ return i;
+ break;
+ case PET_FOOD:
+ if(pet_db[i].FoodID == key)
+ return i;
+ break;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+}
+
+int pet_hungry_timer_delete(struct pet_data *pd)
+{
+ nullpo_retr(0, pd);
+ if(pd->pet_hungry_timer != -1) {
+ delete_timer(pd->pet_hungry_timer,pet_hungry);
+ pd->pet_hungry_timer = -1;
+ }
+
+ return 1;
+}
+
+static int pet_performance(struct map_session_data *sd, struct pet_data *pd)
+{
+ int val;
+
+ if (pd->pet.intimate > 900)
+ val = (pd->petDB->s_perfor > 0)? 4:3;
+ else if(pd->pet.intimate > 750)
+ val = 2;
+ else
+ val = 1;
+
+ pet_stop_walking(pd,2000<<8);
+ clif_pet_performance(&pd->bl,rand()%val + 1);
+ pet_lootitem_drop(pd,NULL);
+ return 1;
+}
+
+static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd)
+{
+ struct item tmp_item;
+ int flag;
+
+ pet_lootitem_drop(pd,sd);
+ malloc_set(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = pd->petDB->EggID;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = CARD0_PET;
+ tmp_item.card[1] = GetWord(pd->pet.pet_id,0);
+ tmp_item.card[2] = GetWord(pd->pet.pet_id,1);
+ tmp_item.card[3] = pd->pet.rename_flag;
+ if((flag = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ pd->pet.incuvate = 1;
+ //No need, pet is saved on unit_free below.
+ //intif_save_petdata(sd->status.account_id,&pd->pet);
+ if(pd->state.skillbonus) {
+ pd->state.skillbonus = 0;
+ status_calc_pc(sd,0);
+ }
+ unit_free(&pd->bl,0);
+ sd->status.pet_id = 0;
+
+ return 1;
+}
+
+int pet_data_init(struct map_session_data *sd, struct s_pet *pet)
+{
+ struct pet_data *pd;
+ int i=0,interval=0;
+
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.account_id != pet->account_id || sd->status.char_id != pet->char_id) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ if (sd->status.pet_id != pet->pet_id) {
+ if (sd->status.pet_id) {
+ //Wrong pet?? Set incuvate to no and send it back for saving.
+ pet->incuvate = 1;
+ intif_save_petdata(sd->status.account_id,pet);
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ //The pet_id value was lost? odd... restore it.
+ sd->status.pet_id = pet->pet_id;
+ }
+
+ i = search_petDB_index(pet->class_,PET_CLASS);
+ if(i < 0) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data));
+ pd->petDB = &pet_db[i];
+ memcpy(&pd->pet, pet, sizeof(struct s_pet));
+ pd->bl.m = sd->bl.m;
+ pd->bl.x = sd->bl.x;
+ pd->bl.y = sd->bl.y;
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir);
+ pd->bl.x = pd->ud.to_x;
+ pd->bl.y = pd->ud.to_y;
+ pd->bl.id = npc_get_new_npc_id();
+ pd->db = mob_db(pet->class_);
+ pd->bl.subtype = MONS;
+ pd->bl.type = BL_PET;
+ pd->msd = sd;
+ status_set_viewdata(&pd->bl, pet->class_);
+ unit_dataset(&pd->bl);
+ pd->ud.dir = sd->ud.dir;
+ pd->last_thinktime = gettick();
+
+ map_addiddb(&pd->bl);
+
+ // initialise
+ status_calc_pet(pd,1);
+
+ pd->state.skillbonus = 0;
+ if (battle_config.pet_status_support) //Skotlex
+ run_script(pet_db[i].script,0,sd->bl.id,0);
+
+ if(battle_config.pet_hungry_delay_rate != 100)
+ interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100;
+ else
+ interval = pd->petDB->hungry_delay;
+ if(interval <= 0)
+ interval = 1;
+ pd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0);
+ return 0;
+}
+
+int pet_birth_process(struct map_session_data *sd, struct s_pet *pet)
+{
+ nullpo_retr(1, sd);
+
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ if(sd->status.pet_id && pet->incuvate == 1) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+
+ pet->incuvate = 0;
+ pet->account_id = sd->status.account_id;
+ pet->char_id = sd->status.char_id;
+ sd->status.pet_id = pet->pet_id;
+ if(pet_data_init(sd, pet)) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+
+ intif_save_petdata(sd->status.account_id,pet);
+ if (save_settings&8)
+ chrif_save(sd,0); //is it REALLY Needed to save the char for hatching a pet? [Skotlex]
+
+ if(sd->bl.prev != NULL) {
+ map_addblock(&sd->pd->bl);
+ clif_spawn(&sd->pd->bl);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ clif_pet_equip(sd->pd);
+ clif_send_petstatus(sd);
+ }
+ Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd);
+
+ return 0;
+}
+
+int pet_recv_petdata(int account_id,struct s_pet *p,int flag)
+{
+ struct map_session_data *sd;
+
+ sd = map_id2sd(account_id);
+ if(sd == NULL)
+ return 1;
+ if(flag == 1) {
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ if(p->incuvate == 1) {
+ int i;
+ //Delete egg from inventory. [Skotlex]
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if(sd->status.inventory[i].card[0] == CARD0_PET &&
+ p->pet_id == MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]))
+ break;
+ }
+ if(i >= MAX_INVENTORY) {
+ if (battle_config.error_log)
+ ShowError("pet_recv_petdata: Hatching pet (%d:%s) aborted, couldn't find egg in inventory for removal!\n",p->pet_id, p->name);
+ sd->status.pet_id = 0;
+ return 1;
+ }
+ if (!pet_birth_process(sd,p)) //Pet hatched. Delete egg.
+ pc_delitem(sd,i,1,0);
+ } else {
+ pet_data_init(sd,p);
+ if(sd->pd && sd->bl.prev != NULL) {
+ map_addblock(&sd->pd->bl);
+ clif_spawn(&sd->pd->bl);
+ clif_send_petdata(sd,0,0);
+ clif_send_petdata(sd,5,battle_config.pet_hair_style);
+ clif_pet_equip(sd->pd);
+ clif_send_petstatus(sd);
+ }
+ }
+
+ return 0;
+}
+
+int pet_select_egg(struct map_session_data *sd,short egg_index)
+{
+ nullpo_retr(0, sd);
+
+ if(egg_index < 0 || egg_index >= MAX_INVENTORY)
+ return 0; //Forged packet!
+
+ if(sd->status.inventory[egg_index].card[0] == CARD0_PET)
+ intif_request_petdata(sd->status.account_id, sd->status.char_id, MakeDWord(sd->status.inventory[egg_index].card[1], sd->status.inventory[egg_index].card[2]) );
+ else {
+ if(battle_config.error_log)
+ ShowError("wrong egg item inventory %d\n",egg_index);
+ }
+ return 0;
+}
+
+int pet_catch_process1(struct map_session_data *sd,int target_class)
+{
+ nullpo_retr(0, sd);
+
+ sd->catch_target_class = target_class;
+ clif_catch_process(sd);
+
+ return 0;
+}
+
+int pet_catch_process2(struct map_session_data *sd,int target_id)
+{
+ struct mob_data *md;
+ int i=0,pet_catch_rate=0;
+
+ nullpo_retr(1, sd);
+
+ md=(struct mob_data*)map_id2bl(target_id);
+ if(!md || md->bl.type != BL_MOB || md->bl.prev == NULL){
+ //Abort capture.
+ sd->catch_target_class = -1;
+ sd->itemid = sd->itemindex = -1;
+ return 1;
+ }
+
+ if (sd->menuskill_id != SA_TAMINGMONSTER)
+ { //Exploit?
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+
+ if (sd->menuskill_lv > 0)
+ { //Consume the pet lure [Skotlex]
+ i=pc_search_inventory(sd,sd->menuskill_lv);
+ if (i < 0)
+ { //they tried an exploit?
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+ //Delete the item
+ if (sd->itemid == sd->menuskill_lv)
+ sd->itemid = sd->itemindex = -1;
+ sd->menuskill_id = sd->menuskill_lv = 0;
+ pc_delitem(sd,i,1,0);
+ }
+
+ i = search_petDB_index(md->class_,PET_CLASS);
+ //catch_target_class == 0 is used for universal lures. [Skotlex]
+ //for now universal lures do not include bosses.
+ if (sd->catch_target_class == 0 && !(md->status.mode&MD_BOSS))
+ sd->catch_target_class = md->class_;
+ if(i < 0 || sd->catch_target_class != md->class_) {
+ clif_emotion(&md->bl, 7); //mob will do /ag if wrong lure is used on them.
+ clif_pet_rulet(sd,0);
+ sd->catch_target_class = -1;
+ return 1;
+ }
+
+ pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - md->status.hp*100/md->status.max_hp)/100;
+ if(pet_catch_rate < 1) pet_catch_rate = 1;
+ if(battle_config.pet_catch_rate != 100)
+ pet_catch_rate = (pet_catch_rate*battle_config.pet_catch_rate)/100;
+
+ if(rand()%10000 < pet_catch_rate) {
+ unit_remove_map(&md->bl,0);
+ status_kill(&md->bl);
+ clif_pet_rulet(sd,1);
+// if(battle_config.etc_log)
+// printf("rulet success %d\n",target_id);
+ intif_create_pet(sd->status.account_id,sd->status.char_id,pet_db[i].class_,mob_db(pet_db[i].class_)->lv,
+ pet_db[i].EggID,0,pet_db[i].intimate,100,0,1,pet_db[i].jname);
+ }
+ else
+ {
+ sd->catch_target_class = -1;
+ clif_pet_rulet(sd,0);
+ }
+
+ return 0;
+}
+
+int pet_get_egg(int account_id,int pet_id,int flag)
+{ //This function is invoked when a new pet has been created, and at no other time!
+ struct map_session_data *sd;
+ struct item tmp_item;
+ int i=0,ret=0;
+
+ if(flag)
+ return 0;
+
+ sd = map_id2sd(account_id);
+ if(sd == NULL)
+ return 0;
+
+ i = search_petDB_index(sd->catch_target_class,PET_CLASS);
+ sd->catch_target_class = -1;
+
+ if(i < 0) {
+ intif_delete_petdata(pet_id);
+ return 0;
+ }
+
+ malloc_set(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = pet_db[i].EggID;
+ tmp_item.identify = 1;
+ tmp_item.card[0] = CARD0_PET;
+ tmp_item.card[1] = GetWord(pet_id,0);
+ tmp_item.card[2] = GetWord(pet_id,1);
+ tmp_item.card[3] = 0; //New pets are not named.
+ if((ret = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,ret);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+
+ return 1;
+}
+
+static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd);
+static int pet_food(struct map_session_data *sd, struct pet_data *pd);
+static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap);
+
+int pet_menu(struct map_session_data *sd,int menunum)
+{
+ nullpo_retr(0, sd);
+ if (sd->pd == NULL)
+ return 1;
+
+ //You lost the pet already.
+ if(!sd->status.pet_id || sd->pd->pet.intimate <= 0)
+ return 1;
+
+ switch(menunum) {
+ case 0:
+ clif_send_petstatus(sd);
+ break;
+ case 1:
+ pet_food(sd, sd->pd);
+ break;
+ case 2:
+ pet_performance(sd, sd->pd);
+ break;
+ case 3:
+ pet_return_egg(sd, sd->pd);
+ break;
+ case 4:
+ pet_unequipitem(sd, sd->pd);
+ break;
+ }
+ return 0;
+}
+
+int pet_change_name(struct map_session_data *sd,char *name, int flag) //flag 0 = check name, 1 = good name
+{
+ int i;
+ struct pet_data *pd;
+ nullpo_retr(1, sd);
+
+ pd = sd->pd;
+ if((pd == NULL) || (pd->pet.rename_flag == 1 && !battle_config.pet_rename))
+ return 1;
+
+ for(i=0;i<NAME_LENGTH && name[i];i++){
+ if( !(name[i]&0xe0) || name[i]==0x7f)
+ return 1;
+ }
+
+ if (!flag)
+ return intif_rename_pet(sd, name);
+
+ pet_stop_walking(pd,1);
+
+ memcpy(pd->pet.name, name, NAME_LENGTH-1);
+
+ clif_charnameack (0,&pd->bl);
+ pd->pet.rename_flag = 1;
+ clif_pet_equip(pd);
+ clif_send_petstatus(sd);
+
+ return 0;
+}
+
+int pet_equipitem(struct map_session_data *sd,int index)
+{
+ struct pet_data *pd;
+ int nameid;
+
+ nullpo_retr(1, sd);
+ pd = sd->pd;
+ if (!pd) return 1;
+
+ nameid = sd->status.inventory[index].nameid;
+
+ if(pd->petDB->AcceID == 0 || nameid != pd->petDB->AcceID || pd->pet.equip != 0) {
+ clif_equipitemack(sd,0,0,0);
+ return 1;
+ }
+
+ pc_delitem(sd,index,1,0);
+ pd->pet.equip = nameid;
+ status_set_viewdata(&pd->bl, pd->pet.class_); //Updates view_data.
+ clif_pet_equip(pd);
+ if (battle_config.pet_equip_required)
+ { //Skotlex: start support timers if need
+ unsigned int tick = gettick();
+ if (pd->s_skill && pd->s_skill->timer == -1)
+ {
+ if (pd->s_skill->id)
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0);
+ else
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0);
+ }
+ if (pd->bonus && pd->bonus->timer == -1)
+ pd->bonus->timer=add_timer(tick+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0);
+ }
+
+ return 0;
+}
+
+static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd)
+{
+ struct item tmp_item;
+ int nameid,flag;
+
+ if(pd->pet.equip == 0)
+ return 1;
+
+ nameid = pd->pet.equip;
+ pd->pet.equip = 0;
+ status_set_viewdata(&pd->bl, pd->pet.class_);
+ clif_pet_equip(pd);
+ malloc_set(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = nameid;
+ tmp_item.identify = 1;
+ if((flag = pc_additem(sd,&tmp_item,1))) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0);
+ }
+ if (battle_config.pet_equip_required)
+ { //Skotlex: halt support timers if needed
+ if(pd->state.skillbonus) {
+ pd->state.skillbonus = 0;
+ status_calc_pc(sd,0);
+ }
+ if (pd->s_skill && pd->s_skill->timer != -1)
+ {
+ if (pd->s_skill->id)
+ delete_timer(pd->s_skill->timer, pet_skill_support_timer);
+ else
+ delete_timer(pd->s_skill->timer, pet_heal_timer);
+ pd->s_skill->timer = -1;
+ }
+ if (pd->bonus && pd->bonus->timer != -1)
+ {
+ delete_timer(pd->bonus->timer, pet_skill_bonus_timer);
+ pd->bonus->timer = -1;
+ }
+ }
+
+ return 0;
+}
+
+static int pet_food(struct map_session_data *sd, struct pet_data *pd)
+{
+ int i,k;
+
+ k=pd->petDB->FoodID;
+ i=pc_search_inventory(sd,k);
+ if(i < 0) {
+ clif_pet_food(sd,k,0);
+ return 1;
+ }
+ pc_delitem(sd,i,1,0);
+
+ if(pd->pet.hungry > 90)
+ pd->pet.intimate -= pd->petDB->r_full;
+ else {
+ if(battle_config.pet_friendly_rate != 100)
+ k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100;
+ else
+ k = pd->petDB->r_hungry;
+ if(pd->pet.hungry > 75) {
+ k = k >> 1;
+ if(k <= 0)
+ k = 1;
+ }
+ pd->pet.intimate += k;
+ }
+ if(pd->pet.intimate <= 0) {
+ pd->pet.intimate = 0;
+ pet_stop_attack(pd);
+ pd->status.speed = pd->db->status.speed;
+ }
+ else if(pd->pet.intimate > 1000)
+ pd->pet.intimate = 1000;
+ status_calc_pet(pd, 0);
+ pd->pet.hungry += pd->petDB->fullness;
+ if(pd->pet.hungry > 100)
+ pd->pet.hungry = 100;
+
+ clif_send_petdata(sd,2,pd->pet.hungry);
+ clif_send_petdata(sd,1,pd->pet.intimate);
+ clif_pet_food(sd,pd->petDB->FoodID,1);
+
+ return 0;
+}
+
+static int pet_randomwalk(struct pet_data *pd,unsigned int tick)
+{
+ const int retrycount=20;
+
+ nullpo_retr(0, pd);
+
+ Assert((pd->msd == 0) || (pd->msd->pd == pd));
+
+ if(DIFF_TICK(pd->next_walktime,tick) < 0 && unit_can_move(&pd->bl)) {
+ int i,x,y,c,d=12-pd->move_fail_count;
+ if(d<5) d=5;
+ for(i=0;i<retrycount;i++){
+ int r=rand();
+ x=pd->bl.x+r%(d*2+1)-d;
+ y=pd->bl.y+r/(d*2+1)%(d*2+1)-d;
+ if(map_getcell(pd->bl.m,x,y,CELL_CHKPASS) && unit_walktoxy(&pd->bl,x,y,0)){
+ pd->move_fail_count=0;
+ break;
+ }
+ if(i+1>=retrycount){
+ pd->move_fail_count++;
+ if(pd->move_fail_count>1000){
+ if(battle_config.error_log)
+ ShowWarning("PET cant move. hold position %d, class = %d\n",pd->bl.id,pd->pet.class_);
+ pd->move_fail_count=0;
+ pd->ud.canmove_tick = tick + 60000;
+ return 0;
+ }
+ }
+ }
+ for(i=c=0;i<pd->ud.walkpath.path_len;i++){
+ if(pd->ud.walkpath.path[i]&1)
+ c+=pd->status.speed*14/10;
+ else
+ c+=pd->status.speed;
+ }
+ pd->next_walktime = tick+rand()%3000+3000+c;
+
+ return 1;
+ }
+ return 0;
+}
+
+static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, unsigned int tick)
+{
+ struct block_list *target = NULL;
+
+ if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL)
+ return 0;
+
+ if(DIFF_TICK(tick,pd->last_thinktime) < MIN_PETTHINKTIME)
+ return 0;
+ pd->last_thinktime=tick;
+
+ if(pd->ud.attacktimer != -1 || pd->ud.skilltimer != -1 || pd->bl.m != sd->bl.m)
+ return 0;
+
+ if(pd->ud.walktimer != -1 && pd->ud.walkpath.path_pos <= 2)
+ return 0; //No thinking when you just started to walk.
+
+ if(pd->pet.intimate <= 0) {
+ //Pet should just... well, random walk.
+ pet_randomwalk(pd,tick);
+ return 0;
+ }
+
+ if (!check_distance_bl(&sd->bl, &pd->bl, pd->db->range3)) {
+ //Master too far, chase.
+ if(pd->target_id)
+ pet_unlocktarget(pd);
+ if(pd->ud.walktimer != -1 && pd->ud.target == sd->bl.id)
+ return 0; //Already walking to him
+ if (DIFF_TICK(tick, pd->ud.canmove_tick) < 0)
+ return 0; //Can't move yet.
+ pd->status.speed = (sd->battle_status.speed>>1);
+ if(pd->status.speed <= 0)
+ pd->status.speed = 1;
+ if (!unit_walktobl(&pd->bl, &sd->bl, 3, 0))
+ pet_randomwalk(pd,tick);
+ return 0;
+ }
+
+ //Return speed to normal.
+ if (pd->status.speed != pd->petDB->speed) {
+ if (pd->ud.walktimer != -1)
+ return 0; //Wait until the pet finishes walking back to master.
+ pd->status.speed = pd->petDB->speed;
+ }
+
+ if (pd->target_id) {
+ target= map_id2bl(pd->target_id);
+ if (!target || pd->bl.m != target->m || status_isdead(target) ||
+ !check_distance_bl(&pd->bl, target, pd->db->range3))
+ {
+ target = NULL;
+ pet_unlocktarget(pd);
+ }
+ }
+
+ if(!target && pd->loot && pd->loot->count < pd->loot->max && DIFF_TICK(tick,pd->ud.canact_tick)>0) {
+ //Use half the pet's range of sight.
+ map_foreachinrange(pet_ai_sub_hard_lootsearch,&pd->bl,
+ pd->db->range2/2, BL_ITEM,pd,&target);
+ }
+
+ if (!target) {
+ //Just walk around.
+ if (check_distance_bl(&sd->bl, &pd->bl, 3))
+ return 0; //Already next to master.
+
+ if(pd->ud.walktimer != -1 && check_distance_blxy(&sd->bl, pd->ud.to_x,pd->ud.to_y, 3))
+ return 0; //Already walking to him
+
+ pet_calc_pos(pd,sd->bl.x,sd->bl.y,sd->ud.dir);
+ if(!unit_walktoxy(&pd->bl,pd->ud.to_x,pd->ud.to_y,0))
+ pet_randomwalk(pd,tick);
+
+ return 0;
+ }
+
+ if(pd->ud.target == target->id &&
+ (pd->ud.attacktimer != -1 || pd->ud.walktimer != -1))
+ return 0; //Target already locked.
+
+ if (target->type != BL_ITEM)
+ { //enemy targetted
+ if(!battle_check_range(&pd->bl,target,pd->status.rhw.range))
+ { //Chase
+ if(!unit_walktobl(&pd->bl, target, pd->status.rhw.range, 2))
+ pet_unlocktarget(pd); //Unreachable target.
+ return 0;
+ }
+ //Continuous attack.
+ unit_attack(&pd->bl, pd->target_id, 1);
+ } else { //Item Targeted, attempt loot
+ if (!check_distance_bl(&pd->bl, target, 1))
+ { //Out of range
+ if(!unit_walktobl(&pd->bl, target, 0, 1)) //Unreachable target.
+ pet_unlocktarget(pd);
+ return 0;
+ } else{
+ struct flooritem_data *fitem = (struct flooritem_data *)target;
+ if(pd->loot->count < pd->loot->max){
+ memcpy(&pd->loot->item[pd->loot->count++],&fitem->item_data,sizeof(pd->loot->item[0]));
+ pd->loot->weight += itemdb_search(fitem->item_data.nameid)->weight*fitem->item_data.amount;
+ map_clearflooritem(target->id);
+ }
+ //Target is unlocked regardless of whether it was picked or not.
+ pet_unlocktarget(pd);
+ }
+ }
+ return 0;
+}
+
+static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap)
+{
+ unsigned int tick = va_arg(ap,unsigned int);
+ if(sd->status.pet_id && sd->pd)
+ pet_ai_sub_hard(sd->pd,sd,tick);
+
+ return 0;
+}
+
+static int pet_ai_hard(int tid,unsigned int tick,int id,int data)
+{
+ clif_foreachclient(pet_ai_sub_foreachclient,tick);
+
+ return 0;
+}
+
+static int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap)
+{
+ struct pet_data* pd;
+ struct flooritem_data *fitem = (struct flooritem_data *)bl;
+ struct block_list **target;
+ int sd_id =0;
+
+ pd=va_arg(ap,struct pet_data *);
+ target=va_arg(ap,struct block_list**);
+
+ sd_id = fitem->first_get_id;
+
+ if(sd_id && sd_id != pd->msd->bl.id)
+ return 0;
+
+ if(unit_can_reach_bl(&pd->bl,bl, pd->db->range2, 1, NULL, NULL) &&
+ ((*target) == NULL || //New target closer than previous one.
+ !check_distance_bl(&pd->bl, *target, distance_bl(&pd->bl, bl))))
+ {
+ (*target) = bl;
+ pd->target_id = bl->id;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int pet_delay_item_drop(int tid,unsigned int tick,int id,int data)
+{
+ struct item_drop_list *list;
+ struct item_drop *ditem, *ditem_prev;
+ list=(struct item_drop_list *)id;
+ ditem = list->item;
+ while (ditem) {
+ map_addflooritem(&ditem->item_data,ditem->item_data.amount,
+ list->m,list->x,list->y,
+ list->first_sd,list->second_sd,list->third_sd,0);
+ ditem_prev = ditem;
+ ditem = ditem->next;
+ ers_free(item_drop_ers, ditem_prev);
+ }
+ ers_free(item_drop_list_ers, list);
+ return 0;
+}
+
+int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd)
+{
+ int i,flag=0;
+ struct item_drop_list *dlist;
+ struct item_drop *ditem;
+ struct item *it;
+ if(!pd || !pd->loot || !pd->loot->count)
+ return 0;
+ dlist = ers_alloc(item_drop_list_ers, struct item_drop_list);
+ dlist->m = pd->bl.m;
+ dlist->x = pd->bl.x;
+ dlist->y = pd->bl.y;
+ dlist->first_sd = NULL;
+ dlist->second_sd = NULL;
+ dlist->third_sd = NULL;
+ dlist->item = NULL;
+
+ for(i=0;i<pd->loot->count;i++) {
+ it = &pd->loot->item[i];
+ if(sd){
+ if((flag = pc_additem(sd,it,it->amount))){
+ clif_additem(sd,0,0,flag);
+ ditem = ers_alloc(item_drop_ers, struct item_drop);
+ memcpy(&ditem->item_data, it, sizeof(struct item));
+ ditem->next = dlist->item;
+ dlist->item = ditem;
+ }
+ }
+ else {
+ ditem = ers_alloc(item_drop_ers, struct item_drop);
+ memcpy(&ditem->item_data, it, sizeof(struct item));
+ ditem->next = dlist->item;
+ dlist->item = ditem;
+ }
+ }
+ //The smart thing to do is use pd->loot->max (thanks for pointing it out, Shinomori)
+ malloc_set(pd->loot->item,0,pd->loot->max * sizeof(struct item));
+ pd->loot->count = 0;
+ pd->loot->weight = 0;
+ pd->ud.canact_tick = gettick()+10000; // 10*1000msの間拾わない
+
+ if (dlist->item)
+ add_timer(gettick()+540,pet_delay_item_drop,(int)dlist,0);
+ else
+ ers_free(item_drop_list_ers, dlist);
+ return 1;
+}
+
+/*==========================================
+ * pet bonus giving skills [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ struct pet_data *pd;
+ int bonus;
+ int timer = 0;
+
+ if(sd == NULL || sd->pd==NULL || sd->pd->bonus == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->bonus->timer != tid) {
+ if(battle_config.error_log)
+ {
+ ShowError("pet_skill_bonus_timer %d != %d\n",pd->bonus->timer,tid);
+ pd->bonus->timer = -1;
+ }
+ return 0;
+ }
+
+ // determine the time for the next timer
+ if (pd->state.skillbonus && pd->bonus->delay > 0) {
+ bonus = 0;
+ timer = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again
+ } else if (pd->pet.intimate) {
+ bonus = 1;
+ timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect
+ } else { //Lost pet...
+ pd->bonus->timer = -1;
+ return 0;
+ }
+
+ if (pd->state.skillbonus != bonus) {
+ pd->state.skillbonus = bonus;
+ status_calc_pc(sd, 0);
+ }
+ // wait for the next timer
+ pd->bonus->timer=add_timer(tick+timer,pet_skill_bonus_timer,sd->bl.id,0);
+ return 0;
+}
+
+/*==========================================
+ * pet recovery skills [Valaris] / Rewritten by [Skotlex]
+ *------------------------------------------
+ */
+int pet_recovery_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ struct pet_data *pd;
+
+ if(sd==NULL || sd->pd == NULL || sd->pd->recovery == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->recovery->timer != tid) {
+ if(battle_config.error_log)
+ ShowError("pet_recovery_timer %d != %d\n",pd->recovery->timer,tid);
+ return 0;
+ }
+
+ if(sd->sc.count && sd->sc.data[pd->recovery->type].timer != -1)
+ { //Display a heal animation?
+ //Detoxify is chosen for now.
+ clif_skill_nodamage(&pd->bl,&sd->bl,TF_DETOXIFY,1,1);
+ status_change_end(&sd->bl,pd->recovery->type,-1);
+ clif_emotion(&pd->bl, 33);
+ }
+
+ pd->recovery->timer = -1;
+
+ return 0;
+}
+
+int pet_heal_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ struct status_data *status;
+ struct pet_data *pd;
+ short rate = 100;
+
+ if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->s_skill->timer != tid) {
+ if(battle_config.error_log)
+ ShowError("pet_heal_timer %d != %d\n",pd->s_skill->timer,tid);
+ return 0;
+ }
+
+ status = status_get_status_data(&sd->bl);
+
+ if(pc_isdead(sd) ||
+ (rate = status->sp*100/status->max_sp) > pd->s_skill->sp ||
+ (rate = status->hp*100/status->max_hp) > pd->s_skill->hp ||
+ (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect
+ ) { //Wait (how long? 1 sec for every 10% of remaining)
+ pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0);
+ return 0;
+ }
+ pet_stop_attack(pd);
+ pet_stop_walking(pd,1);
+ clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1);
+ status_heal(&sd->bl, pd->s_skill->lv,0, 0);
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0);
+ return 0;
+}
+
+/*==========================================
+ * pet support skills [Skotlex]
+ *------------------------------------------
+ */
+int pet_skill_support_timer(int tid,unsigned int tick,int id,int data)
+{
+ struct map_session_data *sd=map_id2sd(id);
+ struct pet_data *pd;
+ struct status_data *status;
+ short rate = 100;
+ if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL)
+ return 1;
+
+ pd=sd->pd;
+
+ if(pd->s_skill->timer != tid) {
+ if(battle_config.error_log)
+ ShowError("pet_skill_support_timer %d != %d\n",pd->s_skill->timer,tid);
+ return 0;
+ }
+
+ status = status_get_status_data(&sd->bl);
+
+ if (DIFF_TICK(pd->ud.canact_tick, tick) > 0)
+ { //Wait until the pet can act again.
+ pd->s_skill->timer=add_timer(pd->ud.canact_tick,pet_skill_support_timer,sd->bl.id,0);
+ return 0;
+ }
+
+ if(pc_isdead(sd) ||
+ (rate = status->sp*100/status->max_sp) > pd->s_skill->sp ||
+ (rate = status->hp*100/status->max_hp) > pd->s_skill->hp ||
+ (rate = (pd->ud.skilltimer != -1)) //Another skill is in effect
+ ) { //Wait (how long? 1 sec for every 10% of remaining)
+ pd->s_skill->timer=add_timer(tick+(rate>10?rate:10)*100,pet_skill_support_timer,sd->bl.id,0);
+ return 0;
+ }
+
+ pet_stop_attack(pd);
+ pet_stop_walking(pd,1);
+
+ if (skill_get_inf(pd->s_skill->id) & INF_GROUND_SKILL)
+ unit_skilluse_pos(&pd->bl, sd->bl.x, sd->bl.y, pd->s_skill->id, pd->s_skill->lv);
+ else
+ unit_skilluse_id(&pd->bl, sd->bl.id, pd->s_skill->id, pd->s_skill->lv);
+
+ pd->s_skill->timer=add_timer(tick+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0);
+ return 0;
+}
+
+/*==========================================
+ *ペットデータ読み込み
+ *------------------------------------------
+ */
+int read_petdb()
+{
+ FILE *fp;
+ char line[1024];
+ int nameid,i,k;
+ int j=0;
+ int lines;
+ char *filename[]={"pet_db.txt","pet_db2.txt"};
+ char *str[32],*p,*np;
+
+
+//Remove any previous scripts in case reloaddb was invoked.
+ for(j =0; j < MAX_PET_DB; j++)
+ if (pet_db[j].script) {
+ aFree(pet_db[j].script);
+ pet_db[j].script = NULL;
+ }
+ j = 0;
+ malloc_set(pet_db,0,sizeof(pet_db));
+ for(i=0;i<2;i++){
+ sprintf(line, "%s/%s", db_path, filename[i]);
+ fp=fopen(line,"r");
+ if(fp==NULL){
+ if(i>0)
+ continue;
+ ShowError("can't read %s\n",line);
+ return -1;
+ }
+ lines = 0;
+ while(fgets(line,1020,fp) && j < MAX_PET_DB){
+
+ lines++;
+
+ if(line[0] == '/' && line[1] == '/')
+ continue;
+
+ for(k=0,p=line;k<20;k++){
+ if((np=strchr(p,','))!=NULL){
+ str[k]=p;
+ *np=0;
+ p=np+1;
+ } else {
+ str[k]=p;
+ p+=strlen(p);
+ }
+ }
+
+ nameid=atoi(str[0]);
+ if(nameid<=0)
+ continue;
+
+ if (!mobdb_checkid(nameid)) {
+ ShowWarning("pet_db reading: Invalid mob-class %d, pet not read.\n", nameid);
+ continue;
+ }
+
+ pet_db[j].class_ = nameid;
+ memcpy(pet_db[j].name,str[1],NAME_LENGTH-1);
+ memcpy(pet_db[j].jname,str[2],NAME_LENGTH-1);
+ pet_db[j].itemID=atoi(str[3]);
+ pet_db[j].EggID=atoi(str[4]);
+ pet_db[j].AcceID=atoi(str[5]);
+ pet_db[j].FoodID=atoi(str[6]);
+ pet_db[j].fullness=atoi(str[7]);
+ pet_db[j].hungry_delay=atoi(str[8])*1000;
+ pet_db[j].r_hungry=atoi(str[9]);
+ if(pet_db[j].r_hungry <= 0)
+ pet_db[j].r_hungry=1;
+ pet_db[j].r_full=atoi(str[10]);
+ pet_db[j].intimate=atoi(str[11]);
+ pet_db[j].die=atoi(str[12]);
+ pet_db[j].capture=atoi(str[13]);
+ pet_db[j].speed=atoi(str[14]);
+ pet_db[j].s_perfor=(char)atoi(str[15]);
+ pet_db[j].talk_convert_class=atoi(str[16]);
+ pet_db[j].attack_rate=atoi(str[17]);
+ pet_db[j].defence_attack_rate=atoi(str[18]);
+ pet_db[j].change_target_rate=atoi(str[19]);
+ pet_db[j].script = NULL;
+ if((np=strchr(p,'{'))==NULL)
+ continue;
+ pet_db[j].script = parse_script((unsigned char *) np, filename[i], lines);
+ j++;
+ }
+ if (j >= MAX_PET_DB)
+ ShowWarning("read_petdb: Reached max number of pets [%d]. Remaining pets were not read.\n ", MAX_PET_DB);
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' pets in '"CL_WHITE"%s"CL_RESET"'.\n",j,filename[i]);
+ }
+ return 0;
+}
+
+/*==========================================
+ * スキル関係初期化処理
+ *------------------------------------------
+ */
+int do_init_pet(void)
+{
+ malloc_set(pet_db,0,sizeof(pet_db));
+ read_petdb();
+
+ item_drop_ers = ers_new((uint32)sizeof(struct item_drop));
+ item_drop_list_ers = ers_new((uint32)sizeof(struct item_drop_list));
+
+ add_timer_func_list(pet_hungry,"pet_hungry");
+ add_timer_func_list(pet_ai_hard,"pet_ai_hard");
+ add_timer_func_list(pet_skill_bonus_timer,"pet_skill_bonus_timer"); // [Valaris]
+ add_timer_func_list(pet_delay_item_drop,"pet_delay_item_drop");
+ add_timer_func_list(pet_skill_support_timer, "pet_skill_support_timer"); // [Skotlex]
+ add_timer_func_list(pet_recovery_timer,"pet_recovery_timer"); // [Valaris]
+ add_timer_func_list(pet_heal_timer,"pet_heal_timer"); // [Valaris]
+ add_timer_interval(gettick()+MIN_PETTHINKTIME,pet_ai_hard,0,0,MIN_PETTHINKTIME);
+
+ return 0;
+}
+
+int do_final_pet(void) {
+ int i;
+ for(i = 0;i < MAX_PET_DB; i++) {
+ if(pet_db[i].script) {
+ script_free_code(pet_db[i].script);
+ pet_db[i].script = NULL;
+ }
+ }
+ ers_destroy(item_drop_ers);
+ ers_destroy(item_drop_list_ers);
+ return 0;
+}
diff --git a/src/map/pet.h b/src/map/pet.h
index 89ff3dbab..71c1684e5 100644
--- a/src/map/pet.h
+++ b/src/map/pet.h
@@ -1,68 +1,68 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _PET_H_
-#define _PET_H_
-
-#define MAX_PET_DB 300
-#define MAX_PETLOOT_SIZE 30 // [Valaris] - Changed to MAX_PETLOOT_SIZE [Skotlex]
-
-struct pet_db {
- short class_;
- char name[NAME_LENGTH],jname[NAME_LENGTH];
- short itemID;
- short EggID;
- short AcceID;
- short FoodID;
- int fullness;
- int hungry_delay;
- int r_hungry;
- int r_full;
- int intimate;
- int die;
- int capture;
- int speed;
- char s_perfor;
- int talk_convert_class;
- int attack_rate;
- int defence_attack_rate;
- int change_target_rate;
- struct script_code *script;
-};
-extern struct pet_db pet_db[MAX_PET_DB];
-
-enum { PET_CLASS,PET_CATCH,PET_EGG,PET_EQUIP,PET_FOOD };
-
-int pet_create_egg(struct map_session_data *sd, int item_id);
-int pet_hungry_val(struct pet_data *pd);
-int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type);
-int pet_unlocktarget(struct pet_data *pd);
-int pet_sc_check(struct map_session_data *sd, int type); //Skotlex
-int search_petDB_index(int key,int type);
-int pet_hungry_timer_delete(struct pet_data *pd);
-int pet_data_init(struct map_session_data *sd, struct s_pet *pet);
-int pet_birth_process(struct map_session_data *sd, struct s_pet *pet);
-int pet_recv_petdata(int account_id,struct s_pet *p,int flag);
-int pet_select_egg(struct map_session_data *sd,short egg_index);
-int pet_catch_process1(struct map_session_data *sd,int target_class);
-int pet_catch_process2(struct map_session_data *sd,int target_id);
-int pet_get_egg(int account_id,int pet_id,int flag);
-int pet_menu(struct map_session_data *sd,int menunum);
-int pet_change_name(struct map_session_data *sd,char *name,int flag); //flag 0 = check name, 1 = good name
-int pet_equipitem(struct map_session_data *sd,int index);
-int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd);
-int pet_attackskill(struct pet_data *pd, int target_id);
-int pet_skill_support_timer(int tid, unsigned int tick, int id, int data); // [Skotlex]
-int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
-int pet_recovery_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
-int pet_heal_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
-
-#define pet_stop_walking(pd, type) { if((pd)->ud.walktimer != -1) unit_stop_walking(&(pd)->bl, type); }
-#define pet_stop_attack(pd) { if((pd)->ud.attacktimer != -1) unit_stop_attack(&(pd)->bl); }
-
-int read_petdb(void);
-int do_init_pet(void);
-int do_final_pet(void);
-
-#endif
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _PET_H_
+#define _PET_H_
+
+#define MAX_PET_DB 300
+#define MAX_PETLOOT_SIZE 30 // [Valaris] - Changed to MAX_PETLOOT_SIZE [Skotlex]
+
+struct pet_db {
+ short class_;
+ char name[NAME_LENGTH],jname[NAME_LENGTH];
+ short itemID;
+ short EggID;
+ short AcceID;
+ short FoodID;
+ int fullness;
+ int hungry_delay;
+ int r_hungry;
+ int r_full;
+ int intimate;
+ int die;
+ int capture;
+ int speed;
+ char s_perfor;
+ int talk_convert_class;
+ int attack_rate;
+ int defence_attack_rate;
+ int change_target_rate;
+ struct script_code *script;
+};
+extern struct pet_db pet_db[MAX_PET_DB];
+
+enum { PET_CLASS,PET_CATCH,PET_EGG,PET_EQUIP,PET_FOOD };
+
+int pet_create_egg(struct map_session_data *sd, int item_id);
+int pet_hungry_val(struct pet_data *pd);
+int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type);
+int pet_unlocktarget(struct pet_data *pd);
+int pet_sc_check(struct map_session_data *sd, int type); //Skotlex
+int search_petDB_index(int key,int type);
+int pet_hungry_timer_delete(struct pet_data *pd);
+int pet_data_init(struct map_session_data *sd, struct s_pet *pet);
+int pet_birth_process(struct map_session_data *sd, struct s_pet *pet);
+int pet_recv_petdata(int account_id,struct s_pet *p,int flag);
+int pet_select_egg(struct map_session_data *sd,short egg_index);
+int pet_catch_process1(struct map_session_data *sd,int target_class);
+int pet_catch_process2(struct map_session_data *sd,int target_id);
+int pet_get_egg(int account_id,int pet_id,int flag);
+int pet_menu(struct map_session_data *sd,int menunum);
+int pet_change_name(struct map_session_data *sd,char *name,int flag); //flag 0 = check name, 1 = good name
+int pet_equipitem(struct map_session_data *sd,int index);
+int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd);
+int pet_attackskill(struct pet_data *pd, int target_id);
+int pet_skill_support_timer(int tid, unsigned int tick, int id, int data); // [Skotlex]
+int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_recovery_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+int pet_heal_timer(int tid,unsigned int tick,int id,int data); // [Valaris]
+
+#define pet_stop_walking(pd, type) { if((pd)->ud.walktimer != -1) unit_stop_walking(&(pd)->bl, type); }
+#define pet_stop_attack(pd) { if((pd)->ud.attacktimer != -1) unit_stop_attack(&(pd)->bl); }
+
+int read_petdb(void);
+int do_init_pet(void);
+int do_final_pet(void);
+
+#endif
+
diff --git a/src/map/script.h b/src/map/script.h
index 9fcd2f739..60fc3e990 100644
--- a/src/map/script.h
+++ b/src/map/script.h
@@ -1,91 +1,91 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _SCRIPT_H_
-#define _SCRIPT_H_
-
-extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
-extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp;
-extern int potion_target;
-
-extern struct Script_Config {
- unsigned verbose_mode : 1;
- unsigned warn_func_no_comma : 1;
- unsigned warn_cmd_no_comma : 1;
- unsigned warn_func_mismatch_paramnum : 1;
- unsigned warn_cmd_mismatch_paramnum : 1;
- int check_cmdcount;
- int check_gotocount;
-
- unsigned event_script_type : 1;
- unsigned event_requires_trigger : 1;
- char die_event_name[NAME_LENGTH];
- char kill_pc_event_name[NAME_LENGTH];
- char kill_mob_event_name[NAME_LENGTH];
- char login_event_name[NAME_LENGTH];
- char logout_event_name[NAME_LENGTH];
- char loadmap_event_name[NAME_LENGTH];
- char baselvup_event_name[NAME_LENGTH];
- char joblvup_event_name[NAME_LENGTH];
-} script_config;
-
-struct script_data {
- int type;
- union {
- int num;
- char *str;
- } u;
- struct linkdb_node** ref;
-};
-
-// Moved defsp from script_state to script_stack since
-// it must be saved when script state is RERUNLINE. [Eoe / jA 1094]
-struct script_code {
- int script_size;
- unsigned char* script_buf;
- struct linkdb_node* script_vars;
-};
-
-struct script_state {
- struct script_stack {
- int sp,sp_max,defsp;
- struct script_data *stack_data;
- struct linkdb_node **var_function; // 関数依存変数
- } *stack;
- int start,end;
- int pos,state;
- int rid,oid;
- struct script_code *script, *scriptroot;
- struct sleep_data {
- int tick,timer,charid;
- } sleep;
-};
-
-struct script_code* parse_script(unsigned char *,const char*,int);
-void run_script_sub(struct script_code *rootscript,int pos,int rid,int oid, char* file, int lineno);
-void run_script(struct script_code*,int,int,int);
-
-int set_var(struct map_session_data *sd, char *name, void *val);
-int conv_num(struct script_state *st,struct script_data *data);
-char* conv_str(struct script_state *st,struct script_data *data);
-void setd_sub(struct script_state *st, struct map_session_data *sd, char *varname, int elem, void *value, struct linkdb_node **ref);
-int run_script_timer(int tid, unsigned int tick, int id, int data);
-void run_script_main(struct script_state *st);
-
-struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n);
-void script_free_stack(struct script_stack*);
-void script_free_code(struct script_code* code);
-
-struct dbt* script_get_label_db(void);
-struct dbt* script_get_userfunc_db(void);
-
-int script_config_read(char *cfgName);
-int do_init_script(void);
-int do_final_script(void);
-int add_str(const unsigned char *p);
-int script_reload(void);
-
-extern char mapreg_txt[];
-
-#endif
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SCRIPT_H_
+#define _SCRIPT_H_
+
+extern int potion_flag; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex]
+extern int potion_hp, potion_per_hp, potion_sp, potion_per_sp;
+extern int potion_target;
+
+extern struct Script_Config {
+ unsigned verbose_mode : 1;
+ unsigned warn_func_no_comma : 1;
+ unsigned warn_cmd_no_comma : 1;
+ unsigned warn_func_mismatch_paramnum : 1;
+ unsigned warn_cmd_mismatch_paramnum : 1;
+ int check_cmdcount;
+ int check_gotocount;
+
+ unsigned event_script_type : 1;
+ unsigned event_requires_trigger : 1;
+ char die_event_name[NAME_LENGTH];
+ char kill_pc_event_name[NAME_LENGTH];
+ char kill_mob_event_name[NAME_LENGTH];
+ char login_event_name[NAME_LENGTH];
+ char logout_event_name[NAME_LENGTH];
+ char loadmap_event_name[NAME_LENGTH];
+ char baselvup_event_name[NAME_LENGTH];
+ char joblvup_event_name[NAME_LENGTH];
+} script_config;
+
+struct script_data {
+ int type;
+ union {
+ int num;
+ char *str;
+ } u;
+ struct linkdb_node** ref;
+};
+
+// Moved defsp from script_state to script_stack since
+// it must be saved when script state is RERUNLINE. [Eoe / jA 1094]
+struct script_code {
+ int script_size;
+ unsigned char* script_buf;
+ struct linkdb_node* script_vars;
+};
+
+struct script_state {
+ struct script_stack {
+ int sp,sp_max,defsp;
+ struct script_data *stack_data;
+ struct linkdb_node **var_function; // 関数依存変数
+ } *stack;
+ int start,end;
+ int pos,state;
+ int rid,oid;
+ struct script_code *script, *scriptroot;
+ struct sleep_data {
+ int tick,timer,charid;
+ } sleep;
+};
+
+struct script_code* parse_script(unsigned char *,const char*,int);
+void run_script_sub(struct script_code *rootscript,int pos,int rid,int oid, char* file, int lineno);
+void run_script(struct script_code*,int,int,int);
+
+int set_var(struct map_session_data *sd, char *name, void *val);
+int conv_num(struct script_state *st,struct script_data *data);
+char* conv_str(struct script_state *st,struct script_data *data);
+void setd_sub(struct script_state *st, struct map_session_data *sd, char *varname, int elem, void *value, struct linkdb_node **ref);
+int run_script_timer(int tid, unsigned int tick, int id, int data);
+void run_script_main(struct script_state *st);
+
+struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n);
+void script_free_stack(struct script_stack*);
+void script_free_code(struct script_code* code);
+
+struct dbt* script_get_label_db(void);
+struct dbt* script_get_userfunc_db(void);
+
+int script_config_read(char *cfgName);
+int do_init_script(void);
+int do_final_script(void);
+int add_str(const unsigned char *p);
+int script_reload(void);
+
+extern char mapreg_txt[];
+
+#endif
+
diff --git a/src/map/skill.h b/src/map/skill.h
index 952448d74..973648499 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -1,958 +1,958 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _SKILL_H_
-#define _SKILL_H_
-
-#include "map.h"
-
-#define MAX_SKILL_DB 1100
-#define MAX_SKILL_PRODUCE_DB 150
-#define MAX_PRODUCE_RESOURCE 12
-#define MAX_SKILL_ARROW_DB 150
-#define MAX_SKILL_ABRA_DB 350
-
-//Constants to identify the skill's inf value:
-#define INF_ATTACK_SKILL 1
-#define INF_GROUND_SKILL 2
-// Skills casted on self where target is automatically chosen:
-#define INF_SELF_SKILL 4
-#define INF_SUPPORT_SKILL 16
-#define INF_TARGET_TRAP 32
-
-//Constants to identify a skill's nk value.
-//The NK value applies only to non INF_GROUND_SKILL skills.
-#define NK_NO_DAMAGE 0x1
-#define NK_SPLASH (0x2|0x4) // 0x4 = splash & split
-#define NK_SPLASHSPLIT 0x4
-//A skill with 3 would be no damage + splash: area of effect.
-//Constants to identify a skill's inf2 value.
-#define INF2_QUEST_SKILL 1
-//NPC skills are those that players can't have in their skill tree.
-#define INF2_NPC_SKILL 2
-#define INF2_WEDDING_SKILL 4
-#define INF2_SPIRIT_SKILL 8
-#define INF2_GUILD_SKILL 16
-#define INF2_SONG_DANCE 32
-#define INF2_ENSEMBLE_SKILL 64
-#define INF2_TRAP 128
-//Refers to ground placed skills that will target the caster as well (like Grandcross)
-#define INF2_TARGET_SELF 256
-#define INF2_NO_TARGET_SELF 512
-#define INF2_PARTY_ONLY 1024
-#define INF2_GUILD_ONLY 2048
-//For Party/Guild only skills that can ALSO be used on enemies.
-#define INF2_ALLOW_ENEMY 4096
-
-//Walk intervals at which chase-skills are attempted to be triggered.
-#define WALK_SKILL_INTERVAL 5
-
-// To be passed to skill_attack, whether the skill damage should disable level or animation. [Skotlex]
-#define SD_LEVEL 0x1000
-#define SD_ANIMATION 0x2000
-
-// スキルデ?タベ?ス
-struct skill_db {
- char *name;
- char *desc;
- int range[MAX_SKILL_LEVEL],hit,inf,pl,nk,splash[MAX_SKILL_LEVEL],max;
- int num[MAX_SKILL_LEVEL];
- int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL];
- int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL];
- int castcancel,cast_def_rate;
- int inf2,maxcount,skill_type;
- int blewcount[MAX_SKILL_LEVEL];
- int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL];
- int weapon,ammo,ammo_qty[MAX_SKILL_LEVEL],state,spiritball[MAX_SKILL_LEVEL];
- int itemid[10],amount[10];
- int castnodex[MAX_SKILL_LEVEL];
- int delaynoagi[MAX_SKILL_LEVEL];
- int nocast;
- int unit_id[2];
- int unit_layout_type[MAX_SKILL_LEVEL];
- int unit_range[MAX_SKILL_LEVEL];
- int unit_interval;
- int unit_target;
- int unit_flag;
-};
-extern struct skill_db skill_db[MAX_SKILL_DB];
-
-struct skill_name_db {
- int id; // skill id
- char *name; // search strings
- char *desc; // description that shows up for search's
-};
-
-#define MAX_SKILL_UNIT_LAYOUT 50
-#define MAX_SQUARE_LAYOUT 5 // 11*11のユニット配置が最大
-#define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1))
-struct skill_unit_layout {
- int count;
- int dx[MAX_SKILL_UNIT_COUNT];
- int dy[MAX_SKILL_UNIT_COUNT];
-};
-
-enum {
- UF_DEFNOTENEMY = 0x0001, // defnotenemy 設定でBCT_NOENEMYに切り替え
- UF_NOREITERATION = 0x0002, // 重複置き禁止
- UF_NOFOOTSET = 0x0004, // 足元置き禁止
- UF_NOOVERLAP = 0x0008, // ユニット効果が重複しない
- UF_NOPC = 0x0010, //May not target players
- UF_NOMOB = 0x0020, //May not target mobs
- UF_SKILL = 0x0080, //May target skills
- UF_DANCE = 0x0100, //Dance
- UF_ENSEMBLE = 0x0200, //Duet
- UF_SONG = 0x0400, //Song
- UF_DUALMODE = 0x0800, //Spells should trigger both ontimer and onplace/onout/onleft effects.
-};
-
-// アイテム作成デ?タベ?ス
-struct skill_produce_db {
- int nameid, trigger;
- int req_skill,req_skill_lv,itemlv;
- int mat_id[MAX_PRODUCE_RESOURCE],mat_amount[MAX_PRODUCE_RESOURCE];
-};
-extern struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
-
-// 矢作成デ?タベ?ス
-struct skill_arrow_db {
- int nameid, trigger;
- int cre_id[5],cre_amount[5];
-};
-extern struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
-
-// アブラカダブラデ?タベ?ス
-struct skill_abra_db {
- int nameid;
- int req_lv;
- int per;
-};
-extern struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
-
-extern int enchant_eff[5];
-extern int deluge_eff[5];
-
-struct block_list;
-struct map_session_data;
-struct skill_unit;
-struct skill_unit_group;
-
-int do_init_skill(void);
-int do_final_skill(void);
-
-//Returns the cast type of the skill: ground cast, castend damage, castend no damage
-enum { CAST_GROUND, CAST_DAMAGE, CAST_NODAMAGE };
-int skill_get_casttype(int id); //[Skotlex]
-// スキルデ?タベ?スへのアクセサ
-//
-int skill_get_type( int id );
-int skill_get_hit( int id );
-int skill_get_inf( int id );
-int skill_get_pl( int id );
-int skill_get_nk( int id );
-int skill_get_max( int id );
-int skill_get_range( int id , int lv );
-int skill_get_range2(struct block_list *bl, int id, int lv);
-int skill_get_splash( int id , int lv );
-int skill_get_hp( int id ,int lv );
-int skill_get_mhp( int id ,int lv );
-int skill_get_sp( int id ,int lv );
-int skill_get_state(int id);
-int skill_get_zeny( int id ,int lv );
-int skill_get_num( int id ,int lv );
-int skill_get_cast( int id ,int lv );
-int skill_get_delay( int id ,int lv );
-int skill_get_walkdelay( int id ,int lv );
-int skill_get_time( int id ,int lv );
-int skill_get_time2( int id ,int lv );
-int skill_get_castnodex( int id ,int lv );
-int skill_get_castdef( int id );
-int skill_get_weapontype( int id );
-int skill_get_ammotype( int id );
-int skill_get_ammo_qty( int id, int lv );
-int skill_get_nocast( int id );
-int skill_get_unit_id(int id,int flag);
-int skill_get_inf2( int id );
-int skill_get_castcancel( int id );
-int skill_get_maxcount( int id );
-int skill_get_blewcount( int id ,int lv );
-int skill_get_unit_flag( int id );
-int skill_get_unit_target( int id );
-int skill_tree_get_max( int id, int b_class ); // Celest
-const char* skill_get_name( int id ); // [Skotlex]
-
-int skill_isammotype(TBL_PC *sd, int skill);
-int skill_castend_id( int tid, unsigned int tick, int id,int data );
-int skill_castend_pos( int tid, unsigned int tick, int id,int data );
-int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map);
-
-int skill_cleartimerskill(struct block_list *src);
-int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag);
-
-// 追加?果
-int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
-int skill_counter_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
-int skill_blown( struct block_list *src, struct block_list *target,int count);
-int skill_break_equip(struct block_list *bl, unsigned short where, int rate, int flag);
-// ユニットスキル
-struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag);
-struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2);
-int skill_delunit(struct skill_unit *unit, int flag);
-struct skill_unit_group *skill_initunitgroup(struct block_list *src,
- int count,int skillid,int skilllv,int unit_id, int limit, int interval);
-int skill_delunitgroup(struct block_list *src, struct skill_unit_group *group, int flag);
-int skill_clear_unitgroup(struct block_list *src);
-int skill_clear_group(struct block_list *bl, int flag);
-
-int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,
- int damage,unsigned int tick);
-
-int skill_castfix( struct block_list *bl, int skill_id, int skill_lv);
-int skill_castfix_sc( struct block_list *bl, int time);
-int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv);
-int skill_check_condition( struct map_session_data *sd,int skill, int lv, int type);
-int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag);
-// -- moonsoul (added skill_check_unit_cell)
-int skill_check_unit_cell(int skillid,int m,int x,int y,int unit_id);
-int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range);
-int skill_unit_move(struct block_list *bl,unsigned int tick,int flag);
-int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy);
-void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag);
-
-struct skill_unit_group *skill_check_dancing( struct block_list *src );
-void skill_stop_dancing(struct block_list *src);
-
-// Guild skills [celest]
-int skill_guildaura_sub (struct block_list *bl,va_list ap);
-
-// 詠唱キャンセル
-int skill_castcancel(struct block_list *bl,int type);
-
-int skill_sit (struct map_session_data *sd, int type);
-void skill_brandishspear_first(struct square *tc,int dir,int x,int y);
-void skill_brandishspear_dir(struct square *tc,int dir,int are);
-void skill_repairweapon(struct map_session_data *sd, int idx);
-void skill_identify(struct map_session_data *sd,int idx);
-void skill_weaponrefine(struct map_session_data *sd,int idx); // [Celest]
-int skill_autospell(struct map_session_data *md,int skillid);
-
-int skill_calc_heal(struct block_list *bl, int skill_lv);
-
-int skill_check_cloaking(struct block_list *bl, struct status_change *sc);
-
-// ステ?タス異常
-int skill_enchant_elemental_end(struct block_list *bl, int type);
-int skillnotok(int skillid, struct map_session_data *sd);
-int skillnotok_hom (int skillid, struct homun_data *hd) ; //[orn]
-int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap); //[orn]
-
-// アイテム作成
-int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty);
-int skill_produce_mix( struct map_session_data *sd,
- int skill_id, int nameid, int slot1, int slot2, int slot3, int qty );
-
-int skill_arrow_create( struct map_session_data *sd,int nameid);
-
-// mobスキルのため
-int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
-int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
-int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag);
-int skill_blockpc_start (struct map_session_data*,int,int); // [celest]
-int skill_blockmerc_start (struct homun_data*,int,int); //[orn]
-
-// スキル攻?一括?理
-int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,
- struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
-
-void skill_reload(void);
-
-enum {
- ST_NONE,ST_HIDING,ST_CLOAKING,ST_HIDDEN,ST_RIDING,ST_FALCON,ST_CART,ST_SHIELD,ST_SIGHT,ST_EXPLOSIONSPIRITS,ST_CARTBOOST,
- ST_RECOV_WEIGHT_RATE,ST_MOVE_ENABLE,ST_WATER,
-};
-
-enum {
- NV_BASIC = 1,
-
- SM_SWORD,
- SM_TWOHAND,
- SM_RECOVERY,
- SM_BASH,
- SM_PROVOKE,
- SM_MAGNUM,
- SM_ENDURE,
-
- MG_SRECOVERY,
- MG_SIGHT,
- MG_NAPALMBEAT,
- MG_SAFETYWALL,
- MG_SOULSTRIKE,
- MG_COLDBOLT,
- MG_FROSTDIVER,
- MG_STONECURSE,
- MG_FIREBALL,
- MG_FIREWALL,
- MG_FIREBOLT,
- MG_LIGHTNINGBOLT,
- MG_THUNDERSTORM,
-
- AL_DP,
- AL_DEMONBANE,
- AL_RUWACH,
- AL_PNEUMA,
- AL_TELEPORT,
- AL_WARP,
- AL_HEAL,
- AL_INCAGI,
- AL_DECAGI,
- AL_HOLYWATER,
- AL_CRUCIS,
- AL_ANGELUS,
- AL_BLESSING,
- AL_CURE,
-
- MC_INCCARRY,
- MC_DISCOUNT,
- MC_OVERCHARGE,
- MC_PUSHCART,
- MC_IDENTIFY,
- MC_VENDING,
- MC_MAMMONITE,
-
- AC_OWL,
- AC_VULTURE,
- AC_CONCENTRATION,
- AC_DOUBLE,
- AC_SHOWER,
-
- TF_DOUBLE,
- TF_MISS,
- TF_STEAL,
- TF_HIDING,
- TF_POISON,
- TF_DETOXIFY,
-
- ALL_RESURRECTION,
-
- KN_SPEARMASTERY,
- KN_PIERCE,
- KN_BRANDISHSPEAR,
- KN_SPEARSTAB,
- KN_SPEARBOOMERANG,
- KN_TWOHANDQUICKEN,
- KN_AUTOCOUNTER,
- KN_BOWLINGBASH,
- KN_RIDING,
- KN_CAVALIERMASTERY,
-
- PR_MACEMASTERY,
- PR_IMPOSITIO,
- PR_SUFFRAGIUM,
- PR_ASPERSIO,
- PR_BENEDICTIO,
- PR_SANCTUARY,
- PR_SLOWPOISON,
- PR_STRECOVERY,
- PR_KYRIE,
- PR_MAGNIFICAT,
- PR_GLORIA,
- PR_LEXDIVINA,
- PR_TURNUNDEAD,
- PR_LEXAETERNA,
- PR_MAGNUS,
-
- WZ_FIREPILLAR,
- WZ_SIGHTRASHER,
- WZ_FIREIVY,
- WZ_METEOR,
- WZ_JUPITEL,
- WZ_VERMILION,
- WZ_WATERBALL,
- WZ_ICEWALL,
- WZ_FROSTNOVA,
- WZ_STORMGUST,
- WZ_EARTHSPIKE,
- WZ_HEAVENDRIVE,
- WZ_QUAGMIRE,
- WZ_ESTIMATION,
-
- BS_IRON,
- BS_STEEL,
- BS_ENCHANTEDSTONE,
- BS_ORIDEOCON,
- BS_DAGGER,
- BS_SWORD,
- BS_TWOHANDSWORD,
- BS_AXE,
- BS_MACE,
- BS_KNUCKLE,
- BS_SPEAR,
- BS_HILTBINDING,
- BS_FINDINGORE,
- BS_WEAPONRESEARCH,
- BS_REPAIRWEAPON,
- BS_SKINTEMPER,
- BS_HAMMERFALL,
- BS_ADRENALINE,
- BS_WEAPONPERFECT,
- BS_OVERTHRUST,
- BS_MAXIMIZE,
-
- HT_SKIDTRAP,
- HT_LANDMINE,
- HT_ANKLESNARE,
- HT_SHOCKWAVE,
- HT_SANDMAN,
- HT_FLASHER,
- HT_FREEZINGTRAP,
- HT_BLASTMINE,
- HT_CLAYMORETRAP,
- HT_REMOVETRAP,
- HT_TALKIEBOX,
- HT_BEASTBANE,
- HT_FALCON,
- HT_STEELCROW,
- HT_BLITZBEAT,
- HT_DETECTING,
- HT_SPRINGTRAP,
-
- AS_RIGHT,
- AS_LEFT,
- AS_KATAR,
- AS_CLOAKING,
- AS_SONICBLOW,
- AS_GRIMTOOTH,
- AS_ENCHANTPOISON,
- AS_POISONREACT,
- AS_VENOMDUST,
- AS_SPLASHER,
-
- NV_FIRSTAID,
- NV_TRICKDEAD,
- SM_MOVINGRECOVERY,
- SM_FATALBLOW,
- SM_AUTOBERSERK,
- AC_MAKINGARROW,
- AC_CHARGEARROW,
- TF_SPRINKLESAND,
- TF_BACKSLIDING,
- TF_PICKSTONE,
- TF_THROWSTONE,
- MC_CARTREVOLUTION,
- MC_CHANGECART,
- MC_LOUD,
- AL_HOLYLIGHT,
- MG_ENERGYCOAT,
-
- NPC_PIERCINGATT,
- NPC_MENTALBREAKER,
- NPC_RANGEATTACK,
- NPC_ATTRICHANGE,
- NPC_CHANGEWATER,
- NPC_CHANGEGROUND,
- NPC_CHANGEFIRE,
- NPC_CHANGEWIND,
- NPC_CHANGEPOISON,
- NPC_CHANGEHOLY,
- NPC_CHANGEDARKNESS,
- NPC_CHANGETELEKINESIS,
- NPC_CRITICALSLASH,
- NPC_COMBOATTACK,
- NPC_GUIDEDATTACK,
- NPC_SELFDESTRUCTION,
- NPC_SPLASHATTACK,
- NPC_SUICIDE,
- NPC_POISON,
- NPC_BLINDATTACK,
- NPC_SILENCEATTACK,
- NPC_STUNATTACK,
- NPC_PETRIFYATTACK,
- NPC_CURSEATTACK,
- NPC_SLEEPATTACK,
- NPC_RANDOMATTACK,
- NPC_WATERATTACK,
- NPC_GROUNDATTACK,
- NPC_FIREATTACK,
- NPC_WINDATTACK,
- NPC_POISONATTACK,
- NPC_HOLYATTACK,
- NPC_DARKNESSATTACK,
- NPC_TELEKINESISATTACK,
- NPC_MAGICALATTACK,
- NPC_METAMORPHOSIS,
- NPC_PROVOCATION,
- NPC_SMOKING,
- NPC_SUMMONSLAVE,
- NPC_EMOTION,
- NPC_TRANSFORMATION,
- NPC_BLOODDRAIN,
- NPC_ENERGYDRAIN,
- NPC_KEEPING,
- NPC_DARKBREATH,
- NPC_DARKBLESSING,
- NPC_BARRIER,
- NPC_DEFENDER,
- NPC_LICK,
- NPC_HALLUCINATION,
- NPC_REBIRTH,
- NPC_SUMMONMONSTER,
-
- RG_SNATCHER,
- RG_STEALCOIN,
- RG_BACKSTAP,
- RG_TUNNELDRIVE,
- RG_RAID,
- RG_STRIPWEAPON,
- RG_STRIPSHIELD,
- RG_STRIPARMOR,
- RG_STRIPHELM,
- RG_INTIMIDATE,
- RG_GRAFFITI,
- RG_FLAGGRAFFITI,
- RG_CLEANER,
- RG_GANGSTER,
- RG_COMPULSION,
- RG_PLAGIARISM,
-
- AM_AXEMASTERY,
- AM_LEARNINGPOTION,
- AM_PHARMACY,
- AM_DEMONSTRATION,
- AM_ACIDTERROR,
- AM_POTIONPITCHER,
- AM_CANNIBALIZE,
- AM_SPHEREMINE,
- AM_CP_WEAPON,
- AM_CP_SHIELD,
- AM_CP_ARMOR,
- AM_CP_HELM,
- AM_BIOETHICS,
- AM_BIOTECHNOLOGY,
- AM_CREATECREATURE,
- AM_CULTIVATION,
- AM_FLAMECONTROL,
- AM_CALLHOMUN,
- AM_REST,
- AM_DRILLMASTER,
- AM_HEALHOMUN,
- AM_RESURRECTHOMUN,
-
- CR_TRUST,
- CR_AUTOGUARD,
- CR_SHIELDCHARGE,
- CR_SHIELDBOOMERANG,
- CR_REFLECTSHIELD,
- CR_HOLYCROSS,
- CR_GRANDCROSS,
- CR_DEVOTION,
- CR_PROVIDENCE,
- CR_DEFENDER,
- CR_SPEARQUICKEN,
-
- MO_IRONHAND,
- MO_SPIRITSRECOVERY,
- MO_CALLSPIRITS,
- MO_ABSORBSPIRITS,
- MO_TRIPLEATTACK,
- MO_BODYRELOCATION,
- MO_DODGE,
- MO_INVESTIGATE,
- MO_FINGEROFFENSIVE,
- MO_STEELBODY,
- MO_BLADESTOP,
- MO_EXPLOSIONSPIRITS,
- MO_EXTREMITYFIST,
- MO_CHAINCOMBO,
- MO_COMBOFINISH,
-
- SA_ADVANCEDBOOK,
- SA_CASTCANCEL,
- SA_MAGICROD,
- SA_SPELLBREAKER,
- SA_FREECAST,
- SA_AUTOSPELL,
- SA_FLAMELAUNCHER,
- SA_FROSTWEAPON,
- SA_LIGHTNINGLOADER,
- SA_SEISMICWEAPON,
- SA_DRAGONOLOGY,
- SA_VOLCANO,
- SA_DELUGE,
- SA_VIOLENTGALE,
- SA_LANDPROTECTOR,
- SA_DISPELL,
- SA_ABRACADABRA,
- SA_MONOCELL,
- SA_CLASSCHANGE,
- SA_SUMMONMONSTER,
- SA_REVERSEORCISH,
- SA_DEATH,
- SA_FORTUNE,
- SA_TAMINGMONSTER,
- SA_QUESTION,
- SA_GRAVITY,
- SA_LEVELUP,
- SA_INSTANTDEATH,
- SA_FULLRECOVERY,
- SA_COMA,
-
- BD_ADAPTATION,
- BD_ENCORE,
- BD_LULLABY,
- BD_RICHMANKIM,
- BD_ETERNALCHAOS,
- BD_DRUMBATTLEFIELD,
- BD_RINGNIBELUNGEN,
- BD_ROKISWEIL,
- BD_INTOABYSS,
- BD_SIEGFRIED,
- BD_RAGNAROK,
-
- BA_MUSICALLESSON,
- BA_MUSICALSTRIKE,
- BA_DISSONANCE,
- BA_FROSTJOKE,
- BA_WHISTLE,
- BA_ASSASSINCROSS,
- BA_POEMBRAGI,
- BA_APPLEIDUN,
-
- DC_DANCINGLESSON,
- DC_THROWARROW,
- DC_UGLYDANCE,
- DC_SCREAM,
- DC_HUMMING,
- DC_DONTFORGETME,
- DC_FORTUNEKISS,
- DC_SERVICEFORYOU,
-
- NPC_RANDOMMOVE,
- NPC_SPEEDUP,
- NPC_REVENGE,
-
- WE_MALE,
- WE_FEMALE,
- WE_CALLPARTNER,
-
- ITM_TOMAHAWK,
-
- NPC_DARKCROSS,
- NPC_GRANDDARKNESS,
- NPC_DARKSTRIKE,
- NPC_DARKTHUNDER,
- NPC_STOP,
- NPC_BREAKWEAPON,
- NPC_BREAKARMOR,
- NPC_BREAKHELM,
- NPC_BREAKSHIELD,
- NPC_UNDEADATTACK,
- NPC_CHANGEUNDEAD,
- NPC_POWERUP,
- NPC_AGIUP,
- NPC_SIEGEMODE,
- NPC_CALLSLAVE,
- NPC_INVISIBLE,
- NPC_RUN,
-
- LK_AURABLADE,
- LK_PARRYING,
- LK_CONCENTRATION,
- LK_TENSIONRELAX,
- LK_BERSERK,
- LK_FURY,
- HP_ASSUMPTIO,
- HP_BASILICA,
- HP_MEDITATIO,
- HW_SOULDRAIN,
- HW_MAGICCRASHER,
- HW_MAGICPOWER,
- PA_PRESSURE,
- PA_SACRIFICE,
- PA_GOSPEL,
- CH_PALMSTRIKE,
- CH_TIGERFIST,
- CH_CHAINCRUSH,
- PF_HPCONVERSION,
- PF_SOULCHANGE,
- PF_SOULBURN,
- ASC_KATAR,
- ASC_HALLUCINATION,
- ASC_EDP,
- ASC_BREAKER,
- SN_SIGHT,
- SN_FALCONASSAULT,
- SN_SHARPSHOOTING,
- SN_WINDWALK,
- WS_MELTDOWN,
- WS_CREATECOIN,
- WS_CREATENUGGET,
- WS_CARTBOOST,
- WS_SYSTEMCREATE,
- ST_CHASEWALK,
- ST_REJECTSWORD,
- ST_STEALBACKPACK,
- CR_ALCHEMY,
- CR_SYNTHESISPOTION,
- CG_ARROWVULCAN,
- CG_MOONLIT,
- CG_MARIONETTE,
- LK_SPIRALPIERCE,
- LK_HEADCRUSH,
- LK_JOINTBEAT,
- HW_NAPALMVULCAN,
- CH_SOULCOLLECT,
- PF_MINDBREAKER,
- PF_MEMORIZE,
- PF_FOGWALL,
- PF_SPIDERWEB,
- ASC_METEORASSAULT,
- ASC_CDP,
- WE_BABY,
- WE_CALLPARENT,
- WE_CALLBABY,
-
- TK_RUN,
- TK_READYSTORM,
- TK_STORMKICK,
- TK_READYDOWN,
- TK_DOWNKICK,
- TK_READYTURN,
- TK_TURNKICK,
- TK_READYCOUNTER,
- TK_COUNTER,
- TK_DODGE,
- TK_JUMPKICK,
- TK_HPTIME,
- TK_SPTIME,
- TK_POWER,
- TK_SEVENWIND,
- TK_HIGHJUMP,
- SG_FEEL,
- SG_SUN_WARM,
- SG_MOON_WARM,
- SG_STAR_WARM,
- SG_SUN_COMFORT,
- SG_MOON_COMFORT,
- SG_STAR_COMFORT,
- SG_HATE,
- SG_SUN_ANGER,
- SG_MOON_ANGER,
- SG_STAR_ANGER,
- SG_SUN_BLESS,
- SG_MOON_BLESS,
- SG_STAR_BLESS,
- SG_DEVIL,
- SG_FRIEND,
- SG_KNOWLEDGE,
- SG_FUSION,
- SL_ALCHEMIST,
- AM_BERSERKPITCHER,
- SL_MONK,
- SL_STAR,
- SL_SAGE,
- SL_CRUSADER,
- SL_SUPERNOVICE,
- SL_KNIGHT,
- SL_WIZARD,
- SL_PRIEST,
- SL_BARDDANCER,
- SL_ROGUE,
- SL_ASSASIN,
- SL_BLACKSMITH,
- BS_ADRENALINE2,
- SL_HUNTER,
- SL_SOULLINKER,
- SL_KAIZEL,
- SL_KAAHI,
- SL_KAUPE,
- SL_KAITE,
- SL_KAINA,
- SL_STIN,
- SL_STUN,
- SL_SMA,
- SL_SWOO,
- SL_SKE,
- SL_SKA,
-
- SM_SELFPROVOKE,
- NPC_EMOTION_ON,
- ST_PRESERVE,
- ST_FULLSTRIP,
- WS_WEAPONREFINE,
- CR_SLIMPITCHER,
- CR_FULLPROTECTION,
- PA_SHIELDCHAIN,
- HP_MANARECHARGE,
- PF_DOUBLECASTING,
- HW_GANBANTEIN,
- HW_GRAVITATION,
- WS_CARTTERMINATION,
- WS_OVERTHRUSTMAX,
- CG_LONGINGFREEDOM,
- CG_HERMODE,
- CG_TAROTCARD,
- CR_ACIDDEMONSTRATION,
- CR_CULTIVATION,
- //492,missing?
- TK_MISSION = 493,
- SL_HIGH,
- KN_ONEHAND,
- AM_TWILIGHT1,
- AM_TWILIGHT2,
- AM_TWILIGHT3,
- HT_POWER,
- GS_GLITTERING,
- GS_FLING,
- GS_TRIPLEACTION,
- GS_BULLSEYE,
- GS_MADNESSCANCEL,
- GS_ADJUSTMENT,
- GS_INCREASING,
- GS_MAGICALBULLET,
- GS_CRACKER,
- GS_SINGLEACTION,
- GS_SNAKEEYE,
- GS_CHAINACTION,
- GS_TRACKING,
- GS_DISARM,
- GS_PIERCINGSHOT,
- GS_RAPIDSHOWER,
- GS_DESPERADO,
- GS_GATLINGFEVER,
- GS_DUST,
- GS_FULLBUSTER,
- GS_SPREADATTACK,
- GS_GROUNDDRIFT,
- NJ_TOBIDOUGU,
- NJ_SYURIKEN,
- NJ_KUNAI,
- NJ_HUUMA,
- NJ_ZENYNAGE,
- NJ_TATAMIGAESHI,
- NJ_KASUMIKIRI,
- NJ_SHADOWJUMP,
- NJ_KIRIKAGE,
- NJ_UTSUSEMI,
- NJ_BUNSINJYUTSU,
- NJ_NINPOU,
- NJ_KOUENKA,
- NJ_KAENSIN,
- NJ_BAKUENRYU,
- NJ_HYOUSENSOU,
- NJ_SUITON,
- NJ_HYOUSYOURAKU,
- NJ_HUUJIN,
- NJ_RAIGEKISAI,
- NJ_KAMAITACHI,
- NJ_NEN,
- NJ_ISSEN,
-
- KN_CHARGEATK = 1001,
- CR_SHRINK,
- AS_SONICACCEL,
- AS_VENOMKNIFE,
- RG_CLOSECONFINE,
- WZ_SIGHTBLASTER,
- SA_CREATECON,
- SA_ELEMENTWATER,
- HT_PHANTASMIC,
- BA_PANGVOICE,
- DC_WINKCHARM,
- BS_UNFAIRLYTRICK,
- BS_GREED,
- PR_REDEMPTIO,
- MO_KITRANSLATION,
- MO_BALKYOUNG,
- SA_ELEMENTGROUND,
- SA_ELEMENTFIRE,
- SA_ELEMENTWIND,
-
- HLIF_HEAL = 8001,
- HLIF_AVOID,
- HLIF_BRAIN,
- HLIF_CHANGE,
- HAMI_CASTLE,
- HAMI_DEFENCE,
- HAMI_SKIN,
- HAMI_BLOODLUST,
- HFLI_MOON,
- HFLI_FLEET,
- HFLI_SPEED,
- HFLI_SBR44,
- HVAN_CAPRICE,
- HVAN_CHAOTIC,
- HVAN_INSTRUCT,
- HVAN_EXPLOSION,
-};
-
-enum {
- UNT_SAFETYWALL = 0x7e,
- UNT_FIREWALL,
- UNT_WARP_WAITING,
- UNT_WARP_ACTIVE,
- //0x82
- UNT_SANCTUARY = 0x83,
- UNT_MAGNUS,
- UNT_PNEUMA,
- UNT_ATTACK_SKILLS, //These show no effect on the client, therefore can be used for attack skills.
- UNT_FIREPILLAR_WAITING,
- UNT_FIREPILLAR_ACTIVE,
- //0x89, 0x8a, 0x8b
- UNT_USED_TRAPS = 0x8c,
- UNT_ICEWALL,
- UNT_QUAGMIRE,
- UNT_BLASTMINE,
- UNT_SKIDTRAP,
- UNT_ANKLESNARE,
- UNT_VENOMDUST,
- UNT_LANDMINE,
- UNT_SHOCKWAVE,
- UNT_SANDMAN,
- UNT_FLASHER,
- UNT_FREEZINGTRAP,
- UNT_CLAYMORETRAP,
- UNT_TALKIEBOX,
- UNT_VOLCANO,
- UNT_DELUGE,
- UNT_VIOLENTGALE,
- UNT_LANDPROTECTOR,
- UNT_LULLABY,
- UNT_RICHMANKIM,
- UNT_ETERNALCHAOS,
- UNT_DRUMBATTLEFIELD,
- UNT_RINGNIBELUNGEN,
- UNT_ROKISWEIL,
- UNT_INTOABYSS,
- UNT_SIEGFRIED,
- UNT_DISSONANCE,
- UNT_WHISTLE,
- UNT_ASSASSINCROSS,
- UNT_POEMBRAGI,
- UNT_APPLEIDUN,
- UNT_UGLYDANCE,
- UNT_HUMMING,
- UNT_DONTFORGETME,
- UNT_FORTUNEKISS,
- UNT_SERVICEFORYOU,
- UNT_GRAFFITI,
- UNT_DEMONSTRATION,
- UNT_CALLFAMILY,
- UNT_GOSPEL,
- UNT_BASILICA,
- UNT_MOONLIT,//0xb5 //I HOPE this one doesn't shows any effects
- UNT_FOGWALL = 0xb6,
- UNT_SPIDERWEB,
- UNT_GRAVITATION,
- UNT_HERMODE,
- UNT_DESPERADO, //0xba //Temporary setting until correct value is found.
- UNT_SUITON = 0xbb,
- UNT_TATAMIGAESHI,
- UNT_KAENSIN,
- UNT_GROUNDDRIFT_WIND,
- UNT_GROUNDDRIFT_DARK,
- UNT_GROUNDDRIFT_POISON,
- UNT_GROUNDDRIFT_WATER,
- UNT_GROUNDDRIFT_FIRE,
-};
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _SKILL_H_
+#define _SKILL_H_
+
+#include "map.h"
+
+#define MAX_SKILL_DB 1100
+#define MAX_SKILL_PRODUCE_DB 150
+#define MAX_PRODUCE_RESOURCE 12
+#define MAX_SKILL_ARROW_DB 150
+#define MAX_SKILL_ABRA_DB 350
+
+//Constants to identify the skill's inf value:
+#define INF_ATTACK_SKILL 1
+#define INF_GROUND_SKILL 2
+// Skills casted on self where target is automatically chosen:
+#define INF_SELF_SKILL 4
+#define INF_SUPPORT_SKILL 16
+#define INF_TARGET_TRAP 32
+
+//Constants to identify a skill's nk value.
+//The NK value applies only to non INF_GROUND_SKILL skills.
+#define NK_NO_DAMAGE 0x1
+#define NK_SPLASH (0x2|0x4) // 0x4 = splash & split
+#define NK_SPLASHSPLIT 0x4
+//A skill with 3 would be no damage + splash: area of effect.
+//Constants to identify a skill's inf2 value.
+#define INF2_QUEST_SKILL 1
+//NPC skills are those that players can't have in their skill tree.
+#define INF2_NPC_SKILL 2
+#define INF2_WEDDING_SKILL 4
+#define INF2_SPIRIT_SKILL 8
+#define INF2_GUILD_SKILL 16
+#define INF2_SONG_DANCE 32
+#define INF2_ENSEMBLE_SKILL 64
+#define INF2_TRAP 128
+//Refers to ground placed skills that will target the caster as well (like Grandcross)
+#define INF2_TARGET_SELF 256
+#define INF2_NO_TARGET_SELF 512
+#define INF2_PARTY_ONLY 1024
+#define INF2_GUILD_ONLY 2048
+//For Party/Guild only skills that can ALSO be used on enemies.
+#define INF2_ALLOW_ENEMY 4096
+
+//Walk intervals at which chase-skills are attempted to be triggered.
+#define WALK_SKILL_INTERVAL 5
+
+// To be passed to skill_attack, whether the skill damage should disable level or animation. [Skotlex]
+#define SD_LEVEL 0x1000
+#define SD_ANIMATION 0x2000
+
+// スキルデ?タベ?ス
+struct skill_db {
+ char *name;
+ char *desc;
+ int range[MAX_SKILL_LEVEL],hit,inf,pl,nk,splash[MAX_SKILL_LEVEL],max;
+ int num[MAX_SKILL_LEVEL];
+ int cast[MAX_SKILL_LEVEL],walkdelay[MAX_SKILL_LEVEL],delay[MAX_SKILL_LEVEL];
+ int upkeep_time[MAX_SKILL_LEVEL],upkeep_time2[MAX_SKILL_LEVEL];
+ int castcancel,cast_def_rate;
+ int inf2,maxcount,skill_type;
+ int blewcount[MAX_SKILL_LEVEL];
+ int hp[MAX_SKILL_LEVEL],sp[MAX_SKILL_LEVEL],mhp[MAX_SKILL_LEVEL],hp_rate[MAX_SKILL_LEVEL],sp_rate[MAX_SKILL_LEVEL],zeny[MAX_SKILL_LEVEL];
+ int weapon,ammo,ammo_qty[MAX_SKILL_LEVEL],state,spiritball[MAX_SKILL_LEVEL];
+ int itemid[10],amount[10];
+ int castnodex[MAX_SKILL_LEVEL];
+ int delaynoagi[MAX_SKILL_LEVEL];
+ int nocast;
+ int unit_id[2];
+ int unit_layout_type[MAX_SKILL_LEVEL];
+ int unit_range[MAX_SKILL_LEVEL];
+ int unit_interval;
+ int unit_target;
+ int unit_flag;
+};
+extern struct skill_db skill_db[MAX_SKILL_DB];
+
+struct skill_name_db {
+ int id; // skill id
+ char *name; // search strings
+ char *desc; // description that shows up for search's
+};
+
+#define MAX_SKILL_UNIT_LAYOUT 50
+#define MAX_SQUARE_LAYOUT 5 // 11*11のユニット配置が最大
+#define MAX_SKILL_UNIT_COUNT ((MAX_SQUARE_LAYOUT*2+1)*(MAX_SQUARE_LAYOUT*2+1))
+struct skill_unit_layout {
+ int count;
+ int dx[MAX_SKILL_UNIT_COUNT];
+ int dy[MAX_SKILL_UNIT_COUNT];
+};
+
+enum {
+ UF_DEFNOTENEMY = 0x0001, // defnotenemy 設定でBCT_NOENEMYに切り替え
+ UF_NOREITERATION = 0x0002, // 重複置き禁止
+ UF_NOFOOTSET = 0x0004, // 足元置き禁止
+ UF_NOOVERLAP = 0x0008, // ユニット効果が重複しない
+ UF_NOPC = 0x0010, //May not target players
+ UF_NOMOB = 0x0020, //May not target mobs
+ UF_SKILL = 0x0080, //May target skills
+ UF_DANCE = 0x0100, //Dance
+ UF_ENSEMBLE = 0x0200, //Duet
+ UF_SONG = 0x0400, //Song
+ UF_DUALMODE = 0x0800, //Spells should trigger both ontimer and onplace/onout/onleft effects.
+};
+
+// アイテム作成デ?タベ?ス
+struct skill_produce_db {
+ int nameid, trigger;
+ int req_skill,req_skill_lv,itemlv;
+ int mat_id[MAX_PRODUCE_RESOURCE],mat_amount[MAX_PRODUCE_RESOURCE];
+};
+extern struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
+
+// 矢作成デ?タベ?ス
+struct skill_arrow_db {
+ int nameid, trigger;
+ int cre_id[5],cre_amount[5];
+};
+extern struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
+
+// アブラカダブラデ?タベ?ス
+struct skill_abra_db {
+ int nameid;
+ int req_lv;
+ int per;
+};
+extern struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
+
+extern int enchant_eff[5];
+extern int deluge_eff[5];
+
+struct block_list;
+struct map_session_data;
+struct skill_unit;
+struct skill_unit_group;
+
+int do_init_skill(void);
+int do_final_skill(void);
+
+//Returns the cast type of the skill: ground cast, castend damage, castend no damage
+enum { CAST_GROUND, CAST_DAMAGE, CAST_NODAMAGE };
+int skill_get_casttype(int id); //[Skotlex]
+// スキルデ?タベ?スへのアクセサ
+//
+int skill_get_type( int id );
+int skill_get_hit( int id );
+int skill_get_inf( int id );
+int skill_get_pl( int id );
+int skill_get_nk( int id );
+int skill_get_max( int id );
+int skill_get_range( int id , int lv );
+int skill_get_range2(struct block_list *bl, int id, int lv);
+int skill_get_splash( int id , int lv );
+int skill_get_hp( int id ,int lv );
+int skill_get_mhp( int id ,int lv );
+int skill_get_sp( int id ,int lv );
+int skill_get_state(int id);
+int skill_get_zeny( int id ,int lv );
+int skill_get_num( int id ,int lv );
+int skill_get_cast( int id ,int lv );
+int skill_get_delay( int id ,int lv );
+int skill_get_walkdelay( int id ,int lv );
+int skill_get_time( int id ,int lv );
+int skill_get_time2( int id ,int lv );
+int skill_get_castnodex( int id ,int lv );
+int skill_get_castdef( int id );
+int skill_get_weapontype( int id );
+int skill_get_ammotype( int id );
+int skill_get_ammo_qty( int id, int lv );
+int skill_get_nocast( int id );
+int skill_get_unit_id(int id,int flag);
+int skill_get_inf2( int id );
+int skill_get_castcancel( int id );
+int skill_get_maxcount( int id );
+int skill_get_blewcount( int id ,int lv );
+int skill_get_unit_flag( int id );
+int skill_get_unit_target( int id );
+int skill_tree_get_max( int id, int b_class ); // Celest
+const char* skill_get_name( int id ); // [Skotlex]
+
+int skill_isammotype(TBL_PC *sd, int skill);
+int skill_castend_id( int tid, unsigned int tick, int id,int data );
+int skill_castend_pos( int tid, unsigned int tick, int id,int data );
+int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map);
+
+int skill_cleartimerskill(struct block_list *src);
+int skill_addtimerskill(struct block_list *src,unsigned int tick,int target,int x,int y,int skill_id,int skill_lv,int type,int flag);
+
+// 追加?果
+int skill_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
+int skill_counter_additional_effect( struct block_list* src, struct block_list *bl,int skillid,int skilllv,int attack_type,unsigned int tick);
+int skill_blown( struct block_list *src, struct block_list *target,int count);
+int skill_break_equip(struct block_list *bl, unsigned short where, int rate, int flag);
+// ユニットスキル
+struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag);
+struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int x, int y, int val1, int val2);
+int skill_delunit(struct skill_unit *unit, int flag);
+struct skill_unit_group *skill_initunitgroup(struct block_list *src,
+ int count,int skillid,int skilllv,int unit_id, int limit, int interval);
+int skill_delunitgroup(struct block_list *src, struct skill_unit_group *group, int flag);
+int skill_clear_unitgroup(struct block_list *src);
+int skill_clear_group(struct block_list *bl, int flag);
+
+int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl,
+ int damage,unsigned int tick);
+
+int skill_castfix( struct block_list *bl, int skill_id, int skill_lv);
+int skill_castfix_sc( struct block_list *bl, int time);
+int skill_delayfix( struct block_list *bl, int skill_id, int skill_lv);
+int skill_check_condition( struct map_session_data *sd,int skill, int lv, int type);
+int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag);
+// -- moonsoul (added skill_check_unit_cell)
+int skill_check_unit_cell(int skillid,int m,int x,int y,int unit_id);
+int skill_unit_out_all( struct block_list *bl,unsigned int tick,int range);
+int skill_unit_move(struct block_list *bl,unsigned int tick,int flag);
+int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy);
+void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag);
+
+struct skill_unit_group *skill_check_dancing( struct block_list *src );
+void skill_stop_dancing(struct block_list *src);
+
+// Guild skills [celest]
+int skill_guildaura_sub (struct block_list *bl,va_list ap);
+
+// 詠唱キャンセル
+int skill_castcancel(struct block_list *bl,int type);
+
+int skill_sit (struct map_session_data *sd, int type);
+void skill_brandishspear_first(struct square *tc,int dir,int x,int y);
+void skill_brandishspear_dir(struct square *tc,int dir,int are);
+void skill_repairweapon(struct map_session_data *sd, int idx);
+void skill_identify(struct map_session_data *sd,int idx);
+void skill_weaponrefine(struct map_session_data *sd,int idx); // [Celest]
+int skill_autospell(struct map_session_data *md,int skillid);
+
+int skill_calc_heal(struct block_list *bl, int skill_lv);
+
+int skill_check_cloaking(struct block_list *bl, struct status_change *sc);
+
+// ステ?タス異常
+int skill_enchant_elemental_end(struct block_list *bl, int type);
+int skillnotok(int skillid, struct map_session_data *sd);
+int skillnotok_hom (int skillid, struct homun_data *hd) ; //[orn]
+int skill_chastle_mob_changetarget(struct block_list *bl,va_list ap); //[orn]
+
+// アイテム作成
+int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty);
+int skill_produce_mix( struct map_session_data *sd,
+ int skill_id, int nameid, int slot1, int slot2, int slot3, int qty );
+
+int skill_arrow_create( struct map_session_data *sd,int nameid);
+
+// mobスキルのため
+int skill_castend_nodamage_id( struct block_list *src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag);
+int skill_blockpc_start (struct map_session_data*,int,int); // [celest]
+int skill_blockmerc_start (struct homun_data*,int,int); //[orn]
+
+// スキル攻?一括?理
+int skill_attack( int attack_type, struct block_list* src, struct block_list *dsrc,
+ struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag );
+
+void skill_reload(void);
+
+enum {
+ ST_NONE,ST_HIDING,ST_CLOAKING,ST_HIDDEN,ST_RIDING,ST_FALCON,ST_CART,ST_SHIELD,ST_SIGHT,ST_EXPLOSIONSPIRITS,ST_CARTBOOST,
+ ST_RECOV_WEIGHT_RATE,ST_MOVE_ENABLE,ST_WATER,
+};
+
+enum {
+ NV_BASIC = 1,
+
+ SM_SWORD,
+ SM_TWOHAND,
+ SM_RECOVERY,
+ SM_BASH,
+ SM_PROVOKE,
+ SM_MAGNUM,
+ SM_ENDURE,
+
+ MG_SRECOVERY,
+ MG_SIGHT,
+ MG_NAPALMBEAT,
+ MG_SAFETYWALL,
+ MG_SOULSTRIKE,
+ MG_COLDBOLT,
+ MG_FROSTDIVER,
+ MG_STONECURSE,
+ MG_FIREBALL,
+ MG_FIREWALL,
+ MG_FIREBOLT,
+ MG_LIGHTNINGBOLT,
+ MG_THUNDERSTORM,
+
+ AL_DP,
+ AL_DEMONBANE,
+ AL_RUWACH,
+ AL_PNEUMA,
+ AL_TELEPORT,
+ AL_WARP,
+ AL_HEAL,
+ AL_INCAGI,
+ AL_DECAGI,
+ AL_HOLYWATER,
+ AL_CRUCIS,
+ AL_ANGELUS,
+ AL_BLESSING,
+ AL_CURE,
+
+ MC_INCCARRY,
+ MC_DISCOUNT,
+ MC_OVERCHARGE,
+ MC_PUSHCART,
+ MC_IDENTIFY,
+ MC_VENDING,
+ MC_MAMMONITE,
+
+ AC_OWL,
+ AC_VULTURE,
+ AC_CONCENTRATION,
+ AC_DOUBLE,
+ AC_SHOWER,
+
+ TF_DOUBLE,
+ TF_MISS,
+ TF_STEAL,
+ TF_HIDING,
+ TF_POISON,
+ TF_DETOXIFY,
+
+ ALL_RESURRECTION,
+
+ KN_SPEARMASTERY,
+ KN_PIERCE,
+ KN_BRANDISHSPEAR,
+ KN_SPEARSTAB,
+ KN_SPEARBOOMERANG,
+ KN_TWOHANDQUICKEN,
+ KN_AUTOCOUNTER,
+ KN_BOWLINGBASH,
+ KN_RIDING,
+ KN_CAVALIERMASTERY,
+
+ PR_MACEMASTERY,
+ PR_IMPOSITIO,
+ PR_SUFFRAGIUM,
+ PR_ASPERSIO,
+ PR_BENEDICTIO,
+ PR_SANCTUARY,
+ PR_SLOWPOISON,
+ PR_STRECOVERY,
+ PR_KYRIE,
+ PR_MAGNIFICAT,
+ PR_GLORIA,
+ PR_LEXDIVINA,
+ PR_TURNUNDEAD,
+ PR_LEXAETERNA,
+ PR_MAGNUS,
+
+ WZ_FIREPILLAR,
+ WZ_SIGHTRASHER,
+ WZ_FIREIVY,
+ WZ_METEOR,
+ WZ_JUPITEL,
+ WZ_VERMILION,
+ WZ_WATERBALL,
+ WZ_ICEWALL,
+ WZ_FROSTNOVA,
+ WZ_STORMGUST,
+ WZ_EARTHSPIKE,
+ WZ_HEAVENDRIVE,
+ WZ_QUAGMIRE,
+ WZ_ESTIMATION,
+
+ BS_IRON,
+ BS_STEEL,
+ BS_ENCHANTEDSTONE,
+ BS_ORIDEOCON,
+ BS_DAGGER,
+ BS_SWORD,
+ BS_TWOHANDSWORD,
+ BS_AXE,
+ BS_MACE,
+ BS_KNUCKLE,
+ BS_SPEAR,
+ BS_HILTBINDING,
+ BS_FINDINGORE,
+ BS_WEAPONRESEARCH,
+ BS_REPAIRWEAPON,
+ BS_SKINTEMPER,
+ BS_HAMMERFALL,
+ BS_ADRENALINE,
+ BS_WEAPONPERFECT,
+ BS_OVERTHRUST,
+ BS_MAXIMIZE,
+
+ HT_SKIDTRAP,
+ HT_LANDMINE,
+ HT_ANKLESNARE,
+ HT_SHOCKWAVE,
+ HT_SANDMAN,
+ HT_FLASHER,
+ HT_FREEZINGTRAP,
+ HT_BLASTMINE,
+ HT_CLAYMORETRAP,
+ HT_REMOVETRAP,
+ HT_TALKIEBOX,
+ HT_BEASTBANE,
+ HT_FALCON,
+ HT_STEELCROW,
+ HT_BLITZBEAT,
+ HT_DETECTING,
+ HT_SPRINGTRAP,
+
+ AS_RIGHT,
+ AS_LEFT,
+ AS_KATAR,
+ AS_CLOAKING,
+ AS_SONICBLOW,
+ AS_GRIMTOOTH,
+ AS_ENCHANTPOISON,
+ AS_POISONREACT,
+ AS_VENOMDUST,
+ AS_SPLASHER,
+
+ NV_FIRSTAID,
+ NV_TRICKDEAD,
+ SM_MOVINGRECOVERY,
+ SM_FATALBLOW,
+ SM_AUTOBERSERK,
+ AC_MAKINGARROW,
+ AC_CHARGEARROW,
+ TF_SPRINKLESAND,
+ TF_BACKSLIDING,
+ TF_PICKSTONE,
+ TF_THROWSTONE,
+ MC_CARTREVOLUTION,
+ MC_CHANGECART,
+ MC_LOUD,
+ AL_HOLYLIGHT,
+ MG_ENERGYCOAT,
+
+ NPC_PIERCINGATT,
+ NPC_MENTALBREAKER,
+ NPC_RANGEATTACK,
+ NPC_ATTRICHANGE,
+ NPC_CHANGEWATER,
+ NPC_CHANGEGROUND,
+ NPC_CHANGEFIRE,
+ NPC_CHANGEWIND,
+ NPC_CHANGEPOISON,
+ NPC_CHANGEHOLY,
+ NPC_CHANGEDARKNESS,
+ NPC_CHANGETELEKINESIS,
+ NPC_CRITICALSLASH,
+ NPC_COMBOATTACK,
+ NPC_GUIDEDATTACK,
+ NPC_SELFDESTRUCTION,
+ NPC_SPLASHATTACK,
+ NPC_SUICIDE,
+ NPC_POISON,
+ NPC_BLINDATTACK,
+ NPC_SILENCEATTACK,
+ NPC_STUNATTACK,
+ NPC_PETRIFYATTACK,
+ NPC_CURSEATTACK,
+ NPC_SLEEPATTACK,
+ NPC_RANDOMATTACK,
+ NPC_WATERATTACK,
+ NPC_GROUNDATTACK,
+ NPC_FIREATTACK,
+ NPC_WINDATTACK,
+ NPC_POISONATTACK,
+ NPC_HOLYATTACK,
+ NPC_DARKNESSATTACK,
+ NPC_TELEKINESISATTACK,
+ NPC_MAGICALATTACK,
+ NPC_METAMORPHOSIS,
+ NPC_PROVOCATION,
+ NPC_SMOKING,
+ NPC_SUMMONSLAVE,
+ NPC_EMOTION,
+ NPC_TRANSFORMATION,
+ NPC_BLOODDRAIN,
+ NPC_ENERGYDRAIN,
+ NPC_KEEPING,
+ NPC_DARKBREATH,
+ NPC_DARKBLESSING,
+ NPC_BARRIER,
+ NPC_DEFENDER,
+ NPC_LICK,
+ NPC_HALLUCINATION,
+ NPC_REBIRTH,
+ NPC_SUMMONMONSTER,
+
+ RG_SNATCHER,
+ RG_STEALCOIN,
+ RG_BACKSTAP,
+ RG_TUNNELDRIVE,
+ RG_RAID,
+ RG_STRIPWEAPON,
+ RG_STRIPSHIELD,
+ RG_STRIPARMOR,
+ RG_STRIPHELM,
+ RG_INTIMIDATE,
+ RG_GRAFFITI,
+ RG_FLAGGRAFFITI,
+ RG_CLEANER,
+ RG_GANGSTER,
+ RG_COMPULSION,
+ RG_PLAGIARISM,
+
+ AM_AXEMASTERY,
+ AM_LEARNINGPOTION,
+ AM_PHARMACY,
+ AM_DEMONSTRATION,
+ AM_ACIDTERROR,
+ AM_POTIONPITCHER,
+ AM_CANNIBALIZE,
+ AM_SPHEREMINE,
+ AM_CP_WEAPON,
+ AM_CP_SHIELD,
+ AM_CP_ARMOR,
+ AM_CP_HELM,
+ AM_BIOETHICS,
+ AM_BIOTECHNOLOGY,
+ AM_CREATECREATURE,
+ AM_CULTIVATION,
+ AM_FLAMECONTROL,
+ AM_CALLHOMUN,
+ AM_REST,
+ AM_DRILLMASTER,
+ AM_HEALHOMUN,
+ AM_RESURRECTHOMUN,
+
+ CR_TRUST,
+ CR_AUTOGUARD,
+ CR_SHIELDCHARGE,
+ CR_SHIELDBOOMERANG,
+ CR_REFLECTSHIELD,
+ CR_HOLYCROSS,
+ CR_GRANDCROSS,
+ CR_DEVOTION,
+ CR_PROVIDENCE,
+ CR_DEFENDER,
+ CR_SPEARQUICKEN,
+
+ MO_IRONHAND,
+ MO_SPIRITSRECOVERY,
+ MO_CALLSPIRITS,
+ MO_ABSORBSPIRITS,
+ MO_TRIPLEATTACK,
+ MO_BODYRELOCATION,
+ MO_DODGE,
+ MO_INVESTIGATE,
+ MO_FINGEROFFENSIVE,
+ MO_STEELBODY,
+ MO_BLADESTOP,
+ MO_EXPLOSIONSPIRITS,
+ MO_EXTREMITYFIST,
+ MO_CHAINCOMBO,
+ MO_COMBOFINISH,
+
+ SA_ADVANCEDBOOK,
+ SA_CASTCANCEL,
+ SA_MAGICROD,
+ SA_SPELLBREAKER,
+ SA_FREECAST,
+ SA_AUTOSPELL,
+ SA_FLAMELAUNCHER,
+ SA_FROSTWEAPON,
+ SA_LIGHTNINGLOADER,
+ SA_SEISMICWEAPON,
+ SA_DRAGONOLOGY,
+ SA_VOLCANO,
+ SA_DELUGE,
+ SA_VIOLENTGALE,
+ SA_LANDPROTECTOR,
+ SA_DISPELL,
+ SA_ABRACADABRA,
+ SA_MONOCELL,
+ SA_CLASSCHANGE,
+ SA_SUMMONMONSTER,
+ SA_REVERSEORCISH,
+ SA_DEATH,
+ SA_FORTUNE,
+ SA_TAMINGMONSTER,
+ SA_QUESTION,
+ SA_GRAVITY,
+ SA_LEVELUP,
+ SA_INSTANTDEATH,
+ SA_FULLRECOVERY,
+ SA_COMA,
+
+ BD_ADAPTATION,
+ BD_ENCORE,
+ BD_LULLABY,
+ BD_RICHMANKIM,
+ BD_ETERNALCHAOS,
+ BD_DRUMBATTLEFIELD,
+ BD_RINGNIBELUNGEN,
+ BD_ROKISWEIL,
+ BD_INTOABYSS,
+ BD_SIEGFRIED,
+ BD_RAGNAROK,
+
+ BA_MUSICALLESSON,
+ BA_MUSICALSTRIKE,
+ BA_DISSONANCE,
+ BA_FROSTJOKE,
+ BA_WHISTLE,
+ BA_ASSASSINCROSS,
+ BA_POEMBRAGI,
+ BA_APPLEIDUN,
+
+ DC_DANCINGLESSON,
+ DC_THROWARROW,
+ DC_UGLYDANCE,
+ DC_SCREAM,
+ DC_HUMMING,
+ DC_DONTFORGETME,
+ DC_FORTUNEKISS,
+ DC_SERVICEFORYOU,
+
+ NPC_RANDOMMOVE,
+ NPC_SPEEDUP,
+ NPC_REVENGE,
+
+ WE_MALE,
+ WE_FEMALE,
+ WE_CALLPARTNER,
+
+ ITM_TOMAHAWK,
+
+ NPC_DARKCROSS,
+ NPC_GRANDDARKNESS,
+ NPC_DARKSTRIKE,
+ NPC_DARKTHUNDER,
+ NPC_STOP,
+ NPC_BREAKWEAPON,
+ NPC_BREAKARMOR,
+ NPC_BREAKHELM,
+ NPC_BREAKSHIELD,
+ NPC_UNDEADATTACK,
+ NPC_CHANGEUNDEAD,
+ NPC_POWERUP,
+ NPC_AGIUP,
+ NPC_SIEGEMODE,
+ NPC_CALLSLAVE,
+ NPC_INVISIBLE,
+ NPC_RUN,
+
+ LK_AURABLADE,
+ LK_PARRYING,
+ LK_CONCENTRATION,
+ LK_TENSIONRELAX,
+ LK_BERSERK,
+ LK_FURY,
+ HP_ASSUMPTIO,
+ HP_BASILICA,
+ HP_MEDITATIO,
+ HW_SOULDRAIN,
+ HW_MAGICCRASHER,
+ HW_MAGICPOWER,
+ PA_PRESSURE,
+ PA_SACRIFICE,
+ PA_GOSPEL,
+ CH_PALMSTRIKE,
+ CH_TIGERFIST,
+ CH_CHAINCRUSH,
+ PF_HPCONVERSION,
+ PF_SOULCHANGE,
+ PF_SOULBURN,
+ ASC_KATAR,
+ ASC_HALLUCINATION,
+ ASC_EDP,
+ ASC_BREAKER,
+ SN_SIGHT,
+ SN_FALCONASSAULT,
+ SN_SHARPSHOOTING,
+ SN_WINDWALK,
+ WS_MELTDOWN,
+ WS_CREATECOIN,
+ WS_CREATENUGGET,
+ WS_CARTBOOST,
+ WS_SYSTEMCREATE,
+ ST_CHASEWALK,
+ ST_REJECTSWORD,
+ ST_STEALBACKPACK,
+ CR_ALCHEMY,
+ CR_SYNTHESISPOTION,
+ CG_ARROWVULCAN,
+ CG_MOONLIT,
+ CG_MARIONETTE,
+ LK_SPIRALPIERCE,
+ LK_HEADCRUSH,
+ LK_JOINTBEAT,
+ HW_NAPALMVULCAN,
+ CH_SOULCOLLECT,
+ PF_MINDBREAKER,
+ PF_MEMORIZE,
+ PF_FOGWALL,
+ PF_SPIDERWEB,
+ ASC_METEORASSAULT,
+ ASC_CDP,
+ WE_BABY,
+ WE_CALLPARENT,
+ WE_CALLBABY,
+
+ TK_RUN,
+ TK_READYSTORM,
+ TK_STORMKICK,
+ TK_READYDOWN,
+ TK_DOWNKICK,
+ TK_READYTURN,
+ TK_TURNKICK,
+ TK_READYCOUNTER,
+ TK_COUNTER,
+ TK_DODGE,
+ TK_JUMPKICK,
+ TK_HPTIME,
+ TK_SPTIME,
+ TK_POWER,
+ TK_SEVENWIND,
+ TK_HIGHJUMP,
+ SG_FEEL,
+ SG_SUN_WARM,
+ SG_MOON_WARM,
+ SG_STAR_WARM,
+ SG_SUN_COMFORT,
+ SG_MOON_COMFORT,
+ SG_STAR_COMFORT,
+ SG_HATE,
+ SG_SUN_ANGER,
+ SG_MOON_ANGER,
+ SG_STAR_ANGER,
+ SG_SUN_BLESS,
+ SG_MOON_BLESS,
+ SG_STAR_BLESS,
+ SG_DEVIL,
+ SG_FRIEND,
+ SG_KNOWLEDGE,
+ SG_FUSION,
+ SL_ALCHEMIST,
+ AM_BERSERKPITCHER,
+ SL_MONK,
+ SL_STAR,
+ SL_SAGE,
+ SL_CRUSADER,
+ SL_SUPERNOVICE,
+ SL_KNIGHT,
+ SL_WIZARD,
+ SL_PRIEST,
+ SL_BARDDANCER,
+ SL_ROGUE,
+ SL_ASSASIN,
+ SL_BLACKSMITH,
+ BS_ADRENALINE2,
+ SL_HUNTER,
+ SL_SOULLINKER,
+ SL_KAIZEL,
+ SL_KAAHI,
+ SL_KAUPE,
+ SL_KAITE,
+ SL_KAINA,
+ SL_STIN,
+ SL_STUN,
+ SL_SMA,
+ SL_SWOO,
+ SL_SKE,
+ SL_SKA,
+
+ SM_SELFPROVOKE,
+ NPC_EMOTION_ON,
+ ST_PRESERVE,
+ ST_FULLSTRIP,
+ WS_WEAPONREFINE,
+ CR_SLIMPITCHER,
+ CR_FULLPROTECTION,
+ PA_SHIELDCHAIN,
+ HP_MANARECHARGE,
+ PF_DOUBLECASTING,
+ HW_GANBANTEIN,
+ HW_GRAVITATION,
+ WS_CARTTERMINATION,
+ WS_OVERTHRUSTMAX,
+ CG_LONGINGFREEDOM,
+ CG_HERMODE,
+ CG_TAROTCARD,
+ CR_ACIDDEMONSTRATION,
+ CR_CULTIVATION,
+ //492,missing?
+ TK_MISSION = 493,
+ SL_HIGH,
+ KN_ONEHAND,
+ AM_TWILIGHT1,
+ AM_TWILIGHT2,
+ AM_TWILIGHT3,
+ HT_POWER,
+ GS_GLITTERING,
+ GS_FLING,
+ GS_TRIPLEACTION,
+ GS_BULLSEYE,
+ GS_MADNESSCANCEL,
+ GS_ADJUSTMENT,
+ GS_INCREASING,
+ GS_MAGICALBULLET,
+ GS_CRACKER,
+ GS_SINGLEACTION,
+ GS_SNAKEEYE,
+ GS_CHAINACTION,
+ GS_TRACKING,
+ GS_DISARM,
+ GS_PIERCINGSHOT,
+ GS_RAPIDSHOWER,
+ GS_DESPERADO,
+ GS_GATLINGFEVER,
+ GS_DUST,
+ GS_FULLBUSTER,
+ GS_SPREADATTACK,
+ GS_GROUNDDRIFT,
+ NJ_TOBIDOUGU,
+ NJ_SYURIKEN,
+ NJ_KUNAI,
+ NJ_HUUMA,
+ NJ_ZENYNAGE,
+ NJ_TATAMIGAESHI,
+ NJ_KASUMIKIRI,
+ NJ_SHADOWJUMP,
+ NJ_KIRIKAGE,
+ NJ_UTSUSEMI,
+ NJ_BUNSINJYUTSU,
+ NJ_NINPOU,
+ NJ_KOUENKA,
+ NJ_KAENSIN,
+ NJ_BAKUENRYU,
+ NJ_HYOUSENSOU,
+ NJ_SUITON,
+ NJ_HYOUSYOURAKU,
+ NJ_HUUJIN,
+ NJ_RAIGEKISAI,
+ NJ_KAMAITACHI,
+ NJ_NEN,
+ NJ_ISSEN,
+
+ KN_CHARGEATK = 1001,
+ CR_SHRINK,
+ AS_SONICACCEL,
+ AS_VENOMKNIFE,
+ RG_CLOSECONFINE,
+ WZ_SIGHTBLASTER,
+ SA_CREATECON,
+ SA_ELEMENTWATER,
+ HT_PHANTASMIC,
+ BA_PANGVOICE,
+ DC_WINKCHARM,
+ BS_UNFAIRLYTRICK,
+ BS_GREED,
+ PR_REDEMPTIO,
+ MO_KITRANSLATION,
+ MO_BALKYOUNG,
+ SA_ELEMENTGROUND,
+ SA_ELEMENTFIRE,
+ SA_ELEMENTWIND,
+
+ HLIF_HEAL = 8001,
+ HLIF_AVOID,
+ HLIF_BRAIN,
+ HLIF_CHANGE,
+ HAMI_CASTLE,
+ HAMI_DEFENCE,
+ HAMI_SKIN,
+ HAMI_BLOODLUST,
+ HFLI_MOON,
+ HFLI_FLEET,
+ HFLI_SPEED,
+ HFLI_SBR44,
+ HVAN_CAPRICE,
+ HVAN_CHAOTIC,
+ HVAN_INSTRUCT,
+ HVAN_EXPLOSION,
+};
+
+enum {
+ UNT_SAFETYWALL = 0x7e,
+ UNT_FIREWALL,
+ UNT_WARP_WAITING,
+ UNT_WARP_ACTIVE,
+ //0x82
+ UNT_SANCTUARY = 0x83,
+ UNT_MAGNUS,
+ UNT_PNEUMA,
+ UNT_ATTACK_SKILLS, //These show no effect on the client, therefore can be used for attack skills.
+ UNT_FIREPILLAR_WAITING,
+ UNT_FIREPILLAR_ACTIVE,
+ //0x89, 0x8a, 0x8b
+ UNT_USED_TRAPS = 0x8c,
+ UNT_ICEWALL,
+ UNT_QUAGMIRE,
+ UNT_BLASTMINE,
+ UNT_SKIDTRAP,
+ UNT_ANKLESNARE,
+ UNT_VENOMDUST,
+ UNT_LANDMINE,
+ UNT_SHOCKWAVE,
+ UNT_SANDMAN,
+ UNT_FLASHER,
+ UNT_FREEZINGTRAP,
+ UNT_CLAYMORETRAP,
+ UNT_TALKIEBOX,
+ UNT_VOLCANO,
+ UNT_DELUGE,
+ UNT_VIOLENTGALE,
+ UNT_LANDPROTECTOR,
+ UNT_LULLABY,
+ UNT_RICHMANKIM,
+ UNT_ETERNALCHAOS,
+ UNT_DRUMBATTLEFIELD,
+ UNT_RINGNIBELUNGEN,
+ UNT_ROKISWEIL,
+ UNT_INTOABYSS,
+ UNT_SIEGFRIED,
+ UNT_DISSONANCE,
+ UNT_WHISTLE,
+ UNT_ASSASSINCROSS,
+ UNT_POEMBRAGI,
+ UNT_APPLEIDUN,
+ UNT_UGLYDANCE,
+ UNT_HUMMING,
+ UNT_DONTFORGETME,
+ UNT_FORTUNEKISS,
+ UNT_SERVICEFORYOU,
+ UNT_GRAFFITI,
+ UNT_DEMONSTRATION,
+ UNT_CALLFAMILY,
+ UNT_GOSPEL,
+ UNT_BASILICA,
+ UNT_MOONLIT,//0xb5 //I HOPE this one doesn't shows any effects
+ UNT_FOGWALL = 0xb6,
+ UNT_SPIDERWEB,
+ UNT_GRAVITATION,
+ UNT_HERMODE,
+ UNT_DESPERADO, //0xba //Temporary setting until correct value is found.
+ UNT_SUITON = 0xbb,
+ UNT_TATAMIGAESHI,
+ UNT_KAENSIN,
+ UNT_GROUNDDRIFT_WIND,
+ UNT_GROUNDDRIFT_DARK,
+ UNT_GROUNDDRIFT_POISON,
+ UNT_GROUNDDRIFT_WATER,
+ UNT_GROUNDDRIFT_FIRE,
+};
+
+#endif
diff --git a/src/map/status.h b/src/map/status.h
index 5227edc68..c680ab89b 100644
--- a/src/map/status.h
+++ b/src/map/status.h
@@ -1,638 +1,638 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _STATUS_H_
-#define _STATUS_H_
-
-#include "map.h"
-
-//Use this to refer the max refinery level [Skotlex]
-#define MAX_REFINE 10
-#define MAX_REFINE_BONUS 5
-
-extern unsigned long StatusChangeFlagTable[];
-
-// Status changes listing. These code are for use by the server.
-enum {
- //First we enumerate common status ailments which are often used around.
- SC_STONE = 0,
- SC_FREEZE,
- SC_STUN,
- SC_SLEEP,
- SC_POISON,
- SC_CURSE,
- SC_SILENCE,
- SC_CONFUSION,
- SC_BLIND,
- SC_BLEEDING,
- SC_DPOISON, //10
-
- //Next up, we continue on 20, to leave enough room for additional "common" ailments in the future.
- SC_PROVOKE = 20,
- SC_ENDURE,
- SC_TWOHANDQUICKEN,
- SC_CONCENTRATE,
- SC_HIDING,
- SC_CLOAKING,
- SC_ENCPOISON,
- SC_POISONREACT,
- SC_QUAGMIRE,
- SC_ANGELUS,
- SC_BLESSING, //30
- SC_SIGNUMCRUCIS,
- SC_INCREASEAGI,
- SC_DECREASEAGI,
- SC_SLOWPOISON,
- SC_IMPOSITIO ,
- SC_SUFFRAGIUM,
- SC_ASPERSIO,
- SC_BENEDICTIO,
- SC_KYRIE,
- SC_MAGNIFICAT, //40
- SC_GLORIA,
- SC_AETERNA,
- SC_ADRENALINE,
- SC_WEAPONPERFECTION,
- SC_OVERTHRUST,
- SC_MAXIMIZEPOWER,
- SC_TRICKDEAD,
- SC_LOUD,
- SC_ENERGYCOAT,
- SC_BROKENARMOR, //50 - NOTE: These two aren't used anywhere, and they have an icon...
- SC_BROKENWEAPON,
- SC_HALLUCINATION,
- SC_WEIGHT50,
- SC_WEIGHT90,
- SC_ASPDPOTION0,
- SC_ASPDPOTION1,
- SC_ASPDPOTION2,
- SC_ASPDPOTION3,
- SC_SPEEDUP0,
- SC_SPEEDUP1, //60
- SC_ATKPOTION,
- SC_MATKPOTION,
- SC_WEDDING,
- SC_SLOWDOWN,
- SC_ANKLE,
- SC_KEEPING,
- SC_BARRIER,
- SC_STRIPWEAPON,
- SC_STRIPSHIELD,
- SC_STRIPARMOR, //70
- SC_STRIPHELM,
- SC_CP_WEAPON,
- SC_CP_SHIELD,
- SC_CP_ARMOR,
- SC_CP_HELM,
- SC_AUTOGUARD,
- SC_REFLECTSHIELD,
- SC_SPLASHER,
- SC_PROVIDENCE,
- SC_DEFENDER, //80
- SC_MAGICROD,
- SC_SPELLBREAKER,
- SC_AUTOSPELL,
- SC_SIGHTTRASHER,
- SC_AUTOBERSERK,
- SC_SPEARQUICKEN,
- SC_AUTOCOUNTER,
- SC_SIGHT,
- SC_SAFETYWALL,
- SC_RUWACH, //90
- SC_EXTREMITYFIST,
- SC_EXPLOSIONSPIRITS,
- SC_COMBO,
- SC_BLADESTOP_WAIT,
- SC_BLADESTOP,
- SC_FIREWEAPON,
- SC_WATERWEAPON,
- SC_WINDWEAPON,
- SC_EARTHWEAPON,
- SC_VOLCANO, //100,
- SC_DELUGE,
- SC_VIOLENTGALE,
- SC_WATK_ELEMENT,
- SC_ARMOR,
- SC_ARMOR_ELEMENT,
- SC_NOCHAT,
- SC_BABY,
- SC_AURABLADE,
- SC_PARRYING,
- SC_CONCENTRATION, //110
- SC_TENSIONRELAX,
- SC_BERSERK,
- SC_FURY,
- SC_GOSPEL,
- SC_ASSUMPTIO,
- SC_BASILICA,
- SC_GUILDAURA,
- SC_MAGICPOWER,
- SC_EDP,
- SC_TRUESIGHT, //120
- SC_WINDWALK,
- SC_MELTDOWN,
- SC_CARTBOOST,
- SC_CHASEWALK,
- SC_REJECTSWORD,
- SC_MARIONETTE,
- SC_MARIONETTE2,
- SC_UNUSED, //Unused (was SC_MOONLIT)
- SC_JOINTBEAT,
- SC_MINDBREAKER, //130
- SC_MEMORIZE,
- SC_FOGWALL,
- SC_SPIDERWEB,
- SC_DEVOTION,
- SC_SACRIFICE,
- SC_STEELBODY,
- SC_ORCISH,
- SC_READYSTORM,
- SC_READYDOWN,
- SC_READYTURN, //140
- SC_READYCOUNTER,
- SC_DODGE,
- SC_RUN,
- SC_SHADOWWEAPON,
- SC_ADRENALINE2,
- SC_GHOSTWEAPON,
- SC_KAIZEL,
- SC_KAAHI,
- SC_KAUPE,
- SC_ONEHAND, //150
- SC_PRESERVE,
- SC_BATTLEORDERS,
- SC_REGENERATION,
- SC_DOUBLECAST,
- SC_GRAVITATION,
- SC_MAXOVERTHRUST,
- SC_LONGING,
- SC_HERMODE,
- SC_SHRINK,
- SC_SIGHTBLASTER, //160
- SC_WINKCHARM,
- SC_CLOSECONFINE,
- SC_CLOSECONFINE2,
- SC_DANCING,
- SC_ELEMENTALCHANGE,
- SC_RICHMANKIM,
- SC_ETERNALCHAOS,
- SC_DRUMBATTLE,
- SC_NIBELUNGEN,
- SC_ROKISWEIL, //170
- SC_INTOABYSS,
- SC_SIEGFRIED,
- SC_WHISTLE,
- SC_ASSNCROS,
- SC_POEMBRAGI,
- SC_APPLEIDUN,
- SC_MODECHANGE,
- SC_HUMMING,
- SC_DONTFORGETME,
- SC_FORTUNE, //180
- SC_SERVICE4U,
- SC_STOP, //Prevents inflicted chars from walking. [Skotlex]
- SC_SPURT,
- SC_SPIRIT,
- SC_COMA, //Not a real SC_, it makes a char's HP/SP hit 1.
- SC_INTRAVISION,
- SC_INCALLSTATUS,
- SC_INCSTR,
- SC_INCAGI,
- SC_INCVIT, //190
- SC_INCINT,
- SC_INCDEX,
- SC_INCLUK,
- SC_INCHIT,
- SC_INCHITRATE,
- SC_INCFLEE,
- SC_INCFLEERATE,
- SC_INCMHPRATE,
- SC_INCMSPRATE,
- SC_INCATKRATE, //200
- SC_INCMATKRATE,
- SC_INCDEFRATE,
- SC_STRFOOD,
- SC_AGIFOOD,
- SC_VITFOOD,
- SC_INTFOOD,
- SC_DEXFOOD,
- SC_LUKFOOD,
- SC_HITFOOD,
- SC_FLEEFOOD, //210
- SC_BATKFOOD,
- SC_WATKFOOD,
- SC_MATKFOOD,
- SC_SCRESIST, //Increases resistance to status changes.
- SC_XMAS, // Xmas Suit [Valaris]
- SC_WARM, //SG skills [Komurka]
- SC_SUN_COMFORT,
- SC_MOON_COMFORT,
- SC_STAR_COMFORT,
- SC_FUSION, //220
- SC_SKILLRATE_UP,
- SC_SKE,
- SC_KAITE,
- SC_SWOO, // [marquis007]
- SC_SKA, // [marquis007]
- SC_TKREST, // [marquis007]
- SC_MIRACLE, //SG 'hidden' skill [Komurka]
- //Ninja/GS states
- SC_MADNESSCANCEL,
- SC_ADJUSTMENT,
- SC_INCREASING, //230
- SC_GATLINGFEVER,
- SC_TATAMIGAESHI,
- SC_UTSUSEMI,
- SC_BUNSINJYUTSU,
- SC_KAENSIN,
- SC_SUITON,
- SC_NEN,
- SC_KNOWLEDGE,
- SC_SMA,
- SC_FLING, //240
- SC_AVOID,
- SC_CHANGE,
- SC_BLOODLUST,
- SC_FLEET,
- SC_SPEED, //[orn]
- SC_DEFENCE, //[orn]
- SC_INCAGIRATE,
- SC_INCDEXRATE,
- SC_JAILED,
- SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex]
-};
-int SkillStatusChangeTable(int skill);
-extern int StatusSkillChangeTable[SC_MAX];
-
-//Numerates the Number for the status changes (client-dependent), imported from jA
-enum {
- SI_BLANK = -1,
- SI_PROVOKE = 0,
- SI_ENDURE = 1,
- SI_TWOHANDQUICKEN = 2,
- SI_CONCENTRATE = 3,
- SI_HIDING = 4,
- SI_CLOAKING = 5,
- SI_ENCPOISON = 6,
- SI_POISONREACT = 7,
- SI_QUAGMIRE = 8,
- SI_ANGELUS = 9,
- SI_BLESSING = 10,
- SI_SIGNUMCRUCIS = 11,
- SI_INCREASEAGI = 12,
- SI_DECREASEAGI = 13,
- SI_SLOWPOISON = 14,
- SI_IMPOSITIO = 15,
- SI_SUFFRAGIUM = 16,
- SI_ASPERSIO = 17,
- SI_BENEDICTIO = 18,
- SI_KYRIE = 19,
- SI_MAGNIFICAT = 20,
- SI_GLORIA = 21,
- SI_AETERNA = 22,
- SI_ADRENALINE = 23,
- SI_WEAPONPERFECTION = 24,
- SI_OVERTHRUST = 25,
- SI_MAXIMIZEPOWER = 26,
- SI_RIDING = 27,
- SI_FALCON = 28,
- SI_TRICKDEAD = 29,
- SI_LOUD = 30,
- SI_ENERGYCOAT = 31,
- SI_BROKENARMOR = 32,
- SI_BROKENWEAPON = 33,
- SI_HALLUCINATION = 34,
- SI_WEIGHT50 = 35,
- SI_WEIGHT90 = 36,
- SI_ASPDPOTION = 37,
- //38: Again Aspd Potion
- //39: Again Aspd Potion
- //40: Again Aspd Potion
- SI_SPEEDPOTION1 = 41,
- SI_SPEEDPOTION2 = 42,
- SI_STRIPWEAPON = 50,
- SI_STRIPSHIELD = 51,
- SI_STRIPARMOR = 52,
- SI_STRIPHELM = 53,
- SI_CP_WEAPON = 54,
- SI_CP_SHIELD = 55,
- SI_CP_ARMOR = 56,
- SI_CP_HELM = 57,
- SI_AUTOGUARD = 58,
- SI_REFLECTSHIELD = 59,
- SI_PROVIDENCE = 61,
- SI_DEFENDER = 62,
- SI_AUTOSPELL = 65,
- SI_SPEARQUICKEN = 68,
- SI_EXPLOSIONSPIRITS = 86,
- SI_FURY = 87,
- SI_FIREWEAPON = 90,
- SI_WATERWEAPON = 91,
- SI_WINDWEAPON = 92,
- SI_EARTHWEAPON = 93,
-// 102 = again gloria - from what I saw on screenshots, I wonder if it isn't gospel... [DracoRPG]
- SI_AURABLADE = 103,
- SI_PARRYING = 104,
- SI_CONCENTRATION = 105,
- SI_TENSIONRELAX = 106,
- SI_BERSERK = 107,
- SI_ASSUMPTIO = 110,
- SI_LANDENDOW = 112,
- SI_MAGICPOWER = 113,
- SI_EDP = 114,
- SI_TRUESIGHT = 115,
- SI_WINDWALK = 116,
- SI_MELTDOWN = 117,
- SI_CARTBOOST = 118,
- //119, blank
- SI_REJECTSWORD = 120,
- SI_MARIONETTE = 121,
- SI_MARIONETTE2 = 122,
- SI_MOONLIT = 123,
- SI_BLEEDING = 124,
- SI_JOINTBEAT = 125,
- SI_DEVOTION = 130,
- SI_STEELBODY = 132,
- SI_RUN = 133,
- SI_BUMP = 134,
- SI_READYSTORM = 135,
- SI_READYDOWN = 137,
- SI_READYTURN = 139,
- SI_READYCOUNTER = 141,
- SI_DODGE = 143,
- //SI_RUN = 144, //is not RUN. need info on what this is.
- SI_SPURT = 145,
- SI_SHADOWWEAPON = 146,
- SI_ADRENALINE2 = 147,
- SI_GHOSTWEAPON = 148,
- SI_NIGHT = 149,
- SI_SPIRIT = 149,
- SI_DEVIL = 152,
- SI_KAITE = 153,
- SI_KAIZEL = 156,
- SI_KAAHI = 157,
- SI_KAUPE = 158,
- SI_SMA = 159,
-// 160
- SI_ONEHAND = 161,
- SI_WARM = 165,
-// 166 | The three show the exact same display: ultra red character (165, 166, 167)
-// 167 |
- SI_SUN_COMFORT = 169,
- SI_MOON_COMFORT = 170,
- SI_STAR_COMFORT = 171,
- SI_PRESERVE = 181,
- SI_INCSTR = 182,
- SI_INTRAVISION = 184, //WTF?? creates the black shape of 4_m_02 NPC, with NPC talk cursor. Supposedly intravision shows this.
- SI_DOUBLECAST = 186,
- SI_MAXOVERTHRUST = 188,
- SI_TAROT = 191, // the icon allows no doubt... but what is it really used for ?? [DracoRPG]
- SI_SHRINK = 197,
- SI_SIGHTBLASTER = 198,
- SI_WINKCHARM = 199,
- SI_CLOSECONFINE = 200,
- SI_CLOSECONFINE2 = 201,
- SI_MADNESSCANCEL = 203, //[blackhole89]
- SI_GATLINGFEVER = 204,
- SI_TKREST = 205, // 205 = Gloria again (but TK- Happy State looks like it)
- SI_UTSUSEMI = 206,
- SI_BUNSINJYUTSU = 207,
- SI_NEN = 208,
- SI_ADJUSTMENT = 209,
- SI_ACCURACY = 210
-};
-
-extern int current_equip_item_index;
-extern int current_equip_card_id;
-
-extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex]
-
-//Mode definitions to clear up code reading. [Skotlex]
-#define MD_CANMOVE 0x001
-#define MD_LOOTER 0x002
-//MD_ANGRY mobs are also aggressive.
-#define MD_AGGRESSIVE 0x804
-#define MD_ASSIST 0x008
-#define MD_CASTSENSOR 0x010
-#define MD_BOSS 0x020
-#define MD_PLANT 0x040
-#define MD_CANATTACK 0x080
-#define MD_DETECTOR 0x100
-#define MD_CHANGETARGET 0x200
-#define MD_CHANGECHASE 0x400
-#define MD_ANGRY 0x800
-#define MD_MASK 0xFFF
-
-//Status change option definitions (options are what makes status changes visible to chars
-//who were not on your field of sight when it happened)
-//opt1: Non stackable status changes.
-enum {
- OPT1_STONE = 1, //Petrified
- OPT1_FREEZE,
- OPT1_STUN,
- OPT1_SLEEP,
- //What is 5?
- OPT1_STONEWAIT=6 //Petrifying
-};
-
-//opt2: Stackable status changes.
-#define OPT2_POISON 0x001
-#define OPT2_CURSE 0x002
-#define OPT2_SILENCE 0x004
-#define OPT2_SIGNUMCRUCIS 0x008
-#define OPT2_BLIND 0x010
-//0x020 - nothing
-//0x040 - nothing
-#define OPT2_DPOISON 0x080
-//0x100
-
-#define OPTION_SIGHT 0x00000001
-#define OPTION_HIDE 0x00000002
-#define OPTION_CLOAK 0x00000004
-#define OPTION_CART1 0x00000008
-#define OPTION_FALCON 0x00000010
-#define OPTION_RIDING 0x00000020
-#define OPTION_INVISIBLE 0x00000040
-#define OPTION_CART2 0x00000080
-#define OPTION_CART3 0x00000100
-#define OPTION_CART4 0x00000200
-#define OPTION_CART5 0x00000400
-#define OPTION_ORCISH 0x00000800
-#define OPTION_WEDDING 0x00001000
-#define OPTION_RUWACH 0x00002000
-#define OPTION_CHASEWALK 0x00004000
-//Note that clientside Flying and Xmas are 0x8000!!
-#define OPTION_XMAS 0x00020000
-#define OPTION_FLYING 0x0008000
-//TODO: Get these Missing options...
-#define OPTION_SIGHTTRASHER 0x00010000
-
-#define OPTION_CART (OPTION_CART1|OPTION_CART2|OPTION_CART3|OPTION_CART4|OPTION_CART5)
-
-//Defines for the manner system [Skotlex]
-#define MANNER_NOCHAT 0x01
-#define MANNER_NOSKILL 0x02
-#define MANNER_NOCOMMAND 0x04
-#define MANNER_NOITEM 0x08
-#define MANNER_NOROOM 0x10
-
-//Define flags for the status_calc_bl function. [Skotlex]
-#define SCB_NONE 0x00000000
-#define SCB_BASE 0x00000001
-#define SCB_MAXHP 0x00000002
-#define SCB_MAXSP 0x00000004
-#define SCB_STR 0x00000008
-#define SCB_AGI 0x00000010
-#define SCB_VIT 0x00000020
-#define SCB_INT 0x00000040
-#define SCB_DEX 0x00000080
-#define SCB_LUK 0x00000100
-#define SCB_BATK 0x00000200
-#define SCB_WATK 0x00000400
-#define SCB_MATK 0x00000800
-#define SCB_HIT 0x00001000
-#define SCB_FLEE 0x00002000
-#define SCB_DEF 0x00004000
-#define SCB_DEF2 0x00008000
-#define SCB_MDEF 0x00010000
-#define SCB_MDEF2 0x00020000
-#define SCB_SPEED 0x00040000
-#define SCB_ASPD 0x00080000
-#define SCB_DSPD 0x00100000
-#define SCB_CRI 0x00200000
-#define SCB_FLEE2 0x00400000
-#define SCB_ATK_ELE 0x00800000
-#define SCB_DEF_ELE 0x01000000
-#define SCB_MODE 0x02000000
-#define SCB_SIZE 0x04000000
-#define SCB_RACE 0x08000000
-#define SCB_RANGE 0x10000000
-#define SCB_REGEN 0x20000000
-//SCB_DYE means the sc should force cloth-dye change to 0 to avoid client crashes.
-#define SCB_DYE 0x40000000
-#define SCB_PC 0x80000000
-#define SCB_ALL 0x3FFFFFFF
-
-//Define to determine who gets HP/SP consumed on doing skills/etc. [Skotlex]
-#define BL_CONSUME (BL_PC|BL_HOM)
-//Define to determine who has regen
-#define BL_REGEN (BL_PC|BL_HOM)
-
-int status_damage(struct block_list *src,struct block_list *target,int hp,int sp, int walkdelay, int flag);
-//Define for standard HP damage attacks.
-#define status_fix_damage(src, target, hp, walkdelay) status_damage(src, target, hp, 0, walkdelay, 0)
-//Define for standard HP/SP damage triggers.
-#define status_zap(bl, hp, sp) status_damage(NULL, bl, hp, sp, 0, 1)
-//Define for standard HP/SP skill-related cost triggers (mobs require no HP/SP to use skills)
-#define status_charge(bl, hp, sp) (!((bl)->type&BL_CONSUME) || status_damage(NULL, bl, hp, sp, 0, 3))
-int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag);
-//Easier handling of status_percent_change
-#define status_percent_heal(bl, hp_rate, sp_rate) status_percent_change(NULL, bl, -(hp_rate), -(sp_rate), 1)
-#define status_percent_damage(src, target, hp_rate, sp_rate) status_percent_change(src, target, hp_rate, sp_rate, 0)
-//Instant kill with no drops/exp/etc
-//
-#define status_kill(bl) status_percent_damage(NULL, bl, 100, 0)
-//Used to set the hp/sp of an object to an absolute value (can't kill)
-int status_set_hp(struct block_list *bl, unsigned int hp, int flag);
-int status_set_sp(struct block_list *bl, unsigned int sp, int flag);
-int status_heal(struct block_list *bl,int hp,int sp, int flag);
-int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp);
-
-//Define for copying a status_data structure from b to a, without overwriting current Hp and Sp, nor messing the lhw pointer.
-#define status_cpy(a, b) { memcpy(&((a)->max_hp), &((b)->max_hp), sizeof(struct status_data)-(sizeof((a)->hp)+sizeof((a)->sp)+sizeof((a)->lhw))); \
- if ((a)->lhw && (b)->lhw) { memcpy((a)->lhw, (b)->lhw, sizeof(struct weapon_atk)); }}
-
-struct regen_data *status_get_regen_data(struct block_list *bl);
-struct status_data *status_get_status_data(struct block_list *bl);
-struct status_data *status_get_base_status(struct block_list *bl);
-const char * status_get_name(struct block_list *bl);
-int status_get_class(struct block_list *bl);
-int status_get_lv(struct block_list *bl);
-#define status_get_range(bl) status_get_status_data(bl)->rhw.range
-#define status_get_hp(bl) status_get_status_data(bl)->hp
-#define status_get_max_hp(bl) status_get_status_data(bl)->max_hp
-#define status_get_sp(bl) status_get_status_data(bl)->sp
-#define status_get_max_sp(bl) status_get_status_data(bl)->max_sp
-#define status_get_str(bl) status_get_status_data(bl)->str
-#define status_get_agi(bl) status_get_status_data(bl)->agi
-#define status_get_vit(bl) status_get_status_data(bl)->vit
-#define status_get_int(bl) status_get_status_data(bl)->int_
-#define status_get_dex(bl) status_get_status_data(bl)->dex
-#define status_get_luk(bl) status_get_status_data(bl)->luk
-#define status_get_hit(bl) status_get_status_data(bl)->hit
-#define status_get_flee(bl) status_get_status_data(bl)->flee
-unsigned char status_get_def(struct block_list *bl);
-#define status_get_mdef(bl) status_get_status_data(bl)->mdef
-#define status_get_flee2(bl) status_get_status_data(bl)->flee2
-#define status_get_def2(bl) status_get_status_data(bl)->def2
-#define status_get_mdef2(bl) status_get_status_data(bl)->mdef2
-#define status_get_critical(bl) status_get_status_data(bl)->cri
-#define status_get_batk(bl) status_get_status_data(bl)->batk
-#define status_get_watk(bl) status_get_status_data(bl)->rhw.atk
-#define status_get_watk2(bl) status_get_status_data(bl)->rhw.atk2
-#define status_get_matk_max(bl) status_get_status_data(bl)->matk_max
-#define status_get_matk_min(bl) status_get_status_data(bl)->matk_min
-unsigned short status_get_lwatk(struct block_list *bl);
-unsigned short status_get_lwatk2(struct block_list *bl);
-unsigned short status_get_speed(struct block_list *bl);
-#define status_get_adelay(bl) status_get_status_data(bl)->adelay
-#define status_get_amotion(bl) status_get_status_data(bl)->amotion
-#define status_get_dmotion(bl) status_get_status_data(bl)->dmotion
-#define status_get_element(bl) status_get_status_data(bl)->def_ele
-#define status_get_element_level(bl) status_get_status_data(bl)->ele_lv
-unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element);
-#define status_get_attack_sc_element(bl, sc) status_calc_attack_element(bl, sc, 0)
-#define status_get_attack_element(bl) status_get_status_data(bl)->rhw.ele
-unsigned char status_get_attack_lelement(struct block_list *bl);
-#define status_get_race(bl) status_get_status_data(bl)->race
-#define status_get_size(bl) status_get_status_data(bl)->size
-#define status_get_mode(bl) status_get_status_data(bl)->mode
-int status_get_party_id(struct block_list *bl);
-int status_get_guild_id(struct block_list *bl);
-int status_get_mexp(struct block_list *bl);
-int status_get_race2(struct block_list *bl);
-
-struct view_data *status_get_viewdata(struct block_list *bl);
-void status_set_viewdata(struct block_list *bl, int class_);
-void status_change_init(struct block_list *bl);
-struct status_change *status_get_sc(struct block_list *bl);
-
-int status_isdead(struct block_list *bl);
-int status_isimmune(struct block_list *bl);
-
-int status_get_sc_def(struct block_list *bl, int type);
-#define status_get_sc_def_mdef(bl) (status_get_sc_def(bl, SP_MDEF1))
-#define status_get_sc_def_vit(bl) (status_get_sc_def(bl, SP_DEF2))
-#define status_get_sc_def_int(bl) (status_get_sc_def(bl, SP_MDEF2))
-#define status_get_sc_def_luk(bl) (status_get_sc_def(bl, SP_LUK))
-
-//Short version, receives rate in 1->100 range, and does not uses a flag setting.
-#define sc_start(bl, type, rate, val1, tick) status_change_start(bl,type,100*(rate),val1,0,0,0,tick,0)
-#define sc_start4(bl, type, rate, val1, val2, val3, val4, tick) status_change_start(bl,type,100*(rate),val1,val2,val3,val4,tick,0)
-
-int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag);
-int status_change_end( struct block_list* bl , int type,int tid );
-int kaahi_heal_timer(int tid, unsigned int tick, int id, int data);
-int status_change_timer(int tid, unsigned int tick, int id, int data);
-int status_change_timer_sub(struct block_list *bl, va_list ap );
-int status_change_clear(struct block_list *bl,int type);
-int status_change_clear_buffs(struct block_list *bl, int type);
-
-void status_calc_bl(struct block_list *bl, unsigned long flag);
-int status_calc_pet(struct pet_data* pd, int first); // [Skotlex]
-int status_calc_pc(struct map_session_data* sd,int first);
-int status_calc_mob(struct mob_data* md, int first); //[Skotlex]
-int status_calc_homunculus(struct homun_data *hd, int first);
-void status_calc_misc(struct block_list *bl, struct status_data *status, int level);
-void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen);
-void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc);
-
-void status_freecast_switch(struct map_session_data *sd);
-int status_getrefinebonus(int lv,int type);
-int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag); // [Skotlex]
-int status_check_visibility(struct block_list *src, struct block_list *target); //[Skotlex]
-
-int status_readdb(void);
-int do_init_status(void);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _STATUS_H_
+#define _STATUS_H_
+
+#include "map.h"
+
+//Use this to refer the max refinery level [Skotlex]
+#define MAX_REFINE 10
+#define MAX_REFINE_BONUS 5
+
+extern unsigned long StatusChangeFlagTable[];
+
+// Status changes listing. These code are for use by the server.
+enum {
+ //First we enumerate common status ailments which are often used around.
+ SC_STONE = 0,
+ SC_FREEZE,
+ SC_STUN,
+ SC_SLEEP,
+ SC_POISON,
+ SC_CURSE,
+ SC_SILENCE,
+ SC_CONFUSION,
+ SC_BLIND,
+ SC_BLEEDING,
+ SC_DPOISON, //10
+
+ //Next up, we continue on 20, to leave enough room for additional "common" ailments in the future.
+ SC_PROVOKE = 20,
+ SC_ENDURE,
+ SC_TWOHANDQUICKEN,
+ SC_CONCENTRATE,
+ SC_HIDING,
+ SC_CLOAKING,
+ SC_ENCPOISON,
+ SC_POISONREACT,
+ SC_QUAGMIRE,
+ SC_ANGELUS,
+ SC_BLESSING, //30
+ SC_SIGNUMCRUCIS,
+ SC_INCREASEAGI,
+ SC_DECREASEAGI,
+ SC_SLOWPOISON,
+ SC_IMPOSITIO ,
+ SC_SUFFRAGIUM,
+ SC_ASPERSIO,
+ SC_BENEDICTIO,
+ SC_KYRIE,
+ SC_MAGNIFICAT, //40
+ SC_GLORIA,
+ SC_AETERNA,
+ SC_ADRENALINE,
+ SC_WEAPONPERFECTION,
+ SC_OVERTHRUST,
+ SC_MAXIMIZEPOWER,
+ SC_TRICKDEAD,
+ SC_LOUD,
+ SC_ENERGYCOAT,
+ SC_BROKENARMOR, //50 - NOTE: These two aren't used anywhere, and they have an icon...
+ SC_BROKENWEAPON,
+ SC_HALLUCINATION,
+ SC_WEIGHT50,
+ SC_WEIGHT90,
+ SC_ASPDPOTION0,
+ SC_ASPDPOTION1,
+ SC_ASPDPOTION2,
+ SC_ASPDPOTION3,
+ SC_SPEEDUP0,
+ SC_SPEEDUP1, //60
+ SC_ATKPOTION,
+ SC_MATKPOTION,
+ SC_WEDDING,
+ SC_SLOWDOWN,
+ SC_ANKLE,
+ SC_KEEPING,
+ SC_BARRIER,
+ SC_STRIPWEAPON,
+ SC_STRIPSHIELD,
+ SC_STRIPARMOR, //70
+ SC_STRIPHELM,
+ SC_CP_WEAPON,
+ SC_CP_SHIELD,
+ SC_CP_ARMOR,
+ SC_CP_HELM,
+ SC_AUTOGUARD,
+ SC_REFLECTSHIELD,
+ SC_SPLASHER,
+ SC_PROVIDENCE,
+ SC_DEFENDER, //80
+ SC_MAGICROD,
+ SC_SPELLBREAKER,
+ SC_AUTOSPELL,
+ SC_SIGHTTRASHER,
+ SC_AUTOBERSERK,
+ SC_SPEARQUICKEN,
+ SC_AUTOCOUNTER,
+ SC_SIGHT,
+ SC_SAFETYWALL,
+ SC_RUWACH, //90
+ SC_EXTREMITYFIST,
+ SC_EXPLOSIONSPIRITS,
+ SC_COMBO,
+ SC_BLADESTOP_WAIT,
+ SC_BLADESTOP,
+ SC_FIREWEAPON,
+ SC_WATERWEAPON,
+ SC_WINDWEAPON,
+ SC_EARTHWEAPON,
+ SC_VOLCANO, //100,
+ SC_DELUGE,
+ SC_VIOLENTGALE,
+ SC_WATK_ELEMENT,
+ SC_ARMOR,
+ SC_ARMOR_ELEMENT,
+ SC_NOCHAT,
+ SC_BABY,
+ SC_AURABLADE,
+ SC_PARRYING,
+ SC_CONCENTRATION, //110
+ SC_TENSIONRELAX,
+ SC_BERSERK,
+ SC_FURY,
+ SC_GOSPEL,
+ SC_ASSUMPTIO,
+ SC_BASILICA,
+ SC_GUILDAURA,
+ SC_MAGICPOWER,
+ SC_EDP,
+ SC_TRUESIGHT, //120
+ SC_WINDWALK,
+ SC_MELTDOWN,
+ SC_CARTBOOST,
+ SC_CHASEWALK,
+ SC_REJECTSWORD,
+ SC_MARIONETTE,
+ SC_MARIONETTE2,
+ SC_UNUSED, //Unused (was SC_MOONLIT)
+ SC_JOINTBEAT,
+ SC_MINDBREAKER, //130
+ SC_MEMORIZE,
+ SC_FOGWALL,
+ SC_SPIDERWEB,
+ SC_DEVOTION,
+ SC_SACRIFICE,
+ SC_STEELBODY,
+ SC_ORCISH,
+ SC_READYSTORM,
+ SC_READYDOWN,
+ SC_READYTURN, //140
+ SC_READYCOUNTER,
+ SC_DODGE,
+ SC_RUN,
+ SC_SHADOWWEAPON,
+ SC_ADRENALINE2,
+ SC_GHOSTWEAPON,
+ SC_KAIZEL,
+ SC_KAAHI,
+ SC_KAUPE,
+ SC_ONEHAND, //150
+ SC_PRESERVE,
+ SC_BATTLEORDERS,
+ SC_REGENERATION,
+ SC_DOUBLECAST,
+ SC_GRAVITATION,
+ SC_MAXOVERTHRUST,
+ SC_LONGING,
+ SC_HERMODE,
+ SC_SHRINK,
+ SC_SIGHTBLASTER, //160
+ SC_WINKCHARM,
+ SC_CLOSECONFINE,
+ SC_CLOSECONFINE2,
+ SC_DANCING,
+ SC_ELEMENTALCHANGE,
+ SC_RICHMANKIM,
+ SC_ETERNALCHAOS,
+ SC_DRUMBATTLE,
+ SC_NIBELUNGEN,
+ SC_ROKISWEIL, //170
+ SC_INTOABYSS,
+ SC_SIEGFRIED,
+ SC_WHISTLE,
+ SC_ASSNCROS,
+ SC_POEMBRAGI,
+ SC_APPLEIDUN,
+ SC_MODECHANGE,
+ SC_HUMMING,
+ SC_DONTFORGETME,
+ SC_FORTUNE, //180
+ SC_SERVICE4U,
+ SC_STOP, //Prevents inflicted chars from walking. [Skotlex]
+ SC_SPURT,
+ SC_SPIRIT,
+ SC_COMA, //Not a real SC_, it makes a char's HP/SP hit 1.
+ SC_INTRAVISION,
+ SC_INCALLSTATUS,
+ SC_INCSTR,
+ SC_INCAGI,
+ SC_INCVIT, //190
+ SC_INCINT,
+ SC_INCDEX,
+ SC_INCLUK,
+ SC_INCHIT,
+ SC_INCHITRATE,
+ SC_INCFLEE,
+ SC_INCFLEERATE,
+ SC_INCMHPRATE,
+ SC_INCMSPRATE,
+ SC_INCATKRATE, //200
+ SC_INCMATKRATE,
+ SC_INCDEFRATE,
+ SC_STRFOOD,
+ SC_AGIFOOD,
+ SC_VITFOOD,
+ SC_INTFOOD,
+ SC_DEXFOOD,
+ SC_LUKFOOD,
+ SC_HITFOOD,
+ SC_FLEEFOOD, //210
+ SC_BATKFOOD,
+ SC_WATKFOOD,
+ SC_MATKFOOD,
+ SC_SCRESIST, //Increases resistance to status changes.
+ SC_XMAS, // Xmas Suit [Valaris]
+ SC_WARM, //SG skills [Komurka]
+ SC_SUN_COMFORT,
+ SC_MOON_COMFORT,
+ SC_STAR_COMFORT,
+ SC_FUSION, //220
+ SC_SKILLRATE_UP,
+ SC_SKE,
+ SC_KAITE,
+ SC_SWOO, // [marquis007]
+ SC_SKA, // [marquis007]
+ SC_TKREST, // [marquis007]
+ SC_MIRACLE, //SG 'hidden' skill [Komurka]
+ //Ninja/GS states
+ SC_MADNESSCANCEL,
+ SC_ADJUSTMENT,
+ SC_INCREASING, //230
+ SC_GATLINGFEVER,
+ SC_TATAMIGAESHI,
+ SC_UTSUSEMI,
+ SC_BUNSINJYUTSU,
+ SC_KAENSIN,
+ SC_SUITON,
+ SC_NEN,
+ SC_KNOWLEDGE,
+ SC_SMA,
+ SC_FLING, //240
+ SC_AVOID,
+ SC_CHANGE,
+ SC_BLOODLUST,
+ SC_FLEET,
+ SC_SPEED, //[orn]
+ SC_DEFENCE, //[orn]
+ SC_INCAGIRATE,
+ SC_INCDEXRATE,
+ SC_JAILED,
+ SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex]
+};
+int SkillStatusChangeTable(int skill);
+extern int StatusSkillChangeTable[SC_MAX];
+
+//Numerates the Number for the status changes (client-dependent), imported from jA
+enum {
+ SI_BLANK = -1,
+ SI_PROVOKE = 0,
+ SI_ENDURE = 1,
+ SI_TWOHANDQUICKEN = 2,
+ SI_CONCENTRATE = 3,
+ SI_HIDING = 4,
+ SI_CLOAKING = 5,
+ SI_ENCPOISON = 6,
+ SI_POISONREACT = 7,
+ SI_QUAGMIRE = 8,
+ SI_ANGELUS = 9,
+ SI_BLESSING = 10,
+ SI_SIGNUMCRUCIS = 11,
+ SI_INCREASEAGI = 12,
+ SI_DECREASEAGI = 13,
+ SI_SLOWPOISON = 14,
+ SI_IMPOSITIO = 15,
+ SI_SUFFRAGIUM = 16,
+ SI_ASPERSIO = 17,
+ SI_BENEDICTIO = 18,
+ SI_KYRIE = 19,
+ SI_MAGNIFICAT = 20,
+ SI_GLORIA = 21,
+ SI_AETERNA = 22,
+ SI_ADRENALINE = 23,
+ SI_WEAPONPERFECTION = 24,
+ SI_OVERTHRUST = 25,
+ SI_MAXIMIZEPOWER = 26,
+ SI_RIDING = 27,
+ SI_FALCON = 28,
+ SI_TRICKDEAD = 29,
+ SI_LOUD = 30,
+ SI_ENERGYCOAT = 31,
+ SI_BROKENARMOR = 32,
+ SI_BROKENWEAPON = 33,
+ SI_HALLUCINATION = 34,
+ SI_WEIGHT50 = 35,
+ SI_WEIGHT90 = 36,
+ SI_ASPDPOTION = 37,
+ //38: Again Aspd Potion
+ //39: Again Aspd Potion
+ //40: Again Aspd Potion
+ SI_SPEEDPOTION1 = 41,
+ SI_SPEEDPOTION2 = 42,
+ SI_STRIPWEAPON = 50,
+ SI_STRIPSHIELD = 51,
+ SI_STRIPARMOR = 52,
+ SI_STRIPHELM = 53,
+ SI_CP_WEAPON = 54,
+ SI_CP_SHIELD = 55,
+ SI_CP_ARMOR = 56,
+ SI_CP_HELM = 57,
+ SI_AUTOGUARD = 58,
+ SI_REFLECTSHIELD = 59,
+ SI_PROVIDENCE = 61,
+ SI_DEFENDER = 62,
+ SI_AUTOSPELL = 65,
+ SI_SPEARQUICKEN = 68,
+ SI_EXPLOSIONSPIRITS = 86,
+ SI_FURY = 87,
+ SI_FIREWEAPON = 90,
+ SI_WATERWEAPON = 91,
+ SI_WINDWEAPON = 92,
+ SI_EARTHWEAPON = 93,
+// 102 = again gloria - from what I saw on screenshots, I wonder if it isn't gospel... [DracoRPG]
+ SI_AURABLADE = 103,
+ SI_PARRYING = 104,
+ SI_CONCENTRATION = 105,
+ SI_TENSIONRELAX = 106,
+ SI_BERSERK = 107,
+ SI_ASSUMPTIO = 110,
+ SI_LANDENDOW = 112,
+ SI_MAGICPOWER = 113,
+ SI_EDP = 114,
+ SI_TRUESIGHT = 115,
+ SI_WINDWALK = 116,
+ SI_MELTDOWN = 117,
+ SI_CARTBOOST = 118,
+ //119, blank
+ SI_REJECTSWORD = 120,
+ SI_MARIONETTE = 121,
+ SI_MARIONETTE2 = 122,
+ SI_MOONLIT = 123,
+ SI_BLEEDING = 124,
+ SI_JOINTBEAT = 125,
+ SI_DEVOTION = 130,
+ SI_STEELBODY = 132,
+ SI_RUN = 133,
+ SI_BUMP = 134,
+ SI_READYSTORM = 135,
+ SI_READYDOWN = 137,
+ SI_READYTURN = 139,
+ SI_READYCOUNTER = 141,
+ SI_DODGE = 143,
+ //SI_RUN = 144, //is not RUN. need info on what this is.
+ SI_SPURT = 145,
+ SI_SHADOWWEAPON = 146,
+ SI_ADRENALINE2 = 147,
+ SI_GHOSTWEAPON = 148,
+ SI_NIGHT = 149,
+ SI_SPIRIT = 149,
+ SI_DEVIL = 152,
+ SI_KAITE = 153,
+ SI_KAIZEL = 156,
+ SI_KAAHI = 157,
+ SI_KAUPE = 158,
+ SI_SMA = 159,
+// 160
+ SI_ONEHAND = 161,
+ SI_WARM = 165,
+// 166 | The three show the exact same display: ultra red character (165, 166, 167)
+// 167 |
+ SI_SUN_COMFORT = 169,
+ SI_MOON_COMFORT = 170,
+ SI_STAR_COMFORT = 171,
+ SI_PRESERVE = 181,
+ SI_INCSTR = 182,
+ SI_INTRAVISION = 184, //WTF?? creates the black shape of 4_m_02 NPC, with NPC talk cursor. Supposedly intravision shows this.
+ SI_DOUBLECAST = 186,
+ SI_MAXOVERTHRUST = 188,
+ SI_TAROT = 191, // the icon allows no doubt... but what is it really used for ?? [DracoRPG]
+ SI_SHRINK = 197,
+ SI_SIGHTBLASTER = 198,
+ SI_WINKCHARM = 199,
+ SI_CLOSECONFINE = 200,
+ SI_CLOSECONFINE2 = 201,
+ SI_MADNESSCANCEL = 203, //[blackhole89]
+ SI_GATLINGFEVER = 204,
+ SI_TKREST = 205, // 205 = Gloria again (but TK- Happy State looks like it)
+ SI_UTSUSEMI = 206,
+ SI_BUNSINJYUTSU = 207,
+ SI_NEN = 208,
+ SI_ADJUSTMENT = 209,
+ SI_ACCURACY = 210
+};
+
+extern int current_equip_item_index;
+extern int current_equip_card_id;
+
+extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex]
+
+//Mode definitions to clear up code reading. [Skotlex]
+#define MD_CANMOVE 0x001
+#define MD_LOOTER 0x002
+//MD_ANGRY mobs are also aggressive.
+#define MD_AGGRESSIVE 0x804
+#define MD_ASSIST 0x008
+#define MD_CASTSENSOR 0x010
+#define MD_BOSS 0x020
+#define MD_PLANT 0x040
+#define MD_CANATTACK 0x080
+#define MD_DETECTOR 0x100
+#define MD_CHANGETARGET 0x200
+#define MD_CHANGECHASE 0x400
+#define MD_ANGRY 0x800
+#define MD_MASK 0xFFF
+
+//Status change option definitions (options are what makes status changes visible to chars
+//who were not on your field of sight when it happened)
+//opt1: Non stackable status changes.
+enum {
+ OPT1_STONE = 1, //Petrified
+ OPT1_FREEZE,
+ OPT1_STUN,
+ OPT1_SLEEP,
+ //What is 5?
+ OPT1_STONEWAIT=6 //Petrifying
+};
+
+//opt2: Stackable status changes.
+#define OPT2_POISON 0x001
+#define OPT2_CURSE 0x002
+#define OPT2_SILENCE 0x004
+#define OPT2_SIGNUMCRUCIS 0x008
+#define OPT2_BLIND 0x010
+//0x020 - nothing
+//0x040 - nothing
+#define OPT2_DPOISON 0x080
+//0x100
+
+#define OPTION_SIGHT 0x00000001
+#define OPTION_HIDE 0x00000002
+#define OPTION_CLOAK 0x00000004
+#define OPTION_CART1 0x00000008
+#define OPTION_FALCON 0x00000010
+#define OPTION_RIDING 0x00000020
+#define OPTION_INVISIBLE 0x00000040
+#define OPTION_CART2 0x00000080
+#define OPTION_CART3 0x00000100
+#define OPTION_CART4 0x00000200
+#define OPTION_CART5 0x00000400
+#define OPTION_ORCISH 0x00000800
+#define OPTION_WEDDING 0x00001000
+#define OPTION_RUWACH 0x00002000
+#define OPTION_CHASEWALK 0x00004000
+//Note that clientside Flying and Xmas are 0x8000!!
+#define OPTION_XMAS 0x00020000
+#define OPTION_FLYING 0x0008000
+//TODO: Get these Missing options...
+#define OPTION_SIGHTTRASHER 0x00010000
+
+#define OPTION_CART (OPTION_CART1|OPTION_CART2|OPTION_CART3|OPTION_CART4|OPTION_CART5)
+
+//Defines for the manner system [Skotlex]
+#define MANNER_NOCHAT 0x01
+#define MANNER_NOSKILL 0x02
+#define MANNER_NOCOMMAND 0x04
+#define MANNER_NOITEM 0x08
+#define MANNER_NOROOM 0x10
+
+//Define flags for the status_calc_bl function. [Skotlex]
+#define SCB_NONE 0x00000000
+#define SCB_BASE 0x00000001
+#define SCB_MAXHP 0x00000002
+#define SCB_MAXSP 0x00000004
+#define SCB_STR 0x00000008
+#define SCB_AGI 0x00000010
+#define SCB_VIT 0x00000020
+#define SCB_INT 0x00000040
+#define SCB_DEX 0x00000080
+#define SCB_LUK 0x00000100
+#define SCB_BATK 0x00000200
+#define SCB_WATK 0x00000400
+#define SCB_MATK 0x00000800
+#define SCB_HIT 0x00001000
+#define SCB_FLEE 0x00002000
+#define SCB_DEF 0x00004000
+#define SCB_DEF2 0x00008000
+#define SCB_MDEF 0x00010000
+#define SCB_MDEF2 0x00020000
+#define SCB_SPEED 0x00040000
+#define SCB_ASPD 0x00080000
+#define SCB_DSPD 0x00100000
+#define SCB_CRI 0x00200000
+#define SCB_FLEE2 0x00400000
+#define SCB_ATK_ELE 0x00800000
+#define SCB_DEF_ELE 0x01000000
+#define SCB_MODE 0x02000000
+#define SCB_SIZE 0x04000000
+#define SCB_RACE 0x08000000
+#define SCB_RANGE 0x10000000
+#define SCB_REGEN 0x20000000
+//SCB_DYE means the sc should force cloth-dye change to 0 to avoid client crashes.
+#define SCB_DYE 0x40000000
+#define SCB_PC 0x80000000
+#define SCB_ALL 0x3FFFFFFF
+
+//Define to determine who gets HP/SP consumed on doing skills/etc. [Skotlex]
+#define BL_CONSUME (BL_PC|BL_HOM)
+//Define to determine who has regen
+#define BL_REGEN (BL_PC|BL_HOM)
+
+int status_damage(struct block_list *src,struct block_list *target,int hp,int sp, int walkdelay, int flag);
+//Define for standard HP damage attacks.
+#define status_fix_damage(src, target, hp, walkdelay) status_damage(src, target, hp, 0, walkdelay, 0)
+//Define for standard HP/SP damage triggers.
+#define status_zap(bl, hp, sp) status_damage(NULL, bl, hp, sp, 0, 1)
+//Define for standard HP/SP skill-related cost triggers (mobs require no HP/SP to use skills)
+#define status_charge(bl, hp, sp) (!((bl)->type&BL_CONSUME) || status_damage(NULL, bl, hp, sp, 0, 3))
+int status_percent_change(struct block_list *src,struct block_list *target,signed char hp_rate, signed char sp_rate, int flag);
+//Easier handling of status_percent_change
+#define status_percent_heal(bl, hp_rate, sp_rate) status_percent_change(NULL, bl, -(hp_rate), -(sp_rate), 1)
+#define status_percent_damage(src, target, hp_rate, sp_rate) status_percent_change(src, target, hp_rate, sp_rate, 0)
+//Instant kill with no drops/exp/etc
+//
+#define status_kill(bl) status_percent_damage(NULL, bl, 100, 0)
+//Used to set the hp/sp of an object to an absolute value (can't kill)
+int status_set_hp(struct block_list *bl, unsigned int hp, int flag);
+int status_set_sp(struct block_list *bl, unsigned int sp, int flag);
+int status_heal(struct block_list *bl,int hp,int sp, int flag);
+int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp);
+
+//Define for copying a status_data structure from b to a, without overwriting current Hp and Sp, nor messing the lhw pointer.
+#define status_cpy(a, b) { memcpy(&((a)->max_hp), &((b)->max_hp), sizeof(struct status_data)-(sizeof((a)->hp)+sizeof((a)->sp)+sizeof((a)->lhw))); \
+ if ((a)->lhw && (b)->lhw) { memcpy((a)->lhw, (b)->lhw, sizeof(struct weapon_atk)); }}
+
+struct regen_data *status_get_regen_data(struct block_list *bl);
+struct status_data *status_get_status_data(struct block_list *bl);
+struct status_data *status_get_base_status(struct block_list *bl);
+const char * status_get_name(struct block_list *bl);
+int status_get_class(struct block_list *bl);
+int status_get_lv(struct block_list *bl);
+#define status_get_range(bl) status_get_status_data(bl)->rhw.range
+#define status_get_hp(bl) status_get_status_data(bl)->hp
+#define status_get_max_hp(bl) status_get_status_data(bl)->max_hp
+#define status_get_sp(bl) status_get_status_data(bl)->sp
+#define status_get_max_sp(bl) status_get_status_data(bl)->max_sp
+#define status_get_str(bl) status_get_status_data(bl)->str
+#define status_get_agi(bl) status_get_status_data(bl)->agi
+#define status_get_vit(bl) status_get_status_data(bl)->vit
+#define status_get_int(bl) status_get_status_data(bl)->int_
+#define status_get_dex(bl) status_get_status_data(bl)->dex
+#define status_get_luk(bl) status_get_status_data(bl)->luk
+#define status_get_hit(bl) status_get_status_data(bl)->hit
+#define status_get_flee(bl) status_get_status_data(bl)->flee
+unsigned char status_get_def(struct block_list *bl);
+#define status_get_mdef(bl) status_get_status_data(bl)->mdef
+#define status_get_flee2(bl) status_get_status_data(bl)->flee2
+#define status_get_def2(bl) status_get_status_data(bl)->def2
+#define status_get_mdef2(bl) status_get_status_data(bl)->mdef2
+#define status_get_critical(bl) status_get_status_data(bl)->cri
+#define status_get_batk(bl) status_get_status_data(bl)->batk
+#define status_get_watk(bl) status_get_status_data(bl)->rhw.atk
+#define status_get_watk2(bl) status_get_status_data(bl)->rhw.atk2
+#define status_get_matk_max(bl) status_get_status_data(bl)->matk_max
+#define status_get_matk_min(bl) status_get_status_data(bl)->matk_min
+unsigned short status_get_lwatk(struct block_list *bl);
+unsigned short status_get_lwatk2(struct block_list *bl);
+unsigned short status_get_speed(struct block_list *bl);
+#define status_get_adelay(bl) status_get_status_data(bl)->adelay
+#define status_get_amotion(bl) status_get_status_data(bl)->amotion
+#define status_get_dmotion(bl) status_get_status_data(bl)->dmotion
+#define status_get_element(bl) status_get_status_data(bl)->def_ele
+#define status_get_element_level(bl) status_get_status_data(bl)->ele_lv
+unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element);
+#define status_get_attack_sc_element(bl, sc) status_calc_attack_element(bl, sc, 0)
+#define status_get_attack_element(bl) status_get_status_data(bl)->rhw.ele
+unsigned char status_get_attack_lelement(struct block_list *bl);
+#define status_get_race(bl) status_get_status_data(bl)->race
+#define status_get_size(bl) status_get_status_data(bl)->size
+#define status_get_mode(bl) status_get_status_data(bl)->mode
+int status_get_party_id(struct block_list *bl);
+int status_get_guild_id(struct block_list *bl);
+int status_get_mexp(struct block_list *bl);
+int status_get_race2(struct block_list *bl);
+
+struct view_data *status_get_viewdata(struct block_list *bl);
+void status_set_viewdata(struct block_list *bl, int class_);
+void status_change_init(struct block_list *bl);
+struct status_change *status_get_sc(struct block_list *bl);
+
+int status_isdead(struct block_list *bl);
+int status_isimmune(struct block_list *bl);
+
+int status_get_sc_def(struct block_list *bl, int type);
+#define status_get_sc_def_mdef(bl) (status_get_sc_def(bl, SP_MDEF1))
+#define status_get_sc_def_vit(bl) (status_get_sc_def(bl, SP_DEF2))
+#define status_get_sc_def_int(bl) (status_get_sc_def(bl, SP_MDEF2))
+#define status_get_sc_def_luk(bl) (status_get_sc_def(bl, SP_LUK))
+
+//Short version, receives rate in 1->100 range, and does not uses a flag setting.
+#define sc_start(bl, type, rate, val1, tick) status_change_start(bl,type,100*(rate),val1,0,0,0,tick,0)
+#define sc_start4(bl, type, rate, val1, val2, val3, val4, tick) status_change_start(bl,type,100*(rate),val1,val2,val3,val4,tick,0)
+
+int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag);
+int status_change_end( struct block_list* bl , int type,int tid );
+int kaahi_heal_timer(int tid, unsigned int tick, int id, int data);
+int status_change_timer(int tid, unsigned int tick, int id, int data);
+int status_change_timer_sub(struct block_list *bl, va_list ap );
+int status_change_clear(struct block_list *bl,int type);
+int status_change_clear_buffs(struct block_list *bl, int type);
+
+void status_calc_bl(struct block_list *bl, unsigned long flag);
+int status_calc_pet(struct pet_data* pd, int first); // [Skotlex]
+int status_calc_pc(struct map_session_data* sd,int first);
+int status_calc_mob(struct mob_data* md, int first); //[Skotlex]
+int status_calc_homunculus(struct homun_data *hd, int first);
+void status_calc_misc(struct block_list *bl, struct status_data *status, int level);
+void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen);
+void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc);
+
+void status_freecast_switch(struct map_session_data *sd);
+int status_getrefinebonus(int lv,int type);
+int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag); // [Skotlex]
+int status_check_visibility(struct block_list *src, struct block_list *target); //[Skotlex]
+
+int status_readdb(void);
+int do_init_status(void);
+
+#endif
diff --git a/src/map/storage.c b/src/map/storage.c
index 6f504eab3..ba44f0f0d 100644
--- a/src/map/storage.c
+++ b/src/map/storage.c
@@ -1,761 +1,761 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "../common/nullpo.h"
-#include "../common/malloc.h"
-#include "../common/showmsg.h"
-
-#include "storage.h"
-#include "chrif.h"
-#include "itemdb.h"
-#include "clif.h"
-#include "intif.h"
-#include "pc.h"
-#include "guild.h"
-#include "battle.h"
-#include "atcommand.h"
-
-static struct dbt *storage_db;
-static struct dbt *guild_storage_db;
-
-/*==========================================
- * 倉庫内アイテムソート
- *------------------------------------------
- */
-int storage_comp_item(const void *_i1, const void *_i2)
-{
- struct item *i1 = (struct item *)_i1;
- struct item *i2 = (struct item *)_i2;
-
- if (i1->nameid == i2->nameid)
- return 0;
- else if (!(i1->nameid) || !(i1->amount))
- return 1;
- else if (!(i2->nameid) || !(i2->amount))
- return -1;
- return i1->nameid - i2->nameid;
-}
-
-void sortage_sortitem (struct storage *stor)
-{
- nullpo_retv(stor);
- qsort(stor->storage_, MAX_STORAGE, sizeof(struct item), storage_comp_item);
-}
-
-void sortage_gsortitem (struct guild_storage* gstor)
-{
- nullpo_retv(gstor);
- qsort(gstor->storage_, MAX_GUILD_STORAGE, sizeof(struct item), storage_comp_item);
-}
-
-/*==========================================
- * 初期化とか
- *------------------------------------------
- */
-int do_init_storage(void) // map.c::do_init()から呼ばれる
-{
- storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
- guild_storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
- return 1;
-}
-void do_final_storage(void) // by [MC Cameri]
-{
- storage_db->destroy(storage_db,NULL);
- guild_storage_db->destroy(guild_storage_db,NULL);
-}
-
-
-static int storage_reconnect_sub(DBKey key,void *data,va_list ap)
-{ //Parses storage and saves 'dirty' ones upon reconnect. [Skotlex]
- int type = va_arg(ap, int);
- if (type)
- { //Guild Storage
- struct guild_storage* stor = (struct guild_storage*) data;
- if (stor->dirty && stor->storage_status == 0) //Save closed storages.
- storage_guild_storagesave(0, stor->guild_id,0);
- }
- else
- { //Account Storage
- struct storage* stor = (struct storage*) data;
- if (stor->dirty && stor->storage_status == 0) //Save closed storages.
- storage_storage_save(stor->account_id, stor->dirty==2?1:0);
- }
- return 0;
-}
-
-//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex
-void do_reconnect_storage(void)
-{
- storage_db->foreach(storage_db, storage_reconnect_sub, 0);
- guild_storage_db->foreach(guild_storage_db, storage_reconnect_sub, 1);
-}
-
-static void* create_storage(DBKey key, va_list args) {
- struct storage *stor;
- stor = (struct storage *) aCallocA (sizeof(struct storage), 1);
- stor->account_id = key.i;
- return stor;
-}
-struct storage *account2storage(int account_id)
-{
- return idb_ensure(storage_db,account_id,create_storage);
-}
-
-// Just to ask storage, without creation
-struct storage *account2storage2(int account_id)
-{
- return idb_get(storage_db, account_id);
-}
-
-int storage_delete(int account_id)
-{
- idb_remove(storage_db,account_id);
- return 0;
-}
-
-/*==========================================
- * Opens a storage. Returns:
- * 0 - success
- * 1 - fail
- * 2 - Storage requested from char-server (will open automatically later)
- *------------------------------------------
- */
-int storage_storageopen(struct map_session_data *sd)
-{
- struct storage *stor;
- nullpo_retr(0, sd);
-
- if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
- return 1;
-
- if(sd->state.storage_flag)
- return 1; //Already open?
-
- if(pc_can_give_items(pc_isGM(sd)))
- { //check is this GM level is allowed to put items to storage
- clif_displaymessage(sd->fd, msg_txt(246));
- return 1;
- }
-
- if((stor = idb_get(storage_db,sd->status.account_id)) == NULL)
- { //Request storage.
- intif_request_storage(sd->status.account_id);
- return 2;
- }
-
- if (stor->storage_status)
- return 1; //Already open/player already has it open...
-
- stor->storage_status = 1;
- sd->state.storage_flag = 1;
- clif_storagelist(sd,stor);
- clif_updatestorageamount(sd,stor);
- return 0;
-}
-
-/*==========================================
- * Internal add-item function.
- *------------------------------------------
- */
-static int storage_additem(struct map_session_data *sd,struct storage *stor,struct item *item_data,int amount)
-{
- struct item_data *data;
- int i;
-
- if (sd->state.finalsave)
- return 1;
-
- if(item_data->nameid <= 0 || amount <= 0)
- return 1;
-
- data = itemdb_search(item_data->nameid);
-
- if (!itemdb_canstore(item_data, pc_isGM(sd)))
- { //Check if item is storable. [Skotlex]
- clif_displaymessage (sd->fd, msg_txt(264));
- return 1;
- }
-
- if(itemdb_isstackable2(data)){ //Stackable
- for(i=0;i<MAX_STORAGE;i++){
- if( compare_item (&stor->storage_[i], item_data)) {
- if(amount > MAX_AMOUNT - stor->storage_[i].amount)
- return 1;
- stor->storage_[i].amount+=amount;
- clif_storageitemadded(sd,stor,i,amount);
- stor->dirty = 1;
- return 0;
- }
- }
- }
- //Add item
- for(i=0;i<MAX_STORAGE && stor->storage_[i].nameid;i++);
-
- if(i>=MAX_STORAGE)
- return 1;
-
- memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
- stor->storage_[i].amount=amount;
- stor->storage_amount++;
- clif_storageitemadded(sd,stor,i,amount);
- clif_updatestorageamount(sd,stor);
- stor->dirty = 1;
- return 0;
-}
-/*==========================================
- * Internal del-item function
- *------------------------------------------
- */
-static int storage_delitem(struct map_session_data *sd,struct storage *stor,int n,int amount)
-{
-
- if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
- return 1;
-
- stor->storage_[n].amount-=amount;
- if(stor->storage_[n].amount==0){
- malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0]));
- stor->storage_amount--;
- clif_updatestorageamount(sd,stor);
- }
- clif_storageitemremoved(sd,n,amount);
-
- stor->dirty = 1;
- return 0;
-}
-/*==========================================
- * Add an item to the storage from the inventory.
- *------------------------------------------
- */
-int storage_storageadd(struct map_session_data *sd,int index,int amount)
-{
- struct storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=account2storage2(sd->status.account_id));
-
- if((stor->storage_amount > MAX_STORAGE) || !stor->storage_status)
- return 0; // storage full / storage closed
-
- if(index<0 || index>=MAX_INVENTORY)
- return 0;
-
- if(sd->status.inventory[index].nameid <= 0)
- return 0; //No item on that spot
-
- if(amount < 1 || amount > sd->status.inventory[index].amount)
- return 0;
-
-// log_tostorage(sd, index, 0);
- if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
- // remove item from inventory
- pc_delitem(sd,index,amount,0);
-
- return 1;
-}
-
-/*==========================================
- * Retrieve an item from the storage.
- *------------------------------------------
- */
-int storage_storageget(struct map_session_data *sd,int index,int amount)
-{
- struct storage *stor;
- int flag;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=account2storage2(sd->status.account_id));
-
-
- if(index<0 || index>=MAX_STORAGE)
- return 0;
-
- if(stor->storage_[index].nameid <= 0)
- return 0; //Nothing there
-
- if(amount < 1 || amount > stor->storage_[index].amount)
- return 0;
-
- if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
- storage_delitem(sd,stor,index,amount);
- else
- clif_additem(sd,0,0,flag);
-// log_fromstorage(sd, index, 0);
- return 1;
-}
-/*==========================================
- * Move an item from cart to storage.
- *------------------------------------------
- */
-int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount)
-{
- struct storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=account2storage2(sd->status.account_id));
-
- if(stor->storage_amount > MAX_STORAGE || !stor->storage_status)
- return 0; // storage full / storage closed
-
- if(index< 0 || index>=MAX_CART)
- return 0;
-
- if(sd->status.cart[index].nameid <= 0)
- return 0; //No item there.
-
- if(amount < 1 || amount > sd->status.cart[index].amount)
- return 0;
-
- if(storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
- pc_cart_delitem(sd,index,amount,0);
-
- return 1;
-}
-
-/*==========================================
- * Get from Storage to the Cart
- *------------------------------------------
- */
-int storage_storagegettocart(struct map_session_data *sd,int index,int amount)
-{
- struct storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=account2storage2(sd->status.account_id));
-
- if(!stor->storage_status)
- return 0;
-
- if(index< 0 || index>=MAX_STORAGE)
- return 0;
-
- if(stor->storage_[index].nameid <= 0)
- return 0; //Nothing there.
-
- if(amount < 1 || amount > stor->storage_[index].amount)
- return 0;
-
- if(pc_cart_additem(sd,&stor->storage_[index],amount)==0)
- storage_delitem(sd,stor,index,amount);
-
- return 1;
-}
-
-
-/*==========================================
- * Modified By Valaris to save upon closing [massdriller]
- *------------------------------------------
- */
-int storage_storageclose(struct map_session_data *sd)
-{
- struct storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=account2storage2(sd->status.account_id));
-
- clif_storageclose(sd);
- if (stor->storage_status)
- {
- if (save_settings&4)
- chrif_save(sd,0); //Invokes the storage saving as well.
- else
- storage_storage_save(sd->status.account_id, 0);
- }
- stor->storage_status=0;
- sd->state.storage_flag=0;
- return 0;
-}
-
-/*==========================================
- * When quitting the game.
- *------------------------------------------
- */
-int storage_storage_quit(struct map_session_data *sd, int flag)
-{
- struct storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=account2storage2(sd->status.account_id));
-
- if (stor->storage_status)
- {
- if (save_settings&4)
- chrif_save(sd, flag); //Invokes the storage saving as well.
- else
- storage_storage_save(sd->status.account_id, flag);
- }
- stor->storage_status = 0;
- sd->state.storage_flag = 0;
- return 0;
-}
-
-void storage_storage_dirty(struct map_session_data *sd)
-{
- struct storage *stor;
-
- stor=account2storage2(sd->status.account_id);
-
- if(stor)
- stor->dirty = 1;
-}
-
-int storage_storage_save(int account_id, int final)
-{
- struct storage *stor;
-
- stor=account2storage2(account_id);
- if(!stor) return 0;
-
- if(stor->dirty)
- {
- if (final) {
- stor->dirty = 2;
- stor->storage_status = 0; //To prevent further manipulation of it.
- }
- intif_send_storage(stor);
- return 1;
- }
- if (final)
- { //Clear storage from memory. Nothing to save.
- storage_delete(account_id);
- return 1;
- }
-
- return 0;
-}
-
-//Ack from Char-server indicating the storage was saved. [Skotlex]
-int storage_storage_saved(int account_id)
-{
- struct storage *stor;
-
- if((stor=account2storage2(account_id)) == NULL)
- return 0;
-
- if (stor->dirty == 2)
- { //Final save of storage. Remove from memory.
- storage_delete(account_id);
- return 1;
- }
-
- if (stor->dirty && stor->storage_status == 0)
- { //Only mark it clean if it's not in use. [Skotlex]
- stor->dirty = 0;
- sortage_sortitem(stor);
- return 1;
- }
- return 0;
-}
-
-static void* create_guildstorage(DBKey key, va_list args) {
- struct guild_storage *gs = NULL;
- gs = (struct guild_storage *) aCallocA(sizeof(struct guild_storage), 1);
- gs->guild_id=key.i;
- return gs;
-}
-struct guild_storage *guild2storage(int guild_id)
-{
- struct guild_storage *gs = NULL;
- if(guild_search(guild_id) != NULL)
- gs=(struct guild_storage *) idb_ensure(guild_storage_db,guild_id,create_guildstorage);
- return gs;
-}
-
-struct guild_storage *guild2storage2(int guild_id)
-{ //For just locating a storage without creating one. [Skotlex]
- return idb_get(guild_storage_db,guild_id);
-}
-
-int guild_storage_delete(int guild_id)
-{
- idb_remove(guild_storage_db,guild_id);
- return 0;
-}
-
-int storage_guild_storageopen(struct map_session_data *sd)
-{
- struct guild_storage *gstor;
-
- nullpo_retr(0, sd);
-
- if(sd->status.guild_id <= 0)
- return 2;
-
- if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
- return 1;
-
- if(sd->state.storage_flag)
- return 1; //Can't open both storages at a time.
-
- if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level can open guild storage and store items [Lupus]
- clif_displaymessage(sd->fd, msg_txt(246));
- return 1;
- }
-
- if((gstor = guild2storage2(sd->status.guild_id)) == NULL) {
- intif_request_guild_storage(sd->status.account_id,sd->status.guild_id);
- return 0;
- }
- if(gstor->storage_status)
- return 1;
-
- gstor->storage_status = 1;
- sd->state.storage_flag = 2;
- clif_guildstoragelist(sd,gstor);
- clif_updateguildstorageamount(sd,gstor);
- return 0;
-}
-
-int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount)
-{
- struct item_data *data;
- int i;
-
- nullpo_retr(1, sd);
- nullpo_retr(1, stor);
- nullpo_retr(1, item_data);
- nullpo_retr(1, data = itemdb_search(item_data->nameid));
-
- if(item_data->nameid <= 0 || amount <= 0)
- return 1;
-
- if (!itemdb_canguildstore(item_data, pc_isGM(sd)))
- { //Check if item is storable. [Skotlex]
- clif_displaymessage (sd->fd, msg_txt(264));
- return 1;
- }
-
- if(itemdb_isstackable2(data)){ //Stackable
- for(i=0;i<MAX_GUILD_STORAGE;i++){
- if(compare_item(&stor->storage_[i], item_data)) {
- if(stor->storage_[i].amount+amount > MAX_AMOUNT)
- return 1;
- stor->storage_[i].amount+=amount;
- clif_guildstorageitemadded(sd,stor,i,amount);
- stor->dirty = 1;
- return 0;
- }
- }
- }
- //Add item
- for(i=0;i<MAX_GUILD_STORAGE && stor->storage_[i].nameid;i++);
-
- if(i>=MAX_GUILD_STORAGE)
- return 1;
-
- memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
- stor->storage_[i].amount=amount;
- stor->storage_amount++;
- clif_guildstorageitemadded(sd,stor,i,amount);
- clif_updateguildstorageamount(sd,stor);
- stor->dirty = 1;
- return 0;
-}
-
-int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount)
-{
- nullpo_retr(1, sd);
- nullpo_retr(1, stor);
-
- if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
- return 1;
-
- stor->storage_[n].amount-=amount;
- if(stor->storage_[n].amount==0){
- malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0]));
- stor->storage_amount--;
- clif_updateguildstorageamount(sd,stor);
- }
- clif_storageitemremoved(sd,n,amount);
- stor->dirty = 1;
- return 0;
-}
-
-int storage_guild_storageadd(struct map_session_data *sd,int index,int amount)
-{
- struct guild_storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
-
- if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE)
- return 0;
-
- if(index<0 || index>=MAX_INVENTORY)
- return 0;
-
- if(sd->status.inventory[index].nameid <= 0)
- return 0;
-
- if(amount < 1 || amount > sd->status.inventory[index].amount)
- return 0;
-
-// log_tostorage(sd, index, 1);
- if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
- pc_delitem(sd,index,amount,0);
-
- return 1;
-}
-
-int storage_guild_storageget(struct map_session_data *sd,int index,int amount)
-{
- struct guild_storage *stor;
- int flag;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
-
- if(!stor->storage_status)
- return 0;
-
- if(index<0 || index>=MAX_GUILD_STORAGE)
- return 0;
-
- if(stor->storage_[index].nameid <= 0)
- return 0;
-
- if(amount < 1 || amount > stor->storage_[index].amount)
- return 0;
-
- if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
- guild_storage_delitem(sd,stor,index,amount);
- else
- clif_additem(sd,0,0,flag);
-// log_fromstorage(sd, index, 1);
-
- return 0;
-}
-
-int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount)
-{
- struct guild_storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
-
- if(!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE)
- return 0;
-
- if(index<0 || index>=MAX_CART)
- return 0;
-
- if(sd->status.cart[index].nameid <= 0)
- return 0;
-
- if(amount < 1 || amount > sd->status.cart[index].amount)
- return 0;
-
- if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
- pc_cart_delitem(sd,index,amount,0);
-
- return 1;
-}
-
-int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount)
-{
- struct guild_storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
-
- if(!stor->storage_status)
- return 0;
-
- if(index<0 || index>=MAX_GUILD_STORAGE)
- return 0;
-
- if(stor->storage_[index].nameid<=0)
- return 0;
-
- if(amount < 1 || amount > stor->storage_[index].amount)
- return 0;
-
- if(pc_cart_additem(sd,&stor->storage_[index],amount)==0)
- guild_storage_delitem(sd,stor,index,amount);
-
- return 1;
-}
-
-int storage_guild_storagesave(int account_id, int guild_id, int flag)
-{
- struct guild_storage *stor = guild2storage2(guild_id);
-
- if(stor)
- {
- if (flag) //Char quitting, close it.
- stor->storage_status = 0;
- if (stor->dirty)
- intif_send_guild_storage(account_id,stor);
- return 1;
- }
- return 0;
-}
-
-int storage_guild_storagesaved(int guild_id)
-{
- struct guild_storage *stor;
-
- if((stor=guild2storage2(guild_id)) != NULL) {
- if (stor->dirty && stor->storage_status == 0)
- { //Storage has been correctly saved.
- stor->dirty = 0;
- sortage_gsortitem(stor);
- }
- return 1;
- }
- return 0;
-}
-
-int storage_guild_storageclose(struct map_session_data *sd)
-{
- struct guild_storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
-
- clif_storageclose(sd);
- if (stor->storage_status)
- {
- if (save_settings&4)
- chrif_save(sd, 0); //This one also saves the storage. [Skotlex]
- else
- storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0);
- stor->storage_status=0;
- }
- sd->state.storage_flag = 0;
-
- return 0;
-}
-
-int storage_guild_storage_quit(struct map_session_data *sd,int flag)
-{
- struct guild_storage *stor;
-
- nullpo_retr(0, sd);
- nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
-
- if(flag)
- { //Only during a guild break flag is 1 (don't save storage)
- sd->state.storage_flag = 0;
- stor->storage_status = 0;
- clif_storageclose(sd);
- if (save_settings&4)
- chrif_save(sd,0);
- return 0;
- }
-
- if(stor->storage_status) {
- if (save_settings&4)
- chrif_save(sd,0);
- else
- storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1);
- }
- sd->state.storage_flag = 0;
- stor->storage_status = 0;
-
- return 0;
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "../common/malloc.h"
+#include "../common/showmsg.h"
+
+#include "storage.h"
+#include "chrif.h"
+#include "itemdb.h"
+#include "clif.h"
+#include "intif.h"
+#include "pc.h"
+#include "guild.h"
+#include "battle.h"
+#include "atcommand.h"
+
+static struct dbt *storage_db;
+static struct dbt *guild_storage_db;
+
+/*==========================================
+ * 倉庫内アイテムソート
+ *------------------------------------------
+ */
+int storage_comp_item(const void *_i1, const void *_i2)
+{
+ struct item *i1 = (struct item *)_i1;
+ struct item *i2 = (struct item *)_i2;
+
+ if (i1->nameid == i2->nameid)
+ return 0;
+ else if (!(i1->nameid) || !(i1->amount))
+ return 1;
+ else if (!(i2->nameid) || !(i2->amount))
+ return -1;
+ return i1->nameid - i2->nameid;
+}
+
+void sortage_sortitem (struct storage *stor)
+{
+ nullpo_retv(stor);
+ qsort(stor->storage_, MAX_STORAGE, sizeof(struct item), storage_comp_item);
+}
+
+void sortage_gsortitem (struct guild_storage* gstor)
+{
+ nullpo_retv(gstor);
+ qsort(gstor->storage_, MAX_GUILD_STORAGE, sizeof(struct item), storage_comp_item);
+}
+
+/*==========================================
+ * 初期化とか
+ *------------------------------------------
+ */
+int do_init_storage(void) // map.c::do_init()から呼ばれる
+{
+ storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ guild_storage_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
+ return 1;
+}
+void do_final_storage(void) // by [MC Cameri]
+{
+ storage_db->destroy(storage_db,NULL);
+ guild_storage_db->destroy(guild_storage_db,NULL);
+}
+
+
+static int storage_reconnect_sub(DBKey key,void *data,va_list ap)
+{ //Parses storage and saves 'dirty' ones upon reconnect. [Skotlex]
+ int type = va_arg(ap, int);
+ if (type)
+ { //Guild Storage
+ struct guild_storage* stor = (struct guild_storage*) data;
+ if (stor->dirty && stor->storage_status == 0) //Save closed storages.
+ storage_guild_storagesave(0, stor->guild_id,0);
+ }
+ else
+ { //Account Storage
+ struct storage* stor = (struct storage*) data;
+ if (stor->dirty && stor->storage_status == 0) //Save closed storages.
+ storage_storage_save(stor->account_id, stor->dirty==2?1:0);
+ }
+ return 0;
+}
+
+//Function to be invoked upon server reconnection to char. To save all 'dirty' storages [Skotlex
+void do_reconnect_storage(void)
+{
+ storage_db->foreach(storage_db, storage_reconnect_sub, 0);
+ guild_storage_db->foreach(guild_storage_db, storage_reconnect_sub, 1);
+}
+
+static void* create_storage(DBKey key, va_list args) {
+ struct storage *stor;
+ stor = (struct storage *) aCallocA (sizeof(struct storage), 1);
+ stor->account_id = key.i;
+ return stor;
+}
+struct storage *account2storage(int account_id)
+{
+ return idb_ensure(storage_db,account_id,create_storage);
+}
+
+// Just to ask storage, without creation
+struct storage *account2storage2(int account_id)
+{
+ return idb_get(storage_db, account_id);
+}
+
+int storage_delete(int account_id)
+{
+ idb_remove(storage_db,account_id);
+ return 0;
+}
+
+/*==========================================
+ * Opens a storage. Returns:
+ * 0 - success
+ * 1 - fail
+ * 2 - Storage requested from char-server (will open automatically later)
+ *------------------------------------------
+ */
+int storage_storageopen(struct map_session_data *sd)
+{
+ struct storage *stor;
+ nullpo_retr(0, sd);
+
+ if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
+ return 1;
+
+ if(sd->state.storage_flag)
+ return 1; //Already open?
+
+ if(pc_can_give_items(pc_isGM(sd)))
+ { //check is this GM level is allowed to put items to storage
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return 1;
+ }
+
+ if((stor = idb_get(storage_db,sd->status.account_id)) == NULL)
+ { //Request storage.
+ intif_request_storage(sd->status.account_id);
+ return 2;
+ }
+
+ if (stor->storage_status)
+ return 1; //Already open/player already has it open...
+
+ stor->storage_status = 1;
+ sd->state.storage_flag = 1;
+ clif_storagelist(sd,stor);
+ clif_updatestorageamount(sd,stor);
+ return 0;
+}
+
+/*==========================================
+ * Internal add-item function.
+ *------------------------------------------
+ */
+static int storage_additem(struct map_session_data *sd,struct storage *stor,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+
+ if (sd->state.finalsave)
+ return 1;
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+
+ data = itemdb_search(item_data->nameid);
+
+ if (!itemdb_canstore(item_data, pc_isGM(sd)))
+ { //Check if item is storable. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ if(itemdb_isstackable2(data)){ //Stackable
+ for(i=0;i<MAX_STORAGE;i++){
+ if( compare_item (&stor->storage_[i], item_data)) {
+ if(amount > MAX_AMOUNT - stor->storage_[i].amount)
+ return 1;
+ stor->storage_[i].amount+=amount;
+ clif_storageitemadded(sd,stor,i,amount);
+ stor->dirty = 1;
+ return 0;
+ }
+ }
+ }
+ //Add item
+ for(i=0;i<MAX_STORAGE && stor->storage_[i].nameid;i++);
+
+ if(i>=MAX_STORAGE)
+ return 1;
+
+ memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
+ stor->storage_[i].amount=amount;
+ stor->storage_amount++;
+ clif_storageitemadded(sd,stor,i,amount);
+ clif_updatestorageamount(sd,stor);
+ stor->dirty = 1;
+ return 0;
+}
+/*==========================================
+ * Internal del-item function
+ *------------------------------------------
+ */
+static int storage_delitem(struct map_session_data *sd,struct storage *stor,int n,int amount)
+{
+
+ if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
+ return 1;
+
+ stor->storage_[n].amount-=amount;
+ if(stor->storage_[n].amount==0){
+ malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0]));
+ stor->storage_amount--;
+ clif_updatestorageamount(sd,stor);
+ }
+ clif_storageitemremoved(sd,n,amount);
+
+ stor->dirty = 1;
+ return 0;
+}
+/*==========================================
+ * Add an item to the storage from the inventory.
+ *------------------------------------------
+ */
+int storage_storageadd(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if((stor->storage_amount > MAX_STORAGE) || !stor->storage_status)
+ return 0; // storage full / storage closed
+
+ if(index<0 || index>=MAX_INVENTORY)
+ return 0;
+
+ if(sd->status.inventory[index].nameid <= 0)
+ return 0; //No item on that spot
+
+ if(amount < 1 || amount > sd->status.inventory[index].amount)
+ return 0;
+
+// log_tostorage(sd, index, 0);
+ if(storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
+ // remove item from inventory
+ pc_delitem(sd,index,amount,0);
+
+ return 1;
+}
+
+/*==========================================
+ * Retrieve an item from the storage.
+ *------------------------------------------
+ */
+int storage_storageget(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+ int flag;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+
+ if(index<0 || index>=MAX_STORAGE)
+ return 0;
+
+ if(stor->storage_[index].nameid <= 0)
+ return 0; //Nothing there
+
+ if(amount < 1 || amount > stor->storage_[index].amount)
+ return 0;
+
+ if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
+ storage_delitem(sd,stor,index,amount);
+ else
+ clif_additem(sd,0,0,flag);
+// log_fromstorage(sd, index, 0);
+ return 1;
+}
+/*==========================================
+ * Move an item from cart to storage.
+ *------------------------------------------
+ */
+int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if(stor->storage_amount > MAX_STORAGE || !stor->storage_status)
+ return 0; // storage full / storage closed
+
+ if(index< 0 || index>=MAX_CART)
+ return 0;
+
+ if(sd->status.cart[index].nameid <= 0)
+ return 0; //No item there.
+
+ if(amount < 1 || amount > sd->status.cart[index].amount)
+ return 0;
+
+ if(storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
+ pc_cart_delitem(sd,index,amount,0);
+
+ return 1;
+}
+
+/*==========================================
+ * Get from Storage to the Cart
+ *------------------------------------------
+ */
+int storage_storagegettocart(struct map_session_data *sd,int index,int amount)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if(!stor->storage_status)
+ return 0;
+
+ if(index< 0 || index>=MAX_STORAGE)
+ return 0;
+
+ if(stor->storage_[index].nameid <= 0)
+ return 0; //Nothing there.
+
+ if(amount < 1 || amount > stor->storage_[index].amount)
+ return 0;
+
+ if(pc_cart_additem(sd,&stor->storage_[index],amount)==0)
+ storage_delitem(sd,stor,index,amount);
+
+ return 1;
+}
+
+
+/*==========================================
+ * Modified By Valaris to save upon closing [massdriller]
+ *------------------------------------------
+ */
+int storage_storageclose(struct map_session_data *sd)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ clif_storageclose(sd);
+ if (stor->storage_status)
+ {
+ if (save_settings&4)
+ chrif_save(sd,0); //Invokes the storage saving as well.
+ else
+ storage_storage_save(sd->status.account_id, 0);
+ }
+ stor->storage_status=0;
+ sd->state.storage_flag=0;
+ return 0;
+}
+
+/*==========================================
+ * When quitting the game.
+ *------------------------------------------
+ */
+int storage_storage_quit(struct map_session_data *sd, int flag)
+{
+ struct storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=account2storage2(sd->status.account_id));
+
+ if (stor->storage_status)
+ {
+ if (save_settings&4)
+ chrif_save(sd, flag); //Invokes the storage saving as well.
+ else
+ storage_storage_save(sd->status.account_id, flag);
+ }
+ stor->storage_status = 0;
+ sd->state.storage_flag = 0;
+ return 0;
+}
+
+void storage_storage_dirty(struct map_session_data *sd)
+{
+ struct storage *stor;
+
+ stor=account2storage2(sd->status.account_id);
+
+ if(stor)
+ stor->dirty = 1;
+}
+
+int storage_storage_save(int account_id, int final)
+{
+ struct storage *stor;
+
+ stor=account2storage2(account_id);
+ if(!stor) return 0;
+
+ if(stor->dirty)
+ {
+ if (final) {
+ stor->dirty = 2;
+ stor->storage_status = 0; //To prevent further manipulation of it.
+ }
+ intif_send_storage(stor);
+ return 1;
+ }
+ if (final)
+ { //Clear storage from memory. Nothing to save.
+ storage_delete(account_id);
+ return 1;
+ }
+
+ return 0;
+}
+
+//Ack from Char-server indicating the storage was saved. [Skotlex]
+int storage_storage_saved(int account_id)
+{
+ struct storage *stor;
+
+ if((stor=account2storage2(account_id)) == NULL)
+ return 0;
+
+ if (stor->dirty == 2)
+ { //Final save of storage. Remove from memory.
+ storage_delete(account_id);
+ return 1;
+ }
+
+ if (stor->dirty && stor->storage_status == 0)
+ { //Only mark it clean if it's not in use. [Skotlex]
+ stor->dirty = 0;
+ sortage_sortitem(stor);
+ return 1;
+ }
+ return 0;
+}
+
+static void* create_guildstorage(DBKey key, va_list args) {
+ struct guild_storage *gs = NULL;
+ gs = (struct guild_storage *) aCallocA(sizeof(struct guild_storage), 1);
+ gs->guild_id=key.i;
+ return gs;
+}
+struct guild_storage *guild2storage(int guild_id)
+{
+ struct guild_storage *gs = NULL;
+ if(guild_search(guild_id) != NULL)
+ gs=(struct guild_storage *) idb_ensure(guild_storage_db,guild_id,create_guildstorage);
+ return gs;
+}
+
+struct guild_storage *guild2storage2(int guild_id)
+{ //For just locating a storage without creating one. [Skotlex]
+ return idb_get(guild_storage_db,guild_id);
+}
+
+int guild_storage_delete(int guild_id)
+{
+ idb_remove(guild_storage_db,guild_id);
+ return 0;
+}
+
+int storage_guild_storageopen(struct map_session_data *sd)
+{
+ struct guild_storage *gstor;
+
+ nullpo_retr(0, sd);
+
+ if(sd->status.guild_id <= 0)
+ return 2;
+
+ if(sd->state.finalsave) //Refuse to open storage when you had your last save done.
+ return 1;
+
+ if(sd->state.storage_flag)
+ return 1; //Can't open both storages at a time.
+
+ if( pc_can_give_items(pc_isGM(sd)) ) { //check is this GM level can open guild storage and store items [Lupus]
+ clif_displaymessage(sd->fd, msg_txt(246));
+ return 1;
+ }
+
+ if((gstor = guild2storage2(sd->status.guild_id)) == NULL) {
+ intif_request_guild_storage(sd->status.account_id,sd->status.guild_id);
+ return 0;
+ }
+ if(gstor->storage_status)
+ return 1;
+
+ gstor->storage_status = 1;
+ sd->state.storage_flag = 2;
+ clif_guildstoragelist(sd,gstor);
+ clif_updateguildstorageamount(sd,gstor);
+ return 0;
+}
+
+int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount)
+{
+ struct item_data *data;
+ int i;
+
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+ nullpo_retr(1, item_data);
+ nullpo_retr(1, data = itemdb_search(item_data->nameid));
+
+ if(item_data->nameid <= 0 || amount <= 0)
+ return 1;
+
+ if (!itemdb_canguildstore(item_data, pc_isGM(sd)))
+ { //Check if item is storable. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(264));
+ return 1;
+ }
+
+ if(itemdb_isstackable2(data)){ //Stackable
+ for(i=0;i<MAX_GUILD_STORAGE;i++){
+ if(compare_item(&stor->storage_[i], item_data)) {
+ if(stor->storage_[i].amount+amount > MAX_AMOUNT)
+ return 1;
+ stor->storage_[i].amount+=amount;
+ clif_guildstorageitemadded(sd,stor,i,amount);
+ stor->dirty = 1;
+ return 0;
+ }
+ }
+ }
+ //Add item
+ for(i=0;i<MAX_GUILD_STORAGE && stor->storage_[i].nameid;i++);
+
+ if(i>=MAX_GUILD_STORAGE)
+ return 1;
+
+ memcpy(&stor->storage_[i],item_data,sizeof(stor->storage_[0]));
+ stor->storage_[i].amount=amount;
+ stor->storage_amount++;
+ clif_guildstorageitemadded(sd,stor,i,amount);
+ clif_updateguildstorageamount(sd,stor);
+ stor->dirty = 1;
+ return 0;
+}
+
+int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount)
+{
+ nullpo_retr(1, sd);
+ nullpo_retr(1, stor);
+
+ if(stor->storage_[n].nameid==0 || stor->storage_[n].amount<amount)
+ return 1;
+
+ stor->storage_[n].amount-=amount;
+ if(stor->storage_[n].amount==0){
+ malloc_set(&stor->storage_[n],0,sizeof(stor->storage_[0]));
+ stor->storage_amount--;
+ clif_updateguildstorageamount(sd,stor);
+ }
+ clif_storageitemremoved(sd,n,amount);
+ stor->dirty = 1;
+ return 0;
+}
+
+int storage_guild_storageadd(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ if (!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE)
+ return 0;
+
+ if(index<0 || index>=MAX_INVENTORY)
+ return 0;
+
+ if(sd->status.inventory[index].nameid <= 0)
+ return 0;
+
+ if(amount < 1 || amount > sd->status.inventory[index].amount)
+ return 0;
+
+// log_tostorage(sd, index, 1);
+ if(guild_storage_additem(sd,stor,&sd->status.inventory[index],amount)==0)
+ pc_delitem(sd,index,amount,0);
+
+ return 1;
+}
+
+int storage_guild_storageget(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+ int flag;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ if(!stor->storage_status)
+ return 0;
+
+ if(index<0 || index>=MAX_GUILD_STORAGE)
+ return 0;
+
+ if(stor->storage_[index].nameid <= 0)
+ return 0;
+
+ if(amount < 1 || amount > stor->storage_[index].amount)
+ return 0;
+
+ if((flag = pc_additem(sd,&stor->storage_[index],amount)) == 0)
+ guild_storage_delitem(sd,stor,index,amount);
+ else
+ clif_additem(sd,0,0,flag);
+// log_fromstorage(sd, index, 1);
+
+ return 0;
+}
+
+int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ if(!stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE)
+ return 0;
+
+ if(index<0 || index>=MAX_CART)
+ return 0;
+
+ if(sd->status.cart[index].nameid <= 0)
+ return 0;
+
+ if(amount < 1 || amount > sd->status.cart[index].amount)
+ return 0;
+
+ if(guild_storage_additem(sd,stor,&sd->status.cart[index],amount)==0)
+ pc_cart_delitem(sd,index,amount,0);
+
+ return 1;
+}
+
+int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ if(!stor->storage_status)
+ return 0;
+
+ if(index<0 || index>=MAX_GUILD_STORAGE)
+ return 0;
+
+ if(stor->storage_[index].nameid<=0)
+ return 0;
+
+ if(amount < 1 || amount > stor->storage_[index].amount)
+ return 0;
+
+ if(pc_cart_additem(sd,&stor->storage_[index],amount)==0)
+ guild_storage_delitem(sd,stor,index,amount);
+
+ return 1;
+}
+
+int storage_guild_storagesave(int account_id, int guild_id, int flag)
+{
+ struct guild_storage *stor = guild2storage2(guild_id);
+
+ if(stor)
+ {
+ if (flag) //Char quitting, close it.
+ stor->storage_status = 0;
+ if (stor->dirty)
+ intif_send_guild_storage(account_id,stor);
+ return 1;
+ }
+ return 0;
+}
+
+int storage_guild_storagesaved(int guild_id)
+{
+ struct guild_storage *stor;
+
+ if((stor=guild2storage2(guild_id)) != NULL) {
+ if (stor->dirty && stor->storage_status == 0)
+ { //Storage has been correctly saved.
+ stor->dirty = 0;
+ sortage_gsortitem(stor);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int storage_guild_storageclose(struct map_session_data *sd)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ clif_storageclose(sd);
+ if (stor->storage_status)
+ {
+ if (save_settings&4)
+ chrif_save(sd, 0); //This one also saves the storage. [Skotlex]
+ else
+ storage_guild_storagesave(sd->status.account_id, sd->status.guild_id,0);
+ stor->storage_status=0;
+ }
+ sd->state.storage_flag = 0;
+
+ return 0;
+}
+
+int storage_guild_storage_quit(struct map_session_data *sd,int flag)
+{
+ struct guild_storage *stor;
+
+ nullpo_retr(0, sd);
+ nullpo_retr(0, stor=guild2storage2(sd->status.guild_id));
+
+ if(flag)
+ { //Only during a guild break flag is 1 (don't save storage)
+ sd->state.storage_flag = 0;
+ stor->storage_status = 0;
+ clif_storageclose(sd);
+ if (save_settings&4)
+ chrif_save(sd,0);
+ return 0;
+ }
+
+ if(stor->storage_status) {
+ if (save_settings&4)
+ chrif_save(sd,0);
+ else
+ storage_guild_storagesave(sd->status.account_id,sd->status.guild_id,1);
+ }
+ sd->state.storage_flag = 0;
+ stor->storage_status = 0;
+
+ return 0;
+}
diff --git a/src/map/storage.h b/src/map/storage.h
index b62ddb47a..2140a2ec5 100644
--- a/src/map/storage.h
+++ b/src/map/storage.h
@@ -1,45 +1,45 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _STORAGE_H_
-#define _STORAGE_H_
-
-#include "../common/mmo.h"
-
-int storage_storageopen(struct map_session_data *sd);
-int storage_storageadd(struct map_session_data *sd,int index,int amount);
-int storage_storageget(struct map_session_data *sd,int index,int amount);
-int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount);
-int storage_storagegettocart(struct map_session_data *sd,int index,int amount);
-int storage_storageclose(struct map_session_data *sd);
-int do_init_storage(void);
-void do_final_storage(void);
-void do_reconnect_storage(void);
-struct storage *account2storage(int account_id);
-struct storage *account2storage2(int account_id);
-int storage_delete(int account_id);
-int storage_storage_quit(struct map_session_data *sd, int flag);
-int storage_storage_save(int account_id, int final);
-int storage_storage_saved(int account_id); //Ack from char server that guild store was saved.
-void storage_storage_dirty(struct map_session_data *sd);
-
-struct guild_storage *guild2storage(int guild_id);
-int guild_storage_delete(int guild_id);
-int storage_guild_storageopen(struct map_session_data *sd);
-int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount);
-int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount);
-int storage_guild_storageadd(struct map_session_data *sd,int index,int amount);
-int storage_guild_storageget(struct map_session_data *sd,int index,int amount);
-int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount);
-int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount);
-int storage_guild_storageclose(struct map_session_data *sd);
-int storage_guild_storage_quit(struct map_session_data *sd,int flag);
-int storage_guild_storagesave(int account_id, int guild_id, int flag);
-int storage_guild_storagesaved(int guild_id); //Ack from char server that guild store was saved.
-
-int storage_comp_item(const void *_i1, const void *_i2);
-//int storage_comp_item(const struct item* i1, const struct item* i2);
-void sortage_sortitem(struct storage* stor);
-void sortage_gsortitem(struct guild_storage* gstor);
-
-#endif
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _STORAGE_H_
+#define _STORAGE_H_
+
+#include "../common/mmo.h"
+
+int storage_storageopen(struct map_session_data *sd);
+int storage_storageadd(struct map_session_data *sd,int index,int amount);
+int storage_storageget(struct map_session_data *sd,int index,int amount);
+int storage_storageaddfromcart(struct map_session_data *sd,int index,int amount);
+int storage_storagegettocart(struct map_session_data *sd,int index,int amount);
+int storage_storageclose(struct map_session_data *sd);
+int do_init_storage(void);
+void do_final_storage(void);
+void do_reconnect_storage(void);
+struct storage *account2storage(int account_id);
+struct storage *account2storage2(int account_id);
+int storage_delete(int account_id);
+int storage_storage_quit(struct map_session_data *sd, int flag);
+int storage_storage_save(int account_id, int final);
+int storage_storage_saved(int account_id); //Ack from char server that guild store was saved.
+void storage_storage_dirty(struct map_session_data *sd);
+
+struct guild_storage *guild2storage(int guild_id);
+int guild_storage_delete(int guild_id);
+int storage_guild_storageopen(struct map_session_data *sd);
+int guild_storage_additem(struct map_session_data *sd,struct guild_storage *stor,struct item *item_data,int amount);
+int guild_storage_delitem(struct map_session_data *sd,struct guild_storage *stor,int n,int amount);
+int storage_guild_storageadd(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageget(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageaddfromcart(struct map_session_data *sd,int index,int amount);
+int storage_guild_storagegettocart(struct map_session_data *sd,int index,int amount);
+int storage_guild_storageclose(struct map_session_data *sd);
+int storage_guild_storage_quit(struct map_session_data *sd,int flag);
+int storage_guild_storagesave(int account_id, int guild_id, int flag);
+int storage_guild_storagesaved(int guild_id); //Ack from char server that guild store was saved.
+
+int storage_comp_item(const void *_i1, const void *_i2);
+//int storage_comp_item(const struct item* i1, const struct item* i2);
+void sortage_sortitem(struct storage* stor);
+void sortage_gsortitem(struct guild_storage* gstor);
+
+#endif
diff --git a/src/map/trade.c b/src/map/trade.c
index 02eb454d3..09b07e02f 100644
--- a/src/map/trade.c
+++ b/src/map/trade.c
@@ -1,553 +1,553 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <string.h>
-
-#include "../common/nullpo.h"
-#include "clif.h"
-#include "itemdb.h"
-#include "map.h"
-#include "trade.h"
-#include "pc.h"
-#include "npc.h"
-#include "battle.h"
-#include "chrif.h"
-#include "storage.h"
-#include "intif.h"
-#include "atcommand.h"
-#include "log.h"
-#include "../common/malloc.h"
-
-//Max distance from traders to enable a trade to take place.
-#define TRADE_DISTANCE 2
-
-/*==========================================
- * Initiates a trade request.
- *------------------------------------------
- */
-void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd) {
- int level;
-
- nullpo_retv(sd);
-
- if (map[sd->bl.m].flag.notrade) {
- clif_displaymessage (sd->fd, msg_txt(272));
- return; //Can't trade in notrade mapflag maps.
- }
-
- if (target_sd == NULL || sd == target_sd) {
- clif_tradestart(sd, 1); // character does not exist
- return;
- }
-
- if (!battle_config.invite_request_check) {
- if (target_sd->guild_invite > 0 || target_sd->party_invite > 0) {
- clif_tradestart(sd, 2);
- return;
- }
- }
-
- if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) {
- trade_tradecancel(sd); // person is in another trade
- return;
- }
-
- level = pc_isGM(sd);
- if ( pc_can_give_items(level) || pc_can_give_items(pc_isGM(target_sd)) ) //check if both GMs are allowed to trade
- {
- clif_displaymessage(sd->fd, msg_txt(246));
- trade_tradecancel(sd); // GM is not allowed to trade
- return;
- }
-
- //Fixed. Only real GMs can request trade from far away! [Lupus]
- if (level < lowest_gm_level && (sd->bl.m != target_sd->bl.m ||
- !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE)
- )) {
- clif_tradestart(sd, 0); // too far
- return ;
- }
-
- target_sd->trade_partner = sd->status.account_id;
- sd->trade_partner = target_sd->status.account_id;
- clif_traderequest(target_sd, sd->status.name);
-}
-
-/*==========================================
- * Reply to a trade-request.
- *------------------------------------------
- */
-void trade_tradeack(struct map_session_data *sd, int type) {
- struct map_session_data *target_sd;
- nullpo_retv(sd);
-
- if (sd->state.trading || !sd->trade_partner)
- return; //Already trading or no partner set.
-
- if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) {
- sd->trade_partner=0;
- return;
- }
-
- if (target_sd->state.trading || target_sd->trade_partner != sd->bl.id)
- return; //Already trading or wrong partner.
-
- //Copied here as well since the original character could had warped.
- if (type == 3 && pc_isGM(target_sd) < lowest_gm_level && (sd->bl.m != target_sd->bl.m ||
- !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE)
- )) {
- sd->trade_partner=0;
- target_sd->trade_partner = 0;
- clif_tradestart(sd, 0); // too far
- return;
- }
-
- //TODO: Type 4/3? What would 1/2 and the rest do?
- if (type == 4) { // Cancel
- sd->state.deal_locked = 0;
- sd->trade_partner = 0;
- target_sd->state.deal_locked = 0;
- target_sd->trade_partner = 0;
- clif_tradestart(target_sd, type);
- clif_tradestart(sd, type);
- }
-
- if (type == 3) { //Initiate trade
- sd->state.trading = 1;
- target_sd->state.trading = 1;
- malloc_set(&sd->deal, 0, sizeof(sd->deal));
- malloc_set(&target_sd->deal, 0, sizeof(target_sd->deal));
- clif_tradestart(target_sd, type);
- clif_tradestart(sd, type);
- if (sd->npc_id)
- npc_event_dequeue(sd);
- if (target_sd->npc_id)
- npc_event_dequeue(target_sd);
- }
-}
-
-/*==========================================
- * Check here hacker for duplicate item in trade
- * normal client refuse to have 2 same types of item (except equipment) in same trade window
- * normal client authorise only no equiped item and only from inventory
- *------------------------------------------
- */
-int impossible_trade_check(struct map_session_data *sd) {
- struct item inventory[MAX_INVENTORY];
- char message_to_gm[200];
- int i, index;
-
- nullpo_retr(1, sd);
-
- if(sd->deal.zeny > sd->status.zeny)
- {
- pc_setglobalreg(sd,"ZENY_HACKER",1);
- return -1;
- }
-
- // get inventory of player
- memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
-
- // remove this part: arrows can be trade and equiped
- // re-added! [celest]
- // remove equiped items (they can not be trade)
- for (i = 0; i < MAX_INVENTORY; i++)
- if (inventory[i].nameid > 0 && inventory[i].equip && !(inventory[i].equip & EQP_AMMO))
- malloc_set(&inventory[i], 0, sizeof(struct item));
-
- // check items in player inventory
- for(i = 0; i < 10; i++) {
- if (!sd->deal.item[i].amount)
- continue;
- index = sd->deal.item[i].index;
- if (inventory[index].amount < sd->deal.item[i].amount)
- { // if more than the player have -> hack
- sprintf(message_to_gm, msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has.
- intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
- sprintf(message_to_gm, msg_txt(539), inventory[index].amount, inventory[index].nameid, sd->deal.item[i].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them.
- intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
- // if we block people
- if (battle_config.ban_hack_trade < 0) {
- chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
- clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
- // message about the ban
- sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked.
- // if we ban people
- } else if (battle_config.ban_hack_trade > 0) {
- chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_hack_trade, 0); // type: 2 - ban (year, month, day, hour, minute, second)
- clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
- // message about the ban
- sprintf(message_to_gm, msg_txt(507), battle_config.ban_hack_trade); // This player has been banned for %d minute(s).
- } else
- // message about the ban
- sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
-
- intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
- return 1;
- }
- inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory
- }
- return 0;
-}
-
-/*==========================================
- * Checks if trade is possible (against zeny limits, inventory limits, etc)
- *------------------------------------------
- */
-int trade_check(struct map_session_data *sd, struct map_session_data *tsd) {
- struct item inventory[MAX_INVENTORY];
- struct item inventory2[MAX_INVENTORY];
- struct item_data *data;
- int trade_i, i, amount, n;
-
- // check zenys value against hackers (Zeny was already checked on time of adding, but you never know when you lost some zeny since then.
- if(sd->deal.zeny > sd->status.zeny || (tsd->status.zeny > MAX_ZENY - sd->deal.zeny))
- return 0;
- if(tsd->deal.zeny > tsd->status.zeny || (sd->status.zeny > MAX_ZENY - tsd->deal.zeny))
- return 0;
-
- // get inventory of player
- memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
- memcpy(&inventory2, &tsd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
-
- // check free slot in both inventory
- for(trade_i = 0; trade_i < 10; trade_i++) {
- amount = sd->deal.item[trade_i].amount;
- if (amount) {
- n = sd->deal.item[trade_i].index;
- if (amount > inventory[n].amount)
- return 0; //qty Exploit?
-
- data = itemdb_search(inventory[n].nameid);
- i = MAX_INVENTORY;
- if (itemdb_isstackable2(data)) { //Stackable item.
- for(i = 0; i < MAX_INVENTORY; i++)
- if (inventory2[i].nameid == inventory[n].nameid &&
- inventory2[i].card[0] == inventory[n].card[0] && inventory2[i].card[1] == inventory[n].card[1] &&
- inventory2[i].card[2] == inventory[n].card[2] && inventory2[i].card[3] == inventory[n].card[3]) {
- if (inventory2[i].amount + amount > MAX_AMOUNT)
- return 0;
- inventory2[i].amount += amount;
- inventory[n].amount -= amount;
- break;
- }
- }
-
- if (i == MAX_INVENTORY) {// look for an empty slot.
- for(i = 0; i < MAX_INVENTORY && inventory2[i].nameid; i++);
- if (i == MAX_INVENTORY)
- return 0;
- memcpy(&inventory2[i], &inventory[n], sizeof(struct item));
- inventory2[i].amount = amount;
- inventory[n].amount -= amount;
- }
- }
- amount = tsd->deal.item[trade_i].amount;
- if (!amount)
- continue;
- n = tsd->deal.item[trade_i].index;
- if (amount > inventory2[n].amount)
- return 0;
- // search if it's possible to add item (for full inventory)
- data = itemdb_search(inventory2[n].nameid);
- i = MAX_INVENTORY;
- if (itemdb_isstackable2(data)) {
- for(i = 0; i < MAX_INVENTORY; i++)
- if (inventory[i].nameid == inventory2[n].nameid &&
- inventory[i].card[0] == inventory2[n].card[0] && inventory[i].card[1] == inventory2[n].card[1] &&
- inventory[i].card[2] == inventory2[n].card[2] && inventory[i].card[3] == inventory2[n].card[3]) {
- if (inventory[i].amount + amount > MAX_AMOUNT)
- return 0;
- inventory[i].amount += amount;
- inventory2[n].amount -= amount;
- break;
- }
- }
- if (i == MAX_INVENTORY) {
- for(i = 0; i < MAX_INVENTORY && inventory[i].nameid; i++);
- if (i == MAX_INVENTORY)
- return 0;
- memcpy(&inventory[i], &inventory2[n], sizeof(struct item));
- inventory[i].amount = amount;
- inventory2[n].amount -= amount;
- }
- }
-
- return 1;
-}
-
-/*==========================================
- * Adds an item/qty to the trade window [rewrite by Skotlex]
- *------------------------------------------
- */
-void trade_tradeadditem(struct map_session_data *sd, int index, int amount) {
- struct map_session_data *target_sd;
- struct item *item;
- int trade_i, trade_weight;
-
- nullpo_retv(sd);
- if (!sd->state.trading || sd->state.deal_locked > 0)
- return; //Can't add stuff.
-
- if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) {
- trade_tradecancel(sd);
- return;
- }
-
- if (amount == 0)
- { //Why do this.. ~.~ just send an ack, the item won't display on the trade window.
- clif_tradeitemok(sd, index, 0);
- return;
- }
-
- if (index == 0)
- { //Adding Zeny
- if (amount >= 0 && amount <= sd->status.zeny && // check amount
- (amount <= MAX_ZENY - target_sd->status.zeny)) // fix positiv overflow
- { //Check Ok
- sd->deal.zeny = amount;
- clif_tradeadditem(sd, target_sd, 0, amount);
- } else //Send overweight when trying to add too much zeny? Hope they get the idea...
- clif_tradeitemok(sd, 0, 1);
- return;
- }
-
- index = index -2; //Why the actual index used is -2?
- //Item checks...
- if (index < 0 || index >= MAX_INVENTORY)
- return;
- if (amount < 0 || amount > sd->status.inventory[index].amount)
- return;
-
- item = &sd->status.inventory[index];
- trade_i = pc_isGM(sd); //Recycling the variables to check for trad restrict.
- trade_weight = pc_isGM(target_sd);
- if (!itemdb_cantrade(item, trade_i, trade_weight) && //Can't trade
- (pc_get_partner(sd) != target_sd ||
- !itemdb_canpartnertrade(item, trade_i, trade_weight))) //Can't partner-trade
- {
- clif_displaymessage (sd->fd, msg_txt(260));
- return;
- }
-
- for(trade_i = 0; trade_i < 10; trade_i++)
- { //Locate a trade position
- if (sd->deal.item[trade_i].index == index ||
- sd->deal.item[trade_i].amount == 0)
- break;
- }
- if (trade_i >= 10) //No space left
- {
- clif_tradeitemok(sd, index+2, 1);
- return;
- }
-
- trade_weight = sd->inventory_data[index]->weight * amount;
- if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight)
- { //fail to add item -- the player was over weighted.
- clif_tradeitemok(sd, index+2, 1);
- return;
- }
-
- if (sd->deal.item[trade_i].index == index)
- { //The same item as before is being readjusted.
- if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount)
- { //packet deal exploit check
- amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount;
- trade_weight = sd->inventory_data[index]->weight * amount;
- }
- sd->deal.item[trade_i].amount += amount;
- } else { //New deal item
- sd->deal.item[trade_i].index = index;
- sd->deal.item[trade_i].amount = amount;
- }
- sd->deal.weight += trade_weight;
-
- clif_tradeitemok(sd, index+2, 0); // Return the index as it was received
- clif_tradeadditem(sd, target_sd, index+2, amount); //index fix
-}
-
-/*==========================================
- * 'Ok' button on the trade window is pressed.
- *------------------------------------------
- */
-void trade_tradeok(struct map_session_data *sd) {
- struct map_session_data *target_sd;
-
- if(sd->state.deal_locked || !sd->state.trading)
- return;
-
- if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) {
- trade_tradecancel(sd);
- return;
- }
- sd->state.deal_locked = 1;
- clif_tradeitemok(sd, 0, 0);
- clif_tradedeal_lock(sd, 0);
- clif_tradedeal_lock(target_sd, 1);
-}
-
-/*==========================================
- * 'Cancel' is pressed. (or trade was force-cancelled by the code)
- *------------------------------------------
- */
-void trade_tradecancel(struct map_session_data *sd) {
- struct map_session_data *target_sd;
- int trade_i;
-
- if(!sd->state.trading)
- return;
-
- for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual)
- if (!sd->deal.item[trade_i].amount)
- continue;
- clif_additem(sd, sd->deal.item[trade_i].index, sd->deal.item[trade_i].amount, 0);
- sd->deal.item[trade_i].index = 0;
- sd->deal.item[trade_i].amount = 0;
- }
- if (sd->deal.zeny) {
- clif_updatestatus(sd, SP_ZENY);
- sd->deal.zeny = 0;
- }
-
- target_sd = map_id2sd(sd->trade_partner);
- sd->state.deal_locked = 0;
- sd->state.trading = 0;
- sd->trade_partner = 0;
- clif_tradecancelled(sd);
-
- if (!target_sd)
- return;
-
- for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual)
- if (!target_sd->deal.item[trade_i].amount)
- continue;
- clif_additem(target_sd, target_sd->deal.item[trade_i].index, target_sd->deal.item[trade_i].amount, 0);
- target_sd->deal.item[trade_i].index = 0;
- target_sd->deal.item[trade_i].amount = 0;
- }
-
- if (target_sd->deal.zeny) {
- clif_updatestatus(target_sd, SP_ZENY);
- target_sd->deal.zeny = 0;
- }
- target_sd->state.deal_locked = 0;
- target_sd->trade_partner = 0;
- target_sd->state.trading = 0;
- clif_tradecancelled(target_sd);
-}
-
-/*==========================================
- * 取引許諾(trade押し)
- *------------------------------------------
- */
-void trade_tradecommit(struct map_session_data *sd) {
- struct map_session_data *tsd;
- int trade_i;
- int flag;
-
- if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade.
- return;
-
- if ((tsd = map_id2sd(sd->trade_partner)) == NULL) {
- trade_tradecancel(sd);
- return;
- }
-
- sd->state.deal_locked = 2;
-
- if (tsd->state.deal_locked < 2)
- return; //Not yet time for trading.
-
- //Now is a good time (to save on resources) to check that the trade can indeed be made and it's not exploitable.
- // check exploit (trade more items that you have)
- if (impossible_trade_check(sd)) {
- trade_tradecancel(sd);
- return;
- }
- // check exploit (trade more items that you have)
- if (impossible_trade_check(tsd)) {
- trade_tradecancel(tsd);
- return;
- }
- // check for full inventory (can not add traded items)
- if (!trade_check(sd,tsd)) { // check the both players
- trade_tradecancel(sd);
- return;
- }
-
- // trade is accepted and correct.
- for(trade_i = 0; trade_i < 10; trade_i++) {
- int n;
- if (sd->deal.item[trade_i].amount) {
- n = sd->deal.item[trade_i].index;
-
- flag = pc_additem(tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount);
- if (flag == 0) {
- //Logs (T)rade [Lupus]
- if(log_config.enable_logs&0x2) {
- log_pick_pc(sd, "T", sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]);
- log_pick_pc(tsd, "T", sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]);
- }
- //Logs
- pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1);
- } else
- clif_additem(sd, n, sd->deal.item[trade_i].amount, 0);
- sd->deal.item[trade_i].index = 0;
- sd->deal.item[trade_i].amount = 0;
- }
- if (tsd->deal.item[trade_i].amount) {
- n = tsd->deal.item[trade_i].index;
-
- flag = pc_additem(sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount);
- if (flag == 0) {
- //Logs (T)rade [Lupus]
- if(log_config.enable_logs&0x2) {
- log_pick_pc(tsd, "T", tsd->status.inventory[n].nameid, -(tsd->deal.item[trade_i].amount), &tsd->status.inventory[n]);
- log_pick_pc(sd, "T", tsd->status.inventory[n].nameid, tsd->deal.item[trade_i].amount, &tsd->status.inventory[n]);
- }
- //Logs
- pc_delitem(tsd, n, tsd->deal.item[trade_i].amount, 1);
- } else
- clif_additem(tsd, n, tsd->deal.item[trade_i].amount, 0);
- tsd->deal.item[trade_i].index = 0;
- tsd->deal.item[trade_i].amount = 0;
- }
- }
- if (sd->deal.zeny || tsd->deal.zeny) {
- if (sd->deal.zeny) {
- sd->status.zeny -= sd->deal.zeny;
- tsd->status.zeny += sd->deal.zeny;
- if (log_config.zeny)
- log_zeny(tsd, "T", sd, sd->deal.zeny);//Logs Zeny (T)rade [Lupus]
- sd->deal.zeny = 0;
- }
- if (tsd->deal.zeny) {
- tsd->status.zeny -= tsd->deal.zeny;
- sd->status.zeny += tsd->deal.zeny;
- if (log_config.zeny)
- log_zeny(sd, "T", tsd, tsd->deal.zeny);//Logs Zeny (T)rade [Lupus]
- tsd->deal.zeny = 0;
- }
- clif_updatestatus(sd, SP_ZENY);
- clif_updatestatus(tsd, SP_ZENY);
- }
-
- sd->state.deal_locked = 0;
- sd->trade_partner = 0;
- sd->state.trading = 0;
-
- tsd->state.deal_locked = 0;
- tsd->trade_partner = 0;
- tsd->state.trading = 0;
-
- clif_tradecompleted(sd, 0);
- clif_tradecompleted(tsd, 0);
- // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players
- if (save_settings&1)
- {
- chrif_save(sd,0);
- chrif_save(tsd,0);
- }
-}
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "clif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "trade.h"
+#include "pc.h"
+#include "npc.h"
+#include "battle.h"
+#include "chrif.h"
+#include "storage.h"
+#include "intif.h"
+#include "atcommand.h"
+#include "log.h"
+#include "../common/malloc.h"
+
+//Max distance from traders to enable a trade to take place.
+#define TRADE_DISTANCE 2
+
+/*==========================================
+ * Initiates a trade request.
+ *------------------------------------------
+ */
+void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd) {
+ int level;
+
+ nullpo_retv(sd);
+
+ if (map[sd->bl.m].flag.notrade) {
+ clif_displaymessage (sd->fd, msg_txt(272));
+ return; //Can't trade in notrade mapflag maps.
+ }
+
+ if (target_sd == NULL || sd == target_sd) {
+ clif_tradestart(sd, 1); // character does not exist
+ return;
+ }
+
+ if (!battle_config.invite_request_check) {
+ if (target_sd->guild_invite > 0 || target_sd->party_invite > 0) {
+ clif_tradestart(sd, 2);
+ return;
+ }
+ }
+
+ if ((target_sd->trade_partner != 0) || (sd->trade_partner != 0)) {
+ trade_tradecancel(sd); // person is in another trade
+ return;
+ }
+
+ level = pc_isGM(sd);
+ if ( pc_can_give_items(level) || pc_can_give_items(pc_isGM(target_sd)) ) //check if both GMs are allowed to trade
+ {
+ clif_displaymessage(sd->fd, msg_txt(246));
+ trade_tradecancel(sd); // GM is not allowed to trade
+ return;
+ }
+
+ //Fixed. Only real GMs can request trade from far away! [Lupus]
+ if (level < lowest_gm_level && (sd->bl.m != target_sd->bl.m ||
+ !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE)
+ )) {
+ clif_tradestart(sd, 0); // too far
+ return ;
+ }
+
+ target_sd->trade_partner = sd->status.account_id;
+ sd->trade_partner = target_sd->status.account_id;
+ clif_traderequest(target_sd, sd->status.name);
+}
+
+/*==========================================
+ * Reply to a trade-request.
+ *------------------------------------------
+ */
+void trade_tradeack(struct map_session_data *sd, int type) {
+ struct map_session_data *target_sd;
+ nullpo_retv(sd);
+
+ if (sd->state.trading || !sd->trade_partner)
+ return; //Already trading or no partner set.
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) {
+ sd->trade_partner=0;
+ return;
+ }
+
+ if (target_sd->state.trading || target_sd->trade_partner != sd->bl.id)
+ return; //Already trading or wrong partner.
+
+ //Copied here as well since the original character could had warped.
+ if (type == 3 && pc_isGM(target_sd) < lowest_gm_level && (sd->bl.m != target_sd->bl.m ||
+ !check_distance_bl(&sd->bl, &target_sd->bl, TRADE_DISTANCE)
+ )) {
+ sd->trade_partner=0;
+ target_sd->trade_partner = 0;
+ clif_tradestart(sd, 0); // too far
+ return;
+ }
+
+ //TODO: Type 4/3? What would 1/2 and the rest do?
+ if (type == 4) { // Cancel
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ clif_tradestart(target_sd, type);
+ clif_tradestart(sd, type);
+ }
+
+ if (type == 3) { //Initiate trade
+ sd->state.trading = 1;
+ target_sd->state.trading = 1;
+ malloc_set(&sd->deal, 0, sizeof(sd->deal));
+ malloc_set(&target_sd->deal, 0, sizeof(target_sd->deal));
+ clif_tradestart(target_sd, type);
+ clif_tradestart(sd, type);
+ if (sd->npc_id)
+ npc_event_dequeue(sd);
+ if (target_sd->npc_id)
+ npc_event_dequeue(target_sd);
+ }
+}
+
+/*==========================================
+ * Check here hacker for duplicate item in trade
+ * normal client refuse to have 2 same types of item (except equipment) in same trade window
+ * normal client authorise only no equiped item and only from inventory
+ *------------------------------------------
+ */
+int impossible_trade_check(struct map_session_data *sd) {
+ struct item inventory[MAX_INVENTORY];
+ char message_to_gm[200];
+ int i, index;
+
+ nullpo_retr(1, sd);
+
+ if(sd->deal.zeny > sd->status.zeny)
+ {
+ pc_setglobalreg(sd,"ZENY_HACKER",1);
+ return -1;
+ }
+
+ // get inventory of player
+ memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+
+ // remove this part: arrows can be trade and equiped
+ // re-added! [celest]
+ // remove equiped items (they can not be trade)
+ for (i = 0; i < MAX_INVENTORY; i++)
+ if (inventory[i].nameid > 0 && inventory[i].equip && !(inventory[i].equip & EQP_AMMO))
+ malloc_set(&inventory[i], 0, sizeof(struct item));
+
+ // check items in player inventory
+ for(i = 0; i < 10; i++) {
+ if (!sd->deal.item[i].amount)
+ continue;
+ index = sd->deal.item[i].index;
+ if (inventory[index].amount < sd->deal.item[i].amount)
+ { // if more than the player have -> hack
+ sprintf(message_to_gm, msg_txt(538), sd->status.name, sd->status.account_id); // Hack on trade: character '%s' (account: %d) try to trade more items that he has.
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ sprintf(message_to_gm, msg_txt(539), inventory[index].amount, inventory[index].nameid, sd->deal.item[i].amount); // This player has %d of a kind of item (id: %d), and try to trade %d of them.
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ // if we block people
+ if (battle_config.ban_hack_trade < 0) {
+ chrif_char_ask_name(-1, sd->status.name, 1, 0, 0, 0, 0, 0, 0); // type: 1 - block
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(540)); // This player has been definitivly blocked.
+ // if we ban people
+ } else if (battle_config.ban_hack_trade > 0) {
+ chrif_char_ask_name(-1, sd->status.name, 2, 0, 0, 0, 0, battle_config.ban_hack_trade, 0); // type: 2 - ban (year, month, day, hour, minute, second)
+ clif_setwaitclose(sd->fd); // forced to disconnect because of the hack
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(507), battle_config.ban_hack_trade); // This player has been banned for %d minute(s).
+ } else
+ // message about the ban
+ sprintf(message_to_gm, msg_txt(508)); // This player hasn't been banned (Ban option is disabled).
+
+ intif_wis_message_to_gm(wisp_server_name, battle_config.hack_info_GM_level, message_to_gm);
+ return 1;
+ }
+ inventory[index].amount -= sd->deal.item[i].amount; // remove item from inventory
+ }
+ return 0;
+}
+
+/*==========================================
+ * Checks if trade is possible (against zeny limits, inventory limits, etc)
+ *------------------------------------------
+ */
+int trade_check(struct map_session_data *sd, struct map_session_data *tsd) {
+ struct item inventory[MAX_INVENTORY];
+ struct item inventory2[MAX_INVENTORY];
+ struct item_data *data;
+ int trade_i, i, amount, n;
+
+ // check zenys value against hackers (Zeny was already checked on time of adding, but you never know when you lost some zeny since then.
+ if(sd->deal.zeny > sd->status.zeny || (tsd->status.zeny > MAX_ZENY - sd->deal.zeny))
+ return 0;
+ if(tsd->deal.zeny > tsd->status.zeny || (sd->status.zeny > MAX_ZENY - tsd->deal.zeny))
+ return 0;
+
+ // get inventory of player
+ memcpy(&inventory, &sd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+ memcpy(&inventory2, &tsd->status.inventory, sizeof(struct item) * MAX_INVENTORY);
+
+ // check free slot in both inventory
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ amount = sd->deal.item[trade_i].amount;
+ if (amount) {
+ n = sd->deal.item[trade_i].index;
+ if (amount > inventory[n].amount)
+ return 0; //qty Exploit?
+
+ data = itemdb_search(inventory[n].nameid);
+ i = MAX_INVENTORY;
+ if (itemdb_isstackable2(data)) { //Stackable item.
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (inventory2[i].nameid == inventory[n].nameid &&
+ inventory2[i].card[0] == inventory[n].card[0] && inventory2[i].card[1] == inventory[n].card[1] &&
+ inventory2[i].card[2] == inventory[n].card[2] && inventory2[i].card[3] == inventory[n].card[3]) {
+ if (inventory2[i].amount + amount > MAX_AMOUNT)
+ return 0;
+ inventory2[i].amount += amount;
+ inventory[n].amount -= amount;
+ break;
+ }
+ }
+
+ if (i == MAX_INVENTORY) {// look for an empty slot.
+ for(i = 0; i < MAX_INVENTORY && inventory2[i].nameid; i++);
+ if (i == MAX_INVENTORY)
+ return 0;
+ memcpy(&inventory2[i], &inventory[n], sizeof(struct item));
+ inventory2[i].amount = amount;
+ inventory[n].amount -= amount;
+ }
+ }
+ amount = tsd->deal.item[trade_i].amount;
+ if (!amount)
+ continue;
+ n = tsd->deal.item[trade_i].index;
+ if (amount > inventory2[n].amount)
+ return 0;
+ // search if it's possible to add item (for full inventory)
+ data = itemdb_search(inventory2[n].nameid);
+ i = MAX_INVENTORY;
+ if (itemdb_isstackable2(data)) {
+ for(i = 0; i < MAX_INVENTORY; i++)
+ if (inventory[i].nameid == inventory2[n].nameid &&
+ inventory[i].card[0] == inventory2[n].card[0] && inventory[i].card[1] == inventory2[n].card[1] &&
+ inventory[i].card[2] == inventory2[n].card[2] && inventory[i].card[3] == inventory2[n].card[3]) {
+ if (inventory[i].amount + amount > MAX_AMOUNT)
+ return 0;
+ inventory[i].amount += amount;
+ inventory2[n].amount -= amount;
+ break;
+ }
+ }
+ if (i == MAX_INVENTORY) {
+ for(i = 0; i < MAX_INVENTORY && inventory[i].nameid; i++);
+ if (i == MAX_INVENTORY)
+ return 0;
+ memcpy(&inventory[i], &inventory2[n], sizeof(struct item));
+ inventory[i].amount = amount;
+ inventory2[n].amount -= amount;
+ }
+ }
+
+ return 1;
+}
+
+/*==========================================
+ * Adds an item/qty to the trade window [rewrite by Skotlex]
+ *------------------------------------------
+ */
+void trade_tradeadditem(struct map_session_data *sd, int index, int amount) {
+ struct map_session_data *target_sd;
+ struct item *item;
+ int trade_i, trade_weight;
+
+ nullpo_retv(sd);
+ if (!sd->state.trading || sd->state.deal_locked > 0)
+ return; //Can't add stuff.
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) {
+ trade_tradecancel(sd);
+ return;
+ }
+
+ if (amount == 0)
+ { //Why do this.. ~.~ just send an ack, the item won't display on the trade window.
+ clif_tradeitemok(sd, index, 0);
+ return;
+ }
+
+ if (index == 0)
+ { //Adding Zeny
+ if (amount >= 0 && amount <= sd->status.zeny && // check amount
+ (amount <= MAX_ZENY - target_sd->status.zeny)) // fix positiv overflow
+ { //Check Ok
+ sd->deal.zeny = amount;
+ clif_tradeadditem(sd, target_sd, 0, amount);
+ } else //Send overweight when trying to add too much zeny? Hope they get the idea...
+ clif_tradeitemok(sd, 0, 1);
+ return;
+ }
+
+ index = index -2; //Why the actual index used is -2?
+ //Item checks...
+ if (index < 0 || index >= MAX_INVENTORY)
+ return;
+ if (amount < 0 || amount > sd->status.inventory[index].amount)
+ return;
+
+ item = &sd->status.inventory[index];
+ trade_i = pc_isGM(sd); //Recycling the variables to check for trad restrict.
+ trade_weight = pc_isGM(target_sd);
+ if (!itemdb_cantrade(item, trade_i, trade_weight) && //Can't trade
+ (pc_get_partner(sd) != target_sd ||
+ !itemdb_canpartnertrade(item, trade_i, trade_weight))) //Can't partner-trade
+ {
+ clif_displaymessage (sd->fd, msg_txt(260));
+ return;
+ }
+
+ for(trade_i = 0; trade_i < 10; trade_i++)
+ { //Locate a trade position
+ if (sd->deal.item[trade_i].index == index ||
+ sd->deal.item[trade_i].amount == 0)
+ break;
+ }
+ if (trade_i >= 10) //No space left
+ {
+ clif_tradeitemok(sd, index+2, 1);
+ return;
+ }
+
+ trade_weight = sd->inventory_data[index]->weight * amount;
+ if (target_sd->weight + sd->deal.weight + trade_weight > target_sd->max_weight)
+ { //fail to add item -- the player was over weighted.
+ clif_tradeitemok(sd, index+2, 1);
+ return;
+ }
+
+ if (sd->deal.item[trade_i].index == index)
+ { //The same item as before is being readjusted.
+ if (sd->deal.item[trade_i].amount + amount > sd->status.inventory[index].amount)
+ { //packet deal exploit check
+ amount = sd->status.inventory[index].amount - sd->deal.item[trade_i].amount;
+ trade_weight = sd->inventory_data[index]->weight * amount;
+ }
+ sd->deal.item[trade_i].amount += amount;
+ } else { //New deal item
+ sd->deal.item[trade_i].index = index;
+ sd->deal.item[trade_i].amount = amount;
+ }
+ sd->deal.weight += trade_weight;
+
+ clif_tradeitemok(sd, index+2, 0); // Return the index as it was received
+ clif_tradeadditem(sd, target_sd, index+2, amount); //index fix
+}
+
+/*==========================================
+ * 'Ok' button on the trade window is pressed.
+ *------------------------------------------
+ */
+void trade_tradeok(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+
+ if(sd->state.deal_locked || !sd->state.trading)
+ return;
+
+ if ((target_sd = map_id2sd(sd->trade_partner)) == NULL) {
+ trade_tradecancel(sd);
+ return;
+ }
+ sd->state.deal_locked = 1;
+ clif_tradeitemok(sd, 0, 0);
+ clif_tradedeal_lock(sd, 0);
+ clif_tradedeal_lock(target_sd, 1);
+}
+
+/*==========================================
+ * 'Cancel' is pressed. (or trade was force-cancelled by the code)
+ *------------------------------------------
+ */
+void trade_tradecancel(struct map_session_data *sd) {
+ struct map_session_data *target_sd;
+ int trade_i;
+
+ if(!sd->state.trading)
+ return;
+
+ for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual)
+ if (!sd->deal.item[trade_i].amount)
+ continue;
+ clif_additem(sd, sd->deal.item[trade_i].index, sd->deal.item[trade_i].amount, 0);
+ sd->deal.item[trade_i].index = 0;
+ sd->deal.item[trade_i].amount = 0;
+ }
+ if (sd->deal.zeny) {
+ clif_updatestatus(sd, SP_ZENY);
+ sd->deal.zeny = 0;
+ }
+
+ target_sd = map_id2sd(sd->trade_partner);
+ sd->state.deal_locked = 0;
+ sd->state.trading = 0;
+ sd->trade_partner = 0;
+ clif_tradecancelled(sd);
+
+ if (!target_sd)
+ return;
+
+ for(trade_i = 0; trade_i < 10; trade_i++) { // give items back (only virtual)
+ if (!target_sd->deal.item[trade_i].amount)
+ continue;
+ clif_additem(target_sd, target_sd->deal.item[trade_i].index, target_sd->deal.item[trade_i].amount, 0);
+ target_sd->deal.item[trade_i].index = 0;
+ target_sd->deal.item[trade_i].amount = 0;
+ }
+
+ if (target_sd->deal.zeny) {
+ clif_updatestatus(target_sd, SP_ZENY);
+ target_sd->deal.zeny = 0;
+ }
+ target_sd->state.deal_locked = 0;
+ target_sd->trade_partner = 0;
+ target_sd->state.trading = 0;
+ clif_tradecancelled(target_sd);
+}
+
+/*==========================================
+ * 取引許諾(trade押し)
+ *------------------------------------------
+ */
+void trade_tradecommit(struct map_session_data *sd) {
+ struct map_session_data *tsd;
+ int trade_i;
+ int flag;
+
+ if (!sd->state.trading || !sd->state.deal_locked) //Locked should be 1 (pressed ok) before you can press trade.
+ return;
+
+ if ((tsd = map_id2sd(sd->trade_partner)) == NULL) {
+ trade_tradecancel(sd);
+ return;
+ }
+
+ sd->state.deal_locked = 2;
+
+ if (tsd->state.deal_locked < 2)
+ return; //Not yet time for trading.
+
+ //Now is a good time (to save on resources) to check that the trade can indeed be made and it's not exploitable.
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(sd)) {
+ trade_tradecancel(sd);
+ return;
+ }
+ // check exploit (trade more items that you have)
+ if (impossible_trade_check(tsd)) {
+ trade_tradecancel(tsd);
+ return;
+ }
+ // check for full inventory (can not add traded items)
+ if (!trade_check(sd,tsd)) { // check the both players
+ trade_tradecancel(sd);
+ return;
+ }
+
+ // trade is accepted and correct.
+ for(trade_i = 0; trade_i < 10; trade_i++) {
+ int n;
+ if (sd->deal.item[trade_i].amount) {
+ n = sd->deal.item[trade_i].index;
+
+ flag = pc_additem(tsd, &sd->status.inventory[n], sd->deal.item[trade_i].amount);
+ if (flag == 0) {
+ //Logs (T)rade [Lupus]
+ if(log_config.enable_logs&0x2) {
+ log_pick_pc(sd, "T", sd->status.inventory[n].nameid, -(sd->deal.item[trade_i].amount), &sd->status.inventory[n]);
+ log_pick_pc(tsd, "T", sd->status.inventory[n].nameid, sd->deal.item[trade_i].amount, &sd->status.inventory[n]);
+ }
+ //Logs
+ pc_delitem(sd, n, sd->deal.item[trade_i].amount, 1);
+ } else
+ clif_additem(sd, n, sd->deal.item[trade_i].amount, 0);
+ sd->deal.item[trade_i].index = 0;
+ sd->deal.item[trade_i].amount = 0;
+ }
+ if (tsd->deal.item[trade_i].amount) {
+ n = tsd->deal.item[trade_i].index;
+
+ flag = pc_additem(sd, &tsd->status.inventory[n], tsd->deal.item[trade_i].amount);
+ if (flag == 0) {
+ //Logs (T)rade [Lupus]
+ if(log_config.enable_logs&0x2) {
+ log_pick_pc(tsd, "T", tsd->status.inventory[n].nameid, -(tsd->deal.item[trade_i].amount), &tsd->status.inventory[n]);
+ log_pick_pc(sd, "T", tsd->status.inventory[n].nameid, tsd->deal.item[trade_i].amount, &tsd->status.inventory[n]);
+ }
+ //Logs
+ pc_delitem(tsd, n, tsd->deal.item[trade_i].amount, 1);
+ } else
+ clif_additem(tsd, n, tsd->deal.item[trade_i].amount, 0);
+ tsd->deal.item[trade_i].index = 0;
+ tsd->deal.item[trade_i].amount = 0;
+ }
+ }
+ if (sd->deal.zeny || tsd->deal.zeny) {
+ if (sd->deal.zeny) {
+ sd->status.zeny -= sd->deal.zeny;
+ tsd->status.zeny += sd->deal.zeny;
+ if (log_config.zeny)
+ log_zeny(tsd, "T", sd, sd->deal.zeny);//Logs Zeny (T)rade [Lupus]
+ sd->deal.zeny = 0;
+ }
+ if (tsd->deal.zeny) {
+ tsd->status.zeny -= tsd->deal.zeny;
+ sd->status.zeny += tsd->deal.zeny;
+ if (log_config.zeny)
+ log_zeny(sd, "T", tsd, tsd->deal.zeny);//Logs Zeny (T)rade [Lupus]
+ tsd->deal.zeny = 0;
+ }
+ clif_updatestatus(sd, SP_ZENY);
+ clif_updatestatus(tsd, SP_ZENY);
+ }
+
+ sd->state.deal_locked = 0;
+ sd->trade_partner = 0;
+ sd->state.trading = 0;
+
+ tsd->state.deal_locked = 0;
+ tsd->trade_partner = 0;
+ tsd->state.trading = 0;
+
+ clif_tradecompleted(sd, 0);
+ clif_tradecompleted(tsd, 0);
+ // save both player to avoid crash: they always have no advantage/disadvantage between the 2 players
+ if (save_settings&1)
+ {
+ chrif_save(sd,0);
+ chrif_save(tsd,0);
+ }
+}
diff --git a/src/map/trade.h b/src/map/trade.h
index a695a50e8..bcd609271 100644
--- a/src/map/trade.h
+++ b/src/map/trade.h
@@ -1,15 +1,15 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _TRADE_H_
-#define _TRADE_H_
-
-#include "map.h"
-void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd);
-void trade_tradeack(struct map_session_data *sd,int type);
-void trade_tradeadditem(struct map_session_data *sd,int index,int amount);
-void trade_tradeok(struct map_session_data *sd);
-void trade_tradecancel(struct map_session_data *sd);
-void trade_tradecommit(struct map_session_data *sd);
-
-#endif // _TRADE_H_
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _TRADE_H_
+#define _TRADE_H_
+
+#include "map.h"
+void trade_traderequest(struct map_session_data *sd, struct map_session_data *target_sd);
+void trade_tradeack(struct map_session_data *sd,int type);
+void trade_tradeadditem(struct map_session_data *sd,int index,int amount);
+void trade_tradeok(struct map_session_data *sd);
+void trade_tradecancel(struct map_session_data *sd);
+void trade_tradecommit(struct map_session_data *sd);
+
+#endif // _TRADE_H_
diff --git a/src/map/vending.c b/src/map/vending.c
index 0f8ceaa30..9b4b797e0 100644
--- a/src/map/vending.c
+++ b/src/map/vending.c
@@ -1,267 +1,267 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#include <stdio.h>
-#include <string.h>
-
-#include "../common/nullpo.h"
-#include "clif.h"
-#include "itemdb.h"
-#include "atcommand.h"
-#include "map.h"
-#include "chrif.h"
-#include "vending.h"
-#include "pc.h"
-#include "skill.h"
-#include "battle.h"
-#include "log.h"
-
-#include "irc.h"
-
-/*==========================================
- * 露店閉鎖
- *------------------------------------------
-*/
-void vending_closevending(struct map_session_data *sd)
-{
- nullpo_retv(sd);
-
- sd->vender_id=0;
- clif_closevendingboard(&sd->bl,0);
- if(use_irc && irc_announce_shop_flag)
- irc_announce_shop(sd,0);
-}
-
-/*==========================================
- * 露店アイテムリスト要求
- *------------------------------------------
- */
-void vending_vendinglistreq(struct map_session_data *sd,int id)
-{
- struct map_session_data *vsd;
-
- nullpo_retv(sd);
-
- if( (vsd=map_id2sd(id)) == NULL )
- return;
- if(vsd->vender_id==0)
- return;
- clif_vendinglist(sd,id,vsd->vending);
-}
-
-/*==========================================
- * 露店アイテム購入
- *------------------------------------------
- */
-void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p)
-{
- int i, j, w, new_ = 0, blank, vend_list[MAX_VENDING];
- double z;
- unsigned short amount;
- short idx;
- struct map_session_data *vsd = map_id2sd(id);
- struct vending vending[MAX_VENDING]; // against duplicate packets
-
- nullpo_retv(sd);
-
- if (vsd == NULL)
- return;
- if (vsd->vender_id == 0)
- return;
- if (vsd->vender_id == sd->bl.id)
- return;
-
- // check number of buying items
- if (len < 8 + 4 || len > 8 + 4 * MAX_VENDING) {
- clif_buyvending(sd, 0, 32767, 4); // not enough quantity (index and amount are unknown)
- return;
- }
-
- blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory
-
- // duplicate item in vending to check hacker with multiple packets
- memcpy(&vending, &vsd->vending, sizeof(struct vending) * MAX_VENDING); // copy vending list
-
- // some checks
- z = 0.;
- w = 0;
- for(i = 0; 8 + 4 * i < len; i++) {
- amount = *(unsigned short*)(p + 4 * i);
- idx = *(short*)(p + 2 + 4 * i) - 2;
-
- if (amount <= 0)
- return;
-
- // check of item index in the cart
- if (idx < 0 || idx >= MAX_CART)
- return;
-
- for(j = 0; j < vsd->vend_num; j++) {
- if (vsd->vending[j].index == idx) {
- vend_list[i] = j;
- break;
- }
- }
- if (j == vsd->vend_num)
- return; //picked non-existing item
-
- z += ((double)vsd->vending[j].value * (double)amount);
- if (z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY) { // fix positiv overflow (buyer)
- clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny
- return; // zeny s'<
- }
- if (z + (double)vsd->status.zeny > (double)MAX_ZENY) { // fix positiv overflow (merchand)
- clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
- return; // zeny s'<
- }
- w += itemdb_weight(vsd->status.cart[idx].nameid) * amount;
- if (w + sd->weight > sd->max_weight) {
- clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight
- return;
- }
-
- if (vending[j].amount > vsd->status.cart[idx].amount) //Check to see if cart/vend info is in sync.
- vending[j].amount = vsd->status.cart[idx].amount;
-
- // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples).
- // here, we check cumulativ amounts
- if (vending[j].amount < amount) {
- // send more quantity is not a hack (an other player can have buy items just before)
- clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity
- return;
- } else
- vending[j].amount -= amount;
-
- switch(pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount)) {
- case ADDITEM_EXIST:
- break; //We'd add this item to the existing one (in buyers inventory)
- case ADDITEM_NEW:
- new_++;
- if (new_ > blank)
- return; //Buyer has no space in his inventory
- break;
- case ADDITEM_OVERAMOUNT:
- return; //too many items
- }
- }
-
- //Logs (V)ending Zeny [Lupus]
- if(log_config.zeny > 0 )
- log_zeny(vsd, "V", sd, (int)z);
- //Logs
-
- pc_payzeny(sd, (int)z);
- pc_getzeny(vsd, (int)z);
-
- for(i = 0; 8 + 4 * i < len; i++) {
- amount = *(short*)(p + 4 *i);
- idx = *(short*)(p + 2 + 4 * i) - 2;
- //if (amount < 0) break; // tested at start of the function
-
- //Logs sold (V)ending items [Lupus]
- if(log_config.enable_logs&0x4) {
- log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, (struct item*)&vsd->status.cart[idx]);
- log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, (struct item*)&vsd->status.cart[idx]);
- }
- //Logs
-
- // vending item
- pc_additem(sd, &vsd->status.cart[idx], amount);
- vsd->vending[vend_list[i]].amount -= amount;
- pc_cart_delitem(vsd, idx, amount, 0);
- clif_vendingreport(vsd, idx, amount);
-
- //print buyer's name
- if(battle_config.buyer_name) {
- char temp[256];
- sprintf(temp, msg_txt(265), sd->status.name);
- clif_disp_onlyself(vsd,temp,strlen(temp));
- }
- }
-
- //Always save BOTH: buyer and customer
- if (save_settings&2) {
- chrif_save(sd,0);
- chrif_save(vsd,0);
- }
- //check for @AUTOTRADE users [durf]
- if (vsd->state.autotrade)
- {
- //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex]
- for(i = 0; i < vsd->vend_num && vsd->vending[i].amount < 1; i++);
- if (i == vsd->vend_num)
- {
- vending_closevending(vsd);
- map_quit(vsd); //They have no reason to stay around anymore, do they?
- }
- }
-}
-
-/*==========================================
- * 露店開設
- *------------------------------------------
- */
-void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p)
-{
- int i, j;
- int vending_skill_lvl;
- nullpo_retv(sd);
-
- if (map[sd->bl.m].flag.novending) {
- clif_displaymessage (sd->fd, msg_txt(276));
- return; //Can't vend in novending mapflag maps.
- }
-
- //check shopname len
- if(message[0] == '\0')
- return;
-
- vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
- if(!vending_skill_lvl || !pc_iscarton(sd)) { // cart skill and cart check [Valaris]
- clif_skill_fail(sd,MC_VENDING,0,0);
- return;
- }
-
- if (flag) {
- // check number of items in shop
- if (len < 85 + 8 || len > 85 + 8 * MAX_VENDING || len > 85 + 8 * (2 + vending_skill_lvl)) {
- clif_skill_fail(sd, MC_VENDING, 0, 0);
- return;
- }
- for(i = 0, j = 0; (85 + 8 * j < len) && (i < MAX_VENDING); i++, j++) {
- sd->vending[i].index = *(short*)(p+8*j)-2;
- if (sd->vending[i].index < 0 || sd->vending[i].index >= MAX_CART ||
- !itemdb_cantrade(&sd->status.cart[sd->vending[i].index], pc_isGM(sd), pc_isGM(sd)))
- {
- i--; //Preserve the vending index, skip to the next item.
- continue;
- }
- sd->vending[i].amount = *(short*)(p+2+8*j);
- sd->vending[i].value = *(int*)(p+4+8*j);
- if(sd->vending[i].value > battle_config.vending_max_value)
- sd->vending[i].value=battle_config.vending_max_value;
- else if(sd->vending[i].value < 1)
- sd->vending[i].value = 1000000; // auto set to 1 million [celest]
- // カート内のアイテム数と販売するアイテム数に相違があったら中止
- if(pc_cartitem_amount(sd, sd->vending[i].index, sd->vending[i].amount) < 0) { // fixes by Valaris and fritz
- clif_skill_fail(sd, MC_VENDING, 0, 0);
- return;
- }
- }
- if (i != j)
- { //Some items were not vended. [Skotlex]
- clif_displaymessage (sd->fd, msg_txt(266));
- }
- sd->vender_id = sd->bl.id;
- sd->vend_num = i;
- memcpy(sd->message,message, MESSAGE_SIZE-1);
- if (clif_openvending(sd,sd->vender_id,sd->vending) > 0){
- pc_stop_walking(sd,1);
- clif_showvendingboard(&sd->bl,message,0);
- if(use_irc && irc_announce_shop_flag)
- irc_announce_shop(sd,1);
- } else
- sd->vender_id = 0;
- }
-}
-
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../common/nullpo.h"
+#include "clif.h"
+#include "itemdb.h"
+#include "atcommand.h"
+#include "map.h"
+#include "chrif.h"
+#include "vending.h"
+#include "pc.h"
+#include "skill.h"
+#include "battle.h"
+#include "log.h"
+
+#include "irc.h"
+
+/*==========================================
+ * 露店閉鎖
+ *------------------------------------------
+*/
+void vending_closevending(struct map_session_data *sd)
+{
+ nullpo_retv(sd);
+
+ sd->vender_id=0;
+ clif_closevendingboard(&sd->bl,0);
+ if(use_irc && irc_announce_shop_flag)
+ irc_announce_shop(sd,0);
+}
+
+/*==========================================
+ * 露店アイテムリスト要求
+ *------------------------------------------
+ */
+void vending_vendinglistreq(struct map_session_data *sd,int id)
+{
+ struct map_session_data *vsd;
+
+ nullpo_retv(sd);
+
+ if( (vsd=map_id2sd(id)) == NULL )
+ return;
+ if(vsd->vender_id==0)
+ return;
+ clif_vendinglist(sd,id,vsd->vending);
+}
+
+/*==========================================
+ * 露店アイテム購入
+ *------------------------------------------
+ */
+void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p)
+{
+ int i, j, w, new_ = 0, blank, vend_list[MAX_VENDING];
+ double z;
+ unsigned short amount;
+ short idx;
+ struct map_session_data *vsd = map_id2sd(id);
+ struct vending vending[MAX_VENDING]; // against duplicate packets
+
+ nullpo_retv(sd);
+
+ if (vsd == NULL)
+ return;
+ if (vsd->vender_id == 0)
+ return;
+ if (vsd->vender_id == sd->bl.id)
+ return;
+
+ // check number of buying items
+ if (len < 8 + 4 || len > 8 + 4 * MAX_VENDING) {
+ clif_buyvending(sd, 0, 32767, 4); // not enough quantity (index and amount are unknown)
+ return;
+ }
+
+ blank = pc_inventoryblank(sd); //number of free cells in the buyer's inventory
+
+ // duplicate item in vending to check hacker with multiple packets
+ memcpy(&vending, &vsd->vending, sizeof(struct vending) * MAX_VENDING); // copy vending list
+
+ // some checks
+ z = 0.;
+ w = 0;
+ for(i = 0; 8 + 4 * i < len; i++) {
+ amount = *(unsigned short*)(p + 4 * i);
+ idx = *(short*)(p + 2 + 4 * i) - 2;
+
+ if (amount <= 0)
+ return;
+
+ // check of item index in the cart
+ if (idx < 0 || idx >= MAX_CART)
+ return;
+
+ for(j = 0; j < vsd->vend_num; j++) {
+ if (vsd->vending[j].index == idx) {
+ vend_list[i] = j;
+ break;
+ }
+ }
+ if (j == vsd->vend_num)
+ return; //picked non-existing item
+
+ z += ((double)vsd->vending[j].value * (double)amount);
+ if (z > (double)sd->status.zeny || z < 0. || z > (double)MAX_ZENY) { // fix positiv overflow (buyer)
+ clif_buyvending(sd, idx, amount, 1); // you don't have enough zeny
+ return; // zeny s'<
+ }
+ if (z + (double)vsd->status.zeny > (double)MAX_ZENY) { // fix positiv overflow (merchand)
+ clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // too much zeny = overflow
+ return; // zeny s'<
+ }
+ w += itemdb_weight(vsd->status.cart[idx].nameid) * amount;
+ if (w + sd->weight > sd->max_weight) {
+ clif_buyvending(sd, idx, amount, 2); // you can not buy, because overweight
+ return;
+ }
+
+ if (vending[j].amount > vsd->status.cart[idx].amount) //Check to see if cart/vend info is in sync.
+ vending[j].amount = vsd->status.cart[idx].amount;
+
+ // if they try to add packets (example: get twice or more 2 apples if marchand has only 3 apples).
+ // here, we check cumulativ amounts
+ if (vending[j].amount < amount) {
+ // send more quantity is not a hack (an other player can have buy items just before)
+ clif_buyvending(sd, idx, vsd->vending[j].amount, 4); // not enough quantity
+ return;
+ } else
+ vending[j].amount -= amount;
+
+ switch(pc_checkadditem(sd, vsd->status.cart[idx].nameid, amount)) {
+ case ADDITEM_EXIST:
+ break; //We'd add this item to the existing one (in buyers inventory)
+ case ADDITEM_NEW:
+ new_++;
+ if (new_ > blank)
+ return; //Buyer has no space in his inventory
+ break;
+ case ADDITEM_OVERAMOUNT:
+ return; //too many items
+ }
+ }
+
+ //Logs (V)ending Zeny [Lupus]
+ if(log_config.zeny > 0 )
+ log_zeny(vsd, "V", sd, (int)z);
+ //Logs
+
+ pc_payzeny(sd, (int)z);
+ pc_getzeny(vsd, (int)z);
+
+ for(i = 0; 8 + 4 * i < len; i++) {
+ amount = *(short*)(p + 4 *i);
+ idx = *(short*)(p + 2 + 4 * i) - 2;
+ //if (amount < 0) break; // tested at start of the function
+
+ //Logs sold (V)ending items [Lupus]
+ if(log_config.enable_logs&0x4) {
+ log_pick_pc(vsd, "V", vsd->status.cart[idx].nameid, -amount, (struct item*)&vsd->status.cart[idx]);
+ log_pick_pc( sd, "V", vsd->status.cart[idx].nameid, amount, (struct item*)&vsd->status.cart[idx]);
+ }
+ //Logs
+
+ // vending item
+ pc_additem(sd, &vsd->status.cart[idx], amount);
+ vsd->vending[vend_list[i]].amount -= amount;
+ pc_cart_delitem(vsd, idx, amount, 0);
+ clif_vendingreport(vsd, idx, amount);
+
+ //print buyer's name
+ if(battle_config.buyer_name) {
+ char temp[256];
+ sprintf(temp, msg_txt(265), sd->status.name);
+ clif_disp_onlyself(vsd,temp,strlen(temp));
+ }
+ }
+
+ //Always save BOTH: buyer and customer
+ if (save_settings&2) {
+ chrif_save(sd,0);
+ chrif_save(vsd,0);
+ }
+ //check for @AUTOTRADE users [durf]
+ if (vsd->state.autotrade)
+ {
+ //Close Vending (this was automatically done by the client, we have to do it manually for autovenders) [Skotlex]
+ for(i = 0; i < vsd->vend_num && vsd->vending[i].amount < 1; i++);
+ if (i == vsd->vend_num)
+ {
+ vending_closevending(vsd);
+ map_quit(vsd); //They have no reason to stay around anymore, do they?
+ }
+ }
+}
+
+/*==========================================
+ * 露店開設
+ *------------------------------------------
+ */
+void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p)
+{
+ int i, j;
+ int vending_skill_lvl;
+ nullpo_retv(sd);
+
+ if (map[sd->bl.m].flag.novending) {
+ clif_displaymessage (sd->fd, msg_txt(276));
+ return; //Can't vend in novending mapflag maps.
+ }
+
+ //check shopname len
+ if(message[0] == '\0')
+ return;
+
+ vending_skill_lvl = pc_checkskill(sd, MC_VENDING);
+ if(!vending_skill_lvl || !pc_iscarton(sd)) { // cart skill and cart check [Valaris]
+ clif_skill_fail(sd,MC_VENDING,0,0);
+ return;
+ }
+
+ if (flag) {
+ // check number of items in shop
+ if (len < 85 + 8 || len > 85 + 8 * MAX_VENDING || len > 85 + 8 * (2 + vending_skill_lvl)) {
+ clif_skill_fail(sd, MC_VENDING, 0, 0);
+ return;
+ }
+ for(i = 0, j = 0; (85 + 8 * j < len) && (i < MAX_VENDING); i++, j++) {
+ sd->vending[i].index = *(short*)(p+8*j)-2;
+ if (sd->vending[i].index < 0 || sd->vending[i].index >= MAX_CART ||
+ !itemdb_cantrade(&sd->status.cart[sd->vending[i].index], pc_isGM(sd), pc_isGM(sd)))
+ {
+ i--; //Preserve the vending index, skip to the next item.
+ continue;
+ }
+ sd->vending[i].amount = *(short*)(p+2+8*j);
+ sd->vending[i].value = *(int*)(p+4+8*j);
+ if(sd->vending[i].value > battle_config.vending_max_value)
+ sd->vending[i].value=battle_config.vending_max_value;
+ else if(sd->vending[i].value < 1)
+ sd->vending[i].value = 1000000; // auto set to 1 million [celest]
+ // カート内のアイテム数と販売するアイテム数に相違があったら中止
+ if(pc_cartitem_amount(sd, sd->vending[i].index, sd->vending[i].amount) < 0) { // fixes by Valaris and fritz
+ clif_skill_fail(sd, MC_VENDING, 0, 0);
+ return;
+ }
+ }
+ if (i != j)
+ { //Some items were not vended. [Skotlex]
+ clif_displaymessage (sd->fd, msg_txt(266));
+ }
+ sd->vender_id = sd->bl.id;
+ sd->vend_num = i;
+ memcpy(sd->message,message, MESSAGE_SIZE-1);
+ if (clif_openvending(sd,sd->vender_id,sd->vending) > 0){
+ pc_stop_walking(sd,1);
+ clif_showvendingboard(&sd->bl,message,0);
+ if(use_irc && irc_announce_shop_flag)
+ irc_announce_shop(sd,1);
+ } else
+ sd->vender_id = 0;
+ }
+}
+
diff --git a/src/map/vending.h b/src/map/vending.h
index 021866d25..f4014a894 100644
--- a/src/map/vending.h
+++ b/src/map/vending.h
@@ -1,14 +1,14 @@
-// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
-// For more information, see LICENCE in the main folder
-
-#ifndef _VENDING_H_
-#define _VENDING_H_
-
-#include "map.h"
-
-void vending_closevending(struct map_session_data *sd);
-void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p);
-void vending_vendinglistreq(struct map_session_data *sd,int id);
-void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p);
-
-#endif // _VENDING_H_
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _VENDING_H_
+#define _VENDING_H_
+
+#include "map.h"
+
+void vending_closevending(struct map_session_data *sd);
+void vending_openvending(struct map_session_data *sd,int len,char *message,int flag,unsigned char *p);
+void vending_vendinglistreq(struct map_session_data *sd,int id);
+void vending_purchasereq(struct map_session_data *sd,int len,int id,unsigned char *p);
+
+#endif // _VENDING_H_