diff options
author | FlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2006-12-05 13:23:07 +0000 |
---|---|---|
committer | FlavioJS <FlavioJS@54d463be-8e91-2dee-dedb-b68131a5f0ec> | 2006-12-05 13:23:07 +0000 |
commit | 288490094a7fe9167747dc78d416940759a31197 (patch) | |
tree | 53dc4f5c2375f4b688b53ca8841630ddec5e1f88 /src/map | |
parent | 8ec1c47aed09c90343949d57c92760ba84738a46 (diff) | |
download | hercules-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')
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(×tamp));
- 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(×tamp)); + 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_ |