From cbccd8815d0db4828d40e278c999b87eeb103e67 Mon Sep 17 00:00:00 2001 From: skotlex Date: Sat, 27 May 2006 18:08:30 +0000 Subject: - Added structure status_data which holds status-related information (str, agi, etc, speed, amotion, adelay, dmotion, weapon-damage, race, size, etc) and weapon_atk structure with the weapon specific info (atk, atk2, element) to be used by all combat structures (TODO: Homun needs to be updated to use it) - Cleaned up TBL_PC, TBL_MOB, TBL_PC and mob_db structures to use status_data. - Set the NPC-Change Attribute duration on Time1, updated their max to 1 in the db. - Berserk's HP cost interval is now defined as time2. - Split damage received functions into pc_damage/pc_dead and mob_damage/mob_dead - Rewrote the @heal related functions to use the new status_* healing functions. - Added status functions to deal with damage and healing (status_damage, status_heal, status_percent_change) and a bunch of defines for easier handling of them (status_percent_heal, status_percent_damage, status_fix_damage, status_kill, etc) - Splitted mob_once_spawn into two. mob_once_spawn_sub creates the mob instance without spawning it. - Added defines for Elements (ELE_*) - Modified battle_calc_(weapon/magic/misc)_attack to use the status data structure. - Rewrote and cleaned up battle_calc_misc_attack - Merged config options pc_attack_attr_none, mob_attack_attr_none, pet_attack_attr_none into attack_attr_none (type 4) - Removed config options player_defense_type, monster_defense_type, pet_defense_type in favor of weapon_defense_type - Cleaned up pet.c to stop invoking status_calc_pc when unnecessary - Modified skill_calc_heal to take into account the MEDITATION bonus. - Cleaned up code of Adjustment, Madness Cancel and other GS/NJ skills inside skill_check_condition - Added status change SC_MODECHANGE which handles mob state changes (this SC is continous until manually ended, eg: like Weight50) - Modified Slim Pitcher so it will work when casted by non-players. Will now also work with SP-healing items. - Rewrote Freedom of Cast code to use function status_freecast_switch to switch adelay/speed when cast begins/ends. - Modified Magic Power to store amplified MATK/MATK2 in val3/val4 for easier updating when used in conjunction with ground skills. - Fixed Asura Strike being usable from within a combo regardless of combo skill. - Modifed SC_DANCING to store speed-change in val3 (it is shared with skill duration...) - Added StatusChangeFlagTable to store which statuses are changed by each SC - Added SCB_* constants to specify the different stats that each sc changes. SCB_PC is the only one that means a change hardcoded in status_calc_pc, the rest are handled by status_calc_bl - Added some helper functions to simplify with basic status calculations (status_base_atk, status_calc_misc, status_base_pc_maxhp, status_base_pc_maxsp) - Added status_calc_mob which calculates initial status and special base status alterations (HP changes, stat changes due to big/small mobs, etc) - Made all the status_calc functions static. - Added status_calc_bl_sub_pc for PC related calculations that must happen after status-change adjustments. - Added status_calc_bl which does status-change related calculations using as base the base_status of the bl object and the SCB_* flag passed. - Added status_get_status_data and status_get_base_status to retrieve the bl objects current status_data and basic status_data (current never returns null, instead it returns a dummy structure with basic data) - The main switch in status_change_start now only sets the tick and val values, therefore it is skipped when loading (flag&4) - Cleaned up status_change_start and replaced many of the ex-japanese comments for english ones. - Changed Hiding to store the speed penalty on val3. val4 stores interval SP cost. - Changed Chase Walk to store Speed adjustment on val3, sp cost in val4 - Changed Cloaking to store speed penalty on val3, val4&2 signals wall-present, val4&1 is infinite cloaking. - Changed Wind walk to store speed bonus on val3 - Rewrote Marionette Control to store the status to add/substract in val3/val4, it now works on anyone (players/mobs) - Changed Improve concentration to store Card bonuses (which are not counted for total % increase) on val3/val4 - Changed SC_ADRENALINE, SC_CONCENTRATION, SC_ANGELUS, SC_IMPOSITIO, SC_MELTDOWN, SC_TRUESIGHT, SC_SUN_COMFORT, SC_MOON_COMFORT, SC_STAR_COMFORT, SC_QUAGMIRE, SC_GATLINGFEVER to store the bonus modifiers in their val values rather than calculate them in status_calc_* - Status_change_start/end will use clif_status_load rather than clif_status_change when related bl is not on a map. - Modified status_change_timer to use the status_charge function rather than directly substracting SP - Added SC_ELEMENTALCHANGE to modify someone's base defense element. - pc_clean_skilltree will now also remove item-granted skills. - Learning skills will now only invoke status_calc_pc when the skill is passive. - Cleaned up pc_steal_coin - Cleaned up pc_check_base/job_lvup to only invoke the lv-up related packets and functions ONCE regardless of skill-levls earned. - Cleaned up pc_ regen related functions. - Made player-sprite mobs have item pickup animation and walkdelay when taking items. - Cleaned up mob_dead code. - Removed paramb, parame from struct map_session_data, replaced them by param_bonus[6],param_equip[6] - mob special ai state 3 signals summon flora. - Moved petDB pet_hungry_timer vars from TBL_PC to TBL_PET - Cleaned up some pet functions, made the menu functions receive as argument both pet and master. - Clones will copy a player's base status rather than battle status (so status-change alterations are not cloned) git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/trunk@6791 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/map/atcommand.c | 116 +- src/map/battle.c | 8886 +++++++++--------- src/map/battle.h | 15 +- src/map/charcommand.c | 11 +- src/map/clif.c | 169 +- src/map/clif.h | 2 +- src/map/guild.c | 2 - src/map/map.c | 7 +- src/map/map.h | 115 +- src/map/mob.c | 8196 ++++++++-------- src/map/mob.h | 22 +- src/map/npc.c | 4 +- src/map/pc.c | 1113 +-- src/map/pc.h | 12 +- src/map/pet.c | 425 +- src/map/pet.h | 5 +- src/map/script.c | 24112 ++++++++++++++++++++++++------------------------ src/map/skill.c | 22080 ++++++++++++++++++++++---------------------- src/map/skill.h | 5 +- src/map/status.c | 12297 ++++++++++++------------ src/map/status.h | 179 +- src/map/unit.c | 132 +- 22 files changed, 38519 insertions(+), 39386 deletions(-) (limited to 'src') diff --git a/src/map/atcommand.c b/src/map/atcommand.c index c5a9baf50..5274c4225 100644 --- a/src/map/atcommand.c +++ b/src/map/atcommand.c @@ -2113,9 +2113,7 @@ int atcommand_speed( speed = atoi(message); if (speed >= MIN_WALK_SPEED && speed <= MAX_WALK_SPEED) { - sd->speed = speed; - //sd->walktimer = x; - //この文を追加 by れ + sd->battle_status.speed = speed; clif_updatestatus(sd, SP_SPEED); clif_displaymessage(fd, msg_table[8]); // Speed changed. } else { @@ -2381,7 +2379,7 @@ int atcommand_die( { nullpo_retr(-1, sd); clif_specialeffect(&sd->bl,450,1); - pc_damage(NULL, sd, sd->status.hp); + status_kill(&sd->bl); clif_displaymessage(fd, msg_table[13]); // A pity! You've died. return 0; @@ -2407,7 +2405,7 @@ int atcommand_kill( if ((pl_sd = map_nick2sd(atcmd_player_name)) != NULL) { if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level - pc_damage(NULL, pl_sd, pl_sd->status.hp); + status_kill(&pl_sd->bl); clif_displaymessage(fd, msg_table[14]); // Character killed. } else { clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. @@ -2501,37 +2499,46 @@ int atcommand_heal( sscanf(message, "%d %d", &hp, &sp); if (hp == 0 && sp == 0) { - hp = sd->status.max_hp - sd->status.hp; - sp = sd->status.max_sp - sd->status.sp; - } else { - if (hp > 0 && (hp > sd->status.max_hp || hp > (sd->status.max_hp - sd->status.hp))) // fix positiv overflow - hp = sd->status.max_hp - sd->status.hp; - else if (hp < 0 && (hp < -sd->status.max_hp || hp < (1 - sd->status.hp))) // fix negativ overflow - hp = 1 - sd->status.hp; - if (sp > 0 && (sp > sd->status.max_sp || sp > (sd->status.max_sp - sd->status.sp))) // fix positiv overflow - sp = sd->status.max_sp - sd->status.sp; - else if (sp < 0 && (sp < -sd->status.max_sp || sp < (1 - sd->status.sp))) // fix negativ overflow - sp = 1 - sd->status.sp; - } - - if (hp > 0) // display like heal - clif_heal(fd, SP_HP, hp); - else if (hp < 0) // display like damage + if (!status_heal(&sd->bl, sd->battle_status.max_hp, sd->battle_status.max_sp, 2)) + clif_displaymessage(fd, msg_table[157]); // HP and SP are already with the good value. + else + clif_displaymessage(fd, msg_table[17]); // HP, SP recovered. + return 0; + } + + if(hp > 0 && sp >= 0) { + if(!status_heal(&sd->bl, hp, sp, 2)) + clif_displaymessage(fd, msg_table[157]); // HP and SP are already with the good value. + else + clif_displaymessage(fd, msg_table[17]); // HP, SP recovered. + return 0; + } + + if(hp < 0 && sp <= 0) { + status_damage(NULL, &sd->bl, -hp, -sp, 0, 0); clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0 , 4, 0); - if (sp > 0) // no display when we lost SP - clif_heal(fd, SP_SP, sp); + clif_displaymessage(fd, msg_table[156]); // HP or/and SP modified. + return 0; + } - if (hp != 0 || sp != 0) { - pc_heal(sd, hp, sp); - if (hp >= 0 && sp >= 0) - clif_displaymessage(fd, msg_table[17]); // HP, SP recovered. + //Opposing signs. + if (hp) { + if (hp > 0) + status_heal(&sd->bl, hp, 0, 2); + else { + status_damage(NULL, &sd->bl, -hp, 0, 0, 0); + clif_damage(&sd->bl,&sd->bl, gettick(), 0, 0, -hp, 0 , 4, 0); + } + } + + if (sp) { + if (sp > 0) + status_heal(&sd->bl, 0, sp, 2); else - clif_displaymessage(fd, msg_table[156]); // HP or/and SP modified. - } else { - clif_displaymessage(fd, msg_table[157]); // HP and SP are already with the good value. - return -1; + status_damage(NULL, &sd->bl, 0, -sp, 0, 0); } + clif_displaymessage(fd, msg_table[156]); // HP or/and SP modified. return 0; } @@ -2767,7 +2774,7 @@ int atcommand_baselevelup( clif_updatestatus(sd, SP_NEXTBASEEXP); clif_updatestatus(sd, SP_STATUSPOINT); status_calc_pc(sd, 0); - pc_heal(sd, sd->status.max_hp, sd->status.max_sp); + status_percent_heal(&sd->bl, 100, 100); clif_misceffect(&sd->bl, 0); clif_displaymessage(fd, msg_table[21]); /* Base level raised. */ } else { @@ -3691,7 +3698,7 @@ static int atkillmonster_sub(struct block_list *bl, va_list ap) { return 0; //Do not touch WoE mobs! if (flag) - mob_damage(NULL, md, md->hp, 2); + status_kill(bl); else unit_remove_map(&md->bl,1); @@ -4841,7 +4848,7 @@ int atcommand_doom( pl_allsd = map_getallusers(&users); for(i = 0; i < users; i++) { if ((pl_sd = pl_allsd[i]) && pl_sd->fd != fd && pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can doom only lower or same gm level - pc_damage(NULL, pl_sd, pl_sd->status.hp); + status_kill(&pl_sd->bl); clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement. } } @@ -4867,7 +4874,7 @@ int atcommand_doommap( if ((pl_sd = pl_allsd[i]) && pl_sd->fd != fd && sd->bl.m == pl_sd->bl.m && pc_isGM(sd) >= pc_isGM(pl_sd)) // you can doom only lower or same gm level { - pc_damage(NULL, pl_sd, pl_sd->status.hp); + status_kill(&pl_sd->bl); // clif_specialeffect(&pl_sd->bl,450,1); clif_displaymessage(pl_sd->fd, msg_table[61]); // The holy messenger has given judgement. } @@ -8247,8 +8254,6 @@ atcommand_summon( { char name[NAME_LENGTH]; int mob_id = 0; - int x = 0; - int y = 0; int id = 0; int duration = 0; struct mob_data *md; @@ -8271,18 +8276,17 @@ atcommand_summon( if(mob_id == 0 || mobdb_checkid(mob_id) == 0) return -1; - x = sd->bl.x + (rand() % 10 - 5); - y = sd->bl.y + (rand() % 10 - 5); + md = mob_once_spawn_sub(&sd->bl, sd->bl.m, -1, -1, "--ja--", mob_id, ""); - id = mob_once_spawn(sd,"this", x, y, "--ja--", mob_id, 1, ""); - if((md=(struct mob_data *)map_id2bl(id))){ + if(md){ md->master_id=sd->bl.id; md->special_state.ai=1; - md->mode=md->db->mode|MD_AGGRESSIVE; md->deletetimer=add_timer(tick+(duration*60000),mob_timer_delete,id,0); clif_misceffect2(&md->bl,344); + mob_spawn(md); + sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); + clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,md->bl.x,md->bl.y,tick); } - clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,x,y,tick); return 0; } @@ -9115,7 +9119,7 @@ int atcommand_killid( { if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) { if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level - pc_damage(NULL, pl_sd, pl_sd->status.hp); + status_kill(&pl_sd->bl); clif_displaymessage(fd, msg_table[14]); // Character killed. } else { clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. @@ -9163,7 +9167,7 @@ int atcommand_killid2( { if ((pl_sd = (struct map_session_data *) session[session_id]->session_data) != NULL) { if (pc_isGM(sd) >= pc_isGM(pl_sd)) { // you can kill only lower or same level - pc_damage(NULL, pl_sd, pl_sd->status.hp); + status_kill(&pl_sd->bl); clif_displaymessage(fd, msg_table[14]); // Character killed. } else { clif_displaymessage(fd, msg_table[81]); // Your GM level don't authorise you to do this action on this player. @@ -9417,19 +9421,17 @@ int atcommand_mobinfo( else sprintf(atcmd_output, "Monster: '%s'/'%s' (%d)", mob->name, mob->jname, mob_id); clif_displaymessage(fd, atcmd_output); - sprintf(atcmd_output, " Level:%d HP:%d SP:%d Base EXP:%d Job EXP:%d", mob->lv, mob->max_hp, mob->max_sp, mob->base_exp, mob->job_exp); + sprintf(atcmd_output, " Level:%d HP:%d SP:%d Base EXP:%d Job EXP:%d", mob->lv, mob->status.max_hp, mob->status.max_sp, mob->base_exp, mob->job_exp); clif_displaymessage(fd, atcmd_output); - sprintf(atcmd_output, " DEF:%d MDEF:%d STR:%d AGI:%d VIT:%d INT:%d DEX:%d LUK:%d", mob->def, mob->mdef, mob->str, mob->agi, mob->vit, mob->int_, mob->dex, mob->luk); + sprintf(atcmd_output, " DEF:%d MDEF:%d STR:%d AGI:%d VIT:%d INT:%d DEX:%d LUK:%d", + mob->status.def, mob->status.mdef, mob->status.str, mob->status.agi, + mob->status.vit, mob->status.int_, mob->status.dex, mob->status.luk); clif_displaymessage(fd, atcmd_output); - if (mob->element < 20) { - //Element - None, Level 0 - i = 0; - j = 0; - } else { - i = mob->element % 20 + 1; - j = mob->element / 20; - } - sprintf(atcmd_output, " ATK:%d~%d Range:%d~%d~%d Size:%s Race: %s Element: %s (Lv:%d)", mob->atk1, mob->atk2, mob->range, mob->range2 , mob->range3, msize[mob->size], mrace[mob->race], melement[i], j); + + sprintf(atcmd_output, " ATK:%d~%d Range:%d~%d~%d Size:%s Race: %s Element: %s (Lv:%d)", + mob->status.rhw.atk, mob->status.rhw.atk2, mob->status.rhw.range, + mob->range2 , mob->range3, msize[mob->status.size], + mrace[mob->status.race], melement[mob->status.def_ele], mob->status.ele_lv); clif_displaymessage(fd, atcmd_output); // drops clif_displaymessage(fd, " Drops:"); @@ -10195,7 +10197,7 @@ int atcommand_clone( y = sd->bl.y; } - if((x = mob_clone_spawn(pl_sd, (char*)mapindex_id2name(sd->mapindex), x, y, "", master, 0, flag?1:0, 0)) > 0) { + if((x = mob_clone_spawn(pl_sd, sd->bl.m, x, y, "", master, 0, flag?1:0, 0)) > 0) { clif_displaymessage(fd, msg_txt(128+flag*2)); return 0; } diff --git a/src/map/battle.c b/src/map/battle.c index f869b9460..ecaf692bf 100644 --- a/src/map/battle.c +++ b/src/map/battle.c @@ -1,4621 +1,4265 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include - -#include "battle.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 "pc.h" -#include "status.h" -#include "skill.h" -#include "mob.h" -#include "itemdb.h" -#include "clif.h" -#include "pet.h" -#include "guild.h" -#include "party.h" - -#include "mercenary.h" - -#define is_boss(bl) status_get_mexp(bl) // Can refine later [Aru] - -int attr_fix_table[4][10][10]; - -struct Battle_Config battle_config; -static struct eri *delay_damage_ers; //For battle delay damage structures. - -int battle_getcurrentskill(struct block_list *bl) -{ //Returns the current/last skill in use by this bl. - struct unit_data *ud; - - if (bl->type == BL_SKILL) { - struct skill_unit * su = (struct skill_unit*)bl; - return su->group?su->group->skill_id:0; - } - ud = unit_bl2ud(bl); - return ud?ud->skillid:0; -} - -/*========================================== - * Get random targetting enemy - *------------------------------------------ - */ -static int battle_gettargeted_sub(struct block_list *bl, va_list ap) -{ - struct block_list **bl_list; - struct unit_data *ud; - int target_id; - int *c; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - - bl_list = va_arg(ap, struct block_list **); - c = va_arg(ap, int *); - target_id = va_arg(ap, int); - - if (bl->id == target_id) - return 0; - if (*c >= 24) - return 0; - - ud = unit_bl2ud(bl); - if (!ud) return 0; - - if (ud->target == target_id || ud->skilltarget == target_id) { - bl_list[(*c)++] = bl; - return 1; - } - return 0; -} - -struct block_list* battle_gettargeted(struct block_list *target) -{ - struct block_list *bl_list[24]; - int c = 0; - nullpo_retr(NULL, target); - - memset(bl_list, 0, sizeof(bl_list)); - map_foreachinrange(battle_gettargeted_sub, target, AREA_SIZE, BL_CHAR, bl_list, &c, target->id); - if (c == 0 || c > 24) - return NULL; - return bl_list[rand()%c]; -} - - -//Returns the id of the current targetted character of the passed bl. [Skotlex] -int battle_gettarget(struct block_list *bl) -{ - switch (bl->type) - { - case BL_PC: - return ((struct map_session_data*)bl)->ud.target; - case BL_MOB: - return ((struct mob_data*)bl)->target_id; - case BL_PET: - return ((struct pet_data*)bl)->target_id; - } - return 0; -} -// ダ??[ジの遅延 -struct delay_damage { - struct block_list *src; - int target; - int damage; - int delay; - unsigned short distance; - unsigned short skill_lv; - unsigned short skill_id; - unsigned short dmg_lv; - unsigned short flag; - unsigned char attack_type; -}; - -int battle_delay_damage_sub (int tid, unsigned int tick, int id, int data) -{ - struct delay_damage *dat = (struct delay_damage *)data; - struct block_list *target = map_id2bl(dat->target); - if (target && dat && map_id2bl(id) == dat->src && target->prev != NULL && !status_isdead(target) && - target->m == dat->src->m && check_distance_bl(dat->src, target, dat->distance)) //Check to see if you haven't teleported. [Skotlex] - { - battle_damage(dat->src, target, dat->damage, dat->delay, dat->flag); - if ((dat->dmg_lv == ATK_DEF || dat->damage > 0) && dat->attack_type) - { - if (!status_isdead(target)) - skill_additional_effect(dat->src,target,dat->skill_id,dat->skill_lv,dat->attack_type, tick); - skill_counter_additional_effect(dat->src,target,dat->skill_id,dat->skill_lv,dat->attack_type,tick); - } - - } - ers_free(delay_damage_ers, dat); - return 0; -} - -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 flag) -{ - struct delay_damage *dat; - nullpo_retr(0, src); - nullpo_retr(0, target); - - if (!battle_config.delay_battle_damage) { - battle_damage(src, target, damage, ddelay, flag); - if ((damage > 0 || dmg_lv == ATK_DEF) && attack_type) - { - if (!status_isdead(target)) - skill_additional_effect(src, target, skill_id, skill_lv, attack_type, gettick()); - skill_counter_additional_effect(src, target, skill_id, skill_lv, attack_type, gettick()); - } - return 0; - } - dat = ers_alloc(delay_damage_ers, struct delay_damage); - dat->src = src; - dat->target = target->id; - dat->skill_id = skill_id; - dat->skill_lv = skill_lv; - dat->attack_type = attack_type; - dat->damage = damage; - dat->dmg_lv = dmg_lv; - dat->delay = ddelay; - dat->flag = flag; - dat->distance = distance_bl(src, target)+10; //Attack should connect regardless unless you teleported. - add_timer(tick, battle_delay_damage_sub, src->id, (int)dat); - - return 0; -} - -// 実?ロにHPを操? -int battle_damage(struct block_list *src,struct block_list *target,int damage, int walkdelay, int flag) -{ - struct map_session_data *sd = NULL; - struct status_change *sc; - int r_damage=0; - - nullpo_retr(0, target); //srcはNULLで呼ばれることがあるので他でチェック - - if (damage == 0 || status_isdead(target)) - return 0; - - if (damage < 0) - return battle_heal(src,target,-damage,0,flag); - - if (src) - BL_CAST(BL_PC, src, sd); - - sc = status_get_sc(target); - - if (!flag && sc && sc->count) { - // 凍結?A?ホ化?A?眠を?チ去 - if (sc->data[SC_FREEZE].timer != -1) - status_change_end(target,SC_FREEZE,-1); - if (sc->data[SC_STONE].timer!=-1 && sc->data[SC_STONE].val2 == 0) - status_change_end(target,SC_STONE,-1); - if (sc->data[SC_SLEEP].timer != -1) - status_change_end(target,SC_SLEEP,-1); - if (sc->data[SC_WINKCHARM].timer != -1) - status_change_end(target,SC_WINKCHARM,-1); - if (sc->data[SC_CONFUSION].timer != -1) - status_change_end(target, SC_CONFUSION, -1); - if (sc->data[SC_TRICKDEAD].timer != -1) - status_change_end(target, SC_TRICKDEAD, -1); - if (sc->data[SC_HIDING].timer != -1) - status_change_end(target, SC_HIDING, -1); - if (sc->data[SC_CLOAKING].timer != -1) - status_change_end(target, SC_CLOAKING, -1); - if (sc->data[SC_CHASEWALK].timer != -1) - status_change_end(target, SC_CHASEWALK, -1); - if (sc->data[SC_ENDURE].timer != -1 && !sc->data[SC_ENDURE].val4) { - //Endure count is only reduced by non-players on non-gvg maps. - //val4 signals infinite endure. [Skotlex] - if (src && src->type != BL_PC && !map_flag_gvg(target->m) - && --(sc->data[SC_ENDURE].val2) < 0) - status_change_end(target, SC_ENDURE, -1); - } - if (sc->data[SC_GRAVITATION].timer != -1 && - sc->data[SC_GRAVITATION].val3 == BCT_SELF) { - struct skill_unit_group *sg = (struct skill_unit_group *)sc->data[SC_GRAVITATION].val4; - if (sg) { - skill_delunitgroup(target,sg); - sc->data[SC_GRAVITATION].val4 = 0; - status_change_end(target, SC_GRAVITATION, -1); - } - } - if (sc->data[SC_DEVOTION].val1 && src && battle_getcurrentskill(src) != PA_PRESSURE) - { - struct map_session_data *sd2 = map_id2sd(sc->data[SC_DEVOTION].val1); - if (sd2 && sd2->devotion[sc->data[SC_DEVOTION].val2] == target->id) - { - clif_damage(src, &sd2->bl, gettick(), 0, 0, damage, 0, 0, 0); - pc_damage(&sd2->bl, sd2, damage); - return 0; - } else - status_change_end(target, SC_DEVOTION, -1); - } - } - - if (!flag) - unit_skillcastcancel(target, 2); - - switch (target->type) - { - case BL_MOB: - r_damage = mob_damage(src,(TBL_MOB*)target, damage,flag&2?3:0); - break; - case BL_PC: - r_damage = pc_damage(src,(TBL_PC*)target,damage); - break; - case BL_HOMUNCULUS: //[blackhole89] - r_damage = merc_damage(src,(struct homun_data*)target,damage,0); - break; - case BL_SKILL: - r_damage = skill_unit_ondamaged((struct skill_unit *)target, src, damage, gettick()); - break; - } - - if (walkdelay && !status_isdead(target)) - unit_set_walkdelay(target, gettick(), walkdelay, 0); - - return r_damage; -} - -int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp, int flag) -{ - struct status_change *sc; - nullpo_retr(0, target); - - if (status_isdead(target)) - return 0; - - if (!flag) { - sc = status_get_sc(target); - if (sc && sc->count) { - if (sc->data[SC_BERSERK].timer!=-1) - hp = 0; - } - } - - if (sp == 0) { - if (hp < 0) //Use flag 1 because heal-damage shouldn't make you flinch. - return battle_damage(bl, target, -hp, 0, 1); - if (hp == 0) - return 0; - } - - if (target->type == BL_MOB) - return mob_heal((struct mob_data *)target,hp); - else if (target->type == BL_PC) - return pc_heal((struct map_session_data *)target,hp,sp); - else if (target->type == BL_HOMUNCULUS) //[blackhole89] - return merc_heal((struct homun_data *)target,hp,sp); - return 0; -} - -/*========================================== - * Does attribute fix modifiers. - * Added passing of the chars so that the status changes can affect it. [Skotlex] - * Note: Passing src/target == NULL is perfectly valid, it skips SC_ checks. - *------------------------------------------ - */ -int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_elem) -{ - int def_type = def_elem % 10, def_lv = def_elem / 10 / 2; - struct status_change *sc=NULL, *tsc=NULL; - int ratio; - - if (src) sc = status_get_sc(src); - if (target) tsc = status_get_sc(target); - - if (atk_elem < 0 || atk_elem > 9) - atk_elem = rand()%9; //?器属?ォランダムで付加 - - if (def_type < 0 || def_type > 9 || - def_lv < 1 || def_lv > 4) { // 属 ?ォ値がおかしいのでとりあえずそのまま返す - if (battle_config.error_log) - ShowError("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n",atk_elem,def_type,def_lv); - //TODO: Remove this debug case once the cause is resolved. [Skotlex] - if (src) switch (src->type) { - case BL_MOB: - ShowDebug("src: Mob %s-%d\n", ((struct mob_data*)src)->name, ((struct mob_data*)src)->class_); - break; - case BL_PC: - ShowDebug("src: Player %s-%d\n", ((struct map_session_data*)src)->status.name, ((struct map_session_data*)src)->bl.id); - break; - case BL_PET: - ShowDebug("src: Pet %s-%d\n", ((struct pet_data*)src)->name, ((struct pet_data*)src)->bl.id); - break; - case BL_SKILL: - ShowDebug("src: Ground Skill id: %d\n", ((struct skill_unit*)src)->group->skill_id); - break; - default: - ShowDebug("unknown source type %d.\n", src->type); - } - if (target) switch (target->type) { - case BL_MOB: - ShowDebug("target: Mob %s-%d\n", ((struct mob_data*)target)->name, ((struct mob_data*)target)->class_); - break; - case BL_PC: - ShowDebug("target: Player %s-%d\n", ((struct map_session_data*)target)->status.name, ((struct map_session_data*)target)->bl.id); - break; - case BL_PET: - ShowDebug("target: Pet %s-%d\n", ((struct pet_data*)target)->name, ((struct pet_data*)target)->bl.id); - break; - case BL_SKILL: - ShowDebug("target: Ground Skill id: %d\n", ((struct skill_unit*)target)->group->skill_id); - break; - default: - ShowDebug("unknown target type %d.\n", target->type); - } - return damage; - } - - ratio = attr_fix_table[def_lv-1][atk_elem][def_type]; - if (sc && sc->count) - { - if(sc->data[SC_VOLCANO].timer!=-1 && atk_elem == 3) - ratio += enchant_eff[sc->data[SC_VOLCANO].val1-1]; - if(sc->data[SC_VIOLENTGALE].timer!=-1 && atk_elem == 4) - ratio += enchant_eff[sc->data[SC_VIOLENTGALE].val1-1]; - if(sc->data[SC_DELUGE].timer!=-1 && atk_elem == 1) - ratio += enchant_eff[sc->data[SC_DELUGE].val1-1]; - } - if (tsc && tsc->count) - { - if(tsc->data[SC_ARMOR_ELEMENT].timer!=-1) - { - if (tsc->data[SC_ARMOR_ELEMENT].val1 == atk_elem) - ratio -= tsc->data[SC_ARMOR_ELEMENT].val2; - else - if (tsc->data[SC_ARMOR_ELEMENT].val3 == atk_elem) - ratio -= tsc->data[SC_ARMOR_ELEMENT].val4; - } - } - return damage*ratio/100; -} - -/*========================================== - * ダ??[ジ?ナ?I計算 - *------------------------------------------ - */ -int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag) -{ - struct map_session_data *sd = NULL; - struct mob_data *md = NULL; - struct status_change *sc; - struct status_change_entry *sci; - - nullpo_retr(0, bl); - - if (damage <= 0) - return 0; - - if (bl->type == BL_MOB) { - md=(struct mob_data *)bl; - } else if (bl->type == BL_PC) { - sd=(struct map_session_data *)bl; - } - - sc = status_get_sc(bl); - - if(flag&BF_LONG && map_getcell(bl->m, bl->x, bl->y, CELL_CHKPNEUMA) && - ((flag&BF_WEAPON && skill_num != NPC_GUIDEDATTACK) || - (flag&BF_MISC && skill_num != PA_PRESSURE) - )){ - return 0; - } - - if (sc && sc->count) { - //First, sc_*'s that reduce damage to 0. - if (sc->data[SC_SAFETYWALL].timer!=-1 && flag&BF_SHORT && (skill_num != NPC_GUIDEDATTACK && skill_num != AM_DEMONSTRATION) - ) { - // セ?[フティウォ?[ル - struct skill_unit_group *group = (struct skill_unit_group *)sc->data[SC_SAFETYWALL].val3; - if (group) { - if (--group->val2<=0) - skill_delunitgroup(NULL,group); - return 0; - } else { - status_change_end(bl,SC_SAFETYWALL,-1); - } - } - - if(sc->data[SC_LANDPROTECTOR].timer!=-1 && flag&BF_MAGIC) - return 0; - - if(sc->data[SC_AUTOGUARD].timer != -1 && flag&BF_WEAPON && - rand()%100 < sc->data[SC_AUTOGUARD].val2) { - int delay; - clif_skill_nodamage(bl,bl,CR_AUTOGUARD,sc->data[SC_AUTOGUARD].val1,1); - // different delay depending on skill level [celest] - if (sc->data[SC_AUTOGUARD].val1 <= 5) - delay = 300; - else if (sc->data[SC_AUTOGUARD].val1 > 5 && sc->data[SC_AUTOGUARD].val1 <= 9) - delay = 200; - else - delay = 100; - unit_set_walkdelay(bl, gettick(), delay, 1); - - if(sc->data[SC_SHRINK].timer != -1 && rand()%100<5*sc->data[SC_AUTOGUARD].val1) - skill_blown(bl,src,skill_get_blewcount(CR_SHRINK,1)); - return 0; - } - -// -- moonsoul (chance to block attacks with new Lord Knight skill parrying) -// - if(sc->data[SC_PARRYING].timer != -1 && flag&BF_WEAPON && - rand()%100 < sc->data[SC_PARRYING].val2) { - clif_skill_nodamage(bl,bl,LK_PARRYING,sc->data[SC_PARRYING].val1,1); - return 0; - } - - if(sc->data[SC_DODGE].timer != -1 && !sc->opt1 && - (flag&BF_LONG || sc->data[SC_SPURT].timer != -1) - && rand()%100 < 20) { - clif_skill_nodamage(bl,bl,TK_DODGE,1,1); - if (sc->data[SC_COMBO].timer == -1) - sc_start4(bl, SC_COMBO, 100, TK_JUMPKICK, src->id, 0, 0, 2000); - return 0; - } - - if(sc->data[SC_FOGWALL].timer != -1 && flag&BF_MAGIC - && rand()%100 < 75 && !(skill_get_inf(skill_num)&INF_GROUND_SKILL)) - return 0; - - if(sc->data[SC_KAUPE].timer != -1 && - rand()%100 < sc->data[SC_KAUPE].val2 && - (src->type == BL_PC || !skill_num)) - { //Kaupe only blocks all skills of players. - clif_skill_nodamage(bl,bl,SL_KAUPE,1,1); - if (--sc->data[SC_KAUPE].val3 <= 0) //We make it work like Safety Wall, even though it only blocks 1 time. - status_change_end(bl, SC_KAUPE, -1); - return 0; - } - - //Now damage increasing effects - if(sc->data[SC_AETERNA].timer!=-1 && skill_num != PA_PRESSURE && skill_num != PF_SOULBURN){ - damage<<=1; - status_change_end( bl,SC_AETERNA,-1 ); - } - - if(sc->data[SC_SPIDERWEB].timer!=-1) // [Celest] - if ((flag&BF_SKILL && skill_get_pl(skill_num)==3) || - (!flag&BF_SKILL && status_get_attack_element(src)==3)) { - damage<<=1; - status_change_end(bl, SC_SPIDERWEB, -1); - } - - //Finally damage reductions.... - if(sc->data[SC_ASSUMPTIO].timer != -1){ - if(map_flag_vs(bl->m)) - damage=damage*2/3; //Receive 66% damage - else - damage>>=1; //Receive 50% damage - } - - if(sc->data[SC_DEFENDER].timer != -1 && flag&BF_LONG && flag&BF_WEAPON) - damage=damage*(100-sc->data[SC_DEFENDER].val2)/100; - - if(sc->data[SC_FOGWALL].timer != -1 && flag&BF_LONG && flag&BF_WEAPON) - damage >>=1; - - if(sc->data[SC_ENERGYCOAT].timer!=-1 && flag&BF_WEAPON){ - if(sd && sd->status.max_sp){ - if(sd->status.sp>0){ - int per = 100*sd->status.sp / sd->status.max_sp; - per /=20; //Uses 20% SP intervals. - //SP Cost: 1% + 0.5% per every 20% SP - if (pc_damage_sp(sd, (10+5*per)*sd->status.max_sp/10000, 0) <= 0) - status_change_end( bl,SC_ENERGYCOAT,-1 ); - //Reduction: 6% + 6% every 20% - damage -= damage * 6 * (1+per) / 100; - } - } - else - damage -= damage * (sc->data[SC_ENERGYCOAT].val1 * 6) / 100; - } - - if(sc->data[SC_REJECTSWORD].timer!=-1 && flag&BF_WEAPON && - // Fixed the condition check [Aalye] - (src->type==BL_MOB || (src->type==BL_PC && (((struct map_session_data *)src)->status.weapon == W_DAGGER || - ((struct map_session_data *)src)->status.weapon == W_1HSWORD || - ((struct map_session_data *)src)->status.weapon == W_2HSWORD)))){ - if(rand()%100 < (15*sc->data[SC_REJECTSWORD].val1)){ - damage = damage*50/100; - battle_damage(bl,src,damage,clif_damage(bl,src,gettick(),0,0,damage,0,0,0),0); - clif_skill_nodamage(bl,bl,ST_REJECTSWORD,sc->data[SC_REJECTSWORD].val1,1); - if((--sc->data[SC_REJECTSWORD].val2)<=0) - status_change_end(bl, SC_REJECTSWORD, -1); - } - } - - //Finally Kyrie because it may, or not, reduce damage to 0. - if(sc->data[SC_KYRIE].timer!=-1){ - sci=&sc->data[SC_KYRIE]; - sci->val2-=damage; - if(flag&BF_WEAPON || skill_num == TF_THROWSTONE){ - if(sci->val2>=0) - damage=0; - else - damage=-sci->val2; - } - if((--sci->val3)<=0 || (sci->val2<=0) || skill_num == AL_HOLYLIGHT) - status_change_end(bl, SC_KYRIE, -1); - } - if (damage <= 0) return 0; - } - - //SC effects from caster side. - sc = status_get_sc(src); - if (sc && sc->count) { - if(sc->data[SC_FOGWALL].timer != -1 && flag&(BF_LONG|BF_MAGIC)) { - if (flag&BF_MAGIC) { - if(!(skill_get_inf(skill_num)&INF_GROUND_SKILL) && rand()%100 < 75) - return 0; - } else if (flag&BF_WEAPON) - damage >>=1; - } - } - - if (battle_config.pk_mode && sd && damage > 0) - { - if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex] - if (flag&BF_WEAPON) - damage = damage * battle_config.pk_weapon_damage_rate/100; - if (flag&BF_MAGIC) - damage = damage * battle_config.pk_magic_damage_rate/100; - if (flag&BF_MISC) - damage = damage * battle_config.pk_misc_damage_rate/100; - } else { //Normal attacks get reductions based on range. - if (flag & BF_SHORT) - damage = damage * battle_config.pk_short_damage_rate/100; - if (flag & BF_LONG) - damage = damage * battle_config.pk_long_damage_rate/100; - } - if(damage < 1) damage = 1; - } - - if(battle_config.skill_min_damage && damage > 0 && damage < div_) - { - if ((flag&BF_WEAPON && battle_config.skill_min_damage&1) - || (flag&BF_MAGIC && battle_config.skill_min_damage&2) - || (flag&BF_MISC && battle_config.skill_min_damage&4) - ) - damage = div_; - } - - if( md && !status_isdead(bl) && src != bl) { - if (damage > 0 ) - mobskill_event(md,src,gettick(),flag); - if (skill_num) - mobskill_event(md,src,gettick(),MSC_SKILLUSED|(skill_num<<16)); - } - - return damage; -} - -/*========================================== - * Calculates GVG related damage adjustments. - *------------------------------------------ - */ -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) -{ - struct mob_data *md = NULL; - int class_; - - if (damage <= 0) - return 0; - - class_ = status_get_class(bl); - - if (bl->type == BL_MOB) - md=(struct mob_data *)bl; - - if(md && md->guardian_data) { - if(class_ == MOBID_EMPERIUM && flag&BF_SKILL) - //SKill inmunity. - switch (skill_num) { - case PA_PRESSURE: - case MO_TRIPLEATTACK: - case HW_GRAVITATION: - break; - default: - return 0; - } - if(src->type != BL_MOB) { - struct guild *g=guild_search(status_get_guild_id(src)); - if (!g) return 0; - if (class_ == MOBID_EMPERIUM && guild_checkskill(g,GD_APPROVAL) <= 0) - return 0; - if (battle_config.guild_max_castles && - guild_checkcastles(g)>=battle_config.guild_max_castles) - return 0; // [MouseJstr] - } - } - - switch (skill_num) { - //Skills with no damage reduction. - case PA_PRESSURE: - case HW_GRAVITATION: - break; - default: - if (md && md->guardian_data) { - damage -= damage - * (md->guardian_data->castle->defense/100) - * (battle_config.castle_defense_rate/100); - } - if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex] - if (flag&BF_WEAPON) - damage = damage * battle_config.gvg_weapon_damage_rate/100; - if (flag&BF_MAGIC) - damage = damage * battle_config.gvg_magic_damage_rate/100; - if (flag&BF_MISC) - damage = damage * battle_config.gvg_misc_damage_rate/100; - } else { //Normal attacks get reductions based on range. - if (flag & BF_SHORT) - damage = damage * battle_config.gvg_short_damage_rate/100; - if (flag & BF_LONG) - damage = damage * battle_config.gvg_long_damage_rate/100; - } - if(damage < 1) damage = 1; - } - return damage; -} - -/*========================================== - * HP/SP吸収の計算 - *------------------------------------------ - */ -static int battle_calc_drain(int damage, int rate, int per) -{ - int diff = 0; - - if (per && rand()%1000 < rate) { - diff = (damage * per) / 100; - if (diff == 0) { - if (per > 0) - diff = 1; - else - diff = -1; - } - } - return diff; -} - -/*========================================== - * ?C練ダ??[ジ - *------------------------------------------ - */ -int battle_addmastery(struct map_session_data *sd,struct block_list *target,int dmg,int type) -{ - int damage,skill; - int race=status_get_race(target); - int weapon; - damage = dmg; - - nullpo_retr(0, sd); - - // デ?[モンベイン(+3 ?` +30) vs 不死 or 悪魔 (死?lは含めない?H) - if((skill = pc_checkskill(sd,AL_DEMONBANE)) > 0 && (battle_check_undead(race,status_get_elem_type(target)) || race==RC_DEMON) ) - damage += (skill*(int)(3+(sd->status.base_level+1)*0.05)); // submitted by orn - //damage += (skill * 3); - - // ビ?[ストベイン(+4 ?` +40) vs 動物 or ?ゥ虫 - if((skill = pc_checkskill(sd,HT_BEASTBANE)) > 0 && (race==RC_BRUTE || race==RC_INSECT) ) { - damage += (skill * 4); - if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_HUNTER) - damage += sd->status.str; - } - - if(type == 0) - weapon = sd->weapontype1; - else - weapon = sd->weapontype2; - switch(weapon) - { - case W_DAGGER: - case W_1HSWORD: - { - // 剣?C練(+4 ?` +40) 片手剣 短剣含む - if((skill = pc_checkskill(sd,SM_SWORD)) > 0) { - damage += (skill * 4); - } - break; - } - case W_2HSWORD: - { - // 両手剣?C練(+4 ?` +40) 両手剣 - if((skill = pc_checkskill(sd,SM_TWOHAND)) > 0) { - damage += (skill * 4); - } - break; - } - case W_1HSPEAR: - case W_2HSPEAR: - { - // 槍?C練(+4 ?` +40,+5 ?` +50) 槍 - if((skill = pc_checkskill(sd,KN_SPEARMASTERY)) > 0) { - if(!pc_isriding(sd)) - damage += (skill * 4); // ペコに?謔チてない - else - damage += (skill * 5); // ペコに?謔チてる - } - break; - } - case W_1HAXE: - case W_2HAXE: - { - if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) { - damage += (skill * 3); - } - break; - } - case W_MACE: - { - if((skill = pc_checkskill(sd,PR_MACEMASTERY)) > 0) { - damage += (skill * 3); - } - break; - } - case W_FIST: - case W_KNUCKLE: - { - if((skill = pc_checkskill(sd,MO_IRONHAND)) > 0) { - damage += (skill * 3); - } - break; - } - case W_MUSICAL: - { - // 楽器の練?K(+3 ?` +30) 楽器 - if((skill = pc_checkskill(sd,BA_MUSICALLESSON)) > 0) { - damage += (skill * 3); - } - break; - } - case W_WHIP: - { - // Dance Lesson Skill Effect(+3 damage for every lvl = +30) 鞭 - if((skill = pc_checkskill(sd,DC_DANCINGLESSON)) > 0) { - damage += (skill * 3); - } - break; - } - case W_BOOK: - { - // Advance Book Skill Effect(+3 damage for every lvl = +30) { - if((skill = pc_checkskill(sd,SA_ADVANCEDBOOK)) > 0) { - damage += (skill * 3); - } - break; - } - case W_KATAR: - { - if((skill = pc_checkskill(sd,ASC_KATAR)) > 0) { - //Advanced Katar Research by zanetheinsane - damage += damage*(10 +skill * 2)/100; - } - // カタ?[ル?C練(+3 ?` +30) カタ?[ル - if((skill = pc_checkskill(sd,AS_KATAR)) > 0) { - //ソニックブ??[時は別???i1撃に付き1/8適応) - damage += (skill * 3); - } - break; - } - } -/*//need to add this on shuriken skills. - if((skill = pc_checkskill(sd,NJ_TOBIDOUGU)) > 0) { - damage += (skill * 3); - } -*/ - return (damage); -} -/*========================================== - * Calculates the standard damage of a normal attack assuming it hits, - * it calculates nothing extra fancy, is needed for magnum break's WATK_ELEMENT bonus. [Skotlex] - *------------------------------------------ - * Pass damage2 as NULL to not calc it. - * Flag values: - * &1: Critical hit - * &2: Arrow attack - * &4: Skill is Magic Crasher - * &8: Skip target size adjustment (Extremity Fist?) - */ -static void battle_calc_base_damage(struct block_list *src, struct block_list *target, int* damage1, int* damage2, int flag) -{ - unsigned short baseatk=0, baseatk_=0, atkmin=0, atkmax=0, atkmin_=0, atkmax_=0; - struct map_session_data *sd; - struct status_change *sc= status_get_sc(src); - int t_size = status_get_size(target); - - if (src->type == BL_PC) - sd = (struct map_session_data*)src; - else - sd = NULL; - - if (!sd) - { //Mobs/Pets - if ((target->type==BL_MOB && battle_config.enemy_str) || - (target->type==BL_PET && battle_config.pet_str)) - baseatk = status_get_batk(src); - - if(flag&4) - { - if (!(flag&1)) - atkmin = status_get_matk2(src); - atkmax = status_get_matk1(src); - } else { - if (!(flag&1)) - atkmin = status_get_atk(src); - atkmax = status_get_atk2(src); - } - if (atkmin > atkmax) - atkmin = atkmax; - } else { //PCs - if(flag&4) - { - baseatk = status_get_matk2(src); - if (damage2) baseatk_ = baseatk; - } else { - baseatk = status_get_batk(src); - if (damage2) baseatk_ = baseatk; - } - //rodatazone says that Overrefine bonuses are part of baseatk - if(sd->right_weapon.overrefine>0) - baseatk+= rand()%sd->right_weapon.overrefine+1; - if (damage2 && sd->left_weapon.overrefine>0) - baseatk_+= rand()%sd->left_weapon.overrefine+1; - - atkmax = status_get_atk(src); - if (damage2) - atkmax_ = status_get_atk_(src); - - if (!(flag&1) || (flag&2)) - { //Normal attacks - atkmin = atkmin_ = status_get_dex(src); - - if (sd->equip_index[9] >= 0 && sd->inventory_data[sd->equip_index[9]]) - atkmin = atkmin*(80 + sd->inventory_data[sd->equip_index[9]]->wlv*20)/100; - - if (atkmin > atkmax) - atkmin = atkmax; - - if(damage2) - { - if (sd->equip_index[8] >= 0 && sd->inventory_data[sd->equip_index[8]]) - atkmin_ = atkmin_*(80 + sd->inventory_data[sd->equip_index[8]]->wlv*20)/100; - - if (atkmin_ > atkmax_) - atkmin_ = atkmax_; - } - - if(flag&2) - { //Bows - atkmin = atkmin*atkmax/100; - if (atkmin > atkmax) - atkmax = atkmin; - } - } - } - - if (sc && sc->data[SC_MAXIMIZEPOWER].timer!=-1) - { - atkmin = atkmax; - atkmin_ = atkmax_; - } - - //Weapon Damage calculation - if (!(flag&1)) - { - (*damage1) += (atkmax>atkmin? rand()%(atkmax-atkmin):0)+atkmin; - if (damage2) - (*damage2) += (atkmax_>atkmin_? rand()%(atkmax_-atkmin_) :0) +atkmin_; - } else { - (*damage1) += atkmax; - if (damage2) - (*damage2) += atkmax_; - } - - if (sd) - { - //rodatazone says the range is 0~arrow_atk-1 for non crit - if (flag&2 && sd->arrow_atk) - (*damage1) += ((flag&1)?sd->arrow_atk:rand()%sd->arrow_atk); - - //SizeFix only for players - if (!( - sd->special_state.no_sizefix || - (sc && sc->data[SC_WEAPONPERFECTION].timer!=-1) || - (pc_isriding(sd) && (sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR) && t_size==1) || - (flag&8) - )) - { - (*damage1) = (*damage1)*(sd->right_weapon.atkmods[t_size])/100; - if (damage2) - (*damage2) = (*damage2)*(sd->left_weapon.atkmods[t_size])/100; - } - } - - //Finally, add baseatk - (*damage1) += baseatk; - if (damage2) - (*damage2) += baseatk_; - return; -} - -/*========================================== - * Consumes ammo for the given skill. - *------------------------------------------ - */ -void battle_consume_ammo(TBL_PC*sd, int skill, int lv) -{ - int qty=1; - if (!battle_config.arrow_decrement) - return; - - if (skill) - { - qty = skill_get_ammo_qty(skill, lv); - if (!qty) { //Generic skill that consumes ammo? - qty = skill_get_num(skill, lv); - if (qty < 0) qty *= -1; - else - if (qty == 0) qty = 1; - } - } - if(sd->equip_index[10]>=0) //Qty check should have been done in skill_check_condition - pc_delitem(sd,sd->equip_index[10],qty,0); -} - -//For quick div adjustment. -#define damage_div_fix(dmg, div) { if (div > 1) (dmg)*=div; else if (div < 0) (div)*=-1; } -/*========================================== - * battle_calc_weapon_attack (by Skotlex) - *------------------------------------------ - */ -static struct Damage battle_calc_weapon_attack( - struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag) -{ - struct map_session_data *sd=NULL, *tsd=NULL; - struct mob_data *md=NULL, *tmd=NULL; - struct homun_data *hd=NULL, *thd=NULL; //[blackhole89] - struct pet_data *pd=NULL;//, *tpd=NULL; (Noone can target pets) - struct Damage wd; - short skill=0; - unsigned short skillratio = 100; //Skill dmg modifiers. - - short i; - short t_mode = status_get_mode(target), t_size = status_get_size(target); - short t_race=0, t_ele=0, s_race=0; //Set to 0 because the compiler does not notices they are NOT gonna be used uninitialized - short s_ele, s_ele_; - short def1, def2; - struct status_change *sc = status_get_sc(src); - struct status_change *tsc = status_get_sc(target); - struct { - unsigned hit : 1; //the attack Hit? (not a miss) - unsigned cri : 1; //Critical hit - unsigned idef : 1; //Ignore defense - unsigned idef2 : 1; //Ignore defense (left weapon) - unsigned pdef : 2; //Pierces defense (Investigate/Ice Pick) - unsigned pdef2 : 2; //1: Use def+def2/50, 2: Use def+def2/100 - unsigned infdef : 1; //Infinite defense (plants) - unsigned arrow : 1; //Attack is arrow-based - unsigned rh : 1; //Attack considers right hand (wd.damage) - unsigned lh : 1; //Attack considers left hand (wd.damage2) - unsigned weapon : 1; //It's a weapon attack (consider VVS, and all that) - unsigned cardfix : 1; - } flag; - - memset(&wd,0,sizeof(wd)); - memset(&flag,0,sizeof(flag)); - - if(src==NULL || target==NULL) - { - nullpo_info(NLP_MARK); - return wd; - } - //Initial flag - flag.rh=1; - flag.weapon=1; - flag.cardfix=1; - flag.infdef=(t_mode&MD_PLANT?1:0); - - //Initial Values - wd.type=0; //Normal attack - wd.div_=skill_num?skill_get_num(skill_num,skill_lv):1; - wd.amotion=(skill_num && skill_get_inf(skill_num)&INF_GROUND_SKILL)?0:status_get_amotion(src); //Amotion should be 0 for ground skills. - if(skill_num == KN_AUTOCOUNTER) - wd.amotion >>= 1; - wd.dmotion=status_get_dmotion(target); - wd.blewcount=skill_get_blewcount(skill_num,skill_lv); - wd.flag=BF_SHORT|BF_WEAPON|BF_NORMAL; //Initial Flag - wd.dmg_lv=ATK_DEF; //This assumption simplifies the assignation later - - if (sc && !sc->count) - sc = NULL; //Skip checking as there are no status changes active. - if (tsc && !tsc->count) - tsc = NULL; //Skip checking as there are no status changes active. - - switch (src->type) - { - case BL_PC: - sd=(struct map_session_data *)src; - break; - case BL_MOB: - md=(struct mob_data *)src; - break; - case BL_PET: - pd=(struct pet_data *)src; - break; - case BL_HOMUNCULUS: - hd=(struct homun_data *)src; //[blackhole89] - break; - } - switch (target->type) - { - case BL_PC: - tsd=(struct map_session_data *)target; - if (pd) { //Pets can't target players - memset(&wd,0,sizeof(wd)); - return wd; - } - break; - case BL_MOB: - tmd=(struct mob_data *)target; - break; - case BL_PET://Cannot target pets - memset(&wd,0,sizeof(wd)); - return wd; - case BL_HOMUNCULUS: - thd=(struct homun_data *)target; //[blackhole89] - break; - } - - if(sd) { - if (sd->skillblown[0].id != 0) - { //Apply the bonus blewcount. [Skotlex] - for (i = 0; i < 5 && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++); - if (i < 5 && sd->skillblown[i].id == skill_num) - wd.blewcount += sd->skillblown[i].val; - } - } - //Set miscellaneous data that needs be filled regardless of hit/miss - if( - (sd && sd->state.arrow_atk) || - (!sd && ((skill_num && skill_get_ammotype(skill_num)) || status_get_range(src)>3)) - ) { - wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; - flag.arrow = 1; - } - - if(skill_num){ - wd.flag=(wd.flag&~BF_SKILLMASK)|BF_SKILL; - switch(skill_num) - { - case MO_FINGEROFFENSIVE: - if(sd) { - if (battle_config.finger_offensive_type) - wd.div_ = 1; - else - wd.div_ = sd->spiritball_old; - } - wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; - break; - - case CR_SHIELDBOOMERANG: - case PA_SHIELDCHAIN: - flag.weapon = 0; - case AS_GRIMTOOTH: - case KN_SPEARBOOMERANG: - case NPC_RANGEATTACK: - case LK_SPIRALPIERCE: - case ASC_BREAKER: - case AM_ACIDTERROR: - case ITM_TOMAHAWK: //Tomahawk is a ranged attack! [Skotlex] - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; - break; - - case KN_PIERCE: - wd.div_= (wd.div_>0?t_size+1:-(t_size+1)); - break; - - case TF_DOUBLE: //For NPC used skill. - wd.type = 0x08; - break; - - case KN_SPEARSTAB: - case KN_BOWLINGBASH: - case MO_BALKYOUNG: - case TK_TURNKICK: - wd.blewcount=0; - break; - - case CR_SHIELDCHARGE: -// flag.weapon = 0; - case NPC_PIERCINGATT: - wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT; - break; - - case KN_AUTOCOUNTER: - wd.flag=(wd.flag&~BF_SKILLMASK)|BF_NORMAL; - break; - } - } - - if (skill_num && battle_config.skillrange_by_distance && - (src->type&battle_config.skillrange_by_distance) - ) { //Skill range based on distance between src/target [Skotlex] - if (check_distance_bl(src, target, 3)) - wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT; - else - wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; - } - - if(is_boss(target)) //Bosses can't be knocked-back - wd.blewcount = 0; - -/* Apparently counter attack no longer causes you to be critical'ed by mobs. [Skotlex] - //Check for counter - if(!skill_num) - { - if(tsc && tsc->data[SC_AUTOCOUNTER].timer != -1) - //If it got here and you had autocounter active, then the direction/range does not matches: critical - flag.cri = 1; - } //End counter-check -*/ - if (!skill_num && (tsd || thd || battle_config.enemy_perfect_flee)) - { //Check for Lucky Dodge - short flee2 = status_get_flee2(target); - if (rand()%1000 < flee2) - { - wd.type=0x0b; - wd.dmg_lv=ATK_LUCKY; - if (wd.div_ < 0) wd.div_*=-1; - return wd; - } - } - - //Initialize variables that will be used afterwards - t_race = status_get_race(target); - t_ele = status_get_elem_type(target); - - s_race = status_get_race(src); - s_ele = s_ele_ = skill_get_pl(skill_num); - if (!skill_num || s_ele == -1) { //Take weapon's element - s_ele = status_get_attack_element(src); - s_ele_ = status_get_attack_element2(src); - if (flag.arrow && sd && sd->arrow_ele) - s_ele = sd->arrow_ele; - } else if (s_ele == -2) { //Use enchantment's element - s_ele = s_ele_ = status_get_attack_sc_element(src); - } - - if (sd) - { //Set whether damage1 or damage2 (or both) will be used - if(sd->weapontype1 == 0 && sd->weapontype2 > 0) - { - flag.rh=0; - flag.lh=1; - } - if(sd->status.weapon > MAX_WEAPON_TYPE) - flag.rh = flag.lh = 1; - } - - //Check for critical - if(!flag.cri && - (sd || hd || battle_config.enemy_critical_rate) && - (!skill_num || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING || skill_num == NJ_KIRIKAGE)) - { - short cri = status_get_critical(src); - if (sd) - { - cri+= sd->critaddrace[t_race]; - if(flag.arrow) - cri += sd->arrow_cri; - if(sd->status.weapon == 16) - cri <<=1; - } - //The official equation is *2, but that only applies when sd's do critical. - //Therefore, we use the old value 3 on cases when an sd gets attacked by a mob - cri -= status_get_luk(target) * (md&&tsd?3:2); - - if(tsc) - { - if (tsc->data[SC_SLEEP].timer!=-1 ) - cri <<=1; - if(tsc->data[SC_JOINTBEAT].timer != -1 && - tsc->data[SC_JOINTBEAT].val2 == 6) // Always take crits with Neck broken by Joint Beat [DracoRPG] - flag.cri=1; - } - switch (skill_num) - { - case KN_AUTOCOUNTER: - if(battle_config.auto_counter_type && - (battle_config.auto_counter_type&src->type)) - flag.cri = 1; - else - cri <<= 1; - break; - case SN_SHARPSHOOTING: - cri += 200; - break; - case NJ_KIRIKAGE: - cri += 250 + 50*skill_lv; - break; - } - if(tsd && tsd->critical_def) - cri = cri*(100-tsd->critical_def)/100; - if (rand()%1000 < cri) - flag.cri= 1; - } - if (flag.cri) - { - wd.type = 0x0a; - flag.idef = flag.idef2 = flag.hit = 1; - } else { //Check for Perfect Hit - if(sd && sd->perfect_hit > 0 && rand()%100 < sd->perfect_hit) - flag.hit = 1; - if (sc && sc->data[SC_FUSION].timer != -1) { - flag.hit = 1; //SG_FUSION always hit [Komurka] - flag.idef = flag.idef2 = 1; //def ignore [Komurka] - } - if (skill_num && !flag.hit) - switch(skill_num) - { - case AS_SPLASHER: //Reports say it always hits? - if (wflag) //Only if you were the one exploding. - break; - case NPC_GUIDEDATTACK: - case RG_BACKSTAP: - case HT_FREEZINGTRAP: - case AM_ACIDTERROR: - case MO_INVESTIGATE: - case MO_EXTREMITYFIST: - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - case PA_SACRIFICE: - case TK_COUNTER: - case SG_SUN_WARM: - case SG_MOON_WARM: - case SG_STAR_WARM: - case NPC_BLOODDRAIN: - flag.hit = 1; - break; - case CR_SHIELDBOOMERANG: - if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_CRUSADER) - flag.hit = 1; - break; - } - if ((tsc && !flag.hit) && - (tsc->data[SC_SLEEP].timer!=-1 || - tsc->data[SC_STUN].timer!=-1 || - tsc->data[SC_FREEZE].timer!=-1 || - (tsc->data[SC_STONE].timer!=-1 && tsc->data[SC_STONE].val2==0)) - ) - flag.hit = 1; - } - - if (!flag.hit) - { //Hit/Flee calculation - short - flee = status_get_flee(target), - hitrate=80; //Default hitrate - - if(battle_config.agi_penalty_type) - { - unsigned char target_count; //256 max targets should be a sane max - target_count = unit_counttargeted(target,battle_config.agi_penalty_count_lv); - if(target_count >= battle_config.agi_penalty_count) - { - if (battle_config.agi_penalty_type == 1) - flee = (flee * (100 - (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num))/100; - else //asume type 2: absolute reduction - flee -= (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num; - if(flee < 1) flee = 1; - } - } - - hitrate+= status_get_hit(src) - flee; - - if(wd.flag&BF_LONG && ( - (sc && sc->data[SC_FOGWALL].timer!=-1) || - (tsc && tsc->data[SC_FOGWALL].timer!=-1))) - hitrate-=50; - - if(sd && flag.arrow) - hitrate += sd->arrow_hit; - if(skill_num) - switch(skill_num) - { //Hit skill modifiers - case SM_BASH: - hitrate += 5*skill_lv; - break; - case SM_MAGNUM: - hitrate += 10*skill_lv; - break; - case KN_AUTOCOUNTER: - hitrate += 20; - break; - case KN_PIERCE: - hitrate += hitrate*(5*skill_lv)/100; - break; - case PA_SHIELDCHAIN: - hitrate += 20; - break; - case AS_SONICBLOW: - if(sd && pc_checkskill(sd,AS_SONICACCEL)>0) - hitrate += 50; - break; - } - - // Weaponry Research hidden bonus - if (sd && (skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0) - hitrate += hitrate*(2*skill)/100; - - if (hitrate > battle_config.max_hitrate) - hitrate = battle_config.max_hitrate; - else if (hitrate < battle_config.min_hitrate) - hitrate = battle_config.min_hitrate; - - if(rand()%100 >= hitrate) - wd.dmg_lv = ATK_FLEE; - else - flag.hit =1; - } //End hit/miss calculation - - if(tsd && tsd->special_state.no_weapon_damage) { - if (wd.div_ < 0) wd.div_*=-1; - return wd; - } - - if (flag.hit && !flag.infdef) //No need to do the math for plants - { //Hitting attack - -//Assuming that 99% of the cases we will not need to check for the flag.rh... we don't. -//ATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc -#define ATK_RATE( a ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(a)/100; } -#define ATK_RATE2( a , b ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(b)/100; } -//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage -#define ATK_ADDRATE( a ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(a)/100; } -#define ATK_ADDRATE2( a , b ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(b)/100; } -//Adds an absolute value to damage. 100 = +100 damage -#define ATK_ADD( a ) { wd.damage+= a; if (flag.lh) wd.damage2+= a; } -#define ATK_ADD2( a , b ) { wd.damage+= a; if (flag.lh) wd.damage2+= b; } - - def1 = status_get_def(target); - def2 = status_get_def2(target); - - switch (skill_num) - { //Calc base damage according to skill - case PA_SACRIFICE: - { - int hp_dmg = status_get_max_hp(src)* 9/100; - battle_damage(src, src, hp_dmg, 0, 1); //Damage to self is always 9% - clif_damage(src,src, gettick(), 0, 0, hp_dmg, 0 , 0, 0); - - wd.damage = hp_dmg; - wd.damage2 = 0; - - if (sc && sc->data[SC_SACRIFICE].timer != -1) - { - if (--sc->data[SC_SACRIFICE].val2 <= 0) - status_change_end(src, SC_SACRIFICE,-1); - } - break; - } - case LK_SPIRALPIERCE: - if (sd) { - short index = sd->equip_index[9]; - - if (index >= 0 && - sd->inventory_data[index] && - sd->inventory_data[index]->type == 4) - wd.damage = sd->inventory_data[index]->weight*8/100; //80% of weight - - ATK_ADDRATE(50*skill_lv); //Skill modifier applies to weight only. - index = status_get_str(src)/10; - index = index*index; - ATK_ADD(index); //Add str bonus. - - switch (t_size) { //Size-fix. Is this modified by weapon perfection? - case 0: //Small: 125% - ATK_RATE(125); - break; - //case 1: //Medium: 100% - case 2: //Large: 75% - ATK_RATE(75); - break; - } - break; - } - case CR_SHIELDBOOMERANG: - case PA_SHIELDCHAIN: - if (sd) { - short index = sd->equip_index[8]; - - wd.damage = status_get_batk(src); - if (flag.lh) wd.damage2 = wd.damage; - - if (index >= 0 && - sd->inventory_data[index] && - sd->inventory_data[index]->type == 5) - ATK_ADD(sd->inventory_data[index]->weight/10); - break; - } - default: - { - battle_calc_base_damage(src, target, &wd.damage, flag.lh?&wd.damage2:NULL, - (flag.cri?1:0)|(flag.arrow?2:0)|(skill_num == HW_MAGICCRASHER?4:0)|(skill_num == MO_EXTREMITYFIST?8:0)); - //Add any bonuses that modify the base baseatk+watk (pre-skills) - if(sd) - { - if (sd->status.weapon < MAX_WEAPON_TYPE && (sd->atk_rate != 100 || sd->weapon_atk_rate[sd->status.weapon] != 0)) - ATK_RATE(sd->atk_rate + sd->weapon_atk_rate[sd->status.weapon]); - - if(flag.cri && sd->crit_atk_rate) - ATK_ADDRATE(sd->crit_atk_rate); - - if(sd->status.party_id && (skill=pc_checkskill(sd,TK_POWER)) > 0){ - i = party_foreachsamemap(party_sub_count, sd, 0); - ATK_ADDRATE(2*skill*i); - } - } - break; - } //End default case - } //End switch(skill_num) - - //Skill damage modifiers that stack linearly - if(sc && skill_num != PA_SACRIFICE) - { - if(sc->data[SC_OVERTHRUST].timer != -1) - skillratio += 5*sc->data[SC_OVERTHRUST].val1; - if(sc->data[SC_MAXOVERTHRUST].timer != -1) - skillratio += 20*sc->data[SC_MAXOVERTHRUST].val1; - if(sc->data[SC_BERSERK].timer != -1) - skillratio += 100; - } - if (!skill_num) - { - // Random chance to deal multiplied damage - Consider it as part of skill-based-damage - if(sd && - sd->random_attack_increase_add > 0 && - sd->random_attack_increase_per && - rand()%100 < sd->random_attack_increase_per - ) - skillratio += sd->random_attack_increase_add; - - ATK_RATE(skillratio); - } else { //Skills - switch( skill_num ) - { - case SM_BASH: - skillratio += 30*skill_lv; - break; - case SM_MAGNUM: - skillratio += 20*skill_lv; - break; - case MC_MAMMONITE: - skillratio += 50*skill_lv; - break; - case HT_POWER: //FIXME: How exactly is the STR based damage supposed to be done? [Skotlex] - skillratio += 5*status_get_str(src); - break; - case AC_DOUBLE: - skillratio += 10*(skill_lv-1); - break; - case AC_SHOWER: - skillratio += 5*skill_lv-25; - break; - case AC_CHARGEARROW: - skillratio += 50; - break; - case HT_FREEZINGTRAP: - skillratio += -50+10*skill_lv; - break; - case KN_PIERCE: - skillratio += 10*skill_lv; - break; - case KN_SPEARSTAB: - skillratio += 15*skill_lv; - break; - case KN_SPEARBOOMERANG: - skillratio += 50*skill_lv; - break; - case KN_BRANDISHSPEAR: - { - int ratio = 100+20*skill_lv; - skillratio += ratio-100; - if(skill_lv>3 && wflag==1) skillratio += ratio/2; - if(skill_lv>6 && wflag==1) skillratio += ratio/4; - if(skill_lv>9 && wflag==1) skillratio += ratio/8; - if(skill_lv>6 && wflag==2) skillratio += ratio/2; - if(skill_lv>9 && wflag==2) skillratio += ratio/4; - if(skill_lv>9 && wflag==3) skillratio += ratio/2; - break; - } - case KN_BOWLINGBASH: - skillratio+= 40*skill_lv; - break; - case KN_AUTOCOUNTER: - case LK_SPIRALPIERCE: - case NPC_CRITICALSLASH: - flag.idef= flag.idef2= 1; - break; - case AS_GRIMTOOTH: - skillratio += 20*skill_lv; - break; - case AS_POISONREACT: - skillratio += 30*skill_lv; - break; - case AS_SONICBLOW: - skillratio += -50+5*skill_lv; - break; - case TF_SPRINKLESAND: - skillratio += 30; - break; - case MC_CARTREVOLUTION: - skillratio += 50; - if(sd && sd->cart_max_weight > 0 && sd->cart_weight > 0) - skillratio += 100*sd->cart_weight/sd->cart_max_weight; // +1% every 1% weight - else if (!sd) - skillratio += 150; //Max damage for non players. - break; - case NPC_RANDOMATTACK: - skillratio += rand()%150-50; - break; - case NPC_WATERATTACK: - case NPC_GROUNDATTACK: - case NPC_FIREATTACK: - case NPC_WINDATTACK: - case NPC_POISONATTACK: - case NPC_HOLYATTACK: - case NPC_DARKNESSATTACK: - case NPC_UNDEADATTACK: - case NPC_TELEKINESISATTACK: - skillratio += 25*skill_lv; - break; - case RG_BACKSTAP: - if(sd && sd->status.weapon == W_BOW && battle_config.backstab_bow_penalty) - skillratio += (200+40*skill_lv)/2; - else - skillratio += 200+40*skill_lv; - break; - case RG_RAID: - skillratio += 40*skill_lv; - break; - case RG_INTIMIDATE: - skillratio += 30*skill_lv; - break; - case CR_SHIELDCHARGE: - skillratio += 20*skill_lv; - break; - case CR_SHIELDBOOMERANG: - skillratio += 30*skill_lv; - break; - case NPC_DARKCROSS: - case CR_HOLYCROSS: - skillratio += 35*skill_lv; - break; - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - flag.cardfix = 0; - break; - case AM_DEMONSTRATION: - skillratio += 20*skill_lv; - flag.cardfix = 0; - break; - case AM_ACIDTERROR: - skillratio += 40*skill_lv; - flag.cardfix = 0; - break; - case MO_FINGEROFFENSIVE: - skillratio+= 50 * skill_lv; - break; - case MO_INVESTIGATE: - skillratio += 75*skill_lv; - flag.pdef = flag.pdef2 = 2; - break; - case MO_EXTREMITYFIST: - if (sd) - { //Overflow check. [Skotlex] - unsigned int ratio = skillratio + 100*(8 + ((sd->status.sp)/10)); - //You'd need something like 6K SP to reach this max, so should be fine for most purposes. - if (ratio > 60000) ratio = 60000; //We leave some room here in case skillratio gets further increased. - skillratio = (unsigned short)ratio; - sd->status.sp = 0; - clif_updatestatus(sd,SP_SP); - } - flag.idef= flag.idef2= 1; - break; - case MO_TRIPLEATTACK: - skillratio += 20*skill_lv; - break; - case MO_CHAINCOMBO: - skillratio += 50+50*skill_lv; - break; - case MO_COMBOFINISH: - skillratio += 140+60*skill_lv; - break; - case BA_MUSICALSTRIKE: - skillratio += 40*skill_lv-40; - break; - case DC_THROWARROW: - skillratio += 50*skill_lv; - break; - case CH_TIGERFIST: - skillratio += 100*skill_lv-60; - break; - case CH_CHAINCRUSH: - skillratio += 300+100*skill_lv; - break; - case CH_PALMSTRIKE: - skillratio += 100+100*skill_lv; - break; - case LK_HEADCRUSH: - skillratio += 40*skill_lv; - break; - case LK_JOINTBEAT: - skillratio += 10*skill_lv-50; - break; - case ASC_METEORASSAULT: - skillratio += 40*skill_lv-60; - flag.cardfix = 0; - break; - case SN_SHARPSHOOTING: - skillratio += 50*skill_lv; - break; - case CG_ARROWVULCAN: - skillratio += 100+100*skill_lv; - break; - case AS_SPLASHER: - skillratio += 400+50*skill_lv; - if (sd) - skillratio += 20*pc_checkskill(sd,AS_POISONREACT); - if(wflag>1) //FIXME: Splash damage... is this the correct method? [Skotlex] - skillratio /= wflag; - flag.cardfix = 0; - break; - case ASC_BREAKER: - skillratio += 100*skill_lv-100; - flag.cardfix = 0; - break; - case PA_SACRIFICE: - //40% less effective on siege maps. [Skotlex] - skillratio += 10*skill_lv-10; - flag.idef = flag.idef2 = 1; - break; - case PA_SHIELDCHAIN: - skillratio += 30*skill_lv; - break; - case WS_CARTTERMINATION: - if(sd && sd->cart_weight > 0) - skillratio += sd->cart_weight / (10 * (16 - skill_lv)) - 100; - else if (!sd) - skillratio += battle_config.max_cart_weight / (10 * (16 - skill_lv)); - flag.cardfix = 0; - break; - case TK_DOWNKICK: - skillratio += 60 + 20*skill_lv; - break; - case TK_STORMKICK: - skillratio += 60 + 20*skill_lv; - break; - case TK_TURNKICK: - skillratio += 90 + 30*skill_lv; - break; - case TK_COUNTER: - skillratio += 90 + 30*skill_lv; - break; - case TK_JUMPKICK: - skillratio += -70 + 10*skill_lv; - if (sc && sc->data[SC_COMBO].timer != -1 && sc->data[SC_COMBO].val1 == skill_num) - skillratio += 10*status_get_lv(src)/3; - break; - case GS_BULLSEYE: - skillratio += 400; - break; - case GS_TRACKING: - skillratio += 60*skill_lv; - if (skill_lv == 2) skillratio += 20; - if (skill_lv == 3) skillratio += 80; - if (skill_lv >= 4) skillratio += 60*(skill_lv-3); - if (skill_lv == 10) skillratio += 80; - break; - case GS_PIERCINGSHOT: - skillratio += 20*skill_lv; - break; - case GS_RAPIDSHOWER: - skillratio += 10*skill_lv; - break; - case GS_DESPERADO: - skillratio += 50*skill_lv - 50; - break; - case GS_DUST: - skillratio += 50*skill_lv; - break; - case GS_FULLBUSTER: - skillratio += 200 + 100*skill_lv; - break; - case GS_SPREADATTACK: - skillratio += 20*skill_lv-20; - break; - case NJ_HUUMA: - skillratio += 50 + 150*skill_lv; - break; - case NJ_TATAMIGAESHI: - skillratio += 10*skill_lv; - break; - case NJ_KASUMIKIRI: - skillratio += 10*skill_lv; - break; - case NJ_KIRIKAGE: - skillratio += 100*skill_lv-100; - break; - case KN_CHARGEATK: - skillratio += wflag*15; //FIXME: How much is the actual bonus? [Skotlex] - break; - case HT_PHANTASMIC: - skillratio += 50; - break; - case MO_BALKYOUNG: - skillratio += 200; - break; - } - - ATK_RATE(skillratio); - - //Constant/misc additions from skills - switch (skill_num) { - case MO_EXTREMITYFIST: - ATK_ADD(250 + 150*skill_lv); - break; - case TK_DOWNKICK: - case TK_STORMKICK: - case TK_TURNKICK: - case TK_COUNTER: - case TK_JUMPKICK: - //TK_RUN kick damage bonus. - if(sd && sd->weapontype1 == W_FIST && sd->weapontype2 == W_FIST) - ATK_ADD(10*pc_checkskill(sd, TK_RUN)); - break; - case GS_MAGICALBULLET: - { - int matk1=status_get_matk1(src),matk2=status_get_matk2(src); - if(matk1>matk2) - { - ATK_ADD(matk2+rand()%(matk1-matk2+1)); - } else ATK_ADD(matk2); - break; - } - } - } - //Div fix. - damage_div_fix(wd.damage, wd.div_); - //Here comes a second pass for skills that stack to the previously defined % damage. [Skotlex] - skillratio = 100; - //Skill damage modifiers that affect linearly stacked damage. - if (sc && skill_num != PA_SACRIFICE) { - if(sc->data[SC_TRUESIGHT].timer != -1) - skillratio += 2*sc->data[SC_TRUESIGHT].val1; - // It is still not quite decided whether it works on bosses or not... - if(sc->data[SC_EDP].timer != -1 /*&& !(t_mode&MD_BOSS)*/ && skill_num != ASC_BREAKER && skill_num != ASC_METEORASSAULT) - skillratio += 50 +50*sc->data[SC_EDP].val1; - } - switch (skill_num) { - case AS_SONICBLOW: - if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_ASSASIN) - skillratio += (map_flag_gvg(src->m))?25:100; //+25% dmg on woe/+100% dmg on nonwoe - if(sd && pc_checkskill(sd,AS_SONICACCEL)>0) - skillratio += 10; - break; - case CR_SHIELDBOOMERANG: - if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_CRUSADER) - skillratio += 100; - break; - } - if (sd && sd->skillatk[0].id != 0) - { - for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++); - if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num) - //May seem wrong as it also applies on top of other modifiers, but adding, say, 10% - //to 800% dmg -> 810% would make the bonus a little lame. [Skotlex] - skillratio += sd->skillatk[i].val; - } - if (skillratio != 100) - ATK_RATE(skillratio); - - if(sd) - { - if (skill_num != PA_SACRIFICE && skill_num != MO_INVESTIGATE - && skill_num != CR_GRANDCROSS && skill_num != NPC_GRANDDARKNESS - && !flag.cri) - { //Elemental/Racial adjustments - if(sd->right_weapon.def_ratio_atk_ele & (1<right_weapon.def_ratio_atk_race & (1<right_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11) - ) - flag.pdef = 1; - - if(sd->left_weapon.def_ratio_atk_ele & (1<left_weapon.def_ratio_atk_race & (1<left_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11) - ) { //Pass effect onto right hand if configured so. [Skotlex] - if (battle_config.left_cardfix_to_right && flag.rh) - flag.pdef = 1; - else - flag.pdef2 = 1; - } - } - - if (skill_num != CR_GRANDCROSS && skill_num != NPC_GRANDDARKNESS) - { //Ignore Defense? - if (!flag.idef && ( - (tmd && sd->right_weapon.ignore_def_mob & (is_boss(target)?2:1)) || - sd->right_weapon.ignore_def_ele & (1<right_weapon.ignore_def_race & (1<right_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11) - )) - flag.idef = 1; - - if (!flag.idef2 && ( - (tmd && sd->left_weapon.ignore_def_mob & (is_boss(target)?2:1)) || - sd->left_weapon.ignore_def_ele & (1<left_weapon.ignore_def_race & (1<left_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11) - )) { - if(battle_config.left_cardfix_to_right && flag.rh) //Move effect to right hand. [Skotlex] - flag.idef = 1; - else - flag.idef2 = 1; - } - } - } - - if (!flag.idef || !flag.idef2) - { //Defense reduction - short vit_def; - if(battle_config.vit_penalty_type) - { - unsigned char target_count; //256 max targets should be a sane max - target_count = unit_counttargeted(target,battle_config.vit_penalty_count_lv); - if(target_count >= battle_config.vit_penalty_count) { - if(battle_config.vit_penalty_type == 1) { - def1 = (def1 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100; - def2 = (def2 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100; - } else { //Assume type 2 - def1 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num; - def2 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num; - } - } - if(def1 < 0 || skill_num == AM_ACIDTERROR) def1 = 0; //Acid Terror ignores only armor defense. [Skotlex] - if(def2 < 1) def2 = 1; - } - //Vitality reduction from rodatazone: http://rodatazone.simgaming.net/mechanics/substats.php#def - if (tsd) //Sd vit-eq - { //[VIT*0.5] + rnd([VIT*0.3], max([VIT*0.3],[VIT^2/150]-1)) - vit_def = def2*(def2-15)/150; - vit_def = def2/2 + (vit_def>0?rand()%vit_def:0); - - if((battle_check_undead(s_race,status_get_elem_type(src)) || s_race==RC_DEMON) && - (skill=pc_checkskill(tsd,AL_DP)) >0) - vit_def += skill*(int)(3 +(tsd->status.base_level+1)*0.04); // submitted by orn - } else { //Mob-Pet vit-eq - //VIT + rnd(0,[VIT/20]^2-1) - vit_def = (def2/20)*(def2/20); - vit_def = def2 + (vit_def>0?rand()%vit_def:0); - } - - if (sd && battle_config.player_defense_type) { - vit_def += def1*battle_config.player_defense_type; - def1 = 0; - } else if (md && battle_config.monster_defense_type) { - vit_def += def1*battle_config.monster_defense_type; - def1 = 0; - } else if(pd && battle_config.pet_defense_type) { - vit_def += def1*battle_config.pet_defense_type; - def1 = 0; - } - if (def1 > 100) def1 = 100; - ATK_RATE2( - flag.idef ?100: - (flag.pdef ?flag.pdef *(def1 + vit_def): - 100-def1), - flag.idef2?100: - (flag.pdef2?flag.pdef2*(def1 + vit_def): - 100-def1) - ); - ATK_ADD2( - flag.idef ||flag.pdef ?0:-vit_def, - flag.idef2||flag.pdef2?0:-vit_def - ); - } - - //Post skill/vit reduction damage increases - if (sc && skill_num != LK_SPIRALPIERCE) - { //SC skill damages - if(sc->data[SC_AURABLADE].timer!=-1) - ATK_ADD(20*sc->data[SC_AURABLADE].val1); - } - - //Refine bonus - if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) { - if (skill_num == MO_FINGEROFFENSIVE) //Counts refine bonus multiple times - { - ATK_ADD2(wd.div_*status_get_atk2(src), wd.div_*status_get_atk_2(src)); - } else { - ATK_ADD2(status_get_atk2(src), status_get_atk_2(src)); - } - } - - //Set to min of 1 - if (flag.rh && wd.damage < 1) wd.damage = 1; - if (flag.lh && wd.damage2 < 1) wd.damage2 = 1; - - if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST - && skill_num != CR_GRANDCROSS) - { //Add mastery damage - wd.damage = battle_addmastery(sd,target,wd.damage,0); - if (flag.lh) wd.damage2 = battle_addmastery(sd,target,wd.damage2,1); - - if (pc_checkskill(sd,SG_SUN_ANGER) || pc_checkskill(sd,SG_MOON_ANGER) || pc_checkskill(sd,SG_STAR_ANGER)) - { //SG Anger bonus - ATK_ADDRATE [Komurka] - static int type[] = { SG_SUN_ANGER, SG_MOON_ANGER, SG_STAR_ANGER }; - short t_class = status_get_class(target); - if (sc && sc->data[SC_MIRACLE].timer!=-1 && (skill = pc_checkskill(sd,type[2]))) - { - skillratio = (sd->status.base_level + status_get_str(src) + status_get_dex(src)+ status_get_luk(src))/(skill<4?12-3*skill:1); - ATK_ADDRATE(skillratio); - } - else for (i = 0; i < 3; i++) - { - if (t_class == sd->hate_mob[i] && (skill = pc_checkskill(sd,type[i]))) - { - skillratio = (sd->status.base_level + (i==2?status_get_str(src):0) + status_get_dex(src)+ status_get_luk(src))/(skill<4?12-3*skill:1); - ATK_ADDRATE(skillratio); - break; - } - } - } - } - } //Here ends flag.hit section, the rest of the function applies to both hitting and missing attacks - else if(wd.div_ < 0) //Since the attack missed... - wd.div_ *= -1; - - if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS) - return wd; //Enough, rest is not needed. - - if(sd && (skill=pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0) - ATK_ADD(skill*2); - - if(skill_num==TF_POISON) - ATK_ADD(15*skill_lv); - - if(skill_num==ASC_BREAKER) //Breaker's int-based damage. - ATK_ADD(rand()%500 + 500 + skill_lv * status_get_int(src) * 5); - - if ((sd && (skill_num || !battle_config.pc_attack_attr_none)) || - (md && (skill_num || !battle_config.mob_attack_attr_none)) || - (pd && (skill_num || !battle_config.pet_attack_attr_none))) - { //Elemental attribute fix - short t_element = status_get_element(target); - if (!(!sd && tsd && battle_config.mob_ghostring_fix && t_ele==8)) - { - if (wd.damage > 0) - { - wd.damage=battle_attr_fix(src,target,wd.damage,s_ele,t_element); - if(skill_num==MC_CARTREVOLUTION) //Cart Revolution applies the element fix once more with neutral element - wd.damage = battle_attr_fix(src,target,wd.damage,0,t_element); - } - if (flag.lh && wd.damage2 > 0) - wd.damage2 = battle_attr_fix(src,target,wd.damage2,s_ele_,t_element); - } - if(sc && sc->data[SC_WATK_ELEMENT].timer != -1) - { //Descriptions indicate this means adding a percent of a normal attack in another element. [Skotlex] - int damage=0; - battle_calc_base_damage(src, target, &damage, NULL, (flag.arrow?2:0)); - damage = damage*sc->data[SC_WATK_ELEMENT].val2/100; - damage = battle_attr_fix(src,target,damage,sc->data[SC_WATK_ELEMENT].val1,t_element); - ATK_ADD(damage); - } - } - - if ((!flag.rh || wd.damage == 0) && (!flag.lh || wd.damage2 == 0)) - flag.cardfix = 0; //When the attack does no damage, avoid doing %bonuses - - if (sd) - { - if (skill_num != CR_SHIELDBOOMERANG) //Only Shield boomerang doesn't takes the Star Crumbs bonus. - ATK_ADD2(wd.div_*sd->right_weapon.star, wd.div_*sd->left_weapon.star); - if (skill_num==MO_FINGEROFFENSIVE) { //The finger offensive spheres on moment of attack do count. [Skotlex] - ATK_ADD(wd.div_*sd->spiritball_old*3); - } else { - ATK_ADD(wd.div_*sd->spiritball*3); - } - - //Card Fix, sd side - if (flag.cardfix) - { - short cardfix = 1000, cardfix_ = 1000; - short t_class = status_get_class(target); - short t_race2 = status_get_race2(target); - if(sd->state.arrow_atk) - { - cardfix=cardfix*(100+sd->right_weapon.addrace[t_race]+sd->arrow_addrace[t_race])/100; - cardfix=cardfix*(100+sd->right_weapon.addele[t_ele]+sd->arrow_addele[t_ele])/100; - cardfix=cardfix*(100+sd->right_weapon.addsize[t_size]+sd->arrow_addsize[t_size])/100; - cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100; - cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11]+sd->arrow_addrace[is_boss(target)?10:11])/100; - } else { //Melee attack - if(!battle_config.left_cardfix_to_right) - { - cardfix=cardfix*(100+sd->right_weapon.addrace[t_race])/100; - cardfix=cardfix*(100+sd->right_weapon.addele[t_ele])/100; - cardfix=cardfix*(100+sd->right_weapon.addsize[t_size])/100; - cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100; - cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11])/100; - - if (flag.lh) - { - cardfix_=cardfix_*(100+sd->left_weapon.addrace[t_race])/100; - cardfix_=cardfix_*(100+sd->left_weapon.addele[t_ele])/100; - cardfix_=cardfix_*(100+sd->left_weapon.addsize[t_size])/100; - cardfix_=cardfix_*(100+sd->left_weapon.addrace2[t_race2])/100; - cardfix_=cardfix_*(100+sd->left_weapon.addrace[is_boss(target)?10:11])/100; - } - } else { - cardfix=cardfix*(100+sd->right_weapon.addrace[t_race]+sd->left_weapon.addrace[t_race])/100; - cardfix=cardfix*(100+sd->right_weapon.addele[t_ele]+sd->left_weapon.addele[t_ele])/100; - cardfix=cardfix*(100+sd->right_weapon.addsize[t_size]+sd->left_weapon.addsize[t_size])/100; - cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2]+sd->left_weapon.addrace2[t_race2])/100; - cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?10:11]+sd->left_weapon.addrace[is_boss(target)?10:11])/100; - } - } - - for(i=0;iright_weapon.add_damage_class_count;i++) { - if(sd->right_weapon.add_damage_classid[i] == t_class) { - cardfix=cardfix*(100+sd->right_weapon.add_damage_classrate[i])/100; - break; - } - } - - if (flag.lh) - { - for(i=0;ileft_weapon.add_damage_class_count;i++) { - if(sd->left_weapon.add_damage_classid[i] == t_class) { - cardfix_=cardfix_*(100+sd->left_weapon.add_damage_classrate[i])/100; - break; - } - } - } - - if(wd.flag&BF_LONG) - cardfix=cardfix*(100+sd->long_attack_atk_rate)/100; - - if (cardfix != 1000 || cardfix_ != 1000) - ATK_RATE2(cardfix/10, cardfix_/10); //What happens if you use right-to-left and there's no right weapon, only left? - } - - if (skill_num == CR_SHIELDBOOMERANG || skill_num == PA_SHIELDCHAIN) { //Refine bonus applies after cards and elements. - short index= sd->equip_index[8]; - if (index >= 0 && - sd->inventory_data[index] && - sd->inventory_data[index]->type == 5) - ATK_ADD(10*sd->status.inventory[index].refine); - } - } //if (sd) - - //Card Fix, tsd side - Cards always apply on the target. [Skotlex] - if (tsd) { - short s_size,s_race2,s_class; - short cardfix=1000; - - s_size = status_get_size(src); - s_race2 = status_get_race2(src); - s_class = status_get_class(src); - - cardfix=cardfix*(100-tsd->subele[s_ele])/100; - cardfix=cardfix*(100-tsd->subsize[s_size])/100; - cardfix=cardfix*(100-tsd->subrace2[s_race2])/100; - cardfix=cardfix*(100-tsd->subrace[s_race])/100; - cardfix=cardfix*(100-tsd->subrace[is_boss(src)?10:11])/100; - - for(i=0;iadd_dmg_count;i++) { - if(tsd->add_dmg[i].class_ == s_class) { - cardfix=cardfix*(100+tsd->add_dmg[i].rate)/100; - break; - } - } - - if(wd.flag&BF_SHORT) - cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; - else // BF_LONG (there's no other choice) - cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; - - if (cardfix != 1000) - ATK_RATE(cardfix/10); - } - - if(flag.infdef) - { //Plants receive 1 damage when hit - if (flag.rh && (flag.hit || wd.damage>0)) - wd.damage = 1; - if (flag.lh && (flag.hit || wd.damage2>0)) - wd.damage2 = 1; - if (!(battle_config.skill_min_damage&1)) - //Do not return if you are supposed to deal greater damage to plants than 1. [Skotlex] - return wd; - } - - if(sd && !skill_num && !flag.cri) - { //Check for double attack. - if(( (skill_lv = 5*pc_checkskill(sd,TF_DOUBLE)) > 0 && sd->weapontype1 == W_DAGGER) || - sd->double_rate > 0) //Success chance is not added, the higher one is used? [Skotlex] - { - if (rand()%100 < (skill_lv>sd->double_rate?skill_lv:sd->double_rate)) - { - wd.div_=skill_get_num(TF_DOUBLE,skill_lv?skill_lv:1); - damage_div_fix(wd.damage, wd.div_); - wd.type = 0x08; - } - } else if (( (skill_lv = 5*pc_checkskill(sd,GS_CHAINACTION)) > 0 && sd->weapontype1 == W_REVOLVER) || sd->double_rate > 0) - if (rand()%100 < (skill_lv>sd->double_rate?skill_lv:sd->double_rate)) - { - wd.div_=skill_get_num(GS_CHAINACTION,skill_lv?skill_lv:1); - damage_div_fix(wd.damage, wd.div_); - wd.type = 0x08; - } - } - - if(!flag.rh || wd.damage<1) - wd.damage=0; - - if(!flag.lh || wd.damage2<1) - wd.damage2=0; - - if (sd) - { - if (!flag.rh && flag.lh) - { //Move lh damage to the rh - wd.damage = wd.damage2; - wd.damage2 = 0; - flag.rh=1; - flag.lh=0; - } else if(sd->status.weapon > MAX_WEAPON_TYPE) - { //Dual-wield - if (wd.damage > 0) - { - skill = pc_checkskill(sd,AS_RIGHT); - wd.damage = wd.damage * (50 + (skill * 10))/100; - if(wd.damage < 1) wd.damage = 1; - } - if (wd.damage2 > 0) - { - skill = pc_checkskill(sd,AS_LEFT); - wd.damage2 = wd.damage2 * (30 + (skill * 10))/100; - if(wd.damage2 < 1) wd.damage2 = 1; - } - } else if(sd->status.weapon == W_KATAR) - { //Katars - skill = pc_checkskill(sd,TF_DOUBLE); - wd.damage2 = wd.damage * (1 + (skill * 2))/100; - - if(wd.damage > 0 && wd.damage2 < 1) wd.damage2 = 1; - flag.lh = 1; - } - } - - if(wd.damage > 0 || wd.damage2 > 0) - { - if(wd.damage2<1) { - wd.damage=battle_calc_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag); - if (map_flag_gvg(target->m)) - wd.damage=battle_calc_gvg_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag); - } else if(wd.damage<1) { - wd.damage2=battle_calc_damage(src,target,wd.damage2,wd.div_,skill_num,skill_lv,wd.flag); - if (map_flag_gvg(target->m)) - wd.damage2=battle_calc_gvg_damage(src,target,wd.damage2,wd.div_,skill_num,skill_lv,wd.flag); - } - else - { - int d1=wd.damage+wd.damage2,d2=wd.damage2; - wd.damage=battle_calc_damage(src,target,d1,wd.div_,skill_num,skill_lv,wd.flag); - if (map_flag_gvg(target->m)) - wd.damage=battle_calc_gvg_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag); - wd.damage2=(d2*100/d1)*wd.damage/100; - if(wd.damage > 1 && wd.damage2 < 1) wd.damage2=1; - wd.damage-=wd.damage2; - } - } - - if(sd && sd->classchange && tmd && !(t_mode&MD_BOSS) && !tmd->guardian_data && (tmd->class_ < 1324 || tmd->class_ > 1363) - && !mob_is_clone(tmd->class_) && (rand()%10000 < sd->classchange)) - { //Classchange: - struct mob_db *mob; - int k, class_; - i = 0; - do { - do { - class_ = rand() % MAX_MOB_DB; - } while (!mobdb_checkid(class_)); - - k = rand() % 1000000; - mob = mob_db(class_); - } while ((mob->mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= k) && (i++) < 2000); - if (i< 2000) - mob_class_change(((struct mob_data *)target),class_); - } - - if (wd.damage > 0 || wd.damage2 > 0) { - if (sd && battle_config.equip_self_break_rate) - { // Self weapon breaking - int breakrate = battle_config.equip_natural_break_rate; - if (sc) { - if(sc->data[SC_OVERTHRUST].timer!=-1) - breakrate += 10; - if(sc->data[SC_MAXOVERTHRUST].timer!=-1) - breakrate += 10; - } - if (breakrate) - skill_break_equip(src, EQP_WEAPON, breakrate, BCT_SELF); - } - if (battle_config.equip_skill_break_rate) - { // Target equipment breaking - int breakrate[2] = {0,0}; // weapon = 0, armor = 1 - if (sd) { // Break rate from equipment - breakrate[0] += sd->break_weapon_rate; - breakrate[1] += sd->break_armor_rate; - } - if (sc) { - if (sc->data[SC_MELTDOWN].timer!=-1) { - breakrate[0] += 100*sc->data[SC_MELTDOWN].val1; - breakrate[1] += 70*sc->data[SC_MELTDOWN].val1; - } - } - if (breakrate[0]) - skill_break_equip(target, EQP_WEAPON, breakrate[0], BCT_ENEMY); - if (breakrate[1]) - skill_break_equip(target, EQP_ARMOR, breakrate[1], BCT_ENEMY); - } - } - - //SG_FUSION hp penalty [Komurka] - if (sc && sc->data[SC_FUSION].timer!=-1) - { - int hp= status_get_max_hp(src); - if (sd && tsd) { - hp = 8*hp/100; - if (100*sd->status.hp <= 20*sd->status.max_hp) - hp = sd->status.hp; - } else - hp = 5*hp/1000; - battle_damage(NULL, src, hp, 0, 1); - } - - return wd; -} - -/*========================================== - * battle_calc_magic_attack [DracoRPG] - *------------------------------------------ - */ -struct Damage battle_calc_magic_attack( - struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int mflag) - { - struct map_session_data *sd=NULL, *tsd=NULL; - struct mob_data *md=NULL, *tmd=NULL; - struct pet_data *pd=NULL;//, *tpd=NULL; (Noone can target pets) - struct Damage ad; - unsigned short skillratio = 100; //Skill dmg modifiers. - - short i; - short t_mode = status_get_mode(target); - short t_race, t_size, t_ele, s_race, s_size, s_ele; - struct { - unsigned imdef : 1; - unsigned infdef : 1; - unsigned elefix : 1; - unsigned cardfix : 1; - } flag; - - memset(&ad,0,sizeof(ad)); - memset(&flag,0,sizeof(flag)); - - if(src==NULL || target==NULL) - { - nullpo_info(NLP_MARK); - return ad; - } - //Initial flag - flag.elefix=1; - flag.cardfix=1; - - //Initial Values - ad.damage = 1; - ad.div_=skill_get_num(skill_num,skill_lv); - ad.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:status_get_amotion(src); //Amotion should be 0 for ground skills. - ad.dmotion=status_get_dmotion(target); - ad.blewcount = skill_get_blewcount(skill_num,skill_lv); - ad.flag=BF_MAGIC|BF_LONG|BF_SKILL; - ad.dmg_lv=ATK_DEF; - - switch (src->type) - { - case BL_PC: - sd=(struct map_session_data *)src; - break; - case BL_MOB: - md=(struct mob_data *)src; - break; - case BL_PET: - pd=(struct pet_data *)src; - break; - } - switch (target->type) - { - case BL_PC: - tsd=(struct map_session_data *)target; - if (pd) { //Pets can't target players - memset(&ad,0,sizeof(ad)); - return ad; - } - break; - case BL_MOB: - tmd=(struct mob_data *)target; - break; - case BL_PET://Cannot target pets - memset(&ad,0,sizeof(ad)); - return ad; - } - - //Initialize variables that will be used afterwards - t_race = status_get_race(target); - t_size = status_get_size(target); - t_ele = status_get_elem_type(target); - - s_race = status_get_race(src); - s_size = status_get_size(src); - s_ele = skill_get_pl(skill_num); - - if (s_ele == -1) // pl=-1 : the skill takes the weapon's element - s_ele = status_get_attack_element(src); - else if (s_ele == -2) //Use status element - s_ele = status_get_attack_sc_element(src); - - //Set miscellaneous data that needs be filled - if(sd) { - sd->state.arrow_atk = 0; - if (sd->skillblown[0].id != 0) - { //Apply the bonus blewcount. [Skotlex] - for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++); - if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num) - ad.blewcount += sd->skillblown[i].val; - } - } - - if (battle_config.skillrange_by_distance && - (src->type&battle_config.skillrange_by_distance) - ) { //Skill range based on distance between src/target [Skotlex] - if (check_distance_bl(src, target, 3)) - ad.flag=(ad.flag&~BF_RANGEMASK)|BF_SHORT; - else - ad.flag=(ad.flag&~BF_RANGEMASK)|BF_LONG; - } - - flag.infdef=(t_mode&MD_PLANT?1:0); - - switch(skill_num) - { - case MG_FIREWALL: - if(mflag) { //mflag has a value when it was checked against an undead in skill.c [Skotlex] - ad.blewcount = 0; //No knockback - ad.dmotion = 0; //No flinch animation. - } else - ad.blewcount |= 0x10000; - break; - case WZ_STORMGUST: //Should knockback randomly. - ad.blewcount|=0x40000; - break; - case PR_SANCTUARY: - ad.blewcount|=0x10000; - case AL_HEAL: - case PR_BENEDICTIO: - case WZ_FIREPILLAR: - flag.imdef = 1; - break; - case HW_GRAVITATION: - flag.imdef = 1; - flag.elefix = 0; - break; - case PR_ASPERSIO: - flag.imdef = 1; - case PF_SOULBURN: //Does not ignores mdef - flag.elefix = 0; - flag.cardfix = 0; - break; - case PR_TURNUNDEAD: - flag.imdef = 1; - flag.cardfix = 0; - break; - case NPC_GRANDDARKNESS: - case CR_GRANDCROSS: - flag.cardfix = 0; - break; - } - - if(is_boss(target)) //Bosses can't be knocked-back - ad.blewcount = 0; - - if (!flag.infdef) //No need to do the math for plants - { - -//MATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc -#define MATK_RATE( a ) { ad.damage= ad.damage*(a)/100; } -//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage -#define MATK_ADDRATE( a ) { ad.damage+= ad.damage*(a)/100; } -//Adds an absolute value to damage. 100 = +100 damage -#define MATK_ADD( a ) { ad.damage+= a; } - - switch (skill_num) - { //Calc base damage according to skill - case AL_HEAL: - case PR_BENEDICTIO: - ad.damage = skill_calc_heal(src,skill_lv)/2; - if (sd) - ad.damage += ad.damage * pc_checkskill(sd, HP_MEDITATIO) * 2 / 100; - break; - case PR_ASPERSIO: - ad.damage = 40; - break; - case PR_SANCTUARY: - ad.damage = (skill_lv>6)?388:skill_lv*50; - break; - case ALL_RESURRECTION: - case PR_TURNUNDEAD: - if(battle_check_undead(t_race,t_ele)){ - int hp, mhp, thres; - hp = status_get_hp(target); - mhp = status_get_max_hp(target); - thres = (skill_lv * 20) + status_get_luk(src) + status_get_int(src) + status_get_lv(src) + ((200 - hp * 200 / mhp)); - if(thres > 700) thres = 700; - if(rand()%1000 < thres && !(t_mode&MD_BOSS)) - ad.damage = hp; - else - ad.damage = status_get_lv(src) + status_get_int(src) + skill_lv * 10; - } - break; - case PF_SOULBURN: - if (!tsd) { - memset(&ad,0,sizeof(ad)); - return ad; - } else - ad.damage = tsd->status.sp * 2; - break; - case HW_GRAVITATION: - ad.damage = 200+200*skill_lv; - break; - default: - { - unsigned short matkmin,matkmax; - - matkmin = status_get_matk2(src); - matkmax = status_get_matk1(src); - - MATK_ADD(matkmin+(matkmax>matkmin?rand()%(matkmax-matkmin+1):0)); - - if(skill_num == MG_NAPALMBEAT || skill_num == HW_NAPALMVULCAN){ // Divide MATK in case of multiple targets skill - if(mflag>0) - ad.damage/= mflag; - else if(battle_config.error_log) - ShowError("0 enemies targeted by Napalm Beat/Vulcan, divide per 0 avoided!\n"); - } - - switch(skill_num){ - case MG_NAPALMBEAT: - skillratio += skill_lv*10-30; - break; - case MG_SOULSTRIKE: - if (battle_check_undead(t_race,t_ele)) - skillratio += 5*skill_lv; - break; - case MG_FIREBALL: - if(mflag>2) - ad.damage = 0; - else { - int drate[]={100,90,70}; - MATK_RATE(drate[mflag]); - skillratio += 70+10*skill_lv; - } - break; - case MG_FIREWALL: - skillratio -= 50; - break; - case MG_THUNDERSTORM: - skillratio -= 20; - break; - case MG_FROSTDIVER: - skillratio += 10*skill_lv; - break; - case AL_HOLYLIGHT: - skillratio += 25; - if (sd && sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_PRIEST) - skillratio *= 5; //Does 5x damage include bonuses from other skills? - break; - case AL_RUWACH: - skillratio += 45; - break; - case WZ_FROSTNOVA: - skillratio += (100+skill_lv*10)*2/3-100; - break; - case WZ_FIREPILLAR: - if (skill_lv > 10) - skillratio += 100; - else - skillratio -= 80; - break; - case WZ_SIGHTRASHER: - skillratio += 20*skill_lv; - break; - case WZ_VERMILION: - skillratio += 20*skill_lv-20; - break; - case WZ_WATERBALL: - skillratio += 30*skill_lv; - break; - case WZ_STORMGUST: - skillratio += 40*skill_lv; - break; - case HW_NAPALMVULCAN: - skillratio += 10*skill_lv-30; - break; - case SL_STIN: - skillratio += (t_size?-99:10*skill_lv); //target size must be small (0) for full damage. - break; - case SL_STUN: - skillratio += (t_size!=2?5*skill_lv:-99); //Full damage is dealt on small/medium targets - break; - case SL_SMA: - skillratio += -60 + status_get_lv(src); //Base damage is 40% + lv% - break; - case NJ_KOUENKA: - skillratio -= 10; - break; - case NJ_BAKUENRYU: - //skillratio += 50 + 150*skill_lv; - // Possibly just add to matk? - MATK_ADD(150 + 150*skill_lv); - break; - case NJ_HYOUSYOURAKU: - //skillratio += 50*skill_lv; - // Possibly just add to matk? - MATK_ADD(100 + 50*skill_lv); - break; - case NJ_RAIGEKISAI: - //skillratio += 60 + 40*skill_lv; - // Possibly just add to matk? - MATK_ADD(200 + 40*skill_lv); - break; - case NJ_KAMAITACHI: - //skillratio += 100*skill_lv; - // Possibly just add to matk? - MATK_ADD(100 + 100*skill_lv); - break; - } - - if (sd && sd->skillatk[0].id != 0) - { - for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++) - if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num) - //If we apply skillatk[] as ATK_RATE, it will also affect other skills, - //unfortunately this way ignores a skill's constant modifiers... - skillratio += sd->skillatk[i].val; - } - - MATK_RATE(skillratio); - - //Constant/misc additions from skills - if (skill_num == WZ_FIREPILLAR) - MATK_ADD(50); - } - } - - if(sd) { - //Ignore Defense? - if (!flag.imdef && ( - sd->ignore_mdef_ele & (1<ignore_mdef_race & (1<ignore_mdef_race & (is_boss(target)?1<<10:1<<11) - )) - flag.imdef = 1; - } - - if(!flag.imdef){ - if(battle_config.magic_defense_type) - ad.damage = ad.damage - (status_get_mdef(target)*battle_config.magic_defense_type) - status_get_mdef2(target); - else - ad.damage = ad.damage * (100-status_get_mdef(target))/100 - status_get_mdef2(target); - } - - if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS) - { //Apply the physical part of the skill's damage. [Skotlex] - struct Damage wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag); - ad.damage = (wd.damage + ad.damage) * (100 + 40*skill_lv)/100; - if(src==target) - { - if (src->type == BL_PC) - ad.damage = ad.damage/2; - else - ad.damage = 0; - } - } - - if(ad.damage<1) - ad.damage=1; - - if (flag.elefix) - ad.damage=battle_attr_fix(src, target, ad.damage, s_ele, status_get_element(target)); - - if (sd && flag.cardfix) { - short t_class = status_get_class(target); - short cardfix=100; - - cardfix=cardfix*(100+sd->magic_addrace[t_race])/100; - if (flag.elefix) - cardfix=cardfix*(100+sd->magic_addele[t_ele])/100; - cardfix=cardfix*(100+sd->magic_addsize[t_size])/100; - cardfix=cardfix*(100+sd->magic_addrace[is_boss(target)?10:11])/100; - for(i=0;iadd_mdmg_count;i++) { - if(sd->add_mdmg[i].class_ == t_class) { - cardfix=cardfix*(100+sd->add_mdmg[i].rate)/100; - continue; - } - } - - MATK_RATE(cardfix); - } - - if (tsd && skill_num != HW_GRAVITATION && skill_num != PF_SOULBURN) - { //Card fixes always apply on the target side. [Skotlex] - short s_race2=status_get_race2(src); - short s_class= status_get_class(src); - short cardfix=100; - - if (flag.elefix) - cardfix=cardfix*(100-tsd->subele[s_ele])/100; - cardfix=cardfix*(100-tsd->subsize[s_size])/100; - cardfix=cardfix*(100-tsd->subrace2[s_race2])/100; - cardfix=cardfix*(100-tsd->subrace[s_race])/100; - cardfix=cardfix*(100-tsd->subrace[is_boss(src)?10:11])/100; - for(i=0;iadd_mdef_count;i++) { - if(tsd->add_mdef[i].class_ == s_class) { - cardfix=cardfix*(100-tsd->add_mdef[i].rate)/100; - continue; - } - } - //It was discovered that ranged defense also counts vs magic! [Skotlex] - if (ad.flag&BF_SHORT) - cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; - else - cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; - - cardfix=cardfix*(100-tsd->magic_def_rate)/100; - - MATK_RATE(cardfix); - } - } - - damage_div_fix(ad.damage, ad.div_); - - if (flag.infdef && ad.damage > 0) - ad.damage = 1; - - if (tsd && status_isimmune(target)) { - if (sd && battle_config.gtb_pvp_only) { // [MouseJstr] - MATK_RATE(100 - battle_config.gtb_pvp_only); - } else ad.damage = 0; - } - - ad.damage=battle_calc_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag); - if (map_flag_gvg(target->m)) - ad.damage=battle_calc_gvg_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag); - if (ad.damage == 0) //Magic attack blocked? Consider it a miss so it doesn't invokes additional effects. [Skotlex] - ad.dmg_lv = ATK_FLEE; - return ad; -} - -/*========================================== - * その他ダ??[ジ計算 - *------------------------------------------ - */ -struct Damage battle_calc_misc_attack( - struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag) -{ - int int_=status_get_int(bl); - int dex=status_get_dex(bl); - int skill,ele,race,size,cardfix,race2,t_mode; - struct map_session_data *sd=NULL,*tsd=NULL; - int damage=0,div_=1,blewcount=skill_get_blewcount(skill_num,skill_lv); - struct Damage md; - int damagefix=1; - - int aflag=BF_MISC|BF_SHORT|BF_SKILL; - - if( bl == NULL || target == NULL ){ - nullpo_info(NLP_MARK); - memset(&md,0,sizeof(md)); - return md; - } - - if(target->type == BL_PET) { - memset(&md,0,sizeof(md)); - return md; - } - - //Some initial values - md.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:status_get_amotion(bl); - md.dmotion=status_get_dmotion(target); - md.damage2=0; - md.type=0; - md.dmg_lv=ATK_DEF; - - if( bl->type == BL_PC && (sd=(struct map_session_data *)bl) ) { - sd->state.arrow_atk = 0; - if (sd->skillblown[0].id != 0) - { //Apply the bonus blewcount. [Skotlex] - int i; - for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++); - if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num) - blewcount += sd->skillblown[i].val; - } - } - - if( target->type==BL_PC ) - tsd=(struct map_session_data *)target; - - t_mode = status_get_mode(target); - ele = skill_get_pl(skill_num); - if (ele == -1) //Attack that takes weapon's element for misc attacks? Make it neutral [Skotlex] - ele = 0; - race = status_get_race(bl); - size = status_get_size(bl); - race2 = status_get_race(bl); - - switch(skill_num){ - - case HT_LANDMINE: // ランドマイン - damage=skill_lv*(dex+75)*(100+int_)/100; - break; - - case HT_BLASTMINE: // ブラストマイン - damage=skill_lv*(dex/2+50)*(100+int_)/100; - break; - - case HT_CLAYMORETRAP: // クレイモア?[トラップ - damage=skill_lv*(dex/2+75)*(100+int_)/100; - break; - - case HT_BLITZBEAT: // ブリッツビ?[ト - if( sd==NULL || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0) - skill=0; - damage=(dex/10+int_/2+skill*3+40)*2; - if(flag > 1) - damage /= flag; - aflag = (aflag&~BF_RANGEMASK)|BF_LONG; - break; - - case TF_THROWSTONE: // ?ホ投げ - damage=50; - aflag = (aflag&~BF_RANGEMASK)|BF_LONG; - break; - - case BA_DISSONANCE: // 不協和音 - damage=30+skill_lv*10+pc_checkskill(sd,BA_MUSICALLESSON)*3; - break; - - case NPC_SELFDESTRUCTION: // 自爆 - damage = status_get_hp(bl); - damagefix = 0; - break; - - case NPC_SMOKING: // タバコを吸う - damage=3; - damagefix=0; - break; - - case NPC_DARKBREATH: - { - struct status_change *sc = status_get_sc(target); - int hitrate=status_get_hit(bl) - status_get_flee(target) + 80; - hitrate = ( (hitrate>95)?95: ((hitrate<5)?5:hitrate) ); - if(sc && sc->count && (sc->data[SC_SLEEP].timer!=-1 || sc->data[SC_STUN].timer!=-1 || - sc->data[SC_FREEZE].timer!=-1 || (sc->data[SC_STONE].timer!=-1 && sc->data[SC_STONE].val2==0) ) ) - hitrate = 1000000; - if(rand()%100 < hitrate) { - damage = 500 + (skill_lv-1)*1000 + rand()%1000; - if(damage > 9999) damage = 9999; - } else - md.dmg_lv=ATK_FLEE; - } - break; - - case SN_FALCONASSAULT: /* ファルコンアサルト */ - if( sd==NULL || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0) - skill=0; - - //Blitz Beat lv5 Damage - damage=(dex/10+int_/2+skill*3+40)*2; - skill = skill_get_num(HT_BLITZBEAT, 5); - damage_div_fix(damage, skill); - - //Falcon Assault Modifier - damage=damage*(150+70*skill_lv)/100; - if(flag > 1) - damage /= flag; - aflag = (aflag&~BF_RANGEMASK)|BF_LONG; - break; - - case PA_PRESSURE: - damage=500+300*skill_lv; - damagefix=0; - aflag = (aflag&~BF_RANGEMASK)|BF_LONG; - break; - case PA_GOSPEL: - damage = 1+rand()%9999; - aflag = (aflag&~BF_RANGEMASK)|BF_LONG; - break; - case CR_ACIDDEMONSTRATION: - { // updated the formula based on a Japanese formula found to be exact [Reddozen] - int vit = status_get_vit(target); - damage = 7*(vit*int_*int_) / (10*(vit+int_)); - if (tsd) damage/=2; - aflag = (aflag&~BF_RANGEMASK)|BF_LONG; - break; - } - case NJ_ZENYNAGE: - { - int dmgnage = (500*skill_lv)+rand()%(500*skill_lv); - damage=dmgnage; - sd->status.zeny -= dmgnage; - clif_updatestatus(sd,SP_ZENY); - if(map_flag_vs(bl->m) || is_boss(bl)) - damage=damage/2; //temp value - } - break; - } - - if(damagefix){ - if(damage<1 && skill_num != NPC_DARKBREATH) - damage=1; - - if( tsd ){ - cardfix=100; - cardfix=cardfix*(100-tsd->subele[ele])/100; - cardfix=cardfix*(100-tsd->subsize[size])/100; - cardfix=cardfix*(100-tsd->subrace2[race2])/100; - cardfix=cardfix*(100-tsd->subrace[race])/100; - cardfix=cardfix*(100-tsd->subrace[is_boss(bl)?10:11])/100; - cardfix=cardfix*(100-tsd->misc_def_rate)/100; - if(aflag&BF_SHORT) - cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; - else // BF_LONG (there's no other choice) - cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; - - damage=damage*cardfix/100; - } - if (sd && skill_num > 0 && sd->skillatk[0].id != 0) - { - int i; - for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++); - if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num) - damage += damage*sd->skillatk[i].val/100; - } - - if(damage < 0) damage = 0; - damage=battle_attr_fix(bl, target, damage, ele, status_get_element(target) ); // 属?ォ?C?ウ - } - - div_=skill_get_num( skill_num,skill_lv ); - damage_div_fix(damage, div_); - - if(damage > 0 && t_mode&MD_PLANT && skill_num != PA_PRESSURE) //Pressure can vaporize plants. - damage = 1; - - if(is_boss(target)) - blewcount = 0; - - if (battle_config.skillrange_by_distance && - (bl->type&battle_config.skillrange_by_distance) - ) { //Skill range based on distance between src/target [Skotlex] - if (check_distance_bl(bl, target, 3)) - aflag=(aflag&~BF_RANGEMASK)|BF_SHORT; - else - aflag=(aflag&~BF_RANGEMASK)|BF_LONG; - } - - if (skill_num != PA_PRESSURE) //Pressure ignores all these things... - damage=battle_calc_damage(bl,target,damage,div_,skill_num,skill_lv,aflag); // 最終修正 - if (map_flag_gvg(target->m)) - damage=battle_calc_gvg_damage(bl,target,damage,div_,skill_num,skill_lv,aflag); - md.damage=damage; - md.div_=div_; - md.blewcount=blewcount; - md.flag=aflag; - return md; -} -/*========================================== - * ダ??[ジ計算一括??用 - *------------------------------------------ - */ -struct Damage battle_calc_attack( int attack_type, - struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag) -{ - struct Damage d; - switch(attack_type){ - case BF_WEAPON: - d = battle_calc_weapon_attack(bl,target,skill_num,skill_lv,flag); - break; - case BF_MAGIC: - d = battle_calc_magic_attack(bl,target,skill_num,skill_lv,flag); - break; - case BF_MISC: - d = battle_calc_misc_attack(bl,target,skill_num,skill_lv,flag); - break; - default: - if (battle_config.error_log) - ShowError("battle_calc_attack: unknown attack type! %d\n",attack_type); - memset(&d,0,sizeof(d)); - break; - } - return d; -} - -int battle_calc_return_damage(struct block_list *bl, int *damage, int flag) { - struct map_session_data *sd=NULL; - struct status_change *sc; - int rdamage = 0; - - if (bl->type == BL_PC) sd = (struct map_session_data*)bl; - sc = status_get_sc(bl); - - if(flag&BF_WEAPON) { - //Bounces back part of the damage. - if (flag & BF_SHORT) { - if (sd && sd->short_weapon_damage_return) - { - rdamage += *damage * sd->short_weapon_damage_return / 100; - if(rdamage < 1) rdamage = 1; - } - if (sc && sc->data[SC_REFLECTSHIELD].timer != -1) - { - rdamage += *damage * sc->data[SC_REFLECTSHIELD].val2 / 100; - if (rdamage < 1) rdamage = 1; - } - } else if (flag & BF_LONG) { - if (sd && sd->long_weapon_damage_return) - { - rdamage += *damage * sd->long_weapon_damage_return / 100; - if (rdamage < 1) rdamage = 1; - } - } - } else - // magic_damage_return by [AppleGirl] and [Valaris] - if(flag&BF_MAGIC) - { - if(sd && sd->magic_damage_return && rand()%100 < sd->magic_damage_return) - { //Bounces back full damage, you take none. - rdamage = *damage; - *damage = 0; - } - } - return rdamage; -} - -void battle_drain(TBL_PC *sd, TBL_PC* tsd, int rdamage, int ldamage, int race, int boss) -{ - struct weapon_data *wd; - int type, thp = 0, tsp = 0, rhp = 0, rsp = 0, hp, sp, i, *damage; - for (i = 0; i < 4; i++) { - //First two iterations: Right hand - if (i < 2) { wd = &sd->right_weapon; damage = &rdamage; } - else { wd = &sd->left_weapon; damage = &ldamage; } - if (*damage <= 0) continue; - //First and Third iterations: race, other two boss/nonboss state - if (i == 0 || i == 2) - type = race; - else - type = boss?RC_BOSS:RC_NONBOSS; - - hp = wd->hp_drain[type].value; - if (wd->hp_drain[type].rate) - hp += battle_calc_drain(*damage, - wd->hp_drain[type].rate, - wd->hp_drain[type].per); - - sp = wd->sp_drain[type].value; - if (wd->sp_drain[type].rate) - sp += battle_calc_drain(*damage, - wd->sp_drain[type].rate, - wd->sp_drain[type].per); - - if (hp) { - if (wd->hp_drain[type].type) - rhp += hp; - thp += hp; - } - if (sp) { - if (wd->sp_drain[type].type) - rsp += sp; - tsp += sp; - } - } - if (!thp && !tsp) return; - - pc_heal(sd, thp, tsp); - - if (battle_config.show_hp_sp_drain && sd->fd) - { //Display gained values only when they are positive [Skotlex] - if (thp && thp > sd->status.max_hp - sd->status.hp) - thp = sd->status.max_hp - sd->status.hp; - if (tsp && tsp > sd->status.max_sp - sd->status.sp) - tsp = sd->status.max_sp - sd->status.sp; - - if (thp > 0) - clif_heal(sd->fd, SP_HP, thp); - if (tsp > 0) - clif_heal(sd->fd, SP_SP, tsp); - } - - if (tsd) { - if (rhp || rsp) - pc_heal(tsd, -rhp, -rsp); - if (rand()%1000 < sd->sp_vanish_rate) - pc_damage_sp(tsd, 0, sd->sp_vanish_per); - } -} -/*========================================== - * 通??U撃??まとめ - *------------------------------------------ - */ -int battle_weapon_attack( struct block_list *src,struct block_list *target, - unsigned int tick,int flag) -{ - struct map_session_data *sd = NULL, *tsd = NULL; - struct status_change *sc, *tsc; - int race, ele, damage,rdamage=0,rdelay=0; - struct Damage wd; - - nullpo_retr(0, src); - nullpo_retr(0, target); - - if (src->prev == NULL || target->prev == NULL) - return 0; - - if(src->type == BL_PC) - sd = (struct map_session_data *)src; - - if (target->type == BL_PC) - tsd = (struct map_session_data *)target; - - sc = status_get_sc(src); - tsc = status_get_sc(target); - - if (sc && !sc->count) //Avoid sc checks when there's none to check for. [Skotlex] - sc = NULL; - if (tsc && !tsc->count) - tsc = NULL; - - race = status_get_race(target); - ele = status_get_elem_type(target); - - if (sd) - { - sd->state.arrow_atk = (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)); - if (sd->state.arrow_atk && sd->equip_index[10]<0) { - clif_arrow_fail(sd,0); - return 0; - } - } - - if (sc && sc->data[SC_CLOAKING].timer != -1 && !sc->data[SC_CLOAKING].val4) - status_change_end(src,SC_CLOAKING,-1); - - //Check for counter attacks that block your attack. [Skotlex] - if(tsc) - { - if(tsc->data[SC_AUTOCOUNTER].timer != -1 && - (!sc || sc->data[SC_AUTOCOUNTER].timer == -1) && - status_check_skilluse(target, src, KN_AUTOCOUNTER, 1) - ) { - int dir = map_calc_dir(target,src->x,src->y); - int t_dir = unit_getdir(target); - int dist = distance_bl(src, target); - if(dist <= 0 || (!map_check_dir(dir,t_dir) && dist <= status_get_range(target)+1)) - { - int skilllv = tsc->data[SC_AUTOCOUNTER].val1; - clif_skillcastcancel(target); //Remove the casting bar. [Skotlex] - clif_damage(src, target, tick, status_get_amotion(src), 1, 0, 1, 0, 0); //Display MISS. - status_change_end(target,SC_AUTOCOUNTER,-1); - skill_attack(BF_WEAPON,target,target,src,KN_AUTOCOUNTER,skilllv,tick,0); - return 0; - } - } - if (tsc->data[SC_BLADESTOP_WAIT].timer != -1 && !is_boss(src)) { - int skilllv = tsc->data[SC_BLADESTOP_WAIT].val1; - int duration = skill_get_time2(MO_BLADESTOP,skilllv); - status_change_end(target, SC_BLADESTOP_WAIT, -1); - if(sc_start4(src, SC_BLADESTOP, 100, sd?pc_checkskill(sd, MO_BLADESTOP):5, 0, 0, (int)target, duration)) - { //Target locked. - clif_damage(src, target, tick, status_get_amotion(src), 1, 0, 1, 0, 0); //Display MISS. - clif_bladestop(target,src,1); - sc_start4(target, SC_BLADESTOP, 100, skilllv, 0, 0,(int)src, duration); - return 0; - } - } - } - //Recycled the damage variable rather than use a new one... [Skotlex] - if(sd && (damage = pc_checkskill(sd,MO_TRIPLEATTACK)) > 0) // triple blow works with bows ^^ [celest] - { - int triple_rate= 30 - damage; //Base Rate - if (sc && sc->data[SC_SKILLRATE_UP].timer!=-1 && sc->data[SC_SKILLRATE_UP].val1 == MO_TRIPLEATTACK) - { - triple_rate+= triple_rate*(sc->data[SC_SKILLRATE_UP].val2)/100; - status_change_end(src,SC_SKILLRATE_UP,-1); - } - if (rand()%100 < triple_rate) return skill_attack(BF_WEAPON,src,src,target,MO_TRIPLEATTACK,damage,tick,0); - } - else if (sc && sc->data[SC_SACRIFICE].timer != -1) - return skill_attack(BF_WEAPON,src,src,target,PA_SACRIFICE,sc->data[SC_SACRIFICE].val1,tick,0); - - wd = battle_calc_weapon_attack(src,target, 0, 0,0); - - if (sd && sd->state.arrow_atk) //Consume arrow. - battle_consume_ammo(sd, 0, 0); - - damage = wd.damage + wd.damage2; - if (damage > 0 && src != target) { - rdamage = battle_calc_return_damage(target, &damage, wd.flag); - if (rdamage > 0) { - rdelay = clif_damage(src, src, tick, wd.amotion, status_get_dmotion(src), rdamage, 1, 4, 0); - //Use Reflect Shield to signal this kind of skill trigger. [Skotlex] - skill_additional_effect(target,src,CR_REFLECTSHIELD, 1,BF_WEAPON,tick); - } - } - - wd.dmotion = clif_damage(src, target, tick, wd.amotion, wd.dmotion, wd.damage, wd.div_ , wd.type, wd.damage2); - //二?流?カ手とカタ?[ル追撃のミス表示(無?やり?`) - if(sd && sd->status.weapon > MAX_WEAPON_TYPE && wd.damage2 == 0) - clif_damage(src, target, tick+10, wd.amotion, wd.dmotion,0, 1, 0, 0); - - if (sd && sd->splash_range > 0 && damage > 0) - skill_castend_damage_id(src, target, 0, -1, tick, 0); - - map_freeblock_lock(); - - battle_delay_damage(tick+wd.amotion, src, target, BF_WEAPON, 0, 0, damage, wd.dmg_lv, wd.dmotion, 0); - - if (!status_isdead(target) && damage > 0) { - if (sd) { - int boss = status_get_mode(target)&MD_BOSS; - int rate = 0; - if (sd->weapon_coma_ele[ele] > 0) - rate += sd->weapon_coma_ele[ele]; - if (sd->weapon_coma_race[race] > 0) - rate += sd->weapon_coma_race[race]; - if (sd->weapon_coma_race[boss?RC_BOSS:RC_NONBOSS] > 0) - rate += sd->weapon_coma_race[boss?RC_BOSS:RC_NONBOSS]; - if (rate) - status_change_start(target, SC_COMA, rate, 0, 0, 0, 0, 0, 0); - } - } - - if (sc && sc->data[SC_AUTOSPELL].timer != -1 && rand()%100 < sc->data[SC_AUTOSPELL].val4) { - int sp = 0, f = 0; - int skillid = sc->data[SC_AUTOSPELL].val2; - int skilllv = sc->data[SC_AUTOSPELL].val3; - int i = rand()%100; - if (sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_SAGE) - i = 0; //Max chance, no skilllv reduction. [Skotlex] - if (i >= 50) skilllv -= 2; - else if (i >= 15) skilllv--; - if (skilllv < 1) skilllv = 1; - if (sd) sp = skill_get_sp(skillid,skilllv) * 2 / 3; - - if ((sd && sd->status.sp >= sp) || !sd) { - switch (skill_get_casttype(skillid)) { - case CAST_GROUND: - f = skill_castend_pos2(src, target->x, target->y, skillid, skilllv, tick, flag); - break; - case CAST_NODAMAGE: - f = skill_castend_nodamage_id(src, target, skillid, skilllv, tick, flag); - break; - case CAST_DAMAGE: - f = skill_castend_damage_id(src, target, skillid, skilllv, tick, flag); - break; - } - if (sd && !f) { pc_damage_sp(sd, sp, 0); } - } - } - if (sd) { - if (wd.flag & BF_WEAPON && src != target && damage > 0) { - if (battle_config.left_cardfix_to_right) - battle_drain(sd, tsd, wd.damage, wd.damage, race, is_boss(target)); - else - battle_drain(sd, tsd, wd.damage, wd.damage2, race, is_boss(target)); - } - } - if (rdamage > 0) //By sending attack type "none" skill_additional_effect won't be invoked. [Skotlex] - battle_delay_damage(tick+wd.amotion, target, src, 0, 0, 0, rdamage, ATK_DEF, rdelay, 0); - - if (tsc) { - if (tsc->data[SC_POISONREACT].timer != -1 && - check_distance_bl(src, target, status_get_range(target)+1) && - status_check_skilluse(target, src, TF_POISON, 0) - ) { //Poison React - if (status_get_elem_type(src) == 5) { - tsc->data[SC_POISONREACT].val2 = 0; - skill_attack(BF_WEAPON,target,target,src,AS_POISONREACT,tsc->data[SC_POISONREACT].val1,tick,0); - } else { - skill_attack(BF_WEAPON,target,target,src,TF_POISON, 5, tick, flag); - --tsc->data[SC_POISONREACT].val2; - } - if (tsc->data[SC_POISONREACT].val2 <= 0) - status_change_end(target, SC_POISONREACT, -1); - } - } - map_freeblock_unlock(); - return wd.dmg_lv; -} - -int battle_check_undead(int race,int element) -{ - if(battle_config.undead_detect_type == 0) { - if(element == 9) - return 1; - } - else if(battle_config.undead_detect_type == 1) { - if(race == RC_UNDEAD) - return 1; - } - else { - if(element == 9 || race == RC_UNDEAD) - return 1; - } - return 0; -} - -/*========================================== - * Checks the state between two targets (rewritten by Skotlex) - * (enemy, friend, party, guild, etc) - * See battle.h for possible values/combinations - * to be used here (BCT_* constants) - * Return value is: - * 1: flag holds true (is enemy, party, etc) - * -1: flag fails - * 0: Invalid target (non-targetable ever) - *------------------------------------------ - */ -int battle_check_target( struct block_list *src, struct block_list *target,int flag) -{ - int m,state = 0,s_is_homun=0,t_is_homun=0; //Initial state none - int strip_enemy = 1; //Flag which marks whether to remove the BCT_ENEMY status if it's also friend/ally. - struct block_list *s_bl= src, *t_bl= target; - - nullpo_retr(0, src); - nullpo_retr(0, target); - - //[blackhole89] -- check homunculus' targeting by their masters. - if(src->type == BL_HOMUNCULUS) { - s_bl=src=(struct block_list *)((struct homun_data*)src)->master; //Whoever is the master's enemy is the homunculus' enemy. - s_is_homun=1; - } - if(target->type == BL_HOMUNCULUS) { - t_bl=target=(struct block_list *)((struct homun_data*)target)->master; //...and vice versa. - t_is_homun=1; - } - - m = target->m; - if (flag&BCT_ENEMY && !map_flag_gvg(m) && !(status_get_mode(src)&MD_BOSS)) - { //No offensive stuff while in Basilica. - if (map_getcell(m,src->x,src->y,CELL_CHKBASILICA) || - map_getcell(m,target->x,target->y,CELL_CHKBASILICA)) - return -1; - } - - if (target->type == BL_SKILL) //Needed out of the switch in case the ownership needs to be passed skill->mob->master - { - struct skill_unit *su = (struct skill_unit *)target; - if (!su->group) - return 0; - if (skill_get_inf2(su->group->skill_id)&INF2_TRAP) - { //Only a few skills can target traps... - switch (battle_getcurrentskill(src)) - { - case HT_REMOVETRAP: - case AC_SHOWER: - case WZ_HEAVENDRIVE: - state |= BCT_ENEMY; - strip_enemy = 0; - break; - default: - return 0; - } - } else if (su->group->skill_id==WZ_ICEWALL) - { //Icewall can be hit by anything except skills. - if (src->type == BL_SKILL) - return 0; - state |= BCT_ENEMY; - strip_enemy = 0; - } else //Excepting traps and icewall, you should not be able to target skills. - return 0; - if ((t_bl = map_id2bl(su->group->src_id)) == NULL) - t_bl = target; //Fallback on the trap itself, otherwise consider this a "versus caster" scenario. - } - - switch (t_bl->type) - { - case BL_PC: - { - TBL_PC *sd = (TBL_PC*)t_bl; - if (sd->invincible_timer != -1 || (pc_isinvisible(sd) && !t_is_homun)) //[blackhole89] targeted homunculi ignore master's visibility - return -1; //Cannot be targeted yet. - if (sd->state.monster_ignore && src->type == BL_MOB) - return 0; //option to have monsters ignore GMs [Valaris] - if (sd->special_state.killable && t_bl != s_bl) - { - state |= BCT_ENEMY; //Universal Victim - strip_enemy = 0; - } - break; - } - case BL_MOB: - { - TBL_MOB *md = (TBL_MOB*)t_bl; - if(md->state.killer) - if(md->master_id != s_bl->id) - state |= BCT_ENEMY; // If he can attack you, you can attack him. - if (!agit_flag && md->guardian_data && md->guardian_data->guild_id) - return 0; //Disable guardians/emperiums owned by Guilds on non-woe times. - if (md->special_state.ai == 2) - { //Mines are sort of universal enemies. - state |= BCT_ENEMY; - strip_enemy = 0; - } - if (md->master_id && (t_bl = map_id2bl(md->master_id)) == NULL) - t_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "versus master" scenario. - break; - } - case BL_PET: - { - return 0; //Pets cannot be targetted. - } - case BL_SKILL: //Skill with no owner? Kinda odd... but.. let it through. - break; - default: //Invalid target - return 0; - } - - if (src->type == BL_SKILL) - { - struct skill_unit *su = (struct skill_unit *)src; - if (!su->group) - return 0; - - if (su->group->src_id == target->id) - { - int inf2; - inf2 = skill_get_inf2(su->group->skill_id); - if (inf2&INF2_NO_TARGET_SELF) - return -1; - if (inf2&INF2_TARGET_SELF) - return 1; - } - if ((s_bl = map_id2bl(su->group->src_id)) == NULL) - s_bl = src; //Fallback on the trap itself, otherwise consider this a "caster versus enemy" scenario. - } - - switch (s_bl->type) - { - case BL_PC: - { - TBL_PC *sd = (TBL_PC*) s_bl; - if (sd->special_state.killer && s_bl != t_bl) - { - state |= BCT_ENEMY; //Is on a killing rampage :O - strip_enemy = 0; - } else - if (sd->duel_group && t_bl != s_bl && // Duel [LuzZza] - !( - (!battle_config.duel_allow_pvp && map[m].flag.pvp) || - (!battle_config.duel_allow_gvg && map_flag_gvg(m)) - )) - { - if (t_bl->type == BL_PC && - (sd->duel_group == ((TBL_PC*)t_bl)->duel_group)) - //Duel targets can ONLY be your enemy, nothing else. - return (BCT_ENEMY&flag)?1:-1; - else // You can't target anything out of your duel - return 0; - } - if (map_flag_gvg(m) && !sd->status.guild_id && - t_bl->type == BL_MOB && ((TBL_MOB*)t_bl)->guardian_data) - return 0; //If you don't belong to a guild, can't target guardians/emperium. - if (t_bl->type != BL_PC) - state |= BCT_ENEMY; //Natural enemy. - break; - } - case BL_MOB: - { - TBL_MOB*md = (TBL_MOB*)s_bl; - if(md->state.killer){ // Is on a rampage too :D - switch(t_bl->type){ - case BL_MOB: - if(md->master_id != 0 && ((TBL_MOB *)t_bl)->master_id == md->master_id) - state |= BCT_PARTY; - break; - case BL_PC: - if(t_bl->id == md->master_id) - state |= BCT_PARTY; - break; - case BL_PET: - if(((TBL_PET *)t_bl)->msd->bl.id == md->master_id) - state |= BCT_PARTY; - break; - case BL_HOMUNCULUS: - if(((struct homun_data *)t_bl)->master->bl.id == md->master_id) - state |= BCT_PARTY; - break; - } - state |= BCT_ENEMY; - break; - } - if (!agit_flag && md->guardian_data && md->guardian_data->guild_id) - return 0; //Disable guardians/emperium owned by Guilds on non-woe times. - if (!md->special_state.ai) { //Normal mobs. - if (t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai) - state |= BCT_PARTY; //Normal mobs with no ai are friends. - else - state |= BCT_ENEMY; //However, all else are enemies. - } else { - if (t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai) - state |= BCT_ENEMY; //Natural enemy for AI mobs are normal mobs. - } - if (md->master_id && (s_bl = map_id2bl(md->master_id)) == NULL) - s_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "from master" scenario. - break; - } - case BL_PET: - { - TBL_PET *pd = (TBL_PET*)s_bl; - if (t_bl->type != BL_MOB && flag&BCT_ENEMY) - return 0; //Pet may not attack non-mobs/items. - if (t_bl->type == BL_MOB && ((TBL_MOB*)t_bl)->guardian_data && flag&BCT_ENEMY) - return 0; //pet may not attack Guardians/Emperium - if (t_bl->type != BL_PC) - state |= BCT_ENEMY; //Stock enemy type. - if (pd->msd) - s_bl = &pd->msd->bl; //"My master's enemies are my enemies..." - break; - } - case BL_SKILL: //Skill with no owner? Fishy, but let it through. - break; - default: //Invalid source of attack? - return 0; - } - - if ((flag&BCT_ALL) == BCT_ALL) { //All actually stands for all players/mobs/homunculi [blackhole89] - if (target->type == BL_MOB || target->type == BL_PC || target->type == BL_HOMUNCULUS) - return 1; - else - return -1; - } else if (flag == BCT_NOONE) //Why would someone use this? no clue. - return -1; - - if (t_bl == s_bl) - { //No need for further testing. - state |= BCT_SELF|BCT_PARTY|BCT_GUILD; - if (state&BCT_ENEMY && strip_enemy) - state&=~BCT_ENEMY; - return (flag&state)?1:-1; - } - - if (map_flag_vs(m)) { //Check rivalry settings. - if (flag&(BCT_PARTY|BCT_ENEMY)) { - int s_party = status_get_party_id(s_bl); - if ( - !(map[m].flag.pvp && map[m].flag.pvp_noparty) && - !(map_flag_gvg(m) && map[m].flag.gvg_noparty) && - s_party && s_party == status_get_party_id(t_bl) - ) - state |= BCT_PARTY; - else - state |= BCT_ENEMY; - } - if (flag&(BCT_GUILD|BCT_ENEMY)) { - int s_guild = status_get_guild_id(s_bl); - int t_guild = status_get_guild_id(t_bl); - if ( - !(map[m].flag.pvp && map[m].flag.pvp_noguild) && - s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild)) - ) - state |= BCT_GUILD; - else - state |= BCT_ENEMY; - } - if (state&BCT_ENEMY && battle_config.pk_mode && !map_flag_gvg(m) && - s_bl->type == BL_PC && t_bl->type == BL_PC) - { //Prevent novice engagement on pk_mode (feature by Valaris) - TBL_PC *sd = (TBL_PC*)s_bl, *sd2 = (TBL_PC*)t_bl; - if ( - (sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE || - (sd2->class_&MAPID_UPPERMASK) == MAPID_NOVICE || - sd->status.base_level < battle_config.pk_min_level || - sd2->status.base_level < battle_config.pk_min_level || - (battle_config.pk_level_range && ( - sd->status.base_level > sd2->status.base_level ? - sd->status.base_level - sd2->status.base_level : - sd2->status.base_level - sd->status.base_level ) - > battle_config.pk_level_range) - ) - state&=~BCT_ENEMY; - } - } else { //Non pvp/gvg, check party/guild settings. - if (flag&BCT_PARTY || state&BCT_ENEMY) { - int s_party = status_get_party_id(s_bl); - if(s_party && s_party == status_get_party_id(t_bl)) - state |= BCT_PARTY; - } - if (flag&BCT_GUILD || state&BCT_ENEMY) { - int s_guild = status_get_guild_id(s_bl); - int t_guild = status_get_guild_id(t_bl); - if(s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild))) - state |= BCT_GUILD; - } - } - - if (!state) //If not an enemy, nor a guild, nor party, nor yourself, it's neutral. - state = BCT_NEUTRAL; - //Alliance state takes precedence over enemy one. - else if (state&BCT_ENEMY && strip_enemy && state&(BCT_SELF|BCT_PARTY|BCT_GUILD)) - state&=~BCT_ENEMY; - - return (flag&state)?1:-1; -} -/*========================================== - * 射程判定 - *------------------------------------------ - */ -int battle_check_range(struct block_list *src,struct block_list *bl,int range) -{ - nullpo_retr(0, src); - nullpo_retr(0, bl); - - if(src->m != bl->m) // 違うマップ - return 0; - - if (!check_distance_bl(src, bl, range)) - return 0; - - if(distance_bl(src, bl) < 2) //No need for path checking. - return 1; - - // ?瘧Q物判定 - return path_search_long(NULL,src->m,src->x,src->y,bl->x,bl->y); -} - -/*========================================== - * Return numerical value of a switch configuration (modified by [Yor]) - * on/off, english, fran軋is, deutsch, espaol - *------------------------------------------ - */ -int battle_config_switch(const char *str) { - if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) - return 1; - if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) - return 0; - return atoi(str); -} - -static const struct battle_data_short { - const char *str; - unsigned short *val; -} battle_data_short[] = { //List here battle_athena options which are type unsigned short! - { "warp_point_debug", &battle_config.warp_point_debug }, - { "enemy_critical_rate", &battle_config.enemy_critical_rate }, - { "enemy_str", &battle_config.enemy_str }, - { "enemy_perfect_flee", &battle_config.enemy_perfect_flee }, - { "casting_rate", &battle_config.cast_rate }, - { "delay_rate", &battle_config.delay_rate }, - { "delay_dependon_dex", &battle_config.delay_dependon_dex }, - { "skill_delay_attack_enable", &battle_config.sdelay_attack_enable }, - { "left_cardfix_to_right", &battle_config.left_cardfix_to_right }, - { "skill_add_range", &battle_config.skill_add_range }, - { "skill_out_range_consume", &battle_config.skill_out_range_consume }, - { "skillrange_by_distance", &battle_config.skillrange_by_distance }, - { "skillrange_from_weapon", &battle_config.use_weapon_skill_range }, - { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate }, - { "defunit_not_enemy", &battle_config.defnotenemy }, - { "gvg_traps_target_all", &battle_config.vs_traps_bctall }, - { "traps_setting", &battle_config.traps_setting }, - { "clear_skills_on_death", &battle_config.clear_unit_ondeath }, - { "random_monster_checklv", &battle_config.random_monster_checklv }, - { "attribute_recover", &battle_config.attr_recover }, - { "flooritem_lifetime", &battle_config.flooritem_lifetime }, - { "item_auto_get", &battle_config.item_auto_get }, - { "drop_rate0item", &battle_config.drop_rate0item }, - { "pvp_exp", &battle_config.pvp_exp }, - { "gtb_pvp_only", &battle_config.gtb_pvp_only }, - { "guild_max_castles", &battle_config.guild_max_castles }, - { "death_penalty_type", &battle_config.death_penalty_type }, - { "death_penalty_base", &battle_config.death_penalty_base }, - { "death_penalty_job", &battle_config.death_penalty_job }, - { "restart_hp_rate", &battle_config.restart_hp_rate }, - { "restart_sp_rate", &battle_config.restart_sp_rate }, - { "mvp_hp_rate", &battle_config.mvp_hp_rate }, - { "monster_hp_rate", &battle_config.monster_hp_rate }, - { "monster_max_aspd", &battle_config.monster_max_aspd }, - { "view_range_rate", &battle_config.view_range_rate }, - { "chase_range_rate", &battle_config.chase_range_rate }, - { "atcommand_gm_only", &battle_config.atc_gmonly }, - { "atcommand_spawn_quantity_limit", &battle_config.atc_spawn_quantity_limit }, - { "atcommand_slave_clone_limit", &battle_config.atc_slave_clone_limit}, - { "gm_all_skill", &battle_config.gm_allskill }, - { "gm_all_skill_add_abra", &battle_config.gm_allskill_addabra }, - { "gm_all_equipment", &battle_config.gm_allequip }, - { "gm_skill_unconditional", &battle_config.gm_skilluncond }, - { "gm_join_chat", &battle_config.gm_join_chat }, - { "gm_kick_chat", &battle_config.gm_kick_chat }, - { "player_skillfree", &battle_config.skillfree }, - { "player_skillup_limit", &battle_config.skillup_limit }, - { "weapon_produce_rate", &battle_config.wp_rate }, - { "potion_produce_rate", &battle_config.pp_rate }, - { "monster_active_enable", &battle_config.monster_active_enable }, - { "monster_damage_delay_rate", &battle_config.monster_damage_delay_rate}, - { "monster_loot_type", &battle_config.monster_loot_type }, -// { "mob_skill_use", &battle_config.mob_skill_use }, //Deprecated - { "mob_skill_rate", &battle_config.mob_skill_rate }, - { "mob_skill_delay", &battle_config.mob_skill_delay }, - { "mob_count_rate", &battle_config.mob_count_rate }, - { "mob_spawn_delay", &battle_config.mob_spawn_delay }, - { "no_spawn_on_player", &battle_config.no_spawn_on_player }, - { "plant_spawn_delay", &battle_config.plant_spawn_delay }, - { "boss_spawn_delay", &battle_config.boss_spawn_delay }, - { "slaves_inherit_mode", &battle_config.slaves_inherit_mode }, - { "slaves_inherit_speed", &battle_config.slaves_inherit_speed }, - { "summons_inherit_effects", &battle_config.summons_inherit_effects }, - { "pc_damage_walk_delay_rate", &battle_config.pc_walk_delay_rate }, - { "damage_walk_delay_rate", &battle_config.walk_delay_rate }, - { "multihit_delay", &battle_config.multihit_delay }, - { "quest_skill_learn", &battle_config.quest_skill_learn }, - { "quest_skill_reset", &battle_config.quest_skill_reset }, - { "basic_skill_check", &battle_config.basic_skill_check }, - { "guild_emperium_check", &battle_config.guild_emperium_check }, - { "guild_exp_rate", &battle_config.guild_exp_rate }, - { "guild_exp_limit", &battle_config.guild_exp_limit }, - { "player_invincible_time", &battle_config.pc_invincible_time }, - { "pet_catch_rate", &battle_config.pet_catch_rate }, - { "pet_rename", &battle_config.pet_rename }, - { "pet_friendly_rate", &battle_config.pet_friendly_rate }, - { "pet_hungry_delay_rate", &battle_config.pet_hungry_delay_rate }, - { "pet_hungry_friendly_decrease", &battle_config.pet_hungry_friendly_decrease}, - { "pet_str", &battle_config.pet_str }, - { "pet_status_support", &battle_config.pet_status_support }, - { "pet_attack_support", &battle_config.pet_attack_support }, - { "pet_damage_support", &battle_config.pet_damage_support }, - { "pet_support_min_friendly", &battle_config.pet_support_min_friendly }, - { "pet_support_rate", &battle_config.pet_support_rate }, - { "pet_attack_exp_to_master", &battle_config.pet_attack_exp_to_master }, - { "pet_attack_exp_rate", &battle_config.pet_attack_exp_rate }, - { "pet_lv_rate", &battle_config.pet_lv_rate }, //Skotlex - { "pet_max_stats", &battle_config.pet_max_stats }, //Skotlex - { "pet_max_atk1", &battle_config.pet_max_atk1 }, //Skotlex - { "pet_max_atk2", &battle_config.pet_max_atk2 }, //Skotlex - { "pet_disable_in_gvg", &battle_config.pet_no_gvg }, //Skotlex - { "skill_min_damage", &battle_config.skill_min_damage }, - { "finger_offensive_type", &battle_config.finger_offensive_type }, - { "heal_exp", &battle_config.heal_exp }, - { "resurrection_exp", &battle_config.resurrection_exp }, - { "shop_exp", &battle_config.shop_exp }, - { "combo_delay_rate", &battle_config.combo_delay_rate }, - { "item_check", &battle_config.item_check }, - { "item_use_interval", &battle_config.item_use_interval }, - { "wedding_modifydisplay", &battle_config.wedding_modifydisplay }, - { "wedding_ignorepalette", &battle_config.wedding_ignorepalette }, //[Skotlex] - { "xmas_ignorepalette", &battle_config.xmas_ignorepalette }, // [Valaris] - { "natural_heal_weight_rate", &battle_config.natural_heal_weight_rate }, - { "item_name_override_grffile", &battle_config.item_name_override_grffile}, - { "item_equip_override_grffile", &battle_config.item_equip_override_grffile}, // [Celest] - { "item_slots_override_grffile", &battle_config.item_slots_override_grffile}, // [Celest] - { "indoors_override_grffile", &battle_config.indoors_override_grffile}, // [Celest] - { "skill_sp_override_grffile", &battle_config.skill_sp_override_grffile}, // [Celest] - { "cardillust_read_grffile", &battle_config.cardillust_read_grffile}, // [Celest] - { "arrow_decrement", &battle_config.arrow_decrement }, - { "max_aspd", &battle_config.max_aspd }, - { "max_walk_speed", &battle_config.max_walk_speed }, - { "max_lv", &battle_config.max_lv }, - { "aura_lv", &battle_config.aura_lv }, - { "max_parameter", &battle_config.max_parameter }, - { "max_baby_parameter", &battle_config.max_baby_parameter }, - { "max_def", &battle_config.max_def }, - { "over_def_bonus", &battle_config.over_def_bonus }, - { "skill_log", &battle_config.skill_log }, - { "battle_log", &battle_config.battle_log }, - { "save_log", &battle_config.save_log }, - { "error_log", &battle_config.error_log }, - { "etc_log", &battle_config.etc_log }, - { "save_clothcolor", &battle_config.save_clothcolor }, - { "undead_detect_type", &battle_config.undead_detect_type }, - { "auto_counter_type", &battle_config.auto_counter_type }, - { "min_hitrate", &battle_config.min_hitrate }, - { "max_hitrate", &battle_config.max_hitrate }, - { "agi_penalty_type", &battle_config.agi_penalty_type }, - { "agi_penalty_count", &battle_config.agi_penalty_count }, - { "agi_penalty_num", &battle_config.agi_penalty_num }, - { "agi_penalty_count_lv", &battle_config.agi_penalty_count_lv }, - { "vit_penalty_type", &battle_config.vit_penalty_type }, - { "vit_penalty_count", &battle_config.vit_penalty_count }, - { "vit_penalty_num", &battle_config.vit_penalty_num }, - { "vit_penalty_count_lv", &battle_config.vit_penalty_count_lv }, - { "player_defense_type", &battle_config.player_defense_type }, - { "monster_defense_type", &battle_config.monster_defense_type }, - { "pet_defense_type", &battle_config.pet_defense_type }, - { "magic_defense_type", &battle_config.magic_defense_type }, - { "skill_reiteration", &battle_config.skill_reiteration }, - { "skill_nofootset", &battle_config.skill_nofootset }, - { "player_cloak_check_type", &battle_config.pc_cloak_check_type }, - { "monster_cloak_check_type", &battle_config.monster_cloak_check_type }, - { "sense_type", &battle_config.estimation_type }, - { "gvg_short_attack_damage_rate", &battle_config.gvg_short_damage_rate }, - { "gvg_long_attack_damage_rate", &battle_config.gvg_long_damage_rate }, - { "gvg_weapon_attack_damage_rate", &battle_config.gvg_weapon_damage_rate }, - { "gvg_magic_attack_damage_rate", &battle_config.gvg_magic_damage_rate }, - { "gvg_misc_attack_damage_rate", &battle_config.gvg_misc_damage_rate }, - { "gvg_flee_penalty", &battle_config.gvg_flee_penalty }, - { "pk_short_attack_damage_rate", &battle_config.pk_short_damage_rate }, - { "pk_long_attack_damage_rate", &battle_config.pk_long_damage_rate }, - { "pk_weapon_attack_damage_rate", &battle_config.pk_weapon_damage_rate }, - { "pk_magic_attack_damage_rate", &battle_config.pk_magic_damage_rate }, - { "pk_misc_attack_damage_rate", &battle_config.pk_misc_damage_rate }, - { "mob_changetarget_byskill", &battle_config.mob_changetarget_byskill}, - { "attack_direction_change", &battle_config.attack_direction_change }, - { "land_skill_limit", &battle_config.land_skill_limit }, - { "party_skill_penalty", &battle_config.party_skill_penalty }, - { "monster_class_change_full_recover", &battle_config.monster_class_change_full_recover }, - { "produce_item_name_input", &battle_config.produce_item_name_input }, - { "produce_potion_name_input", &battle_config.produce_potion_name_input}, - { "making_arrow_name_input", &battle_config.making_arrow_name_input }, - { "holywater_name_input", &battle_config.holywater_name_input }, - { "cdp_name_input", &battle_config.cdp_name_input }, - { "display_delay_skill_fail", &battle_config.display_delay_skill_fail }, - { "display_snatcher_skill_fail", &battle_config.display_snatcher_skill_fail }, - { "chat_warpportal", &battle_config.chat_warpportal }, - { "mob_warpportal", &battle_config.mob_warpportal }, - { "dead_branch_active", &battle_config.dead_branch_active }, - { "show_steal_in_same_party", &battle_config.show_steal_in_same_party }, - { "show_party_share_picker", &battle_config.party_show_share_picker }, - { "party_item_share_type", &battle_config.party_share_type }, - { "pet_attack_attr_none", &battle_config.pet_attack_attr_none }, - { "mob_attack_attr_none", &battle_config.mob_attack_attr_none }, - { "mob_ghostring_fix", &battle_config.mob_ghostring_fix }, - { "pc_attack_attr_none", &battle_config.pc_attack_attr_none }, - { "gx_allhit", &battle_config.gx_allhit }, - { "gx_disptype", &battle_config.gx_disptype }, - { "devotion_level_difference", &battle_config.devotion_level_difference }, - { "player_skill_partner_check", &battle_config.player_skill_partner_check}, - { "hide_GM_session", &battle_config.hide_GM_session }, - { "invite_request_check", &battle_config.invite_request_check }, - { "skill_removetrap_type", &battle_config.skill_removetrap_type }, - { "disp_experience", &battle_config.disp_experience }, - { "disp_zeny", &battle_config.disp_zeny }, - { "castle_defense_rate", &battle_config.castle_defense_rate }, - { "hp_rate", &battle_config.hp_rate }, - { "sp_rate", &battle_config.sp_rate }, - { "gm_cant_drop_min_lv", &battle_config.gm_cant_drop_min_lv }, - { "gm_cant_drop_max_lv", &battle_config.gm_cant_drop_max_lv }, - { "disp_hpmeter", &battle_config.disp_hpmeter }, - { "bone_drop", &battle_config.bone_drop }, - { "buyer_name", &battle_config.buyer_name }, - { "skill_wall_check", &battle_config.skill_wall_check }, - { "cell_stack_limit", &battle_config.cell_stack_limit }, -// eAthena additions - { "item_logarithmic_drops", &battle_config.logarithmic_drops }, - { "item_drop_common_min", &battle_config.item_drop_common_min }, // Added by TyrNemesis^ - { "item_drop_common_max", &battle_config.item_drop_common_max }, - { "item_drop_equip_min", &battle_config.item_drop_equip_min }, - { "item_drop_equip_max", &battle_config.item_drop_equip_max }, - { "item_drop_card_min", &battle_config.item_drop_card_min }, - { "item_drop_card_max", &battle_config.item_drop_card_max }, - { "item_drop_mvp_min", &battle_config.item_drop_mvp_min }, - { "item_drop_mvp_max", &battle_config.item_drop_mvp_max }, // End Addition - { "item_drop_heal_min", &battle_config.item_drop_heal_min }, - { "item_drop_heal_max", &battle_config.item_drop_heal_max }, - { "item_drop_use_min", &battle_config.item_drop_use_min }, - { "item_drop_use_max", &battle_config.item_drop_use_max }, - { "item_drop_add_min", &battle_config.item_drop_adddrop_min }, - { "item_drop_add_max", &battle_config.item_drop_adddrop_max }, - { "item_drop_treasure_min", &battle_config.item_drop_treasure_min }, - { "item_drop_treasure_max", &battle_config.item_drop_treasure_max }, - { "prevent_logout", &battle_config.prevent_logout }, // Added by RoVeRT - { "alchemist_summon_reward", &battle_config.alchemist_summon_reward }, // [Valaris] - { "drops_by_luk", &battle_config.drops_by_luk }, // [Valaris] - { "drops_by_luk2", &battle_config.drops_by_luk2 }, // [Skotlex] - { "equip_natural_break_rate", &battle_config.equip_natural_break_rate }, - { "equip_self_break_rate", &battle_config.equip_self_break_rate }, - { "equip_skill_break_rate", &battle_config.equip_skill_break_rate }, - { "pk_mode", &battle_config.pk_mode }, // [Valaris] - { "pk_level_range", &battle_config.pk_level_range }, - { "manner_system", &battle_config.manner_system }, // [Komurka] - { "pet_equip_required", &battle_config.pet_equip_required }, // [Valaris] - { "multi_level_up", &battle_config.multi_level_up }, // [Valaris] - { "max_exp_gain_rate", &battle_config.max_exp_gain_rate }, // [Skotlex] - { "backstab_bow_penalty", &battle_config.backstab_bow_penalty }, - { "night_at_start", &battle_config.night_at_start }, // added by [Yor] - { "show_mob_hp", &battle_config.show_mob_hp }, // [Valaris] - { "ban_spoof_namer", &battle_config.ban_spoof_namer }, // added by [Yor] - { "hack_info_GM_level", &battle_config.hack_info_GM_level }, // added by [Yor] - { "any_warp_GM_min_level", &battle_config.any_warp_GM_min_level }, // added by [Yor] - { "packet_ver_flag", &battle_config.packet_ver_flag }, // added by [Yor] - { "min_hair_style", &battle_config.min_hair_style }, // added by [MouseJstr] - { "max_hair_style", &battle_config.max_hair_style }, // added by [MouseJstr] - { "min_hair_color", &battle_config.min_hair_color }, // added by [MouseJstr] - { "max_hair_color", &battle_config.max_hair_color }, // added by [MouseJstr] - { "min_cloth_color", &battle_config.min_cloth_color }, // added by [MouseJstr] - { "max_cloth_color", &battle_config.max_cloth_color }, // added by [MouseJstr] - { "pet_hair_style", &battle_config.pet_hair_style }, // added by [Skotlex] - { "castrate_dex_scale", &battle_config.castrate_dex_scale }, // added by [MouseJstr] - { "area_size", &battle_config.area_size }, // added by [MouseJstr] - { "muting_players", &battle_config.muting_players}, // added by [Apple] - { "zeny_from_mobs", &battle_config.zeny_from_mobs}, // [Valaris] - { "mobs_level_up", &battle_config.mobs_level_up}, // [Valaris] - { "mobs_level_up_exp_rate", &battle_config.mobs_level_up_exp_rate}, // [Valaris] - { "pk_min_level", &battle_config.pk_min_level}, // [celest] - { "skill_steal_type", &battle_config.skill_steal_type}, // [celest] - { "skill_steal_rate", &battle_config.skill_steal_rate}, // [celest] - { "skill_steal_max_tries", &battle_config.skill_steal_max_tries}, // [Lupus] -// { "night_darkness_level", &battle_config.night_darkness_level}, // [celest] - { "motd_type", &battle_config.motd_type}, // [celest] - { "allow_atcommand_when_mute", &battle_config.allow_atcommand_when_mute}, // [celest] - { "finding_ore_rate", &battle_config.finding_ore_rate}, // [celest] - { "exp_calc_type", &battle_config.exp_calc_type}, // [celest] - { "min_skill_delay_limit", &battle_config.min_skill_delay_limit}, // [celest] - { "default_skill_delay", &battle_config.default_skill_delay}, // [Skotlex] - { "require_glory_guild", &battle_config.require_glory_guild}, // [celest] - { "idle_no_share", &battle_config.idle_no_share}, // [celest], for a feature by [MouseJstr] - { "party_even_share_bonus", &battle_config.party_even_share_bonus}, - { "delay_battle_damage", &battle_config.delay_battle_damage}, // [celest] - { "hide_woe_damage", &battle_config.hide_woe_damage}, // [Skotlex] - { "display_version", &battle_config.display_version}, // [Ancyker], for a feature by...? - { "who_display_aid", &battle_config.who_display_aid}, // [Ancyker], for a feature by...? - { "display_hallucination", &battle_config.display_hallucination}, // [Skotlex] - { "use_statpoint_table", &battle_config.use_statpoint_table}, // [Skotlex] - { "ignore_items_gender", &battle_config.ignore_items_gender}, // [Lupus] - { "copyskill_restrict", &battle_config.copyskill_restrict}, // [Aru] - { "berserk_cancels_buffs", &battle_config.berserk_cancels_buffs}, // [Aru] - - { "debuff_on_logout", &battle_config.debuff_on_logout}, - { "monster_ai", &battle_config.mob_ai}, - { "dynamic_mobs", &battle_config.dynamic_mobs}, - { "mob_remove_damaged", &battle_config.mob_remove_damaged}, - { "show_hp_sp_drain", &battle_config.show_hp_sp_drain}, // [Skotlex] - { "show_hp_sp_gain", &battle_config.show_hp_sp_gain}, // [Skotlex] - { "mob_npc_event_type", &battle_config.mob_npc_event_type}, - { "mob_clear_delay", &battle_config.mob_clear_delay}, // [Valaris] - { "character_size", &battle_config.character_size}, // [Lupus] - { "mob_max_skilllvl", &battle_config.mob_max_skilllvl}, // [Lupus] - { "retaliate_to_master", &battle_config.retaliate_to_master}, // [Skotlex] - { "rare_drop_announce", &battle_config.rare_drop_announce}, // [Lupus] - { "firewall_hits_on_undead", &battle_config.firewall_hits_on_undead}, // [Skotlex] - { "title_lvl1", &battle_config.title_lvl1}, // [Lupus] - { "title_lvl2", &battle_config.title_lvl2}, // [Lupus] - { "title_lvl3", &battle_config.title_lvl3}, // [Lupus] - { "title_lvl4", &battle_config.title_lvl4}, // [Lupus] - { "title_lvl5", &battle_config.title_lvl5}, // [Lupus] - { "title_lvl6", &battle_config.title_lvl6}, // [Lupus] - { "title_lvl7", &battle_config.title_lvl7}, // [Lupus] - { "title_lvl8", &battle_config.title_lvl8}, // [Lupus] - - { "duel_enable", &battle_config.duel_enable}, // [LuzZza] - { "duel_allow_pvp", &battle_config.duel_allow_pvp}, // [LuzZza] - { "duel_allow_gvg", &battle_config.duel_allow_gvg}, // [LuzZza] - { "duel_allow_teleport", &battle_config.duel_allow_teleport}, // [LuzZza] - { "duel_autoleave_when_die", &battle_config.duel_autoleave_when_die}, //[LuzZza] - { "duel_time_interval", &battle_config.duel_time_interval}, // [LuzZza] - - { "skip_teleport_lv1_menu", &battle_config.skip_teleport_lv1_menu}, // [LuzZza] - { "allow_skill_without_day", &battle_config.allow_skill_without_day}, // [Komurka] - { "allow_es_magic_player", &battle_config.allow_es_magic_pc }, - { "skill_caster_check", &battle_config.skill_caster_check }, - { "status_cast_cancel", &battle_config.sc_castcancel }, - { "pc_status_def_rate", &battle_config.pc_sc_def_rate }, - { "mob_status_def_rate", &battle_config.mob_sc_def_rate }, - { "pc_max_status_def", &battle_config.pc_max_sc_def }, - { "mob_max_status_def", &battle_config.mob_max_sc_def }, - { "sg_miracle_skill_ratio", &battle_config.sg_miracle_skill_ratio }, - { "autospell_stacking", &battle_config.autospell_stacking }, - { "override_mob_names", &battle_config.override_mob_names }, -}; - -static const struct battle_data_int { - const char *str; - int *val; -} battle_data_int[] = { //List here battle_athena options which are type int! - { "item_first_get_time", &battle_config.item_first_get_time }, - { "item_second_get_time", &battle_config.item_second_get_time }, - { "item_third_get_time", &battle_config.item_third_get_time }, - { "mvp_item_first_get_time", &battle_config.mvp_item_first_get_time }, - { "mvp_item_second_get_time", &battle_config.mvp_item_second_get_time }, - { "mvp_item_third_get_time", &battle_config.mvp_item_third_get_time }, - { "base_exp_rate", &battle_config.base_exp_rate }, - { "job_exp_rate", &battle_config.job_exp_rate }, - { "zeny_penalty", &battle_config.zeny_penalty }, - { "mvp_exp_rate", &battle_config.mvp_exp_rate }, - { "natural_healhp_interval", &battle_config.natural_healhp_interval }, - { "natural_healsp_interval", &battle_config.natural_healsp_interval }, - { "natural_heal_skill_interval", &battle_config.natural_heal_skill_interval}, - { "max_hp", &battle_config.max_hp }, - { "max_sp", &battle_config.max_sp }, - { "max_cart_weight", &battle_config.max_cart_weight }, - { "gvg_eliminate_time", &battle_config.gvg_eliminate_time }, - { "vending_max_value", &battle_config.vending_max_value }, -// eAthena additions - { "item_rate_mvp", &battle_config.item_rate_mvp }, - { "item_rate_common", &battle_config.item_rate_common }, // Added by RoVeRT - { "item_rate_common_boss", &battle_config.item_rate_common_boss }, // [Reddozen] - { "item_rate_equip", &battle_config.item_rate_equip }, - { "item_rate_equip_boss", &battle_config.item_rate_equip_boss }, // [Reddozen] - { "item_rate_card", &battle_config.item_rate_card }, // End Addition - { "item_rate_card_boss", &battle_config.item_rate_card_boss }, // [Reddozen] - { "item_rate_heal", &battle_config.item_rate_heal }, // Added by Valaris - { "item_rate_heal_boss", &battle_config.item_rate_heal_boss }, // [Reddozen] - { "item_rate_use", &battle_config.item_rate_use }, // End - { "item_rate_use_boss", &battle_config.item_rate_use_boss }, // [Reddozen] - { "item_rate_adddrop", &battle_config.item_rate_adddrop }, // End - { "item_rate_treasure", &battle_config.item_rate_treasure }, // End - { "day_duration", &battle_config.day_duration }, // added by [Yor] - { "night_duration", &battle_config.night_duration }, // added by [Yor] - { "mob_remove_delay", &battle_config.mob_remove_delay }, - { "sg_miracle_skill_duration", &battle_config.sg_miracle_skill_duration }, - -}; - -int battle_set_value(char *w1, char *w2) { - int i; - for(i = 0; i < sizeof(battle_data_short) / (sizeof(battle_data_short[0])); i++) - if (strcmpi(w1, battle_data_short[i].str) == 0) { - * battle_data_short[i].val = battle_config_switch(w2); - return 1; - } - for(i = 0; i < sizeof(battle_data_int) / (sizeof(battle_data_int[0])); i++) - if (strcmpi(w1, battle_data_int[i].str) == 0) { - *battle_data_int[i].val = battle_config_switch(w2); - return 1; - } - return 0; -} - -int battle_get_value(char *w1) { - int i; - for(i = 0; i < sizeof(battle_data_short) / (sizeof(battle_data_short[0])); i++) - if (strcmpi(w1, battle_data_short[i].str) == 0) { - return * battle_data_short[i].val; - } - for(i = 0; i < sizeof(battle_data_int) / (sizeof(battle_data_int[0])); i++) - if (strcmpi(w1, battle_data_int[i].str) == 0) { - return *battle_data_int[i].val; - } - return 0; -} - -void battle_set_defaults() { - battle_config.warp_point_debug=0; - battle_config.enemy_critical_rate=0; - battle_config.enemy_str=1; - battle_config.enemy_perfect_flee=0; - battle_config.cast_rate=100; - battle_config.delay_rate=100; - battle_config.delay_dependon_dex=0; - battle_config.sdelay_attack_enable=0; - battle_config.left_cardfix_to_right=0; - battle_config.skill_add_range=0; - battle_config.skill_out_range_consume=1; - battle_config.skillrange_by_distance=BL_MOB|BL_PET; - battle_config.use_weapon_skill_range=0; - battle_config.pc_damage_delay_rate=100; - battle_config.defnotenemy=0; - battle_config.vs_traps_bctall=BL_PC; - battle_config.traps_setting=0; - battle_config.clear_unit_ondeath=BL_ALL; - battle_config.random_monster_checklv=1; - battle_config.attr_recover=1; - battle_config.flooritem_lifetime=LIFETIME_FLOORITEM*1000; - battle_config.item_auto_get=0; - battle_config.item_first_get_time=3000; - battle_config.item_second_get_time=1000; - battle_config.item_third_get_time=1000; - battle_config.mvp_item_first_get_time=10000; - battle_config.mvp_item_second_get_time=10000; - battle_config.mvp_item_third_get_time=2000; - - battle_config.drop_rate0item=0; - battle_config.base_exp_rate=100; - battle_config.job_exp_rate=100; - battle_config.pvp_exp=1; - battle_config.gtb_pvp_only=0; - battle_config.death_penalty_type=0; - battle_config.death_penalty_base=0; - battle_config.death_penalty_job=0; - battle_config.zeny_penalty=0; - battle_config.restart_hp_rate=0; - battle_config.restart_sp_rate=0; - battle_config.mvp_exp_rate=100; - battle_config.mvp_hp_rate=100; - battle_config.monster_hp_rate=100; - battle_config.monster_max_aspd=199; - battle_config.view_range_rate=100; - battle_config.chase_range_rate=100; - battle_config.atc_gmonly=0; - battle_config.atc_spawn_quantity_limit=0; - battle_config.atc_slave_clone_limit=0; - battle_config.gm_allskill=0; - battle_config.gm_allequip=0; - battle_config.gm_skilluncond=0; - battle_config.gm_join_chat=0; - battle_config.gm_kick_chat=0; - battle_config.guild_max_castles=0; - battle_config.skillfree = 0; - battle_config.skillup_limit = 0; - battle_config.wp_rate=100; - battle_config.pp_rate=100; - battle_config.monster_active_enable=1; - battle_config.monster_damage_delay_rate=100; - battle_config.monster_loot_type=0; - battle_config.mob_skill_rate=100; - battle_config.mob_skill_delay=100; - battle_config.mob_count_rate=100; - battle_config.mob_spawn_delay=100; - battle_config.no_spawn_on_player=0; - battle_config.plant_spawn_delay=100; - battle_config.boss_spawn_delay=100; - battle_config.slaves_inherit_mode=1; - battle_config.slaves_inherit_speed=1; - battle_config.summons_inherit_effects=1; - battle_config.pc_walk_delay_rate=20; - battle_config.walk_delay_rate=100; - battle_config.multihit_delay=80; - battle_config.quest_skill_learn=0; - battle_config.quest_skill_reset=1; - battle_config.basic_skill_check=1; - battle_config.guild_emperium_check=1; - battle_config.guild_exp_limit=50; - battle_config.guild_exp_rate=100; - battle_config.pc_invincible_time = 5000; - battle_config.pet_catch_rate=100; - battle_config.pet_rename=0; - battle_config.pet_friendly_rate=100; - battle_config.pet_hungry_delay_rate=100; - battle_config.pet_hungry_friendly_decrease=5; - battle_config.pet_str=0; - battle_config.pet_status_support=0; - battle_config.pet_attack_support=0; - battle_config.pet_damage_support=0; - battle_config.pet_support_min_friendly=900; - battle_config.pet_support_rate=100; - battle_config.pet_attack_exp_to_master=0; - battle_config.pet_attack_exp_rate=100; - battle_config.pet_lv_rate=0; //Skotlex - battle_config.pet_max_stats=99; //Skotlex - battle_config.pet_max_atk1=750; //Skotlex - battle_config.pet_max_atk2=1000; //Skotlex - battle_config.pet_no_gvg=0; //Skotlex - battle_config.skill_min_damage=6; //Ishizu claims that magic and misc attacks always do at least div_ damage. [Skotlex] - battle_config.finger_offensive_type=0; - battle_config.heal_exp=0; - battle_config.resurrection_exp=0; - battle_config.shop_exp=0; - battle_config.combo_delay_rate=100; - battle_config.item_check=1; - battle_config.item_use_interval=100; //Use some very low value that won't bother players, but should cap bots. - battle_config.wedding_modifydisplay=0; - battle_config.wedding_ignorepalette=0; - battle_config.xmas_ignorepalette=0; // [Valaris] - battle_config.natural_healhp_interval=6000; - battle_config.natural_healsp_interval=8000; - battle_config.natural_heal_skill_interval=10000; - battle_config.natural_heal_weight_rate=50; - battle_config.item_name_override_grffile=1; - battle_config.item_equip_override_grffile=0; // [Celest] - battle_config.item_slots_override_grffile=0; // [Celest] - battle_config.indoors_override_grffile=0; // [Celest] - battle_config.skill_sp_override_grffile=0; // [Celest] - battle_config.cardillust_read_grffile=0; // [Celest] - battle_config.arrow_decrement=1; - battle_config.max_aspd = 199; - battle_config.max_walk_speed = 300; - battle_config.max_hp = 32500; - battle_config.max_sp = 32500; - battle_config.max_lv = 99; // [MouseJstr] - battle_config.aura_lv = 99; // [Skotlex] - battle_config.max_parameter = 99; - battle_config.max_baby_parameter = 80; - battle_config.max_cart_weight = 8000; - battle_config.max_def = 99; // [Skotlex] - battle_config.over_def_bonus = 0; // [Skotlex] - battle_config.skill_log = 0; - battle_config.battle_log = 0; - battle_config.save_log = 0; - battle_config.error_log = 1; - battle_config.etc_log = 1; - battle_config.save_clothcolor = 0; - battle_config.undead_detect_type = 0; - battle_config.auto_counter_type = BL_ALL; - battle_config.min_hitrate = 5; - battle_config.max_hitrate = 100; - battle_config.agi_penalty_type = 1; - battle_config.agi_penalty_count = 3; - battle_config.agi_penalty_num = 10; - battle_config.agi_penalty_count_lv = ATK_FLEE; - battle_config.vit_penalty_type = 1; - battle_config.vit_penalty_count = 3; - battle_config.vit_penalty_num = 5; - battle_config.vit_penalty_count_lv = ATK_DEF; - battle_config.player_defense_type = 0; - battle_config.monster_defense_type = 0; - battle_config.pet_defense_type = 0; - battle_config.magic_defense_type = 0; - battle_config.skill_reiteration = 0; - battle_config.skill_nofootset = BL_PC; - battle_config.pc_cloak_check_type = 1; - battle_config.monster_cloak_check_type = 0; - battle_config.estimation_type = 3; - battle_config.gvg_short_damage_rate = 100; - battle_config.gvg_long_damage_rate = 75; - battle_config.gvg_weapon_damage_rate = 60; - battle_config.gvg_magic_damage_rate = 50; - battle_config.gvg_misc_damage_rate = 60; - battle_config.gvg_flee_penalty = 20; - battle_config.gvg_eliminate_time = 7000; - - battle_config.pk_short_damage_rate = 80; - battle_config.pk_long_damage_rate = 70; - battle_config.pk_weapon_damage_rate = 60; - battle_config.pk_magic_damage_rate = 60; - battle_config.pk_misc_damage_rate = 60; - - battle_config.mob_changetarget_byskill = 0; - battle_config.attack_direction_change = BL_ALL; - battle_config.land_skill_limit = BL_ALL; - battle_config.party_skill_penalty = 1; - battle_config.monster_class_change_full_recover = 0; - battle_config.produce_item_name_input = 1; - battle_config.produce_potion_name_input = 1; - battle_config.making_arrow_name_input = 1; - battle_config.holywater_name_input = 1; - battle_config.cdp_name_input = 1; - battle_config.display_delay_skill_fail = 1; - battle_config.display_snatcher_skill_fail = 1; - battle_config.chat_warpportal = 0; - battle_config.mob_warpportal = 0; - battle_config.dead_branch_active = 0; - battle_config.vending_max_value = 10000000; - battle_config.show_steal_in_same_party = 0; - battle_config.party_share_type = 0; - battle_config.party_show_share_picker = 0; - battle_config.pet_attack_attr_none = 0; - battle_config.pc_attack_attr_none = 0; - battle_config.mob_attack_attr_none = 0; - battle_config.mob_ghostring_fix = 1; - battle_config.gx_allhit = 1; - battle_config.gx_disptype = 1; - battle_config.devotion_level_difference = 10; - battle_config.player_skill_partner_check = 1; - battle_config.hide_GM_session = 0; - battle_config.invite_request_check = 1; - battle_config.skill_removetrap_type = 0; - battle_config.disp_experience = 0; - battle_config.disp_zeny = 0; - battle_config.castle_defense_rate = 100; - battle_config.hp_rate = 100; - battle_config.sp_rate = 100; - battle_config.gm_cant_drop_min_lv = 1; - battle_config.gm_cant_drop_max_lv = 0; - battle_config.disp_hpmeter = 60; - battle_config.skill_wall_check = 1; - battle_config.cell_stack_limit = 1; - battle_config.bone_drop = 0; - battle_config.buyer_name = 1; - -// eAthena additions - battle_config.item_rate_mvp=100; - battle_config.item_rate_common = 100; - battle_config.item_rate_common_boss = 100; // [Reddozen] - battle_config.item_rate_equip = 100; - battle_config.item_rate_equip_boss = 100; // [Reddozen] - battle_config.item_rate_card = 100; - battle_config.item_rate_card_boss = 100; // [Reddozen] - battle_config.item_rate_heal = 100; // Added by Valaris - battle_config.item_rate_heal_boss = 100; // [Reddozen] - battle_config.item_rate_use = 100; // End - battle_config.item_rate_use_boss = 100; // [Reddozen] - battle_config.item_rate_adddrop = 100; - battle_config.item_rate_treasure = 100; - battle_config.logarithmic_drops = 0; - battle_config.item_drop_common_min=1; // Added by TyrNemesis^ - battle_config.item_drop_common_max=10000; - battle_config.item_drop_equip_min=1; - battle_config.item_drop_equip_max=10000; - battle_config.item_drop_card_min=1; - battle_config.item_drop_card_max=10000; - battle_config.item_drop_mvp_min=1; - battle_config.item_drop_mvp_max=10000; // End Addition - battle_config.item_drop_heal_min=1; // Added by Valaris - battle_config.item_drop_heal_max=10000; - battle_config.item_drop_use_min=1; - battle_config.item_drop_use_max=10000; // End - battle_config.item_drop_adddrop_min=1; - battle_config.item_drop_adddrop_max=10000; - battle_config.item_drop_treasure_min=1; - battle_config.item_drop_treasure_max=10000; - battle_config.prevent_logout = 10000; // Added by RoVeRT - battle_config.drops_by_luk = 0; // [Valaris] - battle_config.drops_by_luk2 = 0; - battle_config.equip_natural_break_rate = 1; - battle_config.equip_self_break_rate = 100; // [Valaris], adapted by [Skotlex] - battle_config.equip_skill_break_rate = 100; // [Valaris], adapted by [Skotlex] - battle_config.pk_mode = 0; // [Valaris] - battle_config.pk_level_range = 0; // [Skotlex] - battle_config.manner_system = 1; // [Valaris] - battle_config.pet_equip_required = 0; // [Valaris] - battle_config.multi_level_up = 0; // [Valaris] - battle_config.max_exp_gain_rate = 0; // [Skotlex] - battle_config.backstab_bow_penalty = 0; // Akaru - battle_config.night_at_start = 0; // added by [Yor] - battle_config.day_duration = 2*60*60*1000; // added by [Yor] (2 hours) - battle_config.night_duration = 30*60*1000; // added by [Yor] (30 minutes) - battle_config.show_mob_hp = 0; // [Valaris] - battle_config.ban_spoof_namer = 5; // added by [Yor] (default: 5 minutes) - battle_config.hack_info_GM_level = 60; // added by [Yor] (default: 60, GM level) - battle_config.any_warp_GM_min_level = 20; // added by [Yor] - battle_config.packet_ver_flag = 1023; // added by [Yor] - battle_config.min_hair_style = 0; - battle_config.max_hair_style = 23; - battle_config.min_hair_color = 0; - battle_config.max_hair_color = 9; - battle_config.min_cloth_color = 0; - battle_config.max_cloth_color = 4; - battle_config.pet_hair_style = 100; - battle_config.zeny_from_mobs = 0; - battle_config.mobs_level_up = 0; // [Valaris] - battle_config.mobs_level_up_exp_rate = 1; // [Valaris] - battle_config.pk_min_level = 55; - battle_config.skill_steal_type = 1; - battle_config.skill_steal_rate = 100; - battle_config.skill_steal_max_tries = 15; //=16 tries -// battle_config.night_darkness_level = 9; - battle_config.motd_type = 0; - battle_config.allow_atcommand_when_mute = 0; - battle_config.finding_ore_rate = 100; - battle_config.castrate_dex_scale = 150; - battle_config.area_size = 14; - battle_config.exp_calc_type = 1; - battle_config.min_skill_delay_limit = 100; - battle_config.default_skill_delay = 300; //Default skill delay according to official servers. - battle_config.require_glory_guild = 0; - battle_config.idle_no_share = 0; - battle_config.party_even_share_bonus = 0; - battle_config.delay_battle_damage = 1; - battle_config.hide_woe_damage = 0; - battle_config.display_version = 1; - battle_config.who_display_aid = 0; - battle_config.display_hallucination = 1; - battle_config.ignore_items_gender = 1; - battle_config.copyskill_restrict = 2; - battle_config.berserk_cancels_buffs = 1; - battle_config.debuff_on_logout = 1; - battle_config.use_statpoint_table = 1; - battle_config.mob_ai = 0; - battle_config.dynamic_mobs = 1; // use Dynamic Mobs [Wizputer] - battle_config.mob_remove_damaged = 1; // Dynamic Mobs - Remove mobs even if damaged [Wizputer] - battle_config.mob_remove_delay = 60000; - battle_config.show_hp_sp_drain = 0; //Display drained hp/sp from attacks - battle_config.show_hp_sp_gain = 1; //Display gained hp/sp from mob-kills - battle_config.mob_npc_event_type = 1; //Execute npc-event on player that delivered final blow. - battle_config.mob_clear_delay = 0; - battle_config.character_size = 3; //3: Peco riders Size=2, Baby Class Riders Size=1 [Lupus] - battle_config.mob_max_skilllvl = MAX_SKILL_LEVEL; //max possible level of monsters skills [Lupus] - battle_config.retaliate_to_master = 1; //Make mobs retaliate against the master rather than the mob that attacked them. [Skotlex] - battle_config.rare_drop_announce = 1; //show global announces for rare items drops (<= 0.01% chance) [Lupus] - battle_config.firewall_hits_on_undead = 1; - battle_config.title_lvl1 = 1; //Players Titles for @who, etc commands [Lupus] - battle_config.title_lvl2 = 10; - battle_config.title_lvl3 = 20; - battle_config.title_lvl4 = 40; - battle_config.title_lvl5 = 50; - battle_config.title_lvl6 = 60; - battle_config.title_lvl7 = 80; - battle_config.title_lvl8 = 99; - - battle_config.duel_enable = 1; - battle_config.duel_allow_pvp = 0; - battle_config.duel_allow_pvp = 0; - battle_config.duel_allow_teleport = 0; - battle_config.duel_autoleave_when_die = 1; - battle_config.duel_time_interval = 60; - - battle_config.skip_teleport_lv1_menu = 0; - battle_config.allow_skill_without_day = 0; - battle_config.allow_es_magic_pc = 0; - - battle_config.skill_caster_check = 1; - battle_config.sc_castcancel = 0; - battle_config.pc_sc_def_rate = 100; - battle_config.mob_sc_def_rate = 100; - battle_config.pc_max_sc_def = 10000; - battle_config.mob_max_sc_def = 5000; - battle_config.sg_miracle_skill_ratio=1; - battle_config.sg_miracle_skill_duration=600000; - battle_config.autospell_stacking = 0; - battle_config.override_mob_names = 0; -} - -void battle_validate_conf() { - if(battle_config.flooritem_lifetime < 1000) - battle_config.flooritem_lifetime = LIFETIME_FLOORITEM*1000; -/* if(battle_config.restart_hp_rate < 0) - battle_config.restart_hp_rate = 0; - else*/ if(battle_config.restart_hp_rate > 100) - battle_config.restart_hp_rate = 100; -/* if(battle_config.restart_sp_rate < 0) - battle_config.restart_sp_rate = 0; - else*/ if(battle_config.restart_sp_rate > 100) - battle_config.restart_sp_rate = 100; - if(battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL) - battle_config.natural_healhp_interval=NATURAL_HEAL_INTERVAL; - if(battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL) - battle_config.natural_healsp_interval=NATURAL_HEAL_INTERVAL; - if(battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL) - battle_config.natural_heal_skill_interval=NATURAL_HEAL_INTERVAL; - if(battle_config.natural_heal_weight_rate < 50) - battle_config.natural_heal_weight_rate = 50; - if(battle_config.natural_heal_weight_rate > 101) - battle_config.natural_heal_weight_rate = 101; - battle_config.monster_max_aspd = 2000 - battle_config.monster_max_aspd*10; - if(battle_config.monster_max_aspd < 10) - battle_config.monster_max_aspd = 10; - if(battle_config.monster_max_aspd > 1000) - battle_config.monster_max_aspd = 1000; - battle_config.max_aspd = 2000 - battle_config.max_aspd*10; - if(battle_config.max_aspd < 10) - battle_config.max_aspd = 10; - if(battle_config.max_aspd > 1000) - battle_config.max_aspd = 1000; - - if (battle_config.max_walk_speed < 100) - battle_config.max_walk_speed = 100; - battle_config.max_walk_speed = 100*DEFAULT_WALK_SPEED/battle_config.max_walk_speed; - if (battle_config.max_walk_speed < 1) - battle_config.max_walk_speed = 1; - - if(battle_config.hp_rate < 1) - battle_config.hp_rate = 1; - if(battle_config.sp_rate < 1) - battle_config.sp_rate = 1; - if(battle_config.max_hp > 1000000000) - battle_config.max_hp = 1000000000; - if(battle_config.max_hp < 100) - battle_config.max_hp = 100; - if(battle_config.max_sp > 1000000000) - battle_config.max_sp = 1000000000; - if(battle_config.max_sp < 100) - battle_config.max_sp = 100; - if(battle_config.max_parameter < 10) - battle_config.max_parameter = 10; - if(battle_config.max_parameter > 10000) - battle_config.max_parameter = 10000; - if(battle_config.max_baby_parameter < 10) - battle_config.max_baby_parameter = 10; - if(battle_config.max_baby_parameter > 10000) - battle_config.max_baby_parameter = 10000; - if(battle_config.max_cart_weight > 1000000) - battle_config.max_cart_weight = 1000000; - if(battle_config.max_cart_weight < 100) - battle_config.max_cart_weight = 100; - battle_config.max_cart_weight *= 10; - - if(battle_config.max_def > 100 && !battle_config.player_defense_type) // added by [Skotlex] - battle_config.max_def = 100; - if(battle_config.over_def_bonus > 1000) - battle_config.over_def_bonus = 1000; - - if(battle_config.min_hitrate > battle_config.max_hitrate) - battle_config.min_hitrate = battle_config.max_hitrate; - - if(battle_config.agi_penalty_count < 2) - battle_config.agi_penalty_count = 2; - if(battle_config.vit_penalty_count < 2) - battle_config.vit_penalty_count = 2; - - if(battle_config.guild_exp_limit > 99) - battle_config.guild_exp_limit = 99; -/* if(battle_config.guild_exp_limit < 0) - battle_config.guild_exp_limit = 0;*/ - - if(battle_config.pet_support_min_friendly > 950) //Capped to 950/1000 [Skotlex] - battle_config.pet_support_min_friendly = 950; - - if(battle_config.pet_hungry_delay_rate < 10) - battle_config.pet_hungry_delay_rate=10; - - if(battle_config.pet_max_atk1 > battle_config.pet_max_atk2) //Skotlex - battle_config.pet_max_atk1 = battle_config.pet_max_atk2; - -// if(battle_config.castle_defense_rate < 0) -// battle_config.castle_defense_rate = 0; - if(battle_config.castle_defense_rate > 100) - battle_config.castle_defense_rate = 100; - if(battle_config.item_drop_common_min < 1) // Added by TyrNemesis^ - battle_config.item_drop_common_min = 1; - if(battle_config.item_drop_common_max > 10000) - battle_config.item_drop_common_max = 10000; - if(battle_config.item_drop_equip_min < 1) - battle_config.item_drop_equip_min = 1; - if(battle_config.item_drop_equip_max > 10000) - battle_config.item_drop_equip_max = 10000; - if(battle_config.item_drop_card_min < 1) - battle_config.item_drop_card_min = 1; - if(battle_config.item_drop_card_max > 10000) - battle_config.item_drop_card_max = 10000; - if(battle_config.item_drop_mvp_min < 1) - battle_config.item_drop_mvp_min = 1; - if(battle_config.item_drop_mvp_max > 10000) - battle_config.item_drop_mvp_max = 10000; // End Addition - -/* if (battle_config.night_at_start < 0) // added by [Yor] - battle_config.night_at_start = 0; - else if (battle_config.night_at_start > 1) // added by [Yor] - battle_config.night_at_start = 1; */ - if (battle_config.day_duration != 0 && battle_config.day_duration < 60000) // added by [Yor] - battle_config.day_duration = 60000; - if (battle_config.night_duration != 0 && battle_config.night_duration < 60000) // added by [Yor] - battle_config.night_duration = 60000; - -/* if (battle_config.ban_spoof_namer < 0) // added by [Yor] - battle_config.ban_spoof_namer = 0; - else*/ if (battle_config.ban_spoof_namer > 32767) - battle_config.ban_spoof_namer = 32767; - -/* if (battle_config.hack_info_GM_level < 0) // added by [Yor] - battle_config.hack_info_GM_level = 0; - else*/ if (battle_config.hack_info_GM_level > 100) - battle_config.hack_info_GM_level = 100; - -/* if (battle_config.any_warp_GM_min_level < 0) // added by [Yor] - battle_config.any_warp_GM_min_level = 0; - else*/ if (battle_config.any_warp_GM_min_level > 100) - battle_config.any_warp_GM_min_level = 100; - -/* //This is a hassle to keep updated each time there's a new limit to packet_ver_flag.... [Skotlex] - // at least 1 client must be accepted - if ((battle_config.packet_ver_flag & 255) == 0) // added by [Yor] - battle_config.packet_ver_flag = 255; // accept all clients -*/ -/* Deprecated by dynamix's new night system (using SI_NIGHT) - if (battle_config.night_darkness_level <= 0) - battle_config.night_darkness_level = 9; - else if (battle_config.night_darkness_level > 10) // Celest - battle_config.night_darkness_level = 10; -*/ -/* if (battle_config.motd_type < 0) - battle_config.motd_type = 0; - else if (battle_config.motd_type > 1) - battle_config.motd_type = 1; -*/ -// if (battle_config.finding_ore_rate < 0) -// battle_config.finding_ore_rate = 0; - - if (battle_config.vending_max_value > MAX_ZENY || battle_config.vending_max_value==0) - battle_config.vending_max_value = MAX_ZENY; - - if (battle_config.min_skill_delay_limit < 10) - battle_config.min_skill_delay_limit = 10; // minimum delay of 10ms - - //Spawn delays [Skotlex] -/* if (battle_config.mob_spawn_delay < 0) - battle_config.mob_spawn_delay = 0; - if (battle_config.boss_spawn_delay < 0) - battle_config.boss_spawn_delay = 0; - if (battle_config.plant_spawn_delay < 0) - battle_config.plant_spawn_delay = 0; -*/ - if (battle_config.no_spawn_on_player > 50) - battle_config.no_spawn_on_player = 50; - if (battle_config.mob_remove_delay < 15000) //Min 15 sec - battle_config.mob_remove_delay = 15000; - if (battle_config.dynamic_mobs > 1) - battle_config.dynamic_mobs = 1; //The flag will be used in assignations - if (battle_config.mob_max_skilllvl> MAX_SKILL_LEVEL || battle_config.mob_max_skilllvl<1 ) - battle_config.mob_max_skilllvl = MAX_SKILL_LEVEL; - - if (battle_config.firewall_hits_on_undead < 1) - battle_config.firewall_hits_on_undead = 1; - else if (battle_config.firewall_hits_on_undead > 255) //The flag passed to battle_calc_damage is limited to 0xff - battle_config.firewall_hits_on_undead = 255; - - if (battle_config.prevent_logout > 60000) - battle_config.prevent_logout = 60000; - - if (battle_config.mobs_level_up_exp_rate < 1) // [Valaris] - battle_config.mobs_level_up_exp_rate = 1; - - if (battle_config.pc_max_sc_def > 10000) - battle_config.pc_max_sc_def = 10000; - if (battle_config.mob_max_sc_def > 10000) - battle_config.mob_max_sc_def = 10000; - if (battle_config.sg_miracle_skill_ratio > 10000) - battle_config.sg_miracle_skill_ratio = 10000; - - if (battle_config.skill_steal_max_tries > 254) - battle_config.skill_steal_max_tries = 254; - -#ifdef CELL_NOSTACK - if (battle_config.cell_stack_limit < 1) - battle_config.cell_stack_limit = 1; - else - if (battle_config.cell_stack_limit > 255) - battle_config.cell_stack_limit = 255; -#else - if (battle_config.cell_stack_limit != 1) - ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n"); -#endif -} - -/*========================================== - * ?ン定ファイルを読み?桙゙ - *------------------------------------------ - */ -int battle_config_read(const char *cfgName) -{ - char line[1024], w1[1024], w2[1024]; - FILE *fp; - static int count = 0; - - if ((count++) == 0) - battle_set_defaults(); - - fp = fopen(cfgName,"r"); - if (fp == NULL) { - ShowError("File not found: %s\n", cfgName); - return 1; - } - while(fgets(line,1020,fp)){ - if (line[0] == '/' && line[1] == '/') - continue; - if (sscanf(line, "%[^:]:%s", w1, w2) != 2) - continue; - if (strcmpi(w1, "import") == 0) - battle_config_read(w2); - else - battle_set_value(w1, w2); - } - fclose(fp); - - if (--count == 0) { - battle_validate_conf(); - add_timer_func_list(battle_delay_damage_sub, "battle_delay_damage_sub"); - } - - return 0; -} - -void do_init_battle(void) { - delay_damage_ers = ers_new((uint32)sizeof(struct delay_damage)); -} - -void do_final_battle(void) { - ers_destroy(delay_damage_ers); -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include + +#include "battle.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 "pc.h" +#include "status.h" +#include "skill.h" +#include "mob.h" +#include "itemdb.h" +#include "clif.h" +#include "pet.h" +#include "guild.h" +#include "party.h" + +#include "mercenary.h" + +#define is_boss(bl) status_get_mexp(bl) // Can refine later [Aru] + +int attr_fix_table[4][10][10]; + +struct Battle_Config battle_config; +static struct eri *delay_damage_ers; //For battle delay damage structures. + +int battle_getcurrentskill(struct block_list *bl) +{ //Returns the current/last skill in use by this bl. + struct unit_data *ud; + + if (bl->type == BL_SKILL) { + struct skill_unit * su = (struct skill_unit*)bl; + return su->group?su->group->skill_id:0; + } + ud = unit_bl2ud(bl); + return ud?ud->skillid:0; +} + +/*========================================== + * Get random targetting enemy + *------------------------------------------ + */ +static int battle_gettargeted_sub(struct block_list *bl, va_list ap) +{ + struct block_list **bl_list; + struct unit_data *ud; + int target_id; + int *c; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + bl_list = va_arg(ap, struct block_list **); + c = va_arg(ap, int *); + target_id = va_arg(ap, int); + + if (bl->id == target_id) + return 0; + if (*c >= 24) + return 0; + + ud = unit_bl2ud(bl); + if (!ud) return 0; + + if (ud->target == target_id || ud->skilltarget == target_id) { + bl_list[(*c)++] = bl; + return 1; + } + return 0; +} + +struct block_list* battle_gettargeted(struct block_list *target) +{ + struct block_list *bl_list[24]; + int c = 0; + nullpo_retr(NULL, target); + + memset(bl_list, 0, sizeof(bl_list)); + map_foreachinrange(battle_gettargeted_sub, target, AREA_SIZE, BL_CHAR, bl_list, &c, target->id); + if (c == 0 || c > 24) + return NULL; + return bl_list[rand()%c]; +} + + +//Returns the id of the current targetted character of the passed bl. [Skotlex] +int battle_gettarget(struct block_list *bl) +{ + switch (bl->type) + { + case BL_PC: + return ((struct map_session_data*)bl)->ud.target; + case BL_MOB: + return ((struct mob_data*)bl)->target_id; + case BL_PET: + return ((struct pet_data*)bl)->target_id; + } + return 0; +} +// ダ??[ジの遅延 +struct delay_damage { + struct block_list *src; + int target; + int damage; + int delay; + unsigned short distance; + unsigned short skill_lv; + unsigned short skill_id; + unsigned short dmg_lv; + unsigned char attack_type; +}; + +int battle_delay_damage_sub (int tid, unsigned int tick, int id, int data) +{ + struct delay_damage *dat = (struct delay_damage *)data; + struct block_list *target = map_id2bl(dat->target); + if (target && dat && map_id2bl(id) == dat->src && target->prev != NULL && !status_isdead(target) && + target->m == dat->src->m && check_distance_bl(dat->src, target, dat->distance)) //Check to see if you haven't teleported. [Skotlex] + { + status_fix_damage(dat->src, target, dat->damage, dat->delay); + if ((dat->dmg_lv == ATK_DEF || dat->damage > 0) && dat->attack_type) + { + if (!status_isdead(target)) + skill_additional_effect(dat->src,target,dat->skill_id,dat->skill_lv,dat->attack_type, tick); + skill_counter_additional_effect(dat->src,target,dat->skill_id,dat->skill_lv,dat->attack_type,tick); + } + + } + ers_free(delay_damage_ers, dat); + return 0; +} + +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) +{ + struct delay_damage *dat; + nullpo_retr(0, src); + nullpo_retr(0, target); + + if (!battle_config.delay_battle_damage) { + status_fix_damage(src, target, damage, ddelay); + if ((damage > 0 || dmg_lv == ATK_DEF) && attack_type) + { + if (!status_isdead(target)) + skill_additional_effect(src, target, skill_id, skill_lv, attack_type, gettick()); + skill_counter_additional_effect(src, target, skill_id, skill_lv, attack_type, gettick()); + } + return 0; + } + dat = ers_alloc(delay_damage_ers, struct delay_damage); + dat->src = src; + dat->target = target->id; + dat->skill_id = skill_id; + dat->skill_lv = skill_lv; + dat->attack_type = attack_type; + dat->damage = damage; + dat->dmg_lv = dmg_lv; + dat->delay = ddelay; + dat->distance = distance_bl(src, target)+10; //Attack should connect regardless unless you teleported. + add_timer(tick, battle_delay_damage_sub, src->id, (int)dat); + + return 0; +} +/*========================================== + * Does attribute fix modifiers. + * Added passing of the chars so that the status changes can affect it. [Skotlex] + * Note: Passing src/target == NULL is perfectly valid, it skips SC_ checks. + *------------------------------------------ + */ +int battle_attr_fix(struct block_list *src, struct block_list *target, int damage,int atk_elem,int def_type, int def_lv) +{ + struct status_change *sc=NULL, *tsc=NULL; + int ratio; + + if (src) sc = status_get_sc(src); + if (target) tsc = status_get_sc(target); + + if (atk_elem < 0 || atk_elem >= ELE_MAX) + atk_elem = rand()%ELE_MAX; + + if (def_type < 0 || def_type > ELE_MAX || + def_lv < 1 || def_lv > 4) { + if (battle_config.error_log) + ShowError("battle_attr_fix: unknown attr type: atk=%d def_type=%d def_lv=%d\n",atk_elem,def_type,def_lv); + //TODO: Remove this debug case once the cause is resolved. [Skotlex] + if (src) switch (src->type) { + case BL_MOB: + ShowDebug("src: Mob %s-%d\n", ((struct mob_data*)src)->name, ((struct mob_data*)src)->class_); + break; + case BL_PC: + ShowDebug("src: Player %s-%d\n", ((struct map_session_data*)src)->status.name, ((struct map_session_data*)src)->bl.id); + break; + case BL_PET: + ShowDebug("src: Pet %s-%d\n", ((struct pet_data*)src)->name, ((struct pet_data*)src)->bl.id); + break; + case BL_SKILL: + ShowDebug("src: Ground Skill id: %d\n", ((struct skill_unit*)src)->group->skill_id); + break; + default: + ShowDebug("unknown source type %d.\n", src->type); + } + if (target) switch (target->type) { + case BL_MOB: + ShowDebug("target: Mob %s-%d\n", ((struct mob_data*)target)->name, ((struct mob_data*)target)->class_); + break; + case BL_PC: + ShowDebug("target: Player %s-%d\n", ((struct map_session_data*)target)->status.name, ((struct map_session_data*)target)->bl.id); + break; + case BL_PET: + ShowDebug("target: Pet %s-%d\n", ((struct pet_data*)target)->name, ((struct pet_data*)target)->bl.id); + break; + case BL_SKILL: + ShowDebug("target: Ground Skill id: %d\n", ((struct skill_unit*)target)->group->skill_id); + break; + default: + ShowDebug("unknown target type %d.\n", target->type); + } + return damage; + } + + ratio = attr_fix_table[def_lv-1][atk_elem][def_type]; + if (sc && sc->count) + { + if(sc->data[SC_VOLCANO].timer!=-1 && atk_elem == 3) + ratio += enchant_eff[sc->data[SC_VOLCANO].val1-1]; + if(sc->data[SC_VIOLENTGALE].timer!=-1 && atk_elem == 4) + ratio += enchant_eff[sc->data[SC_VIOLENTGALE].val1-1]; + if(sc->data[SC_DELUGE].timer!=-1 && atk_elem == 1) + ratio += enchant_eff[sc->data[SC_DELUGE].val1-1]; + } + if (tsc && tsc->count) + { + if(tsc->data[SC_ARMOR_ELEMENT].timer!=-1) + { + if (tsc->data[SC_ARMOR_ELEMENT].val1 == atk_elem) + ratio -= tsc->data[SC_ARMOR_ELEMENT].val2; + else + if (tsc->data[SC_ARMOR_ELEMENT].val3 == atk_elem) + ratio -= tsc->data[SC_ARMOR_ELEMENT].val4; + } + } + return damage*ratio/100; +} + +/*========================================== + * ダ??[ジ?ナ?I計算 + *------------------------------------------ + */ +int battle_calc_damage(struct block_list *src,struct block_list *bl,int damage,int div_,int skill_num,int skill_lv,int flag) +{ + struct map_session_data *sd = NULL; + struct mob_data *md = NULL; + struct status_change *sc; + struct status_change_entry *sci; + + nullpo_retr(0, bl); + + if (damage <= 0) + return 0; + + if (bl->type == BL_MOB) { + md=(struct mob_data *)bl; + } else if (bl->type == BL_PC) { + sd=(struct map_session_data *)bl; + } + + sc = status_get_sc(bl); + + if(flag&BF_LONG && map_getcell(bl->m, bl->x, bl->y, CELL_CHKPNEUMA) && + ((flag&BF_WEAPON && skill_num != NPC_GUIDEDATTACK) || + (flag&BF_MISC && skill_num != PA_PRESSURE) + )){ + return 0; + } + + if (sc && sc->count) { + //First, sc_*'s that reduce damage to 0. + if (sc->data[SC_SAFETYWALL].timer!=-1 && flag&BF_SHORT && (skill_num != NPC_GUIDEDATTACK && skill_num != AM_DEMONSTRATION) + ) { + // セ?[フティウォ?[ル + struct skill_unit_group *group = (struct skill_unit_group *)sc->data[SC_SAFETYWALL].val3; + if (group) { + if (--group->val2<=0) + skill_delunitgroup(NULL,group); + return 0; + } else { + status_change_end(bl,SC_SAFETYWALL,-1); + } + } + + if(sc->data[SC_LANDPROTECTOR].timer!=-1 && flag&BF_MAGIC) + return 0; + + if(sc->data[SC_AUTOGUARD].timer != -1 && flag&BF_WEAPON && + rand()%100 < sc->data[SC_AUTOGUARD].val2) { + int delay; + clif_skill_nodamage(bl,bl,CR_AUTOGUARD,sc->data[SC_AUTOGUARD].val1,1); + // different delay depending on skill level [celest] + if (sc->data[SC_AUTOGUARD].val1 <= 5) + delay = 300; + else if (sc->data[SC_AUTOGUARD].val1 > 5 && sc->data[SC_AUTOGUARD].val1 <= 9) + delay = 200; + else + delay = 100; + unit_set_walkdelay(bl, gettick(), delay, 1); + + if(sc->data[SC_SHRINK].timer != -1 && rand()%100<5*sc->data[SC_AUTOGUARD].val1) + skill_blown(bl,src,skill_get_blewcount(CR_SHRINK,1)); + return 0; + } + +// -- moonsoul (chance to block attacks with new Lord Knight skill parrying) +// + if(sc->data[SC_PARRYING].timer != -1 && flag&BF_WEAPON && + rand()%100 < sc->data[SC_PARRYING].val2) { + clif_skill_nodamage(bl,bl,LK_PARRYING,sc->data[SC_PARRYING].val1,1); + return 0; + } + + if(sc->data[SC_DODGE].timer != -1 && !sc->opt1 && + (flag&BF_LONG || sc->data[SC_SPURT].timer != -1) + && rand()%100 < 20) { + clif_skill_nodamage(bl,bl,TK_DODGE,1,1); + if (sc->data[SC_COMBO].timer == -1) + sc_start4(bl, SC_COMBO, 100, TK_JUMPKICK, src->id, 0, 0, 2000); + return 0; + } + + if(sc->data[SC_FOGWALL].timer != -1 && flag&BF_MAGIC + && rand()%100 < 75 && !(skill_get_inf(skill_num)&INF_GROUND_SKILL)) + return 0; + + if(sc->data[SC_KAUPE].timer != -1 && + rand()%100 < sc->data[SC_KAUPE].val2 && + (src->type == BL_PC || !skill_num)) + { //Kaupe only blocks all skills of players. + clif_skill_nodamage(bl,bl,SL_KAUPE,1,1); + if (--sc->data[SC_KAUPE].val3 <= 0) //We make it work like Safety Wall, even though it only blocks 1 time. + status_change_end(bl, SC_KAUPE, -1); + return 0; + } + + //Now damage increasing effects + if(sc->data[SC_AETERNA].timer!=-1 && skill_num != PA_PRESSURE && skill_num != PF_SOULBURN){ + damage<<=1; + status_change_end( bl,SC_AETERNA,-1 ); + } + + if(sc->data[SC_SPIDERWEB].timer!=-1) // [Celest] + if ((flag&BF_SKILL && skill_get_pl(skill_num)==ELE_FIRE) || + (!flag&BF_SKILL && status_get_attack_element(src)==ELE_FIRE)) { + damage<<=1; + status_change_end(bl, SC_SPIDERWEB, -1); + } + + //Finally damage reductions.... + if(sc->data[SC_ASSUMPTIO].timer != -1){ + if(map_flag_vs(bl->m)) + damage=damage*2/3; //Receive 66% damage + else + damage>>=1; //Receive 50% damage + } + + if(sc->data[SC_DEFENDER].timer != -1 && flag&BF_LONG && flag&BF_WEAPON) + damage=damage*(100-sc->data[SC_DEFENDER].val2)/100; + + if(sc->data[SC_FOGWALL].timer != -1 && flag&BF_LONG && flag&BF_WEAPON) + damage >>=1; + + if(sc->data[SC_ENERGYCOAT].timer!=-1 && flag&BF_WEAPON){ + struct status_data *status = status_get_status_data(bl); + int per = 100*status->sp / status->max_sp; + per /=20; //Uses 20% SP intervals. + //SP Cost: 1% + 0.5% per every 20% SP + if (!status_charge(bl, 0, (10+5*per)*status->max_sp/10000)) + status_change_end( bl,SC_ENERGYCOAT,-1 ); + //Reduction: 6% + 6% every 20% + damage -= damage * 6 * (1+per) / 100; + } + + if(sc->data[SC_REJECTSWORD].timer!=-1 && flag&BF_WEAPON && + // Fixed the condition check [Aalye] + (src->type!=BL_PC || ( + ((TBL_PC *)src)->status.weapon == W_DAGGER || + ((TBL_PC *)src)->status.weapon == W_1HSWORD || + ((TBL_PC *)src)->status.weapon == W_2HSWORD + )) + ){ + if(rand()%100 < sc->data[SC_REJECTSWORD].val2){ + damage = damage*50/100; + status_fix_damage(bl,src,damage,clif_damage(bl,src,gettick(),0,0,damage,0,0,0)); + clif_skill_nodamage(bl,bl,ST_REJECTSWORD,sc->data[SC_REJECTSWORD].val1,1); + if((--sc->data[SC_REJECTSWORD].val3)<=0) + status_change_end(bl, SC_REJECTSWORD, -1); + } + } + + //Finally Kyrie because it may, or not, reduce damage to 0. + if(sc->data[SC_KYRIE].timer!=-1){ + sci=&sc->data[SC_KYRIE]; + sci->val2-=damage; + if(flag&BF_WEAPON || skill_num == TF_THROWSTONE){ + if(sci->val2>=0) + damage=0; + else + damage=-sci->val2; + } + if((--sci->val3)<=0 || (sci->val2<=0) || skill_num == AL_HOLYLIGHT) + status_change_end(bl, SC_KYRIE, -1); + } + if (damage <= 0) return 0; + } + + //SC effects from caster side. + sc = status_get_sc(src); + if (sc && sc->count) { + if(sc->data[SC_FOGWALL].timer != -1 && flag&(BF_LONG|BF_MAGIC)) { + if (flag&BF_MAGIC) { + if(!(skill_get_inf(skill_num)&INF_GROUND_SKILL) && rand()%100 < 75) + return 0; + } else if (flag&BF_WEAPON) + damage >>=1; + } + } + + if (battle_config.pk_mode && sd && damage > 0) + { + if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex] + if (flag&BF_WEAPON) + damage = damage * battle_config.pk_weapon_damage_rate/100; + if (flag&BF_MAGIC) + damage = damage * battle_config.pk_magic_damage_rate/100; + if (flag&BF_MISC) + damage = damage * battle_config.pk_misc_damage_rate/100; + } else { //Normal attacks get reductions based on range. + if (flag & BF_SHORT) + damage = damage * battle_config.pk_short_damage_rate/100; + if (flag & BF_LONG) + damage = damage * battle_config.pk_long_damage_rate/100; + } + if(damage < 1) damage = 1; + } + + if(battle_config.skill_min_damage && damage > 0 && damage < div_) + { + if ((flag&BF_WEAPON && battle_config.skill_min_damage&1) + || (flag&BF_MAGIC && battle_config.skill_min_damage&2) + || (flag&BF_MISC && battle_config.skill_min_damage&4) + ) + damage = div_; + } + + if( md && !status_isdead(bl) && src != bl) { + if (damage > 0 ) + mobskill_event(md,src,gettick(),flag); + if (skill_num) + mobskill_event(md,src,gettick(),MSC_SKILLUSED|(skill_num<<16)); + } + + return damage; +} + +/*========================================== + * Calculates GVG related damage adjustments. + *------------------------------------------ + */ +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) +{ + struct mob_data *md = NULL; + int class_; + + if (damage <= 0) + return 0; + + class_ = status_get_class(bl); + + if (bl->type == BL_MOB) + md=(struct mob_data *)bl; + + if(md && md->guardian_data) { + if(class_ == MOBID_EMPERIUM && flag&BF_SKILL) + //SKill inmunity. + switch (skill_num) { + case PA_PRESSURE: + case MO_TRIPLEATTACK: + case HW_GRAVITATION: + break; + default: + return 0; + } + if(src->type != BL_MOB) { + struct guild *g=guild_search(status_get_guild_id(src)); + if (!g) return 0; + if (class_ == MOBID_EMPERIUM && guild_checkskill(g,GD_APPROVAL) <= 0) + return 0; + if (battle_config.guild_max_castles && + guild_checkcastles(g)>=battle_config.guild_max_castles) + return 0; // [MouseJstr] + } + } + + switch (skill_num) { + //Skills with no damage reduction. + case PA_PRESSURE: + case HW_GRAVITATION: + break; + default: + if (md && md->guardian_data) { + damage -= damage + * (md->guardian_data->castle->defense/100) + * (battle_config.castle_defense_rate/100); + } + if (flag & BF_SKILL) { //Skills get a different reduction than non-skills. [Skotlex] + if (flag&BF_WEAPON) + damage = damage * battle_config.gvg_weapon_damage_rate/100; + if (flag&BF_MAGIC) + damage = damage * battle_config.gvg_magic_damage_rate/100; + if (flag&BF_MISC) + damage = damage * battle_config.gvg_misc_damage_rate/100; + } else { //Normal attacks get reductions based on range. + if (flag & BF_SHORT) + damage = damage * battle_config.gvg_short_damage_rate/100; + if (flag & BF_LONG) + damage = damage * battle_config.gvg_long_damage_rate/100; + } + if(damage < 1) damage = 1; + } + return damage; +} + +/*========================================== + * HP/SP吸収の計算 + *------------------------------------------ + */ +static int battle_calc_drain(int damage, int rate, int per) +{ + int diff = 0; + + if (per && rand()%1000 < rate) { + diff = (damage * per) / 100; + if (diff == 0) { + if (per > 0) + diff = 1; + else + diff = -1; + } + } + return diff; +} + +/*========================================== + * ?C練ダ??[ジ + *------------------------------------------ + */ +int battle_addmastery(struct map_session_data *sd,struct block_list *target,int dmg,int type) +{ + int damage,skill; + struct status_data *status = status_get_status_data(target); + int weapon; + damage = dmg; + + nullpo_retr(0, sd); + + if((skill = pc_checkskill(sd,AL_DEMONBANE)) > 0 && + (battle_check_undead(status->race,status->def_ele) || status->race==RC_DEMON) ) + damage += (skill*(int)(3+(sd->status.base_level+1)*0.05)); // submitted by orn + //damage += (skill * 3); + + if((skill = pc_checkskill(sd,HT_BEASTBANE)) > 0 && (status->race==RC_BRUTE || status->race==RC_INSECT) ) { + damage += (skill * 4); + if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_HUNTER) + damage += sd->status.str; + } + + if(type == 0) + weapon = sd->weapontype1; + else + weapon = sd->weapontype2; + switch(weapon) + { + case W_DAGGER: + case W_1HSWORD: + if((skill = pc_checkskill(sd,SM_SWORD)) > 0) + damage += (skill * 4); + break; + case W_2HSWORD: + if((skill = pc_checkskill(sd,SM_TWOHAND)) > 0) + damage += (skill * 4); + break; + case W_1HSPEAR: + case W_2HSPEAR: + if((skill = pc_checkskill(sd,KN_SPEARMASTERY)) > 0) { + if(!pc_isriding(sd)) + damage += (skill * 4); + else + damage += (skill * 5); + } + break; + case W_1HAXE: + case W_2HAXE: + if((skill = pc_checkskill(sd,AM_AXEMASTERY)) > 0) + damage += (skill * 3); + break; + case W_MACE: + if((skill = pc_checkskill(sd,PR_MACEMASTERY)) > 0) + damage += (skill * 3); + break; + case W_FIST: + case W_KNUCKLE: + if((skill = pc_checkskill(sd,MO_IRONHAND)) > 0) + damage += (skill * 3); + break; + case W_MUSICAL: + if((skill = pc_checkskill(sd,BA_MUSICALLESSON)) > 0) + damage += (skill * 3); + break; + case W_WHIP: + // Dance Lesson Skill Effect(+3 damage for every lvl = +30) + if((skill = pc_checkskill(sd,DC_DANCINGLESSON)) > 0) + damage += (skill * 3); + break; + case W_BOOK: + // Advance Book Skill Effect(+3 damage for every lvl = +30) + if((skill = pc_checkskill(sd,SA_ADVANCEDBOOK)) > 0) + damage += (skill * 3); + break; + case W_KATAR: + if((skill = pc_checkskill(sd,ASC_KATAR)) > 0) + //Advanced Katar Research by zanetheinsane + damage += damage*(10 +skill * 2)/100; + if((skill = pc_checkskill(sd,AS_KATAR)) > 0) + damage += (skill * 3); + break; + } +/*//need to add this on shuriken skills. + if((skill = pc_checkskill(sd,NJ_TOBIDOUGU)) > 0) { + damage += (skill * 3); + } +*/ + return damage; +} +/*========================================== + * Calculates the standard damage of a normal attack assuming it hits, + * it calculates nothing extra fancy, is needed for magnum break's WATK_ELEMENT bonus. [Skotlex] + *------------------------------------------ + * Pass damage2 as NULL to not calc it. + * Flag values: + * &1: Critical hit + * &2: Arrow attack + * &4: Skill is Magic Crasher + * &8: Skip target size adjustment (Extremity Fist?) + */ +static int battle_calc_base_damage(struct status_data *status, struct weapon_atk *wa, struct status_change *sc, unsigned short t_size, struct map_session_data *sd, int flag) +{ + unsigned short atkmin=0, atkmax=0; + short type; + int damage = 0; + + if (!sd) + { //Mobs/Pets + if(flag&4) + { + atkmin = status->matk_min; + atkmax = status->matk_max; + } else { + atkmin = wa->atk; + atkmax = wa->atk2; + } + if (atkmin > atkmax) + atkmin = atkmax; + } else { //PCs + atkmax = wa->atk; + + if (!(flag&1) || (flag&2)) + { //Normal attacks + atkmin = status->dex; + + type = (wa == status->lhw)?8:9; + if (sd->equip_index[type] >= 0 && sd->inventory_data[sd->equip_index[type]]) + atkmin = atkmin*(80 + sd->inventory_data[sd->equip_index[type]]->wlv*20)/100; + + if (atkmin > atkmax) + atkmin = atkmax; + + if(flag&2) + { //Bows + atkmin = atkmin*atkmax/100; + if (atkmin > atkmax) + atkmax = atkmin; + } + } + } + + if (sc && sc->data[SC_MAXIMIZEPOWER].timer!=-1) + atkmin = atkmax; + + //Weapon Damage calculation + if (!(flag&1)) + damage = (atkmax>atkmin? rand()%(atkmax-atkmin):0)+atkmin; + else + damage = atkmax; + + if (sd) + { + //rodatazone says the range is 0~arrow_atk-1 for non crit + if (flag&2 && sd->arrow_atk) + damage += ((flag&1)?sd->arrow_atk:rand()%sd->arrow_atk); + + //SizeFix only for players + if (!( + sd->special_state.no_sizefix || + (sc && sc->data[SC_WEAPONPERFECTION].timer!=-1) || + (pc_isriding(sd) && (sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR) && t_size==1) || + (flag&8) + )) + damage = damage*(sd->right_weapon.atkmods[t_size])/100; + } + + //Finally, add baseatk + if(flag&4) + damage += status->matk_min; + else + damage += status->batk; + + //rodatazone says that Overrefine bonuses are part of baseatk + if(sd) { + type = (wa == status->lhw)?sd->left_weapon.overrefine:sd->right_weapon.overrefine; + if (type > 0) + damage += rand()%type+1; + } + return damage; +} + +/*========================================== + * Consumes ammo for the given skill. + *------------------------------------------ + */ +void battle_consume_ammo(TBL_PC*sd, int skill, int lv) +{ + int qty=1; + if (!battle_config.arrow_decrement) + return; + + if (skill) + { + qty = skill_get_ammo_qty(skill, lv); + if (!qty) { //Generic skill that consumes ammo? + qty = skill_get_num(skill, lv); + if (qty < 0) qty *= -1; + else + if (qty == 0) qty = 1; + } + } + if(sd->equip_index[10]>=0) //Qty check should have been done in skill_check_condition + pc_delitem(sd,sd->equip_index[10],qty,0); +} + +//For quick div adjustment. +#define damage_div_fix(dmg, div) { if (div > 1) (dmg)*=div; else if (div < 0) (div)*=-1; } +/*========================================== + * battle_calc_weapon_attack (by Skotlex) + *------------------------------------------ + */ +static struct Damage battle_calc_weapon_attack( + struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int wflag) +{ + unsigned short skillratio = 100; //Skill dmg modifiers. + short skill=0; + short s_ele, s_ele_, t_class; + short i; + + struct map_session_data *sd, *tsd; + struct Damage wd; + struct status_change *sc = status_get_sc(src); + struct status_change *tsc = status_get_sc(target); + struct status_data *sstatus = status_get_status_data(src); + struct status_data *tstatus = status_get_status_data(target); + struct { + unsigned hit : 1; //the attack Hit? (not a miss) + unsigned cri : 1; //Critical hit + unsigned idef : 1; //Ignore defense + unsigned idef2 : 1; //Ignore defense (left weapon) + unsigned pdef : 2; //Pierces defense (Investigate/Ice Pick) + unsigned pdef2 : 2; //1: Use def+def2/50, 2: Use def+def2/100 + unsigned infdef : 1; //Infinite defense (plants) + unsigned arrow : 1; //Attack is arrow-based + unsigned rh : 1; //Attack considers right hand (wd.damage) + unsigned lh : 1; //Attack considers left hand (wd.damage2) + unsigned weapon : 1; //It's a weapon attack (consider VVS, and all that) + unsigned cardfix : 1; + } flag; + + memset(&wd,0,sizeof(wd)); + memset(&flag,0,sizeof(flag)); + + if(src==NULL || target==NULL) + { + nullpo_info(NLP_MARK); + return wd; + } + //Initial flag + flag.rh=1; + flag.weapon=1; + flag.cardfix=1; + flag.infdef=(tstatus->mode&MD_PLANT?1:0); + + //Initial Values + wd.type=0; //Normal attack + wd.div_=skill_num?skill_get_num(skill_num,skill_lv):1; + wd.amotion=(skill_num && skill_get_inf(skill_num)&INF_GROUND_SKILL)?0:sstatus->amotion; //Amotion should be 0 for ground skills. + if(skill_num == KN_AUTOCOUNTER) + wd.amotion >>= 1; + wd.dmotion=tstatus->dmotion; + wd.blewcount=skill_get_blewcount(skill_num,skill_lv); + wd.flag=BF_SHORT|BF_WEAPON|BF_NORMAL; //Initial Flag + wd.dmg_lv=ATK_DEF; //This assumption simplifies the assignation later + + if (sc && !sc->count) + sc = NULL; //Skip checking as there are no status changes active. + if (tsc && !tsc->count) + tsc = NULL; //Skip checking as there are no status changes active. + + BL_CAST(BL_PC, src, sd); + BL_CAST(BL_PC, target, tsd); + + if(sd) { + if (sd->skillblown[0].id != 0) + { //Apply the bonus blewcount. [Skotlex] + for (i = 0; i < 5 && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++); + if (i < 5 && sd->skillblown[i].id == skill_num) + wd.blewcount += sd->skillblown[i].val; + } + } + //Set miscellaneous data that needs be filled regardless of hit/miss + if( + (sd && sd->state.arrow_atk) || + (!sd && ((skill_num && skill_get_ammotype(skill_num)) || status_get_range(src)>3)) + ) { + wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; + flag.arrow = 1; + } + + if(skill_num){ + wd.flag=(wd.flag&~BF_SKILLMASK)|BF_SKILL; + switch(skill_num) + { + case MO_FINGEROFFENSIVE: + if(sd) { + if (battle_config.finger_offensive_type) + wd.div_ = 1; + else + wd.div_ = sd->spiritball_old; + } + wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; + break; + + case CR_SHIELDBOOMERANG: + case PA_SHIELDCHAIN: + flag.weapon = 0; + case AS_GRIMTOOTH: + case KN_SPEARBOOMERANG: + case NPC_RANGEATTACK: + case LK_SPIRALPIERCE: + case ASC_BREAKER: + case AM_ACIDTERROR: + case ITM_TOMAHAWK: //Tomahawk is a ranged attack! [Skotlex] + case CR_GRANDCROSS: + case NPC_GRANDDARKNESS: + wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; + break; + + case KN_PIERCE: + wd.div_= (wd.div_>0?tstatus->size+1:-(tstatus->size+1)); + break; + + case TF_DOUBLE: //For NPC used skill. + wd.type = 0x08; + break; + + case KN_SPEARSTAB: + case KN_BOWLINGBASH: + case MO_BALKYOUNG: + case TK_TURNKICK: + wd.blewcount=0; + break; + + case CR_SHIELDCHARGE: +// flag.weapon = 0; + case NPC_PIERCINGATT: + wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT; + break; + + case KN_AUTOCOUNTER: + wd.flag=(wd.flag&~BF_SKILLMASK)|BF_NORMAL; + break; + } + } + + if (skill_num && battle_config.skillrange_by_distance && + (src->type&battle_config.skillrange_by_distance) + ) { //Skill range based on distance between src/target [Skotlex] + if (check_distance_bl(src, target, 3)) + wd.flag=(wd.flag&~BF_RANGEMASK)|BF_SHORT; + else + wd.flag=(wd.flag&~BF_RANGEMASK)|BF_LONG; + } + + if(is_boss(target)) //Bosses can't be knocked-back + wd.blewcount = 0; + +/* Apparently counter attack no longer causes you to be critical'ed by mobs. [Skotlex] + //Check for counter + if(!skill_num) + { + if(tsc && tsc->data[SC_AUTOCOUNTER].timer != -1) + //If it got here and you had autocounter active, then the direction/range does not matches: critical + flag.cri = 1; + } //End counter-check +*/ + if (!skill_num && tstatus->flee2 && rand()%1000 < tstatus->flee2) + { //Check for Lucky Dodge + wd.type=0x0b; + wd.dmg_lv=ATK_LUCKY; + if (wd.div_ < 0) wd.div_*=-1; + return wd; + } + + t_class = status_get_class(target); + s_ele = s_ele_ = skill_get_pl(skill_num); + if (!skill_num || s_ele == -1) { //Take weapon's element + s_ele = sstatus->rhw.ele; + s_ele_ = sstatus->lhw?sstatus->lhw->ele:0; + if (flag.arrow && sd && sd->arrow_ele) + s_ele = sd->arrow_ele; + } else if (s_ele == -2) { //Use enchantment's element + s_ele = s_ele_ = status_get_attack_sc_element(src,sc); + } + + if (sd && sd->weapontype1 == 0 && sd->weapontype2 > 0) + { + flag.rh=0; + flag.lh=1; + } + if (sstatus->lhw && sstatus->lhw->atk) + flag.lh=1; + + //Check for critical + if(!flag.cri && sstatus->cri && + (!skill_num || skill_num == KN_AUTOCOUNTER || skill_num == SN_SHARPSHOOTING || skill_num == NJ_KIRIKAGE)) + { + short cri = sstatus->cri; + if (sd) + { + cri+= sd->critaddrace[tstatus->race]; + if(flag.arrow) + cri += sd->arrow_cri; + if(sd->status.weapon == W_KATAR) + cri <<=1; + } + //The official equation is *2, but that only applies when sd's do critical. + //Therefore, we use the old value 3 on cases when an sd gets attacked by a mob + cri -= tstatus->luk*(!sd&&tsd?3:2); + + if(tsc) + { + if (tsc->data[SC_SLEEP].timer!=-1 ) + cri <<=1; + if(tsc->data[SC_JOINTBEAT].timer != -1 && + tsc->data[SC_JOINTBEAT].val2 == 6) // Always take crits with Neck broken by Joint Beat [DracoRPG] + flag.cri=1; + } + switch (skill_num) + { + case KN_AUTOCOUNTER: + if(battle_config.auto_counter_type && + (battle_config.auto_counter_type&src->type)) + flag.cri = 1; + else + cri <<= 1; + break; + case SN_SHARPSHOOTING: + cri += 200; + break; + case NJ_KIRIKAGE: + cri += 250 + 50*skill_lv; + break; + } + if(tsd && tsd->critical_def) + cri = cri*(100-tsd->critical_def)/100; + if (rand()%1000 < cri) + flag.cri= 1; + } + if (flag.cri) + { + wd.type = 0x0a; + flag.idef = flag.idef2 = flag.hit = 1; + } else { //Check for Perfect Hit + if(sd && sd->perfect_hit > 0 && rand()%100 < sd->perfect_hit) + flag.hit = 1; + if (sc && sc->data[SC_FUSION].timer != -1) { + flag.hit = 1; //SG_FUSION always hit [Komurka] + flag.idef = flag.idef2 = 1; //def ignore [Komurka] + } + if (skill_num && !flag.hit) + switch(skill_num) + { + case AS_SPLASHER: //Reports say it always hits? + if (wflag) //Only if you were the one exploding. + break; + case NPC_GUIDEDATTACK: + case RG_BACKSTAP: + case HT_FREEZINGTRAP: + case AM_ACIDTERROR: + case MO_INVESTIGATE: + case MO_EXTREMITYFIST: + case CR_GRANDCROSS: + case NPC_GRANDDARKNESS: + case PA_SACRIFICE: + case TK_COUNTER: + case SG_SUN_WARM: + case SG_MOON_WARM: + case SG_STAR_WARM: + case NPC_BLOODDRAIN: + flag.hit = 1; + break; + case CR_SHIELDBOOMERANG: + if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_CRUSADER) + flag.hit = 1; + break; + } + if (tsc && !flag.hit && tsc->opt1 && tsc->opt1 != OPT1_STONEWAIT) + flag.hit = 1; + } + + if (!flag.hit) + { //Hit/Flee calculation + short + flee = tstatus->flee, + hitrate=80; //Default hitrate + + if(battle_config.agi_penalty_type) + { + unsigned char target_count; //256 max targets should be a sane max + target_count = unit_counttargeted(target,battle_config.agi_penalty_count_lv); + if(target_count >= battle_config.agi_penalty_count) + { + if (battle_config.agi_penalty_type == 1) + flee = (flee * (100 - (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num))/100; + else //asume type 2: absolute reduction + flee -= (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num; + if(flee < 1) flee = 1; + } + } + + hitrate+= sstatus->hit - flee; + + if(wd.flag&BF_LONG && ( + (sc && sc->data[SC_FOGWALL].timer!=-1) || + (tsc && tsc->data[SC_FOGWALL].timer!=-1))) + hitrate-=50; + + if(sd && flag.arrow) + hitrate += sd->arrow_hit; + if(skill_num) + switch(skill_num) + { //Hit skill modifiers + case SM_BASH: + hitrate += 5*skill_lv; + break; + case SM_MAGNUM: + hitrate += 10*skill_lv; + break; + case KN_AUTOCOUNTER: + hitrate += 20; + break; + case KN_PIERCE: + hitrate += hitrate*(5*skill_lv)/100; + break; + case PA_SHIELDCHAIN: + hitrate += 20; + break; + case AS_SONICBLOW: + if(sd && pc_checkskill(sd,AS_SONICACCEL)>0) + hitrate += 50; + break; + } + + // Weaponry Research hidden bonus + if (sd && (skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0) + hitrate += hitrate*(2*skill)/100; + + if (hitrate > battle_config.max_hitrate) + hitrate = battle_config.max_hitrate; + else if (hitrate < battle_config.min_hitrate) + hitrate = battle_config.min_hitrate; + + if(rand()%100 >= hitrate) + wd.dmg_lv = ATK_FLEE; + else + flag.hit =1; + } //End hit/miss calculation + + if(tsd && tsd->special_state.no_weapon_damage) { + if (wd.div_ < 0) wd.div_*=-1; + return wd; + } + + if (flag.hit && !flag.infdef) //No need to do the math for plants + { //Hitting attack + +//Assuming that 99% of the cases we will not need to check for the flag.rh... we don't. +//ATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc +#define ATK_RATE( a ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(a)/100; } +#define ATK_RATE2( a , b ) { wd.damage= wd.damage*(a)/100 ; if(flag.lh) wd.damage2= wd.damage2*(b)/100; } +//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage +#define ATK_ADDRATE( a ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(a)/100; } +#define ATK_ADDRATE2( a , b ) { wd.damage+= wd.damage*(a)/100 ; if(flag.lh) wd.damage2+= wd.damage2*(b)/100; } +//Adds an absolute value to damage. 100 = +100 damage +#define ATK_ADD( a ) { wd.damage+= a; if (flag.lh) wd.damage2+= a; } +#define ATK_ADD2( a , b ) { wd.damage+= a; if (flag.lh) wd.damage2+= b; } + + switch (skill_num) + { //Calc base damage according to skill + case PA_SACRIFICE: + skill = sstatus->max_hp* 9/100; + status_zap(src, skill, 0);//Damage to self is always 9% + clif_damage(src,src, gettick(), 0, 0, skill, 0 , 0, 0); + + wd.damage = skill; + wd.damage2 = 0; + + if (sc && sc->data[SC_SACRIFICE].timer != -1) + { + if (--sc->data[SC_SACRIFICE].val2 <= 0) + status_change_end(src, SC_SACRIFICE,-1); + } + break; + case LK_SPIRALPIERCE: + if (sd) { + short index = sd->equip_index[9]; + + if (index >= 0 && + sd->inventory_data[index] && + sd->inventory_data[index]->type == 4) + wd.damage = sd->inventory_data[index]->weight*8/100; //80% of weight + + ATK_ADDRATE(50*skill_lv); //Skill modifier applies to weight only. + index = sstatus->str/10; + index = index*index; + ATK_ADD(index); //Add str bonus. + + switch (tstatus->size) { //Size-fix. Is this modified by weapon perfection? + case 0: //Small: 125% + ATK_RATE(125); + break; + //case 1: //Medium: 100% + case 2: //Large: 75% + ATK_RATE(75); + break; + } + break; + } + case CR_SHIELDBOOMERANG: + case PA_SHIELDCHAIN: + if (sd) { + short index = sd->equip_index[8]; + + wd.damage = sstatus->batk; + if (flag.lh) wd.damage2 = wd.damage; + + if (index >= 0 && + sd->inventory_data[index] && + sd->inventory_data[index]->type == 5) + ATK_ADD(sd->inventory_data[index]->weight/10); + break; + } + default: + { + i = (flag.cri?1:0)|(flag.arrow?2:0)|(skill_num == HW_MAGICCRASHER?4:0)|(skill_num == MO_EXTREMITYFIST?8:0); + wd.damage = battle_calc_base_damage(sstatus, &sstatus->rhw, sc, tstatus->size, sd, i); + if (sstatus->lhw) + wd.damage2 = battle_calc_base_damage(sstatus, sstatus->lhw, sc, tstatus->size, sd, i); + + //Add any bonuses that modify the base baseatk+watk (pre-skills) + if(sd) + { + if (sd->status.weapon < MAX_WEAPON_TYPE && (sd->atk_rate != 100 || sd->weapon_atk_rate[sd->status.weapon] != 0)) + ATK_RATE(sd->atk_rate + sd->weapon_atk_rate[sd->status.weapon]); + + if(flag.cri && sd->crit_atk_rate) + ATK_ADDRATE(sd->crit_atk_rate); + + if(sd->status.party_id && (skill=pc_checkskill(sd,TK_POWER)) > 0){ + i = party_foreachsamemap(party_sub_count, sd, 0); + ATK_ADDRATE(2*skill*i); + } + } + break; + } //End default case + } //End switch(skill_num) + + //Skill damage modifiers that stack linearly + if(sc && skill_num != PA_SACRIFICE) + { + if(sc->data[SC_OVERTHRUST].timer != -1) + skillratio += 5*sc->data[SC_OVERTHRUST].val1; + if(sc->data[SC_MAXOVERTHRUST].timer != -1) + skillratio += 20*sc->data[SC_MAXOVERTHRUST].val1; + if(sc->data[SC_BERSERK].timer != -1) + skillratio += 100; + } + if (!skill_num) + { + // Random chance to deal multiplied damage - Consider it as part of skill-based-damage + if(sd && + sd->random_attack_increase_add > 0 && + sd->random_attack_increase_per && + rand()%100 < sd->random_attack_increase_per + ) + skillratio += sd->random_attack_increase_add; + + ATK_RATE(skillratio); + } else { //Skills + switch( skill_num ) + { + case SM_BASH: + skillratio += 30*skill_lv; + break; + case SM_MAGNUM: + skillratio += 20*skill_lv; + break; + case MC_MAMMONITE: + skillratio += 50*skill_lv; + break; + case HT_POWER: //FIXME: How exactly is the STR based damage supposed to be done? [Skotlex] + skillratio += 5*sstatus->str; + break; + case AC_DOUBLE: + skillratio += 10*(skill_lv-1); + break; + case AC_SHOWER: + skillratio += 5*skill_lv-25; + break; + case AC_CHARGEARROW: + skillratio += 50; + break; + case HT_FREEZINGTRAP: + skillratio += -50+10*skill_lv; + break; + case KN_PIERCE: + skillratio += 10*skill_lv; + break; + case KN_SPEARSTAB: + skillratio += 15*skill_lv; + break; + case KN_SPEARBOOMERANG: + skillratio += 50*skill_lv; + break; + case KN_BRANDISHSPEAR: + { + int ratio = 100+20*skill_lv; + skillratio += ratio-100; + if(skill_lv>3 && wflag==1) skillratio += ratio/2; + if(skill_lv>6 && wflag==1) skillratio += ratio/4; + if(skill_lv>9 && wflag==1) skillratio += ratio/8; + if(skill_lv>6 && wflag==2) skillratio += ratio/2; + if(skill_lv>9 && wflag==2) skillratio += ratio/4; + if(skill_lv>9 && wflag==3) skillratio += ratio/2; + break; + } + case KN_BOWLINGBASH: + skillratio+= 40*skill_lv; + break; + case KN_AUTOCOUNTER: + case LK_SPIRALPIERCE: + case NPC_CRITICALSLASH: + flag.idef= flag.idef2= 1; + break; + case AS_GRIMTOOTH: + skillratio += 20*skill_lv; + break; + case AS_POISONREACT: + skillratio += 30*skill_lv; + break; + case AS_SONICBLOW: + skillratio += -50+5*skill_lv; + break; + case TF_SPRINKLESAND: + skillratio += 30; + break; + case MC_CARTREVOLUTION: + skillratio += 50; + if(sd && sd->cart_max_weight > 0 && sd->cart_weight > 0) + skillratio += 100*sd->cart_weight/sd->cart_max_weight; // +1% every 1% weight + else if (!sd) + skillratio += 150; //Max damage for non players. + break; + case NPC_RANDOMATTACK: + skillratio += rand()%150-50; + break; + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_UNDEADATTACK: + case NPC_TELEKINESISATTACK: + skillratio += 25*skill_lv; + break; + case RG_BACKSTAP: + if(sd && sd->status.weapon == W_BOW && battle_config.backstab_bow_penalty) + skillratio += (200+40*skill_lv)/2; + else + skillratio += 200+40*skill_lv; + break; + case RG_RAID: + skillratio += 40*skill_lv; + break; + case RG_INTIMIDATE: + skillratio += 30*skill_lv; + break; + case CR_SHIELDCHARGE: + skillratio += 20*skill_lv; + break; + case CR_SHIELDBOOMERANG: + skillratio += 30*skill_lv; + break; + case NPC_DARKCROSS: + case CR_HOLYCROSS: + skillratio += 35*skill_lv; + break; + case CR_GRANDCROSS: + case NPC_GRANDDARKNESS: + flag.cardfix = 0; + break; + case AM_DEMONSTRATION: + skillratio += 20*skill_lv; + flag.cardfix = 0; + break; + case AM_ACIDTERROR: + skillratio += 40*skill_lv; + flag.cardfix = 0; + break; + case MO_FINGEROFFENSIVE: + skillratio+= 50 * skill_lv; + break; + case MO_INVESTIGATE: + skillratio += 75*skill_lv; + flag.pdef = flag.pdef2 = 2; + break; + case MO_EXTREMITYFIST: + { //Overflow check. [Skotlex] + unsigned int ratio = skillratio + 100*(8 + sstatus->sp/10); + //You'd need something like 6K SP to reach this max, so should be fine for most purposes. + if (ratio > 60000) ratio = 60000; //We leave some room here in case skillratio gets further increased. + skillratio = (unsigned short)ratio; + status_zap(src, 0, sstatus->sp); + flag.idef= flag.idef2= 1; + } + break; + case MO_TRIPLEATTACK: + skillratio += 20*skill_lv; + break; + case MO_CHAINCOMBO: + skillratio += 50+50*skill_lv; + break; + case MO_COMBOFINISH: + skillratio += 140+60*skill_lv; + break; + case BA_MUSICALSTRIKE: + skillratio += 40*skill_lv-40; + break; + case DC_THROWARROW: + skillratio += 50*skill_lv; + break; + case CH_TIGERFIST: + skillratio += 100*skill_lv-60; + break; + case CH_CHAINCRUSH: + skillratio += 300+100*skill_lv; + break; + case CH_PALMSTRIKE: + skillratio += 100+100*skill_lv; + break; + case LK_HEADCRUSH: + skillratio += 40*skill_lv; + break; + case LK_JOINTBEAT: + skillratio += 10*skill_lv-50; + break; + case ASC_METEORASSAULT: + skillratio += 40*skill_lv-60; + flag.cardfix = 0; + break; + case SN_SHARPSHOOTING: + skillratio += 50*skill_lv; + break; + case CG_ARROWVULCAN: + skillratio += 100+100*skill_lv; + break; + case AS_SPLASHER: + skillratio += 400+50*skill_lv; + if (sd) + skillratio += 20*pc_checkskill(sd,AS_POISONREACT); + if(wflag>1) //FIXME: Splash damage... is this the correct method? [Skotlex] + skillratio /= wflag; + flag.cardfix = 0; + break; + case ASC_BREAKER: + skillratio += 100*skill_lv-100; + flag.cardfix = 0; + break; + case PA_SACRIFICE: + //40% less effective on siege maps. [Skotlex] + skillratio += 10*skill_lv-10; + flag.idef = flag.idef2 = 1; + break; + case PA_SHIELDCHAIN: + skillratio += 30*skill_lv; + break; + case WS_CARTTERMINATION: + if(sd && sd->cart_weight > 0) + skillratio += sd->cart_weight / (10 * (16 - skill_lv)) - 100; + else if (!sd) + skillratio += battle_config.max_cart_weight / (10 * (16 - skill_lv)); + flag.cardfix = 0; + break; + case TK_DOWNKICK: + skillratio += 60 + 20*skill_lv; + break; + case TK_STORMKICK: + skillratio += 60 + 20*skill_lv; + break; + case TK_TURNKICK: + skillratio += 90 + 30*skill_lv; + break; + case TK_COUNTER: + skillratio += 90 + 30*skill_lv; + break; + case TK_JUMPKICK: + skillratio += -70 + 10*skill_lv; + if (sc && sc->data[SC_COMBO].timer != -1 && sc->data[SC_COMBO].val1 == skill_num) + skillratio += 10*status_get_lv(src)/3; + break; + case GS_BULLSEYE: + skillratio += 400; + break; + case GS_TRACKING: + skillratio += 60*skill_lv; + if (skill_lv == 2) skillratio += 20; + if (skill_lv == 3) skillratio += 80; + if (skill_lv >= 4) skillratio += 60*(skill_lv-3); + if (skill_lv == 10) skillratio += 80; + break; + case GS_PIERCINGSHOT: + skillratio += 20*skill_lv; + break; + case GS_RAPIDSHOWER: + skillratio += 10*skill_lv; + break; + case GS_DESPERADO: + skillratio += 50*skill_lv - 50; + break; + case GS_DUST: + skillratio += 50*skill_lv; + break; + case GS_FULLBUSTER: + skillratio += 200 + 100*skill_lv; + break; + case GS_SPREADATTACK: + skillratio += 20*skill_lv-20; + break; + case NJ_HUUMA: + skillratio += 50 + 150*skill_lv; + break; + case NJ_TATAMIGAESHI: + skillratio += 10*skill_lv; + break; + case NJ_KASUMIKIRI: + skillratio += 10*skill_lv; + break; + case NJ_KIRIKAGE: + skillratio += 100*skill_lv-100; + break; + case KN_CHARGEATK: + skillratio += wflag*15; //FIXME: How much is the actual bonus? [Skotlex] + break; + case HT_PHANTASMIC: + skillratio += 50; + break; + case MO_BALKYOUNG: + skillratio += 200; + break; + } + + ATK_RATE(skillratio); + + //Constant/misc additions from skills + switch (skill_num) { + case MO_EXTREMITYFIST: + ATK_ADD(250 + 150*skill_lv); + break; + case TK_DOWNKICK: + case TK_STORMKICK: + case TK_TURNKICK: + case TK_COUNTER: + case TK_JUMPKICK: + //TK_RUN kick damage bonus. + if(sd && sd->weapontype1 == W_FIST && sd->weapontype2 == W_FIST) + ATK_ADD(10*pc_checkskill(sd, TK_RUN)); + break; + case GS_MAGICALBULLET: + if(sstatus->matk_max>sstatus->matk_min) { + ATK_ADD(sstatus->matk_min+rand()%(sstatus->matk_max-sstatus->matk_min)); + } else { + ATK_ADD(sstatus->matk_min); + } + break; + } + } + //Div fix. + damage_div_fix(wd.damage, wd.div_); + //Here comes a second pass for skills that stack to the previously defined % damage. [Skotlex] + skillratio = 100; + //Skill damage modifiers that affect linearly stacked damage. + if (sc && skill_num != PA_SACRIFICE) { + if(sc->data[SC_TRUESIGHT].timer != -1) + skillratio += 2*sc->data[SC_TRUESIGHT].val1; + // It is still not quite decided whether it works on bosses or not... + if(sc->data[SC_EDP].timer != -1 /*&& !(t_mode&MD_BOSS)*/ && skill_num != ASC_BREAKER && skill_num != ASC_METEORASSAULT) + skillratio += 50 +50*sc->data[SC_EDP].val1; + } + switch (skill_num) { + case AS_SONICBLOW: + if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_ASSASIN) + skillratio += (map_flag_gvg(src->m))?25:100; //+25% dmg on woe/+100% dmg on nonwoe + if(sd && pc_checkskill(sd,AS_SONICACCEL)>0) + skillratio += 10; + break; + case CR_SHIELDBOOMERANG: + if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_CRUSADER) + skillratio += 100; + break; + } + if (sd && sd->skillatk[0].id != 0) + { + for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++); + if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num) + //May seem wrong as it also applies on top of other modifiers, but adding, say, 10% + //to 800% dmg -> 810% would make the bonus a little lame. [Skotlex] + skillratio += sd->skillatk[i].val; + } + if (skillratio != 100) + ATK_RATE(skillratio); + + if(sd) + { + if (skill_num != PA_SACRIFICE && skill_num != MO_INVESTIGATE + && skill_num != CR_GRANDCROSS && skill_num != NPC_GRANDDARKNESS + && !flag.cri) + { //Elemental/Racial adjustments + if(sd->right_weapon.def_ratio_atk_ele & (1<def_ele) || + sd->right_weapon.def_ratio_atk_race & (1<race) || + sd->right_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11) + ) + flag.pdef = 1; + + if(sd->left_weapon.def_ratio_atk_ele & (1<def_ele) || + sd->left_weapon.def_ratio_atk_race & (1<race) || + sd->left_weapon.def_ratio_atk_race & (is_boss(target)?1<<10:1<<11) + ) { //Pass effect onto right hand if configured so. [Skotlex] + if (battle_config.left_cardfix_to_right && flag.rh) + flag.pdef = 1; + else + flag.pdef2 = 1; + } + } + + if (skill_num != CR_GRANDCROSS && skill_num != NPC_GRANDDARKNESS) + { //Ignore Defense? + if (!flag.idef && ( + (target->type == BL_MOB && sd->right_weapon.ignore_def_mob & (is_boss(target)?2:1)) || + sd->right_weapon.ignore_def_ele & (1<def_ele) || + sd->right_weapon.ignore_def_race & (1<race) || + sd->right_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11) + )) + flag.idef = 1; + + if (!flag.idef2 && ( + (target->type == BL_MOB && sd->left_weapon.ignore_def_mob & (is_boss(target)?2:1)) || + sd->left_weapon.ignore_def_ele & (1<def_ele) || + sd->left_weapon.ignore_def_race & (1<race) || + sd->left_weapon.ignore_def_race & (is_boss(target)?1<<10:1<<11) + )) { + if(battle_config.left_cardfix_to_right && flag.rh) //Move effect to right hand. [Skotlex] + flag.idef = 1; + else + flag.idef2 = 1; + } + } + } + + if (!flag.idef || !flag.idef2) + { //Defense reduction + short vit_def; + char def1 = (char)status_get_def(target); //Don't use tstatus->def1 due to skill timer reductions. + short def2 = (short)tstatus->def2; + if(battle_config.vit_penalty_type) + { + unsigned char target_count; //256 max targets should be a sane max + target_count = unit_counttargeted(target,battle_config.vit_penalty_count_lv); + if(target_count >= battle_config.vit_penalty_count) { + if(battle_config.vit_penalty_type == 1) { + def1 = (def1 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100; + def2 = (def2 * (100 - (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num))/100; + } else { //Assume type 2 + def1 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num; + def2 -= (target_count - (battle_config.vit_penalty_count - 1))*battle_config.vit_penalty_num; + } + } + if(def1 < 0 || skill_num == AM_ACIDTERROR) def1 = 0; //Acid Terror ignores only armor defense. [Skotlex] + if(def2 < 1) def2 = 1; + } + //Vitality reduction from rodatazone: http://rodatazone.simgaming.net/mechanics/substats.php#def + if (tsd) //Sd vit-eq + { //[VIT*0.5] + rnd([VIT*0.3], max([VIT*0.3],[VIT^2/150]-1)) + vit_def = def2*(def2-15)/150; + vit_def = def2/2 + (vit_def>0?rand()%vit_def:0); + + if((battle_check_undead(sstatus->race,sstatus->def_ele) || sstatus->race==RC_DEMON) && + (skill=pc_checkskill(tsd,AL_DP)) >0) + vit_def += skill*(int)(3 +(tsd->status.base_level+1)*0.04); // submitted by orn + } else { //Mob-Pet vit-eq + //VIT + rnd(0,[VIT/20]^2-1) + vit_def = (def2/20)*(def2/20); + vit_def = def2 + (vit_def>0?rand()%vit_def:0); + } + + if (battle_config.weapon_defense_type) { + vit_def += def1*battle_config.weapon_defense_type; + def1 = 0; + } + if (def1 > 100) def1 = 100; + ATK_RATE2( + flag.idef ?100: + (flag.pdef ?flag.pdef *(def1 + vit_def): + 100-def1), + flag.idef2?100: + (flag.pdef2?flag.pdef2*(def1 + vit_def): + 100-def1) + ); + ATK_ADD2( + flag.idef ||flag.pdef ?0:-vit_def, + flag.idef2||flag.pdef2?0:-vit_def + ); + } + + //Post skill/vit reduction damage increases + if (sc && skill_num != LK_SPIRALPIERCE) + { //SC skill damages + if(sc->data[SC_AURABLADE].timer!=-1) + ATK_ADD(20*sc->data[SC_AURABLADE].val1); + } + + //Refine bonus + if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST) { + if (skill_num == MO_FINGEROFFENSIVE) //Counts refine bonus multiple times + { + ATK_ADD2(wd.div_*sstatus->rhw.atk2, wd.div_*sstatus->lhw->atk2); + } else { + ATK_ADD2(sstatus->rhw.atk2, sstatus->lhw->atk2); + } + } + + //Set to min of 1 + if (flag.rh && wd.damage < 1) wd.damage = 1; + if (flag.lh && wd.damage2 < 1) wd.damage2 = 1; + + if (sd && flag.weapon && skill_num != MO_INVESTIGATE && skill_num != MO_EXTREMITYFIST + && skill_num != CR_GRANDCROSS) + { //Add mastery damage + wd.damage = battle_addmastery(sd,target,wd.damage,0); + if (flag.lh) wd.damage2 = battle_addmastery(sd,target,wd.damage2,1); + + if((skill=pc_checkskill(sd,SG_STAR_ANGER)) >0 && (t_class == sd->hate_mob[2] || (sc && sc->data[SC_MIRACLE].timer!=-1))) + { + skillratio = (sd->status.base_level + sstatus->str + sstatus->dex + sstatus->luk)/(skill<4?12-3*skill:1); + ATK_ADDRATE(skillratio); + } else + if( + ((skill=pc_checkskill(sd,SG_SUN_ANGER)) >0 && t_class == sd->hate_mob[0]) || + ((skill=pc_checkskill(sd,SG_MOON_ANGER)) >0 && t_class == sd->hate_mob[1]) + ) { + skillratio = (sd->status.base_level + sstatus->dex+ sstatus->luk)/(skill<4?12-3*skill:1); + ATK_ADDRATE(skillratio); + } + } + } //Here ends flag.hit section, the rest of the function applies to both hitting and missing attacks + else if(wd.div_ < 0) //Since the attack missed... + wd.div_ *= -1; + + if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS) + return wd; //Enough, rest is not needed. + + if(sd && (skill=pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0) + ATK_ADD(skill*2); + + if(skill_num==TF_POISON) + ATK_ADD(15*skill_lv); + + if(skill_num==ASC_BREAKER) //Breaker's int-based damage. + ATK_ADD(rand()%500 + 500 + skill_lv * sstatus->int_ * 5); + + if (skill_num || !(battle_config.attack_attr_none&src->type)) + { //Elemental attribute fix + if (!(!sd && tsd && battle_config.mob_ghostring_fix && tstatus->def_ele==ELE_GHOST)) + { + if (wd.damage > 0) + { + wd.damage=battle_attr_fix(src,target,wd.damage,s_ele,tstatus->def_ele, tstatus->ele_lv); + if(skill_num==MC_CARTREVOLUTION) //Cart Revolution applies the element fix once more with neutral element + wd.damage = battle_attr_fix(src,target,wd.damage,ELE_NEUTRAL,tstatus->def_ele, tstatus->ele_lv); + } + if (flag.lh && wd.damage2 > 0) + wd.damage2 = battle_attr_fix(src,target,wd.damage2,s_ele_,tstatus->def_ele, tstatus->ele_lv); + } + if(sc && sc->data[SC_WATK_ELEMENT].timer != -1) + { //Descriptions indicate this means adding a percent of a normal attack in another element. [Skotlex] + int damage= battle_calc_base_damage(sstatus, &sstatus->rhw, sc, tstatus->size, sd, (flag.arrow?2:0)); + damage = damage*sc->data[SC_WATK_ELEMENT].val2/100; + damage = battle_attr_fix(src,target,damage,sc->data[SC_WATK_ELEMENT].val1,tstatus->def_ele, tstatus->ele_lv); + ATK_ADD(damage); + } + } + + if ((!flag.rh || wd.damage == 0) && (!flag.lh || wd.damage2 == 0)) + flag.cardfix = 0; //When the attack does no damage, avoid doing %bonuses + + if (sd) + { + if (skill_num != CR_SHIELDBOOMERANG) //Only Shield boomerang doesn't takes the Star Crumbs bonus. + ATK_ADD2(wd.div_*sd->right_weapon.star, wd.div_*sd->left_weapon.star); + if (skill_num==MO_FINGEROFFENSIVE) { //The finger offensive spheres on moment of attack do count. [Skotlex] + ATK_ADD(wd.div_*sd->spiritball_old*3); + } else { + ATK_ADD(wd.div_*sd->spiritball*3); + } + + //Card Fix, sd side + if (flag.cardfix) + { + short cardfix = 1000, cardfix_ = 1000; + short t_race2 = status_get_race2(target); + if(sd->state.arrow_atk) + { + cardfix=cardfix*(100+sd->right_weapon.addrace[tstatus->race]+sd->arrow_addrace[tstatus->race])/100; + cardfix=cardfix*(100+sd->right_weapon.addele[tstatus->def_ele]+sd->arrow_addele[tstatus->def_ele])/100; + cardfix=cardfix*(100+sd->right_weapon.addsize[tstatus->size]+sd->arrow_addsize[tstatus->size])/100; + cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100; + cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?RC_BOSS:RC_NONBOSS]+sd->arrow_addrace[is_boss(target)?RC_BOSS:RC_NONBOSS])/100; + } else { //Melee attack + if(!battle_config.left_cardfix_to_right) + { + cardfix=cardfix*(100+sd->right_weapon.addrace[tstatus->race])/100; + cardfix=cardfix*(100+sd->right_weapon.addele[tstatus->def_ele])/100; + cardfix=cardfix*(100+sd->right_weapon.addsize[tstatus->size])/100; + cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2])/100; + cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?RC_BOSS:RC_NONBOSS])/100; + + if (flag.lh) + { + cardfix_=cardfix_*(100+sd->left_weapon.addrace[tstatus->race])/100; + cardfix_=cardfix_*(100+sd->left_weapon.addele[tstatus->def_ele])/100; + cardfix_=cardfix_*(100+sd->left_weapon.addsize[tstatus->size])/100; + cardfix_=cardfix_*(100+sd->left_weapon.addrace2[t_race2])/100; + cardfix_=cardfix_*(100+sd->left_weapon.addrace[is_boss(target)?RC_BOSS:RC_NONBOSS])/100; + } + } else { + cardfix=cardfix*(100+sd->right_weapon.addrace[tstatus->race]+sd->left_weapon.addrace[tstatus->race])/100; + cardfix=cardfix*(100+sd->right_weapon.addele[tstatus->def_ele]+sd->left_weapon.addele[tstatus->def_ele])/100; + cardfix=cardfix*(100+sd->right_weapon.addsize[tstatus->size]+sd->left_weapon.addsize[tstatus->size])/100; + cardfix=cardfix*(100+sd->right_weapon.addrace2[t_race2]+sd->left_weapon.addrace2[t_race2])/100; + cardfix=cardfix*(100+sd->right_weapon.addrace[is_boss(target)?RC_BOSS:RC_NONBOSS]+sd->left_weapon.addrace[is_boss(target)?RC_BOSS:RC_NONBOSS])/100; + } + } + + for(i=0;iright_weapon.add_damage_class_count;i++) { + if(sd->right_weapon.add_damage_classid[i] == t_class) { + cardfix=cardfix*(100+sd->right_weapon.add_damage_classrate[i])/100; + break; + } + } + + if (flag.lh) + { + for(i=0;ileft_weapon.add_damage_class_count;i++) { + if(sd->left_weapon.add_damage_classid[i] == t_class) { + cardfix_=cardfix_*(100+sd->left_weapon.add_damage_classrate[i])/100; + break; + } + } + } + + if(wd.flag&BF_LONG) + cardfix=cardfix*(100+sd->long_attack_atk_rate)/100; + + if (cardfix != 1000 || cardfix_ != 1000) + ATK_RATE2(cardfix/10, cardfix_/10); //What happens if you use right-to-left and there's no right weapon, only left? + } + + if (skill_num == CR_SHIELDBOOMERANG || skill_num == PA_SHIELDCHAIN) { //Refine bonus applies after cards and elements. + short index= sd->equip_index[8]; + if (index >= 0 && + sd->inventory_data[index] && + sd->inventory_data[index]->type == 5) + ATK_ADD(10*sd->status.inventory[index].refine); + } + } //if (sd) + + //Card Fix, tsd side - Cards always apply on the target. [Skotlex] + if (tsd) { + short s_race2,s_class; + short cardfix=1000; + + s_race2 = status_get_race2(src); + s_class = status_get_class(src); + + cardfix=cardfix*(100-tsd->subele[sstatus->def_ele])/100; + cardfix=cardfix*(100-tsd->subsize[sstatus->size])/100; + cardfix=cardfix*(100-tsd->subrace2[s_race2])/100; + cardfix=cardfix*(100-tsd->subrace[sstatus->race])/100; + cardfix=cardfix*(100-tsd->subrace[is_boss(src)?RC_BOSS:RC_NONBOSS])/100; + + for(i=0;iadd_dmg_count;i++) { + if(tsd->add_dmg[i].class_ == s_class) { + cardfix=cardfix*(100+tsd->add_dmg[i].rate)/100; + break; + } + } + + if(wd.flag&BF_SHORT) + cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; + else // BF_LONG (there's no other choice) + cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; + + if (cardfix != 1000) + ATK_RATE(cardfix/10); + } + + if(flag.infdef) + { //Plants receive 1 damage when hit + if (flag.rh && (flag.hit || wd.damage>0)) + wd.damage = 1; + if (flag.lh && (flag.hit || wd.damage2>0)) + wd.damage2 = 1; + if (!(battle_config.skill_min_damage&1)) + //Do not return if you are supposed to deal greater damage to plants than 1. [Skotlex] + return wd; + } + + if(sd && !skill_num && !flag.cri) + { //Check for double attack. + if(( (skill_lv = 5*pc_checkskill(sd,TF_DOUBLE)) > 0 && sd->weapontype1 == W_DAGGER) || + sd->double_rate > 0) //Success chance is not added, the higher one is used? [Skotlex] + { + if (rand()%100 < (skill_lv>sd->double_rate?skill_lv:sd->double_rate)) + { + wd.div_=skill_get_num(TF_DOUBLE,skill_lv?skill_lv/5:1); + damage_div_fix(wd.damage, wd.div_); + wd.type = 0x08; + } + } else if ((skill_lv = pc_checkskill(sd,GS_CHAINACTION)) > 0 && sd->weapontype1 == W_REVOLVER) + { + if (rand()%100 < 5*skill_lv) + { + wd.div_=skill_get_num(GS_CHAINACTION,skill_lv); + damage_div_fix(wd.damage, wd.div_); + wd.type = 0x08; + } + } + } + + if(!flag.rh || wd.damage<1) + wd.damage=0; + + if(!flag.lh || wd.damage2<1) + wd.damage2=0; + + if (sd) + { + if (!flag.rh && flag.lh) + { //Move lh damage to the rh + wd.damage = wd.damage2; + wd.damage2 = 0; + flag.rh=1; + flag.lh=0; + } else if(sd->status.weapon > MAX_WEAPON_TYPE) + { //Dual-wield + if (wd.damage > 0) + { + skill = pc_checkskill(sd,AS_RIGHT); + wd.damage = wd.damage * (50 + (skill * 10))/100; + if(wd.damage < 1) wd.damage = 1; + } + if (wd.damage2 > 0) + { + skill = pc_checkskill(sd,AS_LEFT); + wd.damage2 = wd.damage2 * (30 + (skill * 10))/100; + if(wd.damage2 < 1) wd.damage2 = 1; + } + } else if(sd->status.weapon == W_KATAR) + { //Katars + skill = pc_checkskill(sd,TF_DOUBLE); + wd.damage2 = wd.damage * (1 + (skill * 2))/100; + + if(wd.damage > 0 && wd.damage2 < 1) wd.damage2 = 1; + flag.lh = 1; + } + } + + if(wd.damage > 0 || wd.damage2 > 0) + { + if(wd.damage2<1) { + wd.damage=battle_calc_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag); + if (map_flag_gvg(target->m)) + wd.damage=battle_calc_gvg_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag); + } else if(wd.damage<1) { + wd.damage2=battle_calc_damage(src,target,wd.damage2,wd.div_,skill_num,skill_lv,wd.flag); + if (map_flag_gvg(target->m)) + wd.damage2=battle_calc_gvg_damage(src,target,wd.damage2,wd.div_,skill_num,skill_lv,wd.flag); + } + else + { + int d1=wd.damage+wd.damage2,d2=wd.damage2; + wd.damage=battle_calc_damage(src,target,d1,wd.div_,skill_num,skill_lv,wd.flag); + if (map_flag_gvg(target->m)) + wd.damage=battle_calc_gvg_damage(src,target,wd.damage,wd.div_,skill_num,skill_lv,wd.flag); + wd.damage2=(d2*100/d1)*wd.damage/100; + if(wd.damage > 1 && wd.damage2 < 1) wd.damage2=1; + wd.damage-=wd.damage2; + } + } + + if(sd && sd->classchange && target->type == BL_MOB && !(tstatus->mode&MD_BOSS) && (rand()%10000 < sd->classchange)) + { + struct mob_data *tmd = (TBL_MOB*)target; + if (!tmd->guardian_data && (tmd->class_ < 1324 || tmd->class_ > 1363) && !mob_is_clone(tmd->class_)) + { //Classchange: + struct mob_db *mob; + int k, class_; + i = 0; + do { + do { + class_ = rand() % MAX_MOB_DB; + } while (!mobdb_checkid(class_)); + + k = rand() % 1000000; + mob = mob_db(class_); + } while ((mob->status.mode&(MD_BOSS|MD_PLANT) || mob->summonper[0] <= k) && (i++) < 2000); + if (i< 2000) + mob_class_change(tmd,class_); + } + } + if (wd.damage > 0 || wd.damage2 > 0) { + if (sd && battle_config.equip_self_break_rate) + { // Self weapon breaking + int breakrate = battle_config.equip_natural_break_rate; + if (sc) { + if(sc->data[SC_OVERTHRUST].timer!=-1) + breakrate += 10; + if(sc->data[SC_MAXOVERTHRUST].timer!=-1) + breakrate += 10; + } + if (breakrate) + skill_break_equip(src, EQP_WEAPON, breakrate, BCT_SELF); + } + if (battle_config.equip_skill_break_rate) + { // Target equipment breaking + int breakrate[2] = {0,0}; // weapon = 0, armor = 1 + if (sd) { // Break rate from equipment + breakrate[0] += sd->break_weapon_rate; + breakrate[1] += sd->break_armor_rate; + } + if (sc) { + if (sc->data[SC_MELTDOWN].timer!=-1) { + breakrate[0] += sc->data[SC_MELTDOWN].val2; + breakrate[1] += sc->data[SC_MELTDOWN].val3; + } + } + if (breakrate[0]) + skill_break_equip(target, EQP_WEAPON, breakrate[0], BCT_ENEMY); + if (breakrate[1]) + skill_break_equip(target, EQP_ARMOR, breakrate[1], BCT_ENEMY); + } + } + + //SG_FUSION hp penalty [Komurka] + if (sc && sc->data[SC_FUSION].timer!=-1) + { + int hp= sstatus->max_hp; + if (sd && tsd) { + hp = 8*hp/100; + if (100*sstatus->hp <= 20*sstatus->max_hp) + hp = sstatus->hp; + } else + hp = 5*hp/1000; + status_zap(src, hp, 0); + } + + return wd; +} + +/*========================================== + * battle_calc_magic_attack [DracoRPG] + *------------------------------------------ + */ +struct Damage battle_calc_magic_attack( + struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int mflag) + { + short i; + short s_ele; + unsigned short skillratio = 100; //Skill dmg modifiers. + + struct map_session_data *sd, *tsd; + struct Damage ad; + struct status_data *sstatus = status_get_status_data(src); + struct status_data *tstatus = status_get_status_data(target); + struct { + unsigned imdef : 1; + unsigned infdef : 1; + unsigned elefix : 1; + unsigned cardfix : 1; + } flag; + + memset(&ad,0,sizeof(ad)); + memset(&flag,0,sizeof(flag)); + + if(src==NULL || target==NULL) + { + nullpo_info(NLP_MARK); + return ad; + } + //Initial flag + flag.elefix=1; + flag.cardfix=1; + + //Initial Values + ad.damage = 1; + ad.div_=skill_get_num(skill_num,skill_lv); + ad.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:status_get_amotion(src); //Amotion should be 0 for ground skills. + ad.dmotion=tstatus->dmotion; + ad.blewcount = skill_get_blewcount(skill_num,skill_lv); + ad.flag=BF_MAGIC|BF_LONG|BF_SKILL; + ad.dmg_lv=ATK_DEF; + + BL_CAST(BL_PC, src, sd); + BL_CAST(BL_PC, target, tsd); + + //Initialize variables that will be used afterwards + s_ele = skill_get_pl(skill_num); + + if (s_ele == -1) // pl=-1 : the skill takes the weapon's element + s_ele = sstatus->rhw.ele; + else if (s_ele == -2) //Use status element + s_ele = status_get_attack_sc_element(src,status_get_sc(src)); + + //Set miscellaneous data that needs be filled + if(sd) { + sd->state.arrow_atk = 0; + if (sd->skillblown[0].id != 0) + { //Apply the bonus blewcount. [Skotlex] + for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++); + if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num) + ad.blewcount += sd->skillblown[i].val; + } + } + + if (battle_config.skillrange_by_distance && + (src->type&battle_config.skillrange_by_distance) + ) { //Skill range based on distance between src/target [Skotlex] + if (check_distance_bl(src, target, 3)) + ad.flag=(ad.flag&~BF_RANGEMASK)|BF_SHORT; + else + ad.flag=(ad.flag&~BF_RANGEMASK)|BF_LONG; + } + + flag.infdef=(tstatus->mode&MD_PLANT?1:0); + + switch(skill_num) + { + case MG_FIREWALL: + if(mflag) { //mflag has a value when it was checked against an undead in skill.c [Skotlex] + ad.blewcount = 0; //No knockback + ad.dmotion = 0; //No flinch animation. + } else + ad.blewcount |= 0x10000; + break; + case WZ_STORMGUST: //Should knockback randomly. + ad.blewcount|=0x40000; + break; + case PR_SANCTUARY: + ad.blewcount|=0x10000; + case AL_HEAL: + case PR_BENEDICTIO: + case WZ_FIREPILLAR: + flag.imdef = 1; + break; + case HW_GRAVITATION: + flag.imdef = 1; + flag.elefix = 0; + break; + case PR_ASPERSIO: + flag.imdef = 1; + case PF_SOULBURN: //Does not ignores mdef + flag.elefix = 0; + flag.cardfix = 0; + break; + case PR_TURNUNDEAD: + flag.imdef = 1; + flag.cardfix = 0; + break; + case NPC_GRANDDARKNESS: + case CR_GRANDCROSS: + flag.cardfix = 0; + break; + } + + if(is_boss(target)) //Bosses can't be knocked-back + ad.blewcount = 0; + + if (!flag.infdef) //No need to do the math for plants + { + +//MATK_RATE scales the damage. 100 = no change. 50 is halved, 200 is doubled, etc +#define MATK_RATE( a ) { ad.damage= ad.damage*(a)/100; } +//Adds dmg%. 100 = +100% (double) damage. 10 = +10% damage +#define MATK_ADDRATE( a ) { ad.damage+= ad.damage*(a)/100; } +//Adds an absolute value to damage. 100 = +100 damage +#define MATK_ADD( a ) { ad.damage+= a; } + + switch (skill_num) + { //Calc base damage according to skill + case AL_HEAL: + case PR_BENEDICTIO: + ad.damage = skill_calc_heal(src,skill_lv)/2; + break; + case PR_ASPERSIO: + ad.damage = 40; + break; + case PR_SANCTUARY: + ad.damage = (skill_lv>6)?388:skill_lv*50; + break; + case ALL_RESURRECTION: + case PR_TURNUNDEAD: + if(battle_check_undead(tstatus->race,tstatus->def_ele)){ + int thres; + thres = (skill_lv * 20) + sstatus->luk + sstatus->int_ + status_get_lv(src) + ((200 - tstatus->hp * 200 / tstatus->max_hp)); + if(thres > 700) thres = 700; + if(rand()%1000 < thres && !(tstatus->mode&MD_BOSS)) + ad.damage = tstatus->hp; + else + ad.damage = status_get_lv(src) + sstatus->int_ + skill_lv * 10; + } + break; + case PF_SOULBURN: + ad.damage = tstatus->sp * 2; + break; + case HW_GRAVITATION: + ad.damage = 200+200*skill_lv; + break; + default: + { + if (sstatus->matk_max > sstatus->matk_min) { + MATK_ADD(sstatus->matk_min+rand()%(1+sstatus->matk_max-sstatus->matk_min)); + } else { + MATK_ADD(sstatus->matk_min); + } + + if(skill_num == MG_NAPALMBEAT || skill_num == HW_NAPALMVULCAN){ // Divide MATK in case of multiple targets skill + if(mflag>0) + ad.damage/= mflag; + else if(battle_config.error_log) + ShowError("0 enemies targeted by Napalm Beat/Vulcan, divide per 0 avoided!\n"); + } + + switch(skill_num){ + case MG_NAPALMBEAT: + skillratio += skill_lv*10-30; + break; + case MG_SOULSTRIKE: + if (battle_check_undead(tstatus->race,tstatus->def_ele)) + skillratio += 5*skill_lv; + break; + case MG_FIREBALL: + if(mflag>2) + ad.damage = 0; + else { + int drate[]={100,90,70}; + MATK_RATE(drate[mflag]); + skillratio += 70+10*skill_lv; + } + break; + case MG_FIREWALL: + skillratio -= 50; + break; + case MG_THUNDERSTORM: + skillratio -= 20; + break; + case MG_FROSTDIVER: + skillratio += 10*skill_lv; + break; + case AL_HOLYLIGHT: + skillratio += 25; + if (sd && sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_PRIEST) + skillratio *= 5; //Does 5x damage include bonuses from other skills? + break; + case AL_RUWACH: + skillratio += 45; + break; + case WZ_FROSTNOVA: + skillratio += (100+skill_lv*10)*2/3-100; + break; + case WZ_FIREPILLAR: + if (skill_lv > 10) + skillratio += 100; + else + skillratio -= 80; + break; + case WZ_SIGHTRASHER: + skillratio += 20*skill_lv; + break; + case WZ_VERMILION: + skillratio += 20*skill_lv-20; + break; + case WZ_WATERBALL: + skillratio += 30*skill_lv; + break; + case WZ_STORMGUST: + skillratio += 40*skill_lv; + break; + case HW_NAPALMVULCAN: + skillratio += 10*skill_lv-30; + break; + case SL_STIN: + skillratio += (tstatus->size?-99:10*skill_lv); //target size must be small (0) for full damage. + break; + case SL_STUN: + skillratio += (tstatus->size!=2?5*skill_lv:-99); //Full damage is dealt on small/medium targets + break; + case SL_SMA: + skillratio += -60 + status_get_lv(src); //Base damage is 40% + lv% + break; + case NJ_KOUENKA: + skillratio -= 10; + break; + case NJ_BAKUENRYU: + skillratio += 50 + 150*skill_lv; + break; + case NJ_HYOUSYOURAKU: + skillratio += 50*skill_lv; + break; + case NJ_RAIGEKISAI: + skillratio += 60 + 40*skill_lv; + break; + case NJ_KAMAITACHI: + skillratio += 100*skill_lv; + break; + } + + if (sd && sd->skillatk[0].id != 0) + { + for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++) + if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num) + //If we apply skillatk[] as ATK_RATE, it will also affect other skills, + //unfortunately this way ignores a skill's constant modifiers... + skillratio += sd->skillatk[i].val; + } + + MATK_RATE(skillratio); + + //Constant/misc additions from skills + if (skill_num == WZ_FIREPILLAR) + MATK_ADD(50); + } + } + + if(sd) { + //Ignore Defense? + if (!flag.imdef && ( + sd->ignore_mdef_ele & (1<def_ele) || + sd->ignore_mdef_race & (1<race) || + sd->ignore_mdef_race & (is_boss(target)?1<mdef*battle_config.magic_defense_type - tstatus->mdef2; + else + ad.damage = ad.damage * (100-tstatus->mdef)/100 - tstatus->mdef2; + } + + if(skill_num == CR_GRANDCROSS || skill_num == NPC_GRANDDARKNESS) + { //Apply the physical part of the skill's damage. [Skotlex] + struct Damage wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag); + ad.damage = (wd.damage + ad.damage) * (100 + 40*skill_lv)/100; + if(src==target) + { + if (src->type == BL_PC) + ad.damage = ad.damage/2; + else + ad.damage = 0; + } + } + + if(ad.damage<1) + ad.damage=1; + + if (flag.elefix) + ad.damage=battle_attr_fix(src, target, ad.damage, s_ele, tstatus->def_ele, tstatus->ele_lv); + + if (sd && flag.cardfix) { + short cardfix=100; + short t_class = status_get_class(target); + + cardfix=cardfix*(100+sd->magic_addrace[tstatus->race])/100; + if (flag.elefix) + cardfix=cardfix*(100+sd->magic_addele[tstatus->def_ele])/100; + cardfix=cardfix*(100+sd->magic_addsize[tstatus->size])/100; + cardfix=cardfix*(100+sd->magic_addrace[is_boss(target)?RC_BOSS:RC_NONBOSS])/100; + for(i=0;iadd_mdmg_count;i++) { + if(sd->add_mdmg[i].class_ == t_class) { + cardfix=cardfix*(100+sd->add_mdmg[i].rate)/100; + continue; + } + } + + MATK_RATE(cardfix); + } + + if (tsd && skill_num != HW_GRAVITATION && skill_num != PF_SOULBURN) + { //Card fixes always apply on the target side. [Skotlex] + short s_race2=status_get_race2(src); + short s_class= status_get_class(src); + short cardfix=100; + + if (flag.elefix) + cardfix=cardfix*(100-tsd->subele[sstatus->def_ele])/100; + cardfix=cardfix*(100-tsd->subsize[sstatus->size])/100; + cardfix=cardfix*(100-tsd->subrace2[s_race2])/100; + cardfix=cardfix*(100-tsd->subrace[sstatus->race])/100; + cardfix=cardfix*(100-tsd->subrace[is_boss(src)?RC_BOSS:RC_NONBOSS])/100; + for(i=0;iadd_mdef_count;i++) { + if(tsd->add_mdef[i].class_ == s_class) { + cardfix=cardfix*(100-tsd->add_mdef[i].rate)/100; + continue; + } + } + //It was discovered that ranged defense also counts vs magic! [Skotlex] + if (ad.flag&BF_SHORT) + cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; + else + cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; + + cardfix=cardfix*(100-tsd->magic_def_rate)/100; + + MATK_RATE(cardfix); + } + } + + damage_div_fix(ad.damage, ad.div_); + + if (flag.infdef && ad.damage > 0) + ad.damage = 1; + + if (tsd && status_isimmune(target)) { + if (sd && battle_config.gtb_pvp_only) { // [MouseJstr] + MATK_RATE(100 - battle_config.gtb_pvp_only); + } else ad.damage = 0; + } + + ad.damage=battle_calc_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag); + if (map_flag_gvg(target->m)) + ad.damage=battle_calc_gvg_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag); + if (ad.damage == 0) //Magic attack blocked? Consider it a miss so it doesn't invokes additional effects. [Skotlex] + ad.dmg_lv = ATK_FLEE; + return ad; +} + +/*========================================== + * その他ダ??[ジ計算 + *------------------------------------------ + */ +struct Damage battle_calc_misc_attack( + struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int mflag) +{ + int skill; + short i; + short s_ele; + + struct map_session_data *sd, *tsd; + struct Damage md; //DO NOT CONFUSE with md of mob_data! + struct status_data *sstatus = status_get_status_data(src); + struct status_data *tstatus = status_get_status_data(target); + struct { + unsigned hit : 1; + unsigned idef : 1; + unsigned elefix : 1; + unsigned cardfix : 1; + } flag; + + memset(&md,0,sizeof(md)); + memset(&flag,0,sizeof(flag)); + + if( src == NULL || target == NULL ){ + nullpo_info(NLP_MARK); + memset(&md,0,sizeof(md)); + return md; + } + + //Some initial values + md.amotion=skill_get_inf(skill_num)&INF_GROUND_SKILL?0:sstatus->amotion; + md.dmotion=tstatus->dmotion; + md.div_=skill_get_num( skill_num,skill_lv ); + md.blewcount=skill_get_blewcount(skill_num,skill_lv); + md.dmg_lv=ATK_DEF; + md.flag=BF_MISC|BF_SHORT|BF_SKILL; + + flag.cardfix = flag.elefix = flag.hit = 1; + + BL_CAST(BL_PC, src, sd); + BL_CAST(BL_PC, target, tsd); + + if(sd) { + sd->state.arrow_atk = 0; + if (sd->skillblown[0].id != 0) + { //Apply the bonus blewcount. [Skotlex] + for (i = 0; i < MAX_PC_BONUS && sd->skillblown[i].id != 0 && sd->skillblown[i].id != skill_num; i++); + if (i < MAX_PC_BONUS && sd->skillblown[i].id == skill_num) + md.blewcount += sd->skillblown[i].val; + } + } + + if(is_boss(target)) + md.blewcount = 0; + + s_ele = skill_get_pl(skill_num); + if (s_ele < 0) //Attack that takes weapon's element for misc attacks? Make it neutral [Skotlex] + s_ele = ELE_NEUTRAL; + + //Misc Settings + switch(skill_num){ + case PA_PRESSURE: + flag.elefix = flag.cardfix = 0; + case HT_BLITZBEAT: + case TF_THROWSTONE: + case SN_FALCONASSAULT: + case PA_GOSPEL: + case CR_ACIDDEMONSTRATION: + md.flag = (md.flag&~BF_RANGEMASK)|BF_LONG; + break; + case NPC_SELFDESTRUCTION: + case NPC_SMOKING: + flag.elefix = flag.cardfix = 0; + break; + case NPC_DARKBREATH: + flag.hit = 0; + break; + } + + if (battle_config.skillrange_by_distance && + (src->type&battle_config.skillrange_by_distance) + ) { //Skill range based on distance between src/target [Skotlex] + if (check_distance_bl(src, target, 3)) + md.flag=(md.flag&~BF_RANGEMASK)|BF_SHORT; + else + md.flag=(md.flag&~BF_RANGEMASK)|BF_LONG; + } + + switch(skill_num){ + case HT_LANDMINE: + md.damage=skill_lv*(sstatus->dex+75)*(100+sstatus->int_)/100; + break; + case HT_BLASTMINE: + md.damage=skill_lv*(sstatus->dex/2+50)*(100+sstatus->int_)/100; + break; + case HT_CLAYMORETRAP: + md.damage=skill_lv*(sstatus->dex/2+75)*(100+sstatus->int_)/100; + break; + case HT_BLITZBEAT: + case SN_FALCONASSAULT: + //Blitz-beat Damage. + if(!sd || (skill = pc_checkskill(sd,HT_STEELCROW)) <= 0) + skill=0; + md.damage=(sstatus->dex/10+sstatus->int_/2+skill*3+40)*2; + if(mflag > 1) + md.damage /= mflag; + + if (skill_num == HT_BLITZBEAT) + break; + //Div fix of Blitzbeat + skill = skill_get_num(HT_BLITZBEAT, 5); + damage_div_fix(md.damage, skill); + + //Falcon Assault Modifier + md.damage=md.damage*(150+70*skill_lv)/100; + break; + case TF_THROWSTONE: + md.damage=50; + break; + case BA_DISSONANCE: + md.damage=30+skill_lv*10; + if (sd) + md.damage+= 3*pc_checkskill(sd,BA_MUSICALLESSON); + break; + case NPC_SELFDESTRUCTION: + md.damage = sstatus->hp; + break; + case NPC_SMOKING: + md.damage=3; + break; + case NPC_DARKBREATH: + md.damage = 500 + (skill_lv-1)*1000 + rand()%1000; + if(md.damage > 9999) md.damage = 9999; + break; + case PA_PRESSURE: + md.damage=500+300*skill_lv; + break; + case PA_GOSPEL: + md.damage = 1+rand()%9999; + break; + case CR_ACIDDEMONSTRATION: // updated the formula based on a Japanese formula found to be exact [Reddozen] + md.damage = 7*tstatus->vit*sstatus->int_*sstatus->int_ / (10*(tstatus->vit+sstatus->int_)); + if (tsd) md.damage>>=1; + break; + case NJ_ZENYNAGE: + md.damage = 500*skill_lv +rand()%(500*skill_lv); + if (sd) pc_payzeny(sd, md.damage); + if(map_flag_vs(target->m) || is_boss(target)) + md.damage>>=1; //temp value + break; + } + + damage_div_fix(md.damage, md.div_); + + if (!flag.hit) + { + struct status_change *sc = status_get_sc(target); + if(sc && sc->opt1 && sc->opt1 != OPT1_STONEWAIT) + flag.hit = 1; + else { + short + flee = tstatus->flee, + hitrate=80; //Default hitrate + + if(battle_config.agi_penalty_type) + { + unsigned char target_count; //256 max targets should be a sane max + target_count = unit_counttargeted(target,battle_config.agi_penalty_count_lv); + if(target_count >= battle_config.agi_penalty_count) + { + if (battle_config.agi_penalty_type == 1) + flee = (flee * (100 - (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num))/100; + else //asume type 2: absolute reduction + flee -= (target_count - (battle_config.agi_penalty_count - 1))*battle_config.agi_penalty_num; + if(flee < 1) flee = 1; + } + } + + hitrate+= sstatus->hit - flee; + + if (hitrate > battle_config.max_hitrate) + hitrate = battle_config.max_hitrate; + else if (hitrate < battle_config.min_hitrate) + hitrate = battle_config.min_hitrate; + + if(rand()%100 >= hitrate) + flag.hit = 1; + } + if (!flag.hit) { + md.damage = 0; + md.dmg_lv=ATK_FLEE; + } + } + + if(md.damage && flag.cardfix && tsd){ + int cardfix = 10000; + int race2 = status_get_race(src); + cardfix=cardfix*(100-tsd->subele[sstatus->def_ele])/100; + cardfix=cardfix*(100-tsd->subsize[sstatus->size])/100; + cardfix=cardfix*(100-tsd->subrace2[race2])/100; + cardfix=cardfix*(100-tsd->subrace[sstatus->race])/100; + cardfix=cardfix*(100-tsd->subrace[is_boss(src)?RC_BOSS:RC_NONBOSS])/100; + cardfix=cardfix*(100-tsd->misc_def_rate)/100; + if(md.flag&BF_SHORT) + cardfix=cardfix*(100-tsd->near_attack_def_rate)/100; + else // BF_LONG (there's no other choice) + cardfix=cardfix*(100-tsd->long_attack_def_rate)/100; + + if (cardfix != 10000) + md.damage=md.damage*cardfix/10000; + } + if (sd && skill_num > 0 && sd->skillatk[0].id != 0) + { + for (i = 0; i < MAX_PC_BONUS && sd->skillatk[i].id != 0 && sd->skillatk[i].id != skill_num; i++); + if (i < MAX_PC_BONUS && sd->skillatk[i].id == skill_num) + md.damage += md.damage*sd->skillatk[i].val/100; + } + + if(md.damage < 0) + md.damage = 0; + else if(md.damage && tstatus->mode&MD_PLANT && skill_num != PA_PRESSURE) //Pressure can vaporize plants. + md.damage = 1; + + md.damage=battle_attr_fix(src, target, md.damage, s_ele, tstatus->def_ele, tstatus->ele_lv); + + if (skill_num != PA_PRESSURE) //Pressure ignores all these things... + md.damage=battle_calc_damage(src,target,md.damage,md.div_,skill_num,skill_lv,md.flag); + if (map_flag_gvg(target->m)) + md.damage=battle_calc_gvg_damage(src,target,md.damage,md.div_,skill_num,skill_lv,md.flag); + + return md; +} +/*========================================== + * ダ??[ジ計算一括??用 + *------------------------------------------ + */ +struct Damage battle_calc_attack( int attack_type, + struct block_list *bl,struct block_list *target,int skill_num,int skill_lv,int flag) +{ + struct Damage d; + switch(attack_type){ + case BF_WEAPON: + d = battle_calc_weapon_attack(bl,target,skill_num,skill_lv,flag); + break; + case BF_MAGIC: + d = battle_calc_magic_attack(bl,target,skill_num,skill_lv,flag); + break; + case BF_MISC: + d = battle_calc_misc_attack(bl,target,skill_num,skill_lv,flag); + break; + default: + if (battle_config.error_log) + ShowError("battle_calc_attack: unknown attack type! %d\n",attack_type); + memset(&d,0,sizeof(d)); + break; + } + return d; +} + +int battle_calc_return_damage(struct block_list *bl, int *damage, int flag) { + struct map_session_data *sd=NULL; + struct status_change *sc; + int rdamage = 0; + + if (bl->type == BL_PC) sd = (struct map_session_data*)bl; + sc = status_get_sc(bl); + + if(flag&BF_WEAPON) { + //Bounces back part of the damage. + if (flag & BF_SHORT) { + if (sd && sd->short_weapon_damage_return) + { + rdamage += *damage * sd->short_weapon_damage_return / 100; + if(rdamage < 1) rdamage = 1; + } + if (sc && sc->data[SC_REFLECTSHIELD].timer != -1) + { + rdamage += *damage * sc->data[SC_REFLECTSHIELD].val2 / 100; + if (rdamage < 1) rdamage = 1; + } + } else if (flag & BF_LONG) { + if (sd && sd->long_weapon_damage_return) + { + rdamage += *damage * sd->long_weapon_damage_return / 100; + if (rdamage < 1) rdamage = 1; + } + } + } else + // magic_damage_return by [AppleGirl] and [Valaris] + if(flag&BF_MAGIC) + { + if(sd && sd->magic_damage_return && rand()%100 < sd->magic_damage_return) + { //Bounces back full damage, you take none. + rdamage = *damage; + *damage = 0; + } + } + return rdamage; +} + +void battle_drain(TBL_PC *sd, TBL_PC* tsd, int rdamage, int ldamage, int race, int boss) +{ + struct weapon_data *wd; + int type, thp = 0, tsp = 0, rhp = 0, rsp = 0, hp, sp, i, *damage; + for (i = 0; i < 4; i++) { + //First two iterations: Right hand + if (i < 2) { wd = &sd->right_weapon; damage = &rdamage; } + else { wd = &sd->left_weapon; damage = &ldamage; } + if (*damage <= 0) continue; + //First and Third iterations: race, other two boss/nonboss state + if (i == 0 || i == 2) + type = race; + else + type = boss?RC_BOSS:RC_NONBOSS; + + hp = wd->hp_drain[type].value; + if (wd->hp_drain[type].rate) + hp += battle_calc_drain(*damage, + wd->hp_drain[type].rate, + wd->hp_drain[type].per); + + sp = wd->sp_drain[type].value; + if (wd->sp_drain[type].rate) + sp += battle_calc_drain(*damage, + wd->sp_drain[type].rate, + wd->sp_drain[type].per); + + if (hp) { + if (wd->hp_drain[type].type) + rhp += hp; + thp += hp; + } + if (sp) { + if (wd->sp_drain[type].type) + rsp += sp; + tsp += sp; + } + } + if (!thp && !tsp) return; + + status_heal(&sd->bl, thp, tsp, battle_config.show_hp_sp_drain?3:1); + + if (tsd) { + if (rhp || rsp) + status_zap(&tsd->bl, rhp, rsp); + if (rand()%1000 < sd->sp_vanish_rate) + status_percent_damage(&sd->bl, &tsd->bl, 0, sd->sp_vanish_per); + } +} +/*========================================== + * 通??U撃??まとめ + *------------------------------------------ + */ +int battle_weapon_attack( struct block_list *src,struct block_list *target, + unsigned int tick,int flag) +{ + struct map_session_data *sd = NULL, *tsd = NULL; + struct status_data *sstatus, *tstatus; + struct status_change *sc, *tsc; + int damage,rdamage=0,rdelay=0; + struct Damage wd; + + nullpo_retr(0, src); + nullpo_retr(0, target); + + if (src->prev == NULL || target->prev == NULL) + return 0; + + BL_CAST(BL_PC, src, sd); + BL_CAST(BL_PC, target, tsd); + + sstatus = status_get_status_data(src); + tstatus = status_get_status_data(target); + + sc = status_get_sc(src); + tsc = status_get_sc(target); + + if (sc && !sc->count) //Avoid sc checks when there's none to check for. [Skotlex] + sc = NULL; + if (tsc && !tsc->count) + tsc = NULL; + + if (sd) + { + sd->state.arrow_atk = (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)); + if (sd->state.arrow_atk && sd->equip_index[10]<0) { + clif_arrow_fail(sd,0); + return 0; + } + } + + if (sc && sc->data[SC_CLOAKING].timer != -1 && !sc->data[SC_CLOAKING].val4) + status_change_end(src,SC_CLOAKING,-1); + + //Check for counter attacks that block your attack. [Skotlex] + if(tsc) + { + if(tsc->data[SC_AUTOCOUNTER].timer != -1 && + (!sc || sc->data[SC_AUTOCOUNTER].timer == -1) && + status_check_skilluse(target, src, KN_AUTOCOUNTER, 1) + ) { + int dir = map_calc_dir(target,src->x,src->y); + int t_dir = unit_getdir(target); + int dist = distance_bl(src, target); + if(dist <= 0 || (!map_check_dir(dir,t_dir) && dist <= tstatus->rhw.range+1)) + { + int skilllv = tsc->data[SC_AUTOCOUNTER].val1; + clif_skillcastcancel(target); //Remove the casting bar. [Skotlex] + clif_damage(src, target, tick, sstatus->amotion, 1, 0, 1, 0, 0); //Display MISS. + status_change_end(target,SC_AUTOCOUNTER,-1); + skill_attack(BF_WEAPON,target,target,src,KN_AUTOCOUNTER,skilllv,tick,0); + return 0; + } + } + if (tsc->data[SC_BLADESTOP_WAIT].timer != -1 && !is_boss(src)) { + int skilllv = tsc->data[SC_BLADESTOP_WAIT].val1; + int duration = skill_get_time2(MO_BLADESTOP,skilllv); + status_change_end(target, SC_BLADESTOP_WAIT, -1); + if(sc_start4(src, SC_BLADESTOP, 100, sd?pc_checkskill(sd, MO_BLADESTOP):5, 0, 0, (int)target, duration)) + { //Target locked. + clif_damage(src, target, tick, sstatus->amotion, 1, 0, 1, 0, 0); //Display MISS. + clif_bladestop(target,src,1); + sc_start4(target, SC_BLADESTOP, 100, skilllv, 0, 0,(int)src, duration); + return 0; + } + } + } + //Recycled the damage variable rather than use a new one... [Skotlex] + if(sd && (damage = pc_checkskill(sd,MO_TRIPLEATTACK)) > 0) + { + int triple_rate= 30 - damage; //Base Rate + if (sc && sc->data[SC_SKILLRATE_UP].timer!=-1 && sc->data[SC_SKILLRATE_UP].val1 == MO_TRIPLEATTACK) + { + triple_rate+= triple_rate*(sc->data[SC_SKILLRATE_UP].val2)/100; + status_change_end(src,SC_SKILLRATE_UP,-1); + } + if (rand()%100 < triple_rate) + return skill_attack(BF_WEAPON,src,src,target,MO_TRIPLEATTACK,damage,tick,0); + } + else if (sc && sc->data[SC_SACRIFICE].timer != -1) + return skill_attack(BF_WEAPON,src,src,target,PA_SACRIFICE,sc->data[SC_SACRIFICE].val1,tick,0); + + wd = battle_calc_weapon_attack(src,target, 0, 0,0); + + if (sd && sd->state.arrow_atk) //Consume arrow. + battle_consume_ammo(sd, 0, 0); + + damage = wd.damage + wd.damage2; + if (damage > 0 && src != target) { + rdamage = battle_calc_return_damage(target, &damage, wd.flag); + if (rdamage > 0) { + rdelay = clif_damage(src, src, tick, wd.amotion, sstatus->dmotion, rdamage, 1, 4, 0); + //Use Reflect Shield to signal this kind of skill trigger. [Skotlex] + skill_additional_effect(target,src,CR_REFLECTSHIELD, 1,BF_WEAPON,tick); + } + } + + wd.dmotion = clif_damage(src, target, tick, wd.amotion, wd.dmotion, wd.damage, wd.div_ , wd.type, wd.damage2); + +// Eh, battle_calc_damage should take care of not making the off-hand dmg miss. +// if(sd && sd->status.weapon > MAX_WEAPON_TYPE && wd.damage2 == 0) +// clif_damage(src, target, tick+10, wd.amotion, wd.dmotion,0, 1, 0, 0); + + if (sd && sd->splash_range > 0 && damage > 0) + skill_castend_damage_id(src, target, 0, -1, tick, 0); + + map_freeblock_lock(); + + battle_delay_damage(tick+wd.amotion, src, target, BF_WEAPON, 0, 0, damage, wd.dmg_lv, wd.dmotion); + + if (!status_isdead(target) && damage > 0) { + if (sd) { + int rate = 0; + if (sd->weapon_coma_ele[tstatus->def_ele] > 0) + rate += sd->weapon_coma_ele[tstatus->def_ele]; + if (sd->weapon_coma_race[tstatus->race] > 0) + rate += sd->weapon_coma_race[tstatus->race]; + if (sd->weapon_coma_race[tstatus->mode&MD_BOSS?RC_BOSS:RC_NONBOSS] > 0) + rate += sd->weapon_coma_race[tstatus->mode&MD_BOSS?RC_BOSS:RC_NONBOSS]; + if (rate) + status_change_start(target, SC_COMA, rate, 0, 0, 0, 0, 0, 0); + } + } + + if (sc && sc->data[SC_AUTOSPELL].timer != -1 && rand()%100 < sc->data[SC_AUTOSPELL].val4) { + int sp = 0; + int skillid = sc->data[SC_AUTOSPELL].val2; + int skilllv = sc->data[SC_AUTOSPELL].val3; + int i = rand()%100; + if (sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_SAGE) + i = 0; //Max chance, no skilllv reduction. [Skotlex] + if (i >= 50) skilllv -= 2; + else if (i >= 15) skilllv--; + if (skilllv < 1) skilllv = 1; + sp = skill_get_sp(skillid,skilllv) * 2 / 3; + + if (status_charge(src, 0, sp)) { + switch (skill_get_casttype(skillid)) { + case CAST_GROUND: + skill_castend_pos2(src, target->x, target->y, skillid, skilllv, tick, flag); + break; + case CAST_NODAMAGE: + skill_castend_nodamage_id(src, target, skillid, skilllv, tick, flag); + break; + case CAST_DAMAGE: + skill_castend_damage_id(src, target, skillid, skilllv, tick, flag); + break; + } + } + } + if (sd) { + if (wd.flag & BF_WEAPON && src != target && damage > 0) { + if (battle_config.left_cardfix_to_right) + battle_drain(sd, tsd, wd.damage, wd.damage, tstatus->race, is_boss(target)); + else + battle_drain(sd, tsd, wd.damage, wd.damage2, tstatus->race, is_boss(target)); + } + } + if (rdamage > 0) //By sending attack type "none" skill_additional_effect won't be invoked. [Skotlex] + battle_delay_damage(tick+wd.amotion, target, src, 0, 0, 0, rdamage, ATK_DEF, rdelay); + + if (tsc) { + if (tsc->data[SC_POISONREACT].timer != -1 && + check_distance_bl(src, target, tstatus->rhw.range+1) && + status_check_skilluse(target, src, TF_POISON, 0) + ) { //Poison React + if (sstatus->def_ele == ELE_POISON) { + tsc->data[SC_POISONREACT].val2 = 0; + skill_attack(BF_WEAPON,target,target,src,AS_POISONREACT,tsc->data[SC_POISONREACT].val1,tick,0); + } else { + skill_attack(BF_WEAPON,target,target,src,TF_POISON, 5, tick, flag); + --tsc->data[SC_POISONREACT].val2; + } + if (tsc->data[SC_POISONREACT].val2 <= 0) + status_change_end(target, SC_POISONREACT, -1); + } + } + map_freeblock_unlock(); + return wd.dmg_lv; +} + +int battle_check_undead(int race,int element) +{ + if(battle_config.undead_detect_type == 0) { + if(element == ELE_UNDEAD) + return 1; + } + else if(battle_config.undead_detect_type == 1) { + if(race == RC_UNDEAD) + return 1; + } + else { + if(element == ELE_UNDEAD || race == RC_UNDEAD) + return 1; + } + return 0; +} + +/*========================================== + * Checks the state between two targets (rewritten by Skotlex) + * (enemy, friend, party, guild, etc) + * See battle.h for possible values/combinations + * to be used here (BCT_* constants) + * Return value is: + * 1: flag holds true (is enemy, party, etc) + * -1: flag fails + * 0: Invalid target (non-targetable ever) + *------------------------------------------ + */ +int battle_check_target( struct block_list *src, struct block_list *target,int flag) +{ + int m,state = 0; //Initial state none + int strip_enemy = 1; //Flag which marks whether to remove the BCT_ENEMY status if it's also friend/ally. + struct block_list *s_bl= src, *t_bl= target; + + nullpo_retr(0, src); + nullpo_retr(0, target); + + m = target->m; + if (flag&BCT_ENEMY && !map_flag_gvg(m) && !(status_get_mode(src)&MD_BOSS)) + { //No offensive stuff while in Basilica. + if (map_getcell(m,src->x,src->y,CELL_CHKBASILICA) || + map_getcell(m,target->x,target->y,CELL_CHKBASILICA)) + return -1; + } + + if (target->type == BL_SKILL) //Needed out of the switch in case the ownership needs to be passed skill->mob->master + { + struct skill_unit *su = (struct skill_unit *)target; + if (!su->group) + return 0; + if (skill_get_inf2(su->group->skill_id)&INF2_TRAP) + { //Only a few skills can target traps... + switch (battle_getcurrentskill(src)) + { + case HT_REMOVETRAP: + case AC_SHOWER: + case WZ_HEAVENDRIVE: + state |= BCT_ENEMY; + strip_enemy = 0; + break; + default: + return 0; + } + } else if (su->group->skill_id==WZ_ICEWALL) + { //Icewall can be hit by anything except skills. + if (src->type == BL_SKILL) + return 0; + state |= BCT_ENEMY; + strip_enemy = 0; + } else //Excepting traps and icewall, you should not be able to target skills. + return 0; + if ((t_bl = map_id2bl(su->group->src_id)) == NULL) + t_bl = target; //Fallback on the trap itself, otherwise consider this a "versus caster" scenario. + } + + switch (t_bl->type) + { + case BL_PC: + { + TBL_PC *sd = (TBL_PC*)t_bl; + if (sd->invincible_timer != -1 || pc_isinvisible(sd)) + return -1; //Cannot be targeted yet. + if (sd->state.monster_ignore && src->type == BL_MOB) + return 0; //option to have monsters ignore GMs [Valaris] + if (sd->special_state.killable && t_bl != s_bl) + { + state |= BCT_ENEMY; //Universal Victim + strip_enemy = 0; + } + break; + } + case BL_MOB: + { + TBL_MOB *md = (TBL_MOB*)t_bl; + if(md->state.killer) + if(md->master_id != s_bl->id) + state |= BCT_ENEMY; // If he can attack you, you can attack him. + if (!agit_flag && md->guardian_data && md->guardian_data->guild_id) + return 0; //Disable guardians/emperiums owned by Guilds on non-woe times. + if (md->special_state.ai == 2) + { //Mines are sort of universal enemies. + state |= BCT_ENEMY; + strip_enemy = 0; + } + if (md->master_id && (t_bl = map_id2bl(md->master_id)) == NULL) + t_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "versus master" scenario. + break; + } + case BL_PET: + { + return 0; //Pets cannot be targetted. + } + case BL_HOMUNCULUS: + { + t_bl=(struct block_list *)((struct homun_data*)target)->master; //...and vice versa. + break; + } + case BL_SKILL: //Skill with no owner? Kinda odd... but.. let it through. + break; + default: //Invalid target + return 0; + } + + if (src->type == BL_SKILL) + { + struct skill_unit *su = (struct skill_unit *)src; + if (!su->group) + return 0; + + if (su->group->src_id == target->id) + { + int inf2; + inf2 = skill_get_inf2(su->group->skill_id); + if (inf2&INF2_NO_TARGET_SELF) + return -1; + if (inf2&INF2_TARGET_SELF) + return 1; + } + if ((s_bl = map_id2bl(su->group->src_id)) == NULL) + s_bl = src; //Fallback on the trap itself, otherwise consider this a "caster versus enemy" scenario. + } + + switch (s_bl->type) + { + case BL_PC: + { + TBL_PC *sd = (TBL_PC*) s_bl; + if (sd->special_state.killer && s_bl != t_bl) + { + state |= BCT_ENEMY; //Is on a killing rampage :O + strip_enemy = 0; + } else + if (sd->duel_group && t_bl != s_bl && // Duel [LuzZza] + !( + (!battle_config.duel_allow_pvp && map[m].flag.pvp) || + (!battle_config.duel_allow_gvg && map_flag_gvg(m)) + )) + { + if (t_bl->type == BL_PC && + (sd->duel_group == ((TBL_PC*)t_bl)->duel_group)) + //Duel targets can ONLY be your enemy, nothing else. + return (BCT_ENEMY&flag)?1:-1; + else // You can't target anything out of your duel + return 0; + } + if (map_flag_gvg(m) && !sd->status.guild_id && + t_bl->type == BL_MOB && ((TBL_MOB*)t_bl)->guardian_data) + return 0; //If you don't belong to a guild, can't target guardians/emperium. + if (t_bl->type != BL_PC) + state |= BCT_ENEMY; //Natural enemy. + break; + } + case BL_MOB: + { + TBL_MOB*md = (TBL_MOB*)s_bl; + if(md->state.killer) // Is on a rampage too :D + state |= BCT_ENEMY; + if (!agit_flag && md->guardian_data && md->guardian_data->guild_id) + return 0; //Disable guardians/emperium owned by Guilds on non-woe times. + if (!md->special_state.ai) { //Normal mobs. + if (t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai) + state |= BCT_PARTY; //Normal mobs with no ai are friends. + else + state |= BCT_ENEMY; //However, all else are enemies. + } else { + if (t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai) + state |= BCT_ENEMY; //Natural enemy for AI mobs are normal mobs. + } + if (md->master_id && (s_bl = map_id2bl(md->master_id)) == NULL) + s_bl = &md->bl; //Fallback on the mob itself, otherwise consider this a "from master" scenario. + break; + } + case BL_HOMUNCULUS: + { + //[blackhole89] -- check homunculus' targeting by their masters. + if (t_bl->type == BL_MOB && !((TBL_MOB*)t_bl)->special_state.ai) + state |= BCT_ENEMY; //Default enemy. Normal mobs. + //Pass on to master. + s_bl=(struct block_list *)((struct homun_data*)src)->master; //Whoever is the master's enemy is the homunculus' enemy. + break; + } + case BL_PET: + { + TBL_PET *pd = (TBL_PET*)s_bl; + if (t_bl->type != BL_MOB && flag&BCT_ENEMY) + return 0; //Pet may not attack non-mobs. + if (t_bl->type == BL_MOB && ((TBL_MOB*)t_bl)->guardian_data && flag&BCT_ENEMY) + return 0; //pet may not attack Guardians/Emperium + if (t_bl->type != BL_PC) + state |= BCT_ENEMY; //Stock enemy type. + if (pd->msd) + s_bl = &pd->msd->bl; //"My master's enemies are my enemies..." + break; + } + case BL_SKILL: //Skill with no owner? Fishy, but let it through. + break; + default: //Invalid source of attack? + return 0; + } + + if ((flag&BCT_ALL) == BCT_ALL) { //All actually stands for all players/mobs + if (target->type == BL_MOB || target->type == BL_PC) + return 1; + else + return -1; + } else if (flag == BCT_NOONE) //Why would someone use this? no clue. + return -1; + + if (t_bl == s_bl) + { //No need for further testing. + state |= BCT_SELF|BCT_PARTY|BCT_GUILD; + if (state&BCT_ENEMY && strip_enemy) + state&=~BCT_ENEMY; + return (flag&state)?1:-1; + } + + if (map_flag_vs(m)) { //Check rivalry settings. + if (flag&(BCT_PARTY|BCT_ENEMY)) { + int s_party = status_get_party_id(s_bl); + if ( + !(map[m].flag.pvp && map[m].flag.pvp_noparty) && + !(map_flag_gvg(m) && map[m].flag.gvg_noparty) && + s_party && s_party == status_get_party_id(t_bl) + ) + state |= BCT_PARTY; + else + state |= BCT_ENEMY; + } + if (flag&(BCT_GUILD|BCT_ENEMY)) { + int s_guild = status_get_guild_id(s_bl); + int t_guild = status_get_guild_id(t_bl); + if ( + !(map[m].flag.pvp && map[m].flag.pvp_noguild) && + s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild)) + ) + state |= BCT_GUILD; + else + state |= BCT_ENEMY; + } + if (state&BCT_ENEMY && battle_config.pk_mode && !map_flag_gvg(m) && + s_bl->type == BL_PC && t_bl->type == BL_PC) + { //Prevent novice engagement on pk_mode (feature by Valaris) + TBL_PC *sd = (TBL_PC*)s_bl, *sd2 = (TBL_PC*)t_bl; + if ( + (sd->class_&MAPID_UPPERMASK) == MAPID_NOVICE || + (sd2->class_&MAPID_UPPERMASK) == MAPID_NOVICE || + sd->status.base_level < battle_config.pk_min_level || + sd2->status.base_level < battle_config.pk_min_level || + (battle_config.pk_level_range && ( + sd->status.base_level > sd2->status.base_level ? + sd->status.base_level - sd2->status.base_level : + sd2->status.base_level - sd->status.base_level ) + > battle_config.pk_level_range) + ) + state&=~BCT_ENEMY; + } + } else { //Non pvp/gvg, check party/guild settings. + if (flag&BCT_PARTY || state&BCT_ENEMY) { + int s_party = status_get_party_id(s_bl); + if(s_party && s_party == status_get_party_id(t_bl)) + state |= BCT_PARTY; + } + if (flag&BCT_GUILD || state&BCT_ENEMY) { + int s_guild = status_get_guild_id(s_bl); + int t_guild = status_get_guild_id(t_bl); + if(s_guild && t_guild && (s_guild == t_guild || guild_idisallied(s_guild, t_guild))) + state |= BCT_GUILD; + } + } + + if (!state) //If not an enemy, nor a guild, nor party, nor yourself, it's neutral. + state = BCT_NEUTRAL; + //Alliance state takes precedence over enemy one. + else if (state&BCT_ENEMY && strip_enemy && state&(BCT_SELF|BCT_PARTY|BCT_GUILD)) + state&=~BCT_ENEMY; + + return (flag&state)?1:-1; +} +/*========================================== + * 射程判定 + *------------------------------------------ + */ +int battle_check_range(struct block_list *src,struct block_list *bl,int range) +{ + nullpo_retr(0, src); + nullpo_retr(0, bl); + + if(src->m != bl->m) // 違うマップ + return 0; + + if (!check_distance_bl(src, bl, range)) + return 0; + + if(distance_bl(src, bl) < 2) //No need for path checking. + return 1; + + // ?瘧Q物判定 + return path_search_long(NULL,src->m,src->x,src->y,bl->x,bl->y); +} + +/*========================================== + * Return numerical value of a switch configuration (modified by [Yor]) + * on/off, english, fran軋is, deutsch, espaol + *------------------------------------------ + */ +int battle_config_switch(const char *str) { + if (strcmpi(str, "on") == 0 || strcmpi(str, "yes") == 0 || strcmpi(str, "oui") == 0 || strcmpi(str, "ja") == 0 || strcmpi(str, "si") == 0) + return 1; + if (strcmpi(str, "off") == 0 || strcmpi(str, "no") == 0 || strcmpi(str, "non") == 0 || strcmpi(str, "nein") == 0) + return 0; + return atoi(str); +} + +static const struct battle_data_short { + const char *str; + unsigned short *val; +} battle_data_short[] = { //List here battle_athena options which are type unsigned short! + { "warp_point_debug", &battle_config.warp_point_debug }, + { "enemy_critical_rate", &battle_config.enemy_critical_rate }, + { "enemy_str", &battle_config.enemy_str }, + { "enemy_perfect_flee", &battle_config.enemy_perfect_flee }, + { "casting_rate", &battle_config.cast_rate }, + { "delay_rate", &battle_config.delay_rate }, + { "delay_dependon_dex", &battle_config.delay_dependon_dex }, + { "skill_delay_attack_enable", &battle_config.sdelay_attack_enable }, + { "left_cardfix_to_right", &battle_config.left_cardfix_to_right }, + { "skill_add_range", &battle_config.skill_add_range }, + { "skill_out_range_consume", &battle_config.skill_out_range_consume }, + { "skillrange_by_distance", &battle_config.skillrange_by_distance }, + { "skillrange_from_weapon", &battle_config.use_weapon_skill_range }, + { "player_damage_delay_rate", &battle_config.pc_damage_delay_rate }, + { "defunit_not_enemy", &battle_config.defnotenemy }, + { "gvg_traps_target_all", &battle_config.vs_traps_bctall }, + { "traps_setting", &battle_config.traps_setting }, + { "clear_skills_on_death", &battle_config.clear_unit_ondeath }, + { "random_monster_checklv", &battle_config.random_monster_checklv }, + { "attribute_recover", &battle_config.attr_recover }, + { "flooritem_lifetime", &battle_config.flooritem_lifetime }, + { "item_auto_get", &battle_config.item_auto_get }, + { "drop_rate0item", &battle_config.drop_rate0item }, + { "pvp_exp", &battle_config.pvp_exp }, + { "gtb_pvp_only", &battle_config.gtb_pvp_only }, + { "guild_max_castles", &battle_config.guild_max_castles }, + { "death_penalty_type", &battle_config.death_penalty_type }, + { "death_penalty_base", &battle_config.death_penalty_base }, + { "death_penalty_job", &battle_config.death_penalty_job }, + { "restart_hp_rate", &battle_config.restart_hp_rate }, + { "restart_sp_rate", &battle_config.restart_sp_rate }, + { "mvp_hp_rate", &battle_config.mvp_hp_rate }, + { "monster_hp_rate", &battle_config.monster_hp_rate }, + { "monster_max_aspd", &battle_config.monster_max_aspd }, + { "view_range_rate", &battle_config.view_range_rate }, + { "chase_range_rate", &battle_config.chase_range_rate }, + { "atcommand_gm_only", &battle_config.atc_gmonly }, + { "atcommand_spawn_quantity_limit", &battle_config.atc_spawn_quantity_limit }, + { "atcommand_slave_clone_limit", &battle_config.atc_slave_clone_limit}, + { "gm_all_skill", &battle_config.gm_allskill }, + { "gm_all_skill_add_abra", &battle_config.gm_allskill_addabra }, + { "gm_all_equipment", &battle_config.gm_allequip }, + { "gm_skill_unconditional", &battle_config.gm_skilluncond }, + { "gm_join_chat", &battle_config.gm_join_chat }, + { "gm_kick_chat", &battle_config.gm_kick_chat }, + { "player_skillfree", &battle_config.skillfree }, + { "player_skillup_limit", &battle_config.skillup_limit }, + { "weapon_produce_rate", &battle_config.wp_rate }, + { "potion_produce_rate", &battle_config.pp_rate }, + { "monster_active_enable", &battle_config.monster_active_enable }, + { "monster_damage_delay_rate", &battle_config.monster_damage_delay_rate}, + { "monster_loot_type", &battle_config.monster_loot_type }, +// { "mob_skill_use", &battle_config.mob_skill_use }, //Deprecated + { "mob_skill_rate", &battle_config.mob_skill_rate }, + { "mob_skill_delay", &battle_config.mob_skill_delay }, + { "mob_count_rate", &battle_config.mob_count_rate }, + { "mob_spawn_delay", &battle_config.mob_spawn_delay }, + { "no_spawn_on_player", &battle_config.no_spawn_on_player }, + { "plant_spawn_delay", &battle_config.plant_spawn_delay }, + { "boss_spawn_delay", &battle_config.boss_spawn_delay }, + { "slaves_inherit_mode", &battle_config.slaves_inherit_mode }, + { "slaves_inherit_speed", &battle_config.slaves_inherit_speed }, + { "summons_inherit_effects", &battle_config.summons_inherit_effects }, + { "pc_damage_walk_delay_rate", &battle_config.pc_walk_delay_rate }, + { "damage_walk_delay_rate", &battle_config.walk_delay_rate }, + { "multihit_delay", &battle_config.multihit_delay }, + { "quest_skill_learn", &battle_config.quest_skill_learn }, + { "quest_skill_reset", &battle_config.quest_skill_reset }, + { "basic_skill_check", &battle_config.basic_skill_check }, + { "guild_emperium_check", &battle_config.guild_emperium_check }, + { "guild_exp_rate", &battle_config.guild_exp_rate }, + { "guild_exp_limit", &battle_config.guild_exp_limit }, + { "player_invincible_time", &battle_config.pc_invincible_time }, + { "pet_catch_rate", &battle_config.pet_catch_rate }, + { "pet_rename", &battle_config.pet_rename }, + { "pet_friendly_rate", &battle_config.pet_friendly_rate }, + { "pet_hungry_delay_rate", &battle_config.pet_hungry_delay_rate }, + { "pet_hungry_friendly_decrease", &battle_config.pet_hungry_friendly_decrease}, + { "pet_str", &battle_config.pet_str }, + { "pet_status_support", &battle_config.pet_status_support }, + { "pet_attack_support", &battle_config.pet_attack_support }, + { "pet_damage_support", &battle_config.pet_damage_support }, + { "pet_support_min_friendly", &battle_config.pet_support_min_friendly }, + { "pet_support_rate", &battle_config.pet_support_rate }, + { "pet_attack_exp_to_master", &battle_config.pet_attack_exp_to_master }, + { "pet_attack_exp_rate", &battle_config.pet_attack_exp_rate }, + { "pet_lv_rate", &battle_config.pet_lv_rate }, //Skotlex + { "pet_max_stats", &battle_config.pet_max_stats }, //Skotlex + { "pet_max_atk1", &battle_config.pet_max_atk1 }, //Skotlex + { "pet_max_atk2", &battle_config.pet_max_atk2 }, //Skotlex + { "pet_disable_in_gvg", &battle_config.pet_no_gvg }, //Skotlex + { "skill_min_damage", &battle_config.skill_min_damage }, + { "finger_offensive_type", &battle_config.finger_offensive_type }, + { "heal_exp", &battle_config.heal_exp }, + { "resurrection_exp", &battle_config.resurrection_exp }, + { "shop_exp", &battle_config.shop_exp }, + { "combo_delay_rate", &battle_config.combo_delay_rate }, + { "item_check", &battle_config.item_check }, + { "item_use_interval", &battle_config.item_use_interval }, + { "wedding_modifydisplay", &battle_config.wedding_modifydisplay }, + { "wedding_ignorepalette", &battle_config.wedding_ignorepalette }, //[Skotlex] + { "xmas_ignorepalette", &battle_config.xmas_ignorepalette }, // [Valaris] + { "natural_heal_weight_rate", &battle_config.natural_heal_weight_rate }, + { "item_name_override_grffile", &battle_config.item_name_override_grffile}, + { "item_equip_override_grffile", &battle_config.item_equip_override_grffile}, // [Celest] + { "item_slots_override_grffile", &battle_config.item_slots_override_grffile}, // [Celest] + { "indoors_override_grffile", &battle_config.indoors_override_grffile}, // [Celest] + { "skill_sp_override_grffile", &battle_config.skill_sp_override_grffile}, // [Celest] + { "cardillust_read_grffile", &battle_config.cardillust_read_grffile}, // [Celest] + { "arrow_decrement", &battle_config.arrow_decrement }, + { "max_aspd", &battle_config.max_aspd }, + { "max_walk_speed", &battle_config.max_walk_speed }, + { "max_lv", &battle_config.max_lv }, + { "aura_lv", &battle_config.aura_lv }, + { "max_parameter", &battle_config.max_parameter }, + { "max_baby_parameter", &battle_config.max_baby_parameter }, + { "max_def", &battle_config.max_def }, + { "over_def_bonus", &battle_config.over_def_bonus }, + { "skill_log", &battle_config.skill_log }, + { "battle_log", &battle_config.battle_log }, + { "save_log", &battle_config.save_log }, + { "error_log", &battle_config.error_log }, + { "etc_log", &battle_config.etc_log }, + { "save_clothcolor", &battle_config.save_clothcolor }, + { "undead_detect_type", &battle_config.undead_detect_type }, + { "auto_counter_type", &battle_config.auto_counter_type }, + { "min_hitrate", &battle_config.min_hitrate }, + { "max_hitrate", &battle_config.max_hitrate }, + { "agi_penalty_type", &battle_config.agi_penalty_type }, + { "agi_penalty_count", &battle_config.agi_penalty_count }, + { "agi_penalty_num", &battle_config.agi_penalty_num }, + { "agi_penalty_count_lv", &battle_config.agi_penalty_count_lv }, + { "vit_penalty_type", &battle_config.vit_penalty_type }, + { "vit_penalty_count", &battle_config.vit_penalty_count }, + { "vit_penalty_num", &battle_config.vit_penalty_num }, + { "vit_penalty_count_lv", &battle_config.vit_penalty_count_lv }, + { "weapon_defense_type", &battle_config.weapon_defense_type }, + { "magic_defense_type", &battle_config.magic_defense_type }, + { "skill_reiteration", &battle_config.skill_reiteration }, + { "skill_nofootset", &battle_config.skill_nofootset }, + { "player_cloak_check_type", &battle_config.pc_cloak_check_type }, + { "monster_cloak_check_type", &battle_config.monster_cloak_check_type }, + { "sense_type", &battle_config.estimation_type }, + { "gvg_short_attack_damage_rate", &battle_config.gvg_short_damage_rate }, + { "gvg_long_attack_damage_rate", &battle_config.gvg_long_damage_rate }, + { "gvg_weapon_attack_damage_rate", &battle_config.gvg_weapon_damage_rate }, + { "gvg_magic_attack_damage_rate", &battle_config.gvg_magic_damage_rate }, + { "gvg_misc_attack_damage_rate", &battle_config.gvg_misc_damage_rate }, + { "gvg_flee_penalty", &battle_config.gvg_flee_penalty }, + { "pk_short_attack_damage_rate", &battle_config.pk_short_damage_rate }, + { "pk_long_attack_damage_rate", &battle_config.pk_long_damage_rate }, + { "pk_weapon_attack_damage_rate", &battle_config.pk_weapon_damage_rate }, + { "pk_magic_attack_damage_rate", &battle_config.pk_magic_damage_rate }, + { "pk_misc_attack_damage_rate", &battle_config.pk_misc_damage_rate }, + { "mob_changetarget_byskill", &battle_config.mob_changetarget_byskill}, + { "attack_direction_change", &battle_config.attack_direction_change }, + { "land_skill_limit", &battle_config.land_skill_limit }, + { "party_skill_penalty", &battle_config.party_skill_penalty }, + { "monster_class_change_full_recover", &battle_config.monster_class_change_full_recover }, + { "produce_item_name_input", &battle_config.produce_item_name_input }, + { "produce_potion_name_input", &battle_config.produce_potion_name_input}, + { "making_arrow_name_input", &battle_config.making_arrow_name_input }, + { "holywater_name_input", &battle_config.holywater_name_input }, + { "cdp_name_input", &battle_config.cdp_name_input }, + { "display_delay_skill_fail", &battle_config.display_delay_skill_fail }, + { "display_snatcher_skill_fail", &battle_config.display_snatcher_skill_fail }, + { "chat_warpportal", &battle_config.chat_warpportal }, + { "mob_warpportal", &battle_config.mob_warpportal }, + { "dead_branch_active", &battle_config.dead_branch_active }, + { "show_steal_in_same_party", &battle_config.show_steal_in_same_party }, + { "show_party_share_picker", &battle_config.party_show_share_picker }, + { "party_item_share_type", &battle_config.party_share_type }, + { "mob_ghostring_fix", &battle_config.mob_ghostring_fix }, + { "attack_attr_none", &battle_config.attack_attr_none }, + { "gx_allhit", &battle_config.gx_allhit }, + { "gx_disptype", &battle_config.gx_disptype }, + { "devotion_level_difference", &battle_config.devotion_level_difference }, + { "player_skill_partner_check", &battle_config.player_skill_partner_check}, + { "hide_GM_session", &battle_config.hide_GM_session }, + { "invite_request_check", &battle_config.invite_request_check }, + { "skill_removetrap_type", &battle_config.skill_removetrap_type }, + { "disp_experience", &battle_config.disp_experience }, + { "disp_zeny", &battle_config.disp_zeny }, + { "castle_defense_rate", &battle_config.castle_defense_rate }, + { "hp_rate", &battle_config.hp_rate }, + { "sp_rate", &battle_config.sp_rate }, + { "gm_cant_drop_min_lv", &battle_config.gm_cant_drop_min_lv }, + { "gm_cant_drop_max_lv", &battle_config.gm_cant_drop_max_lv }, + { "disp_hpmeter", &battle_config.disp_hpmeter }, + { "bone_drop", &battle_config.bone_drop }, + { "buyer_name", &battle_config.buyer_name }, + { "skill_wall_check", &battle_config.skill_wall_check }, + { "cell_stack_limit", &battle_config.cell_stack_limit }, +// eAthena additions + { "item_logarithmic_drops", &battle_config.logarithmic_drops }, + { "item_drop_common_min", &battle_config.item_drop_common_min }, // Added by TyrNemesis^ + { "item_drop_common_max", &battle_config.item_drop_common_max }, + { "item_drop_equip_min", &battle_config.item_drop_equip_min }, + { "item_drop_equip_max", &battle_config.item_drop_equip_max }, + { "item_drop_card_min", &battle_config.item_drop_card_min }, + { "item_drop_card_max", &battle_config.item_drop_card_max }, + { "item_drop_mvp_min", &battle_config.item_drop_mvp_min }, + { "item_drop_mvp_max", &battle_config.item_drop_mvp_max }, // End Addition + { "item_drop_heal_min", &battle_config.item_drop_heal_min }, + { "item_drop_heal_max", &battle_config.item_drop_heal_max }, + { "item_drop_use_min", &battle_config.item_drop_use_min }, + { "item_drop_use_max", &battle_config.item_drop_use_max }, + { "item_drop_add_min", &battle_config.item_drop_adddrop_min }, + { "item_drop_add_max", &battle_config.item_drop_adddrop_max }, + { "item_drop_treasure_min", &battle_config.item_drop_treasure_min }, + { "item_drop_treasure_max", &battle_config.item_drop_treasure_max }, + { "prevent_logout", &battle_config.prevent_logout }, // Added by RoVeRT + { "alchemist_summon_reward", &battle_config.alchemist_summon_reward }, // [Valaris] + { "drops_by_luk", &battle_config.drops_by_luk }, // [Valaris] + { "drops_by_luk2", &battle_config.drops_by_luk2 }, // [Skotlex] + { "equip_natural_break_rate", &battle_config.equip_natural_break_rate }, + { "equip_self_break_rate", &battle_config.equip_self_break_rate }, + { "equip_skill_break_rate", &battle_config.equip_skill_break_rate }, + { "pk_mode", &battle_config.pk_mode }, // [Valaris] + { "pk_level_range", &battle_config.pk_level_range }, + { "manner_system", &battle_config.manner_system }, // [Komurka] + { "pet_equip_required", &battle_config.pet_equip_required }, // [Valaris] + { "multi_level_up", &battle_config.multi_level_up }, // [Valaris] + { "max_exp_gain_rate", &battle_config.max_exp_gain_rate }, // [Skotlex] + { "backstab_bow_penalty", &battle_config.backstab_bow_penalty }, + { "night_at_start", &battle_config.night_at_start }, // added by [Yor] + { "show_mob_hp", &battle_config.show_mob_hp }, // [Valaris] + { "ban_spoof_namer", &battle_config.ban_spoof_namer }, // added by [Yor] + { "hack_info_GM_level", &battle_config.hack_info_GM_level }, // added by [Yor] + { "any_warp_GM_min_level", &battle_config.any_warp_GM_min_level }, // added by [Yor] + { "packet_ver_flag", &battle_config.packet_ver_flag }, // added by [Yor] + { "min_hair_style", &battle_config.min_hair_style }, // added by [MouseJstr] + { "max_hair_style", &battle_config.max_hair_style }, // added by [MouseJstr] + { "min_hair_color", &battle_config.min_hair_color }, // added by [MouseJstr] + { "max_hair_color", &battle_config.max_hair_color }, // added by [MouseJstr] + { "min_cloth_color", &battle_config.min_cloth_color }, // added by [MouseJstr] + { "max_cloth_color", &battle_config.max_cloth_color }, // added by [MouseJstr] + { "pet_hair_style", &battle_config.pet_hair_style }, // added by [Skotlex] + { "castrate_dex_scale", &battle_config.castrate_dex_scale }, // added by [MouseJstr] + { "area_size", &battle_config.area_size }, // added by [MouseJstr] + { "muting_players", &battle_config.muting_players}, // added by [Apple] + { "zeny_from_mobs", &battle_config.zeny_from_mobs}, // [Valaris] + { "mobs_level_up", &battle_config.mobs_level_up}, // [Valaris] + { "mobs_level_up_exp_rate", &battle_config.mobs_level_up_exp_rate}, // [Valaris] + { "pk_min_level", &battle_config.pk_min_level}, // [celest] + { "skill_steal_type", &battle_config.skill_steal_type}, // [celest] + { "skill_steal_rate", &battle_config.skill_steal_rate}, // [celest] + { "skill_steal_max_tries", &battle_config.skill_steal_max_tries}, // [Lupus] +// { "night_darkness_level", &battle_config.night_darkness_level}, // [celest] + { "motd_type", &battle_config.motd_type}, // [celest] + { "allow_atcommand_when_mute", &battle_config.allow_atcommand_when_mute}, // [celest] + { "finding_ore_rate", &battle_config.finding_ore_rate}, // [celest] + { "exp_calc_type", &battle_config.exp_calc_type}, // [celest] + { "min_skill_delay_limit", &battle_config.min_skill_delay_limit}, // [celest] + { "default_skill_delay", &battle_config.default_skill_delay}, // [Skotlex] + { "require_glory_guild", &battle_config.require_glory_guild}, // [celest] + { "idle_no_share", &battle_config.idle_no_share}, // [celest], for a feature by [MouseJstr] + { "party_even_share_bonus", &battle_config.party_even_share_bonus}, + { "delay_battle_damage", &battle_config.delay_battle_damage}, // [celest] + { "hide_woe_damage", &battle_config.hide_woe_damage}, // [Skotlex] + { "display_version", &battle_config.display_version}, // [Ancyker], for a feature by...? + { "who_display_aid", &battle_config.who_display_aid}, // [Ancyker], for a feature by...? + { "display_hallucination", &battle_config.display_hallucination}, // [Skotlex] + { "use_statpoint_table", &battle_config.use_statpoint_table}, // [Skotlex] + { "ignore_items_gender", &battle_config.ignore_items_gender}, // [Lupus] + { "copyskill_restrict", &battle_config.copyskill_restrict}, // [Aru] + { "berserk_cancels_buffs", &battle_config.berserk_cancels_buffs}, // [Aru] + + { "debuff_on_logout", &battle_config.debuff_on_logout}, + { "monster_ai", &battle_config.mob_ai}, + { "dynamic_mobs", &battle_config.dynamic_mobs}, + { "mob_remove_damaged", &battle_config.mob_remove_damaged}, + { "show_hp_sp_drain", &battle_config.show_hp_sp_drain}, // [Skotlex] + { "show_hp_sp_gain", &battle_config.show_hp_sp_gain}, // [Skotlex] + { "mob_npc_event_type", &battle_config.mob_npc_event_type}, + { "mob_clear_delay", &battle_config.mob_clear_delay}, // [Valaris] + { "character_size", &battle_config.character_size}, // [Lupus] + { "mob_max_skilllvl", &battle_config.mob_max_skilllvl}, // [Lupus] + { "retaliate_to_master", &battle_config.retaliate_to_master}, // [Skotlex] + { "rare_drop_announce", &battle_config.rare_drop_announce}, // [Lupus] + { "firewall_hits_on_undead", &battle_config.firewall_hits_on_undead}, // [Skotlex] + { "title_lvl1", &battle_config.title_lvl1}, // [Lupus] + { "title_lvl2", &battle_config.title_lvl2}, // [Lupus] + { "title_lvl3", &battle_config.title_lvl3}, // [Lupus] + { "title_lvl4", &battle_config.title_lvl4}, // [Lupus] + { "title_lvl5", &battle_config.title_lvl5}, // [Lupus] + { "title_lvl6", &battle_config.title_lvl6}, // [Lupus] + { "title_lvl7", &battle_config.title_lvl7}, // [Lupus] + { "title_lvl8", &battle_config.title_lvl8}, // [Lupus] + + { "duel_enable", &battle_config.duel_enable}, // [LuzZza] + { "duel_allow_pvp", &battle_config.duel_allow_pvp}, // [LuzZza] + { "duel_allow_gvg", &battle_config.duel_allow_gvg}, // [LuzZza] + { "duel_allow_teleport", &battle_config.duel_allow_teleport}, // [LuzZza] + { "duel_autoleave_when_die", &battle_config.duel_autoleave_when_die}, //[LuzZza] + { "duel_time_interval", &battle_config.duel_time_interval}, // [LuzZza] + + { "skip_teleport_lv1_menu", &battle_config.skip_teleport_lv1_menu}, // [LuzZza] + { "allow_skill_without_day", &battle_config.allow_skill_without_day}, // [Komurka] + { "allow_es_magic_player", &battle_config.allow_es_magic_pc }, + { "skill_caster_check", &battle_config.skill_caster_check }, + { "status_cast_cancel", &battle_config.sc_castcancel }, + { "pc_status_def_rate", &battle_config.pc_sc_def_rate }, + { "mob_status_def_rate", &battle_config.mob_sc_def_rate }, + { "pc_max_status_def", &battle_config.pc_max_sc_def }, + { "mob_max_status_def", &battle_config.mob_max_sc_def }, + { "sg_miracle_skill_ratio", &battle_config.sg_miracle_skill_ratio }, + { "autospell_stacking", &battle_config.autospell_stacking }, + { "override_mob_names", &battle_config.override_mob_names }, +}; + +static const struct battle_data_int { + const char *str; + int *val; +} battle_data_int[] = { //List here battle_athena options which are type int! + { "item_first_get_time", &battle_config.item_first_get_time }, + { "item_second_get_time", &battle_config.item_second_get_time }, + { "item_third_get_time", &battle_config.item_third_get_time }, + { "mvp_item_first_get_time", &battle_config.mvp_item_first_get_time }, + { "mvp_item_second_get_time", &battle_config.mvp_item_second_get_time }, + { "mvp_item_third_get_time", &battle_config.mvp_item_third_get_time }, + { "base_exp_rate", &battle_config.base_exp_rate }, + { "job_exp_rate", &battle_config.job_exp_rate }, + { "zeny_penalty", &battle_config.zeny_penalty }, + { "mvp_exp_rate", &battle_config.mvp_exp_rate }, + { "natural_healhp_interval", &battle_config.natural_healhp_interval }, + { "natural_healsp_interval", &battle_config.natural_healsp_interval }, + { "natural_heal_skill_interval", &battle_config.natural_heal_skill_interval}, + { "max_hp", &battle_config.max_hp }, + { "max_sp", &battle_config.max_sp }, + { "max_cart_weight", &battle_config.max_cart_weight }, + { "gvg_eliminate_time", &battle_config.gvg_eliminate_time }, + { "vending_max_value", &battle_config.vending_max_value }, +// eAthena additions + { "item_rate_mvp", &battle_config.item_rate_mvp }, + { "item_rate_common", &battle_config.item_rate_common }, // Added by RoVeRT + { "item_rate_common_boss", &battle_config.item_rate_common_boss }, // [Reddozen] + { "item_rate_equip", &battle_config.item_rate_equip }, + { "item_rate_equip_boss", &battle_config.item_rate_equip_boss }, // [Reddozen] + { "item_rate_card", &battle_config.item_rate_card }, // End Addition + { "item_rate_card_boss", &battle_config.item_rate_card_boss }, // [Reddozen] + { "item_rate_heal", &battle_config.item_rate_heal }, // Added by Valaris + { "item_rate_heal_boss", &battle_config.item_rate_heal_boss }, // [Reddozen] + { "item_rate_use", &battle_config.item_rate_use }, // End + { "item_rate_use_boss", &battle_config.item_rate_use_boss }, // [Reddozen] + { "item_rate_adddrop", &battle_config.item_rate_adddrop }, // End + { "item_rate_treasure", &battle_config.item_rate_treasure }, // End + { "day_duration", &battle_config.day_duration }, // added by [Yor] + { "night_duration", &battle_config.night_duration }, // added by [Yor] + { "mob_remove_delay", &battle_config.mob_remove_delay }, + { "sg_miracle_skill_duration", &battle_config.sg_miracle_skill_duration }, + +}; + +int battle_set_value(char *w1, char *w2) { + int i; + for(i = 0; i < sizeof(battle_data_short) / (sizeof(battle_data_short[0])); i++) + if (strcmpi(w1, battle_data_short[i].str) == 0) { + * battle_data_short[i].val = battle_config_switch(w2); + return 1; + } + for(i = 0; i < sizeof(battle_data_int) / (sizeof(battle_data_int[0])); i++) + if (strcmpi(w1, battle_data_int[i].str) == 0) { + *battle_data_int[i].val = battle_config_switch(w2); + return 1; + } + return 0; +} + +int battle_get_value(char *w1) { + int i; + for(i = 0; i < sizeof(battle_data_short) / (sizeof(battle_data_short[0])); i++) + if (strcmpi(w1, battle_data_short[i].str) == 0) { + return * battle_data_short[i].val; + } + for(i = 0; i < sizeof(battle_data_int) / (sizeof(battle_data_int[0])); i++) + if (strcmpi(w1, battle_data_int[i].str) == 0) { + return *battle_data_int[i].val; + } + return 0; +} + +void battle_set_defaults() { + battle_config.warp_point_debug=0; + battle_config.enemy_critical_rate=0; + battle_config.enemy_str=1; + battle_config.enemy_perfect_flee=0; + battle_config.cast_rate=100; + battle_config.delay_rate=100; + battle_config.delay_dependon_dex=0; + battle_config.sdelay_attack_enable=0; + battle_config.left_cardfix_to_right=0; + battle_config.skill_add_range=0; + battle_config.skill_out_range_consume=1; + battle_config.skillrange_by_distance=BL_MOB|BL_PET; + battle_config.use_weapon_skill_range=0; + battle_config.pc_damage_delay_rate=100; + battle_config.defnotenemy=0; + battle_config.vs_traps_bctall=BL_PC; + battle_config.traps_setting=0; + battle_config.clear_unit_ondeath=BL_ALL; + battle_config.random_monster_checklv=1; + battle_config.attr_recover=1; + battle_config.flooritem_lifetime=LIFETIME_FLOORITEM*1000; + battle_config.item_auto_get=0; + battle_config.item_first_get_time=3000; + battle_config.item_second_get_time=1000; + battle_config.item_third_get_time=1000; + battle_config.mvp_item_first_get_time=10000; + battle_config.mvp_item_second_get_time=10000; + battle_config.mvp_item_third_get_time=2000; + + battle_config.drop_rate0item=0; + battle_config.base_exp_rate=100; + battle_config.job_exp_rate=100; + battle_config.pvp_exp=1; + battle_config.gtb_pvp_only=0; + battle_config.death_penalty_type=0; + battle_config.death_penalty_base=0; + battle_config.death_penalty_job=0; + battle_config.zeny_penalty=0; + battle_config.restart_hp_rate=0; + battle_config.restart_sp_rate=0; + battle_config.mvp_exp_rate=100; + battle_config.mvp_hp_rate=100; + battle_config.monster_hp_rate=100; + battle_config.monster_max_aspd=199; + battle_config.view_range_rate=100; + battle_config.chase_range_rate=100; + battle_config.atc_gmonly=0; + battle_config.atc_spawn_quantity_limit=0; + battle_config.atc_slave_clone_limit=0; + battle_config.gm_allskill=0; + battle_config.gm_allequip=0; + battle_config.gm_skilluncond=0; + battle_config.gm_join_chat=0; + battle_config.gm_kick_chat=0; + battle_config.guild_max_castles=0; + battle_config.skillfree = 0; + battle_config.skillup_limit = 0; + battle_config.wp_rate=100; + battle_config.pp_rate=100; + battle_config.monster_active_enable=1; + battle_config.monster_damage_delay_rate=100; + battle_config.monster_loot_type=0; + battle_config.mob_skill_rate=100; + battle_config.mob_skill_delay=100; + battle_config.mob_count_rate=100; + battle_config.mob_spawn_delay=100; + battle_config.no_spawn_on_player=0; + battle_config.plant_spawn_delay=100; + battle_config.boss_spawn_delay=100; + battle_config.slaves_inherit_mode=1; + battle_config.slaves_inherit_speed=1; + battle_config.summons_inherit_effects=1; + battle_config.pc_walk_delay_rate=20; + battle_config.walk_delay_rate=100; + battle_config.multihit_delay=80; + battle_config.quest_skill_learn=0; + battle_config.quest_skill_reset=1; + battle_config.basic_skill_check=1; + battle_config.guild_emperium_check=1; + battle_config.guild_exp_limit=50; + battle_config.guild_exp_rate=100; + battle_config.pc_invincible_time = 5000; + battle_config.pet_catch_rate=100; + battle_config.pet_rename=0; + battle_config.pet_friendly_rate=100; + battle_config.pet_hungry_delay_rate=100; + battle_config.pet_hungry_friendly_decrease=5; + battle_config.pet_str=0; + battle_config.pet_status_support=0; + battle_config.pet_attack_support=0; + battle_config.pet_damage_support=0; + battle_config.pet_support_min_friendly=900; + battle_config.pet_support_rate=100; + battle_config.pet_attack_exp_to_master=0; + battle_config.pet_attack_exp_rate=100; + battle_config.pet_lv_rate=0; //Skotlex + battle_config.pet_max_stats=99; //Skotlex + battle_config.pet_max_atk1=750; //Skotlex + battle_config.pet_max_atk2=1000; //Skotlex + battle_config.pet_no_gvg=0; //Skotlex + battle_config.skill_min_damage=6; //Ishizu claims that magic and misc attacks always do at least div_ damage. [Skotlex] + battle_config.finger_offensive_type=0; + battle_config.heal_exp=0; + battle_config.resurrection_exp=0; + battle_config.shop_exp=0; + battle_config.combo_delay_rate=100; + battle_config.item_check=1; + battle_config.item_use_interval=100; //Use some very low value that won't bother players, but should cap bots. + battle_config.wedding_modifydisplay=0; + battle_config.wedding_ignorepalette=0; + battle_config.xmas_ignorepalette=0; // [Valaris] + battle_config.natural_healhp_interval=6000; + battle_config.natural_healsp_interval=8000; + battle_config.natural_heal_skill_interval=10000; + battle_config.natural_heal_weight_rate=50; + battle_config.item_name_override_grffile=1; + battle_config.item_equip_override_grffile=0; // [Celest] + battle_config.item_slots_override_grffile=0; // [Celest] + battle_config.indoors_override_grffile=0; // [Celest] + battle_config.skill_sp_override_grffile=0; // [Celest] + battle_config.cardillust_read_grffile=0; // [Celest] + battle_config.arrow_decrement=1; + battle_config.max_aspd = 199; + battle_config.max_walk_speed = 300; + battle_config.max_hp = 32500; + battle_config.max_sp = 32500; + battle_config.max_lv = 99; // [MouseJstr] + battle_config.aura_lv = 99; // [Skotlex] + battle_config.max_parameter = 99; + battle_config.max_baby_parameter = 80; + battle_config.max_cart_weight = 8000; + battle_config.max_def = 99; // [Skotlex] + battle_config.over_def_bonus = 0; // [Skotlex] + battle_config.skill_log = 0; + battle_config.battle_log = 0; + battle_config.save_log = 0; + battle_config.error_log = 1; + battle_config.etc_log = 1; + battle_config.save_clothcolor = 0; + battle_config.undead_detect_type = 0; + battle_config.auto_counter_type = BL_ALL; + battle_config.min_hitrate = 5; + battle_config.max_hitrate = 100; + battle_config.agi_penalty_type = 1; + battle_config.agi_penalty_count = 3; + battle_config.agi_penalty_num = 10; + battle_config.agi_penalty_count_lv = ATK_FLEE; + battle_config.vit_penalty_type = 1; + battle_config.vit_penalty_count = 3; + battle_config.vit_penalty_num = 5; + battle_config.vit_penalty_count_lv = ATK_DEF; + battle_config.weapon_defense_type = 0; + battle_config.magic_defense_type = 0; + battle_config.skill_reiteration = 0; + battle_config.skill_nofootset = BL_PC; + battle_config.pc_cloak_check_type = 1; + battle_config.monster_cloak_check_type = 0; + battle_config.estimation_type = 3; + battle_config.gvg_short_damage_rate = 100; + battle_config.gvg_long_damage_rate = 75; + battle_config.gvg_weapon_damage_rate = 60; + battle_config.gvg_magic_damage_rate = 50; + battle_config.gvg_misc_damage_rate = 60; + battle_config.gvg_flee_penalty = 20; + battle_config.gvg_eliminate_time = 7000; + + battle_config.pk_short_damage_rate = 80; + battle_config.pk_long_damage_rate = 70; + battle_config.pk_weapon_damage_rate = 60; + battle_config.pk_magic_damage_rate = 60; + battle_config.pk_misc_damage_rate = 60; + + battle_config.mob_changetarget_byskill = 0; + battle_config.attack_direction_change = BL_ALL; + battle_config.land_skill_limit = BL_ALL; + battle_config.party_skill_penalty = 1; + battle_config.monster_class_change_full_recover = 0; + battle_config.produce_item_name_input = 1; + battle_config.produce_potion_name_input = 1; + battle_config.making_arrow_name_input = 1; + battle_config.holywater_name_input = 1; + battle_config.cdp_name_input = 1; + battle_config.display_delay_skill_fail = 1; + battle_config.display_snatcher_skill_fail = 1; + battle_config.chat_warpportal = 0; + battle_config.mob_warpportal = 0; + battle_config.dead_branch_active = 0; + battle_config.vending_max_value = 10000000; + battle_config.show_steal_in_same_party = 0; + battle_config.party_share_type = 0; + battle_config.party_show_share_picker = 0; + battle_config.attack_attr_none = 0; + battle_config.mob_ghostring_fix = 1; + battle_config.gx_allhit = 1; + battle_config.gx_disptype = 1; + battle_config.devotion_level_difference = 10; + battle_config.player_skill_partner_check = 1; + battle_config.hide_GM_session = 0; + battle_config.invite_request_check = 1; + battle_config.skill_removetrap_type = 0; + battle_config.disp_experience = 0; + battle_config.disp_zeny = 0; + battle_config.castle_defense_rate = 100; + battle_config.hp_rate = 100; + battle_config.sp_rate = 100; + battle_config.gm_cant_drop_min_lv = 1; + battle_config.gm_cant_drop_max_lv = 0; + battle_config.disp_hpmeter = 60; + battle_config.skill_wall_check = 1; + battle_config.cell_stack_limit = 1; + battle_config.bone_drop = 0; + battle_config.buyer_name = 1; + +// eAthena additions + battle_config.item_rate_mvp=100; + battle_config.item_rate_common = 100; + battle_config.item_rate_common_boss = 100; // [Reddozen] + battle_config.item_rate_equip = 100; + battle_config.item_rate_equip_boss = 100; // [Reddozen] + battle_config.item_rate_card = 100; + battle_config.item_rate_card_boss = 100; // [Reddozen] + battle_config.item_rate_heal = 100; // Added by Valaris + battle_config.item_rate_heal_boss = 100; // [Reddozen] + battle_config.item_rate_use = 100; // End + battle_config.item_rate_use_boss = 100; // [Reddozen] + battle_config.item_rate_adddrop = 100; + battle_config.item_rate_treasure = 100; + battle_config.logarithmic_drops = 0; + battle_config.item_drop_common_min=1; // Added by TyrNemesis^ + battle_config.item_drop_common_max=10000; + battle_config.item_drop_equip_min=1; + battle_config.item_drop_equip_max=10000; + battle_config.item_drop_card_min=1; + battle_config.item_drop_card_max=10000; + battle_config.item_drop_mvp_min=1; + battle_config.item_drop_mvp_max=10000; // End Addition + battle_config.item_drop_heal_min=1; // Added by Valaris + battle_config.item_drop_heal_max=10000; + battle_config.item_drop_use_min=1; + battle_config.item_drop_use_max=10000; // End + battle_config.item_drop_adddrop_min=1; + battle_config.item_drop_adddrop_max=10000; + battle_config.item_drop_treasure_min=1; + battle_config.item_drop_treasure_max=10000; + battle_config.prevent_logout = 10000; // Added by RoVeRT + battle_config.drops_by_luk = 0; // [Valaris] + battle_config.drops_by_luk2 = 0; + battle_config.equip_natural_break_rate = 1; + battle_config.equip_self_break_rate = 100; // [Valaris], adapted by [Skotlex] + battle_config.equip_skill_break_rate = 100; // [Valaris], adapted by [Skotlex] + battle_config.pk_mode = 0; // [Valaris] + battle_config.pk_level_range = 0; // [Skotlex] + battle_config.manner_system = 1; // [Valaris] + battle_config.pet_equip_required = 0; // [Valaris] + battle_config.multi_level_up = 0; // [Valaris] + battle_config.max_exp_gain_rate = 0; // [Skotlex] + battle_config.backstab_bow_penalty = 0; // Akaru + battle_config.night_at_start = 0; // added by [Yor] + battle_config.day_duration = 2*60*60*1000; // added by [Yor] (2 hours) + battle_config.night_duration = 30*60*1000; // added by [Yor] (30 minutes) + battle_config.show_mob_hp = 0; // [Valaris] + battle_config.ban_spoof_namer = 5; // added by [Yor] (default: 5 minutes) + battle_config.hack_info_GM_level = 60; // added by [Yor] (default: 60, GM level) + battle_config.any_warp_GM_min_level = 20; // added by [Yor] + battle_config.packet_ver_flag = 1023; // added by [Yor] + battle_config.min_hair_style = 0; + battle_config.max_hair_style = 23; + battle_config.min_hair_color = 0; + battle_config.max_hair_color = 9; + battle_config.min_cloth_color = 0; + battle_config.max_cloth_color = 4; + battle_config.pet_hair_style = 100; + battle_config.zeny_from_mobs = 0; + battle_config.mobs_level_up = 0; // [Valaris] + battle_config.mobs_level_up_exp_rate = 1; // [Valaris] + battle_config.pk_min_level = 55; + battle_config.skill_steal_type = 1; + battle_config.skill_steal_rate = 100; + battle_config.skill_steal_max_tries = 15; //=16 tries +// battle_config.night_darkness_level = 9; + battle_config.motd_type = 0; + battle_config.allow_atcommand_when_mute = 0; + battle_config.finding_ore_rate = 100; + battle_config.castrate_dex_scale = 150; + battle_config.area_size = 14; + battle_config.exp_calc_type = 1; + battle_config.min_skill_delay_limit = 100; + battle_config.default_skill_delay = 300; //Default skill delay according to official servers. + battle_config.require_glory_guild = 0; + battle_config.idle_no_share = 0; + battle_config.party_even_share_bonus = 0; + battle_config.delay_battle_damage = 1; + battle_config.hide_woe_damage = 0; + battle_config.display_version = 1; + battle_config.who_display_aid = 0; + battle_config.display_hallucination = 1; + battle_config.ignore_items_gender = 1; + battle_config.copyskill_restrict = 2; + battle_config.berserk_cancels_buffs = 1; + battle_config.debuff_on_logout = 1; + battle_config.use_statpoint_table = 1; + battle_config.mob_ai = 0; + battle_config.dynamic_mobs = 1; // use Dynamic Mobs [Wizputer] + battle_config.mob_remove_damaged = 1; // Dynamic Mobs - Remove mobs even if damaged [Wizputer] + battle_config.mob_remove_delay = 60000; + battle_config.show_hp_sp_drain = 0; //Display drained hp/sp from attacks + battle_config.show_hp_sp_gain = 1; //Display gained hp/sp from mob-kills + battle_config.mob_npc_event_type = 1; //Execute npc-event on player that delivered final blow. + battle_config.mob_clear_delay = 0; + battle_config.character_size = 3; //3: Peco riders Size=2, Baby Class Riders Size=1 [Lupus] + battle_config.mob_max_skilllvl = MAX_SKILL_LEVEL; //max possible level of monsters skills [Lupus] + battle_config.retaliate_to_master = 1; //Make mobs retaliate against the master rather than the mob that attacked them. [Skotlex] + battle_config.rare_drop_announce = 1; //show global announces for rare items drops (<= 0.01% chance) [Lupus] + battle_config.firewall_hits_on_undead = 1; + battle_config.title_lvl1 = 1; //Players Titles for @who, etc commands [Lupus] + battle_config.title_lvl2 = 10; + battle_config.title_lvl3 = 20; + battle_config.title_lvl4 = 40; + battle_config.title_lvl5 = 50; + battle_config.title_lvl6 = 60; + battle_config.title_lvl7 = 80; + battle_config.title_lvl8 = 99; + + battle_config.duel_enable = 1; + battle_config.duel_allow_pvp = 0; + battle_config.duel_allow_pvp = 0; + battle_config.duel_allow_teleport = 0; + battle_config.duel_autoleave_when_die = 1; + battle_config.duel_time_interval = 60; + + battle_config.skip_teleport_lv1_menu = 0; + battle_config.allow_skill_without_day = 0; + battle_config.allow_es_magic_pc = 0; + + battle_config.skill_caster_check = 1; + battle_config.sc_castcancel = 0; + battle_config.pc_sc_def_rate = 100; + battle_config.mob_sc_def_rate = 100; + battle_config.pc_max_sc_def = 10000; + battle_config.mob_max_sc_def = 5000; + battle_config.sg_miracle_skill_ratio=1; + battle_config.sg_miracle_skill_duration=600000; + battle_config.autospell_stacking = 0; + battle_config.override_mob_names = 0; +} + +void battle_validate_conf() { + if(battle_config.flooritem_lifetime < 1000) + battle_config.flooritem_lifetime = LIFETIME_FLOORITEM*1000; +/* if(battle_config.restart_hp_rate < 0) + battle_config.restart_hp_rate = 0; + else*/ if(battle_config.restart_hp_rate > 100) + battle_config.restart_hp_rate = 100; +/* if(battle_config.restart_sp_rate < 0) + battle_config.restart_sp_rate = 0; + else*/ if(battle_config.restart_sp_rate > 100) + battle_config.restart_sp_rate = 100; + if(battle_config.natural_healhp_interval < NATURAL_HEAL_INTERVAL) + battle_config.natural_healhp_interval=NATURAL_HEAL_INTERVAL; + if(battle_config.natural_healsp_interval < NATURAL_HEAL_INTERVAL) + battle_config.natural_healsp_interval=NATURAL_HEAL_INTERVAL; + if(battle_config.natural_heal_skill_interval < NATURAL_HEAL_INTERVAL) + battle_config.natural_heal_skill_interval=NATURAL_HEAL_INTERVAL; + if(battle_config.natural_heal_weight_rate < 50) + battle_config.natural_heal_weight_rate = 50; + if(battle_config.natural_heal_weight_rate > 101) + battle_config.natural_heal_weight_rate = 101; + battle_config.monster_max_aspd = 2000 - battle_config.monster_max_aspd*10; + if(battle_config.monster_max_aspd < 10) + battle_config.monster_max_aspd = 10; + if(battle_config.monster_max_aspd > 1000) + battle_config.monster_max_aspd = 1000; + battle_config.max_aspd = 2000 - battle_config.max_aspd*10; + if(battle_config.max_aspd < 10) + battle_config.max_aspd = 10; + if(battle_config.max_aspd > 1000) + battle_config.max_aspd = 1000; + + if (battle_config.max_walk_speed < 100) + battle_config.max_walk_speed = 100; + battle_config.max_walk_speed = 100*DEFAULT_WALK_SPEED/battle_config.max_walk_speed; + if (battle_config.max_walk_speed < 1) + battle_config.max_walk_speed = 1; + + if(battle_config.hp_rate < 1) + battle_config.hp_rate = 1; + if(battle_config.sp_rate < 1) + battle_config.sp_rate = 1; + if(battle_config.max_hp > 1000000000) + battle_config.max_hp = 1000000000; + if(battle_config.max_hp < 100) + battle_config.max_hp = 100; + if(battle_config.max_sp > 1000000000) + battle_config.max_sp = 1000000000; + if(battle_config.max_sp < 100) + battle_config.max_sp = 100; + if(battle_config.max_parameter < 10) + battle_config.max_parameter = 10; + if(battle_config.max_parameter > 10000) + battle_config.max_parameter = 10000; + if(battle_config.max_baby_parameter < 10) + battle_config.max_baby_parameter = 10; + if(battle_config.max_baby_parameter > 10000) + battle_config.max_baby_parameter = 10000; + if(battle_config.max_cart_weight > 1000000) + battle_config.max_cart_weight = 1000000; + if(battle_config.max_cart_weight < 100) + battle_config.max_cart_weight = 100; + battle_config.max_cart_weight *= 10; + + if(battle_config.max_def > 100 && !battle_config.weapon_defense_type) // added by [Skotlex] + battle_config.max_def = 100; + if(battle_config.over_def_bonus > 1000) + battle_config.over_def_bonus = 1000; + + if(battle_config.min_hitrate > battle_config.max_hitrate) + battle_config.min_hitrate = battle_config.max_hitrate; + + if(battle_config.agi_penalty_count < 2) + battle_config.agi_penalty_count = 2; + if(battle_config.vit_penalty_count < 2) + battle_config.vit_penalty_count = 2; + + if(battle_config.guild_exp_limit > 99) + battle_config.guild_exp_limit = 99; +/* if(battle_config.guild_exp_limit < 0) + battle_config.guild_exp_limit = 0;*/ + + if(battle_config.pet_support_min_friendly > 950) //Capped to 950/1000 [Skotlex] + battle_config.pet_support_min_friendly = 950; + + if(battle_config.pet_hungry_delay_rate < 10) + battle_config.pet_hungry_delay_rate=10; + + if(battle_config.pet_max_atk1 > battle_config.pet_max_atk2) //Skotlex + battle_config.pet_max_atk1 = battle_config.pet_max_atk2; + +// if(battle_config.castle_defense_rate < 0) +// battle_config.castle_defense_rate = 0; + if(battle_config.castle_defense_rate > 100) + battle_config.castle_defense_rate = 100; + if(battle_config.item_drop_common_min < 1) // Added by TyrNemesis^ + battle_config.item_drop_common_min = 1; + if(battle_config.item_drop_common_max > 10000) + battle_config.item_drop_common_max = 10000; + if(battle_config.item_drop_equip_min < 1) + battle_config.item_drop_equip_min = 1; + if(battle_config.item_drop_equip_max > 10000) + battle_config.item_drop_equip_max = 10000; + if(battle_config.item_drop_card_min < 1) + battle_config.item_drop_card_min = 1; + if(battle_config.item_drop_card_max > 10000) + battle_config.item_drop_card_max = 10000; + if(battle_config.item_drop_mvp_min < 1) + battle_config.item_drop_mvp_min = 1; + if(battle_config.item_drop_mvp_max > 10000) + battle_config.item_drop_mvp_max = 10000; // End Addition + +/* if (battle_config.night_at_start < 0) // added by [Yor] + battle_config.night_at_start = 0; + else if (battle_config.night_at_start > 1) // added by [Yor] + battle_config.night_at_start = 1; */ + if (battle_config.day_duration != 0 && battle_config.day_duration < 60000) // added by [Yor] + battle_config.day_duration = 60000; + if (battle_config.night_duration != 0 && battle_config.night_duration < 60000) // added by [Yor] + battle_config.night_duration = 60000; + +/* if (battle_config.ban_spoof_namer < 0) // added by [Yor] + battle_config.ban_spoof_namer = 0; + else*/ if (battle_config.ban_spoof_namer > 32767) + battle_config.ban_spoof_namer = 32767; + +/* if (battle_config.hack_info_GM_level < 0) // added by [Yor] + battle_config.hack_info_GM_level = 0; + else*/ if (battle_config.hack_info_GM_level > 100) + battle_config.hack_info_GM_level = 100; + +/* if (battle_config.any_warp_GM_min_level < 0) // added by [Yor] + battle_config.any_warp_GM_min_level = 0; + else*/ if (battle_config.any_warp_GM_min_level > 100) + battle_config.any_warp_GM_min_level = 100; + +/* //This is a hassle to keep updated each time there's a new limit to packet_ver_flag.... [Skotlex] + // at least 1 client must be accepted + if ((battle_config.packet_ver_flag & 255) == 0) // added by [Yor] + battle_config.packet_ver_flag = 255; // accept all clients +*/ +/* Deprecated by dynamix's new night system (using SI_NIGHT) + if (battle_config.night_darkness_level <= 0) + battle_config.night_darkness_level = 9; + else if (battle_config.night_darkness_level > 10) // Celest + battle_config.night_darkness_level = 10; +*/ +/* if (battle_config.motd_type < 0) + battle_config.motd_type = 0; + else if (battle_config.motd_type > 1) + battle_config.motd_type = 1; +*/ +// if (battle_config.finding_ore_rate < 0) +// battle_config.finding_ore_rate = 0; + + if (battle_config.vending_max_value > MAX_ZENY || battle_config.vending_max_value==0) + battle_config.vending_max_value = MAX_ZENY; + + if (battle_config.min_skill_delay_limit < 10) + battle_config.min_skill_delay_limit = 10; // minimum delay of 10ms + + //Spawn delays [Skotlex] +/* if (battle_config.mob_spawn_delay < 0) + battle_config.mob_spawn_delay = 0; + if (battle_config.boss_spawn_delay < 0) + battle_config.boss_spawn_delay = 0; + if (battle_config.plant_spawn_delay < 0) + battle_config.plant_spawn_delay = 0; +*/ + if (battle_config.no_spawn_on_player > 50) + battle_config.no_spawn_on_player = 50; + if (battle_config.mob_remove_delay < 15000) //Min 15 sec + battle_config.mob_remove_delay = 15000; + if (battle_config.dynamic_mobs > 1) + battle_config.dynamic_mobs = 1; //The flag will be used in assignations + if (battle_config.mob_max_skilllvl> MAX_SKILL_LEVEL || battle_config.mob_max_skilllvl<1 ) + battle_config.mob_max_skilllvl = MAX_SKILL_LEVEL; + + if (battle_config.firewall_hits_on_undead < 1) + battle_config.firewall_hits_on_undead = 1; + else if (battle_config.firewall_hits_on_undead > 255) //The flag passed to battle_calc_damage is limited to 0xff + battle_config.firewall_hits_on_undead = 255; + + if (battle_config.prevent_logout > 60000) + battle_config.prevent_logout = 60000; + + if (battle_config.mobs_level_up_exp_rate < 1) // [Valaris] + battle_config.mobs_level_up_exp_rate = 1; + + if (battle_config.pc_max_sc_def > 10000) + battle_config.pc_max_sc_def = 10000; + if (battle_config.mob_max_sc_def > 10000) + battle_config.mob_max_sc_def = 10000; + if (battle_config.sg_miracle_skill_ratio > 10000) + battle_config.sg_miracle_skill_ratio = 10000; + + if (battle_config.skill_steal_max_tries > UCHAR_MAX) + battle_config.skill_steal_max_tries = UCHAR_MAX; + +#ifdef CELL_NOSTACK + if (battle_config.cell_stack_limit < 1) + battle_config.cell_stack_limit = 1; + else + if (battle_config.cell_stack_limit > 255) + battle_config.cell_stack_limit = 255; +#else + if (battle_config.cell_stack_limit != 1) + ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n"); +#endif +} + +/*========================================== + * ?ン定ファイルを読み?桙゙ + *------------------------------------------ + */ +int battle_config_read(const char *cfgName) +{ + char line[1024], w1[1024], w2[1024]; + FILE *fp; + static int count = 0; + + if ((count++) == 0) + battle_set_defaults(); + + fp = fopen(cfgName,"r"); + if (fp == NULL) { + ShowError("File not found: %s\n", cfgName); + return 1; + } + while(fgets(line,1020,fp)){ + if (line[0] == '/' && line[1] == '/') + continue; + if (sscanf(line, "%[^:]:%s", w1, w2) != 2) + continue; + if (strcmpi(w1, "import") == 0) + battle_config_read(w2); + else + battle_set_value(w1, w2); + } + fclose(fp); + + if (--count == 0) { + battle_validate_conf(); + add_timer_func_list(battle_delay_damage_sub, "battle_delay_damage_sub"); + } + + return 0; +} + +void do_init_battle(void) { + delay_damage_ers = ers_new((uint32)sizeof(struct delay_damage)); +} + +void do_final_battle(void) { + ers_destroy(delay_damage_ers); +} diff --git a/src/map/battle.h b/src/map/battle.h index 54ad800e3..2b5da6f0a 100644 --- a/src/map/battle.h +++ b/src/map/battle.h @@ -28,7 +28,7 @@ struct Damage battle_calc_attack(int attack_type,struct block_list *bl,struct bl int battle_calc_return_damage(struct block_list *bl, int *damage, int flag); void battle_drain(struct map_session_data *sd, struct map_session_data *tsd, 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_elem); +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); @@ -47,10 +47,7 @@ enum { // 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 flag); -int battle_damage(struct block_list *bl,struct block_list *target,int damage,int walkdelay,int flag); -int battle_heal(struct block_list *bl,struct block_list *target,int hp,int sp,int flag); - +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, @@ -235,9 +232,7 @@ extern struct Battle_Config { unsigned short vit_penalty_type; unsigned short vit_penalty_count; unsigned short vit_penalty_num; - unsigned short player_defense_type; - unsigned short monster_defense_type; - unsigned short pet_defense_type; + unsigned short weapon_defense_type; unsigned short magic_defense_type; unsigned short skill_reiteration; unsigned short skill_nofootset; @@ -275,10 +270,8 @@ extern struct Battle_Config { unsigned short show_steal_in_same_party; unsigned short party_share_type; unsigned short party_show_share_picker; - unsigned short pet_attack_attr_none; - unsigned short mob_attack_attr_none; unsigned short mob_ghostring_fix; - unsigned short pc_attack_attr_none; + 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 diff --git a/src/map/charcommand.c b/src/map/charcommand.c index 4f7d8eb01..fc8c12820 100644 --- a/src/map/charcommand.c +++ b/src/map/charcommand.c @@ -422,15 +422,6 @@ int charcommand_petfriendly( pl_sd->pet.intimate = friendly; clif_send_petstatus(pl_sd); clif_pet_emotion(pl_sd->pd,0); - if (battle_config.pet_status_support) { - if ((pl_sd->pet.intimate > 0 && t <= 0) || - (pl_sd->pet.intimate <= 0 && t > 0)) { - if (pl_sd->bl.prev != NULL) - status_calc_pc(pl_sd, 0); - else - status_calc_pc(pl_sd, 2); - } - } clif_displaymessage(pl_sd->fd, msg_table[182]); // Pet friendly value changed! clif_displaymessage(sd->fd, msg_table[182]); // Pet friendly value changed! } else { @@ -1317,7 +1308,7 @@ int charcommand_baselevel( clif_updatestatus(pl_sd, SP_NEXTBASEEXP); clif_updatestatus(pl_sd, SP_STATUSPOINT); status_calc_pc(pl_sd, 0); - pc_heal(pl_sd, pl_sd->status.max_hp, pl_sd->status.max_sp); + 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 { diff --git a/src/map/clif.c b/src/map/clif.c index a4b7ea2f2..a485a1501 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -1506,24 +1506,6 @@ void clif_parse_HAttack(int fd,struct map_session_data *sd) { printf("unit_attack returned: %d\n",unit_attack(&sd->hd->bl,RFIFOL(fd,6),0)); } -/*========================================== - * - *------------------------------------------ - */ -int clif_servertick(struct map_session_data *sd) -{ - int fd; - - nullpo_retr(0, sd); - - fd=sd->fd; - WFIFOHEAD(fd, packet_len_table[0x7f]); - WFIFOW(fd,0)=0x7f; - WFIFOL(fd,2)=sd->server_tick; - WFIFOSET(fd,packet_len_table[0x7f]); - - return 0; -} /*========================================== * @@ -1567,7 +1549,7 @@ int clif_movepc(struct map_session_data *sd) { memset(buf,0,packet_len_table[0x7b]); WBUFW(buf,0)=0x7b; WBUFL(buf,2)=-10; - WBUFW(buf,6)=sd->speed; + WBUFW(buf,6)=sd->battle_status.speed; WBUFW(buf,8)=0; WBUFW(buf,10)=0; WBUFW(buf,12)=OPTION_INVISIBLE; @@ -2595,7 +2577,7 @@ int clif_updatestatus(struct map_session_data *sd,int type) WFIFOL(fd,4)=sd->max_weight; break; case SP_SPEED: - WFIFOL(fd,4)=sd->speed; + WFIFOL(fd,4)=sd->battle_status.speed; break; case SP_BASELEVEL: WFIFOL(fd,4)=sd->status.base_level; @@ -2614,66 +2596,64 @@ int clif_updatestatus(struct map_session_data *sd,int type) WFIFOL(fd,4)=sd->status.skill_point; break; case SP_HIT: - WFIFOL(fd,4)=sd->hit; + WFIFOL(fd,4)=sd->battle_status.hit; break; case SP_FLEE1: - WFIFOL(fd,4)=sd->flee; + WFIFOL(fd,4)=sd->battle_status.flee; break; case SP_FLEE2: - WFIFOL(fd,4)=sd->flee2/10; + WFIFOL(fd,4)=sd->battle_status.flee2/10; break; case SP_MAXHP: - WFIFOL(fd,4)=sd->status.max_hp; + WFIFOL(fd,4)=sd->battle_status.max_hp; break; case SP_MAXSP: - WFIFOL(fd,4)=sd->status.max_sp; + WFIFOL(fd,4)=sd->battle_status.max_sp; break; case SP_HP: - WFIFOL(fd,4)=sd->status.hp; + WFIFOL(fd,4)=sd->battle_status.hp; if (sd->status.party_id) clif_party_hp(sd); if (battle_config.disp_hpmeter) clif_hpmeter(sd); break; case SP_SP: - WFIFOL(fd,4)=sd->status.sp; + WFIFOL(fd,4)=sd->battle_status.sp; break; case SP_ASPD: - WFIFOL(fd,4)=sd->aspd; + WFIFOL(fd,4)=sd->battle_status.amotion; break; case SP_ATK1: - WFIFOL(fd,4)=sd->base_atk+sd->right_weapon.watk+sd->left_weapon.watk; + WFIFOL(fd,4)=sd->battle_status.batk +sd->battle_status.rhw.atk +sd->battle_status.lhw->atk; break; case SP_DEF1: - WFIFOL(fd,4)=sd->def; + WFIFOL(fd,4)=sd->battle_status.def; break; case SP_MDEF1: - WFIFOL(fd,4)=sd->mdef; + WFIFOL(fd,4)=sd->battle_status.mdef; break; case SP_ATK2: - WFIFOL(fd,4)=sd->right_weapon.watk2 + sd->left_weapon.watk2; + WFIFOL(fd,4)=sd->battle_status.rhw.atk2 + sd->battle_status.lhw->atk2; break; case SP_DEF2: - WFIFOL(fd,4)=sd->def2; + WFIFOL(fd,4)=sd->battle_status.def2; break; case SP_MDEF2: - WFIFOL(fd,4)=sd->mdef2; + WFIFOL(fd,4)=sd->battle_status.mdef2; break; case SP_CRITICAL: - WFIFOL(fd,4)=sd->critical/10; + WFIFOL(fd,4)=sd->battle_status.cri/10; break; case SP_MATK1: - WFIFOL(fd,4)=sd->matk1; + WFIFOL(fd,4)=sd->battle_status.matk_max; break; case SP_MATK2: - WFIFOL(fd,4)=sd->matk2; + WFIFOL(fd,4)=sd->battle_status.matk_min; break; case SP_ZENY: WFIFOW(fd,0)=0xb1; - if(sd->status.zeny < 0) - sd->status.zeny = 0; WFIFOL(fd,4)=sd->status.zeny; break; case SP_BASEEXP: @@ -2708,7 +2688,7 @@ int clif_updatestatus(struct map_session_data *sd,int type) // 013a 終了 case SP_ATTACKRANGE: WFIFOW(fd,0)=0x13a; - WFIFOW(fd,2)=sd->attackrange; + WFIFOW(fd,2)=sd->battle_status.rhw.range; len=4; break; @@ -2717,42 +2697,42 @@ int clif_updatestatus(struct map_session_data *sd,int type) WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; WFIFOL(fd,6)=sd->status.str; - WFIFOL(fd,10)=sd->paramb[0] + sd->parame[0]; + WFIFOL(fd,10)=sd->battle_status.str - sd->status.str; len=14; break; case SP_AGI: WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; WFIFOL(fd,6)=sd->status.agi; - WFIFOL(fd,10)=sd->paramb[1] + sd->parame[1]; + WFIFOL(fd,10)=sd->battle_status.agi - sd->status.agi; len=14; break; case SP_VIT: WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; WFIFOL(fd,6)=sd->status.vit; - WFIFOL(fd,10)=sd->paramb[2] + sd->parame[2]; + WFIFOL(fd,10)=sd->battle_status.vit - sd->status.vit; len=14; break; case SP_INT: WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; WFIFOL(fd,6)=sd->status.int_; - WFIFOL(fd,10)=sd->paramb[3] + sd->parame[3]; + WFIFOL(fd,10)=sd->battle_status.int_ - sd->status.int_; len=14; break; case SP_DEX: WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; WFIFOL(fd,6)=sd->status.dex; - WFIFOL(fd,10)=sd->paramb[4] + sd->parame[4]; + WFIFOL(fd,10)=sd->battle_status.dex - sd->status.dex; len=14; break; case SP_LUK: WFIFOW(fd,0)=0x141; WFIFOL(fd,2)=type; WFIFOL(fd,6)=sd->status.luk; - WFIFOL(fd,10)=sd->paramb[5] + sd->parame[5]; + WFIFOL(fd,10)=sd->battle_status.luk - sd->status.luk; len=14; break; @@ -3001,18 +2981,18 @@ int clif_initialstatus(struct map_session_data *sd) WBUFB(buf,14)=(sd->status.luk > UCHAR_MAX)? UCHAR_MAX:sd->status.luk; WBUFB(buf,15)=pc_need_status_point(sd,SP_LUK); - WBUFW(buf,16) = sd->base_atk + sd->right_weapon.watk + sd->left_weapon.watk; - WBUFW(buf,18) = sd->right_weapon.watk2 + sd->left_weapon.watk2; //atk bonus - WBUFW(buf,20) = sd->matk1; - WBUFW(buf,22) = sd->matk2; - WBUFW(buf,24) = sd->def; // def - WBUFW(buf,26) = sd->def2; - WBUFW(buf,28) = sd->mdef; // mdef - WBUFW(buf,30) = sd->mdef2; - WBUFW(buf,32) = sd->hit; - WBUFW(buf,34) = sd->flee; - WBUFW(buf,36) = sd->flee2/10; - WBUFW(buf,38) = sd->critical/10; + WBUFW(buf,16) = sd->battle_status.batk + sd->battle_status.rhw.atk + sd->battle_status.lhw->atk; + WBUFW(buf,18) = sd->battle_status.rhw.atk2 + sd->battle_status.lhw->atk2; //atk bonus + WBUFW(buf,20) = sd->battle_status.matk_max; + WBUFW(buf,22) = sd->battle_status.matk_min; + WBUFW(buf,24) = sd->battle_status.def; // def + WBUFW(buf,26) = sd->battle_status.def2; + WBUFW(buf,28) = sd->battle_status.mdef; // mdef + WBUFW(buf,30) = sd->battle_status.mdef2; + WBUFW(buf,32) = sd->battle_status.hit; + WBUFW(buf,34) = sd->battle_status.flee; + WBUFW(buf,36) = sd->battle_status.flee2/10; + WBUFW(buf,38) = sd->battle_status.cri/10; WBUFW(buf,40) = sd->status.karma; WBUFW(buf,42) = sd->status.manner; @@ -4859,7 +4839,7 @@ int clif_skill_teleportmessage(struct map_session_data *sd,int flag) */ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) { - struct mob_data *md; + struct status_data *status; unsigned char buf[64]; int i;//, fix; @@ -4868,24 +4848,24 @@ int clif_skill_estimation(struct map_session_data *sd,struct block_list *dst) if(dst->type!=BL_MOB ) return 0; - if((md=(struct mob_data *)dst) == NULL) - return 0; + + status = status_get_status_data(dst); WBUFW(buf, 0)=0x18c; - WBUFW(buf, 2)=md->vd->class_; - WBUFW(buf, 4)=md->level; - WBUFW(buf, 6)=md->db->size; - WBUFL(buf, 8)=md->hp; - WBUFW(buf,12)= (battle_config.estimation_type&1?status_get_def(&md->bl):0) - +(battle_config.estimation_type&2?status_get_def2(&md->bl):0); - WBUFW(buf,14)=md->db->race; - WBUFW(buf,16)= (battle_config.estimation_type&1?status_get_mdef(&md->bl):0) - +(battle_config.estimation_type&2?status_get_mdef2(&md->bl) - (md->db->vit>>1):0); - WBUFW(buf,18)=status_get_elem_type(&md->bl); + WBUFW(buf, 2)=status_get_class(dst); + WBUFW(buf, 4)=status_get_lv(dst); + WBUFW(buf, 6)=status->size; + WBUFL(buf, 8)=status->hp; + WBUFW(buf,12)= (battle_config.estimation_type&1?status->def:0) + +(battle_config.estimation_type&2?status->def2:0); + WBUFW(buf,14)=status->race; + WBUFW(buf,16)= (battle_config.estimation_type&1?status->mdef:0) + +(battle_config.estimation_type&2?status->mdef - (status->vit>>1):0); + WBUFW(buf,18)= status->def_ele; for(i=0;i<9;i++) - WBUFB(buf,20+i)= (unsigned char)battle_attr_fix(NULL,dst,100,i+1,md->def_ele); + WBUFB(buf,20+i)= (unsigned char)battle_attr_fix(NULL,dst,100,i+1,status->def_ele, status->ele_lv); // The following caps negative attributes to 0 since the client displays them as 255-fix. [Skotlex] -// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_fix(NULL,dst,100,i+1,md->def_ele))<0?0:fix); +// WBUFB(buf,20+i)= (unsigned char)((fix=battle_attr_fix(NULL,dst,100,i+1,status->def_ele, status->ele_lv))<0?0:fix); if(sd->status.party_id>0) clif_send(buf,packet_len_table[0x18c],&sd->bl,PARTY_AREA); @@ -6183,8 +6163,8 @@ int clif_party_hp(struct map_session_data *sd) WBUFW(buf,0)=0x106; WBUFL(buf,2)=sd->status.account_id; - WBUFW(buf,6)=(sd->status.hp > SHRT_MAX)?SHRT_MAX:sd->status.hp; - WBUFW(buf,8)=(sd->status.max_hp > SHRT_MAX)?SHRT_MAX:sd->status.max_hp; + WBUFW(buf,6)=(sd->battle_status.hp > SHRT_MAX)?SHRT_MAX:sd->battle_status.hp; + WBUFW(buf,8)=(sd->battle_status.max_hp > SHRT_MAX)?SHRT_MAX:sd->battle_status.max_hp; clif_send(buf,packet_len_table[0x106],&sd->bl,PARTY_AREA_WOS); return 0; } @@ -6198,8 +6178,8 @@ static void clif_hpmeter_single(int fd, struct map_session_data *sd) WFIFOHEAD(fd,packet_len_table[0x106]); WFIFOW(fd,0) = 0x106; WFIFOL(fd,2) = sd->status.account_id; - WFIFOW(fd,6) = (sd->status.hp > 0x7fff) ? 0x7fff : sd->status.hp; - WFIFOW(fd,8) = (sd->status.max_hp > 0x7fff) ? 0x7fff : sd->status.max_hp; + WFIFOW(fd,6) = (sd->battle_status.hp > SHRT_MAX) ? SHRT_MAX : sd->battle_status.hp; + WFIFOW(fd,8) = (sd->battle_status.max_hp > SHRT_MAX) ? SHRT_MAX : sd->battle_status.max_hp; WFIFOSET (fd, packet_len_table[0x106]); } @@ -6223,8 +6203,8 @@ int clif_hpmeter(struct map_session_data *sd) WBUFW(buf,0) = 0x106; WBUFL(buf,2) = sd->status.account_id; - WBUFW(buf,6) = (sd->status.hp > 0x7fff) ? 0x7fff : sd->status.hp; - WBUFW(buf,8) = (sd->status.max_hp > 0x7fff) ? 0x7fff : sd->status.max_hp; + WBUFW(buf,6) = (sd->battle_status.hp > SHRT_MAX) ? SHRT_MAX : sd->battle_status.hp; + WBUFW(buf,8) = (sd->battle_status.max_hp > SHRT_MAX) ? SHRT_MAX : sd->battle_status.max_hp; for (i = 0; i < fd_max; i++) { if (session[i] && (sd2 = (struct map_session_data*)session[i]->session_data) && sd != sd2 && sd2->state.auth) { if (sd2->bl.m != sd->bl.m || @@ -6431,21 +6411,19 @@ int clif_send_petstatus(struct map_session_data *sd) int clif_pet_emotion(struct pet_data *pd,int param) { unsigned char buf[16]; - struct map_session_data *sd; nullpo_retr(0, pd); - nullpo_retr(0, sd = pd->msd); memset(buf,0,packet_len_table[0x1aa]); WBUFW(buf,0)=0x1aa; WBUFL(buf,2)=pd->bl.id; - if(param >= 100 && sd->petDB->talk_convert_class) { - if(sd->petDB->talk_convert_class < 0) + if(param >= 100 && pd->petDB->talk_convert_class) { + if(pd->petDB->talk_convert_class < 0) return 0; - else if(sd->petDB->talk_convert_class > 0) { + else if(pd->petDB->talk_convert_class > 0) { param -= (pd->class_ - 100)*100; - param += (sd->petDB->talk_convert_class - 100)*100; + param += (pd->petDB->talk_convert_class - 100)*100; } } WBUFL(buf,6)=param; @@ -6724,7 +6702,7 @@ int clif_mvp_item(struct map_session_data *sd,int nameid) * MVP経験値所得 *------------------------------------------ */ -int clif_mvp_exp(struct map_session_data *sd,int exp) +int clif_mvp_exp(struct map_session_data *sd,unsigned long exp) { int fd; @@ -7895,12 +7873,12 @@ int clif_charnameack (int fd, struct block_list *bl) WBUFB(buf,30) = 0; memcpy(WBUFP(buf,54), md->guardian_data->guild_name, NAME_LENGTH); memcpy(WBUFP(buf,78), md->guardian_data->castle->castle_name, NAME_LENGTH); - } else if (battle_config.show_mob_hp == 1) { + } else if (battle_config.show_mob_hp) { char mobhp[50]; WBUFW(buf, 0) = cmd = 0x195; - sprintf(mobhp, "HP: %d/%d", md->hp, md->max_hp); + sprintf(mobhp, "HP: %d/%d", md->status.hp, md->status.max_hp); //Even thought mobhp ain't a name, we send it as one so the client - //can parse it. [Skotlex] + //can parse it. memcpy(WBUFP(buf,30), mobhp, NAME_LENGTH); WBUFB(buf,54) = 0; memcpy(WBUFP(buf,78), mobhp, NAME_LENGTH); @@ -8417,8 +8395,12 @@ void clif_parse_TickSend(int fd, struct map_session_data *sd) { RFIFOHEAD(fd); sd->client_tick=RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]); - sd->server_tick = gettick(); - clif_servertick(sd); + + WFIFOHEAD(fd, packet_len_table[0x7f]); + WFIFOW(fd,0)=0x7f; + WFIFOL(fd,2)=gettick(); + WFIFOSET(fd,packet_len_table[0x7f]); + return; } static int clif_walktoxy_timer(int tid, unsigned int tick, int id, int data) @@ -8969,7 +8951,7 @@ void clif_parse_Restart(int fd, struct map_session_data *sd) { pc_setpos(sd, sd->status.save_point.map, sd->status.save_point.x, sd->status.save_point.y, 2); } // in case the player's status somehow wasn't updated yet [Celest] - else if (sd->status.hp <= 0) + else if (sd->battle_status.hp <= 0) pc_setdead(sd); break; case 0x01: @@ -10616,8 +10598,7 @@ void clif_parse_GMKick(int fd, struct map_session_data *sd) { else clif_GM_kickack(sd, 0); } else if (target->type == BL_MOB) { - struct mob_data *md = (struct mob_data *)target; - mob_damage(&sd->bl, md, md->hp, 2); + status_percent_damage(&sd->bl, target, 100, 0); } else clif_GM_kickack(sd, 0); } else diff --git a/src/map/clif.h b/src/map/clif.h index 492e24587..b269f90ae 100644 --- a/src/map/clif.h +++ b/src/map/clif.h @@ -217,7 +217,7 @@ int clif_item_skill(struct map_session_data *sd,int skillid,int skilllv,const ch 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,int exp); +int clif_mvp_exp(struct map_session_data *sd,unsigned long exp); void clif_changed_dir(struct block_list *bl); // vending diff --git a/src/map/guild.c b/src/map/guild.c index 820e17484..5364fb8bd 100644 --- a/src/map/guild.c +++ b/src/map/guild.c @@ -1206,8 +1206,6 @@ int guild_skillup(struct map_session_data *sd,int skill_num,int flag) g->skill[idx].lv < guild_skill_get_max(skill_num) ){ intif_guild_skillup(g->guild_id,skill_num,sd->status.account_id,flag); } - status_calc_pc (sd, 0); // Celest - return 0; } // スキルポイント割り振り通知 diff --git a/src/map/map.c b/src/map/map.c index dad26f156..3833acb34 100644 --- a/src/map/map.c +++ b/src/map/map.c @@ -514,9 +514,9 @@ int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick) { if (bl->type&BL_CHAR) { skill_unit_move(bl,tick,3); if (sc) { - if (sc->option&OPTION_CLOAK) - skill_check_cloaking(bl); if (sc->count) { + if (sc->data[SC_CLOAKING].timer != -1) + skill_check_cloaking(bl, sc); if (sc->data[SC_DANCING].timer != -1) { //Cancel Moonlight Petals if moved from casting position. [Skotlex] if (sc->data[SC_DANCING].val1 == CG_MOONLIT) @@ -1663,7 +1663,6 @@ int map_quit(struct map_session_data *sd) { if (sd->pd) unit_free(&sd->pd->bl); unit_free(&sd->bl); pc_clean_skilltree(sd); - status_calc_pc(sd,4); if(sd->pet.intimate > 0) intif_save_petdata(sd->status.account_id,&sd->pet); chrif_save(sd,1); @@ -1954,7 +1953,7 @@ int mob_cache_cleanup_sub(struct block_list *bl, va_list ap) { if (!md->special_state.cached) return 0; if (!battle_config.mob_remove_damaged && - md->hp < md->db->max_hp) //don't use status_get_maxhp for speed (by the time you have to remove a mob, their status changes should have expired anyway) + md->status.hp < md->status.max_hp) return 0; //Do not remove damaged mobs. unit_free(&md->bl); diff --git a/src/map/map.h b/src/map/map.h index aa8714349..adbf76620 100644 --- a/src/map/map.h +++ b/src/map/map.h @@ -280,6 +280,20 @@ enum { 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 +}; + struct block_list { struct block_list *next,*prev; int id; @@ -378,6 +392,37 @@ struct unit_data { } 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, + hit, flee, cri, flee2, + def2, mdef2, + speed, + amotion, adelay, dmotion, + mode; + short aspd_rate; + unsigned char + def, mdef, + def_ele, ele_lv, + size, race; + 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; @@ -410,9 +455,6 @@ struct weapon_data { // 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 memset call // in status_calc_pc as well! All the following are automatically zero'ed. [Skotlex] - int watk; - int watk2; - int atk_ele; int overrefine; int star; int ignore_def_ele; @@ -460,6 +502,8 @@ 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; //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] @@ -529,9 +573,9 @@ struct map_session_data { int cart_weight,cart_max_weight,cart_num,cart_max_num; int fd; unsigned short mapindex; - short speed,prev_speed; + unsigned short prev_speed,prev_adelay; unsigned char head_dir; - unsigned int client_tick,server_tick; + 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_pos; @@ -572,15 +616,10 @@ struct map_session_data { short weapontype1,weapontype2; short disguise; // [Valaris] - struct weapon_data right_weapon; - struct weapon_data left_weapon; + struct weapon_data right_weapon, left_weapon; - int paramc[6],paramcard[6]; - // here start arrays to be globally zeroed at the beginning of status_calc_pc() - - int paramb[6]; - int parame[6]; + int param_bonus[6],param_equip[6]; //Stores card/equipment bonuses. int subele[10]; int subrace[RC_MAX]; int subrace2[RC_MAX]; @@ -624,16 +663,7 @@ struct map_session_data { } add_drop[MAX_PC_BONUS]; // zeroed structures end here // zeroed vars start here. - int hit; - int flee, flee2; - int critical; - int aspd; - int def, def2; - int mdef, mdef2; - int def_ele; - int matk1, matk2; - int base_atk; - int arrow_atk,arrow_ele,arrow_cri,arrow_hit,arrow_range; + int arrow_atk,arrow_ele,arrow_cri,arrow_hit; int nhealhp,nhealsp,nshealhp,nshealsp,nsshealhp,nsshealsp; int critical_def,double_rate; int long_attack_atk_rate; //Long range atk rate, not weapon based. [Skotlex] @@ -653,8 +683,9 @@ struct map_session_data { 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 attackrange,attackrange_; short splash_range, splash_add_range; short add_steal_rate; @@ -674,13 +705,11 @@ struct map_session_data { // zeroed vars end here. - int amotion,dmotion; int castrate,delayrate,hprate,sprate,dsprate; int atk_rate; int aspd_rate,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 speed_add_rate, aspd_add_rate; int hp_loss_tick; int sp_loss_tick; @@ -729,9 +758,7 @@ struct map_session_data { struct vending vending[MAX_VENDING]; struct s_pet pet; - struct pet_db *petDB; struct pet_data *pd; - int pet_hungry_timer; struct homun_data *hd; // [blackhole89] @@ -855,13 +882,18 @@ 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 : 3; //Special ai for summoned monsters. + 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; @@ -883,13 +915,11 @@ struct mob_data { struct spawn_data *spawn; //Spawn data. struct item *lootitem; short spawn_n; //Spawn data index on the map server. - short class_,mode; - short speed; + short class_; short attacked_count; - unsigned short level; unsigned char attacked_players; unsigned int tdmg; //Stores total damage given to the mob, for exp calculations. [Skotlex] - int hp, max_hp; + int level; int target_id,attacked_id; unsigned int next_walktime; unsigned int last_deadtime,last_spawntime,last_thinktime,last_linktime; @@ -898,7 +928,6 @@ struct mob_data { short min_chase; int deletetimer; - int def_ele; int master_id,master_dist; struct npc_data *nd; @@ -949,25 +978,21 @@ struct pet_data { struct block_list bl; struct unit_data ud; struct view_data vd; + struct status_data status; struct mob_db *db; + struct pet_db *petDB; + int pet_hungry_timer; int target_id; short n; short class_; - short speed; + short equip; char name[NAME_LENGTH]; struct { - unsigned skillstate : 8 ; - short skillbonus; + unsigned skillbonus : 1; } state; - short equip; int move_fail_count; unsigned int next_walktime,last_thinktime; short rate_fix; //Support rate as modified by intimacy (1000 = 100%) [Skotlex] - struct pet_status { //Pet Status data - short level; - short atk1,atk2; - short str,agi,vit,int_,dex,luk; - } *status; //[Skotlex] struct pet_recovery { //Stat recovery unsigned short type; //Status Change id @@ -1166,8 +1191,10 @@ enum { 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) and - //2007 (SP_FREE, previously Infinite Endure) are available! + //Before adding another, note that + //1077 (SP_FREE, previously disguise), + //2007 (SP_FREE2, previously Infinite Endure) + //are available! }; enum { diff --git a/src/map/mob.c b/src/map/mob.c index f3afa4d1b..8b6b705dc 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -1,4131 +1,4065 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include -#include - -#include "../common/timer.h" -#include "../common/db.h" -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" -#include "../common/ers.h" -#include "../common/strlib.h" - -#include "map.h" -#include "clif.h" -#include "intif.h" -#include "pc.h" -#include "status.h" -#include "mob.h" -#include "guild.h" -#include "itemdb.h" -#include "skill.h" -#include "battle.h" -#include "party.h" -#include "npc.h" -#include "log.h" -#include "script.h" -#include "atcommand.h" -#include "date.h" -#include "irc.h" - -#define MIN_MOBTHINKTIME 100 -#define IDLE_SKILL_INTERVAL 10 //Active idle skills should be triggered every 1 second (1000/MIN_MOBTHINKTIME) - -#define MOB_LAZYSKILLPERC 10 // Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute) -#define MOB_LAZYMOVEPERC 50 // Move probability in the negligent mode MOB (rate of 1000 minute) -#define MOB_LAZYWARPPERC 20 // Warp probability in the negligent mode MOB (rate of 1000 minute) - -#define MOB_SLAVEDISTANCE 2 //Distance that slaves should keep from their master. - -#define MAX_MINCHASE 30 //Max minimum chase value to use for mobs. -//Dynamic mob database, allows saving of memory when there's big gaps in the mob_db [Skotlex] -struct mob_db *mob_db_data[MAX_MOB_DB+1]; -struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant one is requested. - -struct mob_db *mob_db(int index) { if (index < 0 || index > MAX_MOB_DB || mob_db_data[index] == NULL) return mob_dummy; return mob_db_data[index]; } - -static struct eri *item_drop_ers; //For loot drops delay structures. -static struct eri *item_drop_list_ers; -#define CLASSCHANGE_BOSS_NUM 21 - -/*========================================== - * Local prototype declaration (only required thing) - *------------------------------------------ - */ -static int mob_makedummymobdb(int); -static int mob_spawn_guardian_sub(int,unsigned int,int,int); -int mobskill_use(struct mob_data *md,unsigned int tick,int event); -int mob_skillid2skillidx(int class_,int skillid); - -/*========================================== - * Mob is searched with a name. - *------------------------------------------ - */ -int mobdb_searchname(const char *str) -{ - int i; - struct mob_db* mob; - for(i=0;i<=MAX_MOB_DB;i++){ - mob = mob_db(i); - if(mob == mob_dummy) //Skip dummy mobs. - continue; - if(strcmpi(mob->name,str)==0 || strcmpi(mob->jname,str)==0 || strcmpi(mob->sprite,str)==0) - return i; - } - - return 0; -} -static int mobdb_searchname_array_sub(struct mob_db* mob, const char *str) -{ - if (mob == mob_dummy) - return 1; //Invalid mob. - if(!mob->base_exp && !mob->job_exp) - return 1; //Discount slave-mobs (no exp) as requested by Playtester. [Skotlex] - if(stristr(mob->jname,str)) - return 0; - if(stristr(mob->name,str)) - return 0; - return strcmpi(mob->jname,str); -} - -/*========================================== - * Founds up to N matches. Returns number of matches [Skotlex] - *------------------------------------------ - */ -int mobdb_searchname_array(struct mob_db** data, int size, const char *str) -{ - int count = 0, i; - struct mob_db* mob; - for(i=0;i<=MAX_MOB_DB;i++){ - mob = mob_db(i); - if (mob == mob_dummy) - continue; - if (!mobdb_searchname_array_sub(mob, str)) { - if (count < size) - data[count] = mob; - count++; - } - } - return count; -} - -/*========================================== - * Id Mob is checked. - *------------------------------------------ - */ -int mobdb_checkid(const int id) -{ - if (mob_db(id) == mob_dummy) - return 0; - if (mob_is_clone(id)) //checkid is used mostly for random ID based code, therefore clone mobs are out of the question. - return 0; - return id; -} - -/*========================================== - * Returns the view data associated to this mob class. - *------------------------------------------ - */ -struct view_data * mob_get_viewdata(class_) -{ - if (mob_db(class_) == mob_dummy) - return 0; - return &mob_db(class_)->vd; -} -/*========================================== - * Cleans up mob-spawn data to make it "valid" - *------------------------------------------ - */ -int mob_parse_dataset(struct spawn_data *data) { - int i; - //FIXME: This implementation is not stable, npc scripts will stop working once MAX_MOB_DB changes value! [Skotlex] - if(data->class_ > 2*MAX_MOB_DB){ // large/tiny mobs [Valaris] - data->state.size=2; - data->class_ -= 2*MAX_MOB_DB; - } else if (data->class_ > MAX_MOB_DB) { - data->state.size=1; - data->class_ -= MAX_MOB_DB; - } - - if ((!mobdb_checkid(data->class_) && !mob_is_clone(data->class_)) || !data->num) - return 0; - - //better safe than sorry, current md->npc_event has a size of 50 - if (strlen(data->eventname) >= 50) - return 0; - - if (data->eventname[0] && strlen(data->eventname) <= 2) - { //Portable monster big/small implementation. [Skotlex] - i = atoi(data->eventname); - if (i) { - if (i&2) - data->state.size=1; - else if (i&4) - data->state.size=2; - if (i&8) - data->state.ai=1; - data->eventname[0] = '\0'; //Clear event as it is not used. - } - } - if (!data->level) - data->level = mob_db(data->class_)->lv; - - if(strcmp(data->name,"--en--")==0) - strncpy(data->name,mob_db(data->class_)->name,NAME_LENGTH-1); - else if(strcmp(data->name,"--ja--")==0) - strncpy(data->name,mob_db(data->class_)->jname,NAME_LENGTH-1); - - return 1; -} -/*========================================== - * Generates the basic mob data using the spawn_data provided. - *------------------------------------------ - */ -struct mob_data* mob_spawn_dataset(struct spawn_data *data) -{ - struct mob_data *md = aCalloc(1, sizeof(struct mob_data)); - md->bl.id= npc_get_new_npc_id(); - md->bl.type = BL_MOB; - md->bl.subtype = MONS; - md->bl.m = data->m; - md->bl.x = data->x; - md->bl.y = data->y; - md->class_ = data->class_; - md->db = mob_db(md->class_); - md->speed = md->db->speed; - memcpy(md->name, data->name, NAME_LENGTH-1); - if (data->state.ai) - md->special_state.ai = data->state.ai; - if (data->state.size) - md->special_state.size = data->state.size; - if (data->eventname[0] && strlen(data->eventname) >= 4) - memcpy(md->npc_event, data->eventname, 50); - md->level = data->level; - - if(md->db->mode&MD_LOOTER) - md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); - md->spawn_n = -1; - md->deletetimer = -1; - md->skillidx = -1; - status_set_viewdata(&md->bl, md->class_); - status_change_init(&md->bl); - unit_dataset(&md->bl); - - map_addiddb(&md->bl); - return md; -} - -/*========================================== - * Fetches a random mob_id [Skotlex] - * type: Where to fetch from: - * 0: dead branch list - * 1: poring list - * 2: bloody branch list - * flag: - * &1: Apply the summon success chance found in the list. - * &2: Apply a monster check level. - * lv: Mob level to check against - *------------------------------------------ - */ - -int mob_get_random_id(int type, int flag, int lv) { - struct mob_db *mob; - int i=0, k=0, class_; - if(type < 0 || type >= MAX_RANDOMMONSTER) { - if (battle_config.error_log) - ShowError("mob_get_random_id: Invalid type (%d) of random monster.\n", type); - return 0; - } - do { - class_ = rand() % MAX_MOB_DB; - if (flag&1) - k = rand() % 1000000; - mob = mob_db(class_); - } while ((mob == mob_dummy || mob->summonper[type] <= k || - (flag&2 && lv < mob->lv)) && (i++) < MAX_MOB_DB); - if(i >= MAX_MOB_DB) - class_ = mob_db_data[0]->summonper[type]; - return class_; -} - -/*========================================== - * The MOB appearance for one time (for scripts) - *------------------------------------------ - */ -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) -{ - struct mob_data *md = NULL; - struct spawn_data data; - int m, count, lv = 255, rand_flag=0; - - - if(sd) lv = sd->status.base_level; - - if(sd && strcmp(mapname,"this")==0) - m = sd->bl.m; - else - m = map_mapname2mapid(mapname); - - memset(&data, 0, sizeof(struct spawn_data)); - if (m < 0 || amount <= 0) // 値が異常なら召喚を止める - return 0; - data.m = m; - data.num = amount; - data.class_ = class_; - strncpy(data.name, mobname, NAME_LENGTH-1); - - if (class_ < 0) { - data.class_ = mob_get_random_id(-class_ -1, battle_config.random_monster_checklv?3:1, lv); - if (!data.class_) - return 0; - } - strncpy(data.eventname, event, 50); - - if (sd && (x < 0 || y < 0)) //Locate spot around player. - map_search_freecell(&sd->bl, m, &x, &y, 1, 1, 0); - - if (x <= 0 || y <= 0 || map_getcell(m,x,y,CELL_CHKNOREACH)) - rand_flag = 1; //Randomize spot on map for each mob. - else { - data.x = x; - data.y = y; - } - if (!mob_parse_dataset(&data)) - return 0; - - for (count = 0; count < amount; count++) { - if (rand_flag) { //Get a random cell for this mob. - map_search_freecell(NULL, m, &x, &y, -1, -1, 1); - // This should ALWAYS be done. [blackhole89] - data.x = x; - data.y = y; - } - - md =mob_spawn_dataset (&data); - - if (class_ < 0 && battle_config.dead_branch_active) - //Behold Aegis's masterful decisions yet again... - //"I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" - Poki#3 - md->mode = md->db->mode|MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE; - mob_spawn (md); - - if(class_ == MOBID_EMPERIUM) { // emperium hp based on defense level [Valaris] - struct guild_castle *gc = guild_mapname2gc(map[md->bl.m].name); - struct guild *g = gc?guild_search(gc->guild_id):NULL; - if(gc) { - md->max_hp += 2000 * gc->defense; - md->hp = md->max_hp; - md->guardian_data = aCalloc(1, sizeof(struct guardian_data)); - md->guardian_data->castle = gc; - md->guardian_data->number = MAX_GUARDIANS; - md->guardian_data->guild_id = gc->guild_id; - if (g) - { - md->guardian_data->emblem_id = g->emblem_id; - memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH); - } - else if (gc->guild_id) //Guild not yet available, retry in 5. - add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id); - } - } // end addition [Valaris] - } - return (md)?md->bl.id : 0; -} -/*========================================== - * The MOB appearance for one time (& area specification for scripts) - *------------------------------------------ - */ -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 x,y,i,max,lx=-1,ly=-1,id=0; - int m; - - if(strcmp(mapname,"this")==0) - m=sd->bl.m; - else - m=map_mapname2mapid(mapname); - - max=(y1-y0+1)*(x1-x0+1)*3; - if(max>1000)max=1000; - - if (m < 0 || amount <= 0) // 値が異常なら召喚を止める - return 0; - - for(i=0;i=max){ - if(lx>=0){ // Since reference went wrong, the place which boiled before is used. - x=lx; - y=ly; - }else - return 0; // Since reference of the place which boils first went wrong, it stops. - } - if(x==0||y==0) ShowWarning("mob_once_spawn_area: xory=0, x=%d,y=%d,x0=%d,y0=%d\n",x,y,x0,y0); - id=mob_once_spawn(sd,mapname,x,y,mobname,class_,1,event); - lx=x; - ly=y; - } - return id; -} -/*========================================== - * Set a Guardian's guild data [Skotlex] - *------------------------------------------ - */ -static int mob_spawn_guardian_sub(int tid,unsigned int tick,int id,int data) -{ //Needed because the guild_data may not be available at guardian spawn time. - struct block_list* bl = map_id2bl(id); - struct mob_data* md; - struct guild* g; - - if (bl == NULL) //It is possible mob was already removed from map when the castle has no owner. [Skotlex] - return 0; - - if (bl->type != BL_MOB || (md = (struct mob_data*)bl) == NULL) - { - ShowError("mob_spawn_guardian_sub: Block error!\n"); - return 0; - } - - nullpo_retr(0, md->guardian_data); - g = guild_search(data); - - if (g == NULL) - { //Liberate castle, if the guild is not found this is an error! [Skotlex] - ShowError("mob_spawn_guardian_sub: Couldn't load guild %d!\n",data); - if (md->class_ == MOBID_EMPERIUM) - { //Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on. - md->guardian_data->guild_id = 0; - if (md->guardian_data->castle->guild_id) //Free castle up. - { - ShowNotice("Clearing ownership of castle %d (%s)\n", md->guardian_data->castle->castle_id, md->guardian_data->castle->castle_name); - guild_castledatasave(md->guardian_data->castle->castle_id, 1, 0); - } - } else { - if (md->guardian_data->castle->guardian[md->guardian_data->number].visible) - { //Safe removal of guardian. - md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; - guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); - guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); - } - unit_free(&md->bl); //Remove guardian. - } - return 0; - } - md->guardian_data->emblem_id = g->emblem_id; - memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH); - md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP); - return 0; -} - -/*========================================== - * Summoning Guardians [Valaris] - *------------------------------------------ - */ -int mob_spawn_guardian(struct map_session_data *sd,char *mapname, - int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian) -{ - struct mob_data *md=NULL; - struct spawn_data data; - struct guild *g=NULL; - struct guild_castle *gc; - int m, count; - memset(&data, 0, sizeof(struct spawn_data)); - data.num = 1; - - if( sd && strcmp(mapname,"this")==0) - m=sd->bl.m; - else - m=map_mapname2mapid(mapname); - - if(m<0 || amount<=0) - return 0; - data.m = m; - data.num = amount; - if(class_<0) - return 0; - data.class_ = class_; - - if(guardian < 0 || guardian >= MAX_GUARDIANS) - { - ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d (castle map %s)\n", guardian, class_, map[m].name); - return 0; - } - if (amount > 1) - ShowWarning("mob_spawn_guardian: Spawning %d guardians in position %d (castle map %s)\n", amount, map[m].name); - - if(sd){ - if(x<=0) x=sd->bl.x; - if(y<=0) y=sd->bl.y; - } - else if(x<=0 || y<=0) - ShowWarning("mob_spawn_guardian: Invalid coordinates (%d,%d)\n",x,y); - data.x = x; - data.y = y; - strncpy(data.name, mobname, NAME_LENGTH-1); - strncpy(data.eventname, event, 50); - if (!mob_parse_dataset(&data)) - return 0; - - gc=guild_mapname2gc(map[m].name); - if (gc == NULL) - { - ShowError("mob_spawn_guardian: No castle set at map %s\n", map[m].name); - return 0; - } - if (!gc->guild_id) - ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle with no guild (castle map %s)\n", class_, map[m].name); - else - g = guild_search(gc->guild_id); - - if (gc->guardian[guardian].id) - ShowWarning("mob_spawn_guardian: Spawning guardian in position %d which already has a guardian (castle map %s)\n", guardian, map[m].name); - - for(count=0;countmax_hp += 2000 * gc->defense; - md->guardian_data = aCalloc(1, sizeof(struct guardian_data)); - md->guardian_data->number = guardian; - md->guardian_data->guild_id = gc->guild_id; - md->guardian_data->castle = gc; - md->hp = gc->guardian[guardian].hp; - gc->guardian[guardian].id = md->bl.id; - if (g) - { - md->guardian_data->emblem_id = g->emblem_id; - memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH); - md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP); - } else if (md->guardian_data->guild_id) - add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id); - } - - return (amount>0)?md->bl.id:0; -} - -/*========================================== - * Reachability to a Specification ID existence place - * state indicates type of 'seek' mob should do: - * - MSS_LOOT: Looking for item, path must be easy. - * - MSS_RUSH: Chasing attacking player, path is determined by mob_ai&1 - * - MSS_FOLLOW: Initiative/support seek, path must be easy. - *------------------------------------------ - */ -int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state) -{ - int easy = 0; - - nullpo_retr(0, md); - nullpo_retr(0, bl); - switch (state) { - case MSS_RUSH: - easy = (battle_config.mob_ai&1?0:1); - break; - case MSS_LOOT: - case MSS_FOLLOW: - default: - easy = 1; - break; - } - return unit_can_reach_bl(&md->bl, bl, range, easy, NULL, NULL); -} - -/*========================================== - * Links nearby mobs (supportive mobs) - *------------------------------------------ - */ -int mob_linksearch(struct block_list *bl,va_list ap) -{ - struct mob_data *md; - int class_; - struct block_list *target; - unsigned int tick; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - md=(struct mob_data *)bl; - class_ = va_arg(ap, int); - target = va_arg(ap, struct block_list *); - tick=va_arg(ap, unsigned int); - - if (md->class_ == class_ && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME - && !md->target_id) - { - md->last_linktime = tick; - if( mob_can_reach(md,target,md->db->range2, MSS_FOLLOW) ){ // Reachability judging - md->target_id = target->id; - md->min_chase=md->db->range3; - return 1; - } - } - - return 0; -} - -/*========================================== - * mob spawn with delay (timer function) - *------------------------------------------ - */ -static int mob_delayspawn(int tid, unsigned int tick, int m, int n) -{ - struct block_list *bl = map_id2bl(m); - if (bl && bl->type == BL_MOB) - mob_spawn((TBL_MOB*)bl); - return 0; -} - -/*========================================== - * spawn timing calculation - *------------------------------------------ - */ -int mob_setdelayspawn(struct mob_data *md) -{ - unsigned int spawntime, spawntime1, spawntime2, spawntime3; - - - if (!md->spawn) //Doesn't has respawn data! - return unit_free(&md->bl); - - spawntime1 = md->last_spawntime + md->spawn->delay1; - spawntime2 = md->last_deadtime + md->spawn->delay2; - spawntime3 = gettick() + 5000 + rand()%5000; //Lupus - // spawntime = max(spawntime1,spawntime2,spawntime3); - if (DIFF_TICK(spawntime1, spawntime2) > 0) - spawntime = spawntime1; - else - spawntime = spawntime2; - if (DIFF_TICK(spawntime3, spawntime) > 0) - spawntime = spawntime3; - - add_timer(spawntime, mob_delayspawn, md->bl.id, 0); - return 0; -} - -/*========================================== - * Mob spawning. Initialization is also variously here. - *------------------------------------------ - */ -int mob_spawn (struct mob_data *md) -{ - int i=0; - unsigned int c =0, tick = gettick(); - - md->last_spawntime = tick; - md->last_thinktime = tick -MIN_MOBTHINKTIME; - if (md->bl.prev != NULL) - unit_remove_map(&md->bl,2); - else if (md->vd->class_ != md->class_) { - status_set_viewdata(&md->bl, md->class_); - md->db = mob_db(md->class_); - md->speed=md->db->speed; - if (md->spawn) - memcpy(md->name,md->spawn->name,NAME_LENGTH); - else if (battle_config.override_mob_names == 1) - memcpy(md->name,md->db->name,NAME_LENGTH); - else - memcpy(md->name,md->db->jname,NAME_LENGTH); - } - - if (md->spawn) { //Respawn data - md->bl.m = md->spawn->m; - - if ((md->spawn->x == 0 && md->spawn->y == 0) || md->spawn->xs || md->spawn->ys) - { //Monster can be spawned on an area. - short x, y, xs, ys; - if (md->spawn->x == 0 && md->spawn->y == 0) - xs = ys = -1; - else { - x = md->spawn->x; - y = md->spawn->y; - xs = md->spawn->xs/2; - ys = md->spawn->ys/2; - } - if (!map_search_freecell(NULL, md->spawn->m, &x, &y, xs, ys, battle_config.no_spawn_on_player?5:1)) { - // retry again later - add_timer(tick+5000,mob_delayspawn,md->bl.id,0); - return 1; - } - md->bl.x = x; - md->bl.y = y; - } else { - md->bl.x = md->spawn->x; - md->bl.y = md->spawn->y; - } - } - memset(&md->state, 0, sizeof(md->state)); - - md->attacked_id = 0; - md->attacked_players = 0; - md->attacked_count = 0; - md->target_id = 0; - md->mode = 0; - md->move_fail_count = 0; - - md->def_ele = md->db->element; - - if (!md->level) // [Valaris] - md->level=md->db->lv; - -// md->master_id = 0; - md->master_dist = 0; - - md->state.aggressive = md->db->mode&MD_ANGRY?1:0; - md->state.skillstate = MSS_IDLE; - md->next_walktime = tick+rand()%5000+1000; - md->last_linktime = tick; - - /* Guardians should be spawned using mob_spawn_guardian! [Skotlex] - * and the Emperium is spawned using mob_once_spawn. - md->guild_id = 0; - if (md->class_ >= 1285 && md->class_ <= 1288) { - struct guild_castle *gc=guild_mapname2gc(map[md->bl.m].name); - if(gc) - md->guild_id = gc->guild_id; - } - */ - - for (i = 0, c = tick-1000*3600*10; i < MAX_MOBSKILL; i++) - md->skilldelay[i] = c; - - memset(md->dmglog, 0, sizeof(md->dmglog)); - md->tdmg = 0; - if (md->lootitem) - memset(md->lootitem, 0, sizeof(md->lootitem)); - md->lootitem_count = 0; - - if(md->db->option) - // Added for carts, falcons and pecos for cloned monsters. [Valaris] - md->sc.option = md->db->option; - - md->max_hp = md->db->max_hp; - if(md->special_state.size==1) // change for sized monsters [Valaris] - md->max_hp/=2; - else if(md->special_state.size==2) - md->max_hp*=2; - md->hp = md->max_hp; - - map_addblock(&md->bl); - clif_spawn(&md->bl); - skill_unit_move(&md->bl,tick,1); - mobskill_use(md, tick, MSC_SPAWN); - return 0; -} - -/*========================================== - * Determines if the mob can change target. [Skotlex] - *------------------------------------------ - */ -static int mob_can_changetarget(struct mob_data* md, struct block_list* target, int mode) -{ - // if the monster was provoked ignore the above rule [celest] - if(md->state.provoke_flag) - { - if (md->state.provoke_flag == target->id) - return 1; - else if (!battle_config.mob_ai&4) - return 0; - } - - switch (md->state.skillstate) { - case MSS_BERSERK: //Only Assist, Angry or Aggressive+CastSensor mobs can change target while attacking. - if (mode&(MD_ASSIST|MD_ANGRY) || (mode&(MD_AGGRESSIVE|MD_CASTSENSOR)) == (MD_AGGRESSIVE|MD_CASTSENSOR)) - return (battle_config.mob_ai&4 || check_distance_bl(&md->bl, target, 3)); - else - return 0; - case MSS_RUSH: - return (mode&MD_AGGRESSIVE); - case MSS_FOLLOW: - case MSS_ANGRY: - case MSS_IDLE: - case MSS_WALK: - case MSS_LOOT: - return 1; - default: - return 0; - } -} - -/*========================================== - * Determination for an attack of a monster - *------------------------------------------ - */ -int mob_target(struct mob_data *md,struct block_list *bl,int dist) -{ - nullpo_retr(0, md); - nullpo_retr(0, bl); - - // Nothing will be carried out if there is no mind of changing TAGE by TAGE ending. - if(md->target_id && !mob_can_changetarget(md, bl, status_get_mode(&md->bl))) - return 0; - - if(!status_check_skilluse(&md->bl, bl, 0, 0)) - return 0; - - md->target_id = bl->id; // Since there was no disturbance, it locks on to target. - if (md->state.provoke_flag && bl->id != md->state.provoke_flag) - md->state.provoke_flag = 0; - md->min_chase=dist+md->db->range3; - if(md->min_chase>MAX_MINCHASE) - md->min_chase=MAX_MINCHASE; - return 0; -} - -/*========================================== - * The ?? routine of an active monster - *------------------------------------------ - */ -static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap) -{ - struct mob_data *md; - struct block_list **target; - int dist; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - md=va_arg(ap,struct mob_data *); - target= va_arg(ap,struct block_list**); - - //If can't seek yet, not an enemy, or you can't attack it, skip. - if ((*target) == bl || !status_check_skilluse(&md->bl, bl, 0, 0)) - return 0; - - if(md->nd){ - setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)2, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)bl->type, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 2, (void *)bl->id, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); - run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); - return 1; // We have script handling the work. - } - - if(battle_check_target(&md->bl,bl,BCT_ENEMY)<=0) - return 0; - - switch (bl->type) - { - case BL_PC: - if (((TBL_PC*)bl)->state.gangsterparadise && - !(status_get_mode(&md->bl)&MD_BOSS)) - return 0; //Gangster paradise protection. - case BL_MOB: - if((dist=distance_bl(&md->bl, bl)) < md->db->range2 - && (md->db->range > 6 || mob_can_reach(md,bl,dist+1, MSS_FOLLOW)) - && ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) //New target closer than previous one. - ) { - (*target) = bl; - md->target_id=bl->id; - md->min_chase= dist + md->db->range3; - if(md->min_chase>MAX_MINCHASE) - md->min_chase=MAX_MINCHASE; - return 1; - } - break; - } - return 0; -} - -/*========================================== - * chase target-change routine. - *------------------------------------------ - */ -static int mob_ai_sub_hard_changechase(struct block_list *bl,va_list ap) -{ - struct mob_data *md; - struct block_list **target; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - md=va_arg(ap,struct mob_data *); - target= va_arg(ap,struct block_list**); - - //If can't seek yet, not an enemy, or you can't attack it, skip. - if ((*target) == bl || battle_check_target(&md->bl,bl,BCT_ENEMY)<=0 || !status_check_skilluse(&md->bl, bl, 0, 0)) - return 0; - - switch (bl->type) - { - case BL_PC: - case BL_MOB: - if(check_distance_bl(&md->bl, bl, md->db->range) && - battle_check_range (&md->bl, bl, md->db->range) - ) { - (*target) = bl; - md->target_id=bl->id; - md->min_chase= md->db->range3; - return 1; - } - break; - } - return 0; -} - - -/*========================================== - * loot monster item search - *------------------------------------------ - */ -static int mob_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) -{ - struct mob_data* md; - struct block_list **target; - int dist; - - md=va_arg(ap,struct mob_data *); - target= va_arg(ap,struct block_list**); - - if((dist=distance_bl(&md->bl, bl)) < md->db->range2 && - mob_can_reach(md,bl,dist+1, MSS_LOOT) && - ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) //New target closer than previous one. - ) { - (*target) = bl; - md->target_id=bl->id; - md->min_chase=md->db->range3; - } - return 0; -} - -/*========================================== - * Processing of slave monsters - *------------------------------------------ - */ -static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick) -{ - struct block_list *bl; - int old_dist; - - nullpo_retr(0, md); - - bl=map_id2bl(md->master_id); - - if (!bl || status_isdead(bl)) { //主が死亡しているか見つからない - if(md->special_state.ai>0) - unit_remove_map(&md->bl, 1); - else - mob_damage(NULL,md,md->hp,0); - return 0; - } - - if(status_get_mode(&md->bl)&MD_CANMOVE) - { //If the mob can move, follow around. [Check by Skotlex] - - if(bl->m != md->bl.m || md->master_dist > 30) - { // Since it is not in the same map (or is way to far), just warp it - unit_warp(&md->bl,bl->m,bl->x,bl->y,3); - return 0; - } - - // Distance with between slave and master is measured. - old_dist=md->master_dist; - md->master_dist=distance_bl(&md->bl, bl); - - // Since the master was in near immediately before, teleport is carried out and it pursues. - if(old_dist<10 && md->master_dist>18){ - unit_warp(&md->bl,-1,bl->x,bl->y,3); - return 0; - } - - // Approach master if within view range, chase back to Master's area also if standing on top of the master. - if(md->master_distdb->range3 && - (md->master_dist>MOB_SLAVEDISTANCE || md->master_dist == 0) && - unit_can_move(&md->bl)) - { - short x = bl->x, y = bl->y; - mob_stop_attack(md); - if (map_search_freecell(&md->bl, bl->m, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 1)) - unit_walktoxy(&md->bl, x, y, 0); - } - } else if (bl->m != md->bl.m && map_flag_gvg(md->bl.m)) { - //Delete the summoned mob if it's in a gvg ground and the master is elsewhere. [Skotlex] - if(md->special_state.ai>0) - unit_remove_map(&md->bl, 1); - else - mob_damage(NULL,md,md->hp,0); - return 0; - } - - //Avoid attempting to lock the master's target too often to avoid unnecessary overload. [Skotlex] - if (DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME && !md->target_id) { - struct unit_data *ud = unit_bl2ud(bl); - md->last_linktime = tick; - - if (ud) { - struct block_list *tbl=NULL; - if (ud->target && ud->attacktimer != -1) - tbl=map_id2bl(ud->target); - else if (ud->skilltarget) { - tbl = map_id2bl(ud->skilltarget); - //Required check as skilltarget is not always an enemy. [Skotlex] - if (tbl && battle_check_target(&md->bl, tbl, BCT_ENEMY) <= 0) - tbl = NULL; - } - if (tbl && status_check_skilluse(&md->bl, tbl, 0, 0)) { - if(md->nd){ - setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)4, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)tbl->type, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 2, (void *)tbl->id, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); - run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); - } - md->target_id=tbl->id; - md->min_chase=md->db->range3+distance_bl(&md->bl, tbl); - if(md->min_chase>MAX_MINCHASE) - md->min_chase=MAX_MINCHASE; - } - } - } - return 0; -} - -/*========================================== - * A lock of target is stopped and mob moves to a standby state. - *------------------------------------------ - */ -int mob_unlocktarget(struct mob_data *md,int tick) -{ - nullpo_retr(0, md); - - if(md->nd){ - struct block_list *tbl = map_id2bl(md->target_id); - setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)6, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)(tbl?tbl->type:0), &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 2, (void *)(tbl?tbl->id:0), &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); - run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); - } - - md->target_id=0; - md->state.skillstate=MSS_IDLE; - md->next_walktime=tick+rand()%3000+3000; - mob_stop_attack(md); - md->ud.target = 0; - return 0; -} -/*========================================== - * Random walk - *------------------------------------------ - */ -int mob_randomwalk(struct mob_data *md,int tick) -{ - const int retrycount=20; - int i,x,y,c,d; - int speed; - - nullpo_retr(0, md); - - if(DIFF_TICK(md->next_walktime,tick)>0 || md->state.no_random_walk || !unit_can_move(&md->bl)) - return 0; - - d =12-md->move_fail_count; - if(d<5) d=5; - for(i=0;ibl.x; - y+=md->bl.y; - - if((map_getcell(md->bl.m,x,y,CELL_CHKPASS)) && unit_walktoxy(&md->bl,x,y,1)){ - break; - } - } - if(i==retrycount){ - md->move_fail_count++; - if(md->move_fail_count>1000){ - if(battle_config.error_log) - ShowWarning("MOB cant move. random spawn %d, class = %d\n",md->bl.id,md->class_); - md->move_fail_count=0; - mob_spawn(md); - } - return 0; - } - speed=status_get_speed(&md->bl); - for(i=c=0;iud.walkpath.path_len;i++){ // The next walk start time is calculated. - if(md->ud.walkpath.path[i]&1) - c+=speed*14/10; - else - c+=speed; - } - md->state.skillstate=MSS_WALK; - md->move_fail_count=0; - md->next_walktime = tick+rand()%3000+3000+c; - return 1; -} - -/*========================================== - * AI of MOB whose is near a Player - *------------------------------------------ - */ -static int mob_ai_sub_hard(struct block_list *bl,va_list ap) -{ - struct mob_data *md; - struct block_list *tbl = NULL, *abl = NULL; - unsigned int tick; - int dist; - int mode; - int search_size; - int view_range, can_move; - - md = (struct mob_data*)bl; - tick = va_arg(ap, unsigned int); - - if(md->bl.prev == NULL || md->hp <= 0) - return 1; - - if (DIFF_TICK(tick, md->last_thinktime) < MIN_MOBTHINKTIME) - return 0; - md->last_thinktime = tick; - - if (md->ud.skilltimer != -1) - return 0; - - if( md->ud.walktimer != -1 && md->ud.walkpath.path_pos <= 3) - return 0; - - // Abnormalities - if((md->sc.opt1 > 0 && md->sc.opt1 != OPT1_STONEWAIT) || md->sc.data[SC_BLADESTOP].timer != -1) - return 0; - - if (md->sc.count && md->sc.data[SC_BLIND].timer != -1) - view_range = 3; - else - view_range = md->db->range2; - mode = status_get_mode(&md->bl); - - can_move = (mode&MD_CANMOVE)&&unit_can_move(&md->bl); - - if (md->target_id) - { //Check validity of current target. [Skotlex] - tbl = map_id2bl(md->target_id); - if (!tbl || tbl->m != md->bl.m || - (md->ud.attacktimer == -1 && !status_check_skilluse(&md->bl, tbl, 0, 0)) || - (md->ud.walktimer != -1 && !check_distance_bl(&md->bl, tbl, md->min_chase)) || - ( - tbl->type == BL_PC && !(mode&MD_BOSS) && - ((TBL_PC*)tbl)->state.gangsterparadise - )) { //Unlock current target. - if (battle_config.mob_ai&8) //Inmediately stop chasing. - mob_stop_walking(md,1); - mob_unlocktarget(md, tick-(battle_config.mob_ai&8?3000:0)); //Imediately do random walk. - tbl = NULL; - } - } - - // Check for target change. - if (md->attacked_id && mode&MD_CANATTACK) - { - if (md->attacked_id == md->target_id) - { - if (!can_move && (battle_config.mob_ai&2) && - !battle_check_range(&md->bl, tbl, md->db->range)) - { //Rude-attacked. - if (md->attacked_count++ > 3) - mobskill_use(md, tick, MSC_RUDEATTACKED); - } - } else - if ((abl= map_id2bl(md->attacked_id)) && (!tbl || mob_can_changetarget(md, abl, mode))) { - if (md->bl.m != abl->m || abl->prev == NULL || - (dist = distance_bl(&md->bl, abl)) >= MAX_MINCHASE || - battle_check_target(bl, abl, BCT_ENEMY) <= 0 || - (battle_config.mob_ai&2 && !status_check_skilluse(bl, abl, 0, 0)) || - !mob_can_reach(md, abl, dist+2, MSS_RUSH) || - ( //Gangster Paradise check - abl->type == BL_PC && !(mode&MD_BOSS) && - ((TBL_PC*)abl)->state.gangsterparadise - ) - ) { //Can't attack back - if (md->attacked_count++ > 3) { - if (mobskill_use(md, tick, MSC_RUDEATTACKED) == 0 && can_move) - { - int dist = rand() % 10 + 1;//後退する距離 - int dir = map_calc_dir(abl, bl->x, bl->y); - int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}}; - unit_walktoxy(&md->bl, md->bl.x + dist * mask[dir][0], md->bl.y + dist * mask[dir][1], 0); - } - } - } else if (!(battle_config.mob_ai&2) && !status_check_skilluse(bl, abl, 0, 0)) { - //Can't attack back, but didn't invoke a rude attacked skill... - md->attacked_id = 0; //Simply unlock, shouldn't attempt to run away when in dumb_ai mode. - } else { //Attackable - if (!tbl || dist < md->db->range || !check_distance_bl(&md->bl, tbl, dist) - || battle_gettarget(tbl) != md->bl.id) - { //Change if the new target is closer than the actual one - //or if the previous target is not attacking the mob. [Skotlex] - md->target_id = md->attacked_id; // set target - md->state.aggressive = 0; //Retaliating. - md->attacked_count = 0; - md->min_chase = dist+md->db->range3; - if(md->min_chase>MAX_MINCHASE) - md->min_chase=MAX_MINCHASE; - tbl = abl; //Set the new target - } - } - } - if (md->state.aggressive && md->attacked_id == md->target_id) - md->state.aggressive = 0; //No longer aggressive, change to retaliate AI. - //Clear it since it's been checked for already. - md->attacked_players = 0; - md->attacked_id = 0; - } - - // Processing of slave monster, is it needed when there's a target to deal with? - if (md->master_id > 0 && !tbl) - mob_ai_sub_hard_slavemob(md, tick); - - // Scan area for targets - if ((!tbl && mode&MD_AGGRESSIVE && battle_config.monster_active_enable) || - (mode&MD_ANGRY && md->state.skillstate == MSS_FOLLOW) - ) { - map_foreachinrange (mob_ai_sub_hard_activesearch, &md->bl, - view_range, md->special_state.ai?BL_CHAR:BL_PC, md, &tbl); - if(!tbl && mode&MD_ANGRY && !md->state.aggressive) - md->state.aggressive = 1; //Restore angry state when no targets are visible. - } else if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW)) { - search_size = view_rangedb->range ? view_range:md->db->range; - map_foreachinrange (mob_ai_sub_hard_changechase, &md->bl, - search_size, (md->special_state.ai?BL_CHAR:BL_PC), md, &tbl); - } - if (!tbl && mode&MD_LOOTER && md->lootitem && - (md->lootitem_count < LOOTITEM_SIZE || battle_config.monster_loot_type != 1)) - { // Scan area for items to loot, avoid trying to loot of the mob is full and can't consume the items. - map_foreachinrange (mob_ai_sub_hard_lootsearch, &md->bl, - view_range, BL_ITEM, md, &tbl); - } - - if (tbl) - { //Target exists, attack or loot as applicable. - if (tbl->type != BL_ITEM) - { //Attempt to attack. - //At this point we know the target is attackable, we just gotta check if the range matches. - if (md->ud.target == tbl->id && md->ud.attacktimer != -1) - return 0; //Already locked. - - if (!battle_check_range (&md->bl, tbl, md->db->range)) - { //Out of range... - if (!(mode&MD_CANMOVE)) - { //Can't chase. Attempt to use a ranged skill at least? - md->state.skillstate = MSS_IDLE; - if (!mobskill_use(md, tick, -1)) - mob_unlocktarget(md,tick); - return 0; - } - - if (!can_move) - { //Stuck. Use an idle skill. o.O' - md->state.skillstate = MSS_IDLE; - if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL)) - mobskill_use(md, tick, -1); - return 0; - } - - md->state.skillstate = md->state.aggressive?MSS_FOLLOW:MSS_RUSH; - if (md->ud.walktimer != -1 && md->ud.target == tbl->id && - ( - !battle_config.mob_ai&1 || - check_distance_blxy(tbl, md->ud.to_x, md->ud.to_y, md->db->range) - )) //Current target tile is still within attack range. - return 0; - - //Follow up - if (!mob_can_reach(md, tbl, md->min_chase, MSS_RUSH) || - !unit_walktobl(&md->bl, tbl, md->db->range, 2|(!battle_config.mob_ai&1))) - //Give up. - mob_unlocktarget(md,tick); - return 0; - } - //Target within range, engage - md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK; - unit_attack(&md->bl,tbl->id,1); - return 0; - } else { //Target is BL_ITEM, attempt loot. - struct flooritem_data *fitem; - int i; - if (md->ud.target == tbl->id && md->ud.walktimer != -1) - return 0; //Already locked. - if (md->lootitem == NULL) - { //Can't loot... - mob_unlocktarget (md, tick); - mob_stop_walking(md,0); - return 0; - } - - if (!check_distance_bl(&md->bl, tbl, 1)) - { //Still not within loot range. - if (!(mode&MD_CANMOVE)) - { //A looter that can't move? Real smart. - mob_unlocktarget(md,tick); - return 0; - } - if (!can_move) //Stuck. Wait before walking. - return 0; - md->state.skillstate = MSS_LOOT; // ルート時スキル使用 - if (!unit_walktobl(&md->bl, tbl, 0, 1)) - mob_unlocktarget(md, tick); //Can't loot... - return 0; - } - //Within looting range. - if (md->ud.attacktimer != -1) - return 0; //Busy attacking? - - fitem = (struct flooritem_data *)tbl; - if (md->lootitem_count < LOOTITEM_SIZE) { - memcpy (&md->lootitem[md->lootitem_count++], &fitem->item_data, sizeof(md->lootitem[0])); - if(log_config.pick > 0) //Logs items, taken by (L)ooter Mobs [Lupus] - log_pick((struct map_session_data*)md, "L", md->class_, md->lootitem[md->lootitem_count-1].nameid, md->lootitem[md->lootitem_count-1].amount, &md->lootitem[md->lootitem_count-1]); - } else { //Destroy first looted item... - if (md->lootitem[0].card[0] == (short)0xff00) - intif_delete_petdata( MakeDWord(md->lootitem[0].card[1],md->lootitem[0].card[2]) ); - for (i = 0; i < LOOTITEM_SIZE - 1; i++) - memcpy (&md->lootitem[i], &md->lootitem[i+1], sizeof(md->lootitem[0])); - memcpy (&md->lootitem[LOOTITEM_SIZE-1], &fitem->item_data, sizeof(md->lootitem[0])); - } - //Clear item. - map_clearflooritem (tbl->id); - mob_unlocktarget (md,tick); - return 0; - } - } - - if(md->ud.walktimer == -1) { - // When there's no target, it is idling. - // Is it terribly exploitable to reuse the walkcounter for idle state skills? [Skotlex] - md->state.skillstate = MSS_IDLE; - if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mobskill_use(md, tick, -1)) - return 0; - } - // Nothing else to do... except random walking. - // Slaves do not random walk! [Skotlex] - if (can_move && !md->master_id && DIFF_TICK(md->next_walktime, tick) <= 0) - mob_randomwalk(md,tick); - - return 0; -} - -/*========================================== - * Serious processing for mob in PC field of view (foreachclient) - *------------------------------------------ - */ -static int mob_ai_sub_foreachclient(struct map_session_data *sd,va_list ap) -{ - unsigned int tick; - tick=va_arg(ap,unsigned int); - map_foreachinrange(mob_ai_sub_hard,&sd->bl, AREA_SIZE*2, BL_MOB,tick); - - return 0; -} - -/*========================================== - * Negligent mode MOB AI (PC is not in near) - *------------------------------------------ - */ -static int mob_ai_sub_lazy(DBKey key,void * data,va_list app) -{ - struct mob_data *md = (struct mob_data *)data; - va_list ap; - unsigned int tick; - int mode; - - nullpo_retr(0, md); - nullpo_retr(0, app); - - if(md->bl.type!=BL_MOB || md->bl.prev == NULL) - return 0; - - ap = va_arg(app, va_list); - - if (md->nd || (battle_config.mob_ai&32 && map[md->bl.m].users>0)) - return mob_ai_sub_hard(&md->bl, ap); - - tick=va_arg(ap,unsigned int); - - if(DIFF_TICK(tick,md->last_thinktime)last_thinktime=tick; - - if (md->bl.prev==NULL || md->hp <= 0) - return 1; - - // 取り巻きモンスターの処理(呼び戻しされた時) - if (md->master_id) { - mob_ai_sub_hard_slavemob (md,tick); - return 0; - } - - mode = status_get_mode(&md->bl); - if(DIFF_TICK(md->next_walktime,tick)<0 && - (mode&MD_CANMOVE) && unit_can_move(&md->bl) ){ - - if( map[md->bl.m].users>0 ){ - // Since PC is in the same map, somewhat better negligent processing is carried out. - - // It sometimes moves. - if(rand()%1000spawn && !md->spawn->x && !md->spawn->y) -// && !md->target_id && !(mode&MD_BOSS)) -// unit_warp(&md->bl,-1,-1,-1,0); - }else{ - // Since PC is not even in the same map, suitable processing is carried out even if it takes. - - // MOB which is not BOSS which is not Summons MOB, either -- a case -- sometimes -- leaping - if( rand()%1000spawn && !md->spawn->x && !md->spawn->y) - && !(mode&MD_BOSS)) - unit_warp(&md->bl,-1,-1,-1,0); - } - - md->next_walktime = tick+rand()%10000+5000; - } - return 0; -} - -/*========================================== - * Negligent processing for mob outside PC field of view (interval timer function) - *------------------------------------------ - */ -static int mob_ai_lazy(int tid,unsigned int tick,int id,int data) -{ - map_foreachiddb(mob_ai_sub_lazy,tick); - - return 0; -} - -/*========================================== - * Serious processing for mob in PC field of view (interval timer function) - *------------------------------------------ - */ -static int mob_ai_hard(int tid,unsigned int tick,int id,int data) -{ - - if (battle_config.mob_ai&32) - map_foreachiddb(mob_ai_sub_lazy,tick); - else - clif_foreachclient(mob_ai_sub_foreachclient,tick); - - return 0; -} - -/*========================================== - * Initializes the delay drop structure for mob-dropped items. - *------------------------------------------ - */ -static struct item_drop* mob_setdropitem(int nameid, int qty) -{ - struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop); - memset(&drop->item_data, 0, sizeof(struct item)); - drop->item_data.nameid = nameid; - drop->item_data.amount = qty; - drop->item_data.identify = !itemdb_isequip3(nameid); - drop->next = NULL; - return drop; -}; - -/*========================================== - * Initializes the delay drop structure for mob-looted items. - *------------------------------------------ - */ -static struct item_drop* mob_setlootitem(struct item* item) -{ - struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop); - memcpy(&drop->item_data, item, sizeof(struct item)); - drop->next = NULL; - return drop; -}; - -/*========================================== - * item drop with delay (timer function) - *------------------------------------------ - */ -static int mob_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; -} - -/*========================================== - * Sets the item_drop into the item_drop_list. - * Also performs logging and autoloot if enabled. - * rate is the drop-rate of the item, required for autoloot. - *------------------------------------------ - * by [Skotlex] - */ -static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate) -{ - if(log_config.pick > 0) - { //Logs items, dropped by mobs [Lupus] - if (loot) - log_pick((struct map_session_data*)md, "L", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, &ditem->item_data); - else - log_pick((struct map_session_data*)md, "M", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, NULL); - } - - if (dlist->first_sd && dlist->first_sd->state.autoloot && - (drop_rate <= dlist->first_sd->state.autoloot) - ) { //Autoloot. - if (party_share_loot( - dlist->first_sd->status.party_id? - party_search(dlist->first_sd->status.party_id): - NULL, - dlist->first_sd,&ditem->item_data) == 0 - ) { - ers_free(item_drop_ers, ditem); - return; - } - } - ditem->next = dlist->item; - dlist->item = ditem; -} - -int mob_timer_delete(int tid, unsigned int tick, int id, int data) -{ - struct block_list *bl=map_id2bl(id); - nullpo_retr(0, bl); - if (bl->type != BL_MOB) - return 0; //?? -//for Alchemist CANNIBALIZE [Lupus] - ((TBL_MOB*)bl)->deletetimer = -1; - unit_remove_map(bl, 3); - unit_free(bl); - return 0; -} - -int mob_convertslave_sub(struct block_list *bl,va_list ap) -{ - struct mob_data *md, *md2 = NULL; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, md = (struct mob_data *)bl); - - md2=va_arg(ap,TBL_MOB *); - - if(md->master_id > 0 && md->master_id == md2->bl.id){ - md->master_id = md2->master_id; - md->state.killer = md2->state.killer; - md->special_state.ai = md2->special_state.ai; - } - - return 0; -} - -int mob_convertslave(struct mob_data *md) -{ - nullpo_retr(0, md); - - map_foreachinmap(mob_convertslave_sub, md->bl.m, BL_MOB, md); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int mob_deleteslave_sub(struct block_list *bl,va_list ap) -{ - struct mob_data *md; - int id; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, md = (struct mob_data *)bl); - - id=va_arg(ap,int); - if(md->master_id > 0 && md->master_id == id ) - mob_damage(NULL,md,md->hp,1); - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int mob_deleteslave(struct mob_data *md) -{ - nullpo_retr(0, md); - - map_foreachinmap(mob_deleteslave_sub, md->bl.m, BL_MOB,md->bl.id); - return 0; -} -// Mob respawning through KAIZEL or NPC_REBIRTH [Skotlex] -int mob_respawn(int tid, unsigned int tick, int id,int data ) -{ - struct mob_data *md = (struct mob_data*)map_id2bl(id); - if (!md || md->bl.type != BL_MOB) - return 0; - //Mob must be dead and not in a map to respawn! - if (md->bl.prev != NULL || md->hp) - return 0; - - md->state.skillstate = MSS_IDLE; - md->last_thinktime = tick; - md->next_walktime = tick+rand()%50+5000; - md->last_linktime = tick; - map_addblock(&md->bl); - mob_heal(md,data*status_get_max_hp(&md->bl)/100); - clif_spawn(&md->bl); - skill_unit_move(&md->bl,tick,1); - mobskill_use(md, tick, MSC_SPAWN); - return 1; -} - -/*========================================== - * It is the damage of sd to damage to md. - *------------------------------------------ - */ -int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type) -{ - int i,count,minpos,mindmg; - struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE]; - struct { - struct party *p; - int id,zeny; - unsigned int base_exp,job_exp; - } pt[DAMAGELOG_SIZE]; - int pnum=0; - int mvp_damage,max_hp; - unsigned int tick = gettick(); - struct map_session_data *mvp_sd = NULL, *second_sd = NULL,*third_sd = NULL; - double temp; - struct item item; - int ret, mode; - int drop_rate; - int race; - - nullpo_retr(0, md); //srcはNULLで呼ばれる場合もあるので、他でチェック - - max_hp = status_get_max_hp(&md->bl); - race = status_get_race(&md->bl); - - if(src){ - if(md->nd){ - setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)1, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)src->type, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 2, (void *)src->id, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); - run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); - } - if(src->type == BL_PC) { - sd = (struct map_session_data *)src; - mvp_sd = sd; - } - } - - if(md->bl.prev==NULL){ - if(battle_config.error_log==1) - ShowError("mob_damage : BlockError!!\n"); - return 0; - } - - if(md->hp<=0) { - if(md->bl.prev != NULL) - unit_remove_map(&md->bl, 0); - return 0; - } - - if(damage > max_hp>>2) - skill_stop_dancing(&md->bl); - - if(md->hp > max_hp) - md->hp = max_hp; - - // The amount of overkill rounds to hp. - if(damage>md->hp) - damage=md->hp; - md->hp-=damage; - md->tdmg+=damage; //Store total damage... - - if(!(type&2)) { - int id = 0; - if (src) { - md->attacked_players++; - if (!md->attacked_players) //Counter overflow o.O - md->attacked_players++; - - switch (src->type) { - case BL_PC: - id = sd->status.char_id; - if(rand()%1000 < 1000/md->attacked_players) - md->attacked_id = sd->bl.id; - break; - case BL_PET: - { - struct pet_data *pd = (struct pet_data*)src; - if (battle_config.pet_attack_exp_to_master) { - id = pd->msd->status.char_id; - damage=(damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly. - } - //Let mobs retaliate against the pet's master [Skotlex] - if(rand()%1000 < 1000/md->attacked_players) - md->attacked_id = pd->msd->bl.id; - break; - } - case BL_MOB: - { - struct mob_data* md2 = (struct mob_data*)src; - if(md2->special_state.ai && md2->master_id) { - struct map_session_data* msd = map_id2sd(md2->master_id); - if (msd) id = msd->status.char_id; - } - if(rand()%1000 < 1000/md->attacked_players) - { //Let players decide whether to retaliate versus the master or the mob. [Skotlex] - if (md2->master_id && battle_config.retaliate_to_master) - md->attacked_id = md2->master_id; - else - md->attacked_id = md2->bl.id; - } - break; - } - } - } - //Log damage... - if (id && damage > 0) { - for(i=0,minpos=DAMAGELOG_SIZE-1,mindmg=0x7fffffff;idmglog[i].id==id) - break; - if(md->dmglog[i].id==0) { //Store data in first empty slot. - md->dmglog[i].id = id; - break; - } - if(md->dmglog[i].dmgdmglog[i].dmg; - } - } - if(idmglog[i].dmg+=damage; - else { - md->dmglog[minpos].id=id; - md->dmglog[minpos].dmg=damage; - } - } - } - - if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) { // guardian hp update [Valaris] (updated by [Skotlex]) - if ((md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->hp) <= 0) - { - guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); - guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); - } - } // end addition - - if(md->special_state.ai == 2 && //スフィアーマイン - src && md->master_id == src->id) - { - md->state.alchemist = 1; - mobskill_use(md, tick, MSC_ALCHEMIST); - } - - if (battle_config.show_mob_hp) - clif_charnameack (0, &md->bl); - - if(md->hp > 0) - return damage; - - // ----- ここから死亡処理 ----- - - mode = status_get_mode(&md->bl); //Mode will be used for various checks regarding exp/drops. - - //changestate will clear all status effects, so we need to know if RICHMANKIM is in effect before then. [Skotlex] - //I just recycled ret because it isn't used until much later and I didn't want to add a new variable for it. - ret = (md->sc.data[SC_RICHMANKIM].timer != -1)?(25 + 11*md->sc.data[SC_RICHMANKIM].val1):0; - - md->state.skillstate = MSS_DEAD; - mobskill_use(md,tick,-1); //On Dead skill. - - if (md->sc.data[SC_KAIZEL].timer != -1) { - //Revive in a bit. - max_hp = 10*md->sc.data[SC_KAIZEL].val1; //% of life to rebirth with - clif_clearchar_area(&md->bl,1); - mob_unlocktarget(md,tick); - mob_stop_walking(md, 0); - map_delblock(&md->bl); - add_timer(gettick()+3000, mob_respawn, md->bl.id, max_hp); - return damage; - } - - map_freeblock_lock(); - - memset(tmpsd,0,sizeof(tmpsd)); - memset(pt,0,sizeof(pt)); - - max_hp = status_get_max_hp(&md->bl); - - if(src && src->type == BL_MOB) - mob_unlocktarget((struct mob_data *)src,tick); - - if(sd) { - int sp = 0, hp = 0; - sp += sd->sp_gain_value; - sp += sd->sp_gain_race[race]; - sp += sd->sp_gain_race[mode&MD_BOSS?10:11]; - hp += sd->hp_gain_value; - if (sp > 0) { - if(sd->status.sp + sp > sd->status.max_sp) - sp = sd->status.max_sp - sd->status.sp; - sd->status.sp += sp; - if (sp > 0 && battle_config.show_hp_sp_gain) - clif_heal(sd->fd,SP_SP,sp); - } - if (hp > 0) { - if(sd->status.hp + hp > sd->status.max_hp) - hp = sd->status.max_hp - sd->status.hp; - sd->status.hp += hp; - if (hp > 0 && battle_config.show_hp_sp_gain) - clif_heal(sd->fd,SP_HP,hp); - } - if (sd->mission_mobid == md->class_) { //TK_MISSION [Skotlex] - //Recycling hp for new random target id... - if (++sd->mission_count >= 100 && (hp = mob_get_random_id(0, 0, sd->status.base_level))) - { - pc_addfame(sd, 1); - sd->mission_mobid = hp; - pc_setglobalreg(sd,"TK_MISSION_ID", hp); - sd->mission_count = 0; - clif_mission_mob(sd, hp, 0); - } - pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count); - } - } - - // map外に消えた人は計算から除くので - // overkill分は無いけどsumはmax_hpとは違う - - for(i=0,count=0,mvp_damage=0;idmglog[i].id==0) - break; //Reached end of log. - count++; //Count an attacker even if he is dead/logged-out. - tmpsd[i] = map_charid2sd(md->dmglog[i].id); - if(tmpsd[i] == NULL) - continue; - if(tmpsd[i]->bl.m != md->bl.m || pc_isdead(tmpsd[i])) - continue; - - if(mvp_damagedmglog[i].dmg){ - third_sd = second_sd; - second_sd = mvp_sd; - mvp_sd=tmpsd[i]; - mvp_damage=md->dmglog[i].dmg; - } - } - - // [MouseJstr] - if((map[md->bl.m].flag.pvp == 0) || (battle_config.pvp_exp == 1)) { - - // 経験値の分配 - for(i=0;ibl.m != md->bl.m || pc_isdead(tmpsd[i])) - continue; - - if (battle_config.exp_calc_type) // eAthena's exp formula based on max hp. - per = (double)md->dmglog[i].dmg/(double)max_hp; - else //jAthena's exp formula based on total damage. - per = (double)md->dmglog[i].dmg/(double)md->tdmg; - - if (count>1) - per *= (9.+(double)((count > 6)? 6:count))/10.; //attackers count bonus. - - base_exp = md->db->base_exp; - job_exp = md->db->job_exp; - - if (ret) - per += per*ret/100.; //SC_RICHMANKIM bonus. [Skotlex] - - if(sd) { - if (sd->expaddrace[race]) - per += per*sd->expaddrace[race]/100.; - per += per*sd->expaddrace[mode&MD_BOSS?10:11]/100.; - } - if (battle_config.pk_mode && - (int)(md->db->lv - tmpsd[i]->status.base_level) >= 20) //Needed due to unsigned checks - per *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris] - - //SG additional exp from Blessings [Komurka] - probably can be optimalized ^^;; - // - if(md->class_ == tmpsd[i]->hate_mob[2] && (battle_config.allow_skill_without_day || is_day_of_star() || tmpsd[i]->sc.data[SC_MIRACLE].timer!=-1)) - per += per*20*pc_checkskill(tmpsd[i],SG_STAR_BLESS)/100.; - else if(md->class_ == tmpsd[i]->hate_mob[1] && (battle_config.allow_skill_without_day || is_day_of_moon())) - per += per*10*pc_checkskill(tmpsd[i],SG_MOON_BLESS)/100.; - else if(md->class_ == tmpsd[i]->hate_mob[0] && (battle_config.allow_skill_without_day || is_day_of_sun())) - per += per*10*pc_checkskill(tmpsd[i],SG_SUN_BLESS)/100.; - - if(md->special_state.size==1) // change experience for different sized monsters [Valaris] - per /=2.; - else if(md->special_state.size==2) - per *=2.; - if(md->master_id && md->special_state.ai) //New rule: Only player-summoned mobs do not give exp. [Skotlex] - per = 0; - else { - if(battle_config.zeny_from_mobs) { - if(md->level > 0) zeny=(int) ((md->level+rand()%md->level)*per); // zeny calculation moblv + random moblv [Valaris] - if(md->db->mexp > 0) - zeny*=rand()%250; - if(md->special_state.size==1 && zeny >=2) // change zeny for different sized monsters [Valaris] - zeny/=2; - else if(md->special_state.size==2 && zeny >1) - zeny*=2; - } - if(battle_config.mobs_level_up && md->level > md->db->lv) // [Valaris] - per+= per*(md->level-md->db->lv)*battle_config.mobs_level_up_exp_rate/100; - } - - if (per > 4) per = 4; //Limit gained exp to quadro the mob's exp. [3->4 Komurka] - - if (base_exp*per > UINT_MAX) - base_exp = UINT_MAX; - else - base_exp = (unsigned int)(base_exp*per); - - if (job_exp*per > UINT_MAX) - job_exp = UINT_MAX; - else - job_exp = (unsigned int)(job_exp*per); - -/* //mapflags: noexp check [Lorky] - if (map[md->bl.m].flag.nobaseexp == 1) base_exp=0; - else if (base_exp < 1) base_exp = 1; - - if (map[md->bl.m].flag.nojobexp == 1) job_exp=0; - else if (job_exp < 1) job_exp = 1; -*/ - if (map[md->bl.m].flag.nobaseexp == 1) - base_exp=0; - else if (base_exp < 1) - base_exp = (map[md->bl.m].bexp<=100) ? 1 : map[md->bl.m].bexp/100; - else if ( map[md->bl.m].bexp != 100 ) - base_exp=(int)((double)base_exp*((double)map[md->bl.m].bexp/100.0)); - - if (map[md->bl.m].flag.nojobexp == 1) - job_exp=0; - else if (job_exp < 1) - job_exp = (map[md->bl.m].jexp<=100) ? 1 : map[md->bl.m].jexp/100; - else if ( map[md->bl.m].bexp != 100 ) - job_exp=(int)((double)job_exp*((double)map[md->bl.m].jexp/100.0)); - - - - //end added Lorky - if((pid=tmpsd[i]->status.party_id)>0){ // パーティに入っている - int j; - for(j=0;jexp!=0){ - pt[pnum].id=pid; - pt[pnum].p=p; - pt[pnum].base_exp=base_exp; - pt[pnum].job_exp=job_exp; - if(battle_config.zeny_from_mobs) - pt[pnum].zeny=zeny; // zeny share [Valaris] - pnum++; - flag=0; - } - }else{ // いるときは公平 - if (pt[j].base_exp > UINT_MAX - base_exp) - pt[j].base_exp=UINT_MAX; - else - pt[j].base_exp+=base_exp; - - if (pt[j].job_exp > UINT_MAX - job_exp) - pt[j].job_exp=UINT_MAX; - else - pt[j].job_exp+=job_exp; - - if(battle_config.zeny_from_mobs) - pt[j].zeny+=zeny; // zeny share [Valaris] - flag=0; - } - } - if(flag) { // added zeny from mobs [Valaris] - if(base_exp > 0 || job_exp > 0) - pc_gainexp(tmpsd[i],base_exp,job_exp); - if (battle_config.zeny_from_mobs && zeny > 0) { - pc_getzeny(tmpsd[i],zeny); // zeny from mobs [Valaris] - } - } - - } - // 公平分配 - for(i=0;ibl.m,pt[i].base_exp,pt[i].job_exp,pt[i].zeny); - - // item drop - if (!(type&1)) { - struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); - struct item_drop *ditem; - dlist->m = md->bl.m; - dlist->x = md->bl.x; - dlist->y = md->bl.y; - dlist->first_sd = mvp_sd; - dlist->second_sd = second_sd; - dlist->third_sd = third_sd; - dlist->item = NULL; - - if (map[md->bl.m].flag.nomobloot || - (md->master_id && md->special_state.ai && ( - battle_config.alchemist_summon_reward == 0 || //Noone gives items - (md->class_ != 1142 && battle_config.alchemist_summon_reward == 1) //Non Marine spheres don't drop items - ))) - ; //No normal loot. - else - for (i = 0; i < MAX_MOB_DROP; i++) { - if (md->db->dropitem[i].nameid <= 0) - continue; - drop_rate = md->db->dropitem[i].p; - if (drop_rate <= 0 && !battle_config.drop_rate0item) - drop_rate = 1; - // change drops depending on monsters size [Valaris] - if(md->special_state.size==1 && drop_rate >= 2) - drop_rate/=2; - else if(md->special_state.size==2 && drop_rate > 0) - drop_rate*=2; - //Drops affected by luk as a fixed increase [Valaris] - if (src && battle_config.drops_by_luk > 0) - drop_rate += status_get_luk(src)*battle_config.drops_by_luk/100; - //Drops affected by luk as a % increase [Skotlex] - if (src && battle_config.drops_by_luk2 > 0) - drop_rate += (int)(0.5+drop_rate*status_get_luk(src)*battle_config.drops_by_luk2/10000.0); - if (sd && battle_config.pk_mode && - (int)(md->db->lv - sd->status.base_level) >= 20) - drop_rate = (int)(drop_rate*1.25); // pk_mode increase drops if 20 level difference [Valaris] - -// if (10000 < rand()%10000+drop_rate) { //May be better if MAX_RAND is too low? - if (drop_rate < rand() % 10000 + 1) //fixed 0.01% impossible drops bug [Lupus] - continue; - - ditem = mob_setdropitem(md->db->dropitem[i].nameid, 1); - - //A Rare Drop Global Announce by Lupus - if(drop_rate<=battle_config.rare_drop_announce) { - struct item_data *i_data; - char message[128]; - i_data = itemdb_search(ditem->item_data.nameid); - sprintf (message, msg_txt(541), (mvp_sd?mvp_sd->status.name:"???"), md->name, i_data->jname, (float)drop_rate/100); - //MSG: "'%s' won %s's %s (chance: %%%0.02f)" - intif_GMmessage(message,strlen(message)+1,0); - } - // Announce first, or else ditem will be freed. [Lance] - // By popular demand, use base drop rate for autoloot code. [Skotlex] - mob_item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p); - } - - // Ore Discovery [Celest] - if (sd == mvp_sd && !map[md->bl.m].flag.nomobloot && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rand()%10000) { - ditem = mob_setdropitem(itemdb_searchrandomid(IG_FINDINGORE), 1); - mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10); - } - - if(sd) { - int itemid = 0; - for (i = 0; i < sd->add_drop_count; i++) { - if (sd->add_drop[i].id < 0) - continue; - if (sd->add_drop[i].race & (1<add_drop[i].race & 1<<(mode&MD_BOSS?10:11)) - { - //check if the bonus item drop rate should be multiplied with mob level/10 [Lupus] - if(sd->add_drop[i].rate<0) { - //it's negative, then it should be multiplied. e.g. for Mimic,Myst Case Cards, etc - // rate = base_rate * (mob_level/10) + 1 - drop_rate = -sd->add_drop[i].rate*(md->level/10)+1; - if (drop_rate < battle_config.item_drop_adddrop_min) - drop_rate = battle_config.item_drop_adddrop_min; - else if (drop_rate > battle_config.item_drop_adddrop_max) - drop_rate = battle_config.item_drop_adddrop_max; - } - else - //it's positive, then it goes as it is - drop_rate = sd->add_drop[i].rate; - if (drop_rate < rand()%10000 +1) - continue; - itemid = (sd->add_drop[i].id > 0) ? sd->add_drop[i].id : - itemdb_searchrandomid(sd->add_drop[i].group); - - mob_item_drop(md, dlist, mob_setdropitem(itemid,1), 0, drop_rate); - } - } - - if(sd->get_zeny_num && rand()%100 < sd->get_zeny_rate) //Gets get_zeny_num per level +/-10% [Skotlex] - pc_getzeny(sd,md->db->lv*sd->get_zeny_num*(90+rand()%21)/100); - } - if(md->lootitem) { - for(i=0;ilootitem_count;i++) - mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000); - } - if (dlist->item) //There are drop items. - add_timer(tick + (!battle_config.delay_battle_damage?500:0), - mob_delay_item_drop, (int)dlist, 0); - else //No drops - ers_free(item_drop_list_ers, dlist); - } - - // mvp処理 - if(mvp_sd && md->db->mexp > 0 && !md->special_state.ai){ - int log_mvp[2] = {0}; - int j; - int mexp; - temp = ((double)md->db->mexp * (9.+(double)count)/10.); //[Gengar] - mexp = (temp > 2147483647.)? 0x7fffffff:(int)temp; - - //mapflag: noexp check [Lorky] - if (map[md->bl.m].flag.nobaseexp == 1 || map[md->bl.m].flag.nojobexp == 1) mexp=1; - //end added [Lorky] - - if(mexp < 1) mexp = 1; - - if(use_irc && irc_announce_mvp_flag) - irc_announce_mvp(mvp_sd,md); - - clif_mvp_effect(mvp_sd); // エフェクト - clif_mvp_exp(mvp_sd,mexp); - pc_gainexp(mvp_sd,mexp,0); - log_mvp[1] = mexp; - for(j=0;j<3;j++){ - i = rand() % 3; - //mapflag: noloot check [Lorky] - if (map[md->bl.m].flag.nomvploot == 1) break; - //end added Lorky - - if(md->db->mvpitem[i].nameid <= 0) - continue; - drop_rate = md->db->mvpitem[i].p; - if(drop_rate <= 0 && !battle_config.drop_rate0item) - drop_rate = 1; - if(drop_rate <= rand()%10000+1) //if ==0, then it doesn't drop - continue; - memset(&item,0,sizeof(item)); - item.nameid=md->db->mvpitem[i].nameid; - item.identify=!itemdb_isequip3(item.nameid); - clif_mvp_item(mvp_sd,item.nameid); - log_mvp[0] = item.nameid; - if(mvp_sd->weight*2 > mvp_sd->max_weight) - map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1); - else if((ret = pc_additem(mvp_sd,&item,1))) { - clif_additem(sd,0,0,ret); - map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1); - } - - //A Rare MVP Drop Global Announce by Lupus - if(drop_rate<=battle_config.rare_drop_announce) { - struct item_data *i_data; - char message[128]; - i_data = itemdb_exists(item.nameid); - sprintf (message, msg_txt(541), mvp_sd?mvp_sd->status.name :"???", md->name, i_data->jname, (float)drop_rate/100); - //MSG: "'%s' won %s's %s (chance: %%%0.02f)" - intif_GMmessage(message,strlen(message)+1,0); - } - - if(log_config.pick > 0) {//Logs items, MVP prizes [Lupus] - log_pick((struct map_session_data*)md, "M", md->class_, item.nameid, -1, NULL); - log_pick(mvp_sd, "P", 0, item.nameid, 1, NULL); - } - - break; - } - - if(log_config.mvpdrop > 0) - log_mvpdrop(mvp_sd, md->class_, log_mvp); - } - - } // [MouseJstr] - - // NPC Event [OnAgitBreak] - if(md->npc_event[0] && strcmp(((md->npc_event)+strlen(md->npc_event)-13),"::OnAgitBreak") == 0) { - ShowNotice("MOB.C: Run NPC_Event[OnAgitBreak].\n"); - if (agit_flag == 1) //Call to Run NPC_Event[OnAgitBreak] - guild_agit_break(md); - } - - if(src && src->type == BL_MOB){ - struct mob_data *smd = (struct mob_data *)src; - if(smd->nd){ - setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)5, &smd->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)md->bl.type, &smd->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 2, (void *)md->bl.id, &smd->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 3, (void *)smd->bl.id, &smd->nd->u.scr.script->script_vars); - run_script(smd->nd->u.scr.script, 0, 0, smd->nd->bl.id); - } - } - - if(md->nd){ - setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)3, &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)(src?src->type:0), &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 2, (void *)(src?src->id:0), &md->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); - run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); - } else if(md->npc_event[0]){ - // if(battle_config.battle_log) - // printf("mob_damage : run event : %s\n",md->npc_event); - if(src && src->type == BL_PET) - sd = ((struct pet_data *)src)->msd; - if(sd && battle_config.mob_npc_event_type) - npc_event(sd,md->npc_event,0); - else if(mvp_sd) - npc_event(mvp_sd,md->npc_event,0); - - } else if (mvp_sd) { //lordalfa - pc_setglobalreg(mvp_sd,"killedrid",(md->class_)); - if(mvp_sd->state.event_kill_mob) - npc_script_event(mvp_sd, NPCE_KILLNPC); // PCKillNPC [Lance] - } - if(md->level) md->level=0; - map_freeblock_unlock(); - unit_remove_map(&md->bl, 1); - return damage; -} - -int mob_guardian_guildchange(struct block_list *bl,va_list ap) -{ - struct mob_data *md; - struct guild* g; - - nullpo_retr(0, bl); - nullpo_retr(0, md = (struct mob_data *)bl); - - if (!md->guardian_data) - return 0; - - if (md->guardian_data->castle->guild_id == 0) - { //Castle with no owner? Delete the guardians. - if (md->class_ == MOBID_EMPERIUM) - { //But don't delete the emperium, just clear it's guild-data - md->guardian_data->guild_id = 0; - md->guardian_data->emblem_id = 0; - md->guardian_data->guild_name[0] = '\0'; - } else { - if (md->guardian_data->castle->guardian[md->guardian_data->number].visible) - { //Safe removal of guardian. - md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; - guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); - guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); - } - unit_free(&md->bl); //Remove guardian. - } - return 0; - } - - g = guild_search(md->guardian_data->castle->guild_id); - if (g == NULL) - { //Properly remove guardian info from Castle data. - ShowError("mob_guardian_guildchange: New Guild (id %d) does not exists!\n", md->guardian_data->guild_id); - md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; - guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); - guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); - unit_free(&md->bl); - return 0; - } - - md->guardian_data->guild_id = md->guardian_data->castle->guild_id; - md->guardian_data->emblem_id = g->emblem_id; - md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP); - memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH); - - return 1; -} - -/*========================================== - * Pick a random class for the mob - *------------------------------------------ - */ -int mob_random_class (int *value, size_t count) -{ - nullpo_retr(0, value); - - // no count specified, look into the array manually, but take only max 5 elements - if (count < 1) { - count = 0; - while(count < 5 && mobdb_checkid(value[count])) count++; - if(count < 1) // nothing found - return 0; - } else { - // check if at least the first value is valid - if(mobdb_checkid(value[0]) == 0) - return 0; - } - //Pick a random value, hoping it exists. [Skotlex] - return mobdb_checkid(value[rand()%count]); -} - -/*========================================== - * Change mob base class - *------------------------------------------ - */ -int mob_class_change (struct mob_data *md, int class_) -{ - unsigned int tick = gettick(); - int i, c, hp_rate; - - nullpo_retr(0, md); - - if (md->bl.prev == NULL) - return 0; - - hp_rate = md->hp*100/status_get_max_hp(&md->bl); - md->db = mob_db(class_); - md->max_hp = md->db->max_hp; //Update the mob's max HP - if (battle_config.monster_class_change_full_recover) { - md->hp = md->max_hp; - memset(md->dmglog, 0, sizeof(md->dmglog)); - md->tdmg = 0; - } else - md->hp = md->max_hp*hp_rate/100; - if(md->hp > md->max_hp) md->hp = md->max_hp; - else if(md->hp < 1) md->hp = 1; - - if (battle_config.override_mob_names==1) - memcpy(md->name,md->db->name,NAME_LENGTH-1); - else - memcpy(md->name,md->db->jname,NAME_LENGTH-1); - md->speed = md->db->speed; - md->def_ele = md->db->element; - - mob_stop_attack(md); - mob_stop_walking(md, 0); - unit_skillcastcancel(&md->bl, 0); - status_set_viewdata(&md->bl, class_); - clif_mob_class_change(md,class_); - - for(i=0,c=tick-1000*3600*10;iskilldelay[i] = c; - - if(md->lootitem == NULL && md->db->mode&MD_LOOTER) - md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); - - if (battle_config.show_mob_hp) - clif_charnameack(0, &md->bl); - - return 0; -} - -/*========================================== - * mob回復 - *------------------------------------------ - */ -int mob_heal(struct mob_data *md,int heal) -{ - int max_hp; - - nullpo_retr(0, md); - max_hp = status_get_max_hp(&md->bl); - - md->hp += heal; - if( max_hp < md->hp ) - md->hp = max_hp; - else if (md->hp <= 0) { - md->hp = 1; - return mob_damage(NULL, md, 1, 0); - } - - if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) { // guardian hp update [Valaris] (updated by [Skotlex]) - if ((md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->hp) <= 0) - { - guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); - guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); - } - } // end addition - - if (battle_config.show_mob_hp) - clif_charnameack(0, &md->bl); - - return 0; -} - - -/*========================================== - * Added by RoVeRT - *------------------------------------------ - */ -int mob_warpslave_sub(struct block_list *bl,va_list ap) -{ - struct mob_data *md=(struct mob_data *)bl; - struct block_list *master; - short x,y,range=0; - master = va_arg(ap, struct block_list*); - range = va_arg(ap, int); - - if(md->master_id!=master->id) - return 0; - - map_search_freecell(master, 0, &x, &y, range, range, 0); - unit_warp(&md->bl, master->m, x, y,2); - return 1; -} - -/*========================================== - * Added by RoVeRT - * Warps slaves. Range is the area around the master that they can - * appear in randomly. - *------------------------------------------ - */ -int mob_warpslave(struct block_list *bl, int range) -{ - if (range < 1) - range = 1; //Min range needed to avoid crashes and stuff. [Skotlex] - - return map_foreachinmap(mob_warpslave_sub, bl->m, BL_MOB, bl, range); -} - -/*========================================== - * 画面内の取り巻きの数計算用(foreachinarea) - *------------------------------------------ - */ -int mob_countslave_sub(struct block_list *bl,va_list ap) -{ - int id; - struct mob_data *md; - id=va_arg(ap,int); - - md = (struct mob_data *)bl; - if( md->master_id==id ) - return 1; - return 0; -} - -/*========================================== - * 画面内の取り巻きの数計算 - *------------------------------------------ - */ -int mob_countslave(struct block_list *bl) -{ - return map_foreachinmap(mob_countslave_sub, bl->m, BL_MOB,bl->id); -} -/*========================================== - * Summons amount slaves contained in the value[5] array using round-robin. [adapted by Skotlex] - *------------------------------------------ - */ -int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id) -{ - struct mob_data *md; - struct spawn_data data; - int count = 0,k=0,mode; - - nullpo_retr(0, md2); - nullpo_retr(0, value); - - memset(&data, 0, sizeof(struct spawn_data)); - data.m = md2->bl.m; - data.x = md2->bl.x; - data.y = md2->bl.y; - data.num = 1; - data.state.size = md2->special_state.size; - data.state.ai = md2->special_state.ai; - - if(mobdb_checkid(value[0]) == 0) - return 0; - - mode = status_get_mode(&md2->bl); - - while(count < 5 && mobdb_checkid(value[count])) count++; - if(count < 1) return 0; - if (amount > 0 && amount < count) { //Do not start on 0, pick some random sub subset [Skotlex] - k = rand()%count; - amount+=k; //Increase final value by same amount to preserve total number to summon. - } - for(;kbl, 0, &x, &y, 4, 4, 0)) { - data.x = x; - data.y = y; - } else { - data.x = md2->bl.x; - data.y = md2->bl.y; - } - strcpy(data.name, "--ja--"); //These two need to be loaded from the db for each slave. - data.level = 0; - if (!mob_parse_dataset(&data)) - continue; - - md= mob_spawn_dataset(&data); - - if (battle_config.slaves_inherit_speed && mode&MD_CANMOVE - && (skill_id != NPC_METAMORPHOSIS && skill_id != NPC_TRANSFORMATION)) - md->speed=md2->speed; - - //Inherit the aggressive mode of the master. - if (battle_config.slaves_inherit_mode) { - md->mode = md->db->mode; - if (mode&MD_AGGRESSIVE) - md->mode |= MD_AGGRESSIVE; - else - md->mode &=~MD_AGGRESSIVE; - if (md->mode == md->db->mode) - md->mode = 0; //No change. - } - - md->special_state.cached= battle_config.dynamic_mobs; //[Skotlex] - - if (!battle_config.monster_class_change_full_recover && - (skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS)) - { //Scale HP - md->hp = (md->max_hp*md2->hp)/md2->max_hp; - } - - if(skill_id == NPC_SUMMONSLAVE) - md->master_id=md2->bl.id; - - mob_spawn(md); - clif_skill_nodamage(&md->bl,&md->bl,skill_id,amount,1); - } - return 0; -} - -/*========================================== - *MOBskillから該当skillidのskillidxを返す - *------------------------------------------ - */ -int mob_skillid2skillidx(int class_,int skillid) -{ - int i, max = mob_db(class_)->maxskill; - struct mob_skill *ms=mob_db(class_)->skill; - - if(ms==NULL) - return -1; - - for(i=0;ibl.id == bl->id && !(battle_config.mob_ai&16)) - return 0; - - if ((*fr) != NULL) //A friend was already found. - return 0; - - if (battle_check_target(&md->bl,bl,BCT_ENEMY)>0) - return 0; - - rate = 100*status_get_hp(bl)/status_get_max_hp(bl); - - if (rate >= min_rate && rate <= max_rate) - (*fr) = bl; - return 1; -} -static struct block_list *mob_getfriendhprate(struct mob_data *md,int min_rate,int max_rate) -{ - struct block_list *fr=NULL; - int type = BL_MOB; - - nullpo_retr(NULL, md); - - if (md->special_state.ai) //Summoned creatures. [Skotlex] - type = BL_PC; - - map_foreachinrange(mob_getfriendhprate_sub, &md->bl, 8, type,md,min_rate,max_rate,&fr); - return fr; -} -/*========================================== - * Check hp rate of its master - *------------------------------------------ - */ -struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md,int rate) -{ - if (md && md->master_id > 0) { - struct block_list *bl = map_id2bl(md->master_id); - if (status_get_hp(bl) < status_get_max_hp(bl) * rate / 100) - return bl; - } - - return NULL; -} -/*========================================== - * What a status state suits by nearby MOB is looked for. - *------------------------------------------ - */ -int mob_getfriendstatus_sub(struct block_list *bl,va_list ap) -{ - int cond1,cond2; - struct mob_data **fr, *md, *mmd; - int flag=0; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, md=(struct mob_data *)bl); - nullpo_retr(0, mmd=va_arg(ap,struct mob_data *)); - - if( mmd->bl.id == bl->id && !(battle_config.mob_ai&16) ) - return 0; - - if (battle_check_target(&mmd->bl,bl,BCT_ENEMY)>0) - return 0; - cond1=va_arg(ap,int); - cond2=va_arg(ap,int); - fr=va_arg(ap,struct mob_data **); - if( cond2==-1 ){ - int j; - for(j=SC_COMMON_MIN;j<=SC_COMMON_MAX && !flag;j++){ - if ((flag=(md->sc.data[j].timer!=-1))) //Once an effect was found, break out. [Skotlex] - break; - } - }else - flag=( md->sc.data[cond2].timer!=-1 ); - if( flag^( cond1==MSC_FRIENDSTATUSOFF ) ) - (*fr)=md; - - return 0; -} -struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2) -{ - struct mob_data *fr=NULL; - - nullpo_retr(0, md); - - map_foreachinrange(mob_getfriendstatus_sub, &md->bl, 8, - BL_MOB,md,cond1,cond2,&fr); - return fr; -} - -/*========================================== - * Skill use judging - *------------------------------------------ - */ -int mobskill_use(struct mob_data *md, unsigned int tick, int event) -{ - struct mob_skill *ms; - struct block_list *fbl = NULL; //Friend bl, which can either be a BL_PC or BL_MOB depending on the situation. [Skotlex] - struct mob_data *fmd = NULL; - int i,n; - - nullpo_retr (0, md); - nullpo_retr (0, ms = md->db->skill); - - if (!battle_config.mob_skill_rate || md->ud.skilltimer != -1 || !md->db->maxskill) - return 0; - - if (event < 0 && DIFF_TICK(md->ud.canact_tick, tick) > 0) - return 0; //Skill act delay only affects non-event skills. - - //Pick a random starting position and loop from that. - i = rand()%md->db->maxskill; - for (n = 0; n < md->db->maxskill; i++, n++) { - int c2, flag = 0; - - if (i == md->db->maxskill) - i = 0; - - if (DIFF_TICK(tick, md->skilldelay[i]) < ms[i].delay) - continue; - - c2 = ms[i].cond2; - - if (ms[i].state != md->state.skillstate && md->state.skillstate != MSS_DEAD) { - if (ms[i].state == MSS_ANY || (ms[i].state == MSS_ANYTARGET && md->target_id)) - ; //ANYTARGET works with any state as long as there's a target. [Skotlex] - else - continue; - } - if (rand() % 10000 > ms[i].permillage) //Lupus (max value = 10000) - continue; - - // 条件判定 - flag = (event == ms[i].cond1); - //Avoid entering on defined events to avoid "hyper-active skill use" due to the overflow of calls to this function - //in battle. The only exception is MSC_SKILLUSED which explicitly uses the event value to trigger. [Skotlex] - if (!flag && (event == -1 || event == MSC_SKILLUSED)){ - switch (ms[i].cond1) - { - case MSC_ALWAYS: - flag = 1; break; - case MSC_MYHPLTMAXRATE: // HP< maxhp% - flag = 100*md->hp/status_get_max_hp(&md->bl); - flag = (flag <= c2); - break; - case MSC_MYHPINRATE: - flag = 100*md->hp/status_get_max_hp(&md->bl); - flag = (flag >= c2 && flag <= ms[i].val[0]); - break; - case MSC_MYSTATUSON: // status[num] on - case MSC_MYSTATUSOFF: // status[num] off - if (!md->sc.count) { - flag = 0; - } else if (ms[i].cond2 == -1) { - int j; - for (j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++) - if ((flag = (md->sc.data[j].timer != -1)) != 0) - break; - } else { - flag = (md->sc.data[ms[i].cond2].timer != -1); - } - flag ^= (ms[i].cond1 == MSC_MYSTATUSOFF); break; - case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp% - flag = ((fbl = mob_getfriendhprate(md, 0, ms[i].cond2)) != NULL); break; - case MSC_FRIENDHPINRATE : - flag = ((fbl = mob_getfriendhprate(md, ms[i].cond2, ms[i].val[0])) != NULL); break; - case MSC_FRIENDSTATUSON: // friend status[num] on - case MSC_FRIENDSTATUSOFF: // friend status[num] off - flag = ((fmd = mob_getfriendstatus(md, ms[i].cond1, ms[i].cond2)) != NULL); break; - case MSC_SLAVELT: // slave < num - flag = (mob_countslave(&md->bl) < c2 ); break; - case MSC_ATTACKPCGT: // attack pc > num - flag = (unit_counttargeted(&md->bl, 0) > c2); break; - case MSC_SLAVELE: // slave <= num - flag = (mob_countslave(&md->bl) <= c2 ); break; - case MSC_ATTACKPCGE: // attack pc >= num - flag = (unit_counttargeted(&md->bl, 0) >= c2); break; - case MSC_AFTERSKILL: - flag = (md->ud.skillid == c2); break; - case MSC_SKILLUSED: // specificated skill used - flag = ((event & 0xffff) == MSC_SKILLUSED && ((event >> 16) == c2 || c2 == 0)); break; - case MSC_RUDEATTACKED: - flag = (md->attacked_count >= 3); - if (flag) md->attacked_count = 0; //Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex] - break; - case MSC_MASTERHPLTMAXRATE: - flag = ((fbl = mob_getmasterhpltmaxrate(md, ms[i].cond2)) != NULL); break; - case MSC_MASTERATTACKED: - flag = (md->master_id > 0 && unit_counttargeted(map_id2bl(md->master_id), 0) > 0); break; - case MSC_ALCHEMIST: - flag = (md->state.alchemist); - break; - } - } - - if (!flag) - continue; //Skill requisite failed to be fulfilled. - - //Execute skill - if (skill_get_casttype(ms[i].skill_id) == CAST_GROUND) - { - struct block_list *bl = NULL; - short x = 0, y = 0; - if (ms[i].target <= MST_AROUND) { - switch (ms[i].target) { - case MST_TARGET: - case MST_AROUND5: - case MST_AROUND6: - case MST_AROUND7: - case MST_AROUND8: - bl = map_id2bl(md->target_id); - break; - case MST_MASTER: - bl = &md->bl; - if (md->master_id) - bl = map_id2bl(md->master_id); - if (bl) //Otherwise, fall through. - break; - case MST_FRIEND: - if (fbl) - { - bl = fbl; - break; - } else if (fmd) { - bl= &fmd->bl; - break; - } // else fall through - default: - bl = &md->bl; - break; - } - if (bl != NULL) { - x = bl->x; y=bl->y; - } - } - if (x <= 0 || y <= 0) - continue; - // Look for an area to cast the spell around... - if (ms[i].target >= MST_AROUND1 || ms[i].target >= MST_AROUND5) { - int r = ms[i].target >= MST_AROUND1? - (ms[i].target-MST_AROUND1) +1: - (ms[i].target-MST_AROUND5) +1; - map_search_freecell(&md->bl, md->bl.m, &x, &y, r, r, 3); - } - md->skillidx = i; - flag = unit_skilluse_pos2(&md->bl, x, y, ms[i].skill_id, ms[i].skill_lv, - skill_castfix_sc(&md->bl, ms[i].casttime), ms[i].cancel); - if (!flag) md->skillidx = -1; //Skill failed. - return flag; - } else { - if (ms[i].target <= MST_MASTER) { - struct block_list *bl; - switch (ms[i].target) { - case MST_TARGET: - bl = map_id2bl(md->target_id); - break; - case MST_MASTER: - bl = &md->bl; - if (md->master_id) - bl = map_id2bl(md->master_id); - if (bl) //Otherwise, fall through. - break; - case MST_FRIEND: - if (fbl) { - bl = fbl; - break; - } else if (fmd) { - bl = &fmd->bl; - break; - } // else fall through - default: - bl = &md->bl; - break; - } - md->skillidx = i; - flag = (bl && unit_skilluse_id2(&md->bl, bl->id, ms[i].skill_id, ms[i].skill_lv, - skill_castfix_sc(&md->bl,ms[i].casttime), ms[i].cancel)); - if (!flag) md->skillidx = -1; - return flag; - } else { - if (battle_config.error_log) - ShowWarning("Wrong mob skill target 'around' for non-ground skill %d (%s). Mob %d - %s\n", - ms[i].skill_id, skill_get_name(ms[i].skill_id), md->class_, md->db->sprite); - continue; - } - } - return 1; - } - - return 0; -} -/*========================================== - * Skill use event processing - *------------------------------------------ - */ -int mobskill_event(struct mob_data *md, struct block_list *src, unsigned int tick, int flag) -{ - int target_id, res = 0; - - target_id = md->target_id; - if (!target_id || battle_config.mob_changetarget_byskill) - md->target_id = src->id; - - if (flag == -1) - res = mobskill_use(md, tick, MSC_CASTTARGETED); - else if ((flag&0xffff) == MSC_SKILLUSED) - res = mobskill_use(md,tick,flag); - else if (flag&BF_SHORT) - res = mobskill_use(md, tick, MSC_CLOSEDATTACKED); - else if (flag&BF_LONG) - res = mobskill_use(md, tick, MSC_LONGRANGEATTACKED); - - if (!res) - //Restore previous target only if skill condition failed to trigger. [Skotlex] - md->target_id = target_id; - //Otherwise check if the target is an enemy, and unlock if needed. - else if (battle_check_target(&md->bl, src, BCT_ENEMY) <= 0) - md->target_id = target_id; - - return res; -} - -// Player cloned mobs. [Valaris] -int mob_is_clone(int class_) -{ - if(class_ < MOB_CLONE_START || class_ > MOB_CLONE_END) - return 0; - if (mob_db(class_) == mob_dummy) - return 0; - return class_; -} - -//Flag values: -//&1: Set special ai (fight mobs, not players) -//If mode is not passed, a default aggressive mode is used. -//If master_id is passed, clone is attached to him. -//Returns: ID of newly crafted copy. -int mob_clone_spawn(struct map_session_data *sd, char *map, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration) -{ - int class_; - int i,j,inf,skill_id; - struct mob_skill *ms; - - nullpo_retr(0, sd); - - for(class_=MOB_CLONE_START; class_MOB_CLONE_END) - return 0; - - mob_db_data[class_]=(struct mob_db*)aCalloc(1, sizeof(struct mob_db)); - sprintf(mob_db_data[class_]->sprite,sd->status.name); - sprintf(mob_db_data[class_]->name,sd->status.name); - sprintf(mob_db_data[class_]->jname,sd->status.name); - mob_db_data[class_]->lv=status_get_lv(&sd->bl); - mob_db_data[class_]->max_hp=status_get_max_hp(&sd->bl); - mob_db_data[class_]->max_sp=0; - mob_db_data[class_]->base_exp=1; - mob_db_data[class_]->job_exp=1; - mob_db_data[class_]->range=status_get_range(&sd->bl); - mob_db_data[class_]->atk1=status_get_batk(&sd->bl); //Base attack as minimum damage. - mob_db_data[class_]->atk2=mob_db_data[class_]->atk1 + status_get_atk(&sd->bl)+status_get_atk2(&sd->bl); //batk + weapon dmg - mob_db_data[class_]->def=status_get_def(&sd->bl); - mob_db_data[class_]->mdef=status_get_mdef(&sd->bl); - mob_db_data[class_]->str=status_get_str(&sd->bl); - mob_db_data[class_]->agi=status_get_agi(&sd->bl); - mob_db_data[class_]->vit=status_get_vit(&sd->bl); - mob_db_data[class_]->int_=status_get_int(&sd->bl); - mob_db_data[class_]->dex=status_get_dex(&sd->bl); - mob_db_data[class_]->luk=status_get_luk(&sd->bl); - mob_db_data[class_]->range2=AREA_SIZE*2/3; //Chase area of 2/3rds of a screen. - mob_db_data[class_]->range3=AREA_SIZE; //Let them have the same view-range as players. - mob_db_data[class_]->size=status_get_size(&sd->bl); - mob_db_data[class_]->race=status_get_race(&sd->bl); - mob_db_data[class_]->element=status_get_element(&sd->bl); - mob_db_data[class_]->mode=mode?mode:(MD_AGGRESSIVE|MD_ASSIST|MD_CASTSENSOR|MD_CANATTACK|MD_CANMOVE); - mob_db_data[class_]->speed=status_get_speed(&sd->bl); - mob_db_data[class_]->adelay=status_get_adelay(&sd->bl); - mob_db_data[class_]->amotion=status_get_amotion(&sd->bl); - mob_db_data[class_]->dmotion=status_get_dmotion(&sd->bl); - memcpy(&mob_db_data[class_]->vd, &sd->vd, sizeof(struct view_data)); - mob_db_data[class_]->option=sd->sc.option; - - //Skill copy [Skotlex] - ms = &mob_db_data[class_]->skill[0]; - //Go Backwards to give better priority to advanced skills. - for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) { - skill_id = skill_tree[sd->status.class_][j].id; - if (!skill_id || sd->status.skill[skill_id].lv < 1 || (skill_get_inf2(skill_id)&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL))) - continue; - memset (&ms[i], 0, sizeof(struct mob_skill)); - ms[i].skill_id = skill_id; - ms[i].skill_lv = sd->status.skill[skill_id].lv; - ms[i].state = MSS_ANY; - ms[i].permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5% - ms[i].emotion = -1; - ms[i].cancel = 0; - ms[i].delay = 5000+skill_delayfix(&sd->bl,skill_id, ms[i].skill_lv); - ms[i].casttime = skill_castfix(&sd->bl,skill_id, ms[i].skill_lv); - - inf = skill_get_inf(skill_id); - if (inf&INF_ATTACK_SKILL) { - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - if (skill_get_range(skill_id, ms[i].skill_lv) > 3) - ms[i].state = MSS_ANYTARGET; - else - ms[i].state = MSS_BERSERK; - } else if(inf&INF_GROUND_SKILL) { - //Normal aggressive mob, disable skills that cannot help them fight - //against players (those with flags UF_NOMOB and UF_NOPC are specific - //to always aid players!) [Skotlex] - if (!(flag&1) && skill_get_unit_flag(skill_id)&(UF_NOMOB|UF_NOPC)) - continue; - if (skill_get_inf2(skill_id)&INF2_TRAP) { //Traps! - ms[i].state = MSS_IDLE; - ms[i].target = MST_AROUND2; - ms[i].delay = 60000; - } else if (skill_get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy - ms[i].state = MSS_ANYTARGET; - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - } else { //Target allies - ms[i].target = MST_FRIEND; - ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; - ms[i].cond2 = 95; - } - } else if (inf&INF_SELF_SKILL) { - if (skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF) { //auto-select target skill. - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - if (skill_get_range(skill_id, ms[i].skill_lv) > 3) { - ms[i].state = MSS_ANYTARGET; - } else { - ms[i].state = MSS_BERSERK; - } - } else { //Self skill - ms[i].target = MST_SELF; - ms[i].cond1 = MSC_MYHPLTMAXRATE; - ms[i].cond2 = 90; - ms[i].permillage = 2000; - //Delay: Remove the stock 5 secs and add half of the support time. - ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2; - if (ms[i].delay < 5000) - ms[i].delay = 5000; //With a minimum of 5 secs. - } - } else if (inf&INF_SUPPORT_SKILL) { - ms[i].target = MST_FRIEND; - ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; - ms[i].cond2 = 90; - if (skill_id == AL_HEAL) - ms[i].permillage = 5000; //Higher skill rate usage for heal. - else if (skill_id == ALL_RESURRECTION) - ms[i].cond2 = 1; - //Delay: Remove the stock 5 secs and add half of the support time. - ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2; - if (ms[i].delay < 2000) - ms[i].delay = 2000; //With a minimum of 2 secs. - - if (i+1 < MAX_MOBSKILL) { //duplicate this so it also triggers on self. - memcpy(&ms[i+1], &ms[i], sizeof(struct mob_skill)); - mob_db_data[class_]->maxskill = ++i; - ms[i].target = MST_SELF; - ms[i].cond1 = MSC_MYHPLTMAXRATE; - } - } else { - switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered. - case MO_TRIPLEATTACK: - case TF_DOUBLE: - ms[i].state = MSS_BERSERK; - ms[i].target = MST_TARGET; - ms[i].cond1 = MSC_ALWAYS; - ms[i].permillage = skill_id==TF_DOUBLE?(ms[i].skill_lv*500):(3000-ms[i].skill_lv*100); - ms[i].delay -= 5000; //Remove the added delay as these could trigger on "all hits". - break; - default: //Untreated Skill - continue; - } - } - if (battle_config.mob_skill_rate!= 100) - ms[i].permillage = ms[i].permillage*battle_config.mob_skill_rate/100; - if (battle_config.mob_skill_delay != 100) - ms[i].delay = ms[i].delay*battle_config.mob_skill_delay/100; - - mob_db_data[class_]->maxskill = ++i; - } - //Finally, spawn it. - i = mob_once_spawn(sd,(char*)map,x,y,"--en--",class_,1,event); - if ((master_id || flag || duration) && i) { //Further manipulate crafted char. - struct mob_data* md = (struct mob_data*)map_id2bl(i); - if (md && md->bl.type == BL_MOB) { - if (flag&1) //Friendly Character - md->special_state.ai = 1; - if (master_id) //Attach to Master - md->master_id = master_id; - if (duration) //Auto Delete after a while. - md->deletetimer = add_timer (gettick() + duration, mob_timer_delete, i, 0); - } -#if 0 - //I am playing with this for packet-research purposes, enable it if you want, but don't remove it :X [Skotlex] - //Guardian data - if (sd->status.guild_id) { - struct guild* g = guild_search(sd->status.guild_id); - md->guardian_data = aCalloc(1, sizeof(struct guardian_data)); - md->guardian_data->castle = NULL; - md->guardian_data->number = MAX_GUARDIANS; - md->guardian_data->guild_id = sd->status.guild_id; - if (g) - { - md->guardian_data->emblem_id = g->emblem_id; - memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH); - } - } -#endif - } - - return i; -} - -int mob_clone_delete(int class_) -{ - if (class_ >= MOB_CLONE_START && class_ < MOB_CLONE_END - && mob_db_data[class_]!=NULL) { - aFree(mob_db_data[class_]); - mob_db_data[class_]=NULL; - return 1; - } - return 0; -} - -// -// 初期化 -// -/*========================================== - * Since un-setting [ mob ] up was used, it is an initial provisional value setup. - *------------------------------------------ - */ -static int mob_makedummymobdb(int class_) -{ - if (mob_dummy != NULL) - { - if (mob_db(class_) == mob_dummy) - return 1; //Using the mob_dummy data already. [Skotlex] - if (class_ > 0 && class_ <= MAX_MOB_DB) - { //Remove the mob data so that it uses the dummy data instead. - aFree(mob_db_data[class_]); - mob_db_data[class_] = NULL; - } - return 0; - } - //Initialize dummy data. - mob_dummy = (struct mob_db*)aCalloc(1, sizeof(struct mob_db)); //Initializing the dummy mob. - sprintf(mob_dummy->sprite,"DUMMY"); - sprintf(mob_dummy->name,"Dummy"); - sprintf(mob_dummy->jname,"Dummy"); - mob_dummy->lv=1; - mob_dummy->max_hp=1000; - mob_dummy->max_sp=1; - mob_dummy->base_exp=2; - mob_dummy->job_exp=1; - mob_dummy->range=1; - mob_dummy->atk1=7; - mob_dummy->atk2=10; - mob_dummy->def=0; - mob_dummy->mdef=0; - mob_dummy->str=1; - mob_dummy->agi=1; - mob_dummy->vit=1; - mob_dummy->int_=1; - mob_dummy->dex=6; - mob_dummy->luk=2; - mob_dummy->range2=10; - mob_dummy->range3=10; - mob_dummy->race=0; - mob_dummy->element=0; - mob_dummy->mode=0; - mob_dummy->speed=300; - mob_dummy->adelay=1000; - mob_dummy->amotion=500; - mob_dummy->dmotion=500; - return 0; -} - -//Adjusts the drop rate of item according to the criteria given. [Skotlex] -static unsigned int mob_drop_adjust(unsigned int rate, int rate_adjust, unsigned short rate_min, unsigned short rate_max) -{ - if (battle_config.logarithmic_drops && rate_adjust > 0) //Logarithmic drops equation by Ishizu-Chan - //Equation: Droprate(x,y) = x * (5 - log(x)) ^ (ln(y) / ln(5)) - //x is the normal Droprate, y is the Modificator. - rate = (int)(rate * pow((5.0 - log10(rate)), (log(rate_adjust/100.) / log(5.0))) + 0.5); - else //Classical linear rate adjustment. - rate = rate*rate_adjust/100; - return (rate>rate_max)?rate_max:((rate0) - continue; - return -1; - } - while(fgets(line,1020,fp)){ - double exp, maxhp; - char *str[38+2*MAX_MOB_DROP], *p, *np; - - if(line[0] == '/' && line[1] == '/') - continue; - - for(i=0,p=line;i<38+2*MAX_MOB_DROP;i++){ - if((np=strchr(p,','))!=NULL){ - str[i]=p; - *np=0; - p=np+1; - } else - str[i]=p; - } - - class_ = atoi(str[0]); - if (class_ == 0) - continue; //Leave blank lines alone... [Skotlex] - - if (class_ <= 1000 || class_ > MAX_MOB_DB) - { - ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB); - continue; - } else if (pcdb_checkid(class_)) - { - ShowWarning("Mob with ID: %d not loaded. That ID is reserved for player classes.\n"); - continue; - } - if (mob_db_data[class_] == NULL) - mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data)); - - mob_db_data[class_]->vd.class_ = class_; - memcpy(mob_db_data[class_]->sprite, str[1], NAME_LENGTH-1); - memcpy(mob_db_data[class_]->jname, str[2], NAME_LENGTH-1); - memcpy(mob_db_data[class_]->name, str[3], NAME_LENGTH-1); - mob_db_data[class_]->lv = atoi(str[4]); - mob_db_data[class_]->max_hp = atoi(str[5]); - mob_db_data[class_]->max_sp = atoi(str[6]); - - exp = (double)atoi(str[7]) * (double)battle_config.base_exp_rate / 100.; - if (exp < 0) - mob_db_data[class_]->base_exp = 0; - if (exp > UINT_MAX) - mob_db_data[class_]->base_exp = UINT_MAX; - else - mob_db_data[class_]->base_exp = (unsigned int)exp; - - exp = (double)atoi(str[8]) * (double)battle_config.job_exp_rate / 100.; - if (exp < 0) - mob_db_data[class_]->job_exp = 0; - else if (exp > UINT_MAX) - mob_db_data[class_]->job_exp = UINT_MAX; - else - mob_db_data[class_]->job_exp = (unsigned int)exp; - - mob_db_data[class_]->range=atoi(str[9]); - mob_db_data[class_]->atk1=atoi(str[10]); - mob_db_data[class_]->atk2=atoi(str[11]); - mob_db_data[class_]->def=atoi(str[12]); - mob_db_data[class_]->mdef=atoi(str[13]); - mob_db_data[class_]->str=atoi(str[14]); - mob_db_data[class_]->agi=atoi(str[15]); - mob_db_data[class_]->vit=atoi(str[16]); - mob_db_data[class_]->int_=atoi(str[17]); - mob_db_data[class_]->dex=atoi(str[18]); - mob_db_data[class_]->luk=atoi(str[19]); - mob_db_data[class_]->range2=atoi(str[20]); - mob_db_data[class_]->range3=atoi(str[21]); - if (battle_config.view_range_rate!=100) - { - mob_db_data[class_]->range2= - mob_db_data[class_]->range2 - *battle_config.view_range_rate/100; - if (mob_db_data[class_]->range2<1) - mob_db_data[class_]->range2=1; - } - if (battle_config.chase_range_rate!=100) - { - mob_db_data[class_]->range3= - mob_db_data[class_]->range3 - *battle_config.chase_range_rate/100; - if (mob_db_data[class_]->range3range2) - mob_db_data[class_]->range3=mob_db_data[class_]->range2; - } - mob_db_data[class_]->size=atoi(str[22]); - mob_db_data[class_]->race=atoi(str[23]); - mob_db_data[class_]->element=atoi(str[24]); - mob_db_data[class_]->mode=atoi(str[25]); - mob_db_data[class_]->speed=atoi(str[26]); - mob_db_data[class_]->adelay=atoi(str[27]); - mob_db_data[class_]->amotion=atoi(str[28]); - mob_db_data[class_]->dmotion=atoi(str[29]); - - // MVP EXP Bonus, Chance: MEXP,ExpPer - mob_db_data[class_]->mexp=atoi(str[30])*battle_config.mvp_exp_rate/100; - mob_db_data[class_]->mexpper=atoi(str[31]); - //Now that we know if it is an mvp or not, - //apply battle_config modifiers [Skotlex] - maxhp = (double)mob_db_data[class_]->max_hp; - if (mob_db_data[class_]->mexp > 0) - { //Mvp - if (battle_config.mvp_hp_rate != 100) - maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.; - } else if (battle_config.monster_hp_rate != 100) //Normal mob - maxhp = maxhp * (double)battle_config.monster_hp_rate /100.; - if (maxhp < 1) maxhp = 1; - else if (maxhp > INT_MAX) maxhp = INT_MAX; - mob_db_data[class_]->max_hp = (int)maxhp; - - // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per - for(i=0;i<3;i++){ - struct item_data *id; - mob_db_data[class_]->mvpitem[i].nameid=atoi(str[32+i*2]); - if (!mob_db_data[class_]->mvpitem[i].nameid) { - //No item.... - mob_db_data[class_]->mvpitem[i].p = 0; - continue; - } - mob_db_data[class_]->mvpitem[i].p= mob_drop_adjust(atoi(str[33+i*2]), battle_config.item_rate_mvp, - battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max); - - //calculate and store Max available drop chance of the MVP item - if (mob_db_data[class_]->mvpitem[i].p) { - id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid); - if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) { - //item has bigger drop chance or sold in shops - id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate - } - } - } - - for(i=0;idropitem[i].nameid=atoi(str[k]); - if (!mob_db_data[class_]->dropitem[i].nameid) { - //No drop. - mob_db_data[class_]->dropitem[i].p = 0; - continue; - } - type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid); - rate = atoi(str[k+1]); - if (class_ >= 1324 && class_ <= 1363) - { //Treasure box drop rates [Skotlex] - rate_adjust = battle_config.item_rate_treasure; - ratemin = battle_config.item_drop_treasure_min; - ratemax = battle_config.item_drop_treasure_max; - } - else switch (type) // Added suport to restrict normal drops of MVP's [Reddozen] - { - case 0: - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_heal_boss; - else { - rate_adjust = battle_config.item_rate_heal; - } - ratemin = battle_config.item_drop_heal_min; - ratemax = battle_config.item_drop_heal_max; - break; - case 2: - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_use_boss; - else { - rate_adjust = battle_config.item_rate_use; - } - ratemin = battle_config.item_drop_use_min; - ratemax = battle_config.item_drop_use_max; - break; - case 4: - case 5: - case 8: // Changed to include Pet Equip - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_equip_boss; - else { - rate_adjust = battle_config.item_rate_equip; - } - ratemin = battle_config.item_drop_equip_min; - ratemax = battle_config.item_drop_equip_max; - break; - case 6: - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_card_boss; - else { - rate_adjust = battle_config.item_rate_card; - } - ratemin = battle_config.item_drop_card_min; - ratemax = battle_config.item_drop_card_max; - break; - default: - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_common_boss; - else { - rate_adjust = battle_config.item_rate_common; - } - ratemin = battle_config.item_drop_common_min; - ratemax = battle_config.item_drop_common_max; - break; - } - mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax); - - //calculate and store Max available drop chance of the item - if (mob_db_data[class_]->dropitem[i].p && - (class_ < 1324 || class_ > 1363) //Skip treasure chests. - ) { - id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid); - if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) { - //item has bigger drop chance or sold in shops - id->maxchance = mob_db_data[class_]->dropitem[i].p; - } - for (k = 0; k< MAX_SEARCH; k++) { - if (id->mob[k].chance < mob_db_data[class_]->dropitem[i].p && id->mob[k].id != class_) - break; - } - if (k == MAX_SEARCH) - continue; - - memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0])); - id->mob[k].chance = mob_db_data[class_]->dropitem[i].p; - id->mob[k].id = class_; - } - } - - if (mob_db_data[class_]->max_hp <= 0) { - ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->sprite); - mob_makedummymobdb(class_); - } - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[fi]); - } - return 0; -} - -/*========================================== - * MOB display graphic change data reading - *------------------------------------------ - */ -static int mob_readdb_mobavail(void) -{ - FILE *fp; - char line[1024]; - int ln=0; - int class_,j,k; - char *str[20],*p,*np; - - sprintf(line, "%s/mob_avail.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; - memset(str,0,sizeof(str)); - - for(j=0,p=line;j<12;j++){ - if((np=strchr(p,','))!=NULL){ - str[j]=p; - *np=0; - p=np+1; - } else - str[j]=p; - } - - if(str[0]==NULL) - continue; - - class_=atoi(str[0]); - if (class_ == 0) - continue; //Leave blank lines alone... [Skotlex] - - if(mob_db(class_) == mob_dummy) // 値が異常なら処理しない。 - continue; - - k=atoi(str[1]); - if(k < 0) - continue; - - memset(&mob_db_data[class_]->vd, 0, sizeof(struct view_data)); - mob_db_data[class_]->vd.class_=k; - - //Player sprites - if(pcdb_checkid(k) && j>=12) { - mob_db_data[class_]->vd.sex=atoi(str[2]); - mob_db_data[class_]->vd.hair_style=atoi(str[3]); - mob_db_data[class_]->vd.hair_color=atoi(str[4]); - mob_db_data[class_]->vd.weapon=atoi(str[5]); - mob_db_data[class_]->vd.shield=atoi(str[6]); - mob_db_data[class_]->vd.head_top=atoi(str[7]); - mob_db_data[class_]->vd.head_mid=atoi(str[8]); - mob_db_data[class_]->vd.head_bottom=atoi(str[9]); - mob_db_data[class_]->option=atoi(str[10])&~0x46; - mob_db_data[class_]->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris - } - else if(str[2] && atoi(str[2]) > 0) - mob_db_data[class_]->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris] - - ln++; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"mob_avail.txt"); - return 0; -} - -/*========================================== - * Reading of random monster data - *------------------------------------------ - */ -static int mob_read_randommonster(void) -{ - FILE *fp; - char line[1024]; - char *str[10],*p; - int i,j; - - const char* mobfile[] = { - "mob_branch.txt", - "mob_poring.txt", - "mob_boss.txt" }; - - for(i=0;isummonper[i] = 1002; // 設定し忘れた場合はポリンが出るようにしておく - sprintf(line, "%s/%s", db_path, mobfile[i]); - fp=fopen(line,"r"); - if(fp==NULL){ - ShowError("can't read %s\n",line); - return -1; - } - while(fgets(line,1020,fp)){ - int class_,per; - if(line[0] == '/' && line[1] == '/') - continue; - memset(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 || str[2]==NULL) - continue; - - class_ = atoi(str[0]); - per=atoi(str[2]); - if(mob_db(class_) != mob_dummy) - mob_db_data[class_]->summonper[i]=per; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",mobfile[i]); - } - return 0; -} - -/*========================================== - * mob_skill_db.txt reading - *------------------------------------------ - */ -static int mob_readskilldb(void) -{ - FILE *fp; - char line[1024]; - int i,tmp, count; - - const struct { - char str[32]; - int id; - } cond1[] = { - { "always", MSC_ALWAYS }, - { "myhpltmaxrate", MSC_MYHPLTMAXRATE }, - { "myhpinrate", MSC_MYHPINRATE }, - { "friendhpltmaxrate",MSC_FRIENDHPLTMAXRATE }, - { "friendhpinrate", MSC_FRIENDHPINRATE }, - { "mystatuson", MSC_MYSTATUSON }, - { "mystatusoff", MSC_MYSTATUSOFF }, - { "friendstatuson", MSC_FRIENDSTATUSON }, - { "friendstatusoff", MSC_FRIENDSTATUSOFF }, - { "attackpcgt", MSC_ATTACKPCGT }, - { "attackpcge", MSC_ATTACKPCGE }, - { "slavelt", MSC_SLAVELT }, - { "slavele", MSC_SLAVELE }, - { "closedattacked", MSC_CLOSEDATTACKED }, - { "longrangeattacked",MSC_LONGRANGEATTACKED }, - { "skillused", MSC_SKILLUSED }, - { "afterskill", MSC_AFTERSKILL }, - { "casttargeted", MSC_CASTTARGETED }, - { "rudeattacked", MSC_RUDEATTACKED }, - { "masterhpltmaxrate",MSC_MASTERHPLTMAXRATE }, - { "masterattacked", MSC_MASTERATTACKED }, - { "alchemist", MSC_ALCHEMIST }, - { "onspawn", MSC_SPAWN}, - }, cond2[] ={ - { "anybad", -1 }, - { "stone", SC_STONE }, - { "freeze", SC_FREEZE }, - { "stan", SC_STUN }, - { "sleep", SC_SLEEP }, - { "poison", SC_POISON }, - { "curse", SC_CURSE }, - { "silence", SC_SILENCE }, - { "confusion", SC_CONFUSION }, - { "blind", SC_BLIND }, - { "hiding", SC_HIDING }, - { "sight", SC_SIGHT }, - }, state[] = { - { "any", MSS_ANY }, //All states except Dead - { "idle", MSS_IDLE }, - { "walk", MSS_WALK }, - { "loot", MSS_LOOT }, - { "dead", MSS_DEAD }, - { "attack", MSS_BERSERK }, //Retaliating attack - { "angry", MSS_ANGRY }, //Preemptive attack (aggressive mobs) - { "chase", MSS_RUSH }, //Chase escaping target - { "follow", MSS_FOLLOW }, //Preemptive chase (aggressive mobs) - { "anytarget",MSS_ANYTARGET }, //Berserk+Angry+Rush+Follow - }, target[] = { - { "target", MST_TARGET }, - { "self", MST_SELF }, - { "friend", MST_FRIEND }, - { "master", MST_MASTER }, - { "around5", MST_AROUND5 }, - { "around6", MST_AROUND6 }, - { "around7", MST_AROUND7 }, - { "around8", MST_AROUND8 }, - { "around1", MST_AROUND1 }, - { "around2", MST_AROUND2 }, - { "around3", MST_AROUND3 }, - { "around4", MST_AROUND4 }, - { "around", MST_AROUND }, - }; - - int x; - char *filename[]={ "mob_skill_db.txt","mob_skill_db2.txt" }; - - if (!battle_config.mob_skill_rate) { - ShowStatus("Mob skill use disabled. Not reading mob skills.\n"); - return 0; - } - for(x=0;x<2;x++){ - int last_mob_id = 0; - count = 0; - sprintf(line, "%s/%s", db_path, filename[x]); - fp=fopen(line,"r"); - if(fp==NULL){ - if(x==0) - ShowError("can't read %s\n",line); - continue; - } - while(fgets(line,1020,fp)){ - char *sp[20],*p; - int mob_id; - struct mob_skill *ms, gms; - int j=0; - - count++; - if(line[0] == '/' && line[1] == '/') - continue; - - memset(sp,0,sizeof(sp)); - for(i=0,p=line;i<18 && p;i++){ - sp[i]=p; - if((p=strchr(p,','))!=NULL) - *p++=0; - } - if(i == 0 || (mob_id=atoi(sp[0]))== 0) - continue; - if(i < 18) { - ShowError("mob_skill: Insufficient number of fields for skill at %s, line %d\n", filename[x], count); - continue; - } - if (mob_id > 0 && mob_db(mob_id) == mob_dummy) - { - if (mob_id != last_mob_id) { - ShowWarning("mob_skill: Non existant Mob id %d at %s, line %d\n", mob_id, filename[x], count); - last_mob_id = mob_id; - } - continue; - } - if( strcmp(sp[1],"clear")==0 ){ - if (mob_id < 0) - continue; - memset(mob_db_data[mob_id]->skill,0,sizeof(struct mob_skill)); - mob_db_data[mob_id]->maxskill=0; - continue; - } - - if (mob_id < 0) - { //Prepare global skill. [Skotlex] - memset(&gms, 0, sizeof (struct mob_skill)); - ms = &gms; - } else { - for(i=0;iskill[i])->skill_id == 0) - break; - if(i==MAX_MOBSKILL){ - if (mob_id != last_mob_id) { - ShowWarning("mob_skill: readdb: too many skill! Line %d in %d[%s]\n", - count,mob_id,mob_db_data[mob_id]->sprite); - last_mob_id = mob_id; - } - continue; - } - } - - ms->state=atoi(sp[2]); - tmp = sizeof(state)/sizeof(state[0]); - for(j=0;jstate=state[j].id; - else - ShowError("mob_skill: Unrecognized state %s at %s, line %d\n", sp[2], filename[x], count); - - //Skill ID - j=atoi(sp[3]); - if (j<=0 || j>MAX_SKILL_DB) //fixed Lupus - { - if (mob_id < 0) - ShowWarning("Invalid Skill ID (%d) for all mobs\n", j); - else - ShowWarning("Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob_db_data[mob_id]->sprite); - continue; - } - ms->skill_id=j; - //Skill lvl - j= atoi(sp[4])<=0 ? 1 : atoi(sp[4]); - ms->skill_lv= j>battle_config.mob_max_skilllvl ? battle_config.mob_max_skilllvl : j; //we strip max skill level - - //Apply battle_config modifiers to rate (permillage) and delay [Skotlex] - tmp = atoi(sp[5]); - if (battle_config.mob_skill_rate != 100) - tmp = tmp*battle_config.mob_skill_rate/100; - if (tmp > 10000) - ms->permillage= 10000; - else - ms->permillage= tmp; - ms->casttime=atoi(sp[6]); - ms->delay=atoi(sp[7]); - if (battle_config.mob_skill_delay != 100) - ms->delay = ms->delay*battle_config.mob_skill_delay/100; - if (ms->delay < 0) //time overflow? - ms->delay = INT_MAX; - ms->cancel=atoi(sp[8]); - if( strcmp(sp[8],"yes")==0 ) ms->cancel=1; - ms->target=atoi(sp[9]); - for(j=0;jtarget=target[j].id; - } - ms->cond1=-1; - tmp = sizeof(cond1)/sizeof(cond1[0]); - for(j=0;jcond1=cond1[j].id; - else - ShowError("mob_skill: Unrecognized condition 1 %s at %s, line %d\n", sp[10], filename[x], count); - - ms->cond2=atoi(sp[11]); - tmp = sizeof(cond2)/sizeof(cond2[0]); - for(j=0;jcond2=cond2[j].id; - - ms->val[0]=atoi(sp[12]); - ms->val[1]=atoi(sp[13]); - ms->val[2]=atoi(sp[14]); - ms->val[3]=atoi(sp[15]); - ms->val[4]=atoi(sp[16]); - if(sp[17] != NULL && strlen(sp[17])>2) - ms->emotion=atoi(sp[17]); - else - ms->emotion=-1; - if (mob_id < 0) - { //Set this skill to ALL mobs. [Skotlex] - mob_id *= -1; - for (i = 1; i < MAX_MOB_DB; i++) - { - if (mob_db_data[i] == NULL) - continue; - if (mob_db_data[i]->mode&MD_BOSS) - { - if (!(mob_id&2)) //Skill not for bosses - continue; - } else - if (!(mob_id&1)) //Skill not for normal enemies. - continue; - - for(j=0;jskill[j].skill_id == 0) - break; - if(j==MAX_MOBSKILL) - continue; - - memcpy (&mob_db_data[i]->skill[j], ms, sizeof(struct mob_skill)); - mob_db_data[i]->maxskill=j+1; - } - } else //Skill set on a single mob. - mob_db_data[mob_id]->maxskill=i+1; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[x]); - } - return 0; -} -/*========================================== - * mob_race_db.txt reading - *------------------------------------------ - */ -static int mob_readdb_race(void) -{ - FILE *fp; - char line[1024]; - int race,j,k; - char *str[20],*p,*np; - - sprintf(line, "%s/mob_race2_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; - memset(str,0,sizeof(str)); - - for(j=0,p=line;j<12;j++){ - if((np=strchr(p,','))!=NULL){ - str[j]=p; - *np=0; - p=np+1; - } else - str[j]=p; - } - if(str[0]==NULL) - continue; - - race=atoi(str[0]); - if (race < 0 || race >= MAX_MOB_RACE_DB) - continue; - - for (j=1; j<20; j++) { - if (!str[j]) - break; - k=atoi(str[j]); - if (mob_db(k) == mob_dummy) - continue; - mob_db_data[k]->race2 = race; - } - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","mob_race2_db.txt"); - return 0; -} - -#ifndef TXT_ONLY -/*========================================== - * SQL reading - *------------------------------------------ - */ -static int mob_read_sqldb(void) -{ - const char unknown_str[NAME_LENGTH] ="unknown"; - int i, fi, class_, k; - double exp, maxhp; - long unsigned int ln = 0; - char *mob_db_name[] = { mob_db_db, mob_db2_db }; - - //For easier handling of converting. [Skotlex] -#define TO_INT(a) (sql_row[a]==NULL?0:atoi(sql_row[a])) -#define TO_STR(a) (sql_row[a]==NULL?unknown_str:sql_row[a]) - - for (fi = 0; fi < 2; fi++) { - sprintf (tmp_sql, "SELECT * FROM `%s`", mob_db_name[fi]); - if (mysql_query(&mmysql_handle, tmp_sql)) { - ShowSQL("DB error (%s) - %s\n", mob_db_name[fi], mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - continue; - } - sql_res = mysql_store_result(&mmysql_handle); - if (sql_res) { - while((sql_row = mysql_fetch_row(sql_res))){ - class_ = TO_INT(0); - if (class_ <= 1000 || class_ > MAX_MOB_DB) - { - ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB); - continue; - } else if (pcdb_checkid(class_)) - { - ShowWarning("Mob with ID: %d not loaded. That ID is reserved for Upper Classes.\n"); - continue; - } - if (mob_db_data[class_] == NULL) - mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data)); - - ln++; - - mob_db_data[class_]->vd.class_ = class_; - memcpy(mob_db_data[class_]->sprite, TO_STR(1), NAME_LENGTH-1); - memcpy(mob_db_data[class_]->jname, TO_STR(2), NAME_LENGTH-1); - memcpy(mob_db_data[class_]->name, TO_STR(3), NAME_LENGTH-1); - mob_db_data[class_]->lv = TO_INT(4); - mob_db_data[class_]->max_hp = TO_INT(5); - mob_db_data[class_]->max_sp = TO_INT(6); - - exp = (double)TO_INT(7) * (double)battle_config.base_exp_rate / 100.; - if (exp < 0) - mob_db_data[class_]->base_exp = 0; - else if (exp > UINT_MAX) - mob_db_data[class_]->base_exp = UINT_MAX; - else - mob_db_data[class_]->base_exp = (unsigned int)exp; - - exp = (double)TO_INT(8) * (double)battle_config.job_exp_rate / 100.; - if (exp < 0) - mob_db_data[class_]->job_exp = 0; - else if (exp > UINT_MAX) - mob_db_data[class_]->job_exp = UINT_MAX; - else - mob_db_data[class_]->job_exp = (unsigned int)exp; - - mob_db_data[class_]->range = TO_INT(9); - mob_db_data[class_]->atk1 = TO_INT(10); - mob_db_data[class_]->atk2 = TO_INT(11); - mob_db_data[class_]->def = TO_INT(12); - mob_db_data[class_]->mdef = TO_INT(13); - mob_db_data[class_]->str = TO_INT(14); - mob_db_data[class_]->agi = TO_INT(15); - mob_db_data[class_]->vit = TO_INT(16); - mob_db_data[class_]->int_ = TO_INT(17); - mob_db_data[class_]->dex = TO_INT(18); - mob_db_data[class_]->luk = TO_INT(19); - mob_db_data[class_]->range2 = TO_INT(20); - mob_db_data[class_]->range3 = TO_INT(21); - mob_db_data[class_]->size = TO_INT(22); - mob_db_data[class_]->race = TO_INT(23); - mob_db_data[class_]->element = TO_INT(24); - mob_db_data[class_]->mode = TO_INT(25); - mob_db_data[class_]->speed = TO_INT(26); - mob_db_data[class_]->adelay = TO_INT(27); - mob_db_data[class_]->amotion = TO_INT(28); - mob_db_data[class_]->dmotion = TO_INT(29); - - // MVP EXP Bonus, Chance: MEXP,ExpPer - mob_db_data[class_]->mexp = TO_INT(30) * battle_config.mvp_exp_rate / 100; - mob_db_data[class_]->mexpper = TO_INT(31); - //Now that we know if it is an mvp or not, - //apply battle_config modifiers [Skotlex] - maxhp = (double)mob_db_data[class_]->max_hp; - if (mob_db_data[class_]->mexp > 0) - { //Mvp - if (battle_config.mvp_hp_rate != 100) - maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.; - } else if (battle_config.monster_hp_rate != 100) //Normal mob - maxhp = maxhp * (double)battle_config.monster_hp_rate /100.; - if (maxhp < 0) maxhp = 1; - else if (maxhp > INT_MAX) maxhp = INT_MAX; - mob_db_data[class_]->max_hp = (int)maxhp; - - // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per - for (i=0; i<3; i++) { - struct item_data *id; - mob_db_data[class_]->mvpitem[i].nameid = TO_INT(32+i*2); - if (!mob_db_data[class_]->mvpitem[i].nameid) { - //No item.... - mob_db_data[class_]->mvpitem[i].p = 0; - continue; - } - mob_db_data[class_]->mvpitem[i].p = mob_drop_adjust(TO_INT(33+i*2), - battle_config.item_rate_mvp, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max); - - //calculate and store Max available drop chance of the MVP item - id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid); - if (mob_db_data[class_]->mvpitem[i].p) { - if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) { - //item has bigger drop chance or sold in shops - id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate - } - } - } - - for (i = 0; i < MAX_MOB_DROP; i++){ // 8 -> 10 Lupus - int rate = 0, rate_adjust, type; - unsigned short ratemin, ratemax; - struct item_data *id; - k=38+i*2; - mob_db_data[class_]->dropitem[i].nameid=TO_INT(k); - if (!mob_db_data[class_]->dropitem[i].nameid) { - //No drop. - mob_db_data[class_]->dropitem[i].p = 0; - continue; - } - type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid); - rate = TO_INT(k+1); - if (class_ >= 1324 && class_ <= 1363) - { //Treasure box drop rates [Skotlex] - rate_adjust = battle_config.item_rate_treasure; - ratemin = battle_config.item_drop_treasure_min; - ratemax = battle_config.item_drop_treasure_max; - } - else switch (type) // Added suport to restrict normal drops of MVP's [Reddozen] - { - case 0: // Val added heal restrictions - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_heal_boss; - else { - rate_adjust = battle_config.item_rate_heal; - } - ratemin = battle_config.item_drop_heal_min; - ratemax = battle_config.item_drop_heal_max; - break; - case 2: - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_use_boss; - else { - rate_adjust = battle_config.item_rate_use; - } - ratemin = battle_config.item_drop_use_min; - ratemax = battle_config.item_drop_use_max; - break; - case 4: - case 5: - case 8: // Changed to include Pet Equip - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_equip_boss; - else { - rate_adjust = battle_config.item_rate_equip; - } - ratemin = battle_config.item_drop_equip_min; - ratemax = battle_config.item_drop_equip_max; - break; - case 6: - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_card_boss; - else { - rate_adjust = battle_config.item_rate_card; - } - ratemin = battle_config.item_drop_card_min; - ratemax = battle_config.item_drop_card_max; - break; - default: - if (mob_db_data[class_]->mode&MD_BOSS) - rate_adjust = battle_config.item_rate_common_boss; - else { - rate_adjust = battle_config.item_rate_common; - } - ratemin = battle_config.item_drop_common_min; - ratemax = battle_config.item_drop_common_max; - break; - } - mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax); - - //calculate and store Max available drop chance of the item - if (mob_db_data[class_]->dropitem[i].p) { - id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid); - if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) { - //item has bigger drop chance or sold in shops - id->maxchance = mob_db_data[class_]->dropitem[i].p; - } - for (k = 0; k< MAX_SEARCH; k++) { - if (id->mob[k].chance < mob_db_data[class_]->dropitem[i].p && id->mob[k].id != class_) - break; - } - if (k == MAX_SEARCH) - continue; - - memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0])); - id->mob[k].chance = mob_db_data[class_]->dropitem[i].p; - id->mob[k].id = class_; - } - } - if (mob_db_data[class_]->max_hp <= 0) { - ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->sprite); - mob_makedummymobdb(class_); - } - } - - mysql_free_result(sql_res); - ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, mob_db_name[fi]); - ln = 0; - } - } - return 0; -} -#endif /* not TXT_ONLY */ - -void mob_reload(void) -{ - int i; -#ifndef TXT_ONLY - if(db_use_sqldbs) - mob_read_sqldb(); - else -#endif /* TXT_ONLY */ - mob_readdb(); - - mob_readdb_mobavail(); - mob_read_randommonster(); - - //Mob skills need to be cleared before re-reading them. [Skotlex] - for (i = 0; i < MAX_MOB_DB; i++) - if (mob_db_data[i]) - { - memset(&mob_db_data[i]->skill,0,sizeof(mob_db_data[i]->skill)); - mob_db_data[i]->maxskill=0; - } - mob_readskilldb(); - mob_readdb_race(); -} - -/*========================================== - * Circumference initialization of mob - *------------------------------------------ - */ -int do_init_mob(void) -{ //Initialize the mob database - memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array - mob_db_data[0] = aCalloc(1, sizeof (struct mob_data)); //This mob is used for random spawns - mob_makedummymobdb(0); //The first time this is invoked, it creates the dummy mob - item_drop_ers = ers_new((uint32)sizeof(struct item_drop)); - item_drop_list_ers = ers_new((uint32)sizeof(struct item_drop_list)); - -#ifndef TXT_ONLY - if(db_use_sqldbs) - mob_read_sqldb(); - else -#endif /* TXT_ONLY */ - mob_readdb(); - - mob_readdb_mobavail(); - mob_read_randommonster(); - mob_readskilldb(); - mob_readdb_race(); - - add_timer_func_list(mob_delayspawn,"mob_delayspawn"); - add_timer_func_list(mob_delay_item_drop,"mob_delay_item_drop"); - add_timer_func_list(mob_ai_hard,"mob_ai_hard"); - add_timer_func_list(mob_ai_lazy,"mob_ai_lazy"); - add_timer_func_list(mob_timer_delete,"mob_timer_delete"); - add_timer_func_list(mob_spawn_guardian_sub,"mob_spawn_guardian_sub"); - add_timer_func_list(mob_respawn,"mob_respawn"); - add_timer_interval(gettick()+MIN_MOBTHINKTIME,mob_ai_hard,0,0,MIN_MOBTHINKTIME); - add_timer_interval(gettick()+MIN_MOBTHINKTIME*10,mob_ai_lazy,0,0,MIN_MOBTHINKTIME*10); - - return 0; -} - -/*========================================== - * Clean memory usage. - *------------------------------------------ - */ -int do_final_mob(void) -{ - int i; - if (mob_dummy) - { - aFree(mob_dummy); - mob_dummy = NULL; - } - for (i = 0; i <= MAX_MOB_DB; i++) - { - if (mob_db_data[i] != NULL) - { - aFree(mob_db_data[i]); - mob_db_data[i] = 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 +#include +#include +#include +#include +#include + +#include "../common/timer.h" +#include "../common/db.h" +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" +#include "../common/ers.h" +#include "../common/strlib.h" + +#include "map.h" +#include "clif.h" +#include "intif.h" +#include "pc.h" +#include "status.h" +#include "mob.h" +#include "guild.h" +#include "itemdb.h" +#include "skill.h" +#include "battle.h" +#include "party.h" +#include "npc.h" +#include "log.h" +#include "script.h" +#include "atcommand.h" +#include "date.h" +#include "irc.h" + +#define MIN_MOBTHINKTIME 100 +#define IDLE_SKILL_INTERVAL 10 //Active idle skills should be triggered every 1 second (1000/MIN_MOBTHINKTIME) + +#define MOB_LAZYSKILLPERC 10 // Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute) +#define MOB_LAZYMOVEPERC 50 // Move probability in the negligent mode MOB (rate of 1000 minute) +#define MOB_LAZYWARPPERC 20 // Warp probability in the negligent mode MOB (rate of 1000 minute) + +#define MOB_SLAVEDISTANCE 2 //Distance that slaves should keep from their master. + +#define MAX_MINCHASE 30 //Max minimum chase value to use for mobs. +//Dynamic mob database, allows saving of memory when there's big gaps in the mob_db [Skotlex] +struct mob_db *mob_db_data[MAX_MOB_DB+1]; +struct mob_db *mob_dummy = NULL; //Dummy mob to be returned when a non-existant one is requested. + +struct mob_db *mob_db(int index) { if (index < 0 || index > MAX_MOB_DB || mob_db_data[index] == NULL) return mob_dummy; return mob_db_data[index]; } + +static struct eri *item_drop_ers; //For loot drops delay structures. +static struct eri *item_drop_list_ers; +#define CLASSCHANGE_BOSS_NUM 21 + +/*========================================== + * Local prototype declaration (only required thing) + *------------------------------------------ + */ +static int mob_makedummymobdb(int); +static int mob_spawn_guardian_sub(int,unsigned int,int,int); +int mobskill_use(struct mob_data *md,unsigned int tick,int event); +int mob_skillid2skillidx(int class_,int skillid); + +/*========================================== + * Mob is searched with a name. + *------------------------------------------ + */ +int mobdb_searchname(const char *str) +{ + int i; + struct mob_db* mob; + for(i=0;i<=MAX_MOB_DB;i++){ + mob = mob_db(i); + if(mob == mob_dummy) //Skip dummy mobs. + continue; + if(strcmpi(mob->name,str)==0 || strcmpi(mob->jname,str)==0 || strcmpi(mob->sprite,str)==0) + return i; + } + + return 0; +} +static int mobdb_searchname_array_sub(struct mob_db* mob, const char *str) +{ + if (mob == mob_dummy) + return 1; //Invalid mob. + if(!mob->base_exp && !mob->job_exp) + return 1; //Discount slave-mobs (no exp) as requested by Playtester. [Skotlex] + if(stristr(mob->jname,str)) + return 0; + if(stristr(mob->name,str)) + return 0; + return strcmpi(mob->jname,str); +} + +/*========================================== + * Founds up to N matches. Returns number of matches [Skotlex] + *------------------------------------------ + */ +int mobdb_searchname_array(struct mob_db** data, int size, const char *str) +{ + int count = 0, i; + struct mob_db* mob; + for(i=0;i<=MAX_MOB_DB;i++){ + mob = mob_db(i); + if (mob == mob_dummy) + continue; + if (!mobdb_searchname_array_sub(mob, str)) { + if (count < size) + data[count] = mob; + count++; + } + } + return count; +} + +/*========================================== + * Id Mob is checked. + *------------------------------------------ + */ +int mobdb_checkid(const int id) +{ + if (mob_db(id) == mob_dummy) + return 0; + if (mob_is_clone(id)) //checkid is used mostly for random ID based code, therefore clone mobs are out of the question. + return 0; + return id; +} + +/*========================================== + * Returns the view data associated to this mob class. + *------------------------------------------ + */ +struct view_data * mob_get_viewdata(class_) +{ + if (mob_db(class_) == mob_dummy) + return 0; + return &mob_db(class_)->vd; +} +/*========================================== + * Cleans up mob-spawn data to make it "valid" + *------------------------------------------ + */ +int mob_parse_dataset(struct spawn_data *data) { + int i; + //FIXME: This implementation is not stable, npc scripts will stop working once MAX_MOB_DB changes value! [Skotlex] + if(data->class_ > 2*MAX_MOB_DB){ // large/tiny mobs [Valaris] + data->state.size=2; + data->class_ -= 2*MAX_MOB_DB; + } else if (data->class_ > MAX_MOB_DB) { + data->state.size=1; + data->class_ -= MAX_MOB_DB; + } + + if ((!mobdb_checkid(data->class_) && !mob_is_clone(data->class_)) || !data->num) + return 0; + + //better safe than sorry, current md->npc_event has a size of 50 + if (strlen(data->eventname) >= 50) + return 0; + + if (data->eventname[0] && strlen(data->eventname) <= 2) + { //Portable monster big/small implementation. [Skotlex] + i = atoi(data->eventname); + if (i) { + if (i&2) + data->state.size=1; + else if (i&4) + data->state.size=2; + if (i&8) + data->state.ai=1; + data->eventname[0] = '\0'; //Clear event as it is not used. + } + } + if (!data->level) + data->level = mob_db(data->class_)->lv; + + if(strcmp(data->name,"--en--")==0) + strncpy(data->name,mob_db(data->class_)->name,NAME_LENGTH-1); + else if(strcmp(data->name,"--ja--")==0) + strncpy(data->name,mob_db(data->class_)->jname,NAME_LENGTH-1); + + return 1; +} +/*========================================== + * Generates the basic mob data using the spawn_data provided. + *------------------------------------------ + */ +struct mob_data* mob_spawn_dataset(struct spawn_data *data) +{ + struct mob_data *md = aCalloc(1, sizeof(struct mob_data)); + md->bl.id= npc_get_new_npc_id(); + md->bl.type = BL_MOB; + md->bl.subtype = MONS; + md->bl.m = data->m; + md->bl.x = data->x; + md->bl.y = data->y; + md->class_ = data->class_; + md->db = mob_db(md->class_); + memcpy(md->name, data->name, NAME_LENGTH-1); + if (data->state.ai) + md->special_state.ai = data->state.ai; + if (data->state.size) + md->special_state.size = data->state.size; + if (data->eventname[0] && strlen(data->eventname) >= 4) + memcpy(md->npc_event, data->eventname, 50); + md->level = data->level; + + if(md->db->status.mode&MD_LOOTER) + md->lootitem = (struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); + md->spawn_n = -1; + md->deletetimer = -1; + md->skillidx = -1; + status_set_viewdata(&md->bl, md->class_); + status_change_init(&md->bl); + unit_dataset(&md->bl); + + map_addiddb(&md->bl); + return md; +} + +/*========================================== + * Fetches a random mob_id [Skotlex] + * type: Where to fetch from: + * 0: dead branch list + * 1: poring list + * 2: bloody branch list + * flag: + * &1: Apply the summon success chance found in the list. + * &2: Apply a monster check level. + * lv: Mob level to check against + *------------------------------------------ + */ + +int mob_get_random_id(int type, int flag, int lv) { + struct mob_db *mob; + int i=0, k=0, class_; + if(type < 0 || type >= MAX_RANDOMMONSTER) { + if (battle_config.error_log) + ShowError("mob_get_random_id: Invalid type (%d) of random monster.\n", type); + return 0; + } + do { + class_ = rand() % MAX_MOB_DB; + if (flag&1) + k = rand() % 1000000; + mob = mob_db(class_); + } while ((mob == mob_dummy || mob->summonper[type] <= k || + (flag&2 && lv < mob->lv)) && (i++) < MAX_MOB_DB); + if(i >= MAX_MOB_DB) + class_ = mob_db_data[0]->summonper[type]; + return 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) +{ + struct spawn_data data; + + memset(&data, 0, sizeof(struct spawn_data)); + data.m = m; + data.num = 1; + data.class_ = class_; + strncpy(data.name, mobname, NAME_LENGTH-1); + strncpy(data.eventname, event, 50); + + if (bl && (x < 0 || y < 0))//Locate spot around player. + map_search_freecell(bl, m, &x, &y, 1, 1, 0); + + if (x <= 0 || y <= 0 || map_getcell(m,x,y,CELL_CHKNOREACH)) + map_search_freecell(NULL, m, &x, &y, -1, -1, 1); + + data.x = x; + data.y = y; + + if (!mob_parse_dataset(&data)) + return NULL; + + return mob_spawn_dataset(&data); +} +/*========================================== + * The MOB appearance for one time (for scripts) + *------------------------------------------ + */ +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) +{ + struct mob_data *md = NULL; + int m, count, lv = 255; + + if(sd) lv = sd->status.base_level; + + if(sd && strcmp(mapname,"this")==0) + m = sd->bl.m; + else + m = map_mapname2mapid(mapname); + + if (m < 0 || amount <= 0) // 値が異常なら召喚を止める + return 0; + + for (count = 0; count < amount; count++) { + md = mob_once_spawn_sub(sd?&sd->bl:NULL, m, x, y, mobname, + class_<0? + mob_get_random_id(-class_-1, battle_config.random_monster_checklv?3:1, lv): + class_, event); + + if (!md) continue; + + if(class_ == MOBID_EMPERIUM) { + struct guild_castle *gc = guild_mapname2gc(map[md->bl.m].name); + struct guild *g = gc?guild_search(gc->guild_id):NULL; + if(gc) { + md->guardian_data = aCalloc(1, sizeof(struct guardian_data)); + md->guardian_data->castle = gc; + md->guardian_data->number = MAX_GUARDIANS; + md->guardian_data->guild_id = gc->guild_id; + if (g) + { + md->guardian_data->emblem_id = g->emblem_id; + memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH); + } + else if (gc->guild_id) //Guild not yet available, retry in 5. + add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id); + } + } // end addition [Valaris] + mob_spawn (md); + if (class_ < 0 && battle_config.dead_branch_active) + //Behold Aegis's masterful decisions yet again... + //"I understand the "Aggressive" part, but the "Can Move" and "Can Attack" is just stupid" - Poki#3 + sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE|MD_CANATTACK|MD_CANMOVE, 0, 60000); + } + return (md)?md->bl.id : 0; +} +/*========================================== + * The MOB appearance for one time (& area specification for scripts) + *------------------------------------------ + */ +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 x,y,i,max,lx=-1,ly=-1,id=0; + int m; + + if(strcmp(mapname,"this")==0) + m=sd->bl.m; + else + m=map_mapname2mapid(mapname); + + max=(y1-y0+1)*(x1-x0+1)*3; + if(max>1000)max=1000; + + if (m < 0 || amount <= 0) // 値が異常なら召喚を止める + return 0; + + for(i=0;i=max){ + if(lx>=0){ // Since reference went wrong, the place which boiled before is used. + x=lx; + y=ly; + }else + return 0; // Since reference of the place which boils first went wrong, it stops. + } + if(x==0||y==0) ShowWarning("mob_once_spawn_area: xory=0, x=%d,y=%d,x0=%d,y0=%d\n",x,y,x0,y0); + id=mob_once_spawn(sd,mapname,x,y,mobname,class_,1,event); + lx=x; + ly=y; + } + return id; +} +/*========================================== + * Set a Guardian's guild data [Skotlex] + *------------------------------------------ + */ +static int mob_spawn_guardian_sub(int tid,unsigned int tick,int id,int data) +{ //Needed because the guild_data may not be available at guardian spawn time. + struct block_list* bl = map_id2bl(id); + struct mob_data* md; + struct guild* g; + + if (bl == NULL) //It is possible mob was already removed from map when the castle has no owner. [Skotlex] + return 0; + + if (bl->type != BL_MOB) + { + ShowError("mob_spawn_guardian_sub: Block error!\n"); + return 0; + } + + md = (struct mob_data*)bl; + nullpo_retr(0, md->guardian_data); + g = guild_search(data); + + if (g == NULL) + { //Liberate castle, if the guild is not found this is an error! [Skotlex] + ShowError("mob_spawn_guardian_sub: Couldn't load guild %d!\n",data); + if (md->class_ == MOBID_EMPERIUM) + { //Not sure this is the best way, but otherwise we'd be invoking this for ALL guardians spawned later on. + md->guardian_data->guild_id = 0; + if (md->guardian_data->castle->guild_id) //Free castle up. + { + ShowNotice("Clearing ownership of castle %d (%s)\n", md->guardian_data->castle->castle_id, md->guardian_data->castle->castle_name); + guild_castledatasave(md->guardian_data->castle->castle_id, 1, 0); + } + } else { + if (md->guardian_data->castle->guardian[md->guardian_data->number].visible) + { //Safe removal of guardian. + md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; + guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); + guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); + } + unit_free(&md->bl); //Remove guardian. + } + return 0; + } + md->guardian_data->emblem_id = g->emblem_id; + memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH); + md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP); + return 0; +} + +/*========================================== + * Summoning Guardians [Valaris] + *------------------------------------------ + */ +int mob_spawn_guardian(struct map_session_data *sd,char *mapname, + int x,int y,const char *mobname,int class_,int amount,const char *event,int guardian) +{ + struct mob_data *md=NULL; + struct spawn_data data; + struct guild *g=NULL; + struct guild_castle *gc; + int m, count; + memset(&data, 0, sizeof(struct spawn_data)); + data.num = 1; + + if( sd && strcmp(mapname,"this")==0) + m=sd->bl.m; + else + m=map_mapname2mapid(mapname); + + if(m<0 || amount<=0) + return 0; + data.m = m; + data.num = amount; + if(class_<0) + return 0; + data.class_ = class_; + + if(guardian < 0 || guardian >= MAX_GUARDIANS) + { + ShowError("mob_spawn_guardian: Invalid guardian index %d for guardian %d (castle map %s)\n", guardian, class_, map[m].name); + return 0; + } + if (amount > 1) + ShowWarning("mob_spawn_guardian: Spawning %d guardians in position %d (castle map %s)\n", amount, map[m].name); + + if(sd){ + if(x<=0) x=sd->bl.x; + if(y<=0) y=sd->bl.y; + } + else if(x<=0 || y<=0) + ShowWarning("mob_spawn_guardian: Invalid coordinates (%d,%d)\n",x,y); + data.x = x; + data.y = y; + strncpy(data.name, mobname, NAME_LENGTH-1); + strncpy(data.eventname, event, 50); + if (!mob_parse_dataset(&data)) + return 0; + + gc=guild_mapname2gc(map[m].name); + if (gc == NULL) + { + ShowError("mob_spawn_guardian: No castle set at map %s\n", map[m].name); + return 0; + } + if (!gc->guild_id) + ShowWarning("mob_spawn_guardian: Spawning guardian %d on a castle with no guild (castle map %s)\n", class_, map[m].name); + else + g = guild_search(gc->guild_id); + + if (gc->guardian[guardian].id) + ShowWarning("mob_spawn_guardian: Spawning guardian in position %d which already has a guardian (castle map %s)\n", guardian, map[m].name); + + for(count=0;countguardian_data = aCalloc(1, sizeof(struct guardian_data)); + md->guardian_data->number = guardian; + md->guardian_data->guild_id = gc->guild_id; + md->guardian_data->castle = gc; + gc->guardian[guardian].id = md->bl.id; + if (g) + { + md->guardian_data->emblem_id = g->emblem_id; + memcpy (md->guardian_data->guild_name, g->name, NAME_LENGTH); + md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP); + } else if (md->guardian_data->guild_id) + add_timer(gettick()+5000,mob_spawn_guardian_sub,md->bl.id,md->guardian_data->guild_id); + mob_spawn(md); + } + + return (amount>0)?md->bl.id:0; +} + +/*========================================== + * Reachability to a Specification ID existence place + * state indicates type of 'seek' mob should do: + * - MSS_LOOT: Looking for item, path must be easy. + * - MSS_RUSH: Chasing attacking player, path is determined by mob_ai&1 + * - MSS_FOLLOW: Initiative/support seek, path must be easy. + *------------------------------------------ + */ +int mob_can_reach(struct mob_data *md,struct block_list *bl,int range, int state) +{ + int easy = 0; + + nullpo_retr(0, md); + nullpo_retr(0, bl); + switch (state) { + case MSS_RUSH: + easy = (battle_config.mob_ai&1?0:1); + break; + case MSS_LOOT: + case MSS_FOLLOW: + default: + easy = 1; + break; + } + return unit_can_reach_bl(&md->bl, bl, range, easy, NULL, NULL); +} + +/*========================================== + * Links nearby mobs (supportive mobs) + *------------------------------------------ + */ +int mob_linksearch(struct block_list *bl,va_list ap) +{ + struct mob_data *md; + int class_; + struct block_list *target; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + md=(struct mob_data *)bl; + class_ = va_arg(ap, int); + target = va_arg(ap, struct block_list *); + tick=va_arg(ap, unsigned int); + + if (md->class_ == class_ && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME + && !md->target_id) + { + md->last_linktime = tick; + if( mob_can_reach(md,target,md->db->range2, MSS_FOLLOW) ){ // Reachability judging + md->target_id = target->id; + md->min_chase=md->db->range3; + return 1; + } + } + + return 0; +} + +/*========================================== + * mob spawn with delay (timer function) + *------------------------------------------ + */ +static int mob_delayspawn(int tid, unsigned int tick, int m, int n) +{ + struct block_list *bl = map_id2bl(m); + if (bl && bl->type == BL_MOB) + mob_spawn((TBL_MOB*)bl); + return 0; +} + +/*========================================== + * spawn timing calculation + *------------------------------------------ + */ +int mob_setdelayspawn(struct mob_data *md) +{ + unsigned int spawntime, spawntime1, spawntime2, spawntime3; + + + if (!md->spawn) //Doesn't has respawn data! + return unit_free(&md->bl); + + spawntime1 = md->last_spawntime + md->spawn->delay1; + spawntime2 = md->last_deadtime + md->spawn->delay2; + spawntime3 = gettick() + 5000 + rand()%5000; //Lupus + // spawntime = max(spawntime1,spawntime2,spawntime3); + if (DIFF_TICK(spawntime1, spawntime2) > 0) + spawntime = spawntime1; + else + spawntime = spawntime2; + if (DIFF_TICK(spawntime3, spawntime) > 0) + spawntime = spawntime3; + + add_timer(spawntime, mob_delayspawn, md->bl.id, 0); + return 0; +} + +/*========================================== + * Mob spawning. Initialization is also variously here. + *------------------------------------------ + */ +int mob_spawn (struct mob_data *md) +{ + int i=0; + unsigned int c =0, tick = gettick(); + + md->last_spawntime = tick; + md->last_thinktime = tick -MIN_MOBTHINKTIME; + if (md->bl.prev != NULL) + unit_remove_map(&md->bl,2); + else if (md->vd->class_ != md->class_) { + status_set_viewdata(&md->bl, md->class_); + md->db = mob_db(md->class_); + if (md->spawn) + memcpy(md->name,md->spawn->name,NAME_LENGTH); + else if (battle_config.override_mob_names == 1) + memcpy(md->name,md->db->name,NAME_LENGTH); + else + memcpy(md->name,md->db->jname,NAME_LENGTH); + } + + if (md->spawn) { //Respawn data + md->bl.m = md->spawn->m; + + if ((md->spawn->x == 0 && md->spawn->y == 0) || md->spawn->xs || md->spawn->ys) + { //Monster can be spawned on an area. + short x, y, xs, ys; + if (md->spawn->x == 0 && md->spawn->y == 0) + xs = ys = -1; + else { + x = md->spawn->x; + y = md->spawn->y; + xs = md->spawn->xs/2; + ys = md->spawn->ys/2; + } + if (!map_search_freecell(NULL, md->spawn->m, &x, &y, xs, ys, battle_config.no_spawn_on_player?5:1)) { + // retry again later + add_timer(tick+5000,mob_delayspawn,md->bl.id,0); + return 1; + } + md->bl.x = x; + md->bl.y = y; + } else { + md->bl.x = md->spawn->x; + md->bl.y = md->spawn->y; + } + } + memset(&md->state, 0, sizeof(md->state)); + status_calc_mob(md, 1); + md->attacked_id = 0; + md->attacked_players = 0; + md->attacked_count = 0; + md->target_id = 0; + md->move_fail_count = 0; + + if (!md->level) // [Valaris] + md->level=md->db->lv; + +// md->master_id = 0; + md->master_dist = 0; + + md->state.aggressive = md->status.mode&MD_ANGRY?1:0; + md->state.skillstate = MSS_IDLE; + md->next_walktime = tick+rand()%5000+1000; + md->last_linktime = tick; + + for (i = 0, c = tick-1000*3600*10; i < MAX_MOBSKILL; i++) + md->skilldelay[i] = c; + + memset(md->dmglog, 0, sizeof(md->dmglog)); + md->tdmg = 0; + if (md->lootitem) + memset(md->lootitem, 0, sizeof(md->lootitem)); + md->lootitem_count = 0; + + if(md->db->option) + // Added for carts, falcons and pecos for cloned monsters. [Valaris] + md->sc.option = md->db->option; + + map_addblock(&md->bl); + clif_spawn(&md->bl); + skill_unit_move(&md->bl,tick,1); + mobskill_use(md, tick, MSC_SPAWN); + return 0; +} + +/*========================================== + * Determines if the mob can change target. [Skotlex] + *------------------------------------------ + */ +static int mob_can_changetarget(struct mob_data* md, struct block_list* target, int mode) +{ + // if the monster was provoked ignore the above rule [celest] + if(md->state.provoke_flag) + { + if (md->state.provoke_flag == target->id) + return 1; + else if (!battle_config.mob_ai&4) + return 0; + } + + switch (md->state.skillstate) { + case MSS_BERSERK: //Only Assist, Angry or Aggressive+CastSensor mobs can change target while attacking. + if (mode&(MD_ASSIST|MD_ANGRY) || (mode&(MD_AGGRESSIVE|MD_CASTSENSOR)) == (MD_AGGRESSIVE|MD_CASTSENSOR)) + return (battle_config.mob_ai&4 || check_distance_bl(&md->bl, target, 3)); + else + return 0; + case MSS_RUSH: + return (mode&MD_AGGRESSIVE); + case MSS_FOLLOW: + case MSS_ANGRY: + case MSS_IDLE: + case MSS_WALK: + case MSS_LOOT: + return 1; + default: + return 0; + } +} + +/*========================================== + * Determination for an attack of a monster + *------------------------------------------ + */ +int mob_target(struct mob_data *md,struct block_list *bl,int dist) +{ + nullpo_retr(0, md); + nullpo_retr(0, bl); + + // Nothing will be carried out if there is no mind of changing TAGE by TAGE ending. + if(md->target_id && !mob_can_changetarget(md, bl, status_get_mode(&md->bl))) + return 0; + + if(!status_check_skilluse(&md->bl, bl, 0, 0)) + return 0; + + md->target_id = bl->id; // Since there was no disturbance, it locks on to target. + if (md->state.provoke_flag && bl->id != md->state.provoke_flag) + md->state.provoke_flag = 0; + md->min_chase=dist+md->db->range3; + if(md->min_chase>MAX_MINCHASE) + md->min_chase=MAX_MINCHASE; + return 0; +} + +/*========================================== + * The ?? routine of an active monster + *------------------------------------------ + */ +static int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap) +{ + struct mob_data *md; + struct block_list **target; + int dist; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + md=va_arg(ap,struct mob_data *); + target= va_arg(ap,struct block_list**); + + //If can't seek yet, not an enemy, or you can't attack it, skip. + if ((*target) == bl || !status_check_skilluse(&md->bl, bl, 0, 0)) + return 0; + + if(md->nd){ + setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)2, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)bl->type, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 2, (void *)bl->id, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); + run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); + return 1; // We have script handling the work. + } + + if(battle_check_target(&md->bl,bl,BCT_ENEMY)<=0) + return 0; + + switch (bl->type) + { + case BL_PC: + if (((TBL_PC*)bl)->state.gangsterparadise && + !(status_get_mode(&md->bl)&MD_BOSS)) + return 0; //Gangster paradise protection. + case BL_MOB: + if((dist=distance_bl(&md->bl, bl)) < md->db->range2 + && (md->status.rhw.range > 6 || mob_can_reach(md,bl,dist+1, MSS_FOLLOW)) + && ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) //New target closer than previous one. + ) { + (*target) = bl; + md->target_id=bl->id; + md->min_chase= dist + md->db->range3; + if(md->min_chase>MAX_MINCHASE) + md->min_chase=MAX_MINCHASE; + return 1; + } + break; + } + return 0; +} + +/*========================================== + * chase target-change routine. + *------------------------------------------ + */ +static int mob_ai_sub_hard_changechase(struct block_list *bl,va_list ap) +{ + struct mob_data *md; + struct block_list **target; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + md=va_arg(ap,struct mob_data *); + target= va_arg(ap,struct block_list**); + + //If can't seek yet, not an enemy, or you can't attack it, skip. + if ((*target) == bl || battle_check_target(&md->bl,bl,BCT_ENEMY)<=0 || !status_check_skilluse(&md->bl, bl, 0, 0)) + return 0; + + switch (bl->type) + { + case BL_PC: + case BL_MOB: + if(check_distance_bl(&md->bl, bl, md->status.rhw.range) && + battle_check_range (&md->bl, bl, md->status.rhw.range) + ) { + (*target) = bl; + md->target_id=bl->id; + md->min_chase= md->db->range3; + return 1; + } + break; + } + return 0; +} + + +/*========================================== + * loot monster item search + *------------------------------------------ + */ +static int mob_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) +{ + struct mob_data* md; + struct block_list **target; + int dist; + + md=va_arg(ap,struct mob_data *); + target= va_arg(ap,struct block_list**); + + if((dist=distance_bl(&md->bl, bl)) < md->db->range2 && + mob_can_reach(md,bl,dist+1, MSS_LOOT) && + ((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) //New target closer than previous one. + ) { + (*target) = bl; + md->target_id=bl->id; + md->min_chase=md->db->range3; + } + return 0; +} + +/*========================================== + * Processing of slave monsters + *------------------------------------------ + */ +static int mob_ai_sub_hard_slavemob(struct mob_data *md,unsigned int tick) +{ + struct block_list *bl; + int old_dist; + + nullpo_retr(0, md); + + bl=map_id2bl(md->master_id); + + if (!bl || status_isdead(bl)) { //主が死亡しているか見つからない + if(md->special_state.ai) + unit_remove_map(&md->bl, 1); + else + status_kill(&md->bl); + return 0; + } + + if(status_get_mode(&md->bl)&MD_CANMOVE) + { //If the mob can move, follow around. [Check by Skotlex] + + if(bl->m != md->bl.m || md->master_dist > 30) + { // Since it is not in the same map (or is way to far), just warp it + unit_warp(&md->bl,bl->m,bl->x,bl->y,3); + return 0; + } + + // Distance with between slave and master is measured. + old_dist=md->master_dist; + md->master_dist=distance_bl(&md->bl, bl); + + // Since the master was in near immediately before, teleport is carried out and it pursues. + if(old_dist<10 && md->master_dist>18){ + unit_warp(&md->bl,-1,bl->x,bl->y,3); + return 0; + } + + // Approach master if within view range, chase back to Master's area also if standing on top of the master. + if(md->master_distdb->range3 && + (md->master_dist>MOB_SLAVEDISTANCE || md->master_dist == 0) && + unit_can_move(&md->bl)) + { + short x = bl->x, y = bl->y; + mob_stop_attack(md); + if (map_search_freecell(&md->bl, bl->m, &x, &y, MOB_SLAVEDISTANCE, MOB_SLAVEDISTANCE, 1)) + unit_walktoxy(&md->bl, x, y, 0); + } + } else if (bl->m != md->bl.m && map_flag_gvg(md->bl.m)) { + //Delete the summoned mob if it's in a gvg ground and the master is elsewhere. [Skotlex] + if(md->special_state.ai) + unit_remove_map(&md->bl, 1); + else + status_kill(&md->bl); + return 0; + } + + //Avoid attempting to lock the master's target too often to avoid unnecessary overload. [Skotlex] + if (DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME && !md->target_id) { + struct unit_data *ud = unit_bl2ud(bl); + md->last_linktime = tick; + + if (ud) { + struct block_list *tbl=NULL; + if (ud->target && ud->attacktimer != -1) + tbl=map_id2bl(ud->target); + else if (ud->skilltarget) { + tbl = map_id2bl(ud->skilltarget); + //Required check as skilltarget is not always an enemy. [Skotlex] + if (tbl && battle_check_target(&md->bl, tbl, BCT_ENEMY) <= 0) + tbl = NULL; + } + if (tbl && status_check_skilluse(&md->bl, tbl, 0, 0)) { + if(md->nd){ + setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)4, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)tbl->type, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 2, (void *)tbl->id, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); + run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); + } + md->target_id=tbl->id; + md->min_chase=md->db->range3+distance_bl(&md->bl, tbl); + if(md->min_chase>MAX_MINCHASE) + md->min_chase=MAX_MINCHASE; + } + } + } + return 0; +} + +/*========================================== + * A lock of target is stopped and mob moves to a standby state. + *------------------------------------------ + */ +int mob_unlocktarget(struct mob_data *md,int tick) +{ + nullpo_retr(0, md); + + if(md->nd){ + struct block_list *tbl = map_id2bl(md->target_id); + setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)6, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)(tbl?tbl->type:0), &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 2, (void *)(tbl?tbl->id:0), &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); + run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); + } + + md->target_id=0; + md->state.skillstate=MSS_IDLE; + md->next_walktime=tick+rand()%3000+3000; + mob_stop_attack(md); + md->ud.target = 0; + return 0; +} +/*========================================== + * Random walk + *------------------------------------------ + */ +int mob_randomwalk(struct mob_data *md,int tick) +{ + const int retrycount=20; + int i,x,y,c,d; + int speed; + + nullpo_retr(0, md); + + if(DIFF_TICK(md->next_walktime,tick)>0 || md->state.no_random_walk || !unit_can_move(&md->bl)) + return 0; + + d =12-md->move_fail_count; + if(d<5) d=5; + for(i=0;ibl.x; + y+=md->bl.y; + + if((map_getcell(md->bl.m,x,y,CELL_CHKPASS)) && unit_walktoxy(&md->bl,x,y,1)){ + break; + } + } + if(i==retrycount){ + md->move_fail_count++; + if(md->move_fail_count>1000){ + if(battle_config.error_log) + ShowWarning("MOB cant move. random spawn %d, class = %d\n",md->bl.id,md->class_); + md->move_fail_count=0; + mob_spawn(md); + } + return 0; + } + speed=status_get_speed(&md->bl); + for(i=c=0;iud.walkpath.path_len;i++){ // The next walk start time is calculated. + if(md->ud.walkpath.path[i]&1) + c+=speed*14/10; + else + c+=speed; + } + md->state.skillstate=MSS_WALK; + md->move_fail_count=0; + md->next_walktime = tick+rand()%3000+3000+c; + return 1; +} + +/*========================================== + * AI of MOB whose is near a Player + *------------------------------------------ + */ +static int mob_ai_sub_hard(struct block_list *bl,va_list ap) +{ + struct mob_data *md; + struct block_list *tbl = NULL, *abl = NULL; + unsigned int tick; + int dist; + int mode; + int search_size; + int view_range, can_move; + + md = (struct mob_data*)bl; + tick = va_arg(ap, unsigned int); + + if(md->bl.prev == NULL || md->status.hp <= 0) + return 1; + + if (DIFF_TICK(tick, md->last_thinktime) < MIN_MOBTHINKTIME) + return 0; + md->last_thinktime = tick; + + if (md->ud.skilltimer != -1) + return 0; + + if(md->ud.walktimer != -1 && md->ud.walkpath.path_pos <= 3) + return 0; + + // Abnormalities + if((md->sc.opt1 > 0 && md->sc.opt1 != OPT1_STONEWAIT) || md->sc.data[SC_BLADESTOP].timer != -1) + return 0; + + if (md->sc.count && md->sc.data[SC_BLIND].timer != -1) + view_range = 3; + else + view_range = md->db->range2; + mode = status_get_mode(&md->bl); + + can_move = (mode&MD_CANMOVE)&&unit_can_move(&md->bl); + + if (md->target_id) + { //Check validity of current target. [Skotlex] + tbl = map_id2bl(md->target_id); + if (!tbl || tbl->m != md->bl.m || + (md->ud.attacktimer == -1 && !status_check_skilluse(&md->bl, tbl, 0, 0)) || + (md->ud.walktimer != -1 && !check_distance_bl(&md->bl, tbl, md->min_chase)) || + ( + tbl->type == BL_PC && !(mode&MD_BOSS) && + ((TBL_PC*)tbl)->state.gangsterparadise + )) { //Unlock current target. + if (battle_config.mob_ai&8) //Inmediately stop chasing. + mob_stop_walking(md,1); + mob_unlocktarget(md, tick-(battle_config.mob_ai&8?3000:0)); //Imediately do random walk. + tbl = NULL; + } + } + + // Check for target change. + if (md->attacked_id && mode&MD_CANATTACK) + { + if (md->attacked_id == md->target_id) + { + if (!can_move && (battle_config.mob_ai&2) && + !battle_check_range(&md->bl, tbl, md->status.rhw.range)) + { //Rude-attacked. + if (md->attacked_count++ > 3) + mobskill_use(md, tick, MSC_RUDEATTACKED); + } + } else + if ((abl= map_id2bl(md->attacked_id)) && (!tbl || mob_can_changetarget(md, abl, mode))) { + if (md->bl.m != abl->m || abl->prev == NULL || + (dist = distance_bl(&md->bl, abl)) >= MAX_MINCHASE || + battle_check_target(bl, abl, BCT_ENEMY) <= 0 || + (battle_config.mob_ai&2 && !status_check_skilluse(bl, abl, 0, 0)) || + !mob_can_reach(md, abl, dist+2, MSS_RUSH) || + ( //Gangster Paradise check + abl->type == BL_PC && !(mode&MD_BOSS) && + ((TBL_PC*)abl)->state.gangsterparadise + ) + ) { //Can't attack back + if (md->attacked_count++ > 3) { + if (mobskill_use(md, tick, MSC_RUDEATTACKED) == 0 && can_move) + { + int dist = rand() % 10 + 1;//後退する距離 + int dir = map_calc_dir(abl, bl->x, bl->y); + int mask[8][2] = {{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}}; + unit_walktoxy(&md->bl, md->bl.x + dist * mask[dir][0], md->bl.y + dist * mask[dir][1], 0); + } + } + } else if (!(battle_config.mob_ai&2) && !status_check_skilluse(bl, abl, 0, 0)) { + //Can't attack back, but didn't invoke a rude attacked skill... + md->attacked_id = 0; //Simply unlock, shouldn't attempt to run away when in dumb_ai mode. + } else { //Attackable + if (!tbl || dist < md->status.rhw.range || !check_distance_bl(&md->bl, tbl, dist) + || battle_gettarget(tbl) != md->bl.id) + { //Change if the new target is closer than the actual one + //or if the previous target is not attacking the mob. [Skotlex] + md->target_id = md->attacked_id; // set target + md->state.aggressive = 0; //Retaliating. + md->attacked_count = 0; + md->min_chase = dist+md->db->range3; + if(md->min_chase>MAX_MINCHASE) + md->min_chase=MAX_MINCHASE; + tbl = abl; //Set the new target + } + } + } + if (md->state.aggressive && md->attacked_id == md->target_id) + md->state.aggressive = 0; //No longer aggressive, change to retaliate AI. + //Clear it since it's been checked for already. + md->attacked_players = 0; + md->attacked_id = 0; + } + + // Processing of slave monster, is it needed when there's a target to deal with? + if (md->master_id > 0 && !tbl) + mob_ai_sub_hard_slavemob(md, tick); + + // Scan area for targets + if (!tbl && mode&MD_LOOTER && md->lootitem && DIFF_TICK(tick, md->ud.canact_tick) > 0 && + (md->lootitem_count < LOOTITEM_SIZE || battle_config.monster_loot_type != 1)) + { // Scan area for items to loot, avoid trying to loot of the mob is full and can't consume the items. + map_foreachinrange (mob_ai_sub_hard_lootsearch, &md->bl, + view_range, BL_ITEM, md, &tbl); + } + + if ((!tbl && mode&MD_AGGRESSIVE && battle_config.monster_active_enable) || + (mode&MD_ANGRY && md->state.skillstate == MSS_FOLLOW) + ) { + map_foreachinrange (mob_ai_sub_hard_activesearch, &md->bl, + view_range, md->special_state.ai?BL_CHAR:BL_PC, md, &tbl); + if(!tbl && mode&MD_ANGRY && !md->state.aggressive) + md->state.aggressive = 1; //Restore angry state when no targets are visible. + } else if (mode&MD_CHANGECHASE && (md->state.skillstate == MSS_RUSH || md->state.skillstate == MSS_FOLLOW)) { + search_size = view_rangestatus.rhw.range ? view_range:md->status.rhw.range; + map_foreachinrange (mob_ai_sub_hard_changechase, &md->bl, + search_size, (md->special_state.ai?BL_CHAR:BL_PC), md, &tbl); + } + + if (tbl) + { //Target exists, attack or loot as applicable. + if (tbl->type != BL_ITEM) + { //Attempt to attack. + //At this point we know the target is attackable, we just gotta check if the range matches. + if (md->ud.target == tbl->id && md->ud.attacktimer != -1) + return 0; //Already locked. + + if (!battle_check_range (&md->bl, tbl, md->status.rhw.range)) + { //Out of range... + if (!(mode&MD_CANMOVE)) + { //Can't chase. Attempt to use a ranged skill at least? + md->state.skillstate = MSS_IDLE; + if (!mobskill_use(md, tick, -1)) + mob_unlocktarget(md,tick); + return 0; + } + + if (!can_move) + { //Stuck. Use an idle skill. o.O' + md->state.skillstate = MSS_IDLE; + if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL)) + mobskill_use(md, tick, -1); + return 0; + } + + md->state.skillstate = md->state.aggressive?MSS_FOLLOW:MSS_RUSH; + if (md->ud.walktimer != -1 && md->ud.target == tbl->id && + ( + !battle_config.mob_ai&1 || + check_distance_blxy(tbl, md->ud.to_x, md->ud.to_y, md->status.rhw.range) + )) //Current target tile is still within attack range. + return 0; + + //Follow up + if (!mob_can_reach(md, tbl, md->min_chase, MSS_RUSH) || + !unit_walktobl(&md->bl, tbl, md->status.rhw.range, 2|(!battle_config.mob_ai&1))) + //Give up. + mob_unlocktarget(md,tick); + return 0; + } + //Target within range, engage + md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK; + unit_attack(&md->bl,tbl->id,1); + return 0; + } else { //Target is BL_ITEM, attempt loot. + struct flooritem_data *fitem; + int i; + if (md->ud.target == tbl->id && md->ud.walktimer != -1) + return 0; //Already locked. + if (md->lootitem == NULL) + { //Can't loot... + mob_unlocktarget (md, tick); + mob_stop_walking(md,0); + return 0; + } + + if (!check_distance_bl(&md->bl, tbl, 1)) + { //Still not within loot range. + if (!(mode&MD_CANMOVE)) + { //A looter that can't move? Real smart. + mob_unlocktarget(md,tick); + return 0; + } + if (!can_move) //Stuck. Wait before walking. + return 0; + md->state.skillstate = MSS_LOOT; // ルート時スキル使用 + if (!unit_walktobl(&md->bl, tbl, 0, 1)) + mob_unlocktarget(md, tick); //Can't loot... + return 0; + } + //Within looting range. + if (md->ud.attacktimer != -1) + return 0; //Busy attacking? + + fitem = (struct flooritem_data *)tbl; + if (md->lootitem_count < LOOTITEM_SIZE) { + memcpy (&md->lootitem[md->lootitem_count++], &fitem->item_data, sizeof(md->lootitem[0])); + if(log_config.pick > 0) //Logs items, taken by (L)ooter Mobs [Lupus] + log_pick((struct map_session_data*)md, "L", md->class_, md->lootitem[md->lootitem_count-1].nameid, md->lootitem[md->lootitem_count-1].amount, &md->lootitem[md->lootitem_count-1]); + } else { //Destroy first looted item... + if (md->lootitem[0].card[0] == (short)0xff00) + intif_delete_petdata( MakeDWord(md->lootitem[0].card[1],md->lootitem[0].card[2]) ); + for (i = 0; i < LOOTITEM_SIZE - 1; i++) + memcpy (&md->lootitem[i], &md->lootitem[i+1], sizeof(md->lootitem[0])); + memcpy (&md->lootitem[LOOTITEM_SIZE-1], &fitem->item_data, sizeof(md->lootitem[0])); + } + //Clear item. + if (pcdb_checkid(md->vd->class_)) + { //Give them walk act/delay to properly mimic players. [Skotlex] + clif_takeitem(&md->bl,tbl); + md->ud.canact_tick = tick + md->status.amotion; + unit_set_walkdelay(&md->bl, tick, md->status.amotion, 1); + } + map_clearflooritem (tbl->id); + mob_unlocktarget (md,tick); + return 0; + } + } + + if(md->ud.walktimer == -1) { + // When there's no target, it is idling. + // Is it terribly exploitable to reuse the walkcounter for idle state skills? [Skotlex] + md->state.skillstate = MSS_IDLE; + if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mobskill_use(md, tick, -1)) + return 0; + } + // Nothing else to do... except random walking. + // Slaves do not random walk! [Skotlex] + if (can_move && !md->master_id && DIFF_TICK(md->next_walktime, tick) <= 0) + mob_randomwalk(md,tick); + + return 0; +} + +/*========================================== + * Serious processing for mob in PC field of view (foreachclient) + *------------------------------------------ + */ +static int mob_ai_sub_foreachclient(struct map_session_data *sd,va_list ap) +{ + unsigned int tick; + tick=va_arg(ap,unsigned int); + map_foreachinrange(mob_ai_sub_hard,&sd->bl, AREA_SIZE*2, BL_MOB,tick); + + return 0; +} + +/*========================================== + * Negligent mode MOB AI (PC is not in near) + *------------------------------------------ + */ +static int mob_ai_sub_lazy(DBKey key,void * data,va_list app) +{ + struct mob_data *md = (struct mob_data *)data; + va_list ap; + unsigned int tick; + int mode; + + nullpo_retr(0, md); + nullpo_retr(0, app); + + if(md->bl.type!=BL_MOB || md->bl.prev == NULL) + return 0; + + ap = va_arg(app, va_list); + + if (md->nd || (battle_config.mob_ai&32 && map[md->bl.m].users>0)) + return mob_ai_sub_hard(&md->bl, ap); + + tick=va_arg(ap,unsigned int); + + if(DIFF_TICK(tick,md->last_thinktime)last_thinktime=tick; + + if (md->bl.prev==NULL || md->status.hp <= 0) + return 1; + + // 取り巻きモンスターの処理(呼び戻しされた時) + if (md->master_id) { + mob_ai_sub_hard_slavemob (md,tick); + return 0; + } + + mode = status_get_mode(&md->bl); + if(DIFF_TICK(md->next_walktime,tick)<0 && + (mode&MD_CANMOVE) && unit_can_move(&md->bl) ){ + + if( map[md->bl.m].users>0 ){ + // Since PC is in the same map, somewhat better negligent processing is carried out. + + // It sometimes moves. + if(rand()%1000spawn && !md->spawn->x && !md->spawn->y) +// && !md->target_id && !(mode&MD_BOSS)) +// unit_warp(&md->bl,-1,-1,-1,0); + }else{ + // Since PC is not even in the same map, suitable processing is carried out even if it takes. + + // MOB which is not BOSS which is not Summons MOB, either -- a case -- sometimes -- leaping + if( rand()%1000spawn && !md->spawn->x && !md->spawn->y) + && !(mode&MD_BOSS)) + unit_warp(&md->bl,-1,-1,-1,0); + } + + md->next_walktime = tick+rand()%10000+5000; + } + return 0; +} + +/*========================================== + * Negligent processing for mob outside PC field of view (interval timer function) + *------------------------------------------ + */ +static int mob_ai_lazy(int tid,unsigned int tick,int id,int data) +{ + map_foreachiddb(mob_ai_sub_lazy,tick); + + return 0; +} + +/*========================================== + * Serious processing for mob in PC field of view (interval timer function) + *------------------------------------------ + */ +static int mob_ai_hard(int tid,unsigned int tick,int id,int data) +{ + + if (battle_config.mob_ai&32) + map_foreachiddb(mob_ai_sub_lazy,tick); + else + clif_foreachclient(mob_ai_sub_foreachclient,tick); + + return 0; +} + +/*========================================== + * Initializes the delay drop structure for mob-dropped items. + *------------------------------------------ + */ +static struct item_drop* mob_setdropitem(int nameid, int qty) +{ + struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop); + memset(&drop->item_data, 0, sizeof(struct item)); + drop->item_data.nameid = nameid; + drop->item_data.amount = qty; + drop->item_data.identify = !itemdb_isequip3(nameid); + drop->next = NULL; + return drop; +}; + +/*========================================== + * Initializes the delay drop structure for mob-looted items. + *------------------------------------------ + */ +static struct item_drop* mob_setlootitem(struct item* item) +{ + struct item_drop *drop = ers_alloc(item_drop_ers, struct item_drop); + memcpy(&drop->item_data, item, sizeof(struct item)); + drop->next = NULL; + return drop; +}; + +/*========================================== + * item drop with delay (timer function) + *------------------------------------------ + */ +static int mob_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; +} + +/*========================================== + * Sets the item_drop into the item_drop_list. + * Also performs logging and autoloot if enabled. + * rate is the drop-rate of the item, required for autoloot. + *------------------------------------------ + * by [Skotlex] + */ +static void mob_item_drop(struct mob_data *md, struct item_drop_list *dlist, struct item_drop *ditem, int loot, int drop_rate) +{ + if(log_config.pick > 0) + { //Logs items, dropped by mobs [Lupus] + if (loot) + log_pick((struct map_session_data*)md, "L", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, &ditem->item_data); + else + log_pick((struct map_session_data*)md, "M", md->class_, ditem->item_data.nameid, -ditem->item_data.amount, NULL); + } + + if (dlist->first_sd && dlist->first_sd->state.autoloot && + (drop_rate <= dlist->first_sd->state.autoloot) + ) { //Autoloot. + if (party_share_loot( + dlist->first_sd->status.party_id? + party_search(dlist->first_sd->status.party_id): + NULL, + dlist->first_sd,&ditem->item_data) == 0 + ) { + ers_free(item_drop_ers, ditem); + return; + } + } + ditem->next = dlist->item; + dlist->item = ditem; +} + +int mob_timer_delete(int tid, unsigned int tick, int id, int data) +{ + struct block_list *bl=map_id2bl(id); + nullpo_retr(0, bl); + if (bl->type != BL_MOB) + return 0; //?? +//for Alchemist CANNIBALIZE [Lupus] + ((TBL_MOB*)bl)->deletetimer = -1; + unit_remove_map(bl, 3); + unit_free(bl); + return 0; +} + +int mob_convertslave_sub(struct block_list *bl,va_list ap) +{ + struct mob_data *md, *md2 = NULL; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md = (struct mob_data *)bl); + + md2=va_arg(ap,TBL_MOB *); + + if(md->master_id > 0 && md->master_id == md2->bl.id){ + md->master_id = md2->master_id; + md->state.killer = md2->state.killer; + md->special_state.ai = md2->special_state.ai; + } + + return 0; +} + +int mob_convertslave(struct mob_data *md) +{ + nullpo_retr(0, md); + + map_foreachinmap(mob_convertslave_sub, md->bl.m, BL_MOB, md); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int mob_deleteslave_sub(struct block_list *bl,va_list ap) +{ + struct mob_data *md; + int id; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md = (struct mob_data *)bl); + + id=va_arg(ap,int); + if(md->master_id > 0 && md->master_id == id ) + status_kill(bl); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int mob_deleteslave(struct mob_data *md) +{ + nullpo_retr(0, md); + + map_foreachinmap(mob_deleteslave_sub, md->bl.m, BL_MOB,md->bl.id); + return 0; +} +// Mob respawning through KAIZEL or NPC_REBIRTH [Skotlex] +int mob_respawn(int tid, unsigned int tick, int id,int data ) +{ + struct mob_data *md = (struct mob_data*)map_id2bl(id); + if (!md || md->bl.type != BL_MOB) + return 0; + //Mob must be dead and not in a map to respawn! + if (md->bl.prev != NULL || md->status.hp) + return 0; + + md->state.skillstate = MSS_IDLE; + md->last_thinktime = tick; + md->next_walktime = tick+rand()%50+5000; + md->last_linktime = tick; + map_addblock(&md->bl); + status_percent_heal(&md->bl, data, 0); + clif_spawn(&md->bl); + skill_unit_move(&md->bl,tick,1); + mobskill_use(md, tick, MSC_SPAWN); + return 1; +} + +//Call when a mob has received damage. +void mob_damage(struct mob_data *md, struct block_list *src, int damage) +{ + int id = 0; + + if(src && md->nd) + { + setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)1, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)src->type, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 2, (void *)src->id, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); + run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); + } + + md->tdmg+=damage; //Store total damage... + + if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) // guardian hp update [Valaris] (updated by [Skotlex]) + md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->status.hp; + + if (battle_config.show_mob_hp) + clif_charnameack (0, &md->bl); + + if (!src) + return; + + md->attacked_players++; + if (!md->attacked_players) //Counter overflow o.O + md->attacked_players++; + + switch (src->type) { + case BL_PC: + { + struct map_session_data *sd = (struct map_session_data *)src; + id = sd->status.char_id; + if(rand()%1000 < 1000/md->attacked_players) + md->attacked_id = src->id; + break; + } + case BL_PET: + { + struct pet_data *pd = (struct pet_data*)src; + if (battle_config.pet_attack_exp_to_master) { + id = pd->msd->status.char_id; + damage=(damage*battle_config.pet_attack_exp_rate)/100; //Modify logged damage accordingly. + } + //Let mobs retaliate against the pet's master [Skotlex] + if(rand()%1000 < 1000/md->attacked_players) + md->attacked_id = pd->msd->bl.id; + break; + } + case BL_MOB: + { + struct mob_data* md2 = (struct mob_data*)src; + if(md2->special_state.ai && md2->master_id) { + struct map_session_data* msd = map_id2sd(md2->master_id); + if (msd) id = msd->status.char_id; + } + if(rand()%1000 < 1000/md->attacked_players) + { //Let players decide whether to retaliate versus the master or the mob. [Skotlex] + if (md2->master_id && battle_config.retaliate_to_master) + md->attacked_id = md2->master_id; + else + md->attacked_id = src->id; + } + break; + } + } + //Log damage... + if (id && damage > 0) { + int i,minpos,mindmg; + for(i=0,minpos=DAMAGELOG_SIZE-1,mindmg=INT_MAX;idmglog[i].id==id) + break; + if(md->dmglog[i].id==0) { //Store data in first empty slot. + md->dmglog[i].id = id; + break; + } + if(md->dmglog[i].dmgdmglog[i].dmg; + } + } + if(idmglog[i].dmg+=damage; + else { + md->dmglog[minpos].id=id; + md->dmglog[minpos].dmg=damage; + } + } + + if(md->special_state.ai==2 && md->master_id == src->id) + { + md->state.alchemist = 1; + mobskill_use(md, gettick(), MSC_ALCHEMIST); + } +} + +/*========================================== + * Signals death of mob. type&1 -> no drops, type&2 -> no exp + *------------------------------------------ + */ +int mob_dead(struct mob_data *md, struct block_list *src, int type) +{ + struct status_data *status; + struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE], + *mvp_sd = NULL, *second_sd = NULL,*third_sd = NULL; + + struct { + struct party *p; + int id,zeny; + unsigned int base_exp,job_exp; + } pt[DAMAGELOG_SIZE]; + int i,temp,count,pnum=0; + unsigned int mvp_damage, tick = gettick(); + + if(src && src->type == BL_PC) { + sd = (struct map_session_data *)src; + mvp_sd = sd; + } + + status = &md->status; + + if(status->hp) //Requested instant death? + status->hp = 0; + + if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) + { // guardian hp update [Valaris] (updated by [Skotlex]) + guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); + guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); + } + + md->state.skillstate = MSS_DEAD; + mobskill_use(md,tick,-1); //On Dead skill. + + if (md->sc.data[SC_KAIZEL].timer != -1) + { //Revive in a bit. + mob_unlocktarget(md,tick); + mob_stop_walking(md, 0); + clif_clearchar_area(&md->bl,1); + add_timer(gettick()+3000, mob_respawn, md->bl.id, 10*md->sc.data[SC_KAIZEL].val1); //% of life to rebirth with + status_change_end(&md->bl, SC_KAIZEL, -1); + map_delblock(&md->bl); + return 0; + } + + map_freeblock_lock(); + + memset(tmpsd,0,sizeof(tmpsd)); + memset(pt,0,sizeof(pt)); + + if(src && src->type == BL_MOB) + mob_unlocktarget((struct mob_data *)src,tick); + + if(sd) { + int sp = 0, hp = 0; + sp += sd->sp_gain_value; + sp += sd->sp_gain_race[status->race]; + sp += sd->sp_gain_race[status->mode&MD_BOSS?10:11]; + hp += sd->hp_gain_value; + if (hp||sp) + status_heal(src, hp, sp, battle_config.show_hp_sp_gain?2:0); + if (sd->mission_mobid == md->class_) { //TK_MISSION [Skotlex] + if (++sd->mission_count >= 100 && (temp = mob_get_random_id(0, 0, sd->status.base_level))) + { + pc_addfame(sd, 1); + sd->mission_mobid = temp; + pc_setglobalreg(sd,"TK_MISSION_ID", temp); + sd->mission_count = 0; + clif_mission_mob(sd, temp, 0); + } + pc_setglobalreg(sd,"TK_MISSION_COUNT", sd->mission_count); + } + } + + for(temp=0,i=0,count=0,mvp_damage=0;idmglog[i].id;i++) + { + count++; //Count an attacker even if he is dead/logged-out. + tmpsd[temp] = map_charid2sd(md->dmglog[i].id); + if(tmpsd[temp] == NULL) + continue; + if(tmpsd[temp]->bl.m != md->bl.m || pc_isdead(tmpsd[i])) + continue; + temp++; + + if(mvp_damagedmglog[temp].dmg){ + third_sd = second_sd; + second_sd = mvp_sd; + mvp_sd=tmpsd[temp]; + mvp_damage=md->dmglog[temp].dmg; + } + } + + if(!(type&2) && //No exp + (!map[md->bl.m].flag.pvp || battle_config.pvp_exp) && //Pvp no exp rule [MouseJstr] + (!md->master_id || !md->special_state.ai) && //Only player-summoned mobs do not give exp. [Skotlex] + (!map[md->bl.m].flag.nobaseexp || !map[md->bl.m].flag.nojobexp) //Gives Exp + ) { //Experience calculation. + + for(i=0;idmglog[i].dmg/(double)status->max_hp; + else //jAthena's exp formula based on total damage. + per = (double)md->dmglog[i].dmg/(double)md->tdmg; + + if (count>1) + per *= (9.+(double)((count > 6)? 6:count))/10.; //attackers count bonus. + + if(md->special_state.size==1) // change experience for different sized monsters [Valaris] + per /=2.; + else if(md->special_state.size==2) + per *=2.; + + bonus = 100; + + if (md->sc.data[SC_RICHMANKIM].timer != -1) + bonus += md->sc.data[SC_RICHMANKIM].val2; + + if(sd) { + if (sd->expaddrace[status->race]) + bonus += sd->expaddrace[status->race]; + bonus += sd->expaddrace[status->mode&MD_BOSS?10:11]; + } + if (battle_config.pk_mode && + (int)(md->db->lv - tmpsd[i]->status.base_level) >= 20) //Needed due to unsigned checks + bonus += 15; // pk_mode additional exp if monster >20 levels [Valaris] + + //SG additional exp from Blessings [Komurka] - probably can be optimalized ^^;; + if(md->class_ == tmpsd[i]->hate_mob[2] && (battle_config.allow_skill_without_day || is_day_of_star() || tmpsd[i]->sc.data[SC_MIRACLE].timer!=-1)) + bonus += 20*pc_checkskill(tmpsd[i],SG_STAR_BLESS); + else if(md->class_ == tmpsd[i]->hate_mob[1] && (battle_config.allow_skill_without_day || is_day_of_moon())) + bonus += 10*pc_checkskill(tmpsd[i],SG_MOON_BLESS); + else if(md->class_ == tmpsd[i]->hate_mob[0] && (battle_config.allow_skill_without_day || is_day_of_sun())) + bonus += 10*pc_checkskill(tmpsd[i],SG_SUN_BLESS); + + if(battle_config.mobs_level_up && md->level > md->db->lv) // [Valaris] + bonus += (md->level-md->db->lv)*battle_config.mobs_level_up_exp_rate; + + if (bonus > 400) bonus = 400; //Limit gained exp to quadro the mob's exp. [3->4 Komurka] + + if(battle_config.zeny_from_mobs && md->level) { + // zeny calculation moblv + random moblv [Valaris] + zeny=(int) ((md->level+rand()%md->level)*per*bonus/100.); + if(md->db->mexp > 0) + zeny*=rand()%250; + } + + if (map[md->bl.m].flag.nobaseexp) + base_exp=0; + else { + temp = bonus; //Do not alter bonus for the jExp section below. + if (map[md->bl.m].bexp != 100) + temp = map[md->bl.m].bexp*temp/100; + if (temp != 100) + per = per*temp/100.; + + base_exp = md->db->base_exp; + + if (base_exp*per > UINT_MAX) + base_exp = UINT_MAX; + else + base_exp = (unsigned int)(base_exp*per); + + if (base_exp < 1) + base_exp = 1; + } + + if (map[md->bl.m].flag.nojobexp) + job_exp=0; + else { + if (map[md->bl.m].jexp != 100) + bonus = map[md->bl.m].jexp*bonus/100; + if (bonus != 100) + per = per*bonus/100.; + + job_exp = md->db->job_exp; + + if (job_exp*per > UINT_MAX) + job_exp = UINT_MAX; + else + job_exp = (unsigned int)(job_exp*per); + + if (job_exp < 1) + job_exp = 1; + } + + if((temp=tmpsd[i]->status.party_id)>0) + { + int j; + for(j=0;jexp) + { + pt[pnum].id=temp; + pt[pnum].base_exp=base_exp; + pt[pnum].job_exp=job_exp; + pt[pnum].zeny=zeny; // zeny share [Valaris] + pnum++; + flag=0; + } + }else{ //Add to total + if (pt[j].base_exp > UINT_MAX - base_exp) + pt[j].base_exp=UINT_MAX; + else + pt[j].base_exp+=base_exp; + + if (pt[j].job_exp > UINT_MAX - job_exp) + pt[j].job_exp=UINT_MAX; + else + pt[j].job_exp+=job_exp; + + pt[j].zeny+=zeny; // zeny share [Valaris] + flag=0; + } + } + if(flag) { + if(base_exp || job_exp) + pc_gainexp(tmpsd[i],base_exp,job_exp); + if(zeny) // zeny from mobs [Valaris] + pc_getzeny(tmpsd[i],zeny); + } + } + + for(i=0;ibl.m,pt[i].base_exp,pt[i].job_exp,pt[i].zeny); + } //End EXP giving. + + if (!(type&1) && + !map[md->bl.m].flag.nomobloot && + ( + !md->special_state.ai || //Non special mob + battle_config.alchemist_summon_reward == 2 || //All summoned give drops + (md->special_state.ai==2 && battle_config.alchemist_summon_reward == 1) //Marine Sphere Drops items. + ) + ) { //item drop + struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); + struct item_drop *ditem; + int drop_rate; + dlist->m = md->bl.m; + dlist->x = md->bl.x; + dlist->y = md->bl.y; + dlist->first_sd = mvp_sd; + dlist->second_sd = second_sd; + dlist->third_sd = third_sd; + dlist->item = NULL; + + for (i = 0; i < MAX_MOB_DROP; i++) { + if (md->db->dropitem[i].nameid <= 0) + continue; + drop_rate = md->db->dropitem[i].p; + if (drop_rate <= 0) { + if (battle_config.drop_rate0item) + continue; + drop_rate = 1; + } + // change drops depending on monsters size [Valaris] + if(md->special_state.size==1 && drop_rate >= 2) + drop_rate/=2; + else if(md->special_state.size==2) + drop_rate*=2; + if (src) { + //Drops affected by luk as a fixed increase [Valaris] + if (battle_config.drops_by_luk) + drop_rate += status_get_luk(src)*battle_config.drops_by_luk/100; + //Drops affected by luk as a % increase [Skotlex] + if (battle_config.drops_by_luk2) + drop_rate += (int)(0.5+drop_rate*status_get_luk(src)*battle_config.drops_by_luk2/10000.); + } + if (sd && battle_config.pk_mode && + (int)(md->db->lv - sd->status.base_level) >= 20) + drop_rate = (int)(drop_rate*1.25); // pk_mode increase drops if 20 level difference [Valaris] + +// if (10000 < rand()%10000+drop_rate) //May be better if MAX_RAND is too low? + if (drop_rate < rand() % 10000 + 1) //fixed 0.01% impossible drops bug [Lupus] + continue; + + ditem = mob_setdropitem(md->db->dropitem[i].nameid, 1); + + //A Rare Drop Global Announce by Lupus + if(drop_rate<=battle_config.rare_drop_announce) { + struct item_data *i_data; + char message[128]; + i_data = itemdb_search(ditem->item_data.nameid); + sprintf (message, msg_txt(541), (mvp_sd?mvp_sd->status.name:"???"), md->name, i_data->jname, (float)drop_rate/100); + //MSG: "'%s' won %s's %s (chance: %%%0.02f)" + intif_GMmessage(message,strlen(message)+1,0); + } + // Announce first, or else ditem will be freed. [Lance] + // By popular demand, use base drop rate for autoloot code. [Skotlex] + mob_item_drop(md, dlist, ditem, 0, md->db->dropitem[i].p); + } + + // Ore Discovery [Celest] + if (sd == mvp_sd && pc_checkskill(sd,BS_FINDINGORE)>0 && battle_config.finding_ore_rate/10 >= rand()%10000) { + ditem = mob_setdropitem(itemdb_searchrandomid(IG_FINDINGORE), 1); + mob_item_drop(md, dlist, ditem, 0, battle_config.finding_ore_rate/10); + } + + if(sd) { + int itemid = 0; + for (i = 0; i < sd->add_drop_count; i++) { + if (sd->add_drop[i].id < 0) + continue; + if (sd->add_drop[i].race & (1<race) || + sd->add_drop[i].race & 1<<(status->mode&MD_BOSS?RC_BOSS:RC_NONBOSS)) + { + //check if the bonus item drop rate should be multiplied with mob level/10 [Lupus] + if(sd->add_drop[i].rate<0) { + //it's negative, then it should be multiplied. e.g. for Mimic,Myst Case Cards, etc + // rate = base_rate * (mob_level/10) + 1 + drop_rate = -sd->add_drop[i].rate*(md->level/10)+1; + if (drop_rate < battle_config.item_drop_adddrop_min) + drop_rate = battle_config.item_drop_adddrop_min; + else if (drop_rate > battle_config.item_drop_adddrop_max) + drop_rate = battle_config.item_drop_adddrop_max; + } + else + //it's positive, then it goes as it is + drop_rate = sd->add_drop[i].rate; + if (drop_rate < rand()%10000 +1) + continue; + itemid = (sd->add_drop[i].id > 0) ? sd->add_drop[i].id : + itemdb_searchrandomid(sd->add_drop[i].group); + + mob_item_drop(md, dlist, mob_setdropitem(itemid,1), 0, drop_rate); + } + } + + if(sd->get_zeny_num && rand()%100 < sd->get_zeny_rate) //Gets get_zeny_num per level +/-10% [Skotlex] + pc_getzeny(sd,md->db->lv*sd->get_zeny_num*(90+rand()%21)/100); + } + if(md->lootitem) { + for(i=0;ilootitem_count;i++) + mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000); + } + if (dlist->item) //There are drop items. + add_timer(tick + (!battle_config.delay_battle_damage?500:0), + mob_delay_item_drop, (int)dlist, 0); + else //No drops + ers_free(item_drop_list_ers, dlist); + } else if (md->lootitem && md->lootitem_count) { //Loot MUST drop! + struct item_drop_list *dlist = ers_alloc(item_drop_list_ers, struct item_drop_list); + dlist->m = md->bl.m; + dlist->x = md->bl.x; + dlist->y = md->bl.y; + dlist->first_sd = mvp_sd; + dlist->second_sd = second_sd; + dlist->third_sd = third_sd; + dlist->item = NULL; + for(i=0;ilootitem_count;i++) + mob_item_drop(md, dlist, mob_setlootitem(&md->lootitem[i]), 1, 10000); + add_timer(tick + (!battle_config.delay_battle_damage?500:0), + mob_delay_item_drop, (int)dlist, 0); + } + + if(mvp_sd && md->db->mexp > 0 && !md->special_state.ai){ + int log_mvp[2] = {0}; + int j; + unsigned int mexp; + struct item item; + double exp; + + //mapflag: noexp check [Lorky] + if (map[md->bl.m].flag.nobaseexp) + exp =1; + else + exp = (double)md->db->mexp * (9+count)/10.; //[Gengar] + + mexp = (exp > UINT_MAX)?UINT_MAX:(exp<1?1:(unsigned int)exp); + + if(use_irc && irc_announce_mvp_flag) + irc_announce_mvp(mvp_sd,md); + + clif_mvp_effect(mvp_sd); + clif_mvp_exp(mvp_sd,mexp); + pc_gainexp(mvp_sd,mexp,0); + log_mvp[1] = mexp; + if(!map[md->bl.m].flag.nomvploot) + for(j=0;j<3;j++){ + i = rand() % 3; + + if(md->db->mvpitem[i].nameid <= 0) + continue; + + temp = md->db->mvpitem[i].p; + if(temp <= 0 && !battle_config.drop_rate0item) + temp = 1; + if(temp <= rand()%10000+1) //if ==0, then it doesn't drop + continue; + + memset(&item,0,sizeof(item)); + item.nameid=md->db->mvpitem[i].nameid; + item.identify=!itemdb_isequip3(item.nameid); + clif_mvp_item(mvp_sd,item.nameid); + log_mvp[0] = item.nameid; + + //A Rare MVP Drop Global Announce by Lupus + if(temp<=battle_config.rare_drop_announce) { + struct item_data *i_data; + char message[128]; + i_data = itemdb_exists(item.nameid); + sprintf (message, msg_txt(541), mvp_sd->status.name, md->name, i_data->jname, temp/100.); + //MSG: "'%s' won %s's %s (chance: %%%0.02f)" + intif_GMmessage(message,strlen(message)+1,0); + } + + if(mvp_sd->weight*2 > mvp_sd->max_weight) + map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1); + else if((temp = pc_additem(mvp_sd,&item,1))) { + clif_additem(sd,0,0,temp); + map_addflooritem(&item,1,mvp_sd->bl.m,mvp_sd->bl.x,mvp_sd->bl.y,mvp_sd,second_sd,third_sd,1); + } + + if(log_config.pick > 0) {//Logs items, MVP prizes [Lupus] + log_pick((struct map_session_data*)md, "M", md->class_, item.nameid, -1, NULL); + log_pick(mvp_sd, "P", 0, item.nameid, 1, NULL); + } + break; + } + + if(log_config.mvpdrop > 0) + log_mvpdrop(mvp_sd, md->class_, log_mvp); + } + + // NPC Event [OnAgitBreak] + if(md->npc_event[0] && strcmp(((md->npc_event)+strlen(md->npc_event)-13),"::OnAgitBreak") == 0) { + ShowNotice("MOB.C: Run NPC_Event[OnAgitBreak].\n"); + if (agit_flag == 1) //Call to Run NPC_Event[OnAgitBreak] + guild_agit_break(md); + } + + if(src && src->type == BL_MOB){ + struct mob_data *smd = (struct mob_data *)src; + if(smd->nd){ + setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)5, &smd->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)md->bl.type, &smd->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 2, (void *)md->bl.id, &smd->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 3, (void *)smd->bl.id, &smd->nd->u.scr.script->script_vars); + run_script(smd->nd->u.scr.script, 0, 0, smd->nd->bl.id); + } + } + + if(md->nd){ + setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)3, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)(src?src->type:0), &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 2, (void *)(src?src->id:0), &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); + run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); + } else if(md->npc_event[0]){ + if(src && src->type == BL_PET) + sd = ((struct pet_data *)src)->msd; + if(sd && battle_config.mob_npc_event_type) + npc_event(sd,md->npc_event,0); + else if(mvp_sd) + npc_event(mvp_sd,md->npc_event,0); + } else if (mvp_sd) { //lordalfa + pc_setglobalreg(mvp_sd,"killedrid",md->class_); + if(mvp_sd->state.event_kill_mob) + npc_script_event(mvp_sd, NPCE_KILLNPC); // PCKillNPC [Lance] + } + if(md->level) md->level=0; + map_freeblock_unlock(); + unit_remove_map(&md->bl,1); + return 1; +} + +int mob_guardian_guildchange(struct block_list *bl,va_list ap) +{ + struct mob_data *md; + struct guild* g; + + nullpo_retr(0, bl); + nullpo_retr(0, md = (struct mob_data *)bl); + + if (!md->guardian_data) + return 0; + + if (md->guardian_data->castle->guild_id == 0) + { //Castle with no owner? Delete the guardians. + if (md->class_ == MOBID_EMPERIUM) + { //But don't delete the emperium, just clear it's guild-data + md->guardian_data->guild_id = 0; + md->guardian_data->emblem_id = 0; + md->guardian_data->guild_name[0] = '\0'; + } else { + if (md->guardian_data->castle->guardian[md->guardian_data->number].visible) + { //Safe removal of guardian. + md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; + guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); + guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); + } + unit_free(&md->bl); //Remove guardian. + } + return 0; + } + + g = guild_search(md->guardian_data->castle->guild_id); + if (g == NULL) + { //Properly remove guardian info from Castle data. + ShowError("mob_guardian_guildchange: New Guild (id %d) does not exists!\n", md->guardian_data->guild_id); + md->guardian_data->castle->guardian[md->guardian_data->number].visible = 0; + guild_castledatasave(md->guardian_data->castle->castle_id, 10+md->guardian_data->number,0); + guild_castledatasave(md->guardian_data->castle->castle_id, 18+md->guardian_data->number,0); + unit_free(&md->bl); + return 0; + } + + md->guardian_data->guild_id = md->guardian_data->castle->guild_id; + md->guardian_data->emblem_id = g->emblem_id; + md->guardian_data->guardup_lv = guild_checkskill(g,GD_GUARDUP); + memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH); + + return 1; +} + +/*========================================== + * Pick a random class for the mob + *------------------------------------------ + */ +int mob_random_class (int *value, size_t count) +{ + nullpo_retr(0, value); + + // no count specified, look into the array manually, but take only max 5 elements + if (count < 1) { + count = 0; + while(count < 5 && mobdb_checkid(value[count])) count++; + if(count < 1) // nothing found + return 0; + } else { + // check if at least the first value is valid + if(mobdb_checkid(value[0]) == 0) + return 0; + } + //Pick a random value, hoping it exists. [Skotlex] + return mobdb_checkid(value[rand()%count]); +} + +/*========================================== + * Change mob base class + *------------------------------------------ + */ +int mob_class_change (struct mob_data *md, int class_) +{ + unsigned int tick = gettick(); + int i, c, hp_rate; + + nullpo_retr(0, md); + + if (md->bl.prev == NULL) + return 0; + + hp_rate = md->status.hp*100/md->status.max_hp; + md->db = mob_db(class_); + + if (battle_config.override_mob_names==1) + memcpy(md->name,md->db->name,NAME_LENGTH-1); + else + memcpy(md->name,md->db->jname,NAME_LENGTH-1); + + mob_stop_attack(md); + mob_stop_walking(md, 0); + unit_skillcastcancel(&md->bl, 0); + status_set_viewdata(&md->bl, class_); + clif_mob_class_change(md,class_); + status_calc_mob(md, 1); + + if (battle_config.monster_class_change_full_recover) { + memset(md->dmglog, 0, sizeof(md->dmglog)); + md->tdmg = 0; + } else { + md->status.hp = md->status.max_hp*hp_rate/100; + if(md->status.hp < 1) md->status.hp = 1; + } + + for(i=0,c=tick-1000*3600*10;iskilldelay[i] = c; + + if(md->lootitem == NULL && md->db->status.mode&MD_LOOTER) + md->lootitem=(struct item *)aCalloc(LOOTITEM_SIZE,sizeof(struct item)); + + if (battle_config.show_mob_hp) + clif_charnameack(0, &md->bl); + + return 0; +} + +/*========================================== + * mob回復 + *------------------------------------------ + */ +void mob_heal(struct mob_data *md,unsigned int heal) +{ + if(md->guardian_data && md->guardian_data->number < MAX_GUARDIANS) + // guardian hp update [Valaris] (updated by [Skotlex]) + md->guardian_data->castle->guardian[md->guardian_data->number].hp = md->status.hp; + + if (battle_config.show_mob_hp) + clif_charnameack (0, &md->bl); +} + +/*========================================== + * Added by RoVeRT + *------------------------------------------ + */ +int mob_warpslave_sub(struct block_list *bl,va_list ap) +{ + struct mob_data *md=(struct mob_data *)bl; + struct block_list *master; + short x,y,range=0; + master = va_arg(ap, struct block_list*); + range = va_arg(ap, int); + + if(md->master_id!=master->id) + return 0; + + map_search_freecell(master, 0, &x, &y, range, range, 0); + unit_warp(&md->bl, master->m, x, y,2); + return 1; +} + +/*========================================== + * Added by RoVeRT + * Warps slaves. Range is the area around the master that they can + * appear in randomly. + *------------------------------------------ + */ +int mob_warpslave(struct block_list *bl, int range) +{ + if (range < 1) + range = 1; //Min range needed to avoid crashes and stuff. [Skotlex] + + return map_foreachinmap(mob_warpslave_sub, bl->m, BL_MOB, bl, range); +} + +/*========================================== + * 画面内の取り巻きの数計算用(foreachinarea) + *------------------------------------------ + */ +int mob_countslave_sub(struct block_list *bl,va_list ap) +{ + int id; + struct mob_data *md; + id=va_arg(ap,int); + + md = (struct mob_data *)bl; + if( md->master_id==id ) + return 1; + return 0; +} + +/*========================================== + * 画面内の取り巻きの数計算 + *------------------------------------------ + */ +int mob_countslave(struct block_list *bl) +{ + return map_foreachinmap(mob_countslave_sub, bl->m, BL_MOB,bl->id); +} +/*========================================== + * Summons amount slaves contained in the value[5] array using round-robin. [adapted by Skotlex] + *------------------------------------------ + */ +int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id) +{ + struct mob_data *md; + struct spawn_data data; + int count = 0,k=0,hp_rate=0; + + nullpo_retr(0, md2); + nullpo_retr(0, value); + + memset(&data, 0, sizeof(struct spawn_data)); + data.m = md2->bl.m; + data.x = md2->bl.x; + data.y = md2->bl.y; + data.num = 1; + data.state.size = md2->special_state.size; + data.state.ai = md2->special_state.ai; + + if(mobdb_checkid(value[0]) == 0) + return 0; + + while(count < 5 && mobdb_checkid(value[count])) count++; + if(count < 1) return 0; + if (amount > 0 && amount < count) { //Do not start on 0, pick some random sub subset [Skotlex] + k = rand()%count; + amount+=k; //Increase final value by same amount to preserve total number to summon. + } + + if (!battle_config.monster_class_change_full_recover && (skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS)) + hp_rate = 100*md2->status.hp/md2->status.max_hp; + + for(;kbl, 0, &x, &y, 4, 4, 0)) { + data.x = x; + data.y = y; + } else { + data.x = md2->bl.x; + data.y = md2->bl.y; + } + strcpy(data.name, "--ja--"); //These two need to be loaded from the db for each slave. + data.level = 0; + if (!mob_parse_dataset(&data)) + continue; + + md= mob_spawn_dataset(&data); + md->special_state.cached= battle_config.dynamic_mobs; //[Skotlex] + if(skill_id == NPC_SUMMONSLAVE) + md->master_id=md2->bl.id; + mob_spawn(md); + + if (hp_rate) //Scale HP + md->status.hp = md->status.max_hp*hp_rate/100; + + //Inherit the aggressive mode of the master. + if (battle_config.slaves_inherit_mode) { + if (md2->status.mode&MD_AGGRESSIVE) + sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 0); + else + sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, 0, MD_AGGRESSIVE, 0); + } + + clif_skill_nodamage(&md->bl,&md->bl,skill_id,amount,1); + } + return 0; +} + +/*========================================== + *MOBskillから該当skillidのskillidxを返す + *------------------------------------------ + */ +int mob_skillid2skillidx(int class_,int skillid) +{ + int i, max = mob_db(class_)->maxskill; + struct mob_skill *ms=mob_db(class_)->skill; + + if(ms==NULL) + return -1; + + for(i=0;ibl.id == bl->id && !(battle_config.mob_ai&16)) + return 0; + + if ((*fr) != NULL) //A friend was already found. + return 0; + + if (battle_check_target(&md->bl,bl,BCT_ENEMY)>0) + return 0; + + rate = 100*status_get_hp(bl)/status_get_max_hp(bl); + + if (rate >= min_rate && rate <= max_rate) + (*fr) = bl; + return 1; +} +static struct block_list *mob_getfriendhprate(struct mob_data *md,int min_rate,int max_rate) +{ + struct block_list *fr=NULL; + int type = BL_MOB; + + nullpo_retr(NULL, md); + + if (md->special_state.ai) //Summoned creatures. [Skotlex] + type = BL_PC; + + map_foreachinrange(mob_getfriendhprate_sub, &md->bl, 8, type,md,min_rate,max_rate,&fr); + return fr; +} +/*========================================== + * Check hp rate of its master + *------------------------------------------ + */ +struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md,int rate) +{ + if (md && md->master_id > 0) { + struct block_list *bl = map_id2bl(md->master_id); + if (status_get_hp(bl) < status_get_max_hp(bl) * rate / 100) + return bl; + } + + return NULL; +} +/*========================================== + * What a status state suits by nearby MOB is looked for. + *------------------------------------------ + */ +int mob_getfriendstatus_sub(struct block_list *bl,va_list ap) +{ + int cond1,cond2; + struct mob_data **fr, *md, *mmd; + int flag=0; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md=(struct mob_data *)bl); + nullpo_retr(0, mmd=va_arg(ap,struct mob_data *)); + + if( mmd->bl.id == bl->id && !(battle_config.mob_ai&16) ) + return 0; + + if (battle_check_target(&mmd->bl,bl,BCT_ENEMY)>0) + return 0; + cond1=va_arg(ap,int); + cond2=va_arg(ap,int); + fr=va_arg(ap,struct mob_data **); + if( cond2==-1 ){ + int j; + for(j=SC_COMMON_MIN;j<=SC_COMMON_MAX && !flag;j++){ + if ((flag=(md->sc.data[j].timer!=-1))) //Once an effect was found, break out. [Skotlex] + break; + } + }else + flag=( md->sc.data[cond2].timer!=-1 ); + if( flag^( cond1==MSC_FRIENDSTATUSOFF ) ) + (*fr)=md; + + return 0; +} +struct mob_data *mob_getfriendstatus(struct mob_data *md,int cond1,int cond2) +{ + struct mob_data *fr=NULL; + + nullpo_retr(0, md); + + map_foreachinrange(mob_getfriendstatus_sub, &md->bl, 8, + BL_MOB,md,cond1,cond2,&fr); + return fr; +} + +/*========================================== + * Skill use judging + *------------------------------------------ + */ +int mobskill_use(struct mob_data *md, unsigned int tick, int event) +{ + struct mob_skill *ms; + struct block_list *fbl = NULL; //Friend bl, which can either be a BL_PC or BL_MOB depending on the situation. [Skotlex] + struct mob_data *fmd = NULL; + int i,n; + + nullpo_retr (0, md); + nullpo_retr (0, ms = md->db->skill); + + if (!battle_config.mob_skill_rate || md->ud.skilltimer != -1 || !md->db->maxskill) + return 0; + + if (event < 0 && DIFF_TICK(md->ud.canact_tick, tick) > 0) + return 0; //Skill act delay only affects non-event skills. + + //Pick a random starting position and loop from that. + i = rand()%md->db->maxskill; + for (n = 0; n < md->db->maxskill; i++, n++) { + int c2, flag = 0; + + if (i == md->db->maxskill) + i = 0; + + if (DIFF_TICK(tick, md->skilldelay[i]) < ms[i].delay) + continue; + + c2 = ms[i].cond2; + + if (ms[i].state != md->state.skillstate && md->state.skillstate != MSS_DEAD) { + if (ms[i].state == MSS_ANY || (ms[i].state == MSS_ANYTARGET && md->target_id)) + ; //ANYTARGET works with any state as long as there's a target. [Skotlex] + else + continue; + } + if (rand() % 10000 > ms[i].permillage) //Lupus (max value = 10000) + continue; + + // 条件判定 + flag = (event == ms[i].cond1); + //Avoid entering on defined events to avoid "hyper-active skill use" due to the overflow of calls to this function + //in battle. The only exception is MSC_SKILLUSED which explicitly uses the event value to trigger. [Skotlex] + if (!flag && (event == -1 || event == MSC_SKILLUSED)){ + switch (ms[i].cond1) + { + case MSC_ALWAYS: + flag = 1; break; + case MSC_MYHPLTMAXRATE: // HP< maxhp% + flag = 100*md->status.hp/md->status.max_hp; + flag = (flag <= c2); + break; + case MSC_MYHPINRATE: + flag = 100*md->status.hp/md->status.max_hp; + flag = (flag >= c2 && flag <= ms[i].val[0]); + break; + case MSC_MYSTATUSON: // status[num] on + case MSC_MYSTATUSOFF: // status[num] off + if (!md->sc.count) { + flag = 0; + } else if (ms[i].cond2 == -1) { + int j; + for (j = SC_COMMON_MIN; j <= SC_COMMON_MAX; j++) + if ((flag = (md->sc.data[j].timer != -1)) != 0) + break; + } else { + flag = (md->sc.data[ms[i].cond2].timer != -1); + } + flag ^= (ms[i].cond1 == MSC_MYSTATUSOFF); break; + case MSC_FRIENDHPLTMAXRATE: // friend HP < maxhp% + flag = ((fbl = mob_getfriendhprate(md, 0, ms[i].cond2)) != NULL); break; + case MSC_FRIENDHPINRATE : + flag = ((fbl = mob_getfriendhprate(md, ms[i].cond2, ms[i].val[0])) != NULL); break; + case MSC_FRIENDSTATUSON: // friend status[num] on + case MSC_FRIENDSTATUSOFF: // friend status[num] off + flag = ((fmd = mob_getfriendstatus(md, ms[i].cond1, ms[i].cond2)) != NULL); break; + case MSC_SLAVELT: // slave < num + flag = (mob_countslave(&md->bl) < c2 ); break; + case MSC_ATTACKPCGT: // attack pc > num + flag = (unit_counttargeted(&md->bl, 0) > c2); break; + case MSC_SLAVELE: // slave <= num + flag = (mob_countslave(&md->bl) <= c2 ); break; + case MSC_ATTACKPCGE: // attack pc >= num + flag = (unit_counttargeted(&md->bl, 0) >= c2); break; + case MSC_AFTERSKILL: + flag = (md->ud.skillid == c2); break; + case MSC_SKILLUSED: // specificated skill used + flag = ((event & 0xffff) == MSC_SKILLUSED && ((event >> 16) == c2 || c2 == 0)); break; + case MSC_RUDEATTACKED: + flag = (md->attacked_count >= 3); + if (flag) md->attacked_count = 0; //Rude attacked count should be reset after the skill condition is met. Thanks to Komurka [Skotlex] + break; + case MSC_MASTERHPLTMAXRATE: + flag = ((fbl = mob_getmasterhpltmaxrate(md, ms[i].cond2)) != NULL); break; + case MSC_MASTERATTACKED: + flag = (md->master_id > 0 && unit_counttargeted(map_id2bl(md->master_id), 0) > 0); break; + case MSC_ALCHEMIST: + flag = (md->state.alchemist); + break; + } + } + + if (!flag) + continue; //Skill requisite failed to be fulfilled. + + //Execute skill + if (skill_get_casttype(ms[i].skill_id) == CAST_GROUND) + { + struct block_list *bl = NULL; + short x = 0, y = 0; + if (ms[i].target <= MST_AROUND) { + switch (ms[i].target) { + case MST_TARGET: + case MST_AROUND5: + case MST_AROUND6: + case MST_AROUND7: + case MST_AROUND8: + bl = map_id2bl(md->target_id); + break; + case MST_MASTER: + bl = &md->bl; + if (md->master_id) + bl = map_id2bl(md->master_id); + if (bl) //Otherwise, fall through. + break; + case MST_FRIEND: + if (fbl) + { + bl = fbl; + break; + } else if (fmd) { + bl= &fmd->bl; + break; + } // else fall through + default: + bl = &md->bl; + break; + } + if (bl != NULL) { + x = bl->x; y=bl->y; + } + } + if (x <= 0 || y <= 0) + continue; + // Look for an area to cast the spell around... + if (ms[i].target >= MST_AROUND1 || ms[i].target >= MST_AROUND5) { + int r = ms[i].target >= MST_AROUND1? + (ms[i].target-MST_AROUND1) +1: + (ms[i].target-MST_AROUND5) +1; + map_search_freecell(&md->bl, md->bl.m, &x, &y, r, r, 3); + } + md->skillidx = i; + flag = unit_skilluse_pos2(&md->bl, x, y, ms[i].skill_id, ms[i].skill_lv, + skill_castfix_sc(&md->bl, ms[i].casttime), ms[i].cancel); + if (!flag) md->skillidx = -1; //Skill failed. + return flag; + } else { + if (ms[i].target <= MST_MASTER) { + struct block_list *bl; + switch (ms[i].target) { + case MST_TARGET: + bl = map_id2bl(md->target_id); + break; + case MST_MASTER: + bl = &md->bl; + if (md->master_id) + bl = map_id2bl(md->master_id); + if (bl) //Otherwise, fall through. + break; + case MST_FRIEND: + if (fbl) { + bl = fbl; + break; + } else if (fmd) { + bl = &fmd->bl; + break; + } // else fall through + default: + bl = &md->bl; + break; + } + md->skillidx = i; + flag = (bl && unit_skilluse_id2(&md->bl, bl->id, ms[i].skill_id, ms[i].skill_lv, + skill_castfix_sc(&md->bl,ms[i].casttime), ms[i].cancel)); + if (!flag) md->skillidx = -1; + return flag; + } else { + if (battle_config.error_log) + ShowWarning("Wrong mob skill target 'around' for non-ground skill %d (%s). Mob %d - %s\n", + ms[i].skill_id, skill_get_name(ms[i].skill_id), md->class_, md->db->sprite); + continue; + } + } + return 1; + } + + return 0; +} +/*========================================== + * Skill use event processing + *------------------------------------------ + */ +int mobskill_event(struct mob_data *md, struct block_list *src, unsigned int tick, int flag) +{ + int target_id, res = 0; + + target_id = md->target_id; + if (!target_id || battle_config.mob_changetarget_byskill) + md->target_id = src->id; + + if (flag == -1) + res = mobskill_use(md, tick, MSC_CASTTARGETED); + else if ((flag&0xffff) == MSC_SKILLUSED) + res = mobskill_use(md,tick,flag); + else if (flag&BF_SHORT) + res = mobskill_use(md, tick, MSC_CLOSEDATTACKED); + else if (flag&BF_LONG) + res = mobskill_use(md, tick, MSC_LONGRANGEATTACKED); + + if (!res) + //Restore previous target only if skill condition failed to trigger. [Skotlex] + md->target_id = target_id; + //Otherwise check if the target is an enemy, and unlock if needed. + else if (battle_check_target(&md->bl, src, BCT_ENEMY) <= 0) + md->target_id = target_id; + + return res; +} + +// Player cloned mobs. [Valaris] +int mob_is_clone(int class_) +{ + if(class_ < MOB_CLONE_START || class_ > MOB_CLONE_END) + return 0; + if (mob_db(class_) == mob_dummy) + return 0; + return class_; +} + +//Flag values: +//&1: Set special ai (fight mobs, not players) +//If mode is not passed, a default aggressive mode is used. +//If master_id is passed, clone is attached to him. +//Returns: ID of newly crafted copy. +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 class_; + int i,j,inf,skill_id; + struct mob_data *md; + struct mob_skill *ms; + + nullpo_retr(0, sd); + + for(class_=MOB_CLONE_START; class_MOB_CLONE_END) + return 0; + + mob_db_data[class_]=(struct mob_db*)aCalloc(1, sizeof(struct mob_db)); + sprintf(mob_db_data[class_]->sprite,sd->status.name); + sprintf(mob_db_data[class_]->name,sd->status.name); + sprintf(mob_db_data[class_]->jname,sd->status.name); + mob_db_data[class_]->lv=status_get_lv(&sd->bl); + memcpy(&mob_db_data[class_]->status, &sd->base_status, sizeof(struct status_data)); + mob_db_data[class_]->status.lhw = NULL; //Prevent dangling pointer if player quits. + mob_db_data[class_]->status.rhw.atk = + mob_db_data[class_]->status.rhw.atk2 = sd->base_status.dex; //Min ATK + mob_db_data[class_]->status.rhw.atk2+= + sd->base_status.rhw.atk + sd->base_status.rhw.atk2 + + sd->base_status.lhw->atk + sd->base_status.lhw->atk2; //Max ATK + if (flag&1) //Friendly Character, remove looting. + mob_db_data[class_]->status.mode &= ~MD_LOOTER; + mob_db_data[class_]->status.hp = mob_db_data[class_]->status.max_hp; + mob_db_data[class_]->status.sp = mob_db_data[class_]->status.max_sp; + memcpy(&mob_db_data[class_]->vd, &sd->vd, sizeof(struct view_data)); + mob_db_data[class_]->base_exp=1; + mob_db_data[class_]->job_exp=1; + mob_db_data[class_]->range2=AREA_SIZE; //Let them have the same view-range as players. + mob_db_data[class_]->range3=AREA_SIZE; //Min chase of a screen. + mob_db_data[class_]->option=sd->sc.option; + + //Skill copy [Skotlex] + ms = &mob_db_data[class_]->skill[0]; + //Go Backwards to give better priority to advanced skills. + for (i=0,j = MAX_SKILL_TREE-1;j>=0 && i< MAX_MOBSKILL ;j--) { + skill_id = skill_tree[sd->status.class_][j].id; + if (!skill_id || sd->status.skill[skill_id].lv < 1 || (skill_get_inf2(skill_id)&(INF2_WEDDING_SKILL|INF2_GUILD_SKILL))) + continue; + memset (&ms[i], 0, sizeof(struct mob_skill)); + ms[i].skill_id = skill_id; + ms[i].skill_lv = sd->status.skill[skill_id].lv; + ms[i].state = MSS_ANY; + ms[i].permillage = 500*battle_config.mob_skill_rate/100; //Default chance of all skills: 5% + ms[i].emotion = -1; + ms[i].cancel = 0; + ms[i].delay = 5000+skill_delayfix(&sd->bl,skill_id, ms[i].skill_lv); + ms[i].casttime = skill_castfix(&sd->bl,skill_id, ms[i].skill_lv); + + inf = skill_get_inf(skill_id); + if (inf&INF_ATTACK_SKILL) { + ms[i].target = MST_TARGET; + ms[i].cond1 = MSC_ALWAYS; + if (skill_get_range(skill_id, ms[i].skill_lv) > 3) + ms[i].state = MSS_ANYTARGET; + else + ms[i].state = MSS_BERSERK; + } else if(inf&INF_GROUND_SKILL) { + //Normal aggressive mob, disable skills that cannot help them fight + //against players (those with flags UF_NOMOB and UF_NOPC are specific + //to always aid players!) [Skotlex] + if (!(flag&1) && skill_get_unit_flag(skill_id)&(UF_NOMOB|UF_NOPC)) + continue; + if (skill_get_inf2(skill_id)&INF2_TRAP) { //Traps! + ms[i].state = MSS_IDLE; + ms[i].target = MST_AROUND2; + ms[i].delay = 60000; + } else if (skill_get_unit_target(skill_id) == BCT_ENEMY) { //Target Enemy + ms[i].state = MSS_ANYTARGET; + ms[i].target = MST_TARGET; + ms[i].cond1 = MSC_ALWAYS; + } else { //Target allies + ms[i].target = MST_FRIEND; + ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; + ms[i].cond2 = 95; + } + } else if (inf&INF_SELF_SKILL) { + if (skill_get_inf2(skill_id)&INF2_NO_TARGET_SELF) { //auto-select target skill. + ms[i].target = MST_TARGET; + ms[i].cond1 = MSC_ALWAYS; + if (skill_get_range(skill_id, ms[i].skill_lv) > 3) { + ms[i].state = MSS_ANYTARGET; + } else { + ms[i].state = MSS_BERSERK; + } + } else { //Self skill + ms[i].target = MST_SELF; + ms[i].cond1 = MSC_MYHPLTMAXRATE; + ms[i].cond2 = 90; + ms[i].permillage = 2000; + //Delay: Remove the stock 5 secs and add half of the support time. + ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2; + if (ms[i].delay < 5000) + ms[i].delay = 5000; //With a minimum of 5 secs. + } + } else if (inf&INF_SUPPORT_SKILL) { + ms[i].target = MST_FRIEND; + ms[i].cond1 = MSC_FRIENDHPLTMAXRATE; + ms[i].cond2 = 90; + if (skill_id == AL_HEAL) + ms[i].permillage = 5000; //Higher skill rate usage for heal. + else if (skill_id == ALL_RESURRECTION) + ms[i].cond2 = 1; + //Delay: Remove the stock 5 secs and add half of the support time. + ms[i].delay += -5000 +(skill_get_time(skill_id, ms[i].skill_lv) + skill_get_time2(skill_id, ms[i].skill_lv))/2; + if (ms[i].delay < 2000) + ms[i].delay = 2000; //With a minimum of 2 secs. + + if (i+1 < MAX_MOBSKILL) { //duplicate this so it also triggers on self. + memcpy(&ms[i+1], &ms[i], sizeof(struct mob_skill)); + mob_db_data[class_]->maxskill = ++i; + ms[i].target = MST_SELF; + ms[i].cond1 = MSC_MYHPLTMAXRATE; + } + } else { + switch (skill_id) { //Certain Special skills that are passive, and thus, never triggered. + case MO_TRIPLEATTACK: + case TF_DOUBLE: + ms[i].state = MSS_BERSERK; + ms[i].target = MST_TARGET; + ms[i].cond1 = MSC_ALWAYS; + ms[i].permillage = skill_id==TF_DOUBLE?(ms[i].skill_lv*500):(3000-ms[i].skill_lv*100); + ms[i].delay -= 5000; //Remove the added delay as these could trigger on "all hits". + break; + default: //Untreated Skill + continue; + } + } + if (battle_config.mob_skill_rate!= 100) + ms[i].permillage = ms[i].permillage*battle_config.mob_skill_rate/100; + if (battle_config.mob_skill_delay != 100) + ms[i].delay = ms[i].delay*battle_config.mob_skill_delay/100; + + mob_db_data[class_]->maxskill = ++i; + } + //Finally, spawn it. + md = mob_once_spawn_sub(&sd->bl, m, x, y, "--en--",class_,event); + if (!md) return 0; //Failed? + + if (master_id || flag || duration) { //Further manipulate crafted char. + if (flag&1) //Friendly Character + md->special_state.ai = 1; + if (master_id) //Attach to Master + md->master_id = master_id; + if (duration) //Auto Delete after a while. + md->deletetimer = add_timer (gettick() + duration, mob_timer_delete, i, 0); + } +#if 0 + //I am playing with this for packet-research purposes, enable it if you want, but don't remove it :X [Skotlex] + //Guardian data + if (sd->status.guild_id) { + struct guild* g = guild_search(sd->status.guild_id); + md->guardian_data = aCalloc(1, sizeof(struct guardian_data)); + md->guardian_data->castle = NULL; + md->guardian_data->number = MAX_GUARDIANS; + md->guardian_data->guild_id = sd->status.guild_id; + if (g) + { + md->guardian_data->emblem_id = g->emblem_id; + memcpy(md->guardian_data->guild_name, g->name, NAME_LENGTH); + } + } +#endif + mob_spawn(md); + + return md->bl.id; +} + +int mob_clone_delete(int class_) +{ + if (class_ >= MOB_CLONE_START && class_ < MOB_CLONE_END + && mob_db_data[class_]!=NULL) { + aFree(mob_db_data[class_]); + mob_db_data[class_]=NULL; + return 1; + } + return 0; +} + +// +// 初期化 +// +/*========================================== + * Since un-setting [ mob ] up was used, it is an initial provisional value setup. + *------------------------------------------ + */ +static int mob_makedummymobdb(int class_) +{ + if (mob_dummy != NULL) + { + if (mob_db(class_) == mob_dummy) + return 1; //Using the mob_dummy data already. [Skotlex] + if (class_ > 0 && class_ <= MAX_MOB_DB) + { //Remove the mob data so that it uses the dummy data instead. + aFree(mob_db_data[class_]); + mob_db_data[class_] = NULL; + } + return 0; + } + //Initialize dummy data. + mob_dummy = (struct mob_db*)aCalloc(1, sizeof(struct mob_db)); //Initializing the dummy mob. + sprintf(mob_dummy->sprite,"DUMMY"); + sprintf(mob_dummy->name,"Dummy"); + sprintf(mob_dummy->jname,"Dummy"); + mob_dummy->lv=1; + mob_dummy->status.max_hp=1000; + mob_dummy->status.max_sp=1; + mob_dummy->status.rhw.range=1; + mob_dummy->status.rhw.atk=7; + mob_dummy->status.rhw.atk2=10; + mob_dummy->status.str=1; + mob_dummy->status.agi=1; + mob_dummy->status.vit=1; + mob_dummy->status.int_=1; + mob_dummy->status.dex=6; + mob_dummy->status.luk=2; + mob_dummy->status.speed=300; + mob_dummy->status.adelay=1000; + mob_dummy->status.amotion=500; + mob_dummy->status.dmotion=500; + mob_dummy->base_exp=2; + mob_dummy->job_exp=1; + mob_dummy->range2=10; + mob_dummy->range3=10; + + return 0; +} + +//Adjusts the drop rate of item according to the criteria given. [Skotlex] +static unsigned int mob_drop_adjust(unsigned int rate, int rate_adjust, unsigned short rate_min, unsigned short rate_max) +{ + if (battle_config.logarithmic_drops && rate_adjust > 0) //Logarithmic drops equation by Ishizu-Chan + //Equation: Droprate(x,y) = x * (5 - log(x)) ^ (ln(y) / ln(5)) + //x is the normal Droprate, y is the Modificator. + rate = (int)(rate * pow((5.0 - log10(rate)), (log(rate_adjust/100.) / log(5.0))) + 0.5); + else //Classical linear rate adjustment. + rate = rate*rate_adjust/100; + return (rate>rate_max)?rate_max:((rate0) + continue; + return -1; + } + while(fgets(line,1020,fp)){ + double exp, maxhp; + char *str[38+2*MAX_MOB_DROP], *p, *np; + + if(line[0] == '/' && line[1] == '/') + continue; + + for(i=0,p=line;i<38+2*MAX_MOB_DROP;i++){ + if((np=strchr(p,','))!=NULL){ + str[i]=p; + *np=0; + p=np+1; + } else + str[i]=p; + } + + class_ = atoi(str[0]); + if (class_ == 0) + continue; //Leave blank lines alone... [Skotlex] + + if (class_ <= 1000 || class_ > MAX_MOB_DB) + { + ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB); + continue; + } else if (pcdb_checkid(class_)) + { + ShowWarning("Mob with ID: %d not loaded. That ID is reserved for player classes.\n"); + continue; + } + if (mob_db_data[class_] == NULL) + mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data)); + + mob_db_data[class_]->vd.class_ = class_; + memcpy(mob_db_data[class_]->sprite, str[1], NAME_LENGTH-1); + memcpy(mob_db_data[class_]->jname, str[2], NAME_LENGTH-1); + memcpy(mob_db_data[class_]->name, str[3], NAME_LENGTH-1); + mob_db_data[class_]->lv = atoi(str[4]); + status = &mob_db_data[class_]->status; + + status->max_hp = atoi(str[5]); + status->max_sp = atoi(str[6]); + + exp = (double)atoi(str[7]) * (double)battle_config.base_exp_rate / 100.; + if (exp < 0) + mob_db_data[class_]->base_exp = 0; + if (exp > UINT_MAX) + mob_db_data[class_]->base_exp = UINT_MAX; + else + mob_db_data[class_]->base_exp = (unsigned int)exp; + + exp = (double)atoi(str[8]) * (double)battle_config.job_exp_rate / 100.; + if (exp < 0) + mob_db_data[class_]->job_exp = 0; + else if (exp > UINT_MAX) + mob_db_data[class_]->job_exp = UINT_MAX; + else + mob_db_data[class_]->job_exp = (unsigned int)exp; + + status->rhw.range=atoi(str[9]); + status->rhw.atk=atoi(str[10]); + status->rhw.atk2=atoi(str[11]); + status->def=atoi(str[12]); + status->mdef=atoi(str[13]); + status->str=atoi(str[14]); + status->agi=atoi(str[15]); + status->vit=atoi(str[16]); + status->int_=atoi(str[17]); + status->dex=atoi(str[18]); + status->luk=atoi(str[19]); + mob_db_data[class_]->range2=atoi(str[20]); + mob_db_data[class_]->range3=atoi(str[21]); + if (battle_config.view_range_rate!=100) + { + mob_db_data[class_]->range2= + mob_db_data[class_]->range2 + *battle_config.view_range_rate/100; + if (mob_db_data[class_]->range2<1) + mob_db_data[class_]->range2=1; + } + if (battle_config.chase_range_rate!=100) + { + mob_db_data[class_]->range3= + mob_db_data[class_]->range3 + *battle_config.chase_range_rate/100; + if (mob_db_data[class_]->range3range2) + mob_db_data[class_]->range3=mob_db_data[class_]->range2; + } + status->size=atoi(str[22]); + status->race=atoi(str[23]); + i = atoi(str[24]); //Element + status->def_ele = i%10; + status->ele_lv = i/20; + status->mode=atoi(str[25]); + status->speed=atoi(str[26]); + status->aspd_rate = 100; + status->adelay=atoi(str[27]); + status->amotion=atoi(str[28]); + status->dmotion=atoi(str[29]); + if(battle_config.monster_damage_delay_rate != 100) + status->dmotion = status->dmotion*battle_config.monster_damage_delay_rate/100; + + status_calc_misc(status, mob_db_data[class_]->lv); + + if(!battle_config.enemy_str) + status->batk = 0; + + if(battle_config.enemy_critical_rate != 100) + status->cri = status->cri*battle_config.enemy_critical_rate/100; + if(!status->cri && battle_config.enemy_critical_rate) status->cri = 1; + + if(!battle_config.enemy_perfect_flee) + status->flee2 = 0; + + // MVP EXP Bonus, Chance: MEXP,ExpPer + mob_db_data[class_]->mexp=atoi(str[30])*battle_config.mvp_exp_rate/100; + mob_db_data[class_]->mexpper=atoi(str[31]); + //Now that we know if it is an mvp or not, + //apply battle_config modifiers [Skotlex] + maxhp = (double)status->max_hp; + if (mob_db_data[class_]->mexp > 0) + { //Mvp + if (battle_config.mvp_hp_rate != 100) + maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.; + } else if (battle_config.monster_hp_rate != 100) //Normal mob + maxhp = maxhp * (double)battle_config.monster_hp_rate /100.; + if (maxhp < 1) maxhp = 1; + else if (maxhp > UINT_MAX) maxhp = UINT_MAX; + status->max_hp = (unsigned int)maxhp; + + status->hp = status->max_hp; + status->sp = status->max_sp; + + // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per + for(i=0;i<3;i++){ + struct item_data *id; + mob_db_data[class_]->mvpitem[i].nameid=atoi(str[32+i*2]); + if (!mob_db_data[class_]->mvpitem[i].nameid) { + //No item.... + mob_db_data[class_]->mvpitem[i].p = 0; + continue; + } + mob_db_data[class_]->mvpitem[i].p= mob_drop_adjust(atoi(str[33+i*2]), battle_config.item_rate_mvp, + battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max); + + //calculate and store Max available drop chance of the MVP item + if (mob_db_data[class_]->mvpitem[i].p) { + id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid); + if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) { + //item has bigger drop chance or sold in shops + id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate + } + } + } + + for(i=0;idropitem[i].nameid=atoi(str[k]); + if (!mob_db_data[class_]->dropitem[i].nameid) { + //No drop. + mob_db_data[class_]->dropitem[i].p = 0; + continue; + } + type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid); + rate = atoi(str[k+1]); + if (class_ >= 1324 && class_ <= 1363) + { //Treasure box drop rates [Skotlex] + rate_adjust = battle_config.item_rate_treasure; + ratemin = battle_config.item_drop_treasure_min; + ratemax = battle_config.item_drop_treasure_max; + } + else switch (type) // Added suport to restrict normal drops of MVP's [Reddozen] + { + case 0: + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_heal_boss; + else + rate_adjust = battle_config.item_rate_heal; + ratemin = battle_config.item_drop_heal_min; + ratemax = battle_config.item_drop_heal_max; + break; + case 2: + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_use_boss; + else + rate_adjust = battle_config.item_rate_use; + ratemin = battle_config.item_drop_use_min; + ratemax = battle_config.item_drop_use_max; + break; + case 4: + case 5: + case 8: // Changed to include Pet Equip + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_equip_boss; + else + rate_adjust = battle_config.item_rate_equip; + ratemin = battle_config.item_drop_equip_min; + ratemax = battle_config.item_drop_equip_max; + break; + case 6: + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_card_boss; + else + rate_adjust = battle_config.item_rate_card; + ratemin = battle_config.item_drop_card_min; + ratemax = battle_config.item_drop_card_max; + break; + default: + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_common_boss; + else { + rate_adjust = battle_config.item_rate_common; + } + ratemin = battle_config.item_drop_common_min; + ratemax = battle_config.item_drop_common_max; + break; + } + mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax); + + //calculate and store Max available drop chance of the item + if (mob_db_data[class_]->dropitem[i].p && + (class_ < 1324 || class_ > 1363) //Skip treasure chests. + ) { + id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid); + if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) { + //item has bigger drop chance or sold in shops + id->maxchance = mob_db_data[class_]->dropitem[i].p; + } + for (k = 0; k< MAX_SEARCH; k++) { + if (id->mob[k].chance < mob_db_data[class_]->dropitem[i].p && id->mob[k].id != class_) + break; + } + if (k == MAX_SEARCH) + continue; + + memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0])); + id->mob[k].chance = mob_db_data[class_]->dropitem[i].p; + id->mob[k].id = class_; + } + } + + if (status->max_hp <= 0) { + ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->sprite); + mob_makedummymobdb(class_); + } + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[fi]); + } + return 0; +} + +/*========================================== + * MOB display graphic change data reading + *------------------------------------------ + */ +static int mob_readdb_mobavail(void) +{ + FILE *fp; + char line[1024]; + int ln=0; + int class_,j,k; + char *str[20],*p,*np; + + sprintf(line, "%s/mob_avail.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; + memset(str,0,sizeof(str)); + + for(j=0,p=line;j<12;j++){ + if((np=strchr(p,','))!=NULL){ + str[j]=p; + *np=0; + p=np+1; + } else + str[j]=p; + } + + if(str[0]==NULL) + continue; + + class_=atoi(str[0]); + if (class_ == 0) + continue; //Leave blank lines alone... [Skotlex] + + if(mob_db(class_) == mob_dummy) // 値が異常なら処理しない。 + continue; + + k=atoi(str[1]); + if(k < 0) + continue; + + memset(&mob_db_data[class_]->vd, 0, sizeof(struct view_data)); + mob_db_data[class_]->vd.class_=k; + + //Player sprites + if(pcdb_checkid(k) && j>=12) { + mob_db_data[class_]->vd.sex=atoi(str[2]); + mob_db_data[class_]->vd.hair_style=atoi(str[3]); + mob_db_data[class_]->vd.hair_color=atoi(str[4]); + mob_db_data[class_]->vd.weapon=atoi(str[5]); + mob_db_data[class_]->vd.shield=atoi(str[6]); + mob_db_data[class_]->vd.head_top=atoi(str[7]); + mob_db_data[class_]->vd.head_mid=atoi(str[8]); + mob_db_data[class_]->vd.head_bottom=atoi(str[9]); + mob_db_data[class_]->option=atoi(str[10])&~0x46; + mob_db_data[class_]->vd.cloth_color=atoi(str[11]); // Monster player dye option - Valaris + } + else if(str[2] && atoi(str[2]) > 0) + mob_db_data[class_]->vd.head_bottom=atoi(str[2]); // mob equipment [Valaris] + + ln++; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",ln,"mob_avail.txt"); + return 0; +} + +/*========================================== + * Reading of random monster data + *------------------------------------------ + */ +static int mob_read_randommonster(void) +{ + FILE *fp; + char line[1024]; + char *str[10],*p; + int i,j; + + const char* mobfile[] = { + "mob_branch.txt", + "mob_poring.txt", + "mob_boss.txt" }; + + for(i=0;isummonper[i] = 1002; // 設定し忘れた場合はポリンが出るようにしておく + sprintf(line, "%s/%s", db_path, mobfile[i]); + fp=fopen(line,"r"); + if(fp==NULL){ + ShowError("can't read %s\n",line); + return -1; + } + while(fgets(line,1020,fp)){ + int class_,per; + if(line[0] == '/' && line[1] == '/') + continue; + memset(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 || str[2]==NULL) + continue; + + class_ = atoi(str[0]); + per=atoi(str[2]); + if(mob_db(class_) != mob_dummy) + mob_db_data[class_]->summonper[i]=per; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",mobfile[i]); + } + return 0; +} + +/*========================================== + * mob_skill_db.txt reading + *------------------------------------------ + */ +static int mob_readskilldb(void) +{ + FILE *fp; + char line[1024]; + int i,tmp, count; + + const struct { + char str[32]; + int id; + } cond1[] = { + { "always", MSC_ALWAYS }, + { "myhpltmaxrate", MSC_MYHPLTMAXRATE }, + { "myhpinrate", MSC_MYHPINRATE }, + { "friendhpltmaxrate",MSC_FRIENDHPLTMAXRATE }, + { "friendhpinrate", MSC_FRIENDHPINRATE }, + { "mystatuson", MSC_MYSTATUSON }, + { "mystatusoff", MSC_MYSTATUSOFF }, + { "friendstatuson", MSC_FRIENDSTATUSON }, + { "friendstatusoff", MSC_FRIENDSTATUSOFF }, + { "attackpcgt", MSC_ATTACKPCGT }, + { "attackpcge", MSC_ATTACKPCGE }, + { "slavelt", MSC_SLAVELT }, + { "slavele", MSC_SLAVELE }, + { "closedattacked", MSC_CLOSEDATTACKED }, + { "longrangeattacked",MSC_LONGRANGEATTACKED }, + { "skillused", MSC_SKILLUSED }, + { "afterskill", MSC_AFTERSKILL }, + { "casttargeted", MSC_CASTTARGETED }, + { "rudeattacked", MSC_RUDEATTACKED }, + { "masterhpltmaxrate",MSC_MASTERHPLTMAXRATE }, + { "masterattacked", MSC_MASTERATTACKED }, + { "alchemist", MSC_ALCHEMIST }, + { "onspawn", MSC_SPAWN}, + }, cond2[] ={ + { "anybad", -1 }, + { "stone", SC_STONE }, + { "freeze", SC_FREEZE }, + { "stan", SC_STUN }, + { "sleep", SC_SLEEP }, + { "poison", SC_POISON }, + { "curse", SC_CURSE }, + { "silence", SC_SILENCE }, + { "confusion", SC_CONFUSION }, + { "blind", SC_BLIND }, + { "hiding", SC_HIDING }, + { "sight", SC_SIGHT }, + }, state[] = { + { "any", MSS_ANY }, //All states except Dead + { "idle", MSS_IDLE }, + { "walk", MSS_WALK }, + { "loot", MSS_LOOT }, + { "dead", MSS_DEAD }, + { "attack", MSS_BERSERK }, //Retaliating attack + { "angry", MSS_ANGRY }, //Preemptive attack (aggressive mobs) + { "chase", MSS_RUSH }, //Chase escaping target + { "follow", MSS_FOLLOW }, //Preemptive chase (aggressive mobs) + { "anytarget",MSS_ANYTARGET }, //Berserk+Angry+Rush+Follow + }, target[] = { + { "target", MST_TARGET }, + { "self", MST_SELF }, + { "friend", MST_FRIEND }, + { "master", MST_MASTER }, + { "around5", MST_AROUND5 }, + { "around6", MST_AROUND6 }, + { "around7", MST_AROUND7 }, + { "around8", MST_AROUND8 }, + { "around1", MST_AROUND1 }, + { "around2", MST_AROUND2 }, + { "around3", MST_AROUND3 }, + { "around4", MST_AROUND4 }, + { "around", MST_AROUND }, + }; + + int x; + char *filename[]={ "mob_skill_db.txt","mob_skill_db2.txt" }; + + if (!battle_config.mob_skill_rate) { + ShowStatus("Mob skill use disabled. Not reading mob skills.\n"); + return 0; + } + for(x=0;x<2;x++){ + int last_mob_id = 0; + count = 0; + sprintf(line, "%s/%s", db_path, filename[x]); + fp=fopen(line,"r"); + if(fp==NULL){ + if(x==0) + ShowError("can't read %s\n",line); + continue; + } + while(fgets(line,1020,fp)){ + char *sp[20],*p; + int mob_id; + struct mob_skill *ms, gms; + int j=0; + + count++; + if(line[0] == '/' && line[1] == '/') + continue; + + memset(sp,0,sizeof(sp)); + for(i=0,p=line;i<18 && p;i++){ + sp[i]=p; + if((p=strchr(p,','))!=NULL) + *p++=0; + } + if(i == 0 || (mob_id=atoi(sp[0]))== 0) + continue; + if(i < 18) { + ShowError("mob_skill: Insufficient number of fields for skill at %s, line %d\n", filename[x], count); + continue; + } + if (mob_id > 0 && mob_db(mob_id) == mob_dummy) + { + if (mob_id != last_mob_id) { + ShowWarning("mob_skill: Non existant Mob id %d at %s, line %d\n", mob_id, filename[x], count); + last_mob_id = mob_id; + } + continue; + } + if( strcmp(sp[1],"clear")==0 ){ + if (mob_id < 0) + continue; + memset(mob_db_data[mob_id]->skill,0,sizeof(struct mob_skill)); + mob_db_data[mob_id]->maxskill=0; + continue; + } + + if (mob_id < 0) + { //Prepare global skill. [Skotlex] + memset(&gms, 0, sizeof (struct mob_skill)); + ms = &gms; + } else { + for(i=0;iskill[i])->skill_id == 0) + break; + if(i==MAX_MOBSKILL){ + if (mob_id != last_mob_id) { + ShowWarning("mob_skill: readdb: too many skill! Line %d in %d[%s]\n", + count,mob_id,mob_db_data[mob_id]->sprite); + last_mob_id = mob_id; + } + continue; + } + } + + ms->state=atoi(sp[2]); + tmp = sizeof(state)/sizeof(state[0]); + for(j=0;jstate=state[j].id; + else + ShowError("mob_skill: Unrecognized state %s at %s, line %d\n", sp[2], filename[x], count); + + //Skill ID + j=atoi(sp[3]); + if (j<=0 || j>MAX_SKILL_DB) //fixed Lupus + { + if (mob_id < 0) + ShowWarning("Invalid Skill ID (%d) for all mobs\n", j); + else + ShowWarning("Invalid Skill ID (%d) for mob %d (%s)\n", j, mob_id, mob_db_data[mob_id]->sprite); + continue; + } + ms->skill_id=j; + //Skill lvl + j= atoi(sp[4])<=0 ? 1 : atoi(sp[4]); + ms->skill_lv= j>battle_config.mob_max_skilllvl ? battle_config.mob_max_skilllvl : j; //we strip max skill level + + //Apply battle_config modifiers to rate (permillage) and delay [Skotlex] + tmp = atoi(sp[5]); + if (battle_config.mob_skill_rate != 100) + tmp = tmp*battle_config.mob_skill_rate/100; + if (tmp > 10000) + ms->permillage= 10000; + else + ms->permillage= tmp; + ms->casttime=atoi(sp[6]); + ms->delay=atoi(sp[7]); + if (battle_config.mob_skill_delay != 100) + ms->delay = ms->delay*battle_config.mob_skill_delay/100; + if (ms->delay < 0) //time overflow? + ms->delay = INT_MAX; + ms->cancel=atoi(sp[8]); + if( strcmp(sp[8],"yes")==0 ) ms->cancel=1; + ms->target=atoi(sp[9]); + for(j=0;jtarget=target[j].id; + } + ms->cond1=-1; + tmp = sizeof(cond1)/sizeof(cond1[0]); + for(j=0;jcond1=cond1[j].id; + else + ShowError("mob_skill: Unrecognized condition 1 %s at %s, line %d\n", sp[10], filename[x], count); + + ms->cond2=atoi(sp[11]); + tmp = sizeof(cond2)/sizeof(cond2[0]); + for(j=0;jcond2=cond2[j].id; + + ms->val[0]=atoi(sp[12]); + ms->val[1]=atoi(sp[13]); + ms->val[2]=atoi(sp[14]); + ms->val[3]=atoi(sp[15]); + ms->val[4]=atoi(sp[16]); + if(sp[17] != NULL && strlen(sp[17])>2) + ms->emotion=atoi(sp[17]); + else + ms->emotion=-1; + if (mob_id < 0) + { //Set this skill to ALL mobs. [Skotlex] + mob_id *= -1; + for (i = 1; i < MAX_MOB_DB; i++) + { + if (mob_db_data[i] == NULL) + continue; + if (mob_db_data[i]->status.mode&MD_BOSS) + { + if (!(mob_id&2)) //Skill not for bosses + continue; + } else + if (!(mob_id&1)) //Skill not for normal enemies. + continue; + + for(j=0;jskill[j].skill_id == 0) + break; + if(j==MAX_MOBSKILL) + continue; + + memcpy (&mob_db_data[i]->skill[j], ms, sizeof(struct mob_skill)); + mob_db_data[i]->maxskill=j+1; + } + } else //Skill set on a single mob. + mob_db_data[mob_id]->maxskill=i+1; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",filename[x]); + } + return 0; +} +/*========================================== + * mob_race_db.txt reading + *------------------------------------------ + */ +static int mob_readdb_race(void) +{ + FILE *fp; + char line[1024]; + int race,j,k; + char *str[20],*p,*np; + + sprintf(line, "%s/mob_race2_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; + memset(str,0,sizeof(str)); + + for(j=0,p=line;j<12;j++){ + if((np=strchr(p,','))!=NULL){ + str[j]=p; + *np=0; + p=np+1; + } else + str[j]=p; + } + if(str[0]==NULL) + continue; + + race=atoi(str[0]); + if (race < 0 || race >= MAX_MOB_RACE_DB) + continue; + + for (j=1; j<20; j++) { + if (!str[j]) + break; + k=atoi(str[j]); + if (mob_db(k) == mob_dummy) + continue; + mob_db_data[k]->race2 = race; + } + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","mob_race2_db.txt"); + return 0; +} + +#ifndef TXT_ONLY +/*========================================== + * SQL reading + *------------------------------------------ + */ +static int mob_read_sqldb(void) +{ + const char unknown_str[NAME_LENGTH] ="unknown"; + int i, fi, class_, k; + double exp, maxhp; + long unsigned int ln = 0; + struct status_data *status; + char *mob_db_name[] = { mob_db_db, mob_db2_db }; + + //For easier handling of converting. [Skotlex] +#define TO_INT(a) (sql_row[a]==NULL?0:atoi(sql_row[a])) +#define TO_STR(a) (sql_row[a]==NULL?unknown_str:sql_row[a]) + + for (fi = 0; fi < 2; fi++) { + sprintf (tmp_sql, "SELECT * FROM `%s`", mob_db_name[fi]); + if (mysql_query(&mmysql_handle, tmp_sql)) { + ShowSQL("DB error (%s) - %s\n", mob_db_name[fi], mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + continue; + } + sql_res = mysql_store_result(&mmysql_handle); + if (sql_res) { + while((sql_row = mysql_fetch_row(sql_res))){ + class_ = TO_INT(0); + if (class_ <= 1000 || class_ > MAX_MOB_DB) + { + ShowWarning("Mob with ID: %d not loaded. ID must be in range [%d-%d]\n", class_, 1000, MAX_MOB_DB); + continue; + } else if (pcdb_checkid(class_)) + { + ShowWarning("Mob with ID: %d not loaded. That ID is reserved for Upper Classes.\n"); + continue; + } + if (mob_db_data[class_] == NULL) + mob_db_data[class_] = aCalloc(1, sizeof (struct mob_data)); + + ln++; + + mob_db_data[class_]->vd.class_ = class_; + memcpy(mob_db_data[class_]->sprite, TO_STR(1), NAME_LENGTH-1); + memcpy(mob_db_data[class_]->jname, TO_STR(2), NAME_LENGTH-1); + memcpy(mob_db_data[class_]->name, TO_STR(3), NAME_LENGTH-1); + mob_db_data[class_]->lv = TO_INT(4); + status = &mob_db_data[class_]->status; + status->max_hp = TO_INT(5); + status->max_sp = TO_INT(6); + + exp = (double)TO_INT(7) * (double)battle_config.base_exp_rate / 100.; + if (exp < 0) + mob_db_data[class_]->base_exp = 0; + else if (exp > UINT_MAX) + mob_db_data[class_]->base_exp = UINT_MAX; + else + mob_db_data[class_]->base_exp = (unsigned int)exp; + + exp = (double)TO_INT(8) * (double)battle_config.job_exp_rate / 100.; + if (exp < 0) + mob_db_data[class_]->job_exp = 0; + else if (exp > UINT_MAX) + mob_db_data[class_]->job_exp = UINT_MAX; + else + mob_db_data[class_]->job_exp = (unsigned int)exp; + + status->rhw.range = TO_INT(9); + status->rhw.atk = TO_INT(10); + status->rhw.atk2 = TO_INT(11); + status->def = TO_INT(12); + status->mdef = TO_INT(13); + status->str = TO_INT(14); + status->agi = TO_INT(15); + status->vit = TO_INT(16); + status->int_ = TO_INT(17); + status->dex = TO_INT(18); + status->luk = TO_INT(19); + mob_db_data[class_]->range2 = TO_INT(20); + mob_db_data[class_]->range3 = TO_INT(21); + status->size = TO_INT(22); + status->race = TO_INT(23); + i = TO_INT(24); //Element + status->def_ele = i%10; + status->ele_lv = i/20; + status->mode = TO_INT(25); + status->speed = TO_INT(26); + status->aspd_rate = 100; + status->adelay = TO_INT(27); + status->amotion = TO_INT(28); + status->dmotion = TO_INT(29); + if(battle_config.monster_damage_delay_rate != 100) + status->dmotion = status->dmotion*battle_config.monster_damage_delay_rate/100; + + status_calc_misc(status, mob_db_data[class_]->lv); + + if(!battle_config.enemy_str) + status->batk = 0; + + if(battle_config.enemy_critical_rate != 100) + status->cri = status->cri*battle_config.enemy_critical_rate/100; + if(!status->cri && battle_config.enemy_critical_rate) status->cri = 1; + + if(!battle_config.enemy_perfect_flee) + status->flee2 = 0; + + // MVP EXP Bonus, Chance: MEXP,ExpPer + mob_db_data[class_]->mexp = TO_INT(30) * battle_config.mvp_exp_rate / 100; + mob_db_data[class_]->mexpper = TO_INT(31); + //Now that we know if it is an mvp or not, + //apply battle_config modifiers [Skotlex] + maxhp = (double)status->max_hp; + if (mob_db_data[class_]->mexp > 0) + { //Mvp + if (battle_config.mvp_hp_rate != 100) + maxhp = maxhp * (double)battle_config.mvp_hp_rate /100.; + } else if (battle_config.monster_hp_rate != 100) //Normal mob + maxhp = maxhp * (double)battle_config.monster_hp_rate /100.; + if (maxhp < 0) maxhp = 1; + else if (maxhp > UINT_MAX) maxhp = UINT_MAX; + status->max_hp = (unsigned int)maxhp; + + //Since mobs always respawn with full life... + status->hp = status->max_hp; + status->sp = status->max_sp; + + // MVP Drops: MVP1id,MVP1per,MVP2id,MVP2per,MVP3id,MVP3per + for (i=0; i<3; i++) { + struct item_data *id; + mob_db_data[class_]->mvpitem[i].nameid = TO_INT(32+i*2); + if (!mob_db_data[class_]->mvpitem[i].nameid) { + //No item.... + mob_db_data[class_]->mvpitem[i].p = 0; + continue; + } + mob_db_data[class_]->mvpitem[i].p = mob_drop_adjust(TO_INT(33+i*2), + battle_config.item_rate_mvp, battle_config.item_drop_mvp_min, battle_config.item_drop_mvp_max); + + //calculate and store Max available drop chance of the MVP item + id = itemdb_search(mob_db_data[class_]->mvpitem[i].nameid); + if (mob_db_data[class_]->mvpitem[i].p) { + if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->mvpitem[i].p/10+1) ) { + //item has bigger drop chance or sold in shops + id->maxchance = mob_db_data[class_]->mvpitem[i].p/10+1; //reduce MVP drop info to not spoil common drop rate + } + } + } + + for (i = 0; i < MAX_MOB_DROP; i++){ // 8 -> 10 Lupus + int rate = 0, rate_adjust, type; + unsigned short ratemin, ratemax; + struct item_data *id; + k=38+i*2; + mob_db_data[class_]->dropitem[i].nameid=TO_INT(k); + if (!mob_db_data[class_]->dropitem[i].nameid) { + //No drop. + mob_db_data[class_]->dropitem[i].p = 0; + continue; + } + type = itemdb_type(mob_db_data[class_]->dropitem[i].nameid); + rate = TO_INT(k+1); + if (class_ >= 1324 && class_ <= 1363) + { //Treasure box drop rates [Skotlex] + rate_adjust = battle_config.item_rate_treasure; + ratemin = battle_config.item_drop_treasure_min; + ratemax = battle_config.item_drop_treasure_max; + } + else switch (type) // Added suport to restrict normal drops of MVP's [Reddozen] + { + case 0: // Val added heal restrictions + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_heal_boss; + else + rate_adjust = battle_config.item_rate_heal; + ratemin = battle_config.item_drop_heal_min; + ratemax = battle_config.item_drop_heal_max; + break; + case 2: + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_use_boss; + else + rate_adjust = battle_config.item_rate_use; + ratemin = battle_config.item_drop_use_min; + ratemax = battle_config.item_drop_use_max; + break; + case 4: + case 5: + case 8: // Changed to include Pet Equip + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_equip_boss; + else + rate_adjust = battle_config.item_rate_equip; + ratemin = battle_config.item_drop_equip_min; + ratemax = battle_config.item_drop_equip_max; + break; + case 6: + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_card_boss; + else + rate_adjust = battle_config.item_rate_card; + ratemin = battle_config.item_drop_card_min; + ratemax = battle_config.item_drop_card_max; + break; + default: + if (status->mode&MD_BOSS) + rate_adjust = battle_config.item_rate_common_boss; + else + rate_adjust = battle_config.item_rate_common; + ratemin = battle_config.item_drop_common_min; + ratemax = battle_config.item_drop_common_max; + break; + } + mob_db_data[class_]->dropitem[i].p = mob_drop_adjust(rate, rate_adjust, ratemin, ratemax); + + //calculate and store Max available drop chance of the item + if (mob_db_data[class_]->dropitem[i].p) { + id = itemdb_search(mob_db_data[class_]->dropitem[i].nameid); + if (id->maxchance==10000 || (id->maxchance < mob_db_data[class_]->dropitem[i].p) ) { + //item has bigger drop chance or sold in shops + id->maxchance = mob_db_data[class_]->dropitem[i].p; + } + for (k = 0; k< MAX_SEARCH; k++) { + if (id->mob[k].chance < mob_db_data[class_]->dropitem[i].p && id->mob[k].id != class_) + break; + } + if (k == MAX_SEARCH) + continue; + + memmove(&id->mob[k+1], &id->mob[k], (MAX_SEARCH-k-1)*sizeof(id->mob[0])); + id->mob[k].chance = mob_db_data[class_]->dropitem[i].p; + id->mob[k].id = class_; + } + } + if (status->max_hp <= 0) { + ShowWarning ("Mob %d (%s) has no HP, using poring data for it\n", class_, mob_db_data[class_]->sprite); + mob_makedummymobdb(class_); + } + } + + mysql_free_result(sql_res); + ShowStatus("Done reading '"CL_WHITE"%lu"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", ln, mob_db_name[fi]); + ln = 0; + } + } + return 0; +} +#endif /* not TXT_ONLY */ + +void mob_reload(void) +{ + int i; +#ifndef TXT_ONLY + if(db_use_sqldbs) + mob_read_sqldb(); + else +#endif /* TXT_ONLY */ + mob_readdb(); + + mob_readdb_mobavail(); + mob_read_randommonster(); + + //Mob skills need to be cleared before re-reading them. [Skotlex] + for (i = 0; i < MAX_MOB_DB; i++) + if (mob_db_data[i]) + { + memset(&mob_db_data[i]->skill,0,sizeof(mob_db_data[i]->skill)); + mob_db_data[i]->maxskill=0; + } + mob_readskilldb(); + mob_readdb_race(); +} + +/*========================================== + * Circumference initialization of mob + *------------------------------------------ + */ +int do_init_mob(void) +{ //Initialize the mob database + memset(mob_db_data,0,sizeof(mob_db_data)); //Clear the array + mob_db_data[0] = aCalloc(1, sizeof (struct mob_data)); //This mob is used for random spawns + mob_makedummymobdb(0); //The first time this is invoked, it creates the dummy mob + item_drop_ers = ers_new((uint32)sizeof(struct item_drop)); + item_drop_list_ers = ers_new((uint32)sizeof(struct item_drop_list)); + +#ifndef TXT_ONLY + if(db_use_sqldbs) + mob_read_sqldb(); + else +#endif /* TXT_ONLY */ + mob_readdb(); + + mob_readdb_mobavail(); + mob_read_randommonster(); + mob_readskilldb(); + mob_readdb_race(); + + add_timer_func_list(mob_delayspawn,"mob_delayspawn"); + add_timer_func_list(mob_delay_item_drop,"mob_delay_item_drop"); + add_timer_func_list(mob_ai_hard,"mob_ai_hard"); + add_timer_func_list(mob_ai_lazy,"mob_ai_lazy"); + add_timer_func_list(mob_timer_delete,"mob_timer_delete"); + add_timer_func_list(mob_spawn_guardian_sub,"mob_spawn_guardian_sub"); + add_timer_func_list(mob_respawn,"mob_respawn"); + add_timer_interval(gettick()+MIN_MOBTHINKTIME,mob_ai_hard,0,0,MIN_MOBTHINKTIME); + add_timer_interval(gettick()+MIN_MOBTHINKTIME*10,mob_ai_lazy,0,0,MIN_MOBTHINKTIME*10); + + return 0; +} + +/*========================================== + * Clean memory usage. + *------------------------------------------ + */ +int do_final_mob(void) +{ + int i; + if (mob_dummy) + { + aFree(mob_dummy); + mob_dummy = NULL; + } + for (i = 0; i <= MAX_MOB_DB; i++) + { + if (mob_db_data[i] != NULL) + { + aFree(mob_db_data[i]); + mob_db_data[i] = NULL; + } + } + ers_destroy(item_drop_ers); + ers_destroy(item_drop_list_ers); + return 0; +} diff --git a/src/map/mob.h b/src/map/mob.h index fcd0ac6f5..f608ad4aa 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -36,19 +36,14 @@ struct mob_skill { struct mob_db { char sprite[NAME_LENGTH],name[NAME_LENGTH],jname[NAME_LENGTH]; - unsigned short lv; - int max_hp,max_sp; unsigned int base_exp,job_exp; - int atk1,atk2; - int def,mdef; - int str,agi,vit,int_,dex,luk; - int range,range2,range3; - int size,race,element,mode; + unsigned int mexp,mexpper; + int range2,range3; short race2; // celest - int speed,adelay,amotion,dmotion; - int mexp,mexpper; + 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]; @@ -133,6 +128,8 @@ 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, @@ -151,8 +148,9 @@ 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); -int mob_damage(struct block_list *,struct mob_data*,int,int); -int mob_heal(struct mob_data*,int); +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_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); } @@ -179,7 +177,7 @@ int mob_convertslave(struct mob_data *md); int mob_is_clone(int class_); -int mob_clone_spawn(struct map_session_data *sd, char *map, int x, int y, const char *event, int master_id, int mode, int flag, unsigned int duration); +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); diff --git a/src/map/npc.c b/src/map/npc.c index 616042811..a81e371c7 100644 --- a/src/map/npc.c +++ b/src/map/npc.c @@ -2170,7 +2170,7 @@ int npc_parse_mob (char *w1, char *w2, char *w3, char *w4) } //Apply the spawn delay fix [Skotlex] - mode = mob_db(class_)->mode; + mode = mob_db(class_)->status.mode; if (mode & MD_BOSS) { //Bosses if (battle_config.boss_spawn_delay != 100) { @@ -2457,10 +2457,12 @@ static int npc_parse_mapflag (char *w1, char *w2, char *w3, char *w4) 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; } return 0; } diff --git a/src/map/pc.c b/src/map/pc.c index 771bd5c7e..e3a2dfdc1 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -84,24 +84,6 @@ int pc_isGM(struct map_session_data *sd) { } -int pc_iskiller(struct map_session_data *src, struct map_session_data *target) { - nullpo_retr(0, src); - - if(src->bl.type!=BL_PC ) - return 0; - if (src->special_state.killer) - return 1; - - if(target->bl.type!=BL_PC ) - return 0; - - if (target->special_state.killable) - return 1; - - return 0; -} - - int pc_set_gm_level(int account_id, int level) { int i; for (i = 0; i < GM_num; i++) { @@ -291,52 +273,35 @@ unsigned char pc_famerank(int char_id,int job) { } int pc_setrestartvalue(struct map_session_data *sd,int type) { - //?生や養子の場合の元の職業を算出する - + struct status_data *status, *b_status; nullpo_retr(0, sd); - //----------------------- - // 死亡した - if(sd->special_state.restart_full_recover || // オシリスカ?ド - sd->state.snovice_flag == 4) { // [Celest] - sd->status.hp=sd->status.max_hp; - sd->status.sp=sd->status.max_sp; - if (sd->state.snovice_flag == 4) { + b_status = &sd->base_status; + status = &sd->battle_status; + + if (type&1) + { //Normal resurrection + status->hp = 1; //Otherwise status_heal may fail if dead. + if(sd->state.snovice_flag == 4) { // [Celest] + status_heal(&sd->bl, status->max_hp, status->max_sp, 1); sd->state.snovice_flag = 0; sc_start(&sd->bl,SkillStatusChangeTable[MO_STEELBODY],100,1,skill_get_time(MO_STEELBODY,1)); - } - } - else { - if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2) && battle_config.restart_hp_rate < 50) { //ノビは半分回復 - sd->status.hp=(sd->status.max_hp)/2; - } - else { - if(battle_config.restart_hp_rate <= 0) - sd->status.hp = 1; - else { - sd->status.hp = sd->status.max_hp * battle_config.restart_hp_rate /100; - if(sd->status.hp <= 0) - sd->status.hp = 1; - } - } - if(battle_config.restart_sp_rate > 0) { - int sp = sd->status.max_sp * battle_config.restart_sp_rate /100; - if(sd->status.sp < sp) - sd->status.sp = sp; - } + } else + status_heal(&sd->bl, b_status->hp, b_status->sp>status->sp?b_status->sp-status->sp:0, 1); + } else { //Just for saving on the char-server + sd->status.hp = b_status->hp; + if (sd->status.sp < b_status->sp) + sd->status.sp = b_status->sp; } - if(type&1) - clif_updatestatus(sd,SP_HP); - if(type&1) - clif_updatestatus(sd,SP_SP); - /* removed exp penalty on spawn [Valaris] */ if(type&2 && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE && battle_config.zeny_penalty > 0 && !map[sd->bl.m].flag.nozenypenalty) { int zeny = (int)((double)sd->status.zeny * (double)battle_config.zeny_penalty / 10000.); if(zeny < 1) zeny = 1; - sd->status.zeny -= zeny; - if(sd->status.zeny < 0) sd->status.zeny = 0; + if (sd->status.zeny > zeny) + sd->status.zeny -= zeny; + else + sd->status.zeny = 0; clif_updatestatus(sd,SP_ZENY); } @@ -357,7 +322,7 @@ int pc_can_give_items(int level) { } /*========================================== - * saveに必要なステ?タス修正を行なう + * prepares character for saving. *------------------------------------------ */ int pc_makesavestatus(struct map_session_data *sd) @@ -367,23 +332,22 @@ int pc_makesavestatus(struct map_session_data *sd) if (sd->state.finalsave) return 0; //Nothing to change. - // 秒フ色は色?弊害が多いので保存?象にはしない if(!battle_config.save_clothcolor) sd->status.clothes_color=0; - // 死亡?態だったのでhpを1、位置をセ?ブ場所に?更 if(!sd->state.waitingdisconnect) { sd->status.option = sd->sc.option; //Since the option saved is in if(pc_isdead(sd)){ pc_setrestartvalue(sd,0); memcpy(&sd->status.last_point,&sd->status.save_point,sizeof(sd->status.last_point)); } else { + sd->status.hp = sd->battle_status.hp; + sd->status.sp = sd->battle_status.sp; sd->status.last_point.map = sd->mapindex; sd->status.last_point.x = sd->bl.x; sd->status.last_point.y = sd->bl.y; } - // セ?ブ禁止マップだったので指定位置に移動 if(map[sd->bl.m].flag.nosave){ struct map_data *m=&map[sd->bl.m]; if(m->save.map) @@ -641,7 +605,6 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t // 基本的な初期化 sd->state.connect_new = 1; - sd->speed = DEFAULT_WALK_SPEED; sd->followtimer = -1; // [MouseJstr] sd->skillitem = -1; sd->skillitemlv = -1; @@ -676,9 +639,6 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t status_change_init(&sd->bl); unit_dataset(&sd->bl); - // pet - sd->pet_hungry_timer = -1; - if ((battle_config.atc_gmonly == 0 || pc_isGM(sd)) && (pc_isGM(sd) >= get_atcommand_level(AtCommand_Hide))) sd->status.option &= (OPTION_MASK | OPTION_INVISIBLE); @@ -751,7 +711,6 @@ int pc_authok(struct map_session_data *sd, int login_id2, time_t connect_until_t sd->state.event_disconnect = 1; sd->state.event_kill_mob = 1; - // ステ?タス初期計算など status_calc_pc(sd,1); sd->state.auth = 1; //Do not auth him until the initial stats have been placed. @@ -1012,10 +971,14 @@ int pc_calc_skilltree(struct map_session_data *sd) int pc_clean_skilltree(struct map_session_data *sd) { int i; for (i = 0; i < MAX_SKILL; i++){ - if (sd->status.skill[i].flag == 13){ + if (sd->status.skill[i].flag == 13 || sd->status.skill[i].flag == 1) + { sd->status.skill[i].id = 0; sd->status.skill[i].lv = 0; sd->status.skill[i].flag = 0; + } else if (sd->status.skill[i].flag){ + sd->status.skill[i].lv = sd->status.skill[i].flag-2; + sd->status.skill[i].flag = 0; } } @@ -1184,8 +1147,11 @@ static int pc_bonus_item_drop(struct s_add_drop *drop, short *count, short id, s */ int pc_bonus(struct map_session_data *sd,int type,int val) { + struct status_data *status; nullpo_retr(0, sd); + status = &sd->base_status; + switch(type){ case SP_STR: case SP_AGI: @@ -1194,93 +1160,93 @@ int pc_bonus(struct map_session_data *sd,int type,int val) case SP_DEX: case SP_LUK: if(sd->state.lr_flag != 2) - sd->parame[type-SP_STR]+=val; + sd->param_bonus[type-SP_STR]+=val; break; case SP_ATK1: if(!sd->state.lr_flag) - sd->right_weapon.watk+=val; + status->rhw.atk+=val; else if(sd->state.lr_flag == 1) - sd->left_weapon.watk+=val; + status->lhw->atk+=val; break; case SP_ATK2: if(!sd->state.lr_flag) - sd->right_weapon.watk2+=val; + status->rhw.atk2+=val; else if(sd->state.lr_flag == 1) - sd->left_weapon.watk2+=val; + status->lhw->atk2+=val; break; case SP_BASE_ATK: if(sd->state.lr_flag != 2) - sd->base_atk+=val; + status->batk+=val; break; case SP_MATK1: if(sd->state.lr_flag != 2) - sd->matk1 += val; + status->matk_max += val; break; case SP_MATK2: if(sd->state.lr_flag != 2) - sd->matk2 += val; + status->matk_min += val; break; case SP_MATK: if(sd->state.lr_flag != 2) { - sd->matk1 += val; - sd->matk2 += val; + status->matk_max += val; + status->matk_min += val; } break; case SP_DEF1: if(sd->state.lr_flag != 2) - sd->def+=val; + status->def+=val; break; case SP_DEF2: if(sd->state.lr_flag != 2) - sd->def2+=val; + status->def2+=val; break; case SP_MDEF1: if(sd->state.lr_flag != 2) - sd->mdef+=val; + status->mdef+=val; break; case SP_MDEF2: if(sd->state.lr_flag != 2) - sd->mdef+=val; + status->mdef+=val; break; case SP_HIT: if(sd->state.lr_flag != 2) - sd->hit+=val; + status->hit+=val; else sd->arrow_hit+=val; break; case SP_FLEE1: if(sd->state.lr_flag != 2) - sd->flee+=val; + status->flee+=val; break; case SP_FLEE2: if(sd->state.lr_flag != 2) - sd->flee2+=val*10; + status->flee2+=val*10; break; case SP_CRITICAL: if(sd->state.lr_flag != 2) - sd->critical+=val*10; + status->cri+=val*10; else sd->arrow_cri += val*10; break; case SP_ATKELE: if(!sd->state.lr_flag) - sd->right_weapon.atk_ele=val; + status->rhw.ele=val; else if(sd->state.lr_flag == 1) - sd->left_weapon.atk_ele=val; + status->lhw->ele=val; else if(sd->state.lr_flag == 2) sd->arrow_ele=val; break; case SP_DEFELE: if(sd->state.lr_flag != 2) - sd->def_ele=val; + status->def_ele=val; break; case SP_MAXHP: if(sd->state.lr_flag != 2) - sd->status.max_hp+=val; + status->max_hp+=val; break; case SP_MAXSP: if(sd->state.lr_flag != 2) - sd->status.max_sp+=val; + status->max_sp+=val; break; case SP_CASTRATE: if(sd->state.lr_flag != 2) @@ -1300,15 +1266,24 @@ int pc_bonus(struct map_session_data *sd,int type,int val) break; case SP_ATTACKRANGE: if(!sd->state.lr_flag) - sd->attackrange += val; + status->rhw.range += val; else if(sd->state.lr_flag == 1) - sd->attackrange_ += val; - else if(sd->state.lr_flag == 2) - sd->arrow_range += val; + status->lhw->range += val; + else if(sd->state.lr_flag == 2) { + switch (sd->status.weapon) { + case W_BOW: + case W_REVOLVER: + case W_RIFLE: + case W_SHOTGUN: + case W_GATLING: + case W_GRENADE: + status->rhw.range += val; + } + } break; case SP_ADD_SPEED: //Raw increase if(sd->state.lr_flag != 2) - sd->speed -= val; + status->speed -= val; break; case SP_SPEED_RATE: //Non stackable increase if(sd->state.lr_flag != 2 && sd->speed_rate > 100-val) @@ -1320,11 +1295,11 @@ int pc_bonus(struct map_session_data *sd,int type,int val) break; case SP_ASPD: //Raw increase if(sd->state.lr_flag != 2) - sd->aspd -= val*10; + status->adelay -= val*10; break; case SP_ASPD_RATE: //Non stackable increase - if(sd->state.lr_flag != 2 && sd->aspd_rate > 100-val) - sd->aspd_rate = 100-val; + if(sd->state.lr_flag != 2 && status->aspd_rate > 100-val) + status->aspd_rate = 100-val; break; case SP_ASPD_ADDRATE: //Stackable increase - Made it linear as per rodatazone if(sd->state.lr_flag != 2) @@ -1500,25 +1475,25 @@ int pc_bonus(struct map_session_data *sd,int type,int val) break; case SP_ALL_STATS: // [Valaris] if(sd->state.lr_flag!=2) { - sd->parame[SP_STR-SP_STR]+=val; - sd->parame[SP_AGI-SP_STR]+=val; - sd->parame[SP_VIT-SP_STR]+=val; - sd->parame[SP_INT-SP_STR]+=val; - sd->parame[SP_DEX-SP_STR]+=val; - sd->parame[SP_LUK-SP_STR]+=val; + sd->param_bonus[SP_STR-SP_STR]+=val; + sd->param_bonus[SP_AGI-SP_STR]+=val; + sd->param_bonus[SP_VIT-SP_STR]+=val; + sd->param_bonus[SP_INT-SP_STR]+=val; + sd->param_bonus[SP_DEX-SP_STR]+=val; + sd->param_bonus[SP_LUK-SP_STR]+=val; } break; case SP_AGI_VIT: // [Valaris] if(sd->state.lr_flag!=2) { - sd->parame[SP_AGI-SP_STR]+=val; - sd->parame[SP_VIT-SP_STR]+=val; + sd->param_bonus[SP_AGI-SP_STR]+=val; + sd->param_bonus[SP_VIT-SP_STR]+=val; } break; case SP_AGI_DEX_STR: // [Valaris] if(sd->state.lr_flag!=2) { - sd->parame[SP_AGI-SP_STR]+=val; - sd->parame[SP_DEX-SP_STR]+=val; - sd->parame[SP_STR-SP_STR]+=val; + sd->param_bonus[SP_AGI-SP_STR]+=val; + sd->param_bonus[SP_DEX-SP_STR]+=val; + sd->param_bonus[SP_STR-SP_STR]+=val; } break; case SP_PERFECT_HIDE: // [Valaris] @@ -1548,13 +1523,10 @@ int pc_bonus(struct map_session_data *sd,int type,int val) sd->unbreakable_equip |= EQP_SHIELD; break; case SP_CLASSCHANGE: // [Valaris] - if(sd->state.lr_flag !=2){ + if(sd->state.lr_flag !=2) sd->classchange=val; - } break; case SP_LONG_ATK_RATE: - //if(sd->state.lr_flag != 2 && sd->long_attack_atk_rate < val) - // sd->long_attack_atk_rate = val; if(sd->state.lr_flag != 2) //[Lupus] it should stack, too. As any other cards rate bonuses sd->long_attack_atk_rate+=val; break; @@ -2154,12 +2126,14 @@ int pc_skill(struct map_session_data *sd,int id,int level,int flag) } if(!flag && (sd->status.skill[id].id == id || level == 0)){ // クエスト所得ならここで?件を確認して送信する sd->status.skill[id].lv=level; - status_calc_pc(sd,0); + if (!skill_get_inf(id)) //Only recalculate for passive skills. + status_calc_pc(sd,0); clif_skillinfoblock(sd); } else if(flag==2 && (sd->status.skill[id].id == id || level == 0)){ // クエスト所得ならここで?件を確認して送信する sd->status.skill[id].lv+=level; - status_calc_pc(sd,0); + if (!skill_get_inf(id)) //Only recalculate for passive skills. + status_calc_pc(sd,0); clif_skillinfoblock(sd); } else if(sd->status.skill[id].lv < level){ // ?えられるがlvが小さいなら @@ -2905,15 +2879,17 @@ int pc_show_steal(struct block_list *bl,va_list ap) int pc_steal_item(struct map_session_data *sd,struct block_list *bl) { int i,skill,itemid,flag; + struct status_data *sd_status, *md_status; struct mob_data *md; struct item tmp_item; - if(!sd || !bl || bl->type != BL_MOB) + if(!sd || !bl || bl->type!=BL_MOB) return 0; - md = (TBL_MOB *)bl; + sd_status= status_get_status_data(&sd->bl); + md_status= status_get_status_data(bl); - if(md->state.steal_flag>battle_config.skill_steal_max_tries || status_get_mode(bl)&MD_BOSS || md->master_id || + if(md->state.steal_flag>=battle_config.skill_steal_max_tries || md_status->mode&MD_BOSS || md->master_id || (md->class_>=1324 && md->class_<1364) || // prevent stealing from treasure boxes [Valaris] map[md->bl.m].flag.nomobloot || // check noloot map flag [Lorky] md->sc.data[SC_STONE].timer != -1 || md->sc.data[SC_FREEZE].timer != -1 //status change check @@ -2921,16 +2897,15 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl) return 0; skill = battle_config.skill_steal_type == 1 - ? (sd->paramc[4] - md->db->dex)/2 + pc_checkskill(sd,TF_STEAL)*6 + 10 - : sd->paramc[4] - md->db->dex + pc_checkskill(sd,TF_STEAL)*3 + 10; + ? (sd_status->dex - md_status->dex)/2 + pc_checkskill(sd,TF_STEAL)*6 + 10 + : sd_status->dex - md_status->dex + pc_checkskill(sd,TF_STEAL)*3 + 10; skill+= sd->add_steal_rate; //Better make the steal_Rate addition affect % rather than an absolute on top of the total drop rate. [Skotlex] if (skill < 1) return 0; - if(md->state.steal_flag < battle_config.skill_steal_max_tries) - md->state.steal_flag++; //increase steal tries number + md->state.steal_flag++; //increase steal tries number for(i = 0; istate.steal_flag = 255; //you can't steal from this mob any more + md->state.steal_flag = UCHAR_MAX; //you can't steal from this mob any more memset(&tmp_item,0,sizeof(tmp_item)); tmp_item.nameid = itemid; @@ -2979,26 +2954,27 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl) * *------------------------------------------ */ -int pc_steal_coin(struct map_session_data *sd,struct block_list *bl) +int pc_steal_coin(struct map_session_data *sd,struct block_list *target) { - if(sd != NULL && bl != NULL && bl->type == BL_MOB) { - int rate,skill; - struct mob_data *md=(struct mob_data *)bl; - if(md && !md->state.steal_coin_flag) { - if (md->sc.data && (md->sc.data[SC_STONE].timer != -1 || md->sc.data[SC_FREEZE].timer != -1)) - return 0; - skill = pc_checkskill(sd,RG_STEALCOIN)*10; - rate = skill + (sd->status.base_level - md->db->lv)*3 + sd->paramc[4]*2 + sd->paramc[5]*2; - if(rand()%1000 < rate) { - pc_getzeny(sd,md->db->lv*10 + rand()%100); - md->state.steal_coin_flag = 1; - return 1; - } - } - } + int rate,skill; + struct mob_data *md; + if(!sd || !target || target->type != BL_MOB) + return 0; + md = (TBL_MOB*)target; + if(md->state.steal_coin_flag || md->sc.data[SC_STONE].timer != -1 || md->sc.data[SC_FREEZE].timer != -1) + return 0; + + skill = pc_checkskill(sd,RG_STEALCOIN)*10; + rate = skill + (sd->status.base_level - md->db->lv)*3 + sd->battle_status.dex*2 + sd->battle_status.luk*2; + if(rand()%1000 < rate) { + pc_getzeny(sd,md->db->lv*10 + rand()%100); + md->state.steal_coin_flag = 1; + return 1; + } return 0; } + // // // @@ -3729,9 +3705,10 @@ int pc_checkbaselevelup(struct map_session_data *sd) { unsigned int next = pc_nextbaseexp(sd); - nullpo_retr(0, sd); - - if(sd->status.base_exp >= next && next > 0){ + if (!next || sd->status.base_exp < next) + return 0; + + do { sd->status.base_exp -= next; //Kyoki pointed out that the max overcarry exp is the exp needed for the previous level -1. [Skotlex] if(!battle_config.multi_level_up && sd->status.base_exp > next-1) @@ -3739,8 +3716,6 @@ int pc_checkbaselevelup(struct map_session_data *sd) sd->status.base_level ++; - if (battle_config.pet_lv_rate && sd->pd) // update pet's level - status_calc_pet(sd,0); if (battle_config.use_statpoint_table) next = statp[sd->status.base_level] - statp[sd->status.base_level-1]; else //Estimated way. @@ -3749,33 +3724,35 @@ int pc_checkbaselevelup(struct map_session_data *sd) sd->status.status_point = USHRT_MAX; else sd->status.status_point += next; - clif_updatestatus(sd,SP_STATUSPOINT); - clif_updatestatus(sd,SP_BASELEVEL); - clif_updatestatus(sd,SP_NEXTBASEEXP); - status_calc_pc(sd,0); - pc_heal(sd,sd->status.max_hp,sd->status.max_sp); - //スパノビはキリエ、イムポ、マニピ、グロ、サフラLv1がかかる - if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) - { - sc_start(&sd->bl,SkillStatusChangeTable[PR_KYRIE],100,1,skill_get_time(PR_KYRIE,1)); - sc_start(&sd->bl,SkillStatusChangeTable[PR_IMPOSITIO],100,1,skill_get_time(PR_IMPOSITIO,1)); - sc_start(&sd->bl,SkillStatusChangeTable[PR_MAGNIFICAT],100,1,skill_get_time(PR_MAGNIFICAT,1)); - sc_start(&sd->bl,SkillStatusChangeTable[PR_GLORIA],100,1,skill_get_time(PR_GLORIA,1)); - sc_start(&sd->bl,SkillStatusChangeTable[PR_SUFFRAGIUM],100,1,skill_get_time(PR_SUFFRAGIUM,1)); - } else - if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON) - { - sc_start(&sd->bl,SkillStatusChangeTable[AL_INCAGI],100,10,skill_get_time(AL_INCAGI,10)); - sc_start(&sd->bl,SkillStatusChangeTable[AL_BLESSING],100,10,skill_get_time(AL_BLESSING,10)); - } - clif_misceffect(&sd->bl,0); - //LORDALFA - LVLUPEVENT - npc_script_event(sd, NPCE_BASELVUP); - return 1; - } + } while ((next=pc_nextbaseexp(sd)) > 0 && sd->status.base_exp >= next); - return 0; + if (battle_config.pet_lv_rate && sd->pd) // update pet's level + status_calc_pet(sd->pd,0); + + clif_updatestatus(sd,SP_STATUSPOINT); + clif_updatestatus(sd,SP_BASELEVEL); + clif_updatestatus(sd,SP_NEXTBASEEXP); + status_calc_pc(sd,0); + status_percent_heal(&sd->bl,100,100); + + if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE) + { + sc_start(&sd->bl,SkillStatusChangeTable[PR_KYRIE],100,1,skill_get_time(PR_KYRIE,1)); + sc_start(&sd->bl,SkillStatusChangeTable[PR_IMPOSITIO],100,1,skill_get_time(PR_IMPOSITIO,1)); + sc_start(&sd->bl,SkillStatusChangeTable[PR_MAGNIFICAT],100,1,skill_get_time(PR_MAGNIFICAT,1)); + sc_start(&sd->bl,SkillStatusChangeTable[PR_GLORIA],100,1,skill_get_time(PR_GLORIA,1)); + sc_start(&sd->bl,SkillStatusChangeTable[PR_SUFFRAGIUM],100,1,skill_get_time(PR_SUFFRAGIUM,1)); + } else + if((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON) + { + sc_start(&sd->bl,SkillStatusChangeTable[AL_INCAGI],100,10,skill_get_time(AL_INCAGI,10)); + sc_start(&sd->bl,SkillStatusChangeTable[AL_BLESSING],100,10,skill_get_time(AL_BLESSING,10)); + } + clif_misceffect(&sd->bl,0); + //LORDALFA - LVLUPEVENT + npc_script_event(sd, NPCE_BASELVUP); + return 1; } int pc_checkjoblevelup(struct map_session_data *sd) @@ -3783,30 +3760,30 @@ int pc_checkjoblevelup(struct map_session_data *sd) unsigned int next = pc_nextjobexp(sd); nullpo_retr(0, sd); + if(!next || sd->status.job_exp < next) + return 0; - if(sd->status.job_exp >= next && next > 0){ + do { sd->status.job_exp -= next; //Kyoki pointed out that the max overcarry exp is the exp needed for the previous level -1. [Skotlex] if(!battle_config.multi_level_up && sd->status.job_exp > next-1) sd->status.job_exp = next-1; sd->status.job_level ++; - - clif_updatestatus(sd,SP_JOBLEVEL); - clif_updatestatus(sd,SP_NEXTJOBEXP); sd->status.skill_point ++; - clif_updatestatus(sd,SP_SKILLPOINT); - status_calc_pc(sd,0); - clif_misceffect(&sd->bl,1); - if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) - clif_status_change(&sd->bl,SI_DEVIL, 1); //Permanent blind effect from SG_DEVIL. + } while ((next=pc_nextjobexp(sd)) > 0 && sd->status.job_exp >= next); - npc_script_event(sd, NPCE_JOBLVUP); - return 1; - } + clif_updatestatus(sd,SP_JOBLEVEL); + clif_updatestatus(sd,SP_NEXTJOBEXP); + clif_updatestatus(sd,SP_SKILLPOINT); + status_calc_pc(sd,0); + clif_misceffect(&sd->bl,1); + if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) + clif_status_change(&sd->bl,SI_DEVIL, 1); //Permanent blind effect from SG_DEVIL. - return 0; + npc_script_event(sd, NPCE_JOBLVUP); + return 1; } /*========================================== @@ -3864,7 +3841,7 @@ int pc_gainexp(struct map_session_data *sd,unsigned int base_exp,unsigned int jo else sd->status.base_exp += base_exp; - while(pc_checkbaselevelup(sd)) ; + pc_checkbaselevelup(sd) ; clif_updatestatus(sd,SP_BASEEXP); @@ -4096,8 +4073,6 @@ int pc_statusup2(struct map_session_data *sd,int type,int val) sd->status.luk = val; break; } - clif_updatestatus(sd,type-SP_STR+SP_USTR); - clif_updatestatus(sd,type); status_calc_pc(sd,0); clif_statusupack(sd,type,1,val); @@ -4126,7 +4101,8 @@ int pc_skillup(struct map_session_data *sd,int skill_num) { sd->status.skill[skill_num].lv++; sd->status.skill_point--; - status_calc_pc(sd,0); + if (!skill_get_inf(skill_num)) //Only recalculate for passive skills. + status_calc_pc(sd,0); clif_skillup(sd,skill_num); clif_updatestatus(sd,SP_SKILLPOINT); clif_skillinfoblock(sd); @@ -4415,96 +4391,56 @@ static int pc_respawn(int tid,unsigned int tick,int id,int data) } /*========================================== - * Damages a player's SP, returns remaining SP. [Skotlex] - * damage is absolute damage, rate is % damage (100 = 100%) - * if rate is positive, it is % of current sp, if negative, it is % of max - * Returns remaining SP, or -1 if the player did not have enough SP to substract from. - *------------------------------------------ - */ -int pc_damage_sp(struct map_session_data *sd, int damage, int rate) -{ - if (!sd->status.sp) - return 0; - - if (rate > 0) - damage += (rate*sd->status.sp)/100; - else if (rate < 0) - damage -= (rate*sd->status.max_sp)/100; - - - if (sd->status.sp >= damage){ - sd->status.sp -= damage; - clif_updatestatus(sd,SP_SP); - return sd->status.sp; - } - sd->status.sp = 0; - clif_updatestatus(sd,SP_SP); - return -1; -} -/*========================================== - * pcにダメ?ジを?える + * Invoked when a player has received damage *------------------------------------------ */ -int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) +void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int hp, unsigned int sp) { - int i=0,j=0, resurrect_flag=0; + if (sp) clif_updatestatus(sd,SP_SP); + if (!hp) return; - nullpo_retr(0, sd); - - // ?に死んでいたら無? - if(pc_isdead(sd)) - return 0; - // 座ってたら立ち上がる if(pc_issit(sd)) { pc_setstand(sd); skill_gangsterparadise(sd,0); skill_rest(sd,0); } - // 演奏/ダンスの中? - if(damage > sd->status.max_hp>>2) - skill_stop_dancing(&sd->bl); - - if (damage < sd->status.hp) - sd->status.hp-=damage; - else - sd->status.hp = 0; - - if(sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_damage_support) + if(sd->status.pet_id > 0 && sd->pd && battle_config.pet_damage_support) pet_target_check(sd,src,1); clif_updatestatus(sd,SP_HP); - - if (sd->status.hpstatus.max_hp>>2) { //25% HP left effects. - if(sd->status.hp>0 && sd->sc.data[SC_AUTOBERSERK].timer != -1 && - (sd->sc.data[SC_PROVOKE].timer==-1 || sd->sc.data[SC_PROVOKE].val2==0 )) - sc_start4(&sd->bl,SC_PROVOKE,100,10,1,0,0,0); - + if (sd->battle_status.hpbattle_status.max_hp>>2) + { //25% HP left effects. + int i=0; for(i = 0; i < 5; i++) - if (sd->devotion[i]){ - struct map_session_data *devsd = map_id2sd(sd->devotion[i]); - if (devsd) status_change_end(&devsd->bl,SC_DEVOTION,-1); - sd->devotion[i] = 0; - } + if (sd->devotion[i]){ + struct map_session_data *devsd = map_id2sd(sd->devotion[i]); + if (devsd) status_change_end(&devsd->bl,SC_DEVOTION,-1); + sd->devotion[i] = 0; + } } - if(sd->status.hp>0){ - sd->canlog_tick = gettick(); - return damage; - } + sd->canlog_tick = gettick(); + return; +} + +int pc_dead(struct map_session_data *sd,struct block_list *src) +{ + int i=0,j=0,resurrect_flag=0,baby_flag=0; + unsigned int tick = gettick(); + struct status_data *status = &sd->battle_status; + if(sd->vender_id) vending_closevending(sd); - if(sd->status.pet_id > 0 && sd->pd && - !map[sd->bl.m].flag.nopenalty) { - if(sd->petDB) { - sd->pet.intimate -= sd->petDB->die; - if(sd->pet.intimate < 0) - sd->pet.intimate = 0; - clif_send_petdata(sd,1,sd->pet.intimate); - } + if(sd->status.pet_id > 0 && sd->pd && !map[sd->bl.m].flag.nopenalty) + { + sd->pet.intimate -= sd->pd->petDB->die; + if(sd->pet.intimate < 0) + sd->pet.intimate = 0; + clif_send_petdata(sd,1,sd->pet.intimate); } // Leave duel if you die [LuzZza] @@ -4515,71 +4451,97 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) duel_reject(sd->duel_invite, sd); } + //SC data that will be needed later on. + resurrect_flag = (sd->sc.data[SC_KAIZEL].timer != -1)?sd->sc.data[SC_KAIZEL].val1:0; + baby_flag = (sd->sc.data[SC_BABY].timer != -1)?1:0; + pc_stop_attack(sd); pc_stop_walking(sd,0); unit_skillcastcancel(&sd->bl,0); - skill_stop_dancing(&sd->bl); //You should stop dancing when dead... [Skotlex] - if (sd->sc.data[SC_GOSPEL].timer != -1 && sd->sc.data[SC_GOSPEL].val4 == BCT_SELF) - { //Remove Gospel [Skotlex] - struct skill_unit_group *sg = (struct skill_unit_group *)sd->sc.data[SC_GOSPEL].val3; - if (sg) - skill_delunitgroup(&sd->bl, sg); - } clif_clearchar_area(&sd->bl,1); - - if (src) { - if(src->type == BL_MOB){ - struct mob_data *smd = (struct mob_data *)src; - if(smd->nd){ - setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)5, &smd->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)sd->bl.type, &smd->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 2, (void *)sd->bl.id, &smd->nd->u.scr.script->script_vars); - setd_sub(NULL, NULL, ".ai_action", 3, (void *)smd->bl.id, &smd->nd->u.scr.script->script_vars); - run_script(smd->nd->u.scr.script, 0, 0, smd->nd->bl.id); - } - } else if(src->type == BL_PC){ - struct map_session_data *ssd = (struct map_session_data *)src; - if (ssd) { - if (sd->state.event_death) - pc_setglobalreg(sd,"killerrid",(ssd->status.account_id)); - if (ssd->state.event_kill_pc) { - pc_setglobalreg(ssd, "killedrid", sd->bl.id); - npc_script_event(ssd, NPCE_KILLPC); - } - if (battle_config.pk_mode && ssd->status.manner >= 0 && battle_config.manner_system) { - ssd->status.manner -= 5; - if(ssd->status.manner < 0) - sc_start(src,SC_NOCHAT,100,0,0); - - // PK/Karma system code (not enabled yet) [celest] - // originally from Kade Online, so i don't know if any of these is correct ^^; - // note: karma is measured REVERSE, so more karma = more 'evil' / less honourable, - // karma going down = more 'good' / more honourable. - // The Karma System way... - /*if (sd->status.karma > ssd->status.karma) { // If player killed was more evil - sd->status.karma--; - ssd->status.karma--; - } - else if (sd->status.karma < ssd->status.karma) // If player killed was more good - ssd->status.karma++;*/ - - // or the PK System way... - /* if (sd->status.karma > 0) // player killed is dishonourable? - ssd->status.karma--; // honour points earned - sd->status.karma++; // honour points lost */ - // To-do: Receive exp on certain occasions - } + pc_setdead(sd); + skill_unit_move(&sd->bl,tick,4); + if (battle_config.clear_unit_ondeath) + skill_clear_unitgroup(&sd->bl); //orn + status_change_clear(&sd->bl,0); + sd->canregen_tick = tick; + + pc_setglobalreg(sd,"PC_DIE_COUNTER",++sd->die_counter); + + if (sd->state.event_death && (!src || src->type != BL_PC)) + pc_setglobalreg(sd, "killerrid", 0); + + if (src) + switch (src->type) { + case BL_MOB: + { + struct mob_data *md=(struct mob_data *)src; + if(md->target_id==sd->bl.id) + mob_unlocktarget(md,tick); + if(battle_config.mobs_level_up && md->status.hp && + md->level < pc_maxbaselv(sd) && + !md->guardian_data && !md->special_state.ai// Guardians/summons should not level. [Skotlex] + ) { // monster level up [Valaris] + clif_misceffect(&md->bl,0); + md->level++; + status_calc_mob(md, 0); + status_percent_heal(src,10,0); + } + if(md->nd){ + setd_sub(NULL, NULL, ".ai_action", 0, (void *)(int)5, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 1, (void *)(int)sd->bl.type, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 2, (void *)sd->bl.id, &md->nd->u.scr.script->script_vars); + setd_sub(NULL, NULL, ".ai_action", 3, (void *)md->bl.id, &md->nd->u.scr.script->script_vars); + run_script(md->nd->u.scr.script, 0, 0, md->nd->bl.id); + } + } + break; + case BL_PC: + { + struct map_session_data *ssd = (struct map_session_data *)src; + if (sd->state.event_death) + pc_setglobalreg(sd,"killerrid",(ssd->status.account_id)); + if (ssd->state.event_kill_pc) { + pc_setglobalreg(ssd, "killedrid", sd->bl.id); + npc_script_event(ssd, NPCE_KILLPC); + } + if (battle_config.pk_mode && ssd->status.manner >= 0 && battle_config.manner_system) { + ssd->status.manner -= 5; + if(ssd->status.manner < 0) + sc_start(src,SC_NOCHAT,100,0,0); + + // PK/Karma system code (not enabled yet) [celest] + // originally from Kade Online, so i don't know if any of these is correct ^^; + // note: karma is measured REVERSE, so more karma = more 'evil' / less honourable, + // karma going down = more 'good' / more honourable. + // The Karma System way... + /* + if (sd->status.karma > ssd->status.karma) { // If player killed was more evil + sd->status.karma--; + ssd->status.karma--; } + else if (sd->status.karma < ssd->status.karma) // If player killed was more good + ssd->status.karma++; + */ + + // or the PK System way... + /* + if (sd->status.karma > 0) // player killed is dishonourable? + ssd->status.karma--; // honour points earned + sd->status.karma++; // honour points lost + */ + // To-do: Receive exp on certain occasions } - } else { - pc_setglobalreg(sd, "killerrid", 0); + } + break; } if (sd->state.event_death) npc_script_event(sd,NPCE_DIE); -// PK/Karma system code (not enabled yet) [celest] - /*if(sd->status.karma > 0) { + // PK/Karma system code (not enabled yet) [celest] + /* + if(sd->status.karma > 0) { int eq_num=0,eq_n[MAX_INVENTORY]; memset(eq_n,0,sizeof(eq_n)); for(i=0;ibl.m].flag.pvp)){ // ドクロドロップ + || (battle_config.bone_drop==1 && map[sd->bl.m].flag.pvp)) + { struct item item_tmp; memset(&item_tmp,0,sizeof(item_tmp)); item_tmp.nameid=7420; //PVP Skull item ID @@ -4622,18 +4586,12 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) if (i>0 && (j=sd->status.base_exp*1000/i)>=990 && j<1000) sd->state.snovice_flag = 4; } - - pc_setdead(sd); - skill_unit_move(&sd->bl,gettick(),4); - if (battle_config.clear_unit_ondeath) - skill_clear_unitgroup(&sd->bl); //orn - - pc_setglobalreg(sd,"PC_DIE_COUNTER",++sd->die_counter); //死にカウンタ?書き?み - // changed penalty options, added death by player if pk_mode [Valaris] + + // changed penalty options, added death by player if pk_mode [Valaris] if(battle_config.death_penalty_type && sd->state.snovice_flag != 4 && (sd->class_&MAPID_UPPERMASK) != MAPID_NOVICE // only novices will receive no penalty && !map[sd->bl.m].flag.nopenalty && !map_flag_gvg(sd->bl.m) - && !(sd->sc.count && sd->sc.data[SC_BABY].timer!=-1)) + && !baby_flag) { unsigned int base_penalty =0; if (battle_config.death_penalty_base > 0) { @@ -4677,32 +4635,7 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) } } } - if(src && src->type==BL_MOB) { - struct mob_data *md=(struct mob_data *)src; - if(md && md->target_id != 0 && md->target_id==sd->bl.id) { - // reset target id when player dies - mob_unlocktarget(md,gettick()); - } - if(battle_config.mobs_level_up && md && md->hp && - md->level < pc_maxbaselv(sd) && - !md->guardian_data && !md->special_state.ai// Guardians/summons should not level. [Skotlex] - ) { // monster level up [Valaris] - clif_misceffect(&md->bl,0); - md->level++; - md->hp+=(int) (sd->status.max_hp*.1); - if (battle_config.show_mob_hp) - clif_charnameack (0, &md->bl); - } - } - //Clear these data here so that SC_BABY check may work. [Skotlex] - resurrect_flag = (sd->sc.data[SC_KAIZEL].timer != -1)?sd->sc.data[SC_KAIZEL].val1:0; //Auto-resurrect later in the code. - status_change_clear(&sd->bl,0); // ステ?タス異常を解除する - clif_updatestatus(sd,SP_HP); - status_calc_pc(sd,0); - sd->canregen_tick = gettick(); - - //ナイトメアモ?ドアイテムドロップ if(map[sd->bl.m].flag.pvp_nightmaredrop){ // Moved this outside so it works when PVP isnt enabled and during pk mode [Ancyker] for(j=0;jbl.m].drop_list[j].drop_id; @@ -4710,16 +4643,14 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) int per = map[sd->bl.m].drop_list[j].drop_per; if(id == 0) continue; - if(id == -1){//ランダムドロップ + if(id == -1){ int eq_num=0,eq_n[MAX_INVENTORY]; memset(eq_n,0,sizeof(eq_n)); - //先ず?備しているアイテム?をカウント for(i=0;istatus.inventory[i].equip) || (type == 2 && sd->status.inventory[i].equip) || type == 3){ - //InventoryIndexを格納 for(k=0;k 0){ - int n = eq_n[rand()%eq_num];//該?アイテムの中からランダム + int n = eq_n[rand()%eq_num]; if(rand()%10000 < per){ if(sd->status.inventory[n].equip) pc_unequipitem(sd,n,3); @@ -4740,9 +4671,9 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) } else if(id > 0){ for(i=0;istatus.inventory[i].nameid == id//ItemIDが一致していて - && rand()%10000 < per//ドロップ率判定もOKで - && ((type == 1 && !sd->status.inventory[i].equip)//タイプ判定もOKならドロップ + if(sd->status.inventory[i].nameid == id + && rand()%10000 < per + && ((type == 1 && !sd->status.inventory[i].equip) || (type == 2 && sd->status.inventory[i].equip) || type == 3) ){ if(sd->status.inventory[i].equip) @@ -4756,7 +4687,6 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) } // pvp if( map[sd->bl.m].flag.pvp && !battle_config.pk_mode){ // disable certain pvp functions on pk_mode [Valaris] - //ランキング計算 if (!map[sd->bl.m].flag.pvp_nocalcrank) { sd->pvp_point -= 5; sd->pvp_lost++; @@ -4765,28 +4695,29 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) if (ssd) { ssd->pvp_point++; ssd->pvp_won++; } } } - // ?制送還 if( sd->pvp_point < 0 ){ sd->pvp_point=0; - add_timer(gettick()+1000, pc_respawn,sd->bl.id,0); - return damage; + add_timer(tick+1000, pc_respawn,sd->bl.id,0); + return 1; } } //GvG if(map_flag_gvg(sd->bl.m)){ - add_timer(gettick()+1000, pc_respawn,sd->bl.id,0); - return damage; + add_timer(tick+1000, pc_respawn,sd->bl.id,0); + return 1; } - if (sd->state.snovice_flag == 4 || resurrect_flag) { - if (sd->state.snovice_flag == 4 || sd->special_state.restart_full_recover) { - sd->status.hp = sd->status.max_hp; - sd->status.sp = sd->status.max_sp; - } else { //10% life per each level in Kaizel - sd->status.hp = 10*resurrect_flag*sd->status.max_hp/100; - } + if (sd->state.snovice_flag == 4 || resurrect_flag) + { clif_skill_nodamage(&sd->bl,&sd->bl,ALL_RESURRECTION,1,1); pc_setstand(sd); + status->hp = 1; + if (sd->state.snovice_flag == 4 || + sd->special_state.restart_full_recover) { + status_percent_heal(&sd->bl, 100, 100); + } else { //10% life per each level in Kaizel + status_percent_heal(&sd->bl, 10*resurrect_flag, 0); + } clif_updatestatus(sd, SP_HP); clif_updatestatus(sd, SP_SP); clif_resurrection(&sd->bl, 1); @@ -4800,7 +4731,7 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) return 0; } - return damage; + return 1; } // @@ -4940,7 +4871,6 @@ int pc_setparam(struct map_session_data *sd,int type,int val) clif_updatestatus(sd, SP_STATUSPOINT); clif_updatestatus(sd, SP_BASEEXP); status_calc_pc(sd, 0); - pc_heal(sd, sd->status.max_hp, sd->status.max_sp); break; case SP_JOBLEVEL: if ((unsigned int)val >= sd->status.job_level) { @@ -4950,7 +4880,6 @@ int pc_setparam(struct map_session_data *sd,int type,int val) else sd->status.skill_point += val-sd->status.job_level; clif_updatestatus(sd, SP_SKILLPOINT); - clif_misceffect(&sd->bl, 1); } sd->status.job_level = (unsigned int)val; sd->status.job_exp = 0; @@ -5057,51 +4986,25 @@ int pc_setparam(struct map_session_data *sd,int type,int val) return 0; } + /*========================================== - * HP/SP回復 + * HP/SP Healing. If flag is passed, the heal type is through clif_heal, otherwise update status. *------------------------------------------ */ -int pc_heal(struct map_session_data *sd,int hp,int sp) +void pc_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type) { -// if(battle_config.battle_log) -// printf("heal %d %d\n",hp,sp); - - nullpo_retr(0, sd); - -//Uneeded as the hp range adjustment below will auto-adap itself and make hp = max. [Skotlex] -// if(hp > 0 && pc_checkoverhp(sd)) -// hp = 0; - -// if(sp > 0 && pc_checkoversp(sd)) -// sp = 0; - - if(hp > sd->status.max_hp - sd->status.hp) - hp = sd->status.max_hp - sd->status.hp; - sd->status.hp+=hp; - - if(sp > sd->status.max_sp - sd->status.sp) - sp = sd->status.max_sp - sd->status.sp; - sd->status.sp+=sp; - - if(sd->status.sp <= 0) - sd->status.sp = 0; - - if(sd->status.hp <= 0) { - sd->status.hp = 0; - pc_damage(NULL,sd,1); - hp = 0; + if (type) { + if (hp) + clif_heal(sd->fd,SP_HP,hp); + if (sp) + clif_heal(sd->fd,SP_SP,sp); + } else { + if(hp) + clif_updatestatus(sd,SP_HP); + if(sp) + clif_updatestatus(sd,SP_SP); } - - if(hp) - clif_updatestatus(sd,SP_HP); - if(sp) - clif_updatestatus(sd,SP_SP); - - if(sd->status.hp>=sd->status.max_hp>>2 && sd->sc.data[SC_AUTOBERSERK].timer != -1 && - (sd->sc.data[SC_PROVOKE].timer!=-1 && sd->sc.data[SC_PROVOKE].val2==1 )) - status_change_end(&sd->bl,SC_PROVOKE,-1); //End auto berserk. - - return hp + sp; + return; } /*========================================== @@ -5111,58 +5014,29 @@ int pc_heal(struct map_session_data *sd,int hp,int sp) int pc_itemheal(struct map_session_data *sd,int hp,int sp) { int bonus, type; -// if(battle_config.battle_log) -// printf("heal %d %d\n",hp,sp); nullpo_retr(0, sd); - if(hp > 0 && pc_checkoverhp(sd)) - hp = 0; - - if(sp > 0 && pc_checkoversp(sd)) - sp = 0; - - if(hp > 0) { - bonus = (sd->paramc[2]<<1) + 100 + pc_checkskill(sd,SM_RECOVERY)*10 + if(hp) { + bonus = 100 + (sd->battle_status.vit<<1) + + pc_checkskill(sd,SM_RECOVERY)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5; // A potion produced by an Alchemist in the Fame Top 10 gets +50% effect [DracoRPG] bonus += (potion_flag==2)?50:(potion_flag==3?100:0); if ((type = itemdb_group(sd->itemid)) > 0 && type <= 7) bonus = bonus * (100+sd->itemhealrate[type - 1]) / 100; - if(bonus != 100) + if(bonus!=100) hp = hp * bonus / 100; } - if(sp > 0) { - bonus = (sd->paramc[3]<<1) + 100 + pc_checkskill(sd,MG_SRECOVERY)*10 + if(sp) { + bonus = 100 + (sd->battle_status.int_<<1) + + pc_checkskill(sd,MG_SRECOVERY)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5; bonus += (potion_flag==2)?50:(potion_flag==3?100:0); if(bonus != 100) sp = sp * bonus / 100; } - if(hp > sd->status.max_hp - sd->status.hp) - sd->status.hp = sd->status.max_hp; - else - sd->status.hp+=hp; - - if(sp > sd->status.max_sp - sd->status.sp) - sd->status.sp = sd->status.max_sp; - else - sd->status.sp += sp; - - if(sd->status.hp <= 0) { - sd->status.hp = 0; - pc_damage(NULL,sd,1); - hp = 0; - } - if(sd->status.sp <= 0) - sd->status.sp = 0; - - if(hp) - clif_updatestatus(sd,SP_HP); - if(sp) - clif_updatestatus(sd,SP_SP); - - return 0; + return status_heal(&sd->bl, hp, sp, 1); } /*========================================== @@ -5172,63 +5046,35 @@ int pc_itemheal(struct map_session_data *sd,int hp,int sp) int pc_percentheal(struct map_session_data *sd,int hp,int sp) { nullpo_retr(0, sd); -/* Shouldn't be needed, these functions are proof of bad coding xP - if(pc_checkoverhp(sd)) { - if(hp > 0) - hp = 0; - } - if(pc_checkoversp(sd)) { - if(sp > 0) - sp = 0; - } -*/ + + if(hp > 100) hp = 100; + else + if(hp <-100) hp =-100; + + if(sp > 100) sp = 100; + else + if(sp <-100) sp =-100; + + if(hp >= 0 && sp >= 0) //Heal + return status_percent_heal(&sd->bl, hp, sp); + + if(hp <= 0 && sp <= 0) //Damage (negative rates indicate % of max rather than current) + return status_percent_damage(NULL, &sd->bl, hp, sp); + + //Crossed signs if(hp) { - if(hp >= 100) - sd->status.hp = sd->status.max_hp; - else if(hp <= -100) { - sd->status.hp = 0; - pc_damage(NULL,sd,1); - } - else if (hp > 0) { - hp = sd->status.max_hp*hp/100; - if (sd->status.max_hp - sd->status.hp < hp) - sd->status.hp = sd->status.max_hp; - else - sd->status.hp += hp; - } - else { //hp < 0 - hp = sd->status.max_hp*hp/100; - if (sd->status.hp <= -hp) { - sd->status.hp = 0; - pc_damage(NULL,sd,1); - } else - sd->status.hp += hp; - } + if(hp > 0) + status_percent_heal(&sd->bl, hp, 0); + else + status_percent_damage(NULL, &sd->bl, hp, 0); } + if(sp) { - if(sp >= 100) - sd->status.sp = sd->status.max_sp; - else if(sp <= -100) - sd->status.sp = 0; - else if(sp > 0) { - sp = sd->status.max_sp*sp/100; - if (sd->status.max_sp - sd->status.sp < sp) - sd->status.sp = sd->status.max_sp; - else - sd->status.sp += sp; - } else { //sp < 0 - sp = sd->status.max_sp*sp/100; - if (sd->status.sp <= -sp) - sd->status.sp = 0; - else - sd->status.sp += sp; - } + if(sp > 0) + status_percent_heal(&sd->bl, 0, sp); + else + status_percent_damage(NULL, &sd->bl, 0, sp); } - if(hp) - clif_updatestatus(sd,SP_HP); - if(sp) - clif_updatestatus(sd,SP_SP); - return 0; } @@ -5469,7 +5315,6 @@ int pc_setoption(struct map_session_data *sd,int type) } clif_changeoption(&sd->bl); - status_calc_pc(sd,0); return 0; } @@ -5711,8 +5556,9 @@ int pc_setregistry(struct map_session_data *sd,char *reg,int val,int type) { nullpo_retr(0, sd); if (type == 3) { //Some special character reg values... if(strcmp(reg,"PC_DIE_COUNTER") == 0 && sd->die_counter != val){ + i = (!sd->die_counter && (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE); sd->die_counter = val; - // status_calc_pc(sd,0); //I doubt this is needed.... + if (i) status_calc_pc(sd,0); //Lost the bonus. } else if(strcmp(reg,script_config.die_event_name) == 0){ sd->state.event_death = val; } else if(strcmp(reg,script_config.kill_pc_event_name) == 0){ @@ -6219,7 +6065,7 @@ int pc_unequipitem(struct map_session_data *sd,int n,int flag) status_calc_pc(sd,0); } - if(sd->sc.count && sd->sc.data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(RC_DEMIHUMAN,sd->def_ele)) + if(sd->sc.count && sd->sc.data[SC_SIGNUMCRUCIS].timer != -1 && !battle_check_undead(sd->battle_status.race,sd->battle_status.def_ele)) status_change_end(&sd->bl,SC_SIGNUMCRUCIS,-1); //OnUnEquip script [Skotlex] @@ -6340,38 +6186,8 @@ int pc_checkitem(struct map_session_data *sd) } pc_setequipindex(sd); - if(calc_flag) - status_calc_pc(sd,2); - - return 0; -} - -int pc_checkoverhp(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - if(sd->status.hp == sd->status.max_hp) - return 1; - if(sd->status.hp > sd->status.max_hp) { - sd->status.hp = sd->status.max_hp; - clif_updatestatus(sd,SP_HP); - return 2; - } - - return 0; -} - -int pc_checkoversp(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - if(sd->status.sp == sd->status.max_sp) - return 1; - if(sd->status.sp > sd->status.max_sp) { - sd->status.sp = sd->status.max_sp; - clif_updatestatus(sd,SP_SP); - return 2; - } + if(calc_flag && sd->state.auth) + status_calc_pc(sd,0); return 0; } @@ -6594,15 +6410,13 @@ static int pc_spheal(struct map_session_data *sd) { int a = natural_heal_diff_tick; - nullpo_retr(0, sd); - if(pc_issit(sd)) a += a; if (sd->sc.count) { if (sd->sc.data[SC_MAGNIFICAT].timer!=-1) // マグニフィカ?ト a += a; if (sd->sc.data[SC_REGENERATION].timer != -1) - a *= sd->sc.data[SC_REGENERATION].val1; + a *= sd->sc.data[SC_REGENERATION].val3; } // Re-added back to status_calc //if((skill = pc_checkskill(sd,HP_MEDITATIO)) > 0) //Increase natural SP regen with Meditatio [DracoRPG] @@ -6631,15 +6445,13 @@ static int pc_hpheal(struct map_session_data *sd) { int a = natural_heal_diff_tick; - nullpo_retr(0, sd); - if(pc_issit(sd)) a += a; if (sd->sc.count) { if (sd->sc.data[SC_MAGNIFICAT].timer != -1) // Modified by RoVeRT a += a; if (sd->sc.data[SC_REGENERATION].timer != -1) - a *= sd->sc.data[SC_REGENERATION].val1; + a *= sd->sc.data[SC_REGENERATION].val2; } if (sd->status.guild_id > 0) { struct guild_castle *gc = guild_mapindex2gc(sd->mapindex); // Increased guild castle regen [Valaris] @@ -6656,27 +6468,24 @@ static int pc_hpheal(struct map_session_data *sd) return a; } -static int pc_natural_heal_hp(struct map_session_data *sd) +static void pc_natural_heal_hp(struct map_session_data *sd) { - int bhp; + unsigned int hp; int inc_num,bonus,hp_flag; - nullpo_retr(0, sd); - if (sd->no_regen & 1) - return 0; + return; if(pc_checkoverhp(sd)) { sd->hp_sub = sd->inchealhptick = 0; - return 0; + return; } - bhp=sd->status.hp; hp_flag = (pc_checkskill(sd,SM_MOVINGRECOVERY) > 0 && sd->ud.walktimer != -1); if(sd->ud.walktimer == -1) { inc_num = pc_hpheal(sd); - if(sd->sc.data[SC_TENSIONRELAX].timer!=-1 ){ // テンションリラックス + if(sd->sc.data[SC_TENSIONRELAX].timer!=-1 ){ sd->hp_sub += 2*inc_num; sd->inchealhptick += 3*natural_heal_diff_tick; } else { @@ -6691,123 +6500,112 @@ static int pc_natural_heal_hp(struct map_session_data *sd) } else { sd->hp_sub = sd->inchealhptick = 0; - return 0; + return; } if(sd->hp_sub >= battle_config.natural_healhp_interval) { + hp = 0; bonus = sd->nhealhp; if(hp_flag) { bonus >>= 2; if(bonus <= 0) bonus = 1; } - while(sd->hp_sub >= battle_config.natural_healhp_interval) { + do { sd->hp_sub -= battle_config.natural_healhp_interval; - if(sd->status.hp + bonus <= sd->status.max_hp) - sd->status.hp += bonus; - else { - sd->status.hp = sd->status.max_hp; - sd->hp_sub = sd->inchealhptick = 0; - } + hp+= bonus; + } while(sd->hp_sub >= battle_config.natural_healhp_interval); + + if (status_heal(&sd->bl, hp, 0, 1) < hp) + { //At full. + sd->inchealhptick = 0; + return; } } - if(bhp!=sd->status.hp) - clif_updatestatus(sd,SP_HP); - - if(sd->nshealhp > 0) { - if(sd->inchealhptick >= battle_config.natural_heal_skill_interval && sd->status.hp < sd->status.max_hp) { - bonus = sd->nshealhp; - while(sd->inchealhptick >= battle_config.natural_heal_skill_interval) { - sd->inchealhptick -= battle_config.natural_heal_skill_interval; - if(sd->status.hp + bonus <= sd->status.max_hp) - sd->status.hp += bonus; - else { - bonus = sd->status.max_hp - sd->status.hp; - sd->status.hp = sd->status.max_hp; - sd->hp_sub = sd->inchealhptick = 0; - } - clif_heal(sd->fd,SP_HP,bonus); - } + + if(sd->nshealhp <= 0) + { + sd->inchealhptick = 0; + return; + } + + while(sd->inchealhptick >= battle_config.natural_heal_skill_interval) + { + sd->inchealhptick -= battle_config.natural_heal_skill_interval; + if(status_heal(&sd->bl, sd->nshealhp, 0, 3) < sd->nshealhp) + { + sd->hp_sub = sd->inchealhptick = 0; + break; } } - else sd->inchealhptick = 0; - return 0; + return; } -static int pc_natural_heal_sp(struct map_session_data *sd) +static void pc_natural_heal_sp(struct map_session_data *sd) { - int bsp; + int sp; int inc_num,bonus; - nullpo_retr(0, sd); - if (sd->no_regen & 2) - return 0; + return; if(pc_checkoversp(sd)) { sd->sp_sub = sd->inchealsptick = 0; - return 0; + return; } - bsp=sd->status.sp; - inc_num = pc_spheal(sd); if(sd->sc.data[SC_EXPLOSIONSPIRITS].timer == -1 || (sd->sc.data[SC_SPIRIT].timer!=-1 && sd->sc.data[SC_SPIRIT].val2 == SL_MONK)) sd->sp_sub += inc_num; if(sd->ud.walktimer == -1) sd->inchealsptick += natural_heal_diff_tick; - else sd->inchealsptick = 0; + else + sd->inchealsptick = 0; if(sd->sp_sub >= battle_config.natural_healsp_interval){ - bonus = sd->nhealsp;; - while(sd->sp_sub >= battle_config.natural_healsp_interval){ + bonus = sd->nhealsp; + sp = 0; + do { sd->sp_sub -= battle_config.natural_healsp_interval; - if(sd->status.sp + bonus <= sd->status.max_sp) - sd->status.sp += bonus; - else { - sd->status.sp = sd->status.max_sp; - sd->sp_sub = sd->inchealsptick = 0; - } + sp += bonus; + } while(sd->sp_sub >= battle_config.natural_healsp_interval); + if (status_heal(&sd->bl, 0, sp, 1) < sp) { + sd->inchealsptick = 0; + return; } } - if(bsp != sd->status.sp) - clif_updatestatus(sd,SP_SP); - - if(sd->nshealsp > 0) { - if(sd->inchealsptick >= battle_config.natural_heal_skill_interval && sd->status.sp < sd->status.max_sp) { - if(sd->doridori_counter) { - bonus = sd->nshealsp*2; - sd->doridori_counter = 0; - } else - bonus = sd->nshealsp; - while(sd->inchealsptick >= battle_config.natural_heal_skill_interval) { - sd->inchealsptick -= battle_config.natural_heal_skill_interval; - if(sd->status.sp + bonus <= sd->status.max_sp) - sd->status.sp += bonus; - else { - bonus = sd->status.max_sp - sd->status.sp; - sd->status.sp = sd->status.max_sp; - sd->sp_sub = sd->inchealsptick = 0; - } - clif_heal(sd->fd,SP_SP,bonus); + if(sd->nshealsp <= 0) { + sd->inchealsptick = 0; + return; + } + if(sd->inchealsptick >= battle_config.natural_heal_skill_interval) + { + sp = 0; + if(sd->doridori_counter) { + bonus = sd->nshealsp*2; + sd->doridori_counter = 0; + } else + bonus = sd->nshealsp; + do { + sd->inchealsptick -= battle_config.natural_heal_skill_interval; + if (status_heal(&sd->bl, 0, bonus, 3) < sp) { + sd->sp_sub = sd->inchealsptick = 0; + break; } - } + } while(sd->inchealsptick >= battle_config.natural_heal_skill_interval); } - else sd->inchealsptick = 0; - return 0; + return; } -static int pc_spirit_heal_hp(struct map_session_data *sd) +static void pc_spirit_heal_hp(struct map_session_data *sd) { int bonus_hp,interval = battle_config.natural_heal_skill_interval; - nullpo_retr(0, sd); - if(pc_checkoverhp(sd)) { sd->inchealspirithptick = 0; - return 0; + return; } sd->inchealspirithptick += natural_heal_diff_tick; @@ -6815,39 +6613,31 @@ static int pc_spirit_heal_hp(struct map_session_data *sd) if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) interval += interval; - if(sd->inchealspirithptick >= interval) { - bonus_hp = sd->nsshealhp; - while(sd->inchealspirithptick >= interval) { - if(pc_issit(sd)) { - sd->inchealspirithptick -= interval; - if(sd->status.hp < sd->status.max_hp) { - if(sd->status.hp + bonus_hp <= sd->status.max_hp) - sd->status.hp += bonus_hp; - else { - bonus_hp = sd->status.max_hp - sd->status.hp; - sd->status.hp = sd->status.max_hp; - } - clif_heal(sd->fd,SP_HP,bonus_hp); - sd->inchealspirithptick = 0; - } - }else{ - sd->inchealspirithptick -= natural_heal_diff_tick; - break; - } + if(sd->inchealspirithptick < interval) + return; + + if(!pc_issit(sd)) + { + sd->inchealspirithptick -= natural_heal_diff_tick; + return; + } + bonus_hp = sd->nsshealhp; + while(sd->inchealspirithptick >= interval) { + sd->inchealspirithptick -= interval; + if(status_heal(&sd->bl, bonus_hp, 0, 3) < bonus_hp) { + sd->inchealspirithptick = 0; + break; } } - - return 0; + return; } -static int pc_spirit_heal_sp(struct map_session_data *sd) +static void pc_spirit_heal_sp(struct map_session_data *sd) { int bonus_sp,interval = battle_config.natural_heal_skill_interval; - nullpo_retr(0, sd); - if(pc_checkoversp(sd)) { sd->inchealspiritsptick = 0; - return 0; + return; } sd->inchealspiritsptick += natural_heal_diff_tick; @@ -6855,35 +6645,30 @@ static int pc_spirit_heal_sp(struct map_session_data *sd) if(sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) interval += interval; - if(sd->inchealspiritsptick >= interval) { - bonus_sp = sd->nsshealsp; - while(sd->inchealspiritsptick >= interval) { - if(pc_issit(sd)) { - sd->inchealspiritsptick -= interval; - if(sd->status.sp < sd->status.max_sp) { - if(sd->status.sp + bonus_sp <= sd->status.max_sp) - sd->status.sp += bonus_sp; - else { - bonus_sp = sd->status.max_sp - sd->status.sp; - sd->status.sp = sd->status.max_sp; - } - clif_heal(sd->fd,SP_SP,bonus_sp); - sd->inchealspiritsptick = 0; - } - }else{ - sd->inchealspiritsptick -= natural_heal_diff_tick; - break; - } + if(sd->inchealspiritsptick < interval) + return; + + if(!pc_issit(sd)) + { + sd->inchealspiritsptick -= natural_heal_diff_tick; + return; + } + bonus_sp = sd->nsshealsp; + while(sd->inchealspiritsptick >= interval) { + sd->inchealspiritsptick -= interval; + if(status_heal(&sd->bl, 0, bonus_sp, 3) < bonus_sp) + { + sd->inchealspiritsptick = 0; + break; } } - return 0; + return; } -static int pc_bleeding (struct map_session_data *sd) +static void pc_bleeding (struct map_session_data *sd) { int hp = 0, sp = 0; - nullpo_retr(0, sd); if (sd->hp_loss_value > 0) { sd->hp_loss_tick += natural_heal_diff_tick; @@ -6908,9 +6693,9 @@ static int pc_bleeding (struct map_session_data *sd) } if (hp > 0 || sp > 0) - pc_heal(sd,-hp,-sp); + status_zap(&sd->bl, hp, sp); - return 0; + return; } /*========================================== diff --git a/src/map/pc.h b/src/map/pc.h index ae3b2480f..ae7a78f80 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -64,7 +64,6 @@ enum { #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_iskiller(struct map_session_data *src, struct map_session_data *target); // [MouseJstr] int pc_getrefinebonus(int lv,int type); int pc_can_give_items(int level); //[Lupus] @@ -86,8 +85,8 @@ 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); -int pc_checkoverhp(struct map_session_data*); -int pc_checkoversp(struct map_session_data*); +#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); @@ -102,7 +101,6 @@ 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_checkitem(struct map_session_data*); 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); @@ -154,9 +152,9 @@ int pc_unequipitem(struct map_session_data*,int,int); int pc_checkitem(struct map_session_data*); int pc_useitem(struct map_session_data*,int); -int pc_damage_sp(struct map_session_data *, int, int); -int pc_damage(struct block_list *,struct map_session_data*,int); -int pc_heal(struct map_session_data *,int,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_heal(struct map_session_data *sd,unsigned int hp,unsigned int sp, int type); int pc_itemheal(struct map_session_data *sd,int hp,int sp); int pc_percentheal(struct map_session_data *sd,int,int); int pc_jobchange(struct map_session_data *,int, int); diff --git a/src/map/pet.c b/src/map/pet.c index 43c95df86..54ae71631 100644 --- a/src/map/pet.c +++ b/src/map/pet.c @@ -37,20 +37,6 @@ static struct eri *item_drop_list_ers; static int dirx[8]={0,-1,-1,-1,0,1,1,1}; static int diry[8]={1,1,0,-1,-1,-1,0,1}; -static int pet_performance_val(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); - - if(sd->pet.intimate > 900) - return (sd->petDB->s_perfor > 0)? 4:3; - else if(sd->pet.intimate > 750) - return 2; - else - return 1; -} - int pet_hungry_val(struct map_session_data *sd) { nullpo_retr(0, sd); @@ -182,19 +168,19 @@ int pet_target_check(struct map_session_data *sd,struct block_list *bl,int type) return 0; if(!type) { - rate = sd->petDB->attack_rate; + rate = pd->petDB->attack_rate; rate = rate * pd->rate_fix/1000; - if(sd->petDB->attack_rate > 0 && rate <= 0) + if(pd->petDB->attack_rate > 0 && rate <= 0) rate = 1; } else { - rate = sd->petDB->defence_attack_rate; + rate = pd->petDB->defence_attack_rate; rate = rate * pd->rate_fix/1000; - if(sd->petDB->defence_attack_rate > 0 && rate <= 0) + if(pd->petDB->defence_attack_rate > 0 && rate <= 0) rate = 1; } if(rand()%10000 < rate) { - if(pd->target_id == 0 || rand()%10000 < sd->petDB->change_target_rate) + if(pd->target_id == 0 || rand()%10000 < pd->petDB->change_target_rate) pd->target_id = bl->id; } @@ -226,23 +212,23 @@ int pet_sc_check(struct map_session_data *sd, int type) 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==NULL) + if(!sd) return 1; - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); + if(!sd->status.pet_id || !sd->pd) + return 1; - if(sd->pet_hungry_timer != tid){ + pd = sd->pd; + if(pd->pet_hungry_timer != tid){ if(battle_config.error_log) - ShowError("pet_hungry_timer %d != %d\n",sd->pet_hungry_timer,tid); + ShowError("pet_hungry_timer %d != %d\n",pd->pet_hungry_timer,tid); return 0; } - sd->pet_hungry_timer = -1; - if(!sd->status.pet_id || !sd->pd || !sd->petDB) - return 1; + pd->pet_hungry_timer = -1; if (sd->pet.intimate <= 0) return 1; //You lost the pet already, the rest is irrelevant. @@ -250,31 +236,25 @@ static int pet_hungry(int tid,unsigned int tick,int id,int data) sd->pet.hungry--; t = sd->pet.intimate; if(sd->pet.hungry < 0) { - pet_stop_attack(sd->pd); + pet_stop_attack(pd); sd->pet.hungry = 0; sd->pet.intimate -= battle_config.pet_hungry_friendly_decrease; if(sd->pet.intimate <= 0) { sd->pet.intimate = 0; - sd->pd->speed = sd->pd->db->speed; - if(battle_config.pet_status_support && t > 0) { - if(sd->bl.prev != NULL) - status_calc_pc(sd,0); - else - status_calc_pc(sd,2); - } + pd->status.speed = pd->db->status.speed; } - status_calc_pet(sd, 0); + status_calc_pet(pd, 0); clif_send_petdata(sd,1,sd->pet.intimate); } clif_send_petdata(sd,2,sd->pet.hungry); if(battle_config.pet_hungry_delay_rate != 100) - interval = (sd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; + interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; else - interval = sd->petDB->hungry_delay; + interval = pd->petDB->hungry_delay; if(interval <= 0) interval = 1; - sd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0); + pd->pet_hungry_timer = add_timer(tick+interval,pet_hungry,sd->bl.id,0); return 0; } @@ -314,71 +294,62 @@ int search_petDB_index(int key,int type) return -1; } -int pet_hungry_timer_delete(struct map_session_data *sd) +int pet_hungry_timer_delete(struct pet_data *pd) { - nullpo_retr(0, sd); - - if(sd->pet_hungry_timer != -1) { - delete_timer(sd->pet_hungry_timer,pet_hungry); - sd->pet_hungry_timer = -1; + nullpo_retr(0, pd); + if(pd->pet_hungry_timer != -1) { + delete_timer(pd->pet_hungry_timer,pet_hungry); + pd->pet_hungry_timer = -1; } - return 0; + return 1; } -int pet_performance(struct map_session_data *sd) +static int pet_performance(struct map_session_data *sd, struct pet_data *pd) { - struct pet_data *pd; + int val; - nullpo_retr(0, sd); - nullpo_retr(0, pd=sd->pd); - - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); + if (sd->pet.intimate > 900) + val = (pd->petDB->s_perfor > 0)? 4:3; + else if(sd->pet.intimate > 750) + val = 2; + else + val = 1; pet_stop_walking(pd,2000<<8); - clif_pet_performance(&pd->bl,rand()%pet_performance_val(sd) + 1); - // ルートしたItemを落とさせる + clif_pet_performance(&pd->bl,rand()%val + 1); pet_lootitem_drop(pd,NULL); - - return 0; + return 1; } -int pet_return_egg(struct map_session_data *sd) +static int pet_return_egg(struct map_session_data *sd, struct pet_data *pd) { struct item tmp_item; int flag; - nullpo_retr(0, sd); - - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); - - if(sd->status.pet_id && sd->pd) { - // ルートしたItemを落とさせる - pet_lootitem_drop(sd->pd,sd); - if(sd->petDB == NULL) - return 1; - memset(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid = sd->petDB->EggID; - tmp_item.identify = 1; - tmp_item.card[0] = (short)0xff00; - tmp_item.card[1] = GetWord(sd->pet.pet_id,0); - tmp_item.card[2] = GetWord(sd->pet.pet_id,1); - tmp_item.card[3] = sd->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); - } - sd->pet.incuvate = 1; - intif_save_petdata(sd->status.account_id,&sd->pet); - unit_free(&sd->pd->bl); - if(battle_config.pet_status_support && sd->pet.intimate > 0) - status_calc_pc(sd,0); - memset(&sd->pet, 0, sizeof(struct s_pet)); - sd->status.pet_id = 0; - sd->petDB = NULL; + pet_lootitem_drop(pd,sd); + memset(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid = pd->petDB->EggID; + tmp_item.identify = 1; + tmp_item.card[0] = (short)0xff00; + tmp_item.card[1] = GetWord(sd->pet.pet_id,0); + tmp_item.card[2] = GetWord(sd->pet.pet_id,1); + tmp_item.card[3] = sd->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); } + sd->pet.incuvate = 1; + intif_save_petdata(sd->status.account_id,&sd->pet); + if(pd->state.skillbonus) { + pd->state.skillbonus = 0; + status_calc_pc(sd,0); + } + unit_free(&pd->bl); + memset(&sd->pet, 0, sizeof(struct s_pet)); + sd->status.pet_id = 0; - return 0; + return 1; } int pet_data_init(struct map_session_data *sd) @@ -411,8 +382,8 @@ int pet_data_init(struct map_session_data *sd) sd->status.pet_id = 0; return 1; } - sd->petDB = &pet_db[i]; sd->pd = pd = (struct pet_data *)aCalloc(1,sizeof(struct pet_data)); + pd->petDB = &pet_db[i]; pd->bl.m = sd->bl.m; pd->bl.x = sd->bl.x; pd->bl.y = sd->bl.y; @@ -424,36 +395,30 @@ int pet_data_init(struct map_session_data *sd) pd->class_ = sd->pet.class_; pd->db = mob_db(pd->class_); pd->equip = sd->pet.equip; - pd->speed = sd->petDB->speed; pd->bl.subtype = MONS; pd->bl.type = BL_PET; pd->msd = sd; status_set_viewdata(&pd->bl,pd->class_); - unit_dataset(&sd->pd->bl); + unit_dataset(&pd->bl); pd->ud.dir = sd->ud.dir; pd->last_thinktime = gettick(); map_addiddb(&pd->bl); // initialise - if (battle_config.pet_lv_rate) //[Skotlex] - pd->status = (struct pet_status *) aCalloc(1,sizeof(struct pet_status)); - - status_calc_pet(sd,1); + status_calc_pet(pd,1); - pd->state.skillbonus = -1; + pd->state.skillbonus = 0; if (battle_config.pet_status_support) //Skotlex run_script(pet_db[i].script,0,sd->bl.id,0); - if(sd->pet_hungry_timer != -1) - pet_hungry_timer_delete(sd); if(battle_config.pet_hungry_delay_rate != 100) - interval = (sd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; + interval = (pd->petDB->hungry_delay*battle_config.pet_hungry_delay_rate)/100; else - interval = sd->petDB->hungry_delay; + interval = pd->petDB->hungry_delay; if(interval <= 0) interval = 1; - sd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0); + pd->pet_hungry_timer = add_timer(gettick()+interval,pet_hungry,sd->bl.id,0); return 0; } @@ -536,12 +501,6 @@ int pet_recv_petdata(int account_id,struct s_pet *p,int flag) clif_send_petstatus(sd); } } - if(battle_config.pet_status_support && sd->pet.intimate > 0) { - if(sd->bl.prev != NULL) - status_calc_pc(sd,0); - else - status_calc_pc(sd,2); - } return 0; } @@ -613,7 +572,7 @@ int pet_catch_process2(struct map_session_data *sd,int target_id) 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->db->mode&0x20)) + 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. @@ -626,7 +585,7 @@ int pet_catch_process2(struct map_session_data *sd,int target_id) // if(battle_config.etc_log) // printf("mob_id = %d, mob_class = %d\n",md->bl.id,md->class_); //成功の場合 - pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->db->lv)*30 + sd->paramc[5]*20)*(200 - md->hp*100/md->db->max_hp)/100; + pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->db->lv)*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; @@ -654,34 +613,40 @@ int pet_get_egg(int account_id,int pet_id,int flag) struct item tmp_item; int i=0,ret=0; - if(!flag) { - sd = map_id2sd(account_id); - if(sd == NULL) - return 1; - - i = search_petDB_index(sd->catch_target_class,PET_CLASS); - sd->catch_target_class = -1; + if(flag) + return 0; - if(i >= 0) { - memset(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid = pet_db[i].EggID; - tmp_item.identify = 1; - tmp_item.card[0] = (short)0xff00; - 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); - } - } - else - intif_delete_petdata(pet_id); + 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; } - return 0; + memset(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid = pet_db[i].EggID; + tmp_item.identify = 1; + tmp_item.card[0] = (short)0xff00; + 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); @@ -689,7 +654,7 @@ int pet_menu(struct map_session_data *sd,int menunum) return 1; //You lost the pet already. - if(sd->pet.intimate <= 0) + if(sd->pet.intimate <= 0 || !sd->status.pet_id) return 1; switch(menunum) { @@ -697,16 +662,16 @@ int pet_menu(struct map_session_data *sd,int menunum) clif_send_petstatus(sd); break; case 1: - pet_food(sd); + pet_food(sd, sd->pd); break; case 2: - pet_performance(sd); + pet_performance(sd, sd->pd); break; case 3: - pet_return_egg(sd); + pet_return_egg(sd, sd->pd); break; case 4: - pet_unequipitem(sd); + pet_unequipitem(sd, sd->pd); break; } return 0; @@ -730,11 +695,8 @@ int pet_change_name(struct map_session_data *sd,char *name) memcpy(sd->pet.name, name, NAME_LENGTH-1); memcpy(sd->pd->name, name, NAME_LENGTH-1); - - clif_clearchar_area(&sd->pd->bl,0); - clif_spawn(&sd->pd->bl); - clif_send_petdata(sd,0,0); - clif_send_petdata(sd,5,battle_config.pet_hair_style); + + clif_charnameack (0,&sd->pd->bl); sd->pet.rename_flag = 1; clif_pet_equip(sd->pd,sd->pet.equip); clif_send_petstatus(sd); @@ -744,55 +706,51 @@ int pet_change_name(struct map_session_data *sd,char *name) int pet_equipitem(struct map_session_data *sd,int index) { + struct pet_data *pd; int nameid; nullpo_retr(1, sd); - + pd = sd->pd; + nullpo_retr(1, pd); + nameid = sd->status.inventory[index].nameid; - if(sd->petDB == NULL) - return 1; - if(sd->petDB->AcceID == 0 || nameid != sd->petDB->AcceID || sd->pet.equip != 0) { + + if(pd->petDB->AcceID == 0 || nameid != pd->petDB->AcceID || sd->pet.equip != 0) { clif_equipitemack(sd,0,0,0); return 1; } - else { - pc_delitem(sd,index,1,0); - sd->pet.equip = sd->pd->equip = nameid; - status_calc_pc(sd,0); - clif_pet_equip(sd->pd,nameid); - if (battle_config.pet_equip_required) - { //Skotlex: start support timers if needd - if (sd->pd->s_skill && sd->pd->s_skill->timer == -1) - { - if (sd->pd->s_skill->id) - sd->pd->s_skill->timer=add_timer(gettick()+sd->pd->s_skill->delay*1000, pet_skill_support_timer, sd->bl.id, 0); - else - sd->pd->s_skill->timer=add_timer(gettick()+sd->pd->s_skill->delay*1000, pet_heal_timer, sd->bl.id, 0); - } - if (sd->pd->bonus && sd->pd->bonus->timer == -1) - sd->pd->bonus->timer=add_timer(gettick()+sd->pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); + + pc_delitem(sd,index,1,0); + sd->pet.equip = pd->equip = nameid; + clif_pet_equip(pd,nameid); + 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; } -int pet_unequipitem(struct map_session_data *sd) +static int pet_unequipitem(struct map_session_data *sd, struct pet_data *pd) { struct item tmp_item; int nameid,flag; - nullpo_retr(1, sd); - - if(sd->petDB == NULL) - return 1; if(sd->pet.equip == 0) return 1; nameid = sd->pet.equip; - sd->pet.equip = sd->pd->equip = 0; - status_calc_pc(sd,0); - clif_pet_equip(sd->pd,0); + sd->pet.equip = pd->equip = 0; + clif_pet_equip(pd,0); memset(&tmp_item,0,sizeof(tmp_item)); tmp_item.nameid = nameid; tmp_item.identify = 1; @@ -802,46 +760,47 @@ int pet_unequipitem(struct map_session_data *sd) } if (battle_config.pet_equip_required) { //Skotlex: halt support timers if needed - if (sd->pd->s_skill && sd->pd->s_skill->timer != -1) + if(pd->state.skillbonus) { + pd->state.skillbonus = 0; + status_calc_pc(sd,0); + } + if (pd->s_skill && pd->s_skill->timer != -1) { - if (sd->pd->s_skill->id) - delete_timer(sd->pd->s_skill->timer, pet_skill_support_timer); + if (pd->s_skill->id) + delete_timer(pd->s_skill->timer, pet_skill_support_timer); else - delete_timer(sd->pd->s_skill->timer, pet_heal_timer); - sd->pd->s_skill->timer = -1; + delete_timer(pd->s_skill->timer, pet_heal_timer); + pd->s_skill->timer = -1; } - if (sd->pd->bonus && sd->pd->bonus->timer != -1) + if (pd->bonus && pd->bonus->timer != -1) { - delete_timer(sd->pd->bonus->timer, pet_skill_bonus_timer); - sd->pd->bonus->timer = -1; + delete_timer(pd->bonus->timer, pet_skill_bonus_timer); + pd->bonus->timer = -1; } } return 0; } -int pet_food(struct map_session_data *sd) +static int pet_food(struct map_session_data *sd, struct pet_data *pd) { int i,k; - nullpo_retr(1, sd); - - if(sd->petDB == NULL) - return 1; - i=pc_search_inventory(sd,sd->petDB->FoodID); + k=pd->petDB->FoodID; + i=pc_search_inventory(sd,k); if(i < 0) { - clif_pet_food(sd,sd->petDB->FoodID,0); + clif_pet_food(sd,k,0); return 1; } pc_delitem(sd,i,1,0); if(sd->pet.hungry > 90) - sd->pet.intimate -= sd->petDB->r_full; + sd->pet.intimate -= pd->petDB->r_full; else { if(battle_config.pet_friendly_rate != 100) - k = (sd->petDB->r_hungry * battle_config.pet_friendly_rate)/100; + k = (pd->petDB->r_hungry * battle_config.pet_friendly_rate)/100; else - k = sd->petDB->r_hungry; + k = pd->petDB->r_hungry; if(sd->pet.hungry > 75) { k = k >> 1; if(k <= 0) @@ -851,26 +810,19 @@ int pet_food(struct map_session_data *sd) } if(sd->pet.intimate <= 0) { sd->pet.intimate = 0; - pet_stop_attack(sd->pd); - sd->pd->speed = sd->pd->db->speed; - - if(battle_config.pet_status_support) { - if(sd->bl.prev != NULL) - status_calc_pc(sd,0); - else - status_calc_pc(sd,2); - } + pet_stop_attack(pd); + pd->status.speed = pd->db->status.speed; } else if(sd->pet.intimate > 1000) sd->pet.intimate = 1000; - status_calc_pet(sd, 0); - sd->pet.hungry += sd->petDB->fullness; + status_calc_pet(pd, 0); + sd->pet.hungry += pd->petDB->fullness; if(sd->pet.hungry > 100) sd->pet.hungry = 100; clif_send_petdata(sd,2,sd->pet.hungry); clif_send_petdata(sd,1,sd->pet.intimate); - clif_pet_food(sd,sd->petDB->FoodID,1); + clif_pet_food(sd,pd->petDB->FoodID,1); return 0; } @@ -878,14 +830,11 @@ int pet_food(struct map_session_data *sd) static int pet_randomwalk(struct pet_data *pd,unsigned int tick) { const int retrycount=20; - int speed; nullpo_retr(0, pd); Assert((pd->msd == 0) || (pd->msd->pd == pd)); - speed = status_get_speed(&pd->bl); - 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; @@ -910,9 +859,9 @@ static int pet_randomwalk(struct pet_data *pd,unsigned int tick) } for(i=c=0;iud.walkpath.path_len;i++){ if(pd->ud.walkpath.path[i]&1) - c+=speed*14/10; + c+=pd->status.speed*14/10; else - c+=speed; + c+=pd->status.speed; } pd->next_walktime = tick+rand()%3000+3000+c; @@ -921,15 +870,10 @@ static int pet_randomwalk(struct pet_data *pd,unsigned int tick) return 0; } -static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) +static int pet_ai_sub_hard(struct pet_data *pd, struct map_session_data *sd, unsigned int tick) { - struct map_session_data *sd; struct block_list *target = NULL; - sd = pd->msd; - - Assert((sd->status.pet_id == 0 || sd->pd == 0) || sd->pd->msd == sd); - if(pd->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL) return 0; @@ -956,19 +900,19 @@ static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) if(pd->ud.walktimer != -1 && pd->ud.target == sd->bl.id) return 0; //Already walking to him - pd->speed = (sd->speed>>1); - if(pd->speed <= 0) - pd->speed = 1; + 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->speed != sd->petDB->speed) { + if (pd->status.speed != pd->petDB->speed) { if (pd->ud.walktimer != -1) return 0; //Wait until the pet finishes walking back to master. - pd->speed = sd->petDB->speed; + pd->status.speed = pd->petDB->speed; } if (pd->target_id) { @@ -981,7 +925,6 @@ static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) } } - // ペットによるルート 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. int itc=0; @@ -1009,9 +952,9 @@ static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) if (target->type != BL_ITEM) { //enemy targetted - if(!battle_check_range(&pd->bl,target,pd->db->range)) + if(!battle_check_range(&pd->bl,target,pd->status.rhw.range)) { //Chase - if(!unit_walktobl(&pd->bl, target, pd->db->range, 2)) + if(!unit_walktobl(&pd->bl, target, pd->status.rhw.range, 2)) pet_unlocktarget(pd); //Unreachable target. return 0; } @@ -1039,10 +982,9 @@ static int pet_ai_sub_hard(struct pet_data *pd,unsigned int tick) static int pet_ai_sub_foreachclient(struct map_session_data *sd,va_list ap) { - unsigned int tick; - tick=va_arg(ap,unsigned int); - if(sd->status.pet_id && sd->pd && sd->petDB) - pet_ai_sub_hard(sd->pd,tick); + 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; } @@ -1054,7 +996,7 @@ static int pet_ai_hard(int tid,unsigned int tick,int id,int data) return 0; } -int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) +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; @@ -1065,13 +1007,7 @@ int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap) itc=va_arg(ap,int *); sd_id = fitem->first_get_id; - // Removed [Valaris] - //if((pd->lootitem_weight + (itemdb_search(fitem->item_data.))->weight * fitem->item_data.amount) > battle_config.pet_weight) - // return 0; - if(pd->loot == NULL || pd->loot->item == NULL || (pd->loot->count >= pd->loot->max) || - (sd_id && pd->msd && pd->msd->bl.id != sd_id)) - return 0; if(bl->m == pd->bl.m && unit_can_reach_bl(&pd->bl,bl, pd->db->range2, 1, NULL, NULL) && rand()%1000<1000/(++(*itc))) pd->target_id=bl->id; @@ -1169,21 +1105,20 @@ int pet_skill_bonus_timer(int tid,unsigned int tick,int id,int data) } // determine the time for the next timer - if (pd->state.skillbonus == 0) { - // pet bonuses are not active at the moment, so, - pd->state.skillbonus = 1; - timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect - } else if (pd->state.skillbonus == 1) { - // pet bonuses are already active, so, + if (pd->state.skillbonus) { pd->state.skillbonus = 0; timer = pd->bonus->delay*1000; // the duration until pet bonuses will be reactivated again if (timer <= 0) //Always active bonus timer = MIN_PETTHINKTIME; + } else if (sd->pet.intimate) { + pd->state.skillbonus = 1; + timer = pd->bonus->duration*1000; // the duration for pet bonuses to be in effect + } else { //Lost pet... + pd->bonus->timer = -1; + return 0; } - // add/remove our bonuses 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); @@ -1226,6 +1161,7 @@ int pet_recovery_timer(int tid,unsigned int tick,int id,int data) 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; @@ -1233,16 +1169,18 @@ int pet_heal_timer(int tid,unsigned int tick,int id,int data) 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 = sd->status.sp*100/sd->status.max_sp) > pd->s_skill->sp || - (rate = sd->status.hp*100/sd->status.max_hp) > pd->s_skill->hp || + (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); @@ -1251,7 +1189,7 @@ int pet_heal_timer(int tid,unsigned int tick,int id,int data) pet_stop_attack(pd); pet_stop_walking(pd,1); clif_skill_nodamage(&pd->bl,&sd->bl,AL_HEAL,pd->s_skill->lv,1); - battle_heal(&pd->bl, &sd->bl, pd->s_skill->lv,0, 0); + 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; } @@ -1264,6 +1202,7 @@ 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; @@ -1276,9 +1215,11 @@ int pet_skill_support_timer(int tid,unsigned int tick,int id,int data) return 0; } + status = status_get_status_data(&sd->bl); + if(pc_isdead(sd) || - (rate = sd->status.sp*100/sd->status.max_sp) > pd->s_skill->sp || - (rate = sd->status.hp*100/sd->status.max_hp) > pd->s_skill->hp || + (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_skill_support_timer,sd->bl.id,0); diff --git a/src/map/pet.h b/src/map/pet.h index 8278ce9d8..16a0a4b89 100644 --- a/src/map/pet.h +++ b/src/map/pet.h @@ -38,7 +38,7 @@ 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 map_session_data *sd); +int pet_hungry_timer_delete(struct pet_data *pd); int pet_data_init(struct map_session_data *sd); int pet_birth_process(struct map_session_data *sd); int pet_recv_petdata(int account_id,struct s_pet *p,int flag); @@ -49,10 +49,7 @@ 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 pet_equipitem(struct map_session_data *sd,int index); -int pet_unequipitem(struct map_session_data *sd); -int pet_food(struct map_session_data *sd); int pet_lootitem_drop(struct pet_data *pd,struct map_session_data *sd); -int pet_ai_sub_hard_lootsearch(struct block_list *bl,va_list ap); 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] diff --git a/src/map/script.c b/src/map/script.c index d6d5e7123..a8d0f0692 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -1,12057 +1,12055 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -//#define DEBUG_FUNCIN -//#define DEBUG_DISP -//#define DEBUG_RUN - -#include -#include -#include -#include -#include - -#ifndef _WIN32 -#include -#endif - -#include - -#include "../common/socket.h" -#include "../common/timer.h" -#include "../common/malloc.h" -#include "../common/lock.h" -#include "../common/nullpo.h" -#include "../common/showmsg.h" -#include "../common/strlib.h" - -#include "map.h" -#include "clif.h" -#include "chrif.h" -#include "itemdb.h" -#include "pc.h" -#include "status.h" -#include "script.h" -#include "storage.h" -#include "mob.h" -#include "npc.h" -#include "pet.h" -#include "intif.h" -#include "skill.h" -#include "chat.h" -#include "battle.h" -#include "party.h" -#include "guild.h" -#include "atcommand.h" -#include "charcommand.h" -#include "log.h" -#include "unit.h" -#include "irc.h" - -#define SCRIPT_BLOCK_SIZE 256 - -#define FETCH(n, t) \ - if(st->end>st->start+(n)) \ - (t)=conv_num(st,&(st->stack->stack_data[st->start+(n)])); - -enum { LABEL_NEXTLINE=1,LABEL_START }; -static unsigned char * script_buf = NULL; -static int script_pos,script_size; - -char *str_buf; -int str_pos,str_size; -static struct str_data_struct { - int type; - int str; - int backpatch; - int label; - int (*func)(struct script_state *); - int val; - int next; -} *str_data = NULL; -int str_num=LABEL_START,str_data_size; -int str_hash[16]; - -static struct dbt *mapreg_db=NULL; -static struct dbt *mapregstr_db=NULL; -static int mapreg_dirty=-1; -char mapreg_txt[256]="save/mapreg.txt"; -#define MAPREG_AUTOSAVE_INTERVAL (10*1000) - -static struct dbt *scriptlabel_db=NULL; -static struct dbt *userfunc_db=NULL; - -struct dbt* script_get_label_db(){ return scriptlabel_db; } -struct dbt* script_get_userfunc_db(){ return userfunc_db; } - -static char pos[11][100] = {"頭","体","左手","右手","ローブ","靴","アクセサリー1","アクセサリー2","頭2","頭3","装着していない"}; - -struct Script_Config script_config; - -static int parse_cmd; - -// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc ) -// [Eoe / jA 1080, 1081, 1094, 1164] -enum { TYPE_NULL = 0 , TYPE_IF , TYPE_SWITCH , TYPE_WHILE , TYPE_FOR , TYPE_DO , TYPE_USERFUNC}; -static struct { - struct { - int type; - int index; - int count; - int flag; - } curly[256]; // 右カッコの情報 - int curly_count; // 右カッコの数 - int index; // スクリプト内で使用した構文の数 -} syntax; -unsigned char* parse_curly_close(unsigned char *p); -unsigned char* parse_syntax_close(unsigned char *p); -unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag); -unsigned char* parse_syntax(unsigned char *p); -static int parse_syntax_for_flag = 0; - -extern int current_equip_item_index; //for New CARS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus] -int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex] -int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0; -int potion_target=0; - -#if !defined(TXT_ONLY) && defined(MAPREGSQL) -// [zBuffer] SQL Mapreg Saving/Loading Database Declaration -char mapregsql_db[32] = "mapreg"; -char mapregsql_db_varname[32] = "varname"; -char mapregsql_db_index[32] = "index"; -char mapregsql_db_value[32] = "value"; -char tmp_sql[65535]; -// -------------------------------------------------------- -#endif - -static struct linkdb_node *sleep_db; -#define not_server_variable(prefix) (prefix != '$' && prefix != '.') - -/*========================================== - * ローカルプロトタイプ宣言 (必要な物のみ) - *------------------------------------------ - */ -unsigned char* parse_subexpr(unsigned char *,int); -#ifndef TXT_ONLY -int buildin_query_sql(struct script_state *st); -int buildin_escape_sql(struct script_state *st); -#endif -int buildin_atoi(struct script_state *st); -int buildin_axtoi(struct script_state *st); -int buildin_mes(struct script_state *st); -int buildin_goto(struct script_state *st); -int buildin_callsub(struct script_state *st); -int buildin_callfunc(struct script_state *st); -int buildin_return(struct script_state *st); -int buildin_getarg(struct script_state *st); -int buildin_next(struct script_state *st); -int buildin_close(struct script_state *st); -int buildin_close2(struct script_state *st); -int buildin_menu(struct script_state *st); -int buildin_rand(struct script_state *st); -int buildin_warp(struct script_state *st); -int buildin_areawarp(struct script_state *st); -int buildin_warpchar(struct script_state *st); // [LuzZza] -int buildin_warpparty(struct script_state *st); //[Fredzilla] -int buildin_warpguild(struct script_state *st); //[Fredzilla] -int buildin_heal(struct script_state *st); -int buildin_itemheal(struct script_state *st); -int buildin_percentheal(struct script_state *st); -int buildin_jobchange(struct script_state *st); -int buildin_input(struct script_state *st); -int buildin_setlook(struct script_state *st); -int buildin_set(struct script_state *st); -int buildin_setarray(struct script_state *st); -int buildin_cleararray(struct script_state *st); -int buildin_copyarray(struct script_state *st); -int buildin_getarraysize(struct script_state *st); -int buildin_deletearray(struct script_state *st); -int buildin_getelementofarray(struct script_state *st); -int buildin_getitem(struct script_state *st); -int buildin_getitem2(struct script_state *st); -int buildin_getnameditem(struct script_state *st); -int buildin_grouprandomitem(struct script_state *st); -int buildin_makeitem(struct script_state *st); -int buildin_delitem(struct script_state *st); -int buildin_delitem2(struct script_state *st); -int buildin_enableitemuse(struct script_state *st); -int buildin_disableitemuse(struct script_state *st); -int buildin_viewpoint(struct script_state *st); -int buildin_countitem(struct script_state *st); -int buildin_countitem2(struct script_state *st); -int buildin_checkweight(struct script_state *st); -int buildin_readparam(struct script_state *st); -int buildin_getcharid(struct script_state *st); -int buildin_getpartyname(struct script_state *st); -int buildin_getpartymember(struct script_state *st); -int buildin_getguildname(struct script_state *st); -int buildin_getguildmaster(struct script_state *st); -int buildin_getguildmasterid(struct script_state *st); -int buildin_strcharinfo(struct script_state *st); -int buildin_getequipid(struct script_state *st); -int buildin_getequipname(struct script_state *st); -int buildin_getbrokenid(struct script_state *st); // [Valaris] -int buildin_repair(struct script_state *st); // [Valaris] -int buildin_getequipisequiped(struct script_state *st); -int buildin_getequipisenableref(struct script_state *st); -int buildin_getequipisidentify(struct script_state *st); -int buildin_getequiprefinerycnt(struct script_state *st); -int buildin_getequipweaponlv(struct script_state *st); -int buildin_getequippercentrefinery(struct script_state *st); -int buildin_successrefitem(struct script_state *st); -int buildin_failedrefitem(struct script_state *st); -int buildin_cutin(struct script_state *st); -int buildin_cutincard(struct script_state *st); -int buildin_statusup(struct script_state *st); -int buildin_statusup2(struct script_state *st); -int buildin_bonus(struct script_state *st); -int buildin_bonus2(struct script_state *st); -int buildin_bonus3(struct script_state *st); -int buildin_bonus4(struct script_state *st); -int buildin_skill(struct script_state *st); -int buildin_addtoskill(struct script_state *st); // [Valaris] -int buildin_guildskill(struct script_state *st); -int buildin_getskilllv(struct script_state *st); -int buildin_getgdskilllv(struct script_state *st); -int buildin_basicskillcheck(struct script_state *st); -int buildin_getgmlevel(struct script_state *st); -int buildin_end(struct script_state *st); -int buildin_checkoption(struct script_state *st); -int buildin_setoption(struct script_state *st); -int buildin_setcart(struct script_state *st); -int buildin_checkcart(struct script_state *st); // check cart [Valaris] -int buildin_setfalcon(struct script_state *st); -int buildin_checkfalcon(struct script_state *st); // check falcon [Valaris] -int buildin_setriding(struct script_state *st); -int buildin_checkriding(struct script_state *st); // check for pecopeco [Valaris] -int buildin_savepoint(struct script_state *st); -int buildin_gettimetick(struct script_state *st); -int buildin_gettime(struct script_state *st); -int buildin_gettimestr(struct script_state *st); -int buildin_openstorage(struct script_state *st); -int buildin_guildopenstorage(struct script_state *st); -int buildin_itemskill(struct script_state *st); -int buildin_produce(struct script_state *st); -int buildin_monster(struct script_state *st); -int buildin_areamonster(struct script_state *st); -int buildin_killmonster(struct script_state *st); -int buildin_killmonsterall(struct script_state *st); -int buildin_clone(struct script_state *st); -int buildin_doevent(struct script_state *st); -int buildin_donpcevent(struct script_state *st); -int buildin_addtimer(struct script_state *st); -int buildin_deltimer(struct script_state *st); -int buildin_addtimercount(struct script_state *st); -int buildin_initnpctimer(struct script_state *st); -int buildin_stopnpctimer(struct script_state *st); -int buildin_startnpctimer(struct script_state *st); -int buildin_setnpctimer(struct script_state *st); -int buildin_getnpctimer(struct script_state *st); -int buildin_attachnpctimer(struct script_state *st); // [celest] -int buildin_detachnpctimer(struct script_state *st); // [celest] -int buildin_playerattached(struct script_state *st); // [Skotlex] -int buildin_announce(struct script_state *st); -int buildin_mapannounce(struct script_state *st); -int buildin_areaannounce(struct script_state *st); -int buildin_getusers(struct script_state *st); -int buildin_getmapusers(struct script_state *st); -int buildin_getareausers(struct script_state *st); -int buildin_getareadropitem(struct script_state *st); -int buildin_enablenpc(struct script_state *st); -int buildin_disablenpc(struct script_state *st); -int buildin_enablearena(struct script_state *st); // Added by RoVeRT -int buildin_disablearena(struct script_state *st); // Added by RoVeRT -int buildin_hideoffnpc(struct script_state *st); -int buildin_hideonnpc(struct script_state *st); -int buildin_sc_start(struct script_state *st); -int buildin_sc_start2(struct script_state *st); -int buildin_sc_start4(struct script_state *st); -int buildin_sc_end(struct script_state *st); -int buildin_getscrate(struct script_state *st); -int buildin_debugmes(struct script_state *st); -int buildin_catchpet(struct script_state *st); -int buildin_birthpet(struct script_state *st); -int buildin_resetlvl(struct script_state *st); -int buildin_resetstatus(struct script_state *st); -int buildin_resetskill(struct script_state *st); -int buildin_skillpointcount(struct script_state *st); -int buildin_changebase(struct script_state *st); -int buildin_changesex(struct script_state *st); -int buildin_waitingroom(struct script_state *st); -int buildin_delwaitingroom(struct script_state *st); -int buildin_enablewaitingroomevent(struct script_state *st); -int buildin_disablewaitingroomevent(struct script_state *st); -int buildin_getwaitingroomstate(struct script_state *st); -int buildin_warpwaitingpc(struct script_state *st); -int buildin_attachrid(struct script_state *st); -int buildin_detachrid(struct script_state *st); -int buildin_isloggedin(struct script_state *st); -int buildin_setmapflagnosave(struct script_state *st); -int buildin_setmapflag(struct script_state *st); -int buildin_removemapflag(struct script_state *st); -int buildin_pvpon(struct script_state *st); -int buildin_pvpoff(struct script_state *st); -int buildin_gvgon(struct script_state *st); -int buildin_gvgoff(struct script_state *st); -int buildin_emotion(struct script_state *st); -int buildin_maprespawnguildid(struct script_state *st); -int buildin_agitstart(struct script_state *st); // -int buildin_agitend(struct script_state *st); -int buildin_agitcheck(struct script_state *st); // -int buildin_flagemblem(struct script_state *st); // Flag Emblem -int buildin_getcastlename(struct script_state *st); -int buildin_getcastledata(struct script_state *st); -int buildin_setcastledata(struct script_state *st); -int buildin_requestguildinfo(struct script_state *st); -int buildin_getequipcardcnt(struct script_state *st); -int buildin_successremovecards(struct script_state *st); -int buildin_failedremovecards(struct script_state *st); -int buildin_marriage(struct script_state *st); -int buildin_wedding_effect(struct script_state *st); -int buildin_divorce(struct script_state *st); -int buildin_ispartneron(struct script_state *st); // MouseJstr -int buildin_getpartnerid(struct script_state *st); // MouseJstr -int buildin_getchildid(struct script_state *st); // Skotlex -int buildin_getmotherid(struct script_state *st); // Lupus -int buildin_getfatherid(struct script_state *st); // Lupus -int buildin_warppartner(struct script_state *st); // MouseJstr -int buildin_getitemname(struct script_state *st); -int buildin_getitemslots(struct script_state *st); -int buildin_makepet(struct script_state *st); -int buildin_getexp(struct script_state *st); -int buildin_getinventorylist(struct script_state *st); -int buildin_getskilllist(struct script_state *st); -int buildin_clearitem(struct script_state *st); -int buildin_classchange(struct script_state *st); -int buildin_misceffect(struct script_state *st); -int buildin_soundeffect(struct script_state *st); -int buildin_soundeffectall(struct script_state *st); -int buildin_setcastledata(struct script_state *st); -int buildin_mapwarp(struct script_state *st); -int buildin_inittimer(struct script_state *st); -int buildin_stoptimer(struct script_state *st); -int buildin_cmdothernpc(struct script_state *st); -int buildin_mobcount(struct script_state *st); -int buildin_strmobinfo(struct script_state *st); // Script for displaying mob info [Valaris] -int buildin_guardian(struct script_state *st); // Script for displaying mob info [Valaris] -int buildin_guardianinfo(struct script_state *st); // Script for displaying mob info [Valaris] -int buildin_petskillbonus(struct script_state *st); // petskillbonus [Valaris] -int buildin_petrecovery(struct script_state *st); // pet skill for curing status [Valaris] -int buildin_petloot(struct script_state *st); // pet looting [Valaris] -int buildin_petheal(struct script_state *st); // pet healing [Valaris] -//int buildin_petmag(struct script_state *st); // pet magnificat [Valaris] -int buildin_petskillattack(struct script_state *st); // pet skill attacks [Skotlex] -int buildin_petskillattack2(struct script_state *st); // pet skill attacks [Skotlex] -int buildin_petskillsupport(struct script_state *st); // pet support skill [Valaris] -int buildin_skilleffect(struct script_state *st); // skill effects [Celest] -int buildin_npcskilleffect(struct script_state *st); // skill effects for npcs [Valaris] -int buildin_specialeffect(struct script_state *st); // special effect script [Valaris] -int buildin_specialeffect2(struct script_state *st); // special effect script [Valaris] -int buildin_nude(struct script_state *st); // nude [Valaris] -int buildin_atcommand(struct script_state *st); // [MouseJstr] -int buildin_charcommand(struct script_state *st); // [MouseJstr] -int buildin_movenpc(struct script_state *st); // [MouseJstr] -int buildin_message(struct script_state *st); // [MouseJstr] -int buildin_npctalk(struct script_state *st); // [Valaris] -int buildin_hasitems(struct script_state *st); // [Valaris] -int buildin_getlook(struct script_state *st); //Lorky [Lupus] -int buildin_getsavepoint(struct script_state *st); //Lorky [Lupus] -int buildin_npcspeed(struct script_state *st); // [Valaris] -int buildin_npcwalkto(struct script_state *st); // [Valaris] -int buildin_npcstop(struct script_state *st); // [Valaris] -int buildin_getmapxy(struct script_state *st); //get map position for player/npc/pet/mob by Lorky [Lupus] -int buildin_checkoption1(struct script_state *st); // [celest] -int buildin_checkoption2(struct script_state *st); // [celest] -int buildin_guildgetexp(struct script_state *st); // [celest] -int buildin_guildchangegm(struct script_state *st); // [Skotlex] -int buildin_skilluseid(struct script_state *st); // originally by Qamera [celest] -int buildin_skillusepos(struct script_state *st); // originally by Qamera [celest] -int buildin_logmes(struct script_state *st); // [Lupus] -int buildin_summon(struct script_state *st); // [celest] -int buildin_isnight(struct script_state *st); // [celest] -int buildin_isday(struct script_state *st); // [celest] -int buildin_isequipped(struct script_state *st); // [celest] -int buildin_isequippedcnt(struct script_state *st); // [celest] -int buildin_cardscnt(struct script_state *st); // [Lupus] -int buildin_getrefine(struct script_state *st); // [celest] -int buildin_adopt(struct script_state *st); -int buildin_night(struct script_state *st); -int buildin_day(struct script_state *st); -int buildin_getusersname(struct script_state *st); //jA commands added [Lupus] -int buildin_dispbottom(struct script_state *st); -int buildin_recovery(struct script_state *st); -int buildin_getpetinfo(struct script_state *st); -int buildin_checkequipedcard(struct script_state *st); -int buildin_globalmes(struct script_state *st); -int buildin_jump_zero(struct script_state *st); -int buildin_select(struct script_state *st); -int buildin_getmapmobs(struct script_state *st); //jA addition end -int buildin_unequip(struct script_state *st); // unequip [Spectre] -int buildin_getstrlen(struct script_state *st); //strlen [valaris] -int buildin_charisalpha(struct script_state *st);//isalpha [valaris] -int buildin_fakenpcname(struct script_state *st); // [Lance] -int buildin_compare(struct script_state *st); // Lordalfa, to bring strstr to Scripting Engine -int buildin_getiteminfo(struct script_state *st); //[Lupus] returns Items Buy / sell Price, etc info -int buildin_getequipcardid(struct script_state *st); //[Lupus] returns card id from quipped item card slot N -// [zBuffer] List of mathematics commands ---> -int buildin_sqrt(struct script_state *st); -int buildin_pow(struct script_state *st); -int buildin_distance(struct script_state *st); -// <--- [zBuffer] List of mathematics commands -// [zBuffer] List of dynamic var commands ---> -int buildin_getd(struct script_state *st); -int buildin_setd(struct script_state *st); -// <--- [zBuffer] List of dynamic var commands -int buildin_petstat(struct script_state *st); // [Lance] Pet Stat Rq: Dubby -int buildin_callshop(struct script_state *st); // [Skotlex] -int buildin_npcshopitem(struct script_state *st); // [Lance] -int buildin_equip(struct script_state *st); -int buildin_autoequip(struct script_state *st); -int buildin_setbattleflag(struct script_state *st); -int buildin_getbattleflag(struct script_state *st); -// [zBuffer] List of player cont commands ---> -int buildin_rid2name(struct script_state *st); -int buildin_pcwalkxy(struct script_state *st); -int buildin_pctalk(struct script_state *st); -int buildin_pcemote(struct script_state *st); -int buildin_pcfollow(struct script_state *st); -int buildin_pcstopfollow(struct script_state *st); -int buildin_pcblockmove(struct script_state *st); -// <--- [zBuffer] List of player cont commands -// [zBuffer] List of mob control commands ---> -int buildin_spawnmob(struct script_state *st); -int buildin_removemob(struct script_state *st); -int buildin_mobwalk(struct script_state *st); -int buildin_getmobdata(struct script_state *st); -int buildin_setmobdata(struct script_state *st); -int buildin_mobattack(struct script_state *st); -int buildin_mobrandomwalk(struct script_state *st); -int buildin_mobstop(struct script_state *st); -int buildin_mobassist(struct script_state *st); -int buildin_mobtalk(struct script_state *st); -int buildin_mobemote(struct script_state *st); -int buildin_mobattach(struct script_state *st); -// <--- [zBuffer] List of mob control commands -int buildin_sleep(struct script_state *st); -int buildin_sleep2(struct script_state *st); -int buildin_awake(struct script_state *st); -int buildin_getvariableofnpc(struct script_state *st); -void push_val(struct script_stack *stack,int type,int val); -int run_func(struct script_state *st); - -int mapreg_setreg(int num,int val); -int mapreg_setregstr(int num,const char *str); - -int buildin_setitemscript(struct script_state *st); -int buildin_disguise(struct script_state *st); -int buildin_undisguise(struct script_state *st); -int buildin_getmonsterinfo(struct script_state *st); // [Lupus] - -#ifdef PCRE_SUPPORT -int buildin_defpattern(struct script_state *st); // MouseJstr -int buildin_activatepset(struct script_state *st); // MouseJstr -int buildin_deactivatepset(struct script_state *st); // MouseJstr -int buildin_deletepset(struct script_state *st); // MouseJstr -#endif - -struct { - int (*func)(struct script_state *); - char *name; - char *arg; -} buildin_func[]={ - {buildin_axtoi,"axtoi","s"}, -#ifndef TXT_ONLY - {buildin_query_sql, "query_sql", "s*"}, - {buildin_escape_sql, "escape_sql", "s"}, -#endif - {buildin_atoi,"atoi","s"}, - {buildin_mes,"mes","s"}, - {buildin_next,"next",""}, - {buildin_close,"close",""}, - {buildin_close2,"close2",""}, - {buildin_menu,"menu","*"}, - {buildin_goto,"goto","l"}, - {buildin_callsub,"callsub","i*"}, - {buildin_callfunc,"callfunc","s*"}, - {buildin_return,"return","*"}, - {buildin_getarg,"getarg","i"}, - {buildin_jobchange,"jobchange","i*"}, - {buildin_input,"input","*"}, - {buildin_warp,"warp","sii"}, - {buildin_areawarp,"areawarp","siiiisii"}, - {buildin_warpchar,"warpchar","siii"}, // [LuzZza] - {buildin_warpparty,"warpparty","siii"}, // [Fredzilla] - {buildin_warpguild,"warpguild","siii"}, // [Fredzilla] - {buildin_setlook,"setlook","ii"}, - {buildin_set,"set","ii"}, - {buildin_setarray,"setarray","ii*"}, - {buildin_cleararray,"cleararray","iii"}, - {buildin_copyarray,"copyarray","iii"}, - {buildin_getarraysize,"getarraysize","i"}, - {buildin_deletearray,"deletearray","ii"}, - {buildin_getelementofarray,"getelementofarray","ii"}, - {buildin_getitem,"getitem","ii**"}, - {buildin_getitem2,"getitem2","iiiiiiiii*"}, - {buildin_getnameditem,"getnameditem","is"}, - {buildin_grouprandomitem,"groupranditem","i"}, - {buildin_makeitem,"makeitem","iisii"}, - {buildin_delitem,"delitem","ii"}, - {buildin_delitem2,"delitem2","iiiiiiiii"}, - {buildin_enableitemuse,"enable_items",""}, - {buildin_disableitemuse,"disable_items",""}, - {buildin_cutin,"cutin","si"}, - {buildin_cutincard,"cutincard","i"}, - {buildin_viewpoint,"viewpoint","iiiii"}, - {buildin_heal,"heal","ii"}, - {buildin_itemheal,"itemheal","ii"}, - {buildin_percentheal,"percentheal","ii"}, - {buildin_rand,"rand","i*"}, - {buildin_countitem,"countitem","i"}, - {buildin_countitem2,"countitem2","iiiiiiii"}, - {buildin_checkweight,"checkweight","ii"}, - {buildin_readparam,"readparam","i*"}, - {buildin_getcharid,"getcharid","i*"}, - {buildin_getpartyname,"getpartyname","i"}, - {buildin_getpartymember,"getpartymember","i*"}, - {buildin_getguildname,"getguildname","i"}, - {buildin_getguildmaster,"getguildmaster","i"}, - {buildin_getguildmasterid,"getguildmasterid","i"}, - {buildin_strcharinfo,"strcharinfo","i"}, - {buildin_getequipid,"getequipid","i"}, - {buildin_getequipname,"getequipname","i"}, - {buildin_getbrokenid,"getbrokenid","i"}, // [Valaris] - {buildin_repair,"repair","i"}, // [Valaris] - {buildin_getequipisequiped,"getequipisequiped","i"}, - {buildin_getequipisenableref,"getequipisenableref","i"}, - {buildin_getequipisidentify,"getequipisidentify","i"}, - {buildin_getequiprefinerycnt,"getequiprefinerycnt","i"}, - {buildin_getequipweaponlv,"getequipweaponlv","i"}, - {buildin_getequippercentrefinery,"getequippercentrefinery","i"}, - {buildin_successrefitem,"successrefitem","i"}, - {buildin_failedrefitem,"failedrefitem","i"}, - {buildin_statusup,"statusup","i"}, - {buildin_statusup2,"statusup2","ii"}, - {buildin_bonus,"bonus","ii"}, - {buildin_bonus2,"bonus2","iii"}, - {buildin_bonus3,"bonus3","iiii"}, - {buildin_bonus4,"bonus4","iiiii"}, - {buildin_skill,"skill","ii*"}, - {buildin_addtoskill,"addtoskill","ii*"}, // [Valaris] - {buildin_guildskill,"guildskill","ii"}, - {buildin_getskilllv,"getskilllv","i"}, - {buildin_getgdskilllv,"getgdskilllv","ii"}, - {buildin_basicskillcheck,"basicskillcheck","*"}, - {buildin_getgmlevel,"getgmlevel","*"}, - {buildin_end,"end",""}, -// {buildin_end,"break",""}, this might confuse advanced scripting support [Eoe] - {buildin_checkoption,"checkoption","i"}, - {buildin_setoption,"setoption","i*"}, - {buildin_setcart,"setcart",""}, - {buildin_checkcart,"checkcart","*"}, //fixed by Lupus (added '*') - {buildin_setfalcon,"setfalcon",""}, - {buildin_checkfalcon,"checkfalcon","*"}, //fixed by Lupus (fixed wrong pointer, added '*') - {buildin_setriding,"setriding",""}, - {buildin_checkriding,"checkriding","*"}, //fixed by Lupus (fixed wrong pointer, added '*') - {buildin_savepoint,"save","sii"}, - {buildin_savepoint,"savepoint","sii"}, - {buildin_gettimetick,"gettimetick","i"}, - {buildin_gettime,"gettime","i"}, - {buildin_gettimestr,"gettimestr","si"}, - {buildin_openstorage,"openstorage",""}, - {buildin_guildopenstorage,"guildopenstorage","*"}, - {buildin_itemskill,"itemskill","iis"}, - {buildin_produce,"produce","i"}, - {buildin_monster,"monster","siisii*"}, - {buildin_areamonster,"areamonster","siiiisii*"}, - {buildin_killmonster,"killmonster","ss"}, - {buildin_killmonsterall,"killmonsterall","s"}, - {buildin_clone,"clone","siisi*"}, - {buildin_doevent,"doevent","s"}, - {buildin_donpcevent,"donpcevent","s"}, - {buildin_addtimer,"addtimer","is"}, - {buildin_deltimer,"deltimer","s"}, - {buildin_addtimercount,"addtimercount","si"}, - {buildin_initnpctimer,"initnpctimer","*"}, - {buildin_stopnpctimer,"stopnpctimer","*"}, - {buildin_startnpctimer,"startnpctimer","*"}, - {buildin_setnpctimer,"setnpctimer","*"}, - {buildin_getnpctimer,"getnpctimer","i*"}, - {buildin_attachnpctimer,"attachnpctimer","*"}, // attached the player id to the npc timer [Celest] - {buildin_detachnpctimer,"detachnpctimer","*"}, // detached the player id from the npc timer [Celest] - {buildin_playerattached,"playerattached",""}, // returns id of the current attached player. [Skotlex] - {buildin_announce,"announce","si*"}, - {buildin_mapannounce,"mapannounce","ssi*"}, - {buildin_areaannounce,"areaannounce","siiiisi*"}, - {buildin_getusers,"getusers","i"}, - {buildin_getmapusers,"getmapusers","s"}, - {buildin_getareausers,"getareausers","siiii"}, - {buildin_getareadropitem,"getareadropitem","siiiii"}, - {buildin_enablenpc,"enablenpc","s"}, - {buildin_disablenpc,"disablenpc","s"}, - {buildin_enablearena,"enablearena",""}, // Added by RoVeRT - {buildin_disablearena,"disablearena",""}, // Added by RoVeRT - {buildin_hideoffnpc,"hideoffnpc","s"}, - {buildin_hideonnpc,"hideonnpc","s"}, - {buildin_sc_start,"sc_start","iii*"}, - {buildin_sc_start2,"sc_start2","iiii*"}, - {buildin_sc_start4,"sc_start4","iiiiii*"}, - {buildin_sc_end,"sc_end","i"}, - {buildin_getscrate,"getscrate","ii*"}, - {buildin_debugmes,"debugmes","s"}, - {buildin_catchpet,"pet","i"}, - {buildin_birthpet,"bpet",""}, - {buildin_resetlvl,"resetlvl","i"}, - {buildin_resetstatus,"resetstatus",""}, - {buildin_resetskill,"resetskill",""}, - {buildin_skillpointcount,"skillpointcount",""}, - {buildin_changebase,"changebase","i"}, - {buildin_changesex,"changesex",""}, - {buildin_waitingroom,"waitingroom","si*"}, - {buildin_warpwaitingpc,"warpwaitingpc","sii"}, - {buildin_delwaitingroom,"delwaitingroom","*"}, - {buildin_enablewaitingroomevent,"enablewaitingroomevent","*"}, - {buildin_disablewaitingroomevent,"disablewaitingroomevent","*"}, - {buildin_getwaitingroomstate,"getwaitingroomstate","i*"}, - {buildin_warpwaitingpc,"warpwaitingpc","sii*"}, - {buildin_attachrid,"attachrid","i"}, - {buildin_detachrid,"detachrid",""}, - {buildin_isloggedin,"isloggedin","i"}, - {buildin_setmapflagnosave,"setmapflagnosave","ssii"}, - {buildin_setmapflag,"setmapflag","si*"}, - {buildin_removemapflag,"removemapflag","si"}, - {buildin_pvpon,"pvpon","s"}, - {buildin_pvpoff,"pvpoff","s"}, - {buildin_gvgon,"gvgon","s"}, - {buildin_gvgoff,"gvgoff","s"}, - {buildin_emotion,"emotion","i*"}, - {buildin_maprespawnguildid,"maprespawnguildid","sii"}, - {buildin_agitstart,"agitstart",""}, // - {buildin_agitend,"agitend",""}, - {buildin_agitcheck,"agitcheck","i"}, // - {buildin_flagemblem,"flagemblem","i"}, // Flag Emblem - {buildin_getcastlename,"getcastlename","s"}, - {buildin_getcastledata,"getcastledata","si*"}, - {buildin_setcastledata,"setcastledata","sii"}, - {buildin_requestguildinfo,"requestguildinfo","i*"}, - {buildin_getequipcardcnt,"getequipcardcnt","i"}, - {buildin_successremovecards,"successremovecards","i"}, - {buildin_failedremovecards,"failedremovecards","ii"}, - {buildin_marriage,"marriage","s"}, - {buildin_wedding_effect,"wedding",""}, - {buildin_divorce,"divorce",""}, - {buildin_ispartneron,"ispartneron",""}, - {buildin_getpartnerid,"getpartnerid",""}, - {buildin_getchildid,"getchildid",""}, - {buildin_getmotherid,"getmotherid",""}, - {buildin_getfatherid,"getfatherid",""}, - {buildin_warppartner,"warppartner","sii"}, - {buildin_getitemname,"getitemname","i"}, - {buildin_getitemslots,"getitemslots","i"}, - {buildin_makepet,"makepet","i"}, - {buildin_getexp,"getexp","ii"}, - {buildin_getinventorylist,"getinventorylist",""}, - {buildin_getskilllist,"getskilllist",""}, - {buildin_clearitem,"clearitem",""}, - {buildin_classchange,"classchange","ii"}, - {buildin_misceffect,"misceffect","i"}, - {buildin_soundeffect,"soundeffect","si"}, - {buildin_soundeffectall,"soundeffectall","si*"}, // SoundEffectAll [Codemaster] - {buildin_strmobinfo,"strmobinfo","ii"}, // display mob data [Valaris] - {buildin_guardian,"guardian","siisii*i"}, // summon guardians - {buildin_guardianinfo,"guardianinfo","i"}, // display guardian data [Valaris] - {buildin_petskillbonus,"petskillbonus","iiii"}, // [Valaris] - {buildin_petrecovery,"petrecovery","ii"}, // [Valaris] - {buildin_petloot,"petloot","i"}, // [Valaris] - {buildin_petheal,"petheal","iiii"}, // [Valaris] -// {buildin_petmag,"petmag","iiii"}, // [Valaris] - {buildin_petskillattack,"petskillattack","iiii"}, // [Skotlex] - {buildin_petskillattack2,"petskillattack2","iiiii"}, // [Valaris] - {buildin_petskillsupport,"petskillsupport","iiiii"}, // [Skotlex] - {buildin_skilleffect,"skilleffect","ii"}, // skill effect [Celest] - {buildin_npcskilleffect,"npcskilleffect","iiii"}, // npc skill effect [Valaris] - {buildin_specialeffect,"specialeffect","i"}, // npc skill effect [Valaris] - {buildin_specialeffect2,"specialeffect2","i"}, // skill effect on players[Valaris] - {buildin_nude,"nude",""}, // nude command [Valaris] - {buildin_mapwarp,"mapwarp","ssii"}, // Added by RoVeRT - {buildin_inittimer,"inittimer",""}, - {buildin_stoptimer,"stoptimer",""}, - {buildin_cmdothernpc,"cmdothernpc","ss"}, - {buildin_atcommand,"atcommand","*"}, // [MouseJstr] - {buildin_charcommand,"charcommand","*"}, // [MouseJstr] -// {buildin_movenpc,"movenpc","siis"}, // [MouseJstr] - {buildin_message,"message","s*"}, // [MouseJstr] - {buildin_npctalk,"npctalk","*"}, // [Valaris] - {buildin_hasitems,"hasitems","*"}, // [Valaris] - {buildin_mobcount,"mobcount","ss"}, - {buildin_getlook,"getlook","i"}, - {buildin_getsavepoint,"getsavepoint","i"}, - {buildin_npcspeed,"npcspeed","i"}, // [Valaris] - {buildin_npcwalkto,"npcwalkto","ii"}, // [Valaris] - {buildin_npcstop,"npcstop",""}, // [Valaris] - {buildin_getmapxy,"getmapxy","siii*"}, //by Lorky [Lupus] - {buildin_checkoption1,"checkoption1","i"}, - {buildin_checkoption2,"checkoption2","i"}, - {buildin_guildgetexp,"guildgetexp","i"}, - {buildin_guildchangegm,"guildchangegm","is"}, - {buildin_skilluseid,"skilluseid","ii"}, // originally by Qamera [Celest] - {buildin_skilluseid,"doskill","ii"}, // since a lot of scripts would already use 'doskill'... - {buildin_skillusepos,"skillusepos","iiii"}, // [Celest] - {buildin_logmes,"logmes","s"}, //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus] - {buildin_summon,"summon","si*"}, // summons a slave monster [Celest] - {buildin_isnight,"isnight",""}, // check whether it is night time [Celest] - {buildin_isday,"isday",""}, // check whether it is day time [Celest] - {buildin_isequipped,"isequipped","i*"}, // check whether another item/card has been equipped [Celest] - {buildin_isequippedcnt,"isequippedcnt","i*"}, // check how many items/cards are being equipped [Celest] - {buildin_cardscnt,"cardscnt","i*"}, // check how many items/cards are being equipped in the same arm [Lupus] - {buildin_getrefine,"getrefine","*"}, // returns the refined number of the current item, or an item with index specified [celest] - {buildin_adopt,"adopt","sss"}, // allows 2 parents to adopt a child - {buildin_night,"night",""}, // sets the server to night time - {buildin_day,"day",""}, // sets the server to day time -#ifdef PCRE_SUPPORT - {buildin_defpattern, "defpattern", "iss"}, // Define pattern to listen for [MouseJstr] - {buildin_activatepset, "activatepset", "i"}, // Activate a pattern set [MouseJstr] - {buildin_deactivatepset, "deactivatepset", "i"}, // Deactive a pattern set [MouseJstr] - {buildin_deletepset, "deletepset", "i"}, // Delete a pattern set [MouseJstr] -#endif - {buildin_dispbottom,"dispbottom","s"}, //added from jA [Lupus] - {buildin_getusersname,"getusersname","*"}, - {buildin_recovery,"recovery",""}, - {buildin_getpetinfo,"getpetinfo","i"}, - {buildin_checkequipedcard,"checkequipedcard","i"}, - {buildin_jump_zero,"jump_zero","ii"}, //for future jA script compatibility - {buildin_select,"select","*"}, //for future jA script compatibility - {buildin_globalmes,"globalmes","s*"}, - {buildin_getmapmobs,"getmapmobs","s"}, //end jA addition - {buildin_unequip,"unequip","i"}, // unequip command [Spectre] - {buildin_getstrlen,"getstrlen","s"}, //strlen [Valaris] - {buildin_charisalpha,"charisalpha","si"}, //isalpha [Valaris] - {buildin_fakenpcname,"fakenpcname","ssi"}, // [Lance] - {buildin_compare,"compare","ss"}, // Lordalfa - To bring strstr to scripting Engine. - {buildin_getiteminfo,"getiteminfo","ii"}, //[Lupus] returns Items Buy / sell Price, etc info - {buildin_getequipcardid,"getequipcardid","ii"}, //[Lupus] returns CARD ID or other info from CARD slot N of equipped item - // [zBuffer] List of mathematics commands ---> - {buildin_sqrt,"sqrt","i"}, - {buildin_pow,"pow","ii"}, - {buildin_distance,"distance","iiii"}, - // <--- [zBuffer] List of mathematics commands - // [zBuffer] List of dynamic var commands ---> - {buildin_getd,"getd","*"}, - {buildin_setd,"setd","*"}, - // <--- [zBuffer] List of dynamic var commands - {buildin_petstat,"petstat","i"}, - {buildin_callshop,"callshop","si"}, // [Skotlex] - {buildin_npcshopitem,"npcshopitem","*"}, // [Lance] - {buildin_equip,"equip","i"}, - {buildin_autoequip,"autoequip","ii"}, - {buildin_setbattleflag,"setbattleflag","ss"}, - {buildin_getbattleflag,"getbattleflag","s"}, - {buildin_setitemscript,"setitemscript","is"}, //Set NEW item bonus script. Lupus - {buildin_disguise,"disguise","i"}, //disguise player. Lupus - {buildin_undisguise,"undisguise","i"}, //undisguise player. Lupus - {buildin_getmonsterinfo,"getmonsterinfo","ii"}, //Lupus - // [zBuffer] List of player cont commands ---> - {buildin_rid2name,"rid2name","i"}, - {buildin_pcwalkxy,"pcwalkxy","iii"}, - {buildin_pctalk,"pctalk","is"}, - {buildin_pcemote,"pcemote","ii"}, - {buildin_pcfollow,"pcfollow","ii"}, - {buildin_pcstopfollow,"pcstopfollow","i"}, - {buildin_pcblockmove,"pcblockmove","ii"}, - // <--- [zBuffer] List of player cont commands - // [zBuffer] List of mob control commands ---> - {buildin_spawnmob,"spawnmob","*"}, - {buildin_removemob,"removemob","i"}, - {buildin_mobwalk,"mobwalk","i*"}, - {buildin_mobrandomwalk,"mobrandomwalk","ii"}, - {buildin_getmobdata,"getmobdata","i*"}, - {buildin_setmobdata,"setmobdata","iii"}, - {buildin_mobattack,"mobattack","i*"}, - {buildin_mobstop,"mobstop","i"}, - {buildin_mobassist,"mobassist","i*"}, - {buildin_mobtalk,"mobtalk","is"}, - {buildin_mobemote,"mobemote","ii"}, - {buildin_mobattach,"mobattach","i*"}, -// <--- [zBuffer] List of mob control commands -{buildin_sleep,"sleep","i"}, - {buildin_sleep2,"sleep2","i"}, - {buildin_awake,"awake","s"}, - {buildin_getvariableofnpc,"getvariableofnpc","is"}, - {NULL,NULL,NULL}, -}; - -enum { - C_NOP,C_POS,C_INT,C_PARAM,C_FUNC,C_STR,C_CONSTSTR,C_ARG, - C_NAME,C_EOL, C_RETINFO, - C_USERFUNC, C_USERFUNC_POS, // user defined functions - - C_LOR,C_LAND,C_LE,C_LT,C_GE,C_GT,C_EQ,C_NE, //operator - C_XOR,C_OR,C_AND,C_ADD,C_SUB,C_MUL,C_DIV,C_MOD,C_NEG,C_LNOT,C_NOT,C_R_SHIFT,C_L_SHIFT -}; - -//Reports on the console the src of an script error. -static void report_src(struct script_state *st) { - struct block_list *bl; - if (!st->oid) return; //Can't report source. - bl = map_id2bl(st->oid); - if (!bl) return; - switch (bl->type) { - case BL_NPC: - if (bl->m >=0) - ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y); - else - ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name); - - break; - default: - if (bl->m >=0) - ShowDebug("Source (Non-NPC): type %d at %s (%d,%d)\n", bl->type, map[bl->m].name, bl->x, bl->y); - else - ShowDebug("Source (Non-NPC): type %d (invisible/not on a map)\n", bl->type); - break; - } -} - -static void check_event(struct script_state *st, unsigned char *event){ - if(event != NULL && event[0] != '\0' && !strstr(event,"::")){ - ShowError("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n",event); - report_src(st); - } - return; -} -/*========================================== - * 文字列のハッシュを計算 - *------------------------------------------ - */ -static int calc_hash(const unsigned char *p) -{ - int h=0; - while(*p){ - h=(h<<1)+(h>>3)+(h>>5)+(h>>8); - h+=*p++; - } - return h&15; -} - -/*========================================== - * str_dataの中に名前があるか検索する - *------------------------------------------ - */ -// 既存のであれば番号、無ければ-1 -static int search_str(const unsigned char *p) -{ - int i; - i=str_hash[calc_hash(p)]; - while(i){ - if(strcmp(str_buf+str_data[i].str,(char *) p)==0){ - return i; - } - i=str_data[i].next; - } - return -1; -} - -/*========================================== - * str_dataに名前を登録 - *------------------------------------------ - */ -// 既存のであれば番号、無ければ登録して新規番号 -static int add_str(const unsigned char *p) -{ - int i; - char *lowcase; - - lowcase=aStrdup((char *) p); - for(i=0;lowcase[i];i++) - lowcase[i]=tolower(lowcase[i]); - if((i=search_str((unsigned char *) lowcase))>=0){ - aFree(lowcase); - return i; - } - aFree(lowcase); - - i=calc_hash(p); - if(str_hash[i]==0){ - str_hash[i]=str_num; - } else { - i=str_hash[i]; - for(;;){ - if(strcmp(str_buf+str_data[i].str,(char *) p)==0){ - return i; - } - if(str_data[i].next==0) - break; - i=str_data[i].next; - } - str_data[i].next=str_num; - } - if(str_num>=str_data_size){ - str_data_size+=128; - str_data=(struct str_data_struct *) aRealloc(str_data,sizeof(str_data[0])*str_data_size); - memset(str_data + (str_data_size - 128), '\0', 128); - } - while(str_pos+(int)strlen((char *) p)+1>=str_size){ - str_size+=256; - str_buf=(char *)aRealloc(str_buf,str_size); - memset(str_buf + (str_size - 256), '\0', 256); - } - strcpy(str_buf+str_pos, (char *) p); - str_data[str_num].type=C_NOP; - str_data[str_num].str=str_pos; - str_data[str_num].next=0; - str_data[str_num].func=NULL; - str_data[str_num].backpatch=-1; - str_data[str_num].label=-1; - str_pos+=(int)strlen( (char *) p)+1; - return str_num++; -} - - -/*========================================== - * スクリプトバッファサイズの確認と拡張 - *------------------------------------------ - */ -static void check_script_buf(int size) -{ - if(script_pos+size>=script_size){ - script_size+=SCRIPT_BLOCK_SIZE; - script_buf=(unsigned char *)aRealloc(script_buf,script_size); - memset(script_buf + script_size - SCRIPT_BLOCK_SIZE, '\0', - SCRIPT_BLOCK_SIZE); - } -} - -/*========================================== - * スクリプトバッファに1バイト書き込む - *------------------------------------------ - */ -static void add_scriptb(int a) -{ - check_script_buf(1); - script_buf[script_pos++]=a; -} - -/*========================================== - * スクリプトバッファにデータタイプを書き込む - *------------------------------------------ - */ -static void add_scriptc(int a) -{ - while(a>=0x40){ - add_scriptb((a&0x3f)|0x40); - a=(a-0x40)>>6; - } - add_scriptb(a&0x3f); -} - -/*========================================== - * スクリプトバッファに整数を書き込む - *------------------------------------------ - */ -static void add_scripti(int a) -{ - while(a>=0x40){ - add_scriptb(a|0xc0); - a=(a-0x40)>>6; - } - add_scriptb(a|0x80); -} - -/*========================================== - * スクリプトバッファにラベル/変数/関数を書き込む - *------------------------------------------ - */ -// 最大16Mまで -static void add_scriptl(int l) -{ - int backpatch = str_data[l].backpatch; - - switch(str_data[l].type){ - case C_POS: - case C_USERFUNC_POS: - add_scriptc(C_POS); - add_scriptb(str_data[l].label); - add_scriptb(str_data[l].label>>8); - add_scriptb(str_data[l].label>>16); - break; - case C_NOP: - case C_USERFUNC: - // ラベルの可能性があるのでbackpatch用データ埋め込み - add_scriptc(C_NAME); - str_data[l].backpatch=script_pos; - add_scriptb(backpatch); - add_scriptb(backpatch>>8); - add_scriptb(backpatch>>16); - break; - case C_INT: - add_scripti(str_data[l].val); - break; - default: - // もう他の用途と確定してるので数字をそのまま - add_scriptc(C_NAME); - add_scriptb(l); - add_scriptb(l>>8); - add_scriptb(l>>16); - break; - } -} - -/*========================================== - * ラベルを解決する - *------------------------------------------ - */ -void set_label(int l,int pos) -{ - int i,next; - - str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); - str_data[l].label=pos; - for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){ - next=(*(int*)(script_buf+i)) & 0x00ffffff; - script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); - script_buf[i]=pos; - script_buf[i+1]=pos>>8; - script_buf[i+2]=pos>>16; - i=next; - } -} - -/*========================================== - * スペース/コメント読み飛ばし - *------------------------------------------ - */ -static unsigned char *skip_space(unsigned char *p) -{ - while(1){ - while(isspace(*p)) - p++; - if(p[0]=='/' && p[1]=='/'){ - while(*p && *p!='\n') - p++; - } else if(p[0]=='/' && p[1]=='*'){ - p++; - while(*p && (p[-1]!='*' || p[0]!='/')) - p++; - if(*p) p++; - } else - break; - } - return p; -} - -/*========================================== - * 1単語スキップ - *------------------------------------------ - */ -static unsigned char *skip_word(unsigned char *p) -{ - // prefix - if(*p=='.') p++; - if(*p=='$') p++; // MAP鯖内共有変数用 - if(*p=='@') p++; // 一時的変数用(like weiss) - if(*p=='#') p++; // account変数用 - if(*p=='#') p++; // ワールドaccount変数用 - - while(isalnum(*p)||*p=='_'|| *p>=0x81) - if(*p>=0x81 && p[1]){ - p+=2; - } else - p++; - - // postfix - if(*p=='$') p++; // 文字列変数 - - return p; -} - -static unsigned char *startptr; -static int startline; - -/*========================================== - * エラーメッセージ出力 - *------------------------------------------ - */ -static void disp_error_message(const char *mes,const unsigned char *pos) -{ - int line,c=0,i; - unsigned char *p,*linestart,*lineend; - - for(line=startline,p=startptr;p && *p;line++){ - linestart=p; - lineend=(unsigned char *) strchr((char *) p,'\n'); - if(lineend){ - c=*lineend; - *lineend=0; - } - if(lineend==NULL || pos getelementofarray(name,i) ) - add_scriptl(search_str((unsigned char *) "getelementofarray")); - add_scriptc(C_ARG); - add_scriptl(l); - p=parse_subexpr(p+1,-1); - p=skip_space(p); - if((*p++)!=']'){ - disp_error_message("unmatch ']'",p); - exit(1); - } - add_scriptc(C_FUNC); - } else if(str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS) { - add_scriptl(search_str((unsigned char*)"callsub")); - add_scriptc(C_ARG); - add_scriptl(l); - }else - add_scriptl(l); - - } - -#ifdef DEBUG_FUNCIN - if(battle_config.etc_log) - ShowDebug("parse_simpleexpr end %s\n",p); -#endif - return p; -} - -/*========================================== - * 式の解析 - *------------------------------------------ - */ -unsigned char* parse_subexpr(unsigned char *p,int limit) -{ - int op,opl,len; - char *tmpp; - -#ifdef DEBUG_FUNCIN - if(battle_config.etc_log) - ShowDebug("parse_subexpr %s\n",p); -#endif - p=skip_space(p); - - if(*p=='-'){ - tmpp=(char *) skip_space((unsigned char *) (p+1)); - if(*tmpp==';' || *tmpp==','){ - add_scriptl(LABEL_NEXTLINE); - p++; - return p; - } - } - tmpp=(char *) p; - if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){ - p=parse_subexpr(p+1,8); - add_scriptc(op); - } else - p=parse_simpleexpr(p); - p=skip_space(p); - while(((op=C_ADD,opl=6,len=1,*p=='+') || - (op=C_SUB,opl=6,len=1,*p=='-') || - (op=C_MUL,opl=7,len=1,*p=='*') || - (op=C_DIV,opl=7,len=1,*p=='/') || - (op=C_MOD,opl=7,len=1,*p=='%') || - (op=C_FUNC,opl=9,len=1,*p=='(') || - (op=C_LAND,opl=1,len=2,*p=='&' && p[1]=='&') || - (op=C_AND,opl=5,len=1,*p=='&') || - (op=C_LOR,opl=0,len=2,*p=='|' && p[1]=='|') || - (op=C_OR,opl=4,len=1,*p=='|') || - (op=C_XOR,opl=3,len=1,*p=='^') || - (op=C_EQ,opl=2,len=2,*p=='=' && p[1]=='=') || - (op=C_NE,opl=2,len=2,*p=='!' && p[1]=='=') || - (op=C_R_SHIFT,opl=5,len=2,*p=='>' && p[1]=='>') || - (op=C_GE,opl=2,len=2,*p=='>' && p[1]=='=') || - (op=C_GT,opl=2,len=1,*p=='>') || - (op=C_L_SHIFT,opl=5,len=2,*p=='<' && p[1]=='<') || - (op=C_LE,opl=2,len=2,*p=='<' && p[1]=='=') || - (op=C_LT,opl=2,len=1,*p=='<')) && opl>limit){ - p+=len; - if(op==C_FUNC){ - int i=0,func=parse_cmd; - const char *plist[128]; - - if(str_data[parse_cmd].type == C_FUNC){ - // 通常の関数 - add_scriptc(C_ARG); - } else if(str_data[parse_cmd].type == C_USERFUNC || str_data[parse_cmd].type == C_USERFUNC_POS) { - // ユーザー定義関数呼び出し - parse_cmd = search_str((unsigned char*)"callsub"); - i++; - } else { - disp_error_message( - "expect command, missing function name or calling undeclared function",(unsigned char *) tmpp - ); - exit(0); - } - func=parse_cmd; - - do { - plist[i]=(char *) p; - p=parse_subexpr(p,-1); - p=skip_space(p); - if(*p==',') p++; - else if(*p!=')' && script_config.warn_func_no_comma){ - disp_error_message("expect ',' or ')' at func params",p); - } - p=skip_space(p); - i++; - } while(*p && *p!=')' && i<128); - plist[i]=(char *) p; - if(*(p++)!=')'){ - disp_error_message("func request '(' ')'",p); - exit(1); - } - - if (str_data[func].type == C_FUNC && script_config.warn_func_mismatch_paramnum) { - const char *arg = buildin_func[str_data[func].val].arg; - int j = 0; - for (; arg[j]; j++) if (arg[j] == '*') break; - if (!(i <= 1 && j == 0) && ((arg[j] == 0 && i != j) || (arg[j] == '*' && i < j))) { - disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i= 0) { - if(syntax.curly[pos].type == TYPE_DO) { - sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_FOR) { - sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_WHILE) { - sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_SWITCH) { - sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index); - break; - } - pos--; - } - if(pos < 0) { - disp_error_message("unexpected 'break'",p); - } else { - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - } - p = skip_word(p); - p++; - // if, for , while の閉じ判定 - p = parse_syntax_close(p + 1); - return p; - } - break; - case 'c': - if(!strncmp(p,"case",4) && !isalpha(*(p + 4))) { - // case の処理 - if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) { - disp_error_message("unexpected 'case' ",p); - return p+1; - } else { - char *p2; - char label[256]; - int l; - int pos = syntax.curly_count-1; - if(syntax.curly[pos].count != 1) { - // FALLTHRU 用のジャンプ - sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // 現在地のラベルを付ける - sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - } - // switch 判定文 - p = skip_word(p); - p = skip_space(p); - p2 = p; - p = skip_word(p); - p = skip_space(p); - if(*p != ':') { - disp_error_message("expect ':'",p); - exit(1); - } - *p = 0; - sprintf(label,"if(%s != $@__SW%x_VAL) goto __SW%x_%x;", - p2,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - *p = ':'; - // 2回parse しないとダメ - p2 = parse_line(label); - parse_line(p2); - syntax.curly_count--; - if(syntax.curly[pos].count != 1) { - // FALLTHRU 終了後のラベル - sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - } - // 一時変数を消す - sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - syntax.curly[pos].count++; - } - return p + 1; - } else if(!strncmp(p,"continue",8) && !isalpha(*(p + 8))) { - // continue の処理 - char label[256]; - int pos = syntax.curly_count - 1; - while(pos >= 0) { - if(syntax.curly[pos].type == TYPE_DO) { - sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index); - syntax.curly[pos].flag = 1; // continue 用のリンク張るフラグ - break; - } else if(syntax.curly[pos].type == TYPE_FOR) { - sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); - break; - } else if(syntax.curly[pos].type == TYPE_WHILE) { - sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); - break; - } - pos--; - } - if(pos < 0) { - disp_error_message("unexpected 'continue'",p); - } else { - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - } - p = skip_word(p); - p++; - // if, for , while の閉じ判定 - p = parse_syntax_close(p + 1); - return p; - } - break; - case 'd': - if(!strncmp(p,"default",7) && !isalpha(*(p + 7))) { - // switch - default の処理 - if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) { - disp_error_message("unexpected 'delault'",p); - return p+1; - } else if(syntax.curly[syntax.curly_count - 1].flag) { - disp_error_message("dup 'delault'",p); - return p+1; - } else { - char label[256]; - int l; - int pos = syntax.curly_count-1; - // 現在地のラベルを付ける - p = skip_word(p); - p = skip_space(p); - if(*p != ':') { - disp_error_message("need ':'",p); - } - p++; - sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - - // 無条件で次のリンクに飛ばす - sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // default のラベルを付ける - sprintf(label,"__SW%x_DEF",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - - syntax.curly[syntax.curly_count - 1].flag = 1; - syntax.curly[pos].count++; - - p = skip_word(p); - return p + 1; - } - } else if(!strncmp(p,"do",2) && !isalpha(*(p + 2))) { - int l; - char label[256]; - p=skip_word(p); - p=skip_space(p); - - syntax.curly[syntax.curly_count].type = TYPE_DO; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - // 現在地のラベル形成する - sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - syntax.curly_count++; - return p; - } - break; - case 'f': - if(!strncmp(p,"for",3) && !isalpha(*(p + 3))) { - int l; - unsigned char label[256]; - int pos = syntax.curly_count; - syntax.curly[syntax.curly_count].type = TYPE_FOR; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - syntax.curly_count++; - - p=skip_word(p); - p=skip_space(p); - - if(*p != '(') { - disp_error_message("need '('",p); - return p+1; - } - p++; - - // 初期化文を実行する - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - p=parse_line(p); - syntax.curly_count--; - - // 条件判断開始のラベル形成する - sprintf(label,"__FR%x_J",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - - if(*p == ';') { - // for(;;) のパターンなので必ず真 - ; - } else { - // 条件が偽なら終了地点に飛ばす - sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - } - if(*p != ';') { - disp_error_message("need ';'",p); - return p+1; - } - p++; - - // ループ開始に飛ばす - sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // 次のループへのラベル形成する - sprintf(label,"__FR%x_NXT",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - - // 次のループに入る時の処理 - // for 最後の '(' を ';' として扱うフラグ - parse_syntax_for_flag = 1; - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - p=parse_line(p); - syntax.curly_count--; - parse_syntax_for_flag = 0; - - // 条件判定処理に飛ばす - sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // ループ開始のラベル付け - sprintf(label,"__FR%x_BGN",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - return p; - } else if(!strncmp(p,"function",8) && !isalpha(*(p + 8))) { - unsigned char *func_name; - // function - p=skip_word(p); - p=skip_space(p); - // function - name - func_name = p; - p=skip_word(p); - if(*skip_space(p) == ';') { - // 関数の宣言 - 名前を登録して終わり - unsigned char c = *p; - int l; - *p = 0; - l=add_str(func_name); - *p = c; - if(str_data[l].type == C_NOP) { - str_data[l].type = C_USERFUNC; - } - return skip_space(p) + 1; - } else { - // 関数の中身 - char label[256]; - unsigned char c = *p; - int l; - syntax.curly[syntax.curly_count].type = TYPE_USERFUNC; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - syntax.curly_count++; - - // 関数終了まで飛ばす - sprintf(label,"goto __FN%x_FIN;",syntax.curly[syntax.curly_count-1].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // 関数名のラベルを付ける - *p = 0; - l=add_str(func_name); - if(str_data[l].type == C_NOP) { - str_data[l].type = C_USERFUNC; - } - if(str_data[l].label!=-1){ - *p=c; - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - strdb_put(scriptlabel_db,func_name,(void*)script_pos); // 外部用label db登録 - *p = c; - return skip_space(p); - } - } - break; - case 'i': - if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) { - // if() の処理 - char label[256]; - p=skip_word(p); - p=skip_space(p); - - syntax.curly[syntax.curly_count].type = TYPE_IF; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count); - syntax.curly_count++; - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - return p; - } - break; - case 's': - if(!strncmp(p,"switch",6) && !isalpha(*(p + 6))) { - // switch() の処理 - char label[256]; - syntax.curly[syntax.curly_count].type = TYPE_SWITCH; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index); - syntax.curly_count++; - add_scriptl(add_str((unsigned char*)"set")); - add_scriptc(C_ARG); - add_scriptl(add_str(label)); - p=skip_word(p); - p=skip_space(p); - p=parse_expr(p); - p=skip_space(p); - if(*p != '{') { - disp_error_message("need '{'",p); - } - add_scriptc(C_FUNC); - return p + 1; - } - break; - case 'w': - if(!strncmp(p,"while",5) && !isalpha(*(p + 5))) { - int l; - char label[256]; - p=skip_word(p); - p=skip_space(p); - - syntax.curly[syntax.curly_count].type = TYPE_WHILE; - syntax.curly[syntax.curly_count].count = 1; - syntax.curly[syntax.curly_count].index = syntax.index++; - syntax.curly[syntax.curly_count].flag = 0; - // 条件判断開始のラベル形成する - sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - - // 条件が偽なら終了地点に飛ばす - sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index); - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - syntax.curly_count++; - return p; - } - break; - } - return NULL; -} - -unsigned char* parse_syntax_close(unsigned char *p) { - // if(...) for(...) hoge(); のように、1度閉じられたら再度閉じられるか確認する - int flag; - - do { - p = parse_syntax_close_sub(p,&flag); - } while(flag); - return p; -} - -// if, for , while , do の閉じ判定 -// flag == 1 : 閉じられた -// flag == 0 : 閉じられない -unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag) { - unsigned char label[256]; - int pos = syntax.curly_count - 1; - int l; - *flag = 1; - - if(syntax.curly_count <= 0) { - *flag = 0; - return p; - } else if(syntax.curly[pos].type == TYPE_IF) { - char *p2 = p; - // if 最終場所へ飛ばす - sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // 現在地のラベルを付ける - sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - - syntax.curly[pos].count++; - p = skip_space(p); - if(!syntax.curly[pos].flag && !strncmp(p,"else",4) && !isalpha(*(p + 4))) { - // else or else - if - p = skip_word(p); - p = skip_space(p); - if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) { - // else - if - p=skip_word(p); - p=skip_space(p); - sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - *flag = 0; - return p; - } else { - // else - if(!syntax.curly[pos].flag) { - syntax.curly[pos].flag = 1; - *flag = 0; - return p; - } - } - } - // if 閉じ - syntax.curly_count--; - // 最終地のラベルを付ける - sprintf(label,"__IF%x_FIN",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - if(syntax.curly[pos].flag == 1) { - // このifに対するelseじゃないのでポインタの位置は同じ - return p2; - } - return p; - } else if(syntax.curly[pos].type == TYPE_DO) { - int l; - char label[256]; - unsigned char *p2; - - if(syntax.curly[pos].flag) { - // 現在地のラベル形成する(continue でここに来る) - sprintf(label,"__DO%x_NXT",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - } - - // 条件が偽なら終了地点に飛ばす - p = skip_space(p); - p2 = skip_word(p); - if(p2 - p != 5 || strncmp("while",p,5)) { - disp_error_message("need 'while'",p); - } - p = p2; - - sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); - add_scriptl(add_str("jump_zero")); - add_scriptc(C_ARG); - p=parse_expr(p); - p=skip_space(p); - add_scriptl(add_str(label)); - add_scriptc(C_FUNC); - - // 開始地点に飛ばす - sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // 条件終了地点のラベル形成する - sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - p = skip_space(p); - if(*p != ';') { - disp_error_message("need ';'",p); - return p+1; - } - p++; - syntax.curly_count--; - return p; - } else if(syntax.curly[pos].type == TYPE_FOR) { - // 次のループに飛ばす - sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // for 終了のラベル付け - sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - syntax.curly_count--; - return p; - } else if(syntax.curly[pos].type == TYPE_WHILE) { - // while 条件判断へ飛ばす - sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // while 終了のラベル付け - sprintf(label,"__WL%x_FIN",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - syntax.curly_count--; - return p; - } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) { - int pos = syntax.curly_count-1; - char label[256]; - int l; - // 戻す - sprintf(label,"return;"); - syntax.curly[syntax.curly_count++].type = TYPE_NULL; - parse_line(label); - syntax.curly_count--; - - // 現在地のラベルを付ける - sprintf(label,"__FN%x_FIN",syntax.curly[pos].index); - l=add_str(label); - if(str_data[l].label!=-1){ - disp_error_message("dup label ",p); - exit(1); - } - set_label(l,script_pos); - syntax.curly_count--; - return p + 1; - } else { - *flag = 0; - return p; - } -} - -/*========================================== - * 組み込み関数の追加 - *------------------------------------------ - */ -static void add_buildin_func(void) -{ - int i,n; - for(i=0;buildin_func[i].func;i++){ - n=add_str((unsigned char *) buildin_func[i].name); - str_data[n].type=C_FUNC; - str_data[n].val=i; - str_data[n].func=buildin_func[i].func; - } -} - -/*========================================== - * 定数データベースの読み込み - *------------------------------------------ - */ -static void read_constdb(void) -{ - FILE *fp; - char line[1024],name[1024]; - int val,n,i,type; - - sprintf(line, "%s/const.txt", db_path); - fp=fopen(line, "r"); - if(fp==NULL){ - ShowError("can't read %s\n", line); - return ; - } - while(fgets(line,1020,fp)){ - if(line[0]=='/' && line[1]=='/') - continue; - type=0; - if(sscanf(line,"%[A-Za-z0-9_],%d,%d",name,&val,&type)>=2 || - sscanf(line,"%[A-Za-z0-9_] %d %d",name,&val,&type)>=2){ - for(i=0;name[i];i++) - name[i]=tolower(name[i]); - n=add_str((const unsigned char *) name); - if(type==0) - str_data[n].type=C_INT; - else - str_data[n].type=C_PARAM; - str_data[n].val=val; - } - } - fclose(fp); -} - -/*========================================== - * スクリプトの解析 - *------------------------------------------ - */ -struct script_code* parse_script(unsigned char *src,int line) -{ - unsigned char *p, *tmpp; - int i; - struct script_code *code; - static int first = 1; - - if (first) { - add_buildin_func(); - read_constdb(); - } - first = 0; - -////////////////////////////////////////////// -// additional check on the input to filter empty scripts ("{}" and "{ }") - p = src; - p = skip_space(p); - if (*p != '{') { - disp_error_message("not found '{'", p); - return NULL; - } - p++; - p = skip_space(p); - if (*p == '}') { - // an empty function, just return - return NULL; - } - script_buf = (unsigned char *) aCallocA(SCRIPT_BLOCK_SIZE, sizeof(unsigned char)); - script_pos = 0; - script_size = SCRIPT_BLOCK_SIZE; - str_data[LABEL_NEXTLINE].type = C_NOP; - str_data[LABEL_NEXTLINE].backpatch = -1; - str_data[LABEL_NEXTLINE].label = -1; - for (i = LABEL_START; i < str_num; i++) { - if ( - str_data[i].type == C_POS || str_data[i].type == C_NAME || - str_data[i].type == C_USERFUNC || str_data[i].type == C_USERFUNC_POS - ) { - str_data[i].type = C_NOP; - str_data[i].backpatch = -1; - str_data[i].label = -1; - } - } - - //Labels must be reparsed for the script.... - scriptlabel_db->clear(scriptlabel_db, NULL); - - // for error message - startptr = src; - startline = line; - - while (p && *p && (*p != '}' || syntax.curly_count != 0)) { - p = skip_space(p); - // labelだけ特殊処理 - tmpp = skip_space(skip_word(p)); - if (*tmpp == ':' && !(!strncmp(p,"default",7) && !isalpha(*(p + 7)))) { - int l, c; - c = *skip_word(p); - *skip_word(p) = 0; - l = add_str(p); - if (str_data[l].label != -1) { - *skip_word(p) = c; - disp_error_message("dup label ", p); - exit(1); - } - set_label(l, script_pos); - strdb_put(scriptlabel_db, p, (void*)script_pos); // 外部用label db登録 - *skip_word(p) = c; - p = tmpp + 1; - continue; - } - - // 他は全部一緒くた - p = parse_line(p); - p = skip_space(p); - add_scriptc(C_EOL); - - set_label(LABEL_NEXTLINE, script_pos); - str_data[LABEL_NEXTLINE].type = C_NOP; - str_data[LABEL_NEXTLINE].backpatch = -1; - str_data[LABEL_NEXTLINE].label = -1; - } - - add_scriptc(C_NOP); - - script_size = script_pos; - script_buf = (unsigned char *)aRealloc(script_buf, script_pos + 1); - - // 未解決のラベルを解決 - for (i = LABEL_START; i < str_num; i++) { - if (str_data[i].type == C_NOP) { - int j, next; - str_data[i].type = C_NAME; - str_data[i].label = i; - for (j = str_data[i].backpatch; j >= 0 && j != 0x00ffffff; ) { - next = (*(int*)(script_buf+j)) & 0x00ffffff; - script_buf[j] = i; - script_buf[j+1] = i>>8; - script_buf[j+2] = i>>16; - j = next; - } - } - } - -#ifdef DEBUG_DISP - for (i = 0; i < script_pos; i++) { - if ((i & 15) == 0) printf("%04x : ", i); - printf("%02x ", script_buf[i]); - if((i&15) == 15) printf("\n"); - } - printf("\n"); -#endif - - startptr = NULL; //Clear pointer to prevent future references to a src that may be free'd. [Skotlex] - code = aCalloc(1, sizeof(struct script_code)); - code->script_buf = script_buf; - code->script_size = script_size; - code->script_vars = NULL; - return code; -} - -// -// 実行系 -// -enum {RUN = 0,STOP,END,RERUNLINE,GOTO,RETFUNC}; - -/*========================================== - * ridからsdへの解決 - *------------------------------------------ - */ -struct map_session_data *script_rid2sd(struct script_state *st) -{ - struct map_session_data *sd=map_id2sd(st->rid); - if(!sd){ - ShowError("script_rid2sd: fatal error ! player not attached!\n"); - report_src(st); - } - return sd; -} - - -/*========================================== - * 変数の読み取り - *------------------------------------------ - */ -int get_val(struct script_state*st,struct script_data* data) -{ - struct map_session_data *sd=NULL; - if(data->type==C_NAME){ - char *name=str_buf+str_data[data->u.num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - - if(not_server_variable(prefix)){ - if((sd=script_rid2sd(st))==NULL) - ShowError("get_val error name?:%s\n",name); - } - if(postfix=='$'){ - - data->type=C_CONSTSTR; - if( prefix=='@'){ - if(sd) - data->u.str = pc_readregstr(sd,data->u.num); - }else if(prefix=='$'){ - data->u.str = (char *)idb_get(mapregstr_db,data->u.num); - }else if(prefix=='#'){ - if( name[1]=='#'){ - if(sd) - data->u.str = pc_readaccountreg2str(sd,name); - }else{ - if(sd) - data->u.str = pc_readaccountregstr(sd,name); - } - }else if(prefix=='.') { - struct linkdb_node **n; - if( data->ref ) { - n = data->ref; - } else if( name[1] == '@' ) { - n = st->stack->var_function; - } else { - n = &st->script->script_vars; - } - data->u.str = linkdb_search(n, (void*)data->u.num ); - }else{ - if(sd) - data->u.str = pc_readglobalreg_str(sd,name); - } // [zBuffer] - /*else{ - ShowWarning("script: get_val: illegal scope string variable.\n"); - data->u.str = "!!ERROR!!"; - }*/ - if( data->u.str == NULL ) - data->u.str =""; - - }else{ - - data->type=C_INT; - if(str_data[data->u.num&0x00ffffff].type==C_INT){ - data->u.num = str_data[data->u.num&0x00ffffff].val; - }else if(str_data[data->u.num&0x00ffffff].type==C_PARAM){ - if(sd) - data->u.num = pc_readparam(sd,str_data[data->u.num&0x00ffffff].val); - }else if(prefix=='@'){ - if(sd) - data->u.num = pc_readreg(sd,data->u.num); - }else if(prefix=='$'){ - data->u.num = (int)idb_get(mapreg_db,data->u.num); - }else if(prefix=='#'){ - if( name[1]=='#'){ - if(sd) - data->u.num = pc_readaccountreg2(sd,name); - }else{ - if(sd) - data->u.num = pc_readaccountreg(sd,name); - } - }else if(prefix=='.'){ - struct linkdb_node **n; - if( data->ref ) { - n = data->ref; - } else if( name[1] == '@' ) { - n = st->stack->var_function; - } else { - n = &st->script->script_vars; - } - data->u.num = (int)linkdb_search(n, (void*)data->u.num); - }else{ - if(sd) - data->u.num = pc_readglobalreg(sd,name); - } - } - } - return 0; -} -/*========================================== - * 変数の読み取り2 - *------------------------------------------ - */ -void* get_val2(struct script_state*st,int num,struct linkdb_node **ref) -{ - struct script_data dat; - dat.type=C_NAME; - dat.u.num=num; - dat.ref = ref; - get_val(st,&dat); - if( dat.type==C_INT ) return (void*)dat.u.num; - else return (void*)dat.u.str; -} - -/*========================================== - * 変数設定用 - *------------------------------------------ - */ -static int set_reg(struct script_state*st,struct map_session_data *sd,int num,char *name,void *v,struct linkdb_node** ref) -{ - char prefix=*name; - char postfix=name[strlen(name)-1]; - - if( postfix=='$' ){ - char *str=(char*)v; - if( prefix=='@'){ - pc_setregstr(sd,num,str); - }else if(prefix=='$') { - mapreg_setregstr(num,str); - }else if(prefix=='#') { - if( name[1]=='#' ) - pc_setaccountreg2str(sd,name,str); - else - pc_setaccountregstr(sd,name,str); - }else if(prefix=='.') { - char *p; - struct linkdb_node **n; - if( ref ) { - n = ref; - } else if( name[1] == '@' ) { - n = st->stack->var_function; - } else { - n = &st->script->script_vars; - } - p = linkdb_search(n, (void*)num); - if(p) { - linkdb_erase(n, (void*)num); - aFree(p); - } - if( ((char*)v)[0] ) - linkdb_insert(n, (void*)num, aStrdup(v)); - }else{ - pc_setglobalreg_str(sd,name,str); - } // [zBuffer] - - /*else{ - ShowWarning("script: set_reg: illegal scope string variable !"); - }*/ - }else{ - // 数値 - int val = (int)v; - if(str_data[num&0x00ffffff].type==C_PARAM){ - pc_setparam(sd,str_data[num&0x00ffffff].val,val); - }else if(prefix=='@') { - pc_setreg(sd,num,val); - }else if(prefix=='$') { - mapreg_setreg(num,val); - }else if(prefix=='#') { - if( name[1]=='#' ) - pc_setaccountreg2(sd,name,val); - else - pc_setaccountreg(sd,name,val); - }else if(prefix == '.') { - struct linkdb_node **n; - if( ref ) { - n = ref; - } else if( name[1] == '@' ) { - n = st->stack->var_function; - } else { - n = &st->script->script_vars; - } - if( val == 0 ) { - linkdb_erase(n, (void*)num); - } else { - linkdb_replace(n, (void*)num, (void*)val); - } - }else{ - pc_setglobalreg(sd,name,val); - } - } - return 0; -} - -int set_var(struct map_session_data *sd, char *name, void *val) -{ - return set_reg(NULL, sd, add_str((unsigned char *) name), name, val, NULL); -} - -/*========================================== - * 文字列への変換 - *------------------------------------------ - */ -char* conv_str(struct script_state *st,struct script_data *data) -{ - get_val(st,data); - if(data->type==C_INT){ - char *buf; - buf=(char *)aMallocA(ITEM_NAME_LENGTH*sizeof(char)); - snprintf(buf,ITEM_NAME_LENGTH, "%d",data->u.num); - data->type=C_STR; - data->u.str=buf; -#if 1 - } else if(data->type==C_NAME){ - // テンポラリ。本来無いはず - data->type=C_CONSTSTR; - data->u.str=str_buf+str_data[data->u.num].str; -#endif - } - return data->u.str; -} - -/*========================================== - * 数値へ変換 - *------------------------------------------ - */ -int conv_num(struct script_state *st,struct script_data *data) -{ - char *p; - get_val(st,data); - if(data->type==C_STR || data->type==C_CONSTSTR){ - p=data->u.str; - data->u.num = atoi(p); - if(data->type==C_STR) - aFree(p); - data->type=C_INT; - } - return data->u.num; -} - -/*========================================== - * スタックへ数値をプッシュ - *------------------------------------------ - */ -void push_val(struct script_stack *stack,int type,int val) -{ - if(stack->sp >= stack->sp_max){ - stack->sp_max += 64; - stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, - sizeof(stack->stack_data[0]) * stack->sp_max); - memset(stack->stack_data + (stack->sp_max - 64), 0, - 64 * sizeof(*(stack->stack_data))); - } -// if(battle_config.etc_log) -// printf("push (%d,%d)-> %d\n",type,val,stack->sp); - stack->stack_data[stack->sp].type=type; - stack->stack_data[stack->sp].u.num=val; - stack->stack_data[stack->sp].ref = NULL; - stack->sp++; -} - -/*========================================== - * スタックへ数値+リファレンスをプッシュ - *------------------------------------------ - */ - -void push_val2(struct script_stack *stack,int type,int val,struct linkdb_node** ref) { - push_val(stack,type,val); - stack->stack_data[stack->sp-1].ref = ref; -} - -/*========================================== - * スタックへ文字列をプッシュ - *------------------------------------------ - */ -void push_str(struct script_stack *stack,int type,unsigned char *str) -{ - if(stack->sp>=stack->sp_max){ - stack->sp_max += 64; - stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, - sizeof(stack->stack_data[0]) * stack->sp_max); - memset(stack->stack_data + (stack->sp_max - 64), '\0', - 64 * sizeof(*(stack->stack_data))); - } -// if(battle_config.etc_log) -// printf("push (%d,%x)-> %d\n",type,str,stack->sp); - stack->stack_data[stack->sp].type=type; - stack->stack_data[stack->sp].u.str=(char *) str; - stack->stack_data[stack->sp].ref = NULL; - stack->sp++; -} - -/*========================================== - * スタックへ複製をプッシュ - *------------------------------------------ - */ -void push_copy(struct script_stack *stack,int pos) -{ - switch(stack->stack_data[pos].type){ - case C_CONSTSTR: - push_str(stack,C_CONSTSTR,(unsigned char *) stack->stack_data[pos].u.str); - break; - case C_STR: - push_str(stack,C_STR,(unsigned char *) aStrdup(stack->stack_data[pos].u.str)); - break; - default: - push_val2( - stack,stack->stack_data[pos].type,stack->stack_data[pos].u.num, - stack->stack_data[pos].ref - ); - break; - } -} - -/*========================================== - * スタックからポップ - *------------------------------------------ - */ -void pop_stack(struct script_stack* stack,int start,int end) -{ - int i; - for(i=start;istack_data[i].type==C_STR){ - aFree(stack->stack_data[i].u.str); - stack->stack_data[i].type=C_INT; //Might not be correct, but it's done in case to prevent pointer errors later on. [Skotlex] - } - } - if(stack->sp>end){ - memmove(&stack->stack_data[start],&stack->stack_data[end],sizeof(stack->stack_data[0])*(stack->sp-end)); - } - stack->sp-=end-start; -} - -/*========================================== - * スクリプト依存変数、関数依存変数の解放 - *------------------------------------------ - */ -void script_free_vars(struct linkdb_node **node) { - struct linkdb_node *n = *node; - while(n) { - char *name = str_buf + str_data[(int)(n->key)&0x00ffffff].str; - char postfix = name[strlen(name)-1]; - if( postfix == '$' ) { - // 文字型変数なので、データ削除 - aFree(n->data); - } - n = n->next; - } - linkdb_final( node ); -} - -/*========================================== - * Free's the whole stack. Invoked when clearing a character. [Skotlex] - *------------------------------------------ - */ -void script_free_stack(struct script_stack* stack) -{ - int i; - for (i = 0; i < stack->sp; i++) - { - if(stack->stack_data[i].type==C_STR) - { - //ShowDebug ("script_free_stack: freeing %p at sp=%d.\n", stack->stack_data[i].u.str, i); - aFree(stack->stack_data[i].u.str); - stack->stack_data[i].type = C_INT; - }else if( i > 0 && stack->stack_data[i].type == C_RETINFO ) { - struct linkdb_node** n = (struct linkdb_node**)stack->stack_data[i-1].u.num; - script_free_vars( n ); - aFree( n ); - } - } - script_free_vars( stack->var_function ); - aFree(stack->var_function); - aFree (stack->stack_data); - aFree (stack); -} - -void script_free_code(struct script_code* code) { - script_free_vars( &code->script_vars ); - aFree( code->script_buf ); - aFree( code ); -} - -int axtoi(char *hexStg) { - int n = 0; // position in string - int m = 0; // position in digit[] to shift - int count; // loop index - int intValue = 0; // integer value of hex string - int digit[11]; // hold values to convert - while (n < 10) { - if (hexStg[n]=='\0') - break; - if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9 - digit[n] = hexStg[n] & 0x0f; //convert to int - else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f - digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int - else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F - digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int - else break; - n++; - } - count = n; - m = n - 1; - n = 0; - while(n < count) { - // digit[n] is value of hex digit at position n - // (m << 2) is the number of positions to shift - // OR the bits into return value - intValue = intValue | (digit[n] << (m << 2)); - m--; // adjust the position to set - n++; // next digit to process - } - return (intValue); -} - -// [Lance] Hex string to integer converter -int buildin_axtoi(struct script_state *st) -{ - char *hex = conv_str(st,& (st->stack->stack_data[st->start+2])); - push_val(st->stack, C_INT, axtoi(hex)); - return 0; -} - -// -// 埋め込み関数 -// -/*========================================== - * - *------------------------------------------ - */ -int buildin_mes(struct script_state *st) -{ - conv_str(st,& (st->stack->stack_data[st->start+2])); - clif_scriptmes(script_rid2sd(st),st->oid,st->stack->stack_data[st->start+2].u.str); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_goto(struct script_state *st) -{ - int pos; - - if (st->stack->stack_data[st->start+2].type != C_POS){ - int func = st->stack->stack_data[st->start+2].u.num; - ShowMessage("script: goto '"CL_WHITE"%s"CL_RESET"': not label!\n", str_buf + str_data[func].str); - st->state = END; - return 1; - } - - pos = conv_num(st,& (st->stack->stack_data[st->start+2])); - st->pos = pos; - st->state = GOTO; - return 0; -} - -/*========================================== - * ユーザー定義関数の呼び出し - *------------------------------------------ - */ -int buildin_callfunc(struct script_state *st) -{ - struct script_code *scr, *oldscr; - char *str=conv_str(st,& (st->stack->stack_data[st->start+2])); - - if( (scr=(struct script_code *) strdb_get(userfunc_db,(unsigned char*)str)) ){ - int i,j; - struct linkdb_node **oldval = st->stack->var_function; - for(i=st->start+3,j=0;iend;i++,j++) - push_copy(st->stack,i); - - push_val(st->stack,C_INT,j); // 引数の数をプッシュ - push_val(st->stack,C_INT,st->stack->defsp); // 現在の基準スタックポインタをプッシュ - push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ - push_val(st->stack,C_INT,(int)st->stack->var_function); // 現在の関数依存変数をプッシュ - push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ - - oldscr = st->script; - st->pos=0; - st->script=scr; - st->stack->defsp=st->start+5+j; - st->state=GOTO; - st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*)); - - // ' 変数の引き継ぎ - for(i = 0; i < j; i++) { - struct script_data *s = &st->stack->stack_data[st->stack->sp-6-i]; - if( s->type == C_NAME && !s->ref ) { - char *name = str_buf+str_data[s->u.num&0x00ffffff].str; - // '@ 変数の引き継ぎ - if( name[0] == '.' && name[1] == '@' ) { - s->ref = oldval; - } else if( name[0] == '.' ) { - s->ref = &oldscr->script_vars; - } - } - } - }else{ - ShowWarning("script:callfunc: function not found! [%s]\n",str); - st->state=END; - return 1; - } - return 0; -} -/*========================================== - * サブルーティンの呼び出し - *------------------------------------------ - */ -int buildin_callsub(struct script_state *st) -{ - int pos=conv_num(st,& (st->stack->stack_data[st->start+2])); - int i,j; - if(st->stack->stack_data[st->start+2].type != C_POS && st->stack->stack_data[st->start+2].type != C_USERFUNC_POS) { - ShowError("script: callsub: not label !\n"); - st->state=END; - return 1; - } else { - struct linkdb_node **oldval = st->stack->var_function; - for(i=st->start+3,j=0;iend;i++,j++) - push_copy(st->stack,i); - - push_val(st->stack,C_INT,j); // 引数の数をプッシュ - push_val(st->stack,C_INT,st->stack->defsp); // 現在の基準スタックポインタをプッシュ - push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ - push_val(st->stack,C_INT,(int)st->stack->var_function); // 現在の関数依存変数をプッシュ - push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ - - st->pos=pos; - st->stack->defsp=st->start+5+j; - st->state=GOTO; - st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*)); - - // ' 変数の引き継ぎ - for(i = 0; i < j; i++) { - struct script_data *s = &st->stack->stack_data[st->stack->sp-6-i]; - if( s->type == C_NAME && !s->ref ) { - char *name = str_buf+str_data[s->u.num&0x00ffffff].str; - // '@ 変数の引き継ぎ - if( name[0] == '.' && name[1] == '@' ) { - s->ref = oldval; - } - } - } - } - return 0; -} - -/*========================================== - * 引数の所得 - *------------------------------------------ - */ -int buildin_getarg(struct script_state *st) -{ - int num=conv_num(st,& (st->stack->stack_data[st->start+2])); - int max,stsp; - if( st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO ){ - ShowWarning("script:getarg without callfunc or callsub!\n"); - st->state=END; - return 1; - } - max=conv_num(st,& (st->stack->stack_data[st->stack->defsp-5])); - stsp=st->stack->defsp - max -5; - if( num >= max ){ - ShowWarning("script:getarg arg1(%d) out of range(%d) !\n",num,max); - st->state=END; - return 1; - } - push_copy(st->stack,stsp+num); - return 0; -} - -/*========================================== - * サブルーチン/ユーザー定義関数の終了 - *------------------------------------------ - */ -int buildin_return(struct script_state *st) -{ - if(st->end>st->start+2){ // 戻り値有り - struct script_data *sd; - push_copy(st->stack,st->start+2); - sd = &st->stack->stack_data[st->stack->sp-1]; - if(sd->type == C_NAME) { - char *name = str_buf + str_data[sd->u.num&0x00ffffff].str; - if( name[0] == '.' && name[1] == '@') { - // '@ 変数を参照渡しにすると危険なので値渡しにする - get_val(st,sd); - } else if( name[0] == '.' && !sd->ref) { - // ' 変数は参照渡しでも良いが、参照元が設定されていないと - // 元のスクリプトの値を差してしまうので補正する。 - sd->ref = &st->script->script_vars; - } - } - } - st->state=RETFUNC; - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_next(struct script_state *st) -{ - st->state=STOP; - clif_scriptnext(script_rid2sd(st),st->oid); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_close(struct script_state *st) -{ - st->state=END; - clif_scriptclose(script_rid2sd(st),st->oid); - return 0; -} -int buildin_close2(struct script_state *st) -{ - st->state=STOP; - clif_scriptclose(script_rid2sd(st),st->oid); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_menu(struct script_state *st) -{ - char *buf; - int len,i; - struct map_session_data *sd; - - sd=script_rid2sd(st); - - if(sd->state.menu_or_input==0){ - st->state=RERUNLINE; - sd->state.menu_or_input=1; - for(i=st->start+2,len=16;iend;i+=2){ - conv_str(st,& (st->stack->stack_data[i])); - len+=(int)strlen(st->stack->stack_data[i].u.str)+1; - } - buf=(char *)aMallocA((len+1)*sizeof(char)); - buf[0]=0; - for(i=st->start+2,len=0;iend;i+=2){ - strcat(buf,st->stack->stack_data[i].u.str); - strcat(buf,":"); - } - clif_scriptmenu(script_rid2sd(st),st->oid,buf); - aFree(buf); - } else if(sd->npc_menu==0xff){ // cansel - sd->state.menu_or_input=0; - st->state=END; - } else { // goto動作 - sd->state.menu_or_input=0; - if(sd->npc_menu>0){ - //Skip empty menu entries which weren't displayed on the client (blackhole89) - for(i=st->start+2;i<=(st->start+sd->npc_menu*2) && sd->npc_menu<(st->end-st->start)/2;i+=2) - { - conv_str(st,& (st->stack->stack_data[i])); // we should convert variables to strings before access it [jA1983] [EoE] - if((int)strlen(st->stack->stack_data[i].u.str) < 1) - sd->npc_menu++; //Empty selection which wasn't displayed on the client. - } - if(sd->npc_menu >= (st->end-st->start)/2) { - //Invalid selection. - st->state=END; - return 0; - } - if( st->stack->stack_data[st->start+sd->npc_menu*2+1].type!=C_POS ){ - ShowError("script: menu: not label !\n"); - st->state=END; - return 1; - } - pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu); - st->pos= conv_num(st,& (st->stack->stack_data[st->start+sd->npc_menu*2+1])); - st->state=GOTO; - } - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_rand(struct script_state *st) -{ - int range; - - if (st->end > st->start+3){ - int min, max; - min = conv_num(st,& (st->stack->stack_data[st->start+2])); - max = conv_num(st,& (st->stack->stack_data[st->start+3])); - if (max == min){ //Why would someone do this? - push_val(st->stack,C_INT,min); - return 0; - } - if (max < min){ - int tmp = min; - min = max; - max = tmp; - } - range = max - min + 1; - if (range == 0) range = 1; - push_val(st->stack,C_INT,rand()%range+min); - } else { - range = conv_num(st,& (st->stack->stack_data[st->start+2])); - if (range == 0) range = 1; - push_val(st->stack,C_INT,rand()%range); - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_warp(struct script_state *st) -{ - int x,y; - char *str; - struct map_session_data *sd=script_rid2sd(st); - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - if(strcmp(str,"Random")==0) - pc_randomwarp(sd,3); - else if(strcmp(str,"SavePoint")==0){ - if(map[sd->bl.m].flag.noreturn) // 蝶禁止 - return 0; - - pc_setpos(sd,sd->status.save_point.map, - sd->status.save_point.x,sd->status.save_point.y,3); - }else if(strcmp(str,"Save")==0){ - if(map[sd->bl.m].flag.noreturn) // 蝶禁止 - return 0; - - pc_setpos(sd,sd->status.save_point.map, - sd->status.save_point.x,sd->status.save_point.y,3); - }else - pc_setpos(sd,mapindex_name2id(str),x,y,0); - return 0; -} -/*========================================== - * エリア指定ワープ - *------------------------------------------ - */ -int buildin_areawarp_sub(struct block_list *bl,va_list ap) -{ - int x,y; - unsigned int map; - map=va_arg(ap, unsigned int); - x=va_arg(ap,int); - y=va_arg(ap,int); - if(map == 0) - pc_randomwarp((struct map_session_data *)bl,3); - else - pc_setpos((struct map_session_data *)bl,map,x,y,0); - return 0; -} -int buildin_areawarp(struct script_state *st) -{ - int x,y,m; - unsigned int index; - char *str; - char *mapname; - int x0,y0,x1,y1; - - mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - x0=conv_num(st,& (st->stack->stack_data[st->start+3])); - y0=conv_num(st,& (st->stack->stack_data[st->start+4])); - x1=conv_num(st,& (st->stack->stack_data[st->start+5])); - y1=conv_num(st,& (st->stack->stack_data[st->start+6])); - str=conv_str(st,& (st->stack->stack_data[st->start+7])); - x=conv_num(st,& (st->stack->stack_data[st->start+8])); - y=conv_num(st,& (st->stack->stack_data[st->start+9])); - - if( (m=map_mapname2mapid(mapname))< 0) - return 0; - - if(strcmp(str,"Random")==0) - index = 0; - else if(!(index=mapindex_name2id(str))) - return 0; - - map_foreachinarea(buildin_areawarp_sub, - m,x0,y0,x1,y1,BL_PC, index,x,y ); - return 0; -} - -/*========================================== - * warpchar [LuzZza] - * Useful for warp one player from - * another player npc-session. - * Using: warpchar "mapname.gat",x,y,Char_ID; - *------------------------------------------ - */ -int buildin_warpchar(struct script_state *st) -{ - int x,y,a,i; - char *str; - struct map_session_data *sd, **pl_allsd; - int users; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - a=conv_num(st,& (st->stack->stack_data[st->start+5])); - - pl_allsd = map_getallusers(&users); - - for(i=0; istatus.char_id == a) { - - if(strcmp(str, "Random") == 0) - pc_randomwarp(sd, 3); - - else if(strcmp(str, "SavePoint") == 0) - pc_setpos(sd, sd->status.save_point.map, - sd->status.save_point.x, sd->status.save_point.y, 3); - - else - pc_setpos(sd, mapindex_name2id(str), x, y, 3); - } - } - - return 0; -} - -/*========================================== - * Warpparty - [Fredzilla] - * Syntax: warpparty "mapname.gat",x,y,Party_ID; - *------------------------------------------ - */ -int buildin_warpparty(struct script_state *st) -{ - int x,y; - char *str; - int p_id; - int i; - unsigned short mapindex; - struct map_session_data *pl_sd; - struct party *p=NULL; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - p_id=conv_num(st,& (st->stack->stack_data[st->start+5])); - if(p_id < 1) - return 0; - p = party_search(p_id); - if (!p) - return 0; - if(strcmp(str,"Random")==0) - { - for (i = 0; i < MAX_PARTY; i++) - { - if ((pl_sd = p->member[i].sd)) - { - if(map[pl_sd->bl.m].flag.nowarp) - continue; - pc_randomwarp(pl_sd,3); - } - } - } - else if(strcmp(str,"SavePointAll")==0) - { - for (i = 0; i < MAX_PARTY; i++) - { - if ((pl_sd = p->member[i].sd)) - { - if(map[pl_sd->bl.m].flag.noreturn) - continue; - pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); - } - } - } - else if(strcmp(str,"SavePoint")==0) - { - pl_sd=script_rid2sd(st); - if (!pl_sd) return 0; - - mapindex=pl_sd->status.save_point.map; - x=pl_sd->status.save_point.x; - y=pl_sd->status.save_point.y; - - for (i = 0; i < MAX_PARTY; i++) - { - if ((pl_sd = p->member[i].sd)) - { - if(map[pl_sd->bl.m].flag.noreturn) - continue; - pc_setpos(pl_sd,mapindex,x,y,3); - } - } - } - else - { - mapindex = mapindex_name2id(str); - if (!mapindex) //Show source of npc error. - return 1; - for (i = 0; i < MAX_PARTY; i++) - { - if ((pl_sd = p->member[i].sd)) - { - if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp) - continue; - pc_setpos(pl_sd,mapindex,x,y,3); - } - } - } - return 0; -} -/*========================================== - * Warpguild - [Fredzilla] - * Syntax: warpguild "mapname.gat",x,y,Guild_ID; - *------------------------------------------ - */ -int buildin_warpguild(struct script_state *st) -{ - int x,y; - unsigned short mapindex; - char *str; - int g; - int i; - struct map_session_data *pl_sd, **pl_allsd; - int users; - struct map_session_data *sd; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - g=conv_num(st,& (st->stack->stack_data[st->start+5])); - sd=script_rid2sd(st); - - if(map[sd->bl.m].flag.noreturn || map[sd->bl.m].flag.nowarpto) - return 0; - - if(g < 1) - return 0; - - pl_allsd = map_getallusers(&users); - - if(strcmp(str,"Random")==0) - { - - for (i = 0; i < users; i++) - { - if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) - { - if(map[pl_sd->bl.m].flag.nowarp) - continue; - pc_randomwarp(pl_sd,3); - } - } - } - else if(strcmp(str,"SavePointAll")==0) - { - if(map[sd->bl.m].flag.noreturn) - return 0; - - for (i = 0; i < users; i++) - { - if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) - { - if(map[pl_sd->bl.m].flag.noreturn) - continue; - pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); - } - } - } - else if(strcmp(str,"SavePoint")==0) - { - if(map[sd->bl.m].flag.noreturn) - return 0; - - mapindex=sd->status.save_point.map; - x=sd->status.save_point.x; - y=sd->status.save_point.y; - for (i = 0; i < users; i++) - { - if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) - { - if(map[pl_sd->bl.m].flag.noreturn) - continue; - pc_setpos(pl_sd,mapindex,x,y,3); - } - } - } - else - { - mapindex = mapindex_name2id(str); - for (i = 0; i < users; i++) - { - if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) - { - if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp) - continue; - pc_setpos(pl_sd,mapindex,x,y,3); - } - } - } - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_heal(struct script_state *st) -{ - int hp,sp; - - hp=conv_num(st,& (st->stack->stack_data[st->start+2])); - sp=conv_num(st,& (st->stack->stack_data[st->start+3])); - pc_heal(script_rid2sd(st),hp,sp); - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_itemheal(struct script_state *st) -{ - int hp,sp; - - hp=conv_num(st,& (st->stack->stack_data[st->start+2])); - sp=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if(potion_flag==1) { - potion_hp = hp; - potion_sp = sp; - return 0; - } - - pc_itemheal(script_rid2sd(st),hp,sp); - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_percentheal(struct script_state *st) -{ - int hp,sp; - - hp=conv_num(st,& (st->stack->stack_data[st->start+2])); - sp=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if(potion_flag==1) { - potion_per_hp = hp; - potion_per_sp = sp; - return 0; - } - - pc_percentheal(script_rid2sd(st),hp,sp); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_jobchange(struct script_state *st) -{ - int job, upper=-1; - - job=conv_num(st,& (st->stack->stack_data[st->start+2])); - if( st->end>st->start+3 ) - upper=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if ((job >= 0 && job < MAX_PC_CLASS)){ - pc_jobchange(script_rid2sd(st),job, upper); - if(use_irc && irc_announce_jobchange_flag) - irc_announce_jobchange(script_rid2sd(st)); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_input(struct script_state *st) -{ - struct map_session_data *sd=NULL; - int num=(st->end>st->start+2)?st->stack->stack_data[st->start+2].u.num:0; - char *name=(char *) ((st->end>st->start+2)?str_buf+str_data[num&0x00ffffff].str:""); -// char prefix=*name; - char postfix=name[strlen(name)-1]; - - sd=script_rid2sd(st); - if(sd->state.menu_or_input){ - sd->state.menu_or_input=0; - if( postfix=='$' ){ - // 文字列 - if(st->end>st->start+2){ // 引数1個 - set_reg(st,sd,num,name,(void*)sd->npc_str,st->stack->stack_data[st->start+2].ref); - }else{ - ShowError("buildin_input: string discarded !!\n"); - return 1; - } - return 0; - } - // commented by Lupus (check Value Number Input fix in clif.c) - // readded by Yor: set ammount to 0 instead of cancel trade. - // ** Fix by fritz :X keeps people from abusing old input bugs - if (sd->npc_amount < 0) { //** If input amount is less then 0 -// clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris -// buildin_close(st); // ** close - sd->npc_amount = 0; - } else if ((unsigned int)sd->npc_amount > battle_config.vending_max_value) // new fix by Yor - sd->npc_amount = battle_config.vending_max_value; - - // 数値 - if(st->end>st->start+2){ // 引数1個 - set_reg(st,sd,num,name,(void*)sd->npc_amount,st->stack->stack_data[st->start+2].ref); - } else { - // ragemu互換のため - //pc_setreg(sd,add_str((unsigned char *) "l14"),sd->npc_amount); - } - return 0; - } - //state.menu_or_input = 0 - st->state=RERUNLINE; - if(postfix=='$') - clif_scriptinputstr(sd,st->oid); - else - clif_scriptinput(sd,st->oid); - sd->state.menu_or_input=1; - return 0; -} - -/*========================================== - * 変数設定 - *------------------------------------------ - */ -int buildin_set(struct script_state *st) -{ - struct map_session_data *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - - if( st->stack->stack_data[st->start+2].type!=C_NAME ){ - ShowError("script: buildin_set: not name\n"); - return 1; - } - - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - - - if( postfix=='$' ){ - // 文字列 - char *str = conv_str(st,& (st->stack->stack_data[st->start+3])); - set_reg(st,sd,num,name,(void*)str,st->stack->stack_data[st->start+2].ref); - }else{ - // 数値 - int val = conv_num(st,& (st->stack->stack_data[st->start+3])); - set_reg(st,sd,num,name,(void*)val,st->stack->stack_data[st->start+2].ref); - } - - return 0; -} -/*========================================== - * 配列変数設定 - *------------------------------------------ - */ -int buildin_setarray(struct script_state *st) -{ - struct map_session_data *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int i,j; - - if( prefix!='$' && prefix!='@' && prefix!='.'){ - ShowWarning("buildin_setarray: illegal scope !\n"); - return 1; - } - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - - for(j=0,i=st->start+3; iend && j<128;i++,j++){ - void *v; - if( postfix=='$' ) - v=(void*)conv_str(st,& (st->stack->stack_data[i])); - else - v=(void*)conv_num(st,& (st->stack->stack_data[i])); - set_reg(st, sd, num+(j<<24), name, v, st->stack->stack_data[st->start+2].ref); - } - return 0; -} -/*========================================== - * 配列変数クリア - *------------------------------------------ - */ -int buildin_cleararray(struct script_state *st) -{ - struct map_session_data *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int sz=conv_num(st,& (st->stack->stack_data[st->start+4])); - int i; - void *v; - - if( prefix!='$' && prefix!='@' && prefix!='.'){ - ShowWarning("buildin_cleararray: illegal scope !\n"); - return 1; - } - if( not_server_variable(prefix) ) - sd=script_rid2sd(st); - - if( postfix=='$' ) - v=(void*)conv_str(st,& (st->stack->stack_data[st->start+3])); - else - v=(void*)conv_num(st,& (st->stack->stack_data[st->start+3])); - - for(i=0;istack->stack_data[st->start+2].ref); - return 0; -} -/*========================================== - * 配列変数コピー - *------------------------------------------ - */ -int buildin_copyarray(struct script_state *st) -{ - struct map_session_data *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int num2=st->stack->stack_data[st->start+3].u.num; - char *name2=str_buf+str_data[num2&0x00ffffff].str; - char prefix2=*name2; - char postfix2=name2[strlen(name2)-1]; - int sz=conv_num(st,& (st->stack->stack_data[st->start+4])); - int i; - - if( prefix!='$' && prefix!='@' && prefix!='.' ){ - printf("buildin_copyarray: illeagal scope !\n"); - return 0; - } - if( prefix2!='$' && prefix2!='@' && prefix2!='.' ) { - printf("buildin_copyarray: illeagal scope !\n"); - return 0; - } - if( (postfix=='$' || postfix2=='$') && postfix!=postfix2 ){ - printf("buildin_copyarray: type mismatch !\n"); - return 0; - } - if( not_server_variable(prefix) || not_server_variable(prefix2) ) - sd=script_rid2sd(st); - - if((num & 0x00FFFFFF) == (num2 & 0x00FFFFFF) && (num & 0xFF000000) > (num2 & 0xFF000000)) { - // 同じ配列で、num > num2 の場合大きい方からコピーしないといけない - for(i=sz-1;i>=0;i--) - set_reg( - st,sd,num+(i<<24),name, - get_val2(st,num2+(i<<24),st->stack->stack_data[st->start+3].ref), - st->stack->stack_data[st->start+2].ref - ); - } else { - for(i=0;istack->stack_data[st->start+3].ref), - st->stack->stack_data[st->start+2].ref - ); - } - return 0; -} -/*========================================== - * 配列変数のサイズ所得 - *------------------------------------------ - */ -static int getarraysize(struct script_state *st,int num,int postfix,struct linkdb_node** ref) -{ - int i=(num>>24),c=(i==0? -1:i); // Moded to -1 because even if the first element is 0, it will still report as 1 [Lance] - if(postfix == '$'){ - for(;i<128;i++){ - void *v=get_val2(st,(num & 0x00FFFFFF)+(i<<24),ref); - if(*((char*)v)) c=i; - } - }else{ - for(;i<128;i++){ - void *v=get_val2(st,(num & 0x00FFFFFF)+(i<<24),ref); - if((int)v) c=i; - } - } - return c+1; -} - -int buildin_getarraysize(struct script_state *st) -{ - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - - if( prefix!='$' && prefix!='@' && prefix!='.' ){ - ShowWarning("buildin_copyarray: illegal scope !\n"); - return 1; - } - - push_val(st->stack,C_INT,getarraysize(st,num,postfix,st->stack->stack_data[st->start+2].ref)); - return 0; -} -/*========================================== - * 配列変数から要素削除 - *------------------------------------------ - */ -int buildin_deletearray(struct script_state *st) -{ - struct map_session_data *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int count=1; - int i,sz=getarraysize(st,num,postfix,st->stack->stack_data[st->start+2].ref)-(num>>24)-count+1; - - - if( (st->end > st->start+3) ) - count=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if( prefix!='$' && prefix!='@' && prefix!='.' ){ - ShowWarning("buildin_deletearray: illegal scope !\n"); - return 1; - } - if( not_server_variable(prefix) ) - sd=script_rid2sd(st); - - for(i=0;istack->stack_data[st->start+2].ref), - st->stack->stack_data[st->start+2].ref - ); - } - - if(postfix != '$'){ - for(;i<(128-(num>>24));i++) - set_reg(st,sd,num+(i<<24),name, 0,st->stack->stack_data[st->start+2].ref); - } else { - for(;i<(128-(num>>24));i++) - set_reg(st,sd,num+(i<<24),name, (void *) "",st->stack->stack_data[st->start+2].ref); - } - return 0; -} - -/*========================================== - * 指定要素を表す値(キー)を所得する - *------------------------------------------ - */ -int buildin_getelementofarray(struct script_state *st) -{ - if( st->stack->stack_data[st->start+2].type==C_NAME ){ - int i=conv_num(st,& (st->stack->stack_data[st->start+3])); - if(i>127 || i<0){ - ShowWarning("script: getelementofarray (operator[]): param2 illegal number %d\n",i); - push_val(st->stack,C_INT,0); - return 1; - }else{ - push_val(st->stack,C_NAME, - (i<<24) | st->stack->stack_data[st->start+2].u.num ); - } - }else{ - ShowError("script: getelementofarray (operator[]): param1 not name !\n"); - push_val(st->stack,C_INT,0); - } - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_setlook(struct script_state *st) -{ - int type,val; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - val=conv_num(st,& (st->stack->stack_data[st->start+3])); - - pc_changelook(script_rid2sd(st),type,val); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_cutin(struct script_state *st) -{ - int type; - - conv_str(st,& (st->stack->stack_data[st->start+2])); - type=conv_num(st,& (st->stack->stack_data[st->start+3])); - - clif_cutin(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str,type); - - return 0; -} -/*========================================== - * カードのイラストを表示する - *------------------------------------------ - */ -int buildin_cutincard(struct script_state *st) -{ - int itemid; - struct item_data *i_data; - - itemid=conv_num(st,& (st->stack->stack_data[st->start+2])); - - i_data = itemdb_exists(itemid); - if (i_data) - clif_cutin(script_rid2sd(st),i_data->cardillustname,4); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_viewpoint(struct script_state *st) -{ - int type,x,y,id,color; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - id=conv_num(st,& (st->stack->stack_data[st->start+5])); - color=conv_num(st,& (st->stack->stack_data[st->start+6])); - - clif_viewpoint(script_rid2sd(st),st->oid,type,x,y,id,color); - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_countitem(struct script_state *st) -{ - int nameid=0,count=0,i; - struct map_session_data *sd; - - struct script_data *data; - - sd = script_rid2sd(st); - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data; - if( (item_data = itemdb_searchname(name)) != NULL) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - if (nameid>=500) //if no such ID then skip this iteration - for(i=0;istatus.inventory[i].nameid==nameid) - count+=sd->status.inventory[i].amount; - } - else{ - if(battle_config.error_log) - ShowError("wrong item ID : countitem(%i)\n",nameid); - push_val(st->stack,C_INT,0); - return 1; - } - push_val(st->stack,C_INT,count); - return 0; -} - -/*========================================== - * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus] - * returns number of items that met the conditions - *------------------------------------------ - */ -int buildin_countitem2(struct script_state *st) -{ - int nameid=0,count=0,i; - int iden,ref,attr,c1,c2,c3,c4; - struct map_session_data *sd; - - struct script_data *data; - - sd = script_rid2sd(st); - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data; - if( (item_data = itemdb_searchname(name)) != NULL) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - iden=conv_num(st,& (st->stack->stack_data[st->start+3])); - ref=conv_num(st,& (st->stack->stack_data[st->start+4])); - attr=conv_num(st,& (st->stack->stack_data[st->start+5])); - c1=conv_num(st,& (st->stack->stack_data[st->start+6])); - c2=conv_num(st,& (st->stack->stack_data[st->start+7])); - c3=conv_num(st,& (st->stack->stack_data[st->start+8])); - c4=conv_num(st,& (st->stack->stack_data[st->start+9])); - - if (nameid>=500) //if no such ID then skip this iteration - for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid || - sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref || - sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 || - sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 || - sd->status.inventory[i].card[3]!=c4) - continue; - - count+=sd->status.inventory[i].amount; - } - else{ - if(battle_config.error_log) - ShowError("wrong item ID : countitem2(%i)\n",nameid); - push_val(st->stack,C_INT,0); - return 1; - } - push_val(st->stack,C_INT,count); - - return 0; -} - -/*========================================== - * 重量チェック - *------------------------------------------ - */ -int buildin_checkweight(struct script_state *st) -{ - int nameid=0,amount,i; - unsigned long weight; - struct map_session_data *sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data ) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - amount=conv_num(st,& (st->stack->stack_data[st->start+3])); - if ( amount<=0 || nameid<500 ) { //if get wrong item ID or amount<=0, don't count weight of non existing items - push_val(st->stack,C_INT,0); - ShowError("buildin_checkweight: Wrong item ID or amount.\n"); - return 1; - } - - weight = itemdb_weight(nameid)*amount; - if(amount > MAX_AMOUNT || weight + sd->weight > sd->max_weight){ - push_val(st->stack,C_INT,0); - } else { - //Check if the inventory ain't full. - //TODO: Currently does not checks if you can just stack it on top of another item you already have.... - - i = pc_search_inventory(sd,0); - if (i >= 0) //Empty slot available. - push_val(st->stack,C_INT,1); - else //Inventory full - push_val(st->stack,C_INT,0); - - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_getitem(struct script_state *st) -{ - int nameid,nameidsrc,amount,flag = 0; - struct item item_tmp; - struct map_session_data *sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - nameid=512; //Apple item ID - if( item_data != NULL) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - if ( ( amount=conv_num(st,& (st->stack->stack_data[st->start+3])) ) <= 0) { - return 0; //return if amount <=0, skip the useles iteration - } - //Violet Box, Blue Box, etc - random item pick - if((nameidsrc = nameid)<0) { // Save real ID of the source Box [Lupus] - nameid=itemdb_searchrandomid(-nameid); - - flag = 1; - } - - if(nameid > 0) { - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - if(!flag) - item_tmp.identify=1; - else - item_tmp.identify=!itemdb_isequip3(nameid); - if( st->end>st->start+5 ) //アイテムを指定したIDに渡す - sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+5]))); - if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り - return 0; - if((flag = pc_additem(sd,&item_tmp,amount))) { - clif_additem(sd,0,0,flag); - if(pc_candrop(sd,nameid)) - map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, nameid, amount, NULL); - } - //Logs - - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_getitem2(struct script_state *st) -{ - int nameid,amount,flag = 0; - int iden,ref,attr,c1,c2,c3,c4; - struct item_data *item_data; - struct item item_tmp; - struct map_session_data *sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - nameid=512; //Apple item ID - if( item_data ) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - amount=conv_num(st,& (st->stack->stack_data[st->start+3])); - iden=conv_num(st,& (st->stack->stack_data[st->start+4])); - ref=conv_num(st,& (st->stack->stack_data[st->start+5])); - attr=conv_num(st,& (st->stack->stack_data[st->start+6])); - c1=conv_num(st,& (st->stack->stack_data[st->start+7])); - c2=conv_num(st,& (st->stack->stack_data[st->start+8])); - c3=conv_num(st,& (st->stack->stack_data[st->start+9])); - c4=conv_num(st,& (st->stack->stack_data[st->start+10])); - if( st->end>st->start+11 ) //アイテムを指定したIDに渡す - sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+11]))); - if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り - return 0; - - if(nameid<0) { // ランダム - nameid=itemdb_searchrandomid(-nameid); - flag = 1; - } - - if(nameid > 0) { - memset(&item_tmp,0,sizeof(item_tmp)); - item_data=itemdb_exists(nameid); - if (item_data == NULL) - return -1; - if(item_data->type==4 || item_data->type==5){ - if(ref > 10) ref = 10; - } - else if(item_data->type==7) { - iden = 1; - ref = 0; - } - else { - iden = 1; - ref = attr = 0; - } - - item_tmp.nameid=nameid; - if(!flag) - item_tmp.identify=iden; - else if(item_data->type==4 || item_data->type==5) - item_tmp.identify=0; - item_tmp.refine=ref; - item_tmp.attribute=attr; - item_tmp.card[0]=c1; - item_tmp.card[1]=c2; - item_tmp.card[2]=c3; - item_tmp.card[3]=c4; - if((flag = pc_additem(sd,&item_tmp,amount))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, nameid, amount, &item_tmp); - } - //Logs - } - - return 0; -} - -/*========================================== - * gets an item with someone's name inscribed [Skotlex] - * getinscribeditem item_num, character_name - * Returned Qty is always 1, only works on equip-able - * equipment - *------------------------------------------ - */ -int buildin_getnameditem(struct script_state *st) -{ - int nameid; - struct item item_tmp; - struct map_session_data *sd, *tsd; - struct script_data *data; - - sd = script_rid2sd(st); - if (sd == NULL) - { //Player not attached! - push_val(st->stack,C_INT,0); - return 0; - } - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data == NULL) - { //Failed - push_val(st->stack,C_INT,0); - return 0; - } - nameid = item_data->nameid; - }else - nameid = conv_num(st,data); - - if(!itemdb_exists(nameid) || !itemdb_isequip3(nameid)) - { //We don't allow non-equipable/stackable items to be named - //to avoid any qty exploits that could happen because of it. - push_val(st->stack,C_INT,0); - return 0; - } - - data=&(st->stack->stack_data[st->start+3]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ) //Char Name - tsd=map_nick2sd(conv_str(st,data)); - else //Char Id was given - tsd=map_charid2sd(conv_num(st,data)); - - if( tsd == NULL ) - { //Failed - push_val(st->stack,C_INT,0); - return 0; - } - - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - item_tmp.amount=1; - item_tmp.identify=1; - item_tmp.card[0]=254; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] - item_tmp.card[2]=tsd->status.char_id; - item_tmp.card[3]=tsd->status.char_id >> 16; - if(pc_additem(sd,&item_tmp,1)) { - push_val(st->stack,C_INT,0); - return 0; //Failed to add item, we will not drop if they don't fit - } - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, item_tmp.nameid, item_tmp.amount, &item_tmp); - } - //Logs - - push_val(st->stack,C_INT,1); - return 0; -} - -/*========================================== - * gets a random item ID from an item group [Skotlex] - * groupranditem group_num - *------------------------------------------ - */ -int buildin_grouprandomitem(struct script_state *st) -{ - int group; - - group = conv_num(st,& (st->stack->stack_data[st->start+2])); - push_val(st->stack, C_INT, itemdb_searchrandomid(group)); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_makeitem(struct script_state *st) -{ - int nameid,amount,flag = 0; - int x,y,m; - char *mapname; - struct item item_tmp; - struct script_data *data; - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - nameid=512; //Apple Item ID - if( item_data ) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - amount=conv_num(st,& (st->stack->stack_data[st->start+3])); - mapname =conv_str(st,& (st->stack->stack_data[st->start+4])); - x =conv_num(st,& (st->stack->stack_data[st->start+5])); - y =conv_num(st,& (st->stack->stack_data[st->start+6])); - - if(strcmp(mapname,"this")==0) - { - struct map_session_data *sd; - sd = script_rid2sd(st); - if (!sd) return 0; //Failed... - m=sd->bl.m; - } else - m=map_mapname2mapid(mapname); - - if(nameid<0) { // ランダム - nameid=itemdb_searchrandomid(-nameid); - flag = 1; - } - - if(nameid > 0) { - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=nameid; - if(!flag) - item_tmp.identify=1; - else - item_tmp.identify=!itemdb_isequip3(nameid); - - map_addflooritem(&item_tmp,amount,m,x,y,NULL,NULL,NULL,0); - } - - return 0; -} -/*========================================== - * script DELITEM command (fixed 2 bugs by Lupus, added deletion priority by Lupus) - *------------------------------------------ - */ -int buildin_delitem(struct script_state *st) -{ - int nameid=0,amount,i,important_item=0; - struct map_session_data *sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - //nameid=512; - if( item_data ) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - amount=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0 - //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); - return 0; - } - //1st pass - //here we won't delete items with CARDS, named items but we count them - for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ) - continue; - //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle - if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){ - intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) ); - //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^) - sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0; - //now this egg'll be deleted as a common unimportant item - } - //is this item important? does it have cards? or Player's name? or Refined/Upgraded - if( sd->status.inventory[i].card[0] || sd->status.inventory[i].card[1] || - sd->status.inventory[i].card[2] || sd->status.inventory[i].card[3] || sd->status.inventory[i].refine) { - //this is important item, count it - important_item++; - continue; - } - - if(sd->status.inventory[i].amount>=amount){ - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,amount,0); - return 0; //we deleted exact amount of items. now exit - } else { - amount-=sd->status.inventory[i].amount; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,sd->status.inventory[i].amount,0); - } - } - //2nd pass - //now if there WERE items with CARDs/REFINED/NAMED... and if we still have to delete some items. we'll delete them finally - if (important_item>0 && amount>0) - for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ) - continue; - - if(sd->status.inventory[i].amount>=amount){ - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,amount,0); - return 0; //we deleted exact amount of items. now exit - } else { - amount-=sd->status.inventory[i].amount; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,sd->status.inventory[i].amount,0); - } - } - - return 0; -} - -/*========================================== -* advanced version of delitem [modified by Mihilion] -*------------------------------------------ -*/ -int buildin_delitem2(struct script_state *st) -{ - int nameid=0,amount,i=0; - int iden,ref,attr,c1,c2,c3,c4; - struct map_session_data *sd; - struct script_data *data; - - sd = script_rid2sd(st); - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - //nameid=512; - if( item_data ) - nameid=item_data->nameid; - }else - nameid=conv_num(st,data); - - amount=conv_num(st,& (st->stack->stack_data[st->start+3])); - iden=conv_num(st,& (st->stack->stack_data[st->start+4])); - ref=conv_num(st,& (st->stack->stack_data[st->start+5])); - attr=conv_num(st,& (st->stack->stack_data[st->start+6])); - c1=conv_num(st,& (st->stack->stack_data[st->start+7])); - c2=conv_num(st,& (st->stack->stack_data[st->start+8])); - c3=conv_num(st,& (st->stack->stack_data[st->start+9])); - c4=conv_num(st,& (st->stack->stack_data[st->start+10])); - - if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0 - //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); - return 0; - } - - for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || - sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid || - sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref || - sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 || - sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 || - sd->status.inventory[i].card[3]!=c4) - continue; - //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle - if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){ - intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) ); - //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^) - sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0; - //now this egg'll be deleted as a common unimportant item - } - - if(sd->status.inventory[i].amount>=amount){ - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,amount,0); - return 0; //we deleted exact amount of items. now exit - } else { - amount-=sd->status.inventory[i].amount; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,sd->status.inventory[i].amount,0); - } - } - return 0; -} - -/*========================================== - * Enables/Disables use of items while in an NPC [Skotlex] - *------------------------------------------ - */ -int buildin_enableitemuse(struct script_state *st) { - struct map_session_data *sd; - sd=script_rid2sd(st); - if (sd) - sd->npc_item_flag = st->oid; - return 0; -} - -int buildin_disableitemuse(struct script_state *st) { - struct map_session_data *sd; - sd=script_rid2sd(st); - if (sd) - sd->npc_item_flag = 0; - return 0; -} - -/*========================================== - *キャラ関係のパラメータ取得 - *------------------------------------------ - */ -int buildin_readparam(struct script_state *st) -{ - int type; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - if( st->end>st->start+3 ) - sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3]))); - else - sd=script_rid2sd(st); - - if(sd==NULL){ - push_val(st->stack,C_INT,-1); - return 0; - } - - push_val(st->stack,C_INT,pc_readparam(sd,type)); - - return 0; -} -/*========================================== - *キャラ関係のID取得 - *------------------------------------------ - */ -int buildin_getcharid(struct script_state *st) -{ - int num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - if( st->end>st->start+3 ) - sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3]))); - else - sd=script_rid2sd(st); - if(sd==NULL){ - push_val(st->stack,C_INT,-1); - return 0; - } - if(num==0) - push_val(st->stack,C_INT,sd->status.char_id); - if(num==1) - push_val(st->stack,C_INT,sd->status.party_id); - if(num==2) - push_val(st->stack,C_INT,sd->status.guild_id); - if(num==3) - push_val(st->stack,C_INT,sd->status.account_id); - return 0; -} -/*========================================== - *指定IDのPT名取得 - *------------------------------------------ - */ -char *buildin_getpartyname_sub(int party_id) -{ - struct party *p; - - p=NULL; - p=party_search(party_id); - - if(p!=NULL){ - char *buf; - buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); - memcpy(buf, p->name, NAME_LENGTH-1); - buf[NAME_LENGTH-1] = '\0'; - return buf; - } - - return 0; -} -int buildin_getpartyname(struct script_state *st) -{ - char *name; - int party_id; - - party_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - name=buildin_getpartyname_sub(party_id); - if(name != NULL) - push_str(st->stack,C_STR,(unsigned char *)name); - else - push_str(st->stack,C_CONSTSTR, (unsigned char *) "null"); - - return 0; -} -/*========================================== - *指定IDのPT人数とメンバーID取得 - *------------------------------------------ - */ -int buildin_getpartymember(struct script_state *st) -{ - struct party *p; - int i,j=0,type=0; - - p=NULL; - p=party_search(conv_num(st,& (st->stack->stack_data[st->start+2]))); - - if( st->end>st->start+3 ) - type=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if(p!=NULL){ - for(i=0;imember[i].account_id){ - switch (type) { - case 2: - mapreg_setreg(add_str((unsigned char *) "$@partymemberaid")+(j<<24),p->member[i].account_id); - break; - case 1: - mapreg_setreg(add_str((unsigned char *) "$@partymembercid")+(j<<24),p->member[i].char_id); - break; - default: - mapreg_setregstr(add_str((unsigned char *) "$@partymembername$")+(j<<24),p->member[i].name); - } - j++; - } - } - } - mapreg_setreg(add_str((unsigned char *) "$@partymembercount"),j); - - return 0; -} -/*========================================== - *指定IDのギルド名取得 - *------------------------------------------ - */ -char *buildin_getguildname_sub(int guild_id) -{ - struct guild *g=NULL; - g=guild_search(guild_id); - - if(g!=NULL){ - char *buf; - buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); - memcpy(buf, g->name, NAME_LENGTH-1); - buf[NAME_LENGTH-1] = '\0'; - return buf; - } - return NULL; -} -int buildin_getguildname(struct script_state *st) -{ - char *name; - int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - name=buildin_getguildname_sub(guild_id); - if(name != NULL) - push_str(st->stack,C_STR,(unsigned char *) name); - else - push_str(st->stack,C_CONSTSTR,(unsigned char *) "null"); - return 0; -} - -/*========================================== - *指定IDのGuildMaster名取得 - *------------------------------------------ - */ -char *buildin_getguildmaster_sub(int guild_id) -{ - struct guild *g=NULL; - g=guild_search(guild_id); - - if(g!=NULL){ - char *buf; - buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); - memcpy(buf, g->master, NAME_LENGTH-1); - buf[NAME_LENGTH-1] = '\0'; - return buf; - } - - return 0; -} -int buildin_getguildmaster(struct script_state *st) -{ - char *master; - int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - master=buildin_getguildmaster_sub(guild_id); - if(master!=0) - push_str(st->stack,C_STR,(unsigned char *) master); - else - push_str(st->stack,C_CONSTSTR,(unsigned char *) "null"); - return 0; -} - -int buildin_getguildmasterid(struct script_state *st) -{ - char *master; - struct map_session_data *sd=NULL; - int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - master=buildin_getguildmaster_sub(guild_id); - if(master!=0){ - if((sd=map_nick2sd(master)) == NULL){ - push_val(st->stack,C_INT,0); - return 0; - } - push_val(st->stack,C_INT,sd->status.char_id); - }else{ - push_val(st->stack,C_INT,0); - } - return 0; -} - -/*========================================== - * キャラクタの名前 - *------------------------------------------ - */ -int buildin_strcharinfo(struct script_state *st) -{ - struct map_session_data *sd; - int num; - char *buf; - - sd=script_rid2sd(st); - if (!sd) { //Avoid crashing.... - push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); - return 0; - } - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - switch(num){ - case 0: - push_str(st->stack,C_CONSTSTR,(unsigned char *) sd->status.name); - break; - case 1: - buf=buildin_getpartyname_sub(sd->status.party_id); - if(buf!=0) - push_str(st->stack,C_STR,(unsigned char *) buf); - else - push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); - break; - case 2: - buf=buildin_getguildname_sub(sd->status.guild_id); - if(buf != NULL) - push_str(st->stack,C_STR,(unsigned char *) buf); - else - push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); - break; - default: - ShowWarning("buildin_strcharinfo: unknown parameter."); - push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); - break; - } - - return 0; -} - -unsigned int equip[10]={0x0100,0x0010,0x0020,0x0002,0x0004,0x0040,0x0008,0x0080,0x0200,0x0001}; - -/*========================================== - * GetEquipID(Pos); Pos: 1-10 - *------------------------------------------ - */ -int buildin_getequipid(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - struct item_data* item; - - sd=script_rid2sd(st); - if(sd == NULL) - { - ShowError("getequipid: sd == NULL\n"); - return 0; - } - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0){ - item=sd->inventory_data[i]; - if(item) - push_val(st->stack,C_INT,item->nameid); - else - push_val(st->stack,C_INT,0); - }else{ - push_val(st->stack,C_INT,-1); - } - return 0; -} - -/*========================================== - * 装備名文字列(精錬メニュー用) - *------------------------------------------ - */ -int buildin_getequipname(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - struct item_data* item; - char *buf; - - buf=(char *)aMallocA(64*sizeof(char)); - sd=script_rid2sd(st); - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0){ - item=sd->inventory_data[i]; - if(item) - sprintf(buf,"%s-[%s]",pos[num-1],item->jname); - else - sprintf(buf,"%s-[%s]",pos[num-1],pos[10]); - }else{ - sprintf(buf,"%s-[%s]",pos[num-1],pos[10]); - } - push_str(st->stack,C_STR,(unsigned char *) buf); - - return 0; -} - -/*========================================== - * getbrokenid [Valaris] - *------------------------------------------ - */ -int buildin_getbrokenid(struct script_state *st) -{ - int i,num,id=0,brokencounter=0; - struct map_session_data *sd; - - sd=script_rid2sd(st); - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - for(i=0; istatus.inventory[i].attribute==1){ - brokencounter++; - if(num==brokencounter){ - id=sd->status.inventory[i].nameid; - break; - } - } - } - - push_val(st->stack,C_INT,id); - - return 0; -} - -/*========================================== - * repair [Valaris] - *------------------------------------------ - */ -int buildin_repair(struct script_state *st) -{ - int i,num; - int repaircounter=0; - struct map_session_data *sd; - - - sd=script_rid2sd(st); - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - for(i=0; istatus.inventory[i].attribute==1){ - repaircounter++; - if(num==repaircounter){ - sd->status.inventory[i].attribute=0; - clif_equiplist(sd); - clif_produceeffect(sd, 0, sd->status.inventory[i].nameid); - clif_misceffect(&sd->bl, 3); - clif_displaymessage(sd->fd,"Item has been repaired."); - break; - } - } - } - - return 0; -} - -/*========================================== - * 装備チェック - *------------------------------------------ - */ -int buildin_getequipisequiped(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - - if ((num - 1) >= (sizeof(equip) / sizeof(equip[0]))) - i = -1; - else - i=pc_checkequip(sd,equip[num-1]); - - if(i >= 0) - push_val(st->stack,C_INT,1); - else - push_val(st->stack,C_INT,0); - - return 0; -} - -/*========================================== - * 装備品精錬可能チェック - *------------------------------------------ - */ -int buildin_getequipisenableref(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0 && num<7 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine) - { - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } - - return 0; -} - -/*========================================== - * 装備品鑑定チェック - *------------------------------------------ - */ -int buildin_getequipisidentify(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) - push_val(st->stack,C_INT,sd->status.inventory[i].identify); - else - push_val(st->stack,C_INT,0); - - return 0; -} - -/*========================================== - * 装備品精錬度 - *------------------------------------------ - */ -int buildin_getequiprefinerycnt(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) - push_val(st->stack,C_INT,sd->status.inventory[i].refine); - else - push_val(st->stack,C_INT,0); - - return 0; -} - -/*========================================== - * 装備品武器LV - *------------------------------------------ - */ -int buildin_getequipweaponlv(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0 && sd->inventory_data[i]) - push_val(st->stack,C_INT,sd->inventory_data[i]->wlv); - else - push_val(st->stack,C_INT,0); - - return 0; -} - -/*========================================== - * 装備品精錬成功率 - *------------------------------------------ - */ -int buildin_getequippercentrefinery(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE) - push_val(st->stack,C_INT,percentrefinery[itemdb_wlv(sd->status.inventory[i].nameid)][(int)sd->status.inventory[i].refine]); - else - push_val(st->stack,C_INT,0); - - return 0; -} - -/*========================================== - * 精錬成功 - *------------------------------------------ - */ -int buildin_successrefitem(struct script_state *st) -{ - int i,num,ep; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) { - ep=sd->status.inventory[i].equip; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); - } - //Logs - - sd->status.inventory[i].refine++; - pc_unequipitem(sd,i,2); - - clif_refine(sd->fd,sd,0,i,sd->status.inventory[i].refine); - clif_delitem(sd,i,1); - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, 1, &sd->status.inventory[i]); - } - //Logs - - clif_additem(sd,i,1,0); - pc_equipitem(sd,i,ep); - clif_misceffect(&sd->bl,3); - if(sd->status.inventory[i].refine == 10 && sd->status.inventory[i].card[0] == 0x00ff && sd->char_id == MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])){ // Fame point system [DracoRPG] - switch (sd->inventory_data[i]->wlv){ - case 1: - pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point - break; - case 2: - pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point - break; - case 3: - pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point - break; - } - } - } - - return 0; -} - -/*========================================== - * 精錬失敗 - *------------------------------------------ - */ -int buildin_failedrefitem(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0) { - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); - } - //Logs - - sd->status.inventory[i].refine = 0; - pc_unequipitem(sd,i,3); - // 精錬失敗エフェクトのパケット - clif_refine(sd->fd,sd,1,i,sd->status.inventory[i].refine); - - pc_delitem(sd,i,1,0); - // 他の人にも失敗を通知 - clif_misceffect(&sd->bl,2); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_statusup(struct script_state *st) -{ - int type; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - pc_statusup(sd,type); - - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_statusup2(struct script_state *st) -{ - int type,val; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - val=conv_num(st,& (st->stack->stack_data[st->start+3])); - sd=script_rid2sd(st); - pc_statusup2(sd,type,val); - - return 0; -} -/*========================================== - * 装備品による能力値ボーナス - *------------------------------------------ - */ -int buildin_bonus(struct script_state *st) -{ - int type,val; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - val=conv_num(st,& (st->stack->stack_data[st->start+3])); - sd=script_rid2sd(st); - pc_bonus(sd,type,val); - - return 0; -} -/*========================================== - * 装備品による能力値ボーナス - *------------------------------------------ - */ -int buildin_bonus2(struct script_state *st) -{ - int type,type2,val; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - type2=conv_num(st,& (st->stack->stack_data[st->start+3])); - val=conv_num(st,& (st->stack->stack_data[st->start+4])); - sd=script_rid2sd(st); - pc_bonus2(sd,type,type2,val); - - return 0; -} -/*========================================== - * 装備品による能力値ボーナス - *------------------------------------------ - */ -int buildin_bonus3(struct script_state *st) -{ - int type,type2,type3,val; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - type2=conv_num(st,& (st->stack->stack_data[st->start+3])); - type3=conv_num(st,& (st->stack->stack_data[st->start+4])); - val=conv_num(st,& (st->stack->stack_data[st->start+5])); - sd=script_rid2sd(st); - pc_bonus3(sd,type,type2,type3,val); - - return 0; -} - -int buildin_bonus4(struct script_state *st) -{ - int type,type2,type3,type4,val; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - type2=conv_num(st,& (st->stack->stack_data[st->start+3])); - type3=conv_num(st,& (st->stack->stack_data[st->start+4])); - type4=conv_num(st,& (st->stack->stack_data[st->start+5])); - val=conv_num(st,& (st->stack->stack_data[st->start+6])); - sd=script_rid2sd(st); - pc_bonus4(sd,type,type2,type3,type4,val); - - return 0; -} -/*========================================== - * スキル所得 - *------------------------------------------ - */ -int buildin_skill(struct script_state *st) -{ - int id,level,flag=1; - struct map_session_data *sd; - - id=conv_num(st,& (st->stack->stack_data[st->start+2])); - level=conv_num(st,& (st->stack->stack_data[st->start+3])); - if( st->end>st->start+4 ) - flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); - sd=script_rid2sd(st); - pc_skill(sd,id,level,flag); - - return 0; -} - -// add x levels of skill (stackable) [Valaris] -int buildin_addtoskill(struct script_state *st) -{ - int id,level,flag=2; - struct map_session_data *sd; - - id=conv_num(st,& (st->stack->stack_data[st->start+2])); - level=conv_num(st,& (st->stack->stack_data[st->start+3])); - if( st->end>st->start+4 ) - flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); - sd=script_rid2sd(st); - pc_skill(sd,id,level,flag); - - return 0; -} - -/*========================================== - * ギルドスキル取得 - *------------------------------------------ - */ -int buildin_guildskill(struct script_state *st) -{ - int id,level,flag=0; - struct map_session_data *sd; - int i=0; - - id=conv_num(st,& (st->stack->stack_data[st->start+2])); - level=conv_num(st,& (st->stack->stack_data[st->start+3])); - if( st->end>st->start+4 ) - flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); - sd=script_rid2sd(st); - for(i=0;istack->stack_data[st->start+2])); - push_val(st->stack,C_INT, pc_checkskill( script_rid2sd(st) ,id) ); - return 0; -} -/*========================================== - * getgdskilllv(Guild_ID, Skill_ID); - * skill_id = 10000 : GD_APPROVAL - * 10001 : GD_KAFRACONTRACT - * 10002 : GD_GUARDIANRESEARCH - * 10003 : GD_GUARDUP - * 10004 : GD_EXTENSION - *------------------------------------------ - */ -int buildin_getgdskilllv(struct script_state *st) -{ - int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - int skill_id=conv_num(st,& (st->stack->stack_data[st->start+3])); - struct guild *g=guild_search(guild_id); - push_val(st->stack,C_INT, (g==NULL)?-1:guild_checkskill(g,skill_id) ); - return 0; -/* - struct map_session_data *sd=NULL; - struct guild *g=NULL; - int skill_id; - - skill_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - if(sd && sd->status.guild_id > 0) g=guild_search(sd->status.guild_id); - if(sd && g) { - push_val(st->stack,C_INT, guild_checkskill(g,skill_id+9999) ); - } else { - push_val(st->stack,C_INT,-1); - } - return 0; -*/ -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_basicskillcheck(struct script_state *st) -{ - push_val(st->stack,C_INT, battle_config.basic_skill_check); - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_getgmlevel(struct script_state *st) -{ - push_val(st->stack,C_INT, pc_isGM(script_rid2sd(st))); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_end(struct script_state *st) -{ - st->state = END; - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_checkoption(struct script_state *st) -{ - int type; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - - if(sd->sc.option & type){ - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } - - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_checkoption1(struct script_state *st) -{ - int type; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - - if(sd->sc.opt1 & type){ - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } - - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int buildin_checkoption2(struct script_state *st) -{ - int type; - struct map_session_data *sd; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - - if(sd->sc.opt2 & type){ - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_setoption(struct script_state *st) -{ - int type; - struct map_session_data *sd; - int flag=1; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - if(st->end>st->start+3 ) - flag=conv_num(st,&(st->stack->stack_data[st->start+3]) ); - - sd=script_rid2sd(st); - if (!sd) return 0; - - if (flag) {//Add option - if (type&OPTION_WEDDING && !battle_config.wedding_modifydisplay) - type&=~OPTION_WEDDING; //Do not show the wedding sprites - pc_setoption(sd,sd->sc.option|type); - } else//Remove option - pc_setoption(sd,sd->sc.option&~type); - return 0; -} - -/*========================================== - * Checkcart [Valaris] - *------------------------------------------ - */ - -int buildin_checkcart(struct script_state *st) -{ - struct map_session_data *sd; - - sd=script_rid2sd(st); - - if(pc_iscarton(sd)){ - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } - return 0; -} - -/*========================================== - * カートを付ける - *------------------------------------------ - */ -int buildin_setcart(struct script_state *st) -{ - struct map_session_data *sd; - - sd=script_rid2sd(st); - pc_setcart(sd,1); - - return 0; -} - -/*========================================== - * checkfalcon [Valaris] - *------------------------------------------ - */ - -int buildin_checkfalcon(struct script_state *st) -{ - struct map_session_data *sd; - - sd=script_rid2sd(st); - - if(pc_isfalcon(sd)){ - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } - - return 0; -} - - -/*========================================== - * 鷹を付ける - *------------------------------------------ - */ -int buildin_setfalcon(struct script_state *st) -{ - struct map_session_data *sd; - - sd=script_rid2sd(st); - pc_setfalcon(sd); - - return 0; -} - -/*========================================== - * Checkcart [Valaris] - *------------------------------------------ - */ - -int buildin_checkriding(struct script_state *st) -{ - struct map_session_data *sd; - - sd=script_rid2sd(st); - - if(pc_isriding(sd)){ - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } - - return 0; -} - - -/*========================================== - * ペコペコ乗り - *------------------------------------------ - */ -int buildin_setriding(struct script_state *st) -{ - struct map_session_data *sd; - - sd=script_rid2sd(st); - pc_setriding(sd); - - return 0; -} - -/*========================================== - * セーブポイントの保存 - *------------------------------------------ - */ -int buildin_savepoint(struct script_state *st) -{ - int x,y; - short map; - char *str; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - map = mapindex_name2id(str); - if (map) - pc_setsavepoint(script_rid2sd(st),map,x,y); - return 0; -} - -/*========================================== - * GetTimeTick(0: System Tick, 1: Time Second Tick) - *------------------------------------------ - */ -int buildin_gettimetick(struct script_state *st) /* Asgard Version */ -{ - int type; - time_t timer; - struct tm *t; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - - switch(type){ - case 2: - //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC - // from the system clock.) - push_val(st->stack,C_INT,(int)time(NULL)); - break; - case 1: - //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59) - time(&timer); - t=localtime(&timer); - push_val(st->stack,C_INT,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec)); - break; - case 0: - default: - //type 0:(System Ticks) - push_val(st->stack,C_INT,gettick()); - break; - } - return 0; -} - -/*========================================== - * GetTime(Type); - * 1: Sec 2: Min 3: Hour - * 4: WeekDay 5: MonthDay 6: Month - * 7: Year - *------------------------------------------ - */ -int buildin_gettime(struct script_state *st) /* Asgard Version */ -{ - int type; - time_t timer; - struct tm *t; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - - time(&timer); - t=localtime(&timer); - - switch(type){ - case 1://Sec(0~59) - push_val(st->stack,C_INT,t->tm_sec); - break; - case 2://Min(0~59) - push_val(st->stack,C_INT,t->tm_min); - break; - case 3://Hour(0~23) - push_val(st->stack,C_INT,t->tm_hour); - break; - case 4://WeekDay(0~6) - push_val(st->stack,C_INT,t->tm_wday); - break; - case 5://MonthDay(01~31) - push_val(st->stack,C_INT,t->tm_mday); - break; - case 6://Month(01~12) - push_val(st->stack,C_INT,t->tm_mon+1); - break; - case 7://Year(20xx) - push_val(st->stack,C_INT,t->tm_year+1900); - break; - case 8://Year Day(01~366) - push_val(st->stack,C_INT,t->tm_yday+1); - break; - default://(format error) - push_val(st->stack,C_INT,-1); - break; - } - return 0; -} - -/*========================================== - * GetTimeStr("TimeFMT", Length); - *------------------------------------------ - */ -int buildin_gettimestr(struct script_state *st) -{ - char *tmpstr; - char *fmtstr; - int maxlen; - time_t now = time(NULL); - - fmtstr=conv_str(st,& (st->stack->stack_data[st->start+2])); - maxlen=conv_num(st,& (st->stack->stack_data[st->start+3])); - - tmpstr=(char *)aMallocA((maxlen+1)*sizeof(char)); - strftime(tmpstr,maxlen,fmtstr,localtime(&now)); - tmpstr[maxlen]='\0'; - - push_str(st->stack,C_STR,(unsigned char *) tmpstr); - return 0; -} - -/*========================================== - * カプラ倉庫を開く - *------------------------------------------ - */ -int buildin_openstorage(struct script_state *st) -{ - storage_storageopen(script_rid2sd(st)); - return 0; -} - -int buildin_guildopenstorage(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - int ret; - ret = storage_guild_storageopen(sd); - push_val(st->stack,C_INT,ret); - return 0; -} - -/*========================================== - * アイテムによるスキル発動 - *------------------------------------------ - */ -int buildin_itemskill(struct script_state *st) -{ - int id,lv; - char *str; - struct map_session_data *sd=script_rid2sd(st); - - id=conv_num(st,& (st->stack->stack_data[st->start+2])); - lv=conv_num(st,& (st->stack->stack_data[st->start+3])); - str=conv_str(st,& (st->stack->stack_data[st->start+4])); - - // 詠唱中にスキルアイテムは使用できない - if(sd->ud.skilltimer != -1) - return 0; - - sd->skillitem=id; - sd->skillitemlv=lv; - clif_item_skill(sd,id,lv,str); - return 0; -} -/*========================================== - * アイテム作成 - *------------------------------------------ - */ -int buildin_produce(struct script_state *st) -{ - int trigger; - struct map_session_data *sd=script_rid2sd(st); - - trigger=conv_num(st,& (st->stack->stack_data[st->start+2])); - clif_skill_produce_mix_list(sd, trigger); - return 0; -} -/*========================================== - * NPCでペット作る - *------------------------------------------ - */ -int buildin_makepet(struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd(st); - struct script_data *data; - int id,pet_id; - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - - id=conv_num(st,data); - - pet_id = search_petDB_index(id, PET_CLASS); - - if (pet_id < 0) - pet_id = search_petDB_index(id, PET_EGG); - if (pet_id >= 0 && sd) { - 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 0; -} -/*========================================== - * NPCで経験値上げる - *------------------------------------------ - */ -int buildin_getexp(struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd(st); - int base=0,job=0; - - base=conv_num(st,& (st->stack->stack_data[st->start+2])); - job =conv_num(st,& (st->stack->stack_data[st->start+3])); - if(base<0 || job<0) - return 0; - if(sd) - pc_gainexp(sd,base,job); - - return 0; -} - -/*========================================== - * Gain guild exp [Celest] - *------------------------------------------ - */ -int buildin_guildgetexp(struct script_state *st) -{ - struct map_session_data *sd = script_rid2sd(st); - int exp; - - exp = conv_num(st,& (st->stack->stack_data[st->start+2])); - if(exp < 0) - return 0; - if(sd && sd->status.guild_id > 0) - guild_getexp (sd, exp); - - return 0; -} - -/*========================================== - * Changes the guild master of a guild [Skotlex] - *------------------------------------------ - */ -int buildin_guildchangegm(struct script_state *st) -{ - struct map_session_data *sd; - int guild_id; - char *name; - - guild_id = conv_num(st,& (st->stack->stack_data[st->start+2])); - name = conv_str(st,& (st->stack->stack_data[st->start+3])); - sd=map_nick2sd(name); - - if (!sd) - push_val(st->stack,C_INT,0); - else - push_val(st->stack,C_INT,guild_gm_change(guild_id, sd)); - - return 0; -} - -/*========================================== - * モンスター発生 - *------------------------------------------ - */ -int buildin_monster(struct script_state *st) -{ - int class_,amount,x,y; - char *str,*map,*event=""; - - map =conv_str(st,& (st->stack->stack_data[st->start+2])); - x =conv_num(st,& (st->stack->stack_data[st->start+3])); - y =conv_num(st,& (st->stack->stack_data[st->start+4])); - str =conv_str(st,& (st->stack->stack_data[st->start+5])); - class_=conv_num(st,& (st->stack->stack_data[st->start+6])); - amount=conv_num(st,& (st->stack->stack_data[st->start+7])); - if( st->end>st->start+8 ){ - event=conv_str(st,& (st->stack->stack_data[st->start+8])); - check_event(st, event); - } - - if (class_ >= 0 && !mobdb_checkid(class_)) { - ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_); - return 1; - } - mob_once_spawn(map_id2sd(st->rid),map,x,y,str,class_,amount,event); - return 0; -} -/*========================================== - * モンスター発生 - *------------------------------------------ - */ -int buildin_areamonster(struct script_state *st) -{ - int class_,amount,x0,y0,x1,y1; - char *str,*map,*event=""; - - map =conv_str(st,& (st->stack->stack_data[st->start+2])); - x0 =conv_num(st,& (st->stack->stack_data[st->start+3])); - y0 =conv_num(st,& (st->stack->stack_data[st->start+4])); - x1 =conv_num(st,& (st->stack->stack_data[st->start+5])); - y1 =conv_num(st,& (st->stack->stack_data[st->start+6])); - str =conv_str(st,& (st->stack->stack_data[st->start+7])); - class_=conv_num(st,& (st->stack->stack_data[st->start+8])); - amount=conv_num(st,& (st->stack->stack_data[st->start+9])); - if( st->end>st->start+10 ){ - event=conv_str(st,& (st->stack->stack_data[st->start+10])); - check_event(st, event); - } - - mob_once_spawn_area(map_id2sd(st->rid),map,x0,y0,x1,y1,str,class_,amount,event); - return 0; -} -/*========================================== - * モンスター削除 - *------------------------------------------ - */ -int buildin_killmonster_sub(struct block_list *bl,va_list ap) -{ - TBL_MOB* md = (TBL_MOB*)bl; - char *event=va_arg(ap,char *); - int allflag=va_arg(ap,int); - - if(!allflag){ - if(strcmp(event,md->npc_event)==0) - unit_remove_map(bl,1); - return 0; - }else{ - if(!md->spawn) - unit_remove_map(bl,1); - return 0; - } - return 0; -} -int buildin_killmonster(struct script_state *st) -{ - char *mapname,*event; - int m,allflag=0; - mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - event=conv_str(st,& (st->stack->stack_data[st->start+3])); - if(strcmp(event,"All")==0) - allflag = 1; - else - check_event(st, event); - - if( (m=map_mapname2mapid(mapname))<0 ) - return 0; - map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag); - return 0; -} - -int buildin_killmonsterall_sub(struct block_list *bl,va_list ap) -{ - unit_remove_map(bl,1); - return 0; -} -int buildin_killmonsterall(struct script_state *st) -{ - char *mapname; - int m; - mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - - if( (m=map_mapname2mapid(mapname))<0 ) - return 0; - map_foreachinmap(buildin_killmonsterall_sub, - m,BL_MOB); - return 0; -} - -/*========================================== - * Creates a clone of a player. - * clone map, x, y, event, char_id, master_id, mode, flag, duration - *------------------------------------------ - */ -int buildin_clone(struct script_state *st) { - struct map_session_data *sd, *msd=NULL; - int char_id,master_id=0,x,y, mode = 0, flag = 0; - unsigned int duration = 0; - char *map,*event=""; - - map=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - event=conv_str(st,& (st->stack->stack_data[st->start+5])); - char_id=conv_num(st,& (st->stack->stack_data[st->start+6])); - - if( st->end>st->start+7 ) - master_id=conv_num(st,& (st->stack->stack_data[st->start+7])); - - if( st->end>st->start+8 ) - mode=conv_num(st,& (st->stack->stack_data[st->start+8])); - - if( st->end>st->start+9 ) - flag=conv_num(st,& (st->stack->stack_data[st->start+9])); - - if( st->end>st->start+10 ) - duration=conv_num(st,& (st->stack->stack_data[st->start+10])); - - check_event(st, event); - - sd = map_charid2sd(char_id); - if (master_id) { - msd = map_charid2sd(master_id); - if (msd) - master_id = msd->bl.id; - else - master_id = 0; - } - if (sd) //Return ID of newly crafted clone. - push_val(st->stack,C_INT,mob_clone_spawn(sd, map, x, y, event, master_id, mode, flag, 1000*duration)); - else //Failed to create clone. - push_val(st->stack,C_INT,0); - - return 0; -} -/*========================================== - * イベント実行 - *------------------------------------------ - */ -int buildin_doevent(struct script_state *st) -{ - char *event; - event=conv_str(st,& (st->stack->stack_data[st->start+2])); - check_event(st, event); - npc_event(map_id2sd(st->rid),event,0); - return 0; -} -/*========================================== - * NPC主体イベント実行 - *------------------------------------------ - */ -int buildin_donpcevent(struct script_state *st) -{ - char *event; - event=conv_str(st,& (st->stack->stack_data[st->start+2])); - check_event(st, event); - npc_event_do(event); - return 0; -} -/*========================================== - * イベントタイマー追加 - *------------------------------------------ - */ -int buildin_addtimer(struct script_state *st) -{ - char *event; - int tick; - tick=conv_num(st,& (st->stack->stack_data[st->start+2])); - event=conv_str(st,& (st->stack->stack_data[st->start+3])); - check_event(st, event); - pc_addeventtimer(script_rid2sd(st),tick,event); - return 0; -} -/*========================================== - * イベントタイマー削除 - *------------------------------------------ - */ -int buildin_deltimer(struct script_state *st) -{ - char *event; - event=conv_str(st,& (st->stack->stack_data[st->start+2])); - check_event(st, event); - pc_deleventtimer(script_rid2sd(st),event); - return 0; -} -/*========================================== - * イベントタイマーのカウント値追加 - *------------------------------------------ - */ -int buildin_addtimercount(struct script_state *st) -{ - char *event; - int tick; - event=conv_str(st,& (st->stack->stack_data[st->start+2])); - tick=conv_num(st,& (st->stack->stack_data[st->start+3])); - check_event(st, event); - pc_addeventtimercount(script_rid2sd(st),event,tick); - return 0; -} - -/*========================================== - * NPCタイマー初期化 - *------------------------------------------ - */ -int buildin_initnpctimer(struct script_state *st) -{ - struct npc_data *nd; - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - //nd->u.scr.rid = st->rid; //NO, use npcattachtimer if you want a player-attached timer! [Skotlex] - npc_settimerevent_tick(nd,0); - npc_timerevent_start(nd, st->rid); - return 0; -} -/*========================================== - * NPCタイマー開始 - *------------------------------------------ - */ -int buildin_startnpctimer(struct script_state *st) -{ - struct npc_data *nd; - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - npc_timerevent_start(nd, st->rid); - return 0; -} -/*========================================== - * NPCタイマー停止 - *------------------------------------------ - */ -int buildin_stopnpctimer(struct script_state *st) -{ - struct npc_data *nd; - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - npc_timerevent_stop(nd); - return 0; -} -/*========================================== - * NPCタイマー情報所得 - *------------------------------------------ - */ -int buildin_getnpctimer(struct script_state *st) -{ - struct npc_data *nd; - struct map_session_data *sd; - int type=conv_num(st,& (st->stack->stack_data[st->start+2])); - int val=0; - if( st->end > st->start+3 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - switch(type){ - case 0: val=npc_gettimerevent_tick(nd); break; - case 1: - if (nd->u.scr.rid) { - sd = map_id2sd(nd->u.scr.rid); - if (!sd) { - if(battle_config.error_log) - ShowError("buildin_getnpctimer: Attached player not found!\n"); - break; - } - val = (sd->npc_timer_id != -1); - } else - val= (nd->u.scr.timerid !=-1); - break; - case 2: val= nd->u.scr.timeramount; break; - } - push_val(st->stack,C_INT,val); - return 0; -} -/*========================================== - * NPCタイマー値設定 - *------------------------------------------ - */ -int buildin_setnpctimer(struct script_state *st) -{ - int tick; - struct npc_data *nd; - tick=conv_num(st,& (st->stack->stack_data[st->start+2])); - if( st->end > st->start+3 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - npc_settimerevent_tick(nd,tick); - return 0; -} - -/*========================================== - * attaches the player rid to the timer [Celest] - *------------------------------------------ - */ -int buildin_attachnpctimer(struct script_state *st) -{ - struct map_session_data *sd; - struct npc_data *nd; - - nd=(struct npc_data *)map_id2bl(st->oid); - if( st->end > st->start+2 ) { - char *name = conv_str(st,& (st->stack->stack_data[st->start+2])); - sd=map_nick2sd(name); - } else { - sd = script_rid2sd(st); - } - - if (sd==NULL) - return 0; - - nd->u.scr.rid = sd->bl.id; - return 0; -} - -/*========================================== - * detaches a player rid from the timer [Celest] - *------------------------------------------ - */ -int buildin_detachnpctimer(struct script_state *st) -{ - struct npc_data *nd; - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - nd->u.scr.rid = 0; - return 0; -} - -/*========================================== - * To avoid "player not attached" script errors, this function is provided, - * it checks if there is a player attached to the current script. [Skotlex] - * If no, returns 0, if yes, returns the char_id of the attached player. - *------------------------------------------ - */ -int buildin_playerattached(struct script_state *st) -{ - struct map_session_data *sd; - if (st->rid == 0 || (sd = map_id2sd(st->rid)) == NULL) - push_val(st->stack,C_INT,0); - else - push_val(st->stack,C_INT,st->rid); - return 0; -} - -/*========================================== - * 天の声アナウンス - *------------------------------------------ - */ -int buildin_announce(struct script_state *st) -{ - char *str, *color=NULL; - int flag; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - flag=conv_num(st,& (st->stack->stack_data[st->start+3])); - if (st->end>st->start+4) - color=conv_str(st,& (st->stack->stack_data[st->start+4])); - - if(flag&0x0f){ - struct block_list *bl=(flag&0x08)? map_id2bl(st->oid) : - (struct block_list *)script_rid2sd(st); - if (color) - clif_announce(bl,str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0),flag); - else - clif_GMmessage(bl,str,(int)strlen(str)+1,flag); - }else { - if (color) - intif_announce(str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0), flag); - else - intif_GMmessage(str,(int)strlen(str)+1,flag); - } - return 0; -} -/*========================================== - * 天の声アナウンス(特定マップ) - *------------------------------------------ - */ -int buildin_mapannounce_sub(struct block_list *bl,va_list ap) -{ - char *str, *color; - int len,flag; - str=va_arg(ap,char *); - len=va_arg(ap,int); - flag=va_arg(ap,int); - color=va_arg(ap,char *); - if (color) - clif_announce(bl,str,len, strtol(color, (char **)NULL, 0), flag|3); - else - clif_GMmessage(bl,str,len,flag|3); - return 0; -} -int buildin_mapannounce(struct script_state *st) -{ - char *mapname,*str, *color=NULL; - int flag,m; - - mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - str=conv_str(st,& (st->stack->stack_data[st->start+3])); - flag=conv_num(st,& (st->stack->stack_data[st->start+4])); - if (st->end>st->start+5) - color=conv_str(st,& (st->stack->stack_data[st->start+5])); - - if( (m=map_mapname2mapid(mapname))<0 ) - return 0; - - map_foreachinmap(buildin_mapannounce_sub, - m, BL_PC, str,strlen(str)+1,flag&0x10, color); - return 0; -} -/*========================================== - * 天の声アナウンス(特定エリア) - *------------------------------------------ - */ -int buildin_areaannounce(struct script_state *st) -{ - char *map,*str,*color=NULL; - int flag,m; - int x0,y0,x1,y1; - - map=conv_str(st,& (st->stack->stack_data[st->start+2])); - x0=conv_num(st,& (st->stack->stack_data[st->start+3])); - y0=conv_num(st,& (st->stack->stack_data[st->start+4])); - x1=conv_num(st,& (st->stack->stack_data[st->start+5])); - y1=conv_num(st,& (st->stack->stack_data[st->start+6])); - str=conv_str(st,& (st->stack->stack_data[st->start+7])); - flag=conv_num(st,& (st->stack->stack_data[st->start+8])); - if (st->end>st->start+9) - color=conv_str(st,& (st->stack->stack_data[st->start+9])); - - if( (m=map_mapname2mapid(map))<0 ) - return 0; - - map_foreachinarea(buildin_mapannounce_sub, - m,x0,y0,x1,y1,BL_PC, str,strlen(str)+1,flag&0x10, color); - return 0; -} - -/*========================================== - * ユーザー数所得 - *------------------------------------------ - */ -int buildin_getusers(struct script_state *st) -{ - int flag=conv_num(st,& (st->stack->stack_data[st->start+2])); - struct block_list *bl=map_id2bl((flag&0x08)?st->oid:st->rid); - int val=0; - switch(flag&0x07){ - case 0: val=map[bl->m].users; break; - case 1: val=map_getusers(); break; - } - push_val(st->stack,C_INT,val); - return 0; -} -/*========================================== - * Works like @WHO - displays all online users names in window - *------------------------------------------ - */ -int buildin_getusersname(struct script_state *st) -{ - struct map_session_data *pl_sd = NULL, **pl_allsd; - int i=0,disp_num=1, users; - - pl_allsd = map_getallusers(&users); - - for (i=0;ioid); - clif_scriptmes(script_rid2sd(st),st->oid,pl_sd->status.name); - } - } - return 0; -} -/*========================================== - * マップ指定ユーザー数所得 - *------------------------------------------ - */ -int buildin_getmapusers(struct script_state *st) -{ - char *str; - int m; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - if( (m=map_mapname2mapid(str))< 0){ - push_val(st->stack,C_INT,-1); - return 0; - } - push_val(st->stack,C_INT,map[m].users); - return 0; -} -/*========================================== - * エリア指定ユーザー数所得 - *------------------------------------------ - */ -int buildin_getareausers_sub(struct block_list *bl,va_list ap) -{ - int *users=va_arg(ap,int *); - (*users)++; - return 0; -} -int buildin_getareausers(struct script_state *st) -{ - char *str; - int m,x0,y0,x1,y1,users=0; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x0=conv_num(st,& (st->stack->stack_data[st->start+3])); - y0=conv_num(st,& (st->stack->stack_data[st->start+4])); - x1=conv_num(st,& (st->stack->stack_data[st->start+5])); - y1=conv_num(st,& (st->stack->stack_data[st->start+6])); - if( (m=map_mapname2mapid(str))< 0){ - push_val(st->stack,C_INT,-1); - return 0; - } - map_foreachinarea(buildin_getareausers_sub, - m,x0,y0,x1,y1,BL_PC,&users); - push_val(st->stack,C_INT,users); - return 0; -} - -/*========================================== - * エリア指定ドロップアイテム数所得 - *------------------------------------------ - */ -int buildin_getareadropitem_sub(struct block_list *bl,va_list ap) -{ - int item=va_arg(ap,int); - int *amount=va_arg(ap,int *); - struct flooritem_data *drop=(struct flooritem_data *)bl; - - if(drop->item_data.nameid==item) - (*amount)+=drop->item_data.amount; - - return 0; -} -int buildin_getareadropitem(struct script_state *st) -{ - char *str; - int m,x0,y0,x1,y1,item,amount=0; - struct script_data *data; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x0=conv_num(st,& (st->stack->stack_data[st->start+3])); - y0=conv_num(st,& (st->stack->stack_data[st->start+4])); - x1=conv_num(st,& (st->stack->stack_data[st->start+5])); - y1=conv_num(st,& (st->stack->stack_data[st->start+6])); - - data=&(st->stack->stack_data[st->start+7]); - get_val(st,data); - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - item=512; - if( item_data ) - item=item_data->nameid; - }else - item=conv_num(st,data); - - if( (m=map_mapname2mapid(str))< 0){ - push_val(st->stack,C_INT,-1); - return 0; - } - map_foreachinarea(buildin_getareadropitem_sub, - m,x0,y0,x1,y1,BL_ITEM,item,&amount); - push_val(st->stack,C_INT,amount); - return 0; -} -/*========================================== - * NPCの有効化 - *------------------------------------------ - */ -int buildin_enablenpc(struct script_state *st) -{ - char *str; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - npc_enable(str,1); - return 0; -} -/*========================================== - * NPCの無効化 - *------------------------------------------ - */ -int buildin_disablenpc(struct script_state *st) -{ - char *str; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - npc_enable(str,0); - return 0; -} - -int buildin_enablearena(struct script_state *st) // Added by RoVeRT -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - struct chat_data *cd; - - - if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL) - return 0; - - npc_enable(nd->name,1); - nd->arenaflag=1; - - if(cd->users>=cd->trigger && cd->npc_event[0]) - npc_timer_event(cd->npc_event); - - return 0; -} -int buildin_disablearena(struct script_state *st) // Added by RoVeRT -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - nd->arenaflag=0; - - return 0; -} -/*========================================== - * 隠れているNPCの表示 - *------------------------------------------ - */ -int buildin_hideoffnpc(struct script_state *st) -{ - char *str; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - npc_enable(str,2); - return 0; -} -/*========================================== - * NPCをハイディング - *------------------------------------------ - */ -int buildin_hideonnpc(struct script_state *st) -{ - char *str; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - npc_enable(str,4); - return 0; -} -/*========================================== - * 状態異常にかかる - *------------------------------------------ - */ -int buildin_sc_start(struct script_state *st) -{ - struct block_list *bl; - int type,tick,val1,val4=0; - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - tick=conv_num(st,& (st->stack->stack_data[st->start+3])); - val1=conv_num(st,& (st->stack->stack_data[st->start+4])); - if( st->end>st->start+5 ) //指定したキャラを状態異常にする - bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+5]))); - else - bl = map_id2bl(st->rid); - - if (potion_flag==1 && potion_target) { - bl = map_id2bl(potion_target); - tick/=2; //Thrown potions only last half. - val4 = 1; //Mark that this was a thrown sc_effect - } - if (bl) - status_change_start(bl,type,10000,val1,0,0,val4,tick,11); - return 0; -} - -/*========================================== - * 状態異常にかかる(確率指定) - *------------------------------------------ - */ -int buildin_sc_start2(struct script_state *st) -{ - struct block_list *bl; - int type,tick,val1,val4=0,per; - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - tick=conv_num(st,& (st->stack->stack_data[st->start+3])); - val1=conv_num(st,& (st->stack->stack_data[st->start+4])); - per=conv_num(st,& (st->stack->stack_data[st->start+5])); - if( st->end>st->start+6 ) //指定したキャラを状態異常にする - bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6]))); - else - bl = map_id2bl(st->rid); - - if (potion_flag==1 && potion_target) { - bl = map_id2bl(potion_target); - tick/=2; - val4 = 1; - } - - if(bl) - status_change_start(bl,type,per,val1,0,0,val4,tick,11); - return 0; -} - -/*========================================== - * Starts a SC_ change with the four values passed. [Skotlex] - * Final optional argument is the ID of player to affect. - * sc_start4 type, duration, val1, val2, val3, val4, ; - *------------------------------------------ - */ -int buildin_sc_start4(struct script_state *st) -{ - struct block_list *bl; - int type,tick,val1,val2,val3,val4; - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - tick=conv_num(st,& (st->stack->stack_data[st->start+3])); - val1=conv_num(st,& (st->stack->stack_data[st->start+4])); - val2=conv_num(st,& (st->stack->stack_data[st->start+5])); - val3=conv_num(st,& (st->stack->stack_data[st->start+6])); - val4=conv_num(st,& (st->stack->stack_data[st->start+7])); - if( st->end>st->start+8 ) - bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+8]))); - else - bl = map_id2bl(st->rid); - - if (potion_flag==1 && potion_target) { - bl = map_id2bl(potion_target); - tick/=2; - } - if (bl) - status_change_start(bl,type,10000,val1,val2,val3,val4,tick,11); - return 0; -} - -/*========================================== - * 状態異常が直る - *------------------------------------------ - */ -int buildin_sc_end(struct script_state *st) -{ - struct block_list *bl; - int type; - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - bl = map_id2bl(st->rid); - - if (potion_flag==1 && potion_target) - bl = map_id2bl(potion_target); - - if (bl) - status_change_end(bl,type,-1); - return 0; -} -/*========================================== - * 状態異常耐性を計算した確率を返す - *------------------------------------------ - */ -int buildin_getscrate(struct script_state *st) -{ - struct block_list *bl; - int sc_def=0,type,rate; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - rate=conv_num(st,& (st->stack->stack_data[st->start+3])); - if( st->end>st->start+4 ) //指定したキャラの耐性を計算する - bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6]))); - else - bl = map_id2bl(st->rid); - - if (bl) - sc_def = status_get_sc_def(bl,type); - - rate = rate*(10000-sc_def)/10000; - push_val(st->stack,C_INT,rate<0?0:rate); - - return 0; - -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_debugmes(struct script_state *st) -{ - conv_str(st,& (st->stack->stack_data[st->start+2])); - ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,st->stack->stack_data[st->start+2].u.str); - return 0; -} - -/*========================================== - *捕獲アイテム使用 - *------------------------------------------ - */ -int buildin_catchpet(struct script_state *st) -{ - int pet_id; - struct map_session_data *sd; - pet_id= conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - pet_catch_process1(sd,pet_id); - return 0; -} - -/*========================================== - *携帯卵孵化機使用 - *------------------------------------------ - */ -int buildin_birthpet(struct script_state *st) -{ - struct map_session_data *sd; - sd=script_rid2sd(st); - clif_sendegg(sd); - return 0; -} - -/*========================================== - * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes) - *------------------------------------------ - */ -int buildin_resetlvl(struct script_state *st) -{ - struct map_session_data *sd; - - int type=conv_num(st,& (st->stack->stack_data[st->start+2])); - - sd=script_rid2sd(st); - pc_resetlvl(sd,type); - return 0; -} -/*========================================== - * ステータスリセット - *------------------------------------------ - */ -int buildin_resetstatus(struct script_state *st) -{ - struct map_session_data *sd; - sd=script_rid2sd(st); - pc_resetstate(sd); - return 0; -} - -/*========================================== - * script command resetskill - *------------------------------------------ - */ -int buildin_resetskill(struct script_state *st) -{ - struct map_session_data *sd; - sd=script_rid2sd(st); - pc_resetskill(sd,1); - return 0; -} - -/*========================================== - * Counts total amount of skill points. - *------------------------------------------ - */ -int buildin_skillpointcount(struct script_state *st) -{ - struct map_session_data *sd; - sd=script_rid2sd(st); - push_val(st->stack,C_INT,sd->status.skill_point + pc_resetskill(sd,2)); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int buildin_changebase(struct script_state *st) -{ - struct map_session_data *sd=NULL; - int vclass; - - if( st->end>st->start+3 ) - sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+3]))); - else - sd=script_rid2sd(st); - - if(sd == NULL) - return 0; - - vclass = conv_num(st,& (st->stack->stack_data[st->start+2])); - if(vclass == JOB_WEDDING) - { - if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites - sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore. - ) - return 0; - } - - if(!sd->disguise && vclass != sd->vd.class_) { - status_set_viewdata(&sd->bl, vclass); - //Updated client view. Base, Weapon and Cloth Colors. - clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_); - clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); - if (sd->vd.cloth_color) - clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); - } - - return 0; -} - -/*========================================== - * 性別変換 - *------------------------------------------ - */ -int buildin_changesex(struct script_state *st) { - struct map_session_data *sd = NULL; - sd = script_rid2sd(st); - - if (sd->status.sex == 0) { - sd->status.sex = 1; - sd->sex = 1; - if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) - sd->status.class_ -= 1; - } else if (sd->status.sex == 1) { - sd->status.sex = 0; - sd->sex = 0; - if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) - sd->status.class_ += 1; - } - chrif_char_ask_name(-1, sd->status.name, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex - chrif_save(sd,0); - return 0; -} - -/*========================================== - * npcチャット作成 - *------------------------------------------ - */ -int buildin_waitingroom(struct script_state *st) -{ - char *name,*ev=""; - int limit, trigger = 0,pub=1; - name=conv_str(st,& (st->stack->stack_data[st->start+2])); - limit= conv_num(st,& (st->stack->stack_data[st->start+3])); - if(limit==0) - pub=3; - - if( (st->end > st->start+5) ){ - struct script_data* data=&(st->stack->stack_data[st->start+5]); - get_val(st,data); - if(data->type==C_INT){ - // 新Athena仕様(旧Athena仕様と互換性あり) - ev=conv_str(st,& (st->stack->stack_data[st->start+4])); - trigger=conv_num(st,& (st->stack->stack_data[st->start+5])); - }else{ - // eathena仕様 - trigger=conv_num(st,& (st->stack->stack_data[st->start+4])); - ev=conv_str(st,& (st->stack->stack_data[st->start+5])); - } - }else{ - // 旧Athena仕様 - if( st->end > st->start+4 ) - ev=conv_str(st,& (st->stack->stack_data[st->start+4])); - } - chat_createnpcchat( (struct npc_data *)map_id2bl(st->oid), - limit,pub,trigger,name,(int)strlen(name)+1,ev); - return 0; -} -/*========================================== - * Works like 'announce' but outputs in the common chat window - *------------------------------------------ - */ -int buildin_globalmes(struct script_state *st) -{ - struct block_list *bl = map_id2bl(st->oid); - struct npc_data *nd = (struct npc_data *)bl; - char *name=NULL,*mes; - - mes=conv_str(st,& (st->stack->stack_data[st->start+2])); // メッセージの取得 - if(mes==NULL) return 0; - - if(st->end>st->start+3){ // NPC名の取得(123#456) - name=conv_str(st,& (st->stack->stack_data[st->start+3])); - } else { - name=nd->name; - } - - npc_globalmessage(name,mes); // グローバルメッセージ送信 - - return 0; -} -/*========================================== - * npcチャット削除 - *------------------------------------------ - */ -int buildin_delwaitingroom(struct script_state *st) -{ - struct npc_data *nd; - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - chat_deletenpcchat(nd); - return 0; -} -/*========================================== - * npcチャット全員蹴り出す - *------------------------------------------ - */ -int buildin_waitingroomkickall(struct script_state *st) -{ - struct npc_data *nd; - struct chat_data *cd; - - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) - return 0; - chat_npckickall(cd); - return 0; -} - -/*========================================== - * npcチャットイベント有効化 - *------------------------------------------ - */ -int buildin_enablewaitingroomevent(struct script_state *st) -{ - struct npc_data *nd; - struct chat_data *cd; - - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) - return 0; - chat_enableevent(cd); - return 0; -} - -/*========================================== - * npcチャットイベント無効化 - *------------------------------------------ - */ -int buildin_disablewaitingroomevent(struct script_state *st) -{ - struct npc_data *nd; - struct chat_data *cd; - - if( st->end > st->start+2 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) - return 0; - chat_disableevent(cd); - return 0; -} -/*========================================== - * npcチャット状態所得 - *------------------------------------------ - */ -int buildin_getwaitingroomstate(struct script_state *st) -{ - struct npc_data *nd; - struct chat_data *cd; - int val=0,type; - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - if( st->end > st->start+3 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ){ - push_val(st->stack,C_INT,-1); - return 0; - } - - switch(type){ - case 0: val=cd->users; break; - case 1: val=cd->limit; break; - case 2: val=cd->trigger&0x7f; break; - case 3: val=((cd->trigger&0x80)>0); break; - case 32: val=(cd->users >= cd->limit); break; - case 33: val=(cd->users >= cd->trigger); break; - - case 4: - push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->title); - return 0; - case 5: - push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->pass); - return 0; - case 16: - push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->npc_event); - return 0; - } - push_val(st->stack,C_INT,val); - return 0; -} - -/*========================================== - * チャットメンバー(規定人数)ワープ - *------------------------------------------ - */ -int buildin_warpwaitingpc(struct script_state *st) -{ - int x,y,i,n; - char *str; - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - struct chat_data *cd; - - if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) - return 0; - - n=cd->trigger&0x7f; - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - - if( st->end > st->start+5 ) - n=conv_num(st,& (st->stack->stack_data[st->start+5])); - - for(i=0;iusersd[0]; // リスト先頭のPCを次々に。 - - mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpc")+(i<<24),sd->bl.id); - - if(strcmp(str,"Random")==0) - pc_randomwarp(sd,3); - else if(strcmp(str,"SavePoint")==0){ - if(map[sd->bl.m].flag.noteleport) // テレポ禁止 - return 0; - - pc_setpos(sd,sd->status.save_point.map, - sd->status.save_point.x,sd->status.save_point.y,3); - }else - pc_setpos(sd,mapindex_name2id(str),x,y,0); - } - mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpcnum"),n); - return 0; -} -/*========================================== - * RIDのアタッチ - *------------------------------------------ - */ -int buildin_attachrid(struct script_state *st) -{ - st->rid=conv_num(st,& (st->stack->stack_data[st->start+2])); - push_val(st->stack,C_INT, (map_id2sd(st->rid)!=NULL)); - return 0; -} -/*========================================== - * RIDのデタッチ - *------------------------------------------ - */ -int buildin_detachrid(struct script_state *st) -{ - st->rid=0; - return 0; -} -/*========================================== - * 存在チェック - *------------------------------------------ - */ -int buildin_isloggedin(struct script_state *st) -{ - push_val(st->stack,C_INT, map_id2sd( - conv_num(st,& (st->stack->stack_data[st->start+2])) )!=NULL ); - return 0; -} - - -/*========================================== - * - *------------------------------------------ - */ -enum { MF_NOMEMO,MF_NOTELEPORT,MF_NOSAVE,MF_NOBRANCH,MF_NOPENALTY,MF_NOZENYPENALTY, - MF_PVP,MF_PVP_NOPARTY,MF_PVP_NOGUILD,MF_GVG,MF_GVG_NOPARTY,MF_NOTRADE,MF_NOSKILL, - MF_NOWARP,MF_FREE,MF_NOICEWALL,MF_SNOW,MF_FOG,MF_SAKURA,MF_LEAVES,MF_RAIN, - MF_INDOORS,MF_NOGO,MF_CLOUDS,MF_CLOUDS2,MF_FIREWORKS,MF_GVG_CASTLE,MF_GVG_DUNGEON,MF_NIGHTENABLED, - MF_NOBASEEXP, MF_NOJOBEXP, MF_NOMOBLOOT, MF_NOMVPLOOT, MF_NORETURN, MF_NOWARPTO, MF_NIGHTMAREDROP, - MF_RESTRICTED, MF_NOCOMMAND, MF_NODROP, MF_JEXP, MF_BEXP, MF_NOVENDING }; - -int buildin_setmapflagnosave(struct script_state *st) -{ - int m,x,y; - unsigned short mapindex; - char *str,*str2; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - str2=conv_str(st,& (st->stack->stack_data[st->start+3])); - x=conv_num(st,& (st->stack->stack_data[st->start+4])); - y=conv_num(st,& (st->stack->stack_data[st->start+5])); - m = map_mapname2mapid(str); - mapindex = mapindex_name2id(str2); - - if(m >= 0 && mapindex) { - map[m].flag.nosave=1; - map[m].save.map=mapindex; - map[m].save.x=x; - map[m].save.y=y; - } - - return 0; -} - -int buildin_setmapflag(struct script_state *st) -{ - int m,i; - char *str; - char *val=NULL; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - i=conv_num(st,& (st->stack->stack_data[st->start+3])); - if(st->end>st->start+4){ - val=conv_str(st,& (st->stack->stack_data[st->start+4])); - } - m = map_mapname2mapid(str); - if(m >= 0) { - switch(i) { - case MF_NOMEMO: - map[m].flag.nomemo=1; - break; - case MF_NOTELEPORT: - map[m].flag.noteleport=1; - break; - case MF_NOBRANCH: - map[m].flag.nobranch=1; - break; - case MF_NOPENALTY: - map[m].flag.nopenalty=1; - break; - case MF_NOZENYPENALTY: - map[m].flag.nozenypenalty=1; - break; - case MF_PVP: - map[m].flag.pvp=1; - break; - case MF_PVP_NOPARTY: - map[m].flag.pvp_noparty=1; - break; - case MF_PVP_NOGUILD: - map[m].flag.pvp_noguild=1; - break; - case MF_GVG: - map[m].flag.gvg=1; - break; - case MF_GVG_NOPARTY: - map[m].flag.gvg_noparty=1; - break; - case MF_GVG_DUNGEON: - map[m].flag.gvg_dungeon=1; - break; - case MF_GVG_CASTLE: - map[m].flag.gvg_castle=1; - break; - case MF_NOTRADE: - map[m].flag.notrade=1; - break; - case MF_NODROP: - map[m].flag.nodrop=1; - break; - case MF_NOSKILL: - map[m].flag.noskill=1; - break; - case MF_NOWARP: - map[m].flag.nowarp=1; - break; - case MF_NOICEWALL: // [Valaris] - map[m].flag.noicewall=1; - break; - case MF_SNOW: // [Valaris] - map[m].flag.snow=1; - break; - case MF_CLOUDS: - map[m].flag.clouds=1; - break; - case MF_CLOUDS2: // [Valaris] - map[m].flag.clouds2=1; - break; - case MF_FOG: // [Valaris] - map[m].flag.fog=1; - break; - case MF_FIREWORKS: - map[m].flag.fireworks=1; - break; - case MF_SAKURA: // [Valaris] - map[m].flag.sakura=1; - break; - case MF_LEAVES: // [Valaris] - map[m].flag.leaves=1; - break; - case MF_RAIN: // [Valaris] - map[m].flag.rain=1; - break; - case MF_INDOORS: // celest - map[m].flag.indoors=1; - break; - case MF_NIGHTENABLED: - map[m].flag.nightenabled=1; - break; - case MF_NOGO: // celest - map[m].flag.nogo=1; - break; - case MF_NOBASEEXP: - map[m].flag.nobaseexp=1; - break; - case MF_NOJOBEXP: - map[m].flag.nojobexp=1; - break; - case MF_NOMOBLOOT: - map[m].flag.nomobloot=1; - break; - case MF_NOMVPLOOT: - map[m].flag.nomvploot=1; - break; - case MF_NORETURN: - map[m].flag.noreturn=1; - break; - case MF_NOWARPTO: - map[m].flag.nowarpto=1; - break; - case MF_NIGHTMAREDROP: - map[m].flag.pvp_nightmaredrop=1; - break; - case MF_RESTRICTED: - map[m].flag.restricted=1; - break; - case MF_NOCOMMAND: - map[m].flag.nocommand=1; - break; - case MF_JEXP: - map[m].jexp = (!val || atoi(val) < 0) ? 100 : atoi(val); - break; - case MF_BEXP: - map[m].bexp = (!val || atoi(val) < 0) ? 100 : atoi(val); - break; - case MF_NOVENDING: - map[m].flag.novending=1; - break; - } - } - - return 0; -} - -int buildin_removemapflag(struct script_state *st) -{ - int m,i; - char *str; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - i=conv_num(st,& (st->stack->stack_data[st->start+3])); - m = map_mapname2mapid(str); - if(m >= 0) { - switch(i) { - case MF_NOMEMO: - map[m].flag.nomemo=0; - break; - case MF_NOTELEPORT: - map[m].flag.noteleport=0; - break; - case MF_NOSAVE: - map[m].flag.nosave=0; - break; - case MF_NOBRANCH: - map[m].flag.nobranch=0; - break; - case MF_NOPENALTY: - map[m].flag.nopenalty=0; - break; - case MF_PVP: - map[m].flag.pvp=0; - break; - case MF_PVP_NOPARTY: - map[m].flag.pvp_noparty=0; - break; - case MF_PVP_NOGUILD: - map[m].flag.pvp_noguild=0; - break; - case MF_GVG: - map[m].flag.gvg=0; - break; - case MF_GVG_NOPARTY: - map[m].flag.gvg_noparty=0; - break; - case MF_GVG_DUNGEON: - map[m].flag.gvg_dungeon=0; - break; - case MF_GVG_CASTLE: - map[m].flag.gvg_castle=0; - break; - case MF_NOZENYPENALTY: - map[m].flag.nozenypenalty=0; - break; - case MF_NOTRADE: - map[m].flag.notrade=0; - break; - case MF_NODROP: - map[m].flag.nodrop=0; - break; - case MF_NOSKILL: - map[m].flag.noskill=0; - break; - case MF_NOWARP: - map[m].flag.nowarp=0; - break; - case MF_NOICEWALL: // [Valaris] - map[m].flag.noicewall=0; - break; - case MF_SNOW: // [Valaris] - map[m].flag.snow=0; - break; - case MF_CLOUDS: - map[m].flag.clouds=0; - break; - case MF_CLOUDS2: // [Valaris] - map[m].flag.clouds2=0; - break; - case MF_FOG: // [Valaris] - map[m].flag.fog=0; - break; - case MF_FIREWORKS: - map[m].flag.fireworks=0; - break; - case MF_SAKURA: // [Valaris] - map[m].flag.sakura=0; - break; - case MF_LEAVES: // [Valaris] - map[m].flag.leaves=0; - break; - case MF_RAIN: // [Valaris] - map[m].flag.rain=0; - break; - case MF_INDOORS: // celest - map[m].flag.indoors=0; - break; - case MF_NIGHTENABLED: - map[m].flag.nightenabled=0; - break; - case MF_NOGO: // celest - map[m].flag.nogo=0; - break; - case MF_NOBASEEXP: - map[m].flag.nobaseexp=0; - break; - case MF_NOJOBEXP: - map[m].flag.nojobexp=0; - break; - case MF_NOMOBLOOT: - map[m].flag.nomobloot=0; - break; - case MF_NOMVPLOOT: - map[m].flag.nomvploot=0; - break; - case MF_NORETURN: - map[m].flag.noreturn=0; - break; - case MF_NOWARPTO: - map[m].flag.nowarpto=0; - break; - case MF_NIGHTMAREDROP: - map[m].flag.pvp_nightmaredrop=0; - break; - case MF_RESTRICTED: - map[m].flag.restricted=0; - break; - case MF_NOCOMMAND: - map[m].flag.nocommand=0; - break; - case MF_JEXP: - map[m].jexp=100; - break; - case MF_BEXP: - map[m].bexp=100; - break; - case MF_NOVENDING: - map[m].flag.novending=0; - break; - } - } - - return 0; -} - -int buildin_pvpon(struct script_state *st) -{ - int m,i,users; - char *str; - struct map_session_data *pl_sd=NULL, **pl_allsd; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - m = map_mapname2mapid(str); - if(m >= 0 && !map[m].flag.pvp) { - map[m].flag.pvp = 1; - clif_send0199(m,1); - - if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris] - return 0; - - pl_allsd = map_getallusers(&users); - - for(i=0;ibl.m && pl_sd->pvp_timer == -1) - { - pl_sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,pl_sd->bl.id,0); - pl_sd->pvp_rank=0; - pl_sd->pvp_lastusers=0; - pl_sd->pvp_point=5; - pl_sd->pvp_won = 0; - pl_sd->pvp_lost = 0; - } - } - } - return 0; -} - -int buildin_pvpoff(struct script_state *st) -{ - int m,i,users; - char *str; - struct map_session_data *pl_sd=NULL, **pl_allsd; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - m = map_mapname2mapid(str); - if(m >= 0 && map[m].flag.pvp) { //fixed Lupus - map[m].flag.pvp = 0; - clif_send0199(m,0); - - if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] - return 0; - - pl_allsd = map_getallusers(&users); - - for(i=0;ibl.m) - { - clif_pvpset(pl_sd,0,0,2); - if(pl_sd->pvp_timer != -1) { - delete_timer(pl_sd->pvp_timer,pc_calc_pvprank_timer); - pl_sd->pvp_timer = -1; - } - } - } - } - - return 0; -} - -int buildin_gvgon(struct script_state *st) -{ - int m; - char *str; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - m = map_mapname2mapid(str); - if(m >= 0 && !map[m].flag.gvg) { - map[m].flag.gvg = 1; - clif_send0199(m,3); - } - - return 0; -} -int buildin_gvgoff(struct script_state *st) -{ - int m; - char *str; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - m = map_mapname2mapid(str); - if(m >= 0 && map[m].flag.gvg) { - map[m].flag.gvg = 0; - clif_send0199(m,0); - } - - return 0; -} -/*========================================== - * Shows an emoticon on top of the player/npc - * emotion emotion#, - *------------------------------------------ - */ -//Optional second parameter added by [Skotlex] -int buildin_emotion(struct script_state *st) -{ - int type; - int player=0; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - if(type < 0 || type > 100) - return 0; - - if( st->end>st->start+3 ) - player=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if (player) { - struct map_session_data *sd = script_rid2sd(st); - if (sd) - clif_emotion(&sd->bl,type); - } else - clif_emotion(map_id2bl(st->oid),type); - return 0; -} - -int buildin_maprespawnguildid_sub(struct block_list *bl,va_list ap) -{ - int g_id=va_arg(ap,int); - int flag=va_arg(ap,int); - struct map_session_data *sd=NULL; - struct mob_data *md=NULL; - - if(bl->type == BL_PC) - sd=(struct map_session_data*)bl; - if(bl->type == BL_MOB) - md=(struct mob_data *)bl; - - if(sd){ - if((sd->status.guild_id == g_id) && (flag&1)) - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); - else if((sd->status.guild_id != g_id) && (flag&2)) - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); - else if (sd->status.guild_id == 0) // Warp out players not in guild [Valaris] - pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); // end addition [Valaris] - } - if(md && flag&4){ - if(!md->guardian_data && md->class_ != MOBID_EMPERIUM) - unit_remove_map(bl,1); - } - return 0; -} -int buildin_maprespawnguildid(struct script_state *st) -{ - char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - int g_id=conv_num(st,& (st->stack->stack_data[st->start+3])); - int flag=conv_num(st,& (st->stack->stack_data[st->start+4])); - - int m=map_mapname2mapid(mapname); - - if(m) map_foreachinmap(buildin_maprespawnguildid_sub,m,BL_CHAR,g_id,flag); - return 0; -} - -int buildin_agitstart(struct script_state *st) -{ - if(agit_flag==1) return 0; // Agit already Start. - agit_flag=1; - guild_agit_start(); - return 0; -} - -int buildin_agitend(struct script_state *st) -{ - if(agit_flag==0) return 0; // Agit already End. - agit_flag=0; - guild_agit_end(); - return 0; -} -/*========================================== - * agitcheck 1; // choice script - * if(@agit_flag == 1) goto agit; - * if(agitcheck(0) == 1) goto agit; - *------------------------------------------ - */ -int buildin_agitcheck(struct script_state *st) -{ - struct map_session_data *sd; - int cond; - - cond=conv_num(st,& (st->stack->stack_data[st->start+2])); - - if(cond == 0) { - if (agit_flag==1) push_val(st->stack,C_INT,1); - if (agit_flag==0) push_val(st->stack,C_INT,0); - } else { - sd=script_rid2sd(st); - if (agit_flag==1) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),1); - if (agit_flag==0) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),0); - } - return 0; -} -int buildin_flagemblem(struct script_state *st) -{ - int g_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - - if(g_id < 0) return 0; - -// printf("Script.c: [FlagEmblem] GuildID=%d, Emblem=%d.\n", g->guild_id, g->emblem_id); - ((struct npc_data *)map_id2bl(st->oid))->u.scr.guild_id = g_id; - return 0; -} - -int buildin_getcastlename(struct script_state *st) -{ - char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - struct guild_castle *gc; - int i; - for(i=0;imap_name)==0){ - break; - } - } - } - if(gc) - push_str(st->stack,C_CONSTSTR,(unsigned char *) gc->castle_name); - else - push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); - return 0; -} - -int buildin_getcastledata(struct script_state *st) -{ - char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - int index=conv_num(st,& (st->stack->stack_data[st->start+3])); - char *event=NULL; - struct guild_castle *gc; - int i,j; - - if( st->end>st->start+4 && index==0){ - for(i=0,j=-1;imap_name)==0 ) - j=i; - if(j>=0){ - event=conv_str(st,& (st->stack->stack_data[st->start+4])); - check_event(st, event); - guild_addcastleinfoevent(j,17,event); - } - } - - for(i=0;imap_name)==0){ - switch(index){ - case 0: for(j=1;j<26;j++) guild_castledataload(gc->castle_id,j); break; // Initialize[AgitInit] - case 1: push_val(st->stack,C_INT,gc->guild_id); break; - case 2: push_val(st->stack,C_INT,gc->economy); break; - case 3: push_val(st->stack,C_INT,gc->defense); break; - case 4: push_val(st->stack,C_INT,gc->triggerE); break; - case 5: push_val(st->stack,C_INT,gc->triggerD); break; - case 6: push_val(st->stack,C_INT,gc->nextTime); break; - case 7: push_val(st->stack,C_INT,gc->payTime); break; - case 8: push_val(st->stack,C_INT,gc->createTime); break; - case 9: push_val(st->stack,C_INT,gc->visibleC); break; - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: - case 17: - push_val(st->stack,C_INT,gc->guardian[index-10].visible); break; - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - case 24: - case 25: - push_val(st->stack,C_INT,gc->guardian[index-18].hp); break; - default: - push_val(st->stack,C_INT,0); break; - } - return 0; - } - } - } - push_val(st->stack,C_INT,0); - return 0; -} - -int buildin_setcastledata(struct script_state *st) -{ - char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - int index=conv_num(st,& (st->stack->stack_data[st->start+3])); - int value=conv_num(st,& (st->stack->stack_data[st->start+4])); - struct guild_castle *gc; - int i; - - for(i=0;imap_name)==0){ - // Save Data byself First - 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: return 0; - } - guild_castledatasave(gc->castle_id,index,value); - return 0; - } - } - } - return 0; -} - -/* ===================================================================== - * ギルド情報を要求する - * --------------------------------------------------------------------- - */ -int buildin_requestguildinfo(struct script_state *st) -{ - int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - char *event=NULL; - - if( st->end>st->start+3 ){ - event=conv_str(st,& (st->stack->stack_data[st->start+3])); - check_event(st, event); - } - - if(guild_id>0) - guild_npc_request_info(guild_id,event); - return 0; -} - -/* ===================================================================== - * カードの数を得る - * --------------------------------------------------------------------- - */ -int buildin_getequipcardcnt(struct script_state *st) -{ - int i,num; - struct map_session_data *sd; - int c=MAX_SLOTS; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(sd->status.inventory[i].card[0] == 0x00ff){ // 製造武器はカードなし - push_val(st->stack,C_INT,0); - return 0; - } - do{ - if( (sd->status.inventory[i].card[c-1] > 4000 && - sd->status.inventory[i].card[c-1] < 5000) || - itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest] - push_val(st->stack,C_INT,(c)); - return 0; - } - }while(c--); - push_val(st->stack,C_INT,0); - return 0; -} - -/* ================================================================ - * カード取り外し成功 - * ---------------------------------------------------------------- - */ -int buildin_successremovecards(struct script_state *st) -{ - int i,j,num,cardflag=0,flag; - struct map_session_data *sd; - struct item item_tmp; - int c=MAX_SLOTS; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない - return 0; - } - do{ - if( (sd->status.inventory[i].card[c-1] > 4000 && - sd->status.inventory[i].card[c-1] < 5000) || - itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest] - - cardflag = 1; - item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1]; - item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0; - item_tmp.attribute=0; - for (j = 0; j < MAX_SLOTS; j++) - item_tmp.card[j]=0; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL); - } - //Logs - - if((flag=pc_additem(sd,&item_tmp,1))){ // 持てないならドロップ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - } - }while(c--); - - if(cardflag == 1){ // カードを取り除いたアイテム所得 - flag=0; - item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid; - item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine; - item_tmp.attribute=sd->status.inventory[i].attribute; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); - } - //Logs - - for (j = 0; j < MAX_SLOTS; j++) - item_tmp.card[j]=0; - pc_delitem(sd,i,1,0); - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp); - } - //Logs - - if((flag=pc_additem(sd,&item_tmp,1))){ // もてないならドロップ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - clif_misceffect(&sd->bl,3); - return 0; - } - return 0; -} - -/* ================================================================ - * カード取り外し失敗 slot,type - * type=0: 両方損失、1:カード損失、2:武具損失、3:損失無し - * ---------------------------------------------------------------- - */ -int buildin_failedremovecards(struct script_state *st) -{ - int i,j,num,cardflag=0,flag,typefail; - struct map_session_data *sd; - struct item item_tmp; - int c=MAX_SLOTS; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - typefail=conv_num(st,& (st->stack->stack_data[st->start+3])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない - return 0; - } - do{ - if( (sd->status.inventory[i].card[c-1] > 4000 && - sd->status.inventory[i].card[c-1] < 5000) || - itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest] - - cardflag = 1; - - if(typefail == 2){ // 武具のみ損失なら、カードは受け取らせる - item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1]; - item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0; - item_tmp.attribute=0; - for (j = 0; j < MAX_SLOTS; j++) - item_tmp.card[j]=0; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL); - } - //Logs - - if((flag=pc_additem(sd,&item_tmp,1))){ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - } - } - }while(c--); - - if(cardflag == 1){ - - if(typefail == 0 || typefail == 2){ // 武具損失 - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd,i,1,0); - clif_misceffect(&sd->bl,2); - return 0; - } - if(typefail == 1){ // カードのみ損失(武具を返す) - flag=0; - item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid; - item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine; - item_tmp.attribute=sd->status.inventory[i].attribute; - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); - } - //Logs - - for (j = 0; j < MAX_SLOTS; j++) - item_tmp.card[j]=0; - pc_delitem(sd,i,1,0); - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp); - } - //Logs - - if((flag=pc_additem(sd,&item_tmp,1))){ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - } - clif_misceffect(&sd->bl,2); - return 0; - } - return 0; -} - -int buildin_mapwarp(struct script_state *st) // Added by RoVeRT -{ - int x,y,m; - char *str; - char *mapname; - unsigned int index; - mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - str=conv_str(st,& (st->stack->stack_data[st->start+3])); - x=conv_num(st,& (st->stack->stack_data[st->start+4])); - y=conv_num(st,& (st->stack->stack_data[st->start+5])); - - if( (m=map_mapname2mapid(mapname))< 0) - return 0; - - if(!(index=mapindex_name2id(str))) - return 0; - map_foreachinmap(buildin_areawarp_sub, - m,BL_PC,index,x,y); - return 0; -} - -int buildin_cmdothernpc(struct script_state *st) // Added by RoVeRT -{ - char *npc,*command; - - npc=conv_str(st,& (st->stack->stack_data[st->start+2])); - command=conv_str(st,& (st->stack->stack_data[st->start+3])); - - npc_command(map_id2sd(st->rid),npc,command); - return 0; -} - -int buildin_inittimer(struct script_state *st) // Added by RoVeRT -{ -// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid); -// nd->lastaction=nd->timer=gettick(); - - npc_do_ontimer(st->oid, 1); - - return 0; -} - -int buildin_stoptimer(struct script_state *st) // Added by RoVeRT -{ -// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid); -// nd->lastaction=nd->timer=-1; - - npc_do_ontimer(st->oid, 0); - - return 0; -} - -int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT -{ - char *event=va_arg(ap,char *); - int *c=va_arg(ap,int *); - - if(strcmp(event,((struct mob_data *)bl)->npc_event)==0) - (*c)++; - return 0; -} - -int buildin_mobcount(struct script_state *st) // Added by RoVeRT -{ - char *mapname,*event; - int m,c=0; - mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); - event=conv_str(st,& (st->stack->stack_data[st->start+3])); - check_event(st, event); - - if( (m=map_mapname2mapid(mapname))<0 ) { - push_val(st->stack,C_INT,-1); - return 0; - } - map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event,&c ); - - push_val(st->stack,C_INT, (c)); - - return 0; -} -int buildin_marriage(struct script_state *st) -{ - char *partner=conv_str(st,& (st->stack->stack_data[st->start+2])); - struct map_session_data *sd=script_rid2sd(st); - struct map_session_data *p_sd=map_nick2sd(partner); - - if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){ - push_val(st->stack,C_INT,0); - return 0; - } - push_val(st->stack,C_INT,1); - return 0; -} -int buildin_wedding_effect(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - struct block_list *bl; - - if(sd==NULL) { - bl=map_id2bl(st->oid); - } else - bl=&sd->bl; - clif_wedding_effect(bl); - return 0; -} -int buildin_divorce(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - if(sd==NULL || pc_divorce(sd) < 0){ - push_val(st->stack,C_INT,0); - return 0; - } - push_val(st->stack,C_INT,1); - return 0; -} - -int buildin_ispartneron(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - struct map_session_data *p_sd=NULL; - - if(sd==NULL || !pc_ismarried(sd) || - (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) { - push_val(st->stack,C_INT,0); - return 0; - } - - push_val(st->stack,C_INT,1); - return 0; -} - -int buildin_getpartnerid(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - if (sd == NULL) { - push_val(st->stack,C_INT,0); - return 0; - } - - push_val(st->stack,C_INT,sd->status.partner_id); - return 0; -} - -int buildin_getchildid(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - if (sd == NULL) { - push_val(st->stack,C_INT,0); - return 0; - } - - push_val(st->stack,C_INT,sd->status.child); - return 0; -} - -int buildin_getmotherid(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - if (sd == NULL) { - push_val(st->stack,C_INT,0); - return 0; - } - - push_val(st->stack,C_INT,sd->status.mother); - return 0; -} - -int buildin_getfatherid(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - if (sd == NULL) { - push_val(st->stack,C_INT,0); - return 0; - } - - push_val(st->stack,C_INT,sd->status.father); - return 0; -} - -int buildin_warppartner(struct script_state *st) -{ - int x,y; - unsigned short mapindex; - char *str; - struct map_session_data *sd=script_rid2sd(st); - struct map_session_data *p_sd=NULL; - - if(sd==NULL || !pc_ismarried(sd) || - (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) { - push_val(st->stack,C_INT,0); - return 0; - } - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - x=conv_num(st,& (st->stack->stack_data[st->start+3])); - y=conv_num(st,& (st->stack->stack_data[st->start+4])); - - mapindex = mapindex_name2id(str); - if (mapindex) { - pc_setpos(p_sd,mapindex,x,y,0); - push_val(st->stack,C_INT,1); - } else - push_val(st->stack,C_INT,0); - return 0; -} - -/*================================================ - * Script for Displaying MOB Information [Valaris] - *------------------------------------------------ - */ -int buildin_strmobinfo(struct script_state *st) -{ - - int num=conv_num(st,& (st->stack->stack_data[st->start+2])); - int class_=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if((class_>=0 && class_<=1000) || class_ >2000) - return 0; - - switch (num) { - case 1: - { - push_str(st->stack,C_CONSTSTR,(unsigned char *) mob_db(class_)->name); - break; - } - case 2: - { - push_str(st->stack,C_CONSTSTR,(unsigned char *) mob_db(class_)->jname); - break; - } - case 3: - push_val(st->stack,C_INT,mob_db(class_)->lv); - break; - case 4: - push_val(st->stack,C_INT,mob_db(class_)->max_hp); - break; - case 5: - push_val(st->stack,C_INT,mob_db(class_)->max_sp); - break; - case 6: - push_val(st->stack,C_INT,mob_db(class_)->base_exp); - break; - case 7: - push_val(st->stack,C_INT,mob_db(class_)->job_exp); - break; - } - return 0; -} - -/*========================================== - * Summon guardians [Valaris] - *------------------------------------------ - */ -int buildin_guardian(struct script_state *st) -{ - int class_=0,amount=1,x=0,y=0,guardian=0; - char *str,*map,*event=""; - - map =conv_str(st,& (st->stack->stack_data[st->start+2])); - x =conv_num(st,& (st->stack->stack_data[st->start+3])); - y =conv_num(st,& (st->stack->stack_data[st->start+4])); - str =conv_str(st,& (st->stack->stack_data[st->start+5])); - class_=conv_num(st,& (st->stack->stack_data[st->start+6])); - amount=conv_num(st,& (st->stack->stack_data[st->start+7])); - event=conv_str(st,& (st->stack->stack_data[st->start+8])); - if( st->end>st->start+9 ) - guardian=conv_num(st,& (st->stack->stack_data[st->start+9])); - - check_event(st, event); - - mob_spawn_guardian(map_id2sd(st->rid),map,x,y,str,class_,amount,event,guardian); - - return 0; -} - -/*================================================ - * Script for Displaying Guardian Info [Valaris] - *------------------------------------------------ - */ -int buildin_guardianinfo(struct script_state *st) -{ - int guardian=conv_num(st,& (st->stack->stack_data[st->start+2])); - struct map_session_data *sd=script_rid2sd(st); - struct guild_castle *gc=guild_mapname2gc(map[sd->bl.m].name); - - if (guardian < 0 || guardian >= MAX_GUARDIANS || gc==NULL) - { - push_val(st->stack,C_INT,-1); - return 0; - } - - if(gc->guardian[guardian].visible) - push_val(st->stack,C_INT,gc->guardian[guardian].hp); - else push_val(st->stack,C_INT,-1); - - return 0; -} -/*========================================== - * IDからItem名 - *------------------------------------------ - */ -int buildin_getitemname(struct script_state *st) -{ - int item_id=0; - struct item_data *i_data; - char *item_name; - struct script_data *data; - - data=&(st->stack->stack_data[st->start+2]); - get_val(st,data); - - if( data->type==C_STR || data->type==C_CONSTSTR ){ - const char *name=conv_str(st,data); - struct item_data *item_data = itemdb_searchname(name); - if( item_data ) - item_id=item_data->nameid; - }else - item_id=conv_num(st,data); - - i_data = itemdb_exists(item_id); - if (i_data == NULL) - { - push_str(st->stack,C_CONSTSTR,(unsigned char *) "null"); - return 0; - } - item_name=(char *)aMallocA(ITEM_NAME_LENGTH*sizeof(char)); - - memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH); - push_str(st->stack,C_STR,(unsigned char *) item_name); - return 0; -} -/*========================================== - * Returns number of slots an item has. [Skotlex] - *------------------------------------------ - */ -int buildin_getitemslots(struct script_state *st) -{ - int item_id; - struct item_data *i_data; - - item_id=conv_num(st,& (st->stack->stack_data[st->start+2])); - - i_data = itemdb_exists(item_id); - - if (i_data) - push_val(st->stack,C_INT,i_data->slot); - else - push_val(st->stack,C_INT,-1); - return 0; -} - -/*========================================== - * Returns some values of an item [Lupus] - * Price, Weight, etc... - getiteminfo(itemID,n), where n - 0 value_buy; - 1 value_sell; - 2 type; - 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. - if = 0, then monsters don't drop it at all (rare or a quest item) - if = 10000, then this item is sold in NPC shops only - 4 sex; - 5 equip; - 6 weight; - 7 atk; - 8 def; - 9 range; - 10 slot; - 11 look; - 12 elv; - 13 wlv; - *------------------------------------------ - */ -int buildin_getiteminfo(struct script_state *st) -{ - int item_id,n; - int *item_arr; - struct item_data *i_data; - - item_id = conv_num(st,& (st->stack->stack_data[st->start+2])); - n = conv_num(st,& (st->stack->stack_data[st->start+3])); - i_data = itemdb_exists(item_id); - - if (i_data && n>=0 && n<14) { - item_arr = (int*)&i_data->value_buy; - push_val(st->stack,C_INT,item_arr[n]); - } else - push_val(st->stack,C_INT,-1); - return 0; -} - -/*========================================== - * Returns value from equipped item slot n [Lupus] - getequipcardid(num,slot) - where - num = eqip position slot - slot = 0,1,2,3 (Card Slot N) - - This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced) - it's useful when you want to check item cards or if it's signed - Useful for such quests as "Sign this refined item with players name" etc - Hat[0] +4 -> Player's Hat[0] +4 - *------------------------------------------ - */ -int buildin_getequipcardid(struct script_state *st) -{ - int i,num,slot; - struct map_session_data *sd; - - num=conv_num(st,& (st->stack->stack_data[st->start+2])); - slot=conv_num(st,& (st->stack->stack_data[st->start+3])); - sd=script_rid2sd(st); - i=pc_checkequip(sd,equip[num-1]); - if(i >= 0 && slot>=0 && slot<4) - push_val(st->stack,C_INT,sd->status.inventory[i].card[slot]); - else - push_val(st->stack,C_INT,0); - - return 0; -} - -/*========================================== - * petskillbonus [Valaris] //Rewritten by [Skotlex] - *------------------------------------------ - */ - -int buildin_petskillbonus(struct script_state *st) -{ - struct pet_data *pd; - - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->bonus) - { //Clear previous bonus - if (pd->bonus->timer != -1) - delete_timer(pd->bonus->timer, pet_skill_bonus_timer); - } else //init - pd->bonus = (struct pet_bonus *) aMalloc(sizeof(struct pet_bonus)); - - pd->bonus->type=conv_num(st,& (st->stack->stack_data[st->start+2])); - pd->bonus->val=conv_num(st,& (st->stack->stack_data[st->start+3])); - pd->bonus->duration=conv_num(st,& (st->stack->stack_data[st->start+4])); - pd->bonus->delay=conv_num(st,& (st->stack->stack_data[st->start+5])); - - if (pd->state.skillbonus == -1) - pd->state.skillbonus=0; // waiting state - - // wait for timer to start - if (battle_config.pet_equip_required && pd->equip == 0) - pd->bonus->timer=-1; - else - pd->bonus->timer=add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); - - return 0; -} - -/*========================================== - * pet looting [Valaris] //Rewritten by [Skotlex] - *------------------------------------------ - */ -int buildin_petloot(struct script_state *st) -{ - int max; - struct pet_data *pd; - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - max=conv_num(st,& (st->stack->stack_data[st->start+2])); - - if(max < 1) - max = 1; //Let'em loot at least 1 item. - else if (max > MAX_PETLOOT_SIZE) - max = MAX_PETLOOT_SIZE; - - pd = sd->pd; - if (pd->loot != NULL) - { //Release whatever was there already and reallocate memory - pet_lootitem_drop(pd, pd->msd); - aFree(pd->loot->item); - } - else - pd->loot = (struct pet_loot *)aMalloc(sizeof(struct pet_loot)); - - pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item)); - - pd->loot->max=max; - pd->loot->count = 0; - pd->loot->weight = 0; - - return 0; -} -/*========================================== - * PCの所持品情報読み取り - *------------------------------------------ - */ -int buildin_getinventorylist(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - unsigned char card_var[NAME_LENGTH]; - - int i,j=0,k; - if(!sd) return 0; - for(i=0;istatus.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){ - pc_setreg(sd,add_str((unsigned char *) "@inventorylist_id")+(j<<24),sd->status.inventory[i].nameid); - pc_setreg(sd,add_str((unsigned char *) "@inventorylist_amount")+(j<<24),sd->status.inventory[i].amount); - pc_setreg(sd,add_str((unsigned char *) "@inventorylist_equip")+(j<<24),sd->status.inventory[i].equip); - pc_setreg(sd,add_str((unsigned char *) "@inventorylist_refine")+(j<<24),sd->status.inventory[i].refine); - pc_setreg(sd,add_str((unsigned char *) "@inventorylist_identify")+(j<<24),sd->status.inventory[i].identify); - pc_setreg(sd,add_str((unsigned char *) "@inventorylist_attribute")+(j<<24),sd->status.inventory[i].attribute); - for (k = 0; k < MAX_SLOTS; k++) - { - sprintf(card_var, "@inventorylist_card%d",k+1); - pc_setreg(sd,add_str(card_var)+(j<<24),sd->status.inventory[i].card[k]); - } - j++; - } - } - pc_setreg(sd,add_str((unsigned char *) "@inventorylist_count"),j); - return 0; -} - -int buildin_getskilllist(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - int i,j=0; - if(!sd) return 0; - for(i=0;istatus.skill[i].id > 0 && sd->status.skill[i].lv > 0){ - pc_setreg(sd,add_str((unsigned char *) "@skilllist_id")+(j<<24),sd->status.skill[i].id); - pc_setreg(sd,add_str((unsigned char *)"@skilllist_lv")+(j<<24),sd->status.skill[i].lv); - pc_setreg(sd,add_str((unsigned char *)"@skilllist_flag")+(j<<24),sd->status.skill[i].flag); - j++; - } - } - pc_setreg(sd,add_str((unsigned char *) "@skilllist_count"),j); - return 0; -} - -int buildin_clearitem(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - int i; - if(sd==NULL) return 0; - for (i=0; istatus.inventory[i].amount) { - - //Logs items, got from (N)PC scripts [Lupus] - if(log_config.pick > 0 ) { - log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); - } - //Logs - - pc_delitem(sd, i, sd->status.inventory[i].amount, 0); - } - } - return 0; -} - -/*========================================== - Disguise Player (returns Mob/NPC ID if success, 0 on fail) [Lupus] - *------------------------------------------ - */ -int buildin_disguise(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - int id; - - id = conv_num(st,& (st->stack->stack_data[st->start+2])); - - if (!mobdb_checkid(id) && !npcdb_checkid(id)) { - push_val(st->stack,C_INT,0); - return 0; - } - - pc_disguise(sd, id); - push_val(st->stack,C_INT,id); - return 0; -} - -/*========================================== - Undisguise Player (returns 1 if success, 0 on fail) [Lupus] - *------------------------------------------ - */ -int buildin_undisguise(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - - if (sd->disguise) { - pc_disguise(sd, 0); - push_val(st->stack,C_INT,0); - } else { - push_val(st->stack,C_INT,1); - } - return 0; -} - -/*========================================== - * NPCクラスチェンジ - * classは変わりたいclass - * typeは通常0なのかな? - *------------------------------------------ - */ -int buildin_classchange(struct script_state *st) -{ - int _class,type; - struct block_list *bl=map_id2bl(st->oid); - - if(bl==NULL) return 0; - - _class=conv_num(st,& (st->stack->stack_data[st->start+2])); - type=conv_num(st,& (st->stack->stack_data[st->start+3])); - clif_class_change(bl,_class,type); - return 0; -} - -/*========================================== - * NPCから発生するエフェクト - *------------------------------------------ - */ -int buildin_misceffect(struct script_state *st) -{ - int type; - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - if(st->oid) { - struct block_list *bl = map_id2bl(st->oid); - if (bl) - clif_misceffect2(bl,type); - } else{ - struct map_session_data *sd=script_rid2sd(st); - if(sd) - clif_misceffect2(&sd->bl,type); - } - return 0; -} -/*========================================== - * サウンドエフェクト - *------------------------------------------ - */ -int buildin_soundeffect(struct script_state *st) -{ - - // Redundn - struct map_session_data *sd=script_rid2sd(st); - char *name; - int type=0; - - - name=conv_str(st,& (st->stack->stack_data[st->start+2])); - type=conv_num(st,& (st->stack->stack_data[st->start+3])); - if(sd){ - if(!st->rid) - clif_soundeffect(sd,map_id2bl(st->oid),name,type); - else{ - clif_soundeffect(sd,&sd->bl,name,type); - } - } - return 0; -} - -int soundeffect_sub(struct block_list* bl,va_list ap) -{ - char *name; - int type; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - - name = va_arg(ap,char *); - type = va_arg(ap,int); - - clif_soundeffect((struct map_session_data *)bl, bl, name, type); - - return 0; -} - -int buildin_soundeffectall(struct script_state *st) -{ - // [Lance] - Improved. - char *name, *map = NULL; - struct block_list *bl; - int type, coverage, x0, y0, x1, y1; - - name=conv_str(st,& (st->stack->stack_data[st->start+2])); - type=conv_num(st,& (st->stack->stack_data[st->start+3])); - coverage=conv_num(st,& (st->stack->stack_data[st->start+4])); - - if(!st->rid) - bl = map_id2bl(st->oid); - else - bl = &(script_rid2sd(st)->bl); - - if(bl){ - if(coverage < 23){ - clif_soundeffectall(bl,name,type,coverage); - }else { - if(st->end > st->start+9){ - map=conv_str(st,& (st->stack->stack_data[st->start+5])); - x0 = conv_num(st,& (st->stack->stack_data[st->start+6])); - y0 = conv_num(st,& (st->stack->stack_data[st->start+7])); - x1 = conv_num(st,& (st->stack->stack_data[st->start+8])); - y1 = conv_num(st,& (st->stack->stack_data[st->start+9])); - map_foreachinarea(soundeffect_sub,map_mapname2mapid(map),x0,y0,x1,y1,BL_PC,name,type); - } else { - ShowError("buildin_soundeffectall: insufficient arguments for specific area broadcast.\n"); - } - } - } - return 0; -} -/*========================================== - * pet status recovery [Valaris] / Rewritten by [Skotlex] - *------------------------------------------ - */ -int buildin_petrecovery(struct script_state *st) -{ - struct pet_data *pd; - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - - if (pd->recovery) - { //Halt previous bonus - if (pd->recovery->timer != -1) - delete_timer(pd->recovery->timer, pet_recovery_timer); - } else //Init - pd->recovery = (struct pet_recovery *)aMalloc(sizeof(struct pet_recovery)); - - pd->recovery->type=conv_num(st,& (st->stack->stack_data[st->start+2])); - pd->recovery->delay=conv_num(st,& (st->stack->stack_data[st->start+3])); - - pd->recovery->timer=-1; - - return 0; -} - -/*========================================== - * pet healing [Valaris] //Rewritten by [Skotlex] - *------------------------------------------ - */ -int buildin_petheal(struct script_state *st) -{ - struct pet_data *pd; - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->s_skill) - { //Clear previous skill - if (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); - } - } else //init memory - pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support)); - - pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport - //Use the lv as the amount to heal - pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+2])); - pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+3])); - pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+4])); - pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+5])); - - //Use delay as initial offset to avoid skill/heal exploits - if (battle_config.pet_equip_required && pd->equip == 0) - pd->s_skill->timer=-1; - else - pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0); - - return 0; -} - -/*========================================== - * pet attack skills [Valaris] //Rewritten by [Skotlex] - *------------------------------------------ - */ -int buildin_petskillattack(struct script_state *st) -{ - struct pet_data *pd; - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->a_skill == NULL) - pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack)); - - pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2])); - pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3])); - pd->a_skill->div_ = 0; - pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+4])); - pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+5])); - - return 0; -} - -/*========================================== - * pet attack skills [Valaris] - *------------------------------------------ - */ -int buildin_petskillattack2(struct script_state *st) -{ - struct pet_data *pd; - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->a_skill == NULL) - pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack)); - - pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2])); - pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3])); - pd->a_skill->div_ = conv_num(st,& (st->stack->stack_data[st->start+4])); - pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+5])); - pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+6])); - - return 0; -} - -/*========================================== - * pet support skills [Skotlex] - *------------------------------------------ - */ -int buildin_petskillsupport(struct script_state *st) -{ - struct pet_data *pd; - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL || sd->pd==NULL) - return 0; - - pd=sd->pd; - if (pd->s_skill) - { //Clear previous skill - if (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); - } - } else //init memory - pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support)); - - pd->s_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2])); - pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3])); - pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+4])); - pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+5])); - pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+6])); - - //Use delay as initial offset to avoid skill/heal exploits - if (battle_config.pet_equip_required && pd->equip == 0) - pd->s_skill->timer=-1; - else - pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0); - - return 0; -} - -/*========================================== - * Scripted skill effects [Celest] - *------------------------------------------ - */ -int buildin_skilleffect(struct script_state *st) -{ - struct map_session_data *sd; - - int skillid=conv_num(st,& (st->stack->stack_data[st->start+2])); - int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3])); - sd=script_rid2sd(st); - - clif_skill_nodamage(&sd->bl,&sd->bl,skillid,skilllv,1); - - return 0; -} - -/*========================================== - * NPC skill effects [Valaris] - *------------------------------------------ - */ -int buildin_npcskilleffect(struct script_state *st) -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - - int skillid=conv_num(st,& (st->stack->stack_data[st->start+2])); - int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3])); - int x=conv_num(st,& (st->stack->stack_data[st->start+4])); - int y=conv_num(st,& (st->stack->stack_data[st->start+5])); - - clif_skill_poseffect(&nd->bl,skillid,skilllv,x,y,gettick()); - - return 0; -} - -/*========================================== - * Special effects [Valaris] - *------------------------------------------ - */ -int buildin_specialeffect(struct script_state *st) -{ - struct block_list *bl=map_id2bl(st->oid); - - if(bl==NULL) - return 0; - - clif_specialeffect(bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0); - - return 0; -} - -int buildin_specialeffect2(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - - if(sd==NULL) - return 0; - - clif_specialeffect(&sd->bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0); - - return 0; -} - -/*========================================== - * Nude [Valaris] - *------------------------------------------ - */ - -int buildin_nude(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - int i,calcflag=0; - - if(sd==NULL) - return 0; - - for(i=0;i<11;i++) - if(sd->equip_index[i] >= 0) { - if(!calcflag) - calcflag=1; - pc_unequipitem(sd,sd->equip_index[i],2); - } - - if(calcflag) - status_calc_pc(sd,1); - - return 0; -} - -/*========================================== - * gmcommand [MouseJstr] - * - * suggested on the forums... - * splitted into atcommand & charcommand by [Skotlex] - *------------------------------------------ - */ - -int buildin_atcommand(struct script_state *st) -{ - struct map_session_data *sd=NULL; - char *cmd; - - cmd = conv_str(st,& (st->stack->stack_data[st->start+2])); - if (st->rid) - sd = script_rid2sd(st); - - if (sd) is_atcommand(sd->fd, sd, cmd, 99); - else { //Use a dummy character. - struct map_session_data dummy_sd; - struct block_list *bl = NULL; - memset(&dummy_sd, 0, sizeof(struct map_session_data)); - if (st->oid) bl = map_id2bl(st->oid); - if (bl) { - memcpy(&dummy_sd.bl, bl, sizeof(struct block_list)); - if (bl->type == BL_NPC) - strncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH); - } - is_atcommand(0, &dummy_sd, cmd, 99); - } - - return 0; -} - -int buildin_charcommand(struct script_state *st) -{ - struct map_session_data *sd=NULL; - char *cmd; - - cmd = conv_str(st,& (st->stack->stack_data[st->start+2])); - - if (st->rid) - sd = script_rid2sd(st); - - if (sd) is_charcommand(sd->fd, sd, cmd, 99); - else { //Use a dummy character. - struct map_session_data dummy_sd; - struct block_list *bl = NULL; - memset(&dummy_sd, 0, sizeof(struct map_session_data)); - if (st->oid) bl = map_id2bl(st->oid); - if (bl) { - memcpy(&dummy_sd.bl, bl, sizeof(struct block_list)); - if (bl->type == BL_NPC) - strncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH); - } - is_charcommand(0, &dummy_sd, cmd, 99); - } - - return 0; -} - - -/*========================================== - * Displays a message for the player only (like system messages like "you got an apple" ) - *------------------------------------------ - */ -int buildin_dispbottom(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - char *message; - message=conv_str(st,& (st->stack->stack_data[st->start+2])); - if(sd) - clif_disp_onlyself(sd,message,(int)strlen(message)); - return 0; -} - -/*========================================== - * All The Players Full Recovery - (HP/SP full restore and resurrect if need) - *------------------------------------------ - */ -int buildin_recovery(struct script_state *st) -{ - struct map_session_data *sd, **all_sd; - int i = 0, users; - - all_sd = map_getallusers(&users); - - for (i = 0; i < users; i++) - { - sd = all_sd[i]; - sd->status.hp = sd->status.max_hp; - sd->status.sp = sd->status.max_sp; - clif_updatestatus(sd, SP_HP); - clif_updatestatus(sd, SP_SP); - if(pc_isdead(sd)){ - pc_setstand(sd); - clif_resurrection(&sd->bl, 1); - } - clif_displaymessage(sd->fd,"You have been recovered!"); - } - return 0; -} -/*========================================== - * Get your pet info: getpetinfo(n) - * n -> 0:pet_id 1:pet_class 2:pet_name - 3:friendly 4:hungry - *------------------------------------------ - */ -int buildin_getpetinfo(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - int type=conv_num(st,& (st->stack->stack_data[st->start+2])); - - if(sd && sd->status.pet_id){ - switch(type){ - case 0: - push_val(st->stack,C_INT,sd->status.pet_id); - break; - case 1: - push_val(st->stack,C_INT,sd->pet.class_); - break; - case 2: - if(sd->pet.name) - { - push_str(st->stack,C_CONSTSTR,(unsigned char *) sd->pet.name); - } - else - push_str(st->stack,C_CONSTSTR, (unsigned char *) "null"); - break; - case 3: - //if(sd->pet.intimate) - push_val(st->stack,C_INT,sd->pet.intimate); - break; - case 4: - //if(sd->pet.hungry) - push_val(st->stack,C_INT,sd->pet.hungry); - break; - default: - push_val(st->stack,C_INT,0); - break; - } - }else{ - push_val(st->stack,C_INT,0); - } - return 0; -} -/*========================================== - * Shows wether your inventory(and equips) contain - selected card or not. - checkequipedcard(4001); - *------------------------------------------ - */ -int buildin_checkequipedcard(struct script_state *st) -{ - struct map_session_data *sd=script_rid2sd(st); - int n,i,c=0; - c=conv_num(st,& (st->stack->stack_data[st->start+2])); - - if(sd){ - for(i=0;istatus.inventory[i].nameid > 0 && sd->status.inventory[i].amount){ - for(n=0;nstatus.inventory[i].card[n]==c){ - push_val(st->stack,C_INT,1); - return 0; - } - } - } - } - } - push_val(st->stack,C_INT,0); - return 0; -} - -int buildin_jump_zero(struct script_state *st) { - int sel; - sel=conv_num(st,& (st->stack->stack_data[st->start+2])); - if(!sel) { - int pos; - if( st->stack->stack_data[st->start+3].type!=C_POS ){ - ShowError("script: jump_zero: not label !\n"); - st->state=END; - return 0; - } - - pos=conv_num(st,& (st->stack->stack_data[st->start+3])); - st->pos=pos; - st->state=GOTO; - // printf("script: jump_zero: jumpto : %d\n",pos); - } else { - // printf("script: jump_zero: fail\n"); - } - return 0; -} - -int buildin_select(struct script_state *st) -{ - char *buf; - int len,i; - struct map_session_data *sd; - - sd=script_rid2sd(st); - - if(sd->state.menu_or_input==0){ - st->state=RERUNLINE; - sd->state.menu_or_input=1; - for(i=st->start+2,len=16;iend;i++){ - conv_str(st,& (st->stack->stack_data[i])); - len+=(int)strlen(st->stack->stack_data[i].u.str)+1; - } - buf=(char *)aMalloc((len+1)*sizeof(char)); - buf[0]=0; - for(i=st->start+2,len=0;iend;i++){ - strcat(buf,st->stack->stack_data[i].u.str); - strcat(buf,":"); - } - clif_scriptmenu(script_rid2sd(st),st->oid,buf); - aFree(buf); - } else if(sd->npc_menu==0xff){ // cansel - sd->state.menu_or_input=0; - st->state=END; - } else { -// pc_setreg(sd,add_str((unsigned char *) "l15"),sd->npc_menu); - pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu); - sd->state.menu_or_input=0; - push_val(st->stack,C_INT,sd->npc_menu); - } - return 0; -} - -/*========================================== - * GetMapMobs - returns mob counts on a set map: - e.g. GetMapMobs("prontera.gat") - use "this" - for player's map - *------------------------------------------ - */ -int buildin_getmapmobs(struct script_state *st) -{ - char *str=NULL; - int m=-1,bx,by,i; - int count=0,c; - struct block_list *bl; - - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - - if(strcmp(str,"this")==0){ - struct map_session_data *sd=script_rid2sd(st); - if(sd) - m=sd->bl.m; - else{ - push_val(st->stack,C_INT,-1); - return 0; - } - }else - m=map_mapname2mapid(str); - - if(m < 0){ - push_val(st->stack,C_INT,-1); - return 0; - } - - for(by=0;by<=(map[m].ys-1)/BLOCK_SIZE;by++){ - for(bx=0;bx<=(map[m].xs-1)/BLOCK_SIZE;bx++){ - bl = map[m].block_mob[bx+by*map[m].bxs]; - c = map[m].block_mob_count[bx+by*map[m].bxs]; - for(i=0;inext){ - if(bl->x>=0 && bl->x<=map[m].xs-1 && bl->y>=0 && bl->y<=map[m].ys-1) - count++; - } - } - } - push_val(st->stack,C_INT,count); - return 0; -} - -/*========================================== - * movenpc [MouseJstr] - *------------------------------------------ - */ - -int buildin_movenpc(struct script_state *st) -{ - struct map_session_data *sd; - char *map,*npc; - int x,y; - - sd = script_rid2sd(st); - - map = conv_str(st,& (st->stack->stack_data[st->start+2])); - x = conv_num(st,& (st->stack->stack_data[st->start+3])); - y = conv_num(st,& (st->stack->stack_data[st->start+4])); - npc = conv_str(st,& (st->stack->stack_data[st->start+5])); - - return 0; -} - -/*========================================== - * message [MouseJstr] - *------------------------------------------ - */ - -int buildin_message(struct script_state *st) -{ - struct map_session_data *sd; - char *msg,*player; - struct map_session_data *pl_sd = NULL; - - sd = script_rid2sd(st); - - player = conv_str(st,& (st->stack->stack_data[st->start+2])); - msg = conv_str(st,& (st->stack->stack_data[st->start+3])); - - if((pl_sd=map_nick2sd((char *) player)) == NULL) - return 0; - clif_displaymessage(pl_sd->fd, msg); - - return 0; -} - -/*========================================== - * npctalk (sends message to surrounding - * area) [Valaris] - *------------------------------------------ - */ - -int buildin_npctalk(struct script_state *st) -{ - char *str; - char message[255]; - - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - str=conv_str(st,& (st->stack->stack_data[st->start+2])); - - if(nd) { - memcpy(message, nd->name, NAME_LENGTH); - strcat(message," : "); - strncat(message,str, 254); //Prevent overflow possibility. [Skotlex] - clif_message(&(nd->bl), message); - } - - return 0; -} - -/*========================================== - * hasitems (checks to see if player has any - * items on them, if so will return a 1) - * [Valaris] - *------------------------------------------ - */ - -int buildin_hasitems(struct script_state *st) -{ - int i; - struct map_session_data *sd; - - sd=script_rid2sd(st); - - for(i=0; istatus.inventory[i].amount && sd->status.inventory[i].nameid!=2364 && sd->status.inventory[i].nameid!=2365) { - push_val(st->stack,C_INT,1); - return 0; - } - } - - push_val(st->stack,C_INT,0); - - return 0; -} -// change npc walkspeed [Valaris] -int buildin_npcspeed(struct script_state *st) -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - int x=0; - - x=conv_num(st,& (st->stack->stack_data[st->start+2])); - - if(nd) { - nd->speed=x; - } - - return 0; -} -// make an npc walk to a position [Valaris] -int buildin_npcwalkto(struct script_state *st) -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - int x=0,y=0; - - x=conv_num(st,& (st->stack->stack_data[st->start+2])); - y=conv_num(st,& (st->stack->stack_data[st->start+3])); - - if(nd) { - unit_walktoxy(&nd->bl,x,y,0); - } - - return 0; -} -// stop an npc's movement [Valaris] -int buildin_npcstop(struct script_state *st) -{ - struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); - - if(nd) { - unit_stop_walking(&nd->bl,1); - } - - return 0; -} - - -/*========================================== - * getlook char info. getlook(arg) - *------------------------------------------ - */ -int buildin_getlook(struct script_state *st){ - int type,val; - struct map_session_data *sd; - sd=script_rid2sd(st); - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - val=-1; - switch(type){ - case LOOK_HAIR: //1 - val=sd->status.hair; - break; - case LOOK_WEAPON: //2 - val=sd->status.weapon; - break; - case LOOK_HEAD_BOTTOM: //3 - val=sd->status.head_bottom; - break; - case LOOK_HEAD_TOP: //4 - val=sd->status.head_top; - break; - case LOOK_HEAD_MID: //5 - val=sd->status.head_mid; - break; - case LOOK_HAIR_COLOR: //6 - val=sd->status.hair_color; - break; - case LOOK_CLOTHES_COLOR: //7 - val=sd->status.clothes_color; - break; - case LOOK_SHIELD: //8 - val=sd->status.shield; - break; - case LOOK_SHOES: //9 - break; - } - - push_val(st->stack,C_INT,val); - return 0; -} - -/*========================================== - * get char save point. argument: 0- map name, 1- x, 2- y - *------------------------------------------ -*/ -int buildin_getsavepoint(struct script_state *st) -{ - int x,y,type; - char *mapname; - struct map_session_data *sd; - - sd=script_rid2sd(st); - - type=conv_num(st,& (st->stack->stack_data[st->start+2])); - - x=sd->status.save_point.x; - y=sd->status.save_point.y; - switch(type){ - case 0: - mapname=(char *) aMallocA((MAP_NAME_LENGTH+1)*sizeof(char)); - memcpy(mapname, mapindex_id2name(sd->status.save_point.map), MAP_NAME_LENGTH); - mapname[MAP_NAME_LENGTH]='\0'; - push_str(st->stack,C_STR,(unsigned char *) mapname); - break; - case 1: - push_val(st->stack,C_INT,x); - break; - case 2: - push_val(st->stack,C_INT,y); - break; - } - return 0; -} - -/*========================================== - * Get position for char/npc/pet/mob objects. Added by Lorky - * - * int getMapXY(MapName$,MaxX,MapY,type,[CharName$]); - * where type: - * MapName$ - String variable for output map name - * MapX - Integer variable for output coord X - * MapY - Integer variable for output coord Y - * type - type of object - * 0 - Character coord - * 1 - NPC coord - * 2 - Pet coord - * 3 - Mob coord (not released) - * CharName$ - Name object. If miss or "this" the current object - * - * Return: - * 0 - success - * -1 - some error, MapName$,MapX,MapY contains unknown value. - *------------------------------------------ -*/ -int buildin_getmapxy(struct script_state *st){ - struct map_session_data *sd=NULL; - struct npc_data *nd; - struct pet_data *pd; - - int num; - char *name; - char prefix; - - int x,y,type; - char mapname[MAP_NAME_LENGTH+1]; - memset(mapname, 0, sizeof(mapname)); - - if( st->stack->stack_data[st->start+2].type!=C_NAME ){ - ShowWarning("script: buildin_getmapxy: not mapname variable\n"); - push_val(st->stack,C_INT,-1); - return 0; - } - if( st->stack->stack_data[st->start+3].type!=C_NAME ){ - ShowWarning("script: buildin_getmapxy: not mapx variable\n"); - push_val(st->stack,C_INT,-1); - return 0; - } - if( st->stack->stack_data[st->start+4].type!=C_NAME ){ - ShowWarning("script: buildin_getmapxy: not mapy variable\n"); - push_val(st->stack,C_INT,-1); - return 0; - } - -//??????????? >>> Possible needly check function parameters on C_STR,C_INT,C_INT <<< ???????????// - type=conv_num(st,& (st->stack->stack_data[st->start+5])); - - switch (type){ - case 0: //Get Character Position - if( st->end>st->start+6 ) - sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6]))); - else - sd=script_rid2sd(st); - - if ( sd==NULL ) { //wrong char name or char offline - push_val(st->stack,C_INT,-1); - return 0; - } - - - x=sd->bl.x; - y=sd->bl.y; - memcpy(mapname,mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH); - break; - case 1: //Get NPC Position - if( st->end > st->start+6 ) - nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+6]))); - else - nd=(struct npc_data *)map_id2bl(st->oid); - - if ( nd==NULL ) { //wrong npc name or char offline - push_val(st->stack,C_INT,-1); - return 0; - } - - x=nd->bl.x; - y=nd->bl.y; - memcpy(mapname, map[nd->bl.m].name, MAP_NAME_LENGTH); - break; - case 2: //Get Pet Position - if( st->end>st->start+6 ) - sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6]))); - else - sd=script_rid2sd(st); - - if ( sd==NULL ) { //wrong char name or char offline - push_val(st->stack,C_INT,-1); - return 0; - } - - pd=sd->pd; - - if(pd==NULL){ //pet data not found - push_val(st->stack,C_INT,-1); - return 0; - } - x=pd->bl.x; - y=pd->bl.y; - memcpy(mapname, map[pd->bl.m].name, MAP_NAME_LENGTH); - break; - - case 3: //Get Mob Position - push_val(st->stack,C_INT,-1); - return 0; - default: //Wrong type parameter - push_val(st->stack,C_INT,-1); - return 0; - } - - //Set MapName$ - num=st->stack->stack_data[st->start+2].u.num; - name=(char *)(str_buf+str_data[num&0x00ffffff].str); - prefix=*name; - - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - else - sd=NULL; - - set_reg(st,sd,num,name,(void*)mapname,NULL); - - //Set MapX - num=st->stack->stack_data[st->start+3].u.num; - name=(char *)(str_buf+str_data[num&0x00ffffff].str); - prefix=*name; - - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - else - sd=NULL; - set_reg(st,sd,num,name,(void*)x,NULL); - - - //Set MapY - num=st->stack->stack_data[st->start+4].u.num; - name=(char *)(str_buf+str_data[num&0x00ffffff].str); - prefix=*name; - - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - else - sd=NULL; - - set_reg(st,sd,num,name,(void*)y,NULL); - - //Return Success value - push_val(st->stack,C_INT,0); - return 0; -} - -/*===================================================== - * Allows players to use a skill - by Qamera - *----------------------------------------------------- - */ -int buildin_skilluseid (struct script_state *st) -{ - int skid,sklv; - struct map_session_data *sd; - - skid=conv_num(st,& (st->stack->stack_data[st->start+2])); - sklv=conv_num(st,& (st->stack->stack_data[st->start+3])); - sd=script_rid2sd(st); - if (sd) - unit_skilluse_id(&sd->bl,sd->bl.id,skid,sklv); - - return 0; -} - -/*===================================================== - * Allows players to use a skill on a position [Celest] - *----------------------------------------------------- - */ -int buildin_skillusepos(struct script_state *st) -{ - int skid,sklv,x,y; - struct map_session_data *sd; - - skid=conv_num(st,& (st->stack->stack_data[st->start+2])); - sklv=conv_num(st,& (st->stack->stack_data[st->start+3])); - x=conv_num(st,& (st->stack->stack_data[st->start+4])); - y=conv_num(st,& (st->stack->stack_data[st->start+5])); - - sd=script_rid2sd(st); - if (sd) - unit_skilluse_pos(&sd->bl,x,y,skid,sklv); - - return 0; -} - -/*========================================== - * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus] - *------------------------------------------ - */ -int buildin_logmes(struct script_state *st) -{ - if (log_config.npc <= 0 ) return 0; - conv_str(st,& (st->stack->stack_data[st->start+2])); - log_npc(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str); - return 0; -} - -int buildin_summon(struct script_state *st) -{ - int _class, id, timeout=0; - char *str,*event=""; - struct map_session_data *sd; - struct mob_data *md; - - sd=script_rid2sd(st); - if (sd) { - int tick = gettick(); - str =conv_str(st,& (st->stack->stack_data[st->start+2])); - _class=conv_num(st,& (st->stack->stack_data[st->start+3])); - if( st->end>st->start+4 ) - timeout=conv_num(st,& (st->stack->stack_data[st->start+4])); - if( st->end>st->start+5 ){ - event=conv_str(st,& (st->stack->stack_data[st->start+5])); - check_event(st, event); - } - - id=mob_once_spawn(sd, "this", 0, 0, str,_class,1,event); - if((md=(struct mob_data *)map_id2bl(id))){ - md->master_id=sd->bl.id; - md->special_state.ai=1; - md->mode=mob_db(md->class_)->mode|0x04; - md->deletetimer=add_timer(tick+(timeout>0?timeout*1000:60000),mob_timer_delete,id,0); - clif_misceffect2(&md->bl,344); - } - clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick); - } - - return 0; -} - -/*========================================== - * Checks whether it is daytime/nighttime - *------------------------------------------ - */ -int buildin_isnight(struct script_state *st) -{ - push_val(st->stack,C_INT, (night_flag == 1)); - return 0; -} - -int buildin_isday(struct script_state *st) -{ - push_val(st->stack,C_INT, (night_flag == 0)); - return 0; -} - -/*================================================ - * Check whether another item/card has been - * equipped - used for 2/15's cards patch [celest] - *------------------------------------------------ - */ -// leave this here, just in case -#if 0 -int buildin_isequipped(struct script_state *st) -{ - struct map_session_data *sd; - int i, j, k, id = 1; - int ret = -1; - - sd = script_rid2sd(st); - - for (i=0; id!=0; i++) { - int flag = 0; - - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - - for (j=0; j<10; j++) { - int index, type; - index = sd->equip_index[j]; - if(index < 0) continue; - if(j == 9 && sd->equip_index[8] == index) continue; - if(j == 5 && sd->equip_index[4] == index) continue; - if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue; - type = itemdb_type(id); - - if(sd->inventory_data[index]) { - if (type == 4 || type == 5) { - if (sd->inventory_data[index]->nameid == id) - flag = 1; - } else if (type == 6) { - for(k=0; kinventory_data[index]->slot; k++) { - if (sd->status.inventory[index].card[0]!=0x00ff && - sd->status.inventory[index].card[0]!=0x00fe && - sd->status.inventory[index].card[0]!=(short)0xff00 && - sd->status.inventory[index].card[k] == id) { - flag = 1; - break; - } - } - } - if (flag) break; - } - } - if (ret == -1) - ret = flag; - else - ret &= flag; - if (!ret) break; - } - - push_val(st->stack,C_INT,ret); - return 0; -} -#endif - -/*================================================ - * Check how many items/cards in the list are - * equipped - used for 2/15's cards patch [celest] - *------------------------------------------------ - */ -int buildin_isequippedcnt(struct script_state *st) -{ - struct map_session_data *sd; - int i, j, k, id = 1; - int ret = 0; - - sd = script_rid2sd(st); - - for (i=0; id!=0; i++) { - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - - for (j=0; j<10; j++) { - int index, type; - index = sd->equip_index[j]; - if(index < 0) continue; - if(j == 9 && sd->equip_index[8] == index) continue; - if(j == 5 && sd->equip_index[4] == index) continue; - if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue; - type = itemdb_type(id); - - if(sd->inventory_data[index]) { - if (type == 4 || type == 5) { - if (sd->inventory_data[index]->nameid == id) - ret++; //[Lupus] - } else if (type == 6) { - for(k=0; kinventory_data[index]->slot; k++) { - if (sd->status.inventory[index].card[0]!=0x00ff && - sd->status.inventory[index].card[0]!=0x00fe && - sd->status.inventory[index].card[0]!=(short)0xff00 && - sd->status.inventory[index].card[k] == id) { - ret++; //[Lupus] - } - } - } - } - } - } - - push_val(st->stack,C_INT,ret); - return 0; -} - -/*================================================ - * Check whether another card has been - * equipped - used for 2/15's cards patch [celest] - * -- Items checked cannot be reused in another - * card set to prevent exploits - *------------------------------------------------ - */ -int buildin_isequipped(struct script_state *st) -{ - struct map_session_data *sd; - int i, j, k, id = 1; - int index, type, flag; - int ret = -1; - - sd = script_rid2sd(st); - - if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing... - push_val(st->stack,C_INT,0); - return 0; - } - - for (i=0; id!=0; i++) - { - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - - type = itemdb_type(id); - flag = 0; - for (j=0; j<10; j++) - { - index = sd->equip_index[j]; - if(index < 0) continue; - if(j == 9 && sd->equip_index[8] == index) continue; - if(j == 5 && sd->equip_index[4] == index) continue; - if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue; - - if(!sd->inventory_data[index]) - continue; - - switch (type) - { - case 4: - case 5: - if (sd->inventory_data[index]->nameid == id) - flag = 1; - break; - case 6: - if ( - sd->inventory_data[index]->slot == 0 || - sd->status.inventory[index].card[0] == 0x00ff || - sd->status.inventory[index].card[0] == 0x00fe || - sd->status.inventory[index].card[0] == (short)0xff00) - continue; - - for (k = 0; k < sd->inventory_data[index]->slot; k++) - { //New hash system which should support up to 4 slots on any equipment. [Skotlex] - unsigned int hash = 0; - if (sd->status.inventory[index].card[k] != id) - continue; - - hash = 1<<((j<5?j:j-5)*4 + k); - // check if card is already used by another set - if ((j<5?sd->setitem_hash:sd->setitem_hash2) & hash) - continue; - - // We have found a match - flag = 1; - // Set hash so this card cannot be used by another - if (j<5) - sd->setitem_hash |= hash; - else - sd->setitem_hash2 |= hash; - break; - } - //Case 6 end - break; - } - if (flag) break; - } - if (ret == -1) - ret = flag; - else - ret &= flag; - if (!ret) break; - } - - push_val(st->stack,C_INT,ret); - return 0; -} - -/*================================================ - * Check how many given inserted cards in the CURRENT - * weapon - used for 2/15's cards patch [Lupus] - *------------------------------------------------ - */ -int buildin_cardscnt(struct script_state *st) -{ - struct map_session_data *sd; - int i, k, id = 1; - int ret = 0; - int index, type; - - sd = script_rid2sd(st); - - for (i=0; id!=0; i++) { - FETCH (i+2, id) else id = 0; - if (id <= 0) - continue; - - index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus] - if(index < 0) continue; - - type = itemdb_type(id); - - if(sd->inventory_data[index]) { - if (type == 4 || type == 5) { - if (sd->inventory_data[index]->nameid == id) - ret++; - } else if (type == 6) { - for(k=0; kinventory_data[index]->slot; k++) { - if (sd->status.inventory[index].card[0]!=0x00ff && - sd->status.inventory[index].card[0]!=0x00fe && - sd->status.inventory[index].card[0]!=(short)0xff00 && - sd->status.inventory[index].card[k] == id) { - ret++; - } - } - } - } - } - push_val(st->stack,C_INT,ret); -// push_val(st->stack,C_INT,current_equip_item_index); - return 0; -} - -/*======================================================= - * Returns the refined number of the current item, or an - * item with inventory index specified - *------------------------------------------------------- - */ -int buildin_getrefine(struct script_state *st) -{ - struct map_session_data *sd; - if ((sd = script_rid2sd(st))!= NULL) - push_val(st->stack, C_INT, sd->status.inventory[current_equip_item_index].refine); - return 0; -} - -/*======================================================= - * Allows 2 Parents to adopt a character as a Baby - *------------------------------------------------------- - */ -int buildin_adopt(struct script_state *st) -{ - int ret; - - char *parent1 = conv_str(st,& (st->stack->stack_data[st->start+2])); - char *parent2 = conv_str(st,& (st->stack->stack_data[st->start+3])); - char *child = conv_str(st,& (st->stack->stack_data[st->start+4])); - - struct map_session_data *p1_sd = map_nick2sd(parent1); - struct map_session_data *p2_sd = map_nick2sd(parent2); - struct map_session_data *c_sd = map_nick2sd(child); - - if (!p1_sd || !p2_sd || !c_sd || - p1_sd->status.base_level < 70 || - p2_sd->status.base_level < 70) - return 0; - - ret = pc_adoption(p1_sd, p2_sd, c_sd); - push_val(st->stack, C_INT, ret); - - return 0; -} - -/*======================================================= - * Day/Night controls - *------------------------------------------------------- - */ -int buildin_night(struct script_state *st) -{ - if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1); - return 0; -} -int buildin_day(struct script_state *st) -{ - if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1); - return 0; -} - -//======================================================= -// Unequip [Spectre] -//------------------------------------------------------- -int buildin_unequip(struct script_state *st) -{ - int i; - size_t num; - struct map_session_data *sd; - - num = conv_num(st,& (st->stack->stack_data[st->start+2])) - 1; - sd=script_rid2sd(st); - if(sd!=NULL && num<10) - { - i=pc_checkequip(sd,equip[num]); - pc_unequipitem(sd,i,2); - return 0; - } - return 0; -} - -int buildin_equip(struct script_state *st) -{ - int nameid=0,count=0,i; - struct map_session_data *sd; - struct item_data *item_data; - - sd = script_rid2sd(st); - - nameid=conv_num(st,& (st->stack->stack_data[st->start+2])); - if(nameid>=500 && (item_data = itemdb_search(nameid)) != NULL) - for(i=0;istatus.inventory[i].nameid==nameid) - count+=sd->status.inventory[i].amount; - } - else{ - if(battle_config.error_log) - ShowError("wrong item ID : equipitem(%i)\n",nameid); - return 1; - } - - if(count){ - pc_equipitem(sd,nameid,item_data->equip); - } - - return 0; -} - -int buildin_autoequip(struct script_state *st){ - int nameid, flag; - struct item_data *item_data; - nameid=conv_num(st,& (st->stack->stack_data[st->start+2])); - flag=conv_num(st,& (st->stack->stack_data[st->start+3])); - if(nameid>=500 && (item_data = itemdb_search(nameid)) != NULL){ - item_data->flag.autoequip = flag>0?1:0; - } - return 0; -} - -int buildin_setbattleflag(struct script_state *st){ - char *flag, *value; - - flag = conv_str(st,& (st->stack->stack_data[st->start+2])); - value = conv_str(st,& (st->stack->stack_data[st->start+3])); - - if (battle_set_value(flag, value) == 0) - ShowWarning("buildin_setbattleflag: unknown battle_config flag '%s'",flag); - else - ShowInfo("buildin_setbattleflag: battle_config flag '%s' is now set to '%s'.",flag,value); - - return 0; -} - -int buildin_getbattleflag(struct script_state *st){ - char *flag; - flag = conv_str(st,& (st->stack->stack_data[st->start+2])); - push_val(st->stack,C_INT,battle_get_value(flag)); - return 0; -} - -//======================================================= -// strlen [Valaris] -//------------------------------------------------------- -int buildin_getstrlen(struct script_state *st) { - - char *str = conv_str(st,& (st->stack->stack_data[st->start+2])); - int len = (str) ? (int)strlen(str) : 0; - - push_val(st->stack,C_INT,len); - return 0; -} - -//======================================================= -// isalpha [Valaris] -//------------------------------------------------------- -int buildin_charisalpha(struct script_state *st) { - - char *str=conv_str(st,& (st->stack->stack_data[st->start+2])); - int pos=conv_num(st,& (st->stack->stack_data[st->start+3])); - - int val = ( str && pos>0 && (unsigned int)posstack,C_INT,val); - return 0; -} - -// [Lance] -int buildin_fakenpcname(struct script_state *st) -{ - char *name; - char *newname; - int look; - name = conv_str(st,& (st->stack->stack_data[st->start+2])); - newname = conv_str(st,& (st->stack->stack_data[st->start+3])); - look = conv_num(st,& (st->stack->stack_data[st->start+4])); - if(look > 32767 || look < -32768) { - ShowError("buildin_fakenpcname: Invalid look value %d\n",look); - return 1; // Safety measure to prevent runtime errors - } - npc_changename(name,newname,(short)look); - return 0; -} - -int buildin_atoi(struct script_state *st) { - char *value; - value = conv_str(st,& (st->stack->stack_data[st->start+2])); - push_val(st->stack, C_INT, atoi(value)); - return 0; -} - -//-----------------------------------------------------------------------// -// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA START // -//-----------------------------------------------------------------------// -int buildin_compare(struct script_state *st) { - char *message; - char *cmpstring; - int j; - message = conv_str(st,& (st->stack->stack_data[st->start+2])); - cmpstring = conv_str(st,& (st->stack->stack_data[st->start+3])); - for (j = 0; message[j]; j++) - message[j] = tolower(message[j]); - for (j = 0; cmpstring[j]; j++) - cmpstring[j] = tolower(cmpstring[j]); - push_val(st->stack,C_INT,(strstr(message,cmpstring) != NULL)); - return 0; -} - -//-----------------------------------------------------------------------// -// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA END // -//-----------------------------------------------------------------------// -// [zBuffer] List of mathematics commands ---> -int buildin_sqrt(struct script_state *st){ - double i, a; - i = conv_num(st, &(st->stack->stack_data[st->start+2])); - a = sqrt(i); - push_val(st->stack, C_INT, (int)a); - return 0; -} - -int buildin_pow(struct script_state *st){ - double i, a, b; - a = conv_num(st, &(st->stack->stack_data[st->start+2])); - b = conv_num(st, &(st->stack->stack_data[st->start+3])); - i = pow(a,b); - push_val(st->stack, C_INT, (int)i); - return 0; -} -int buildin_distance(struct script_state *st){ - int x0, y0, x1, y1; - - x0 = conv_num(st, &(st->stack->stack_data[st->start+2])); - y0 = conv_num(st, &(st->stack->stack_data[st->start+3])); - x1 = conv_num(st, &(st->stack->stack_data[st->start+4])); - y1 = conv_num(st, &(st->stack->stack_data[st->start+5])); - - push_val(st->stack, C_INT, distance(x0-x1, y0-y1)); - return 0; -} - -// <--- [zBuffer] List of mathematics commands -// [zBuffer] List of dynamic var commands ---> -void setd_sub(struct script_state *st, struct map_session_data *sd, char *varname, int elem, void *value, struct linkdb_node **ref) -{ - set_reg(st, sd, add_str((unsigned char *) varname)+(elem<<24), varname, value, ref); - return; -} - -int buildin_setd(struct script_state *st) -{ - struct map_session_data *sd=NULL; - char varname[100], *buffer; - char *value; - int elem; - buffer = conv_str(st, & (st->stack->stack_data[st->start+2])); - value = conv_str(st, & (st->stack->stack_data[st->start+3])); - - if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2) - elem = 0; - - if(st->rid) - sd = script_rid2sd(st); - - if(varname[strlen(varname)-1] != '$') { - setd_sub(st,sd, varname, elem, (void *)atoi(value),NULL); - } else { - setd_sub(st,sd, varname, elem, (void *)value,NULL); - } - - return 0; -} - -#ifndef TXT_ONLY -int buildin_query_sql(struct script_state *st) { - char *name, *query; - int num, i = 0; - struct map_session_data *sd = (st->rid)? script_rid2sd(st) : NULL; - - query = conv_str(st,& (st->stack->stack_data[st->start+2])); - strcpy(tmp_sql, query); - if(mysql_query(&mmysql_handle,tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return 1; - } - - if(st->end > st->start+3) { - if(st->stack->stack_data[st->start+3].type != C_NAME){ - ShowWarning("buildin_query_sql: 2nd parameter is not a variable!\n"); - } else { - num=st->stack->stack_data[st->start+3].u.num; - name=(char *)(str_buf+str_data[num&0x00ffffff].str); - if((sql_res = mysql_store_result(&mmysql_handle))){ - if(name[strlen(name)-1] != '$') { - while(i<128 && (sql_row = mysql_fetch_row(sql_res))){ - setd_sub(st,sd, name, i, (void *)atoi(sql_row[0]),NULL); - i++; - } - } else { - while(i<128 && (sql_row = mysql_fetch_row(sql_res))){ - setd_sub(st,sd, name, i, (void *)sql_row[0],NULL); - i++; - } - } - mysql_free_result(sql_res); - } - } - } - - return 0; -} - -//Allows escaping of a given string. -int buildin_escape_sql(struct script_state *st) { - char *t_query, *query; - query = conv_str(st,& (st->stack->stack_data[st->start+2])); - - t_query = aMallocA((strlen(query)*2+1)*sizeof(char)); - jstrescapecpy(t_query,query); - push_str(st->stack,C_STR,(unsigned char *)t_query); - return 0; -} -#endif - -int buildin_getd (struct script_state *st) -{ - char varname[100], *buffer; - //struct script_data dat; - int elem; - - buffer = conv_str(st, & (st->stack->stack_data[st->start+2])); - - if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2) - elem = 0; - - /*dat.type=C_NAME; - dat.u.num=add_str((unsigned char *) varname)+(elem<<24); - get_val(st,&dat); - - if(dat.type == C_INT) - push_val(st->stack, C_INT, dat.u.num); - else if(dat.type == C_CONSTSTR){ - buffer = aStrdup((char *)dat.u.str); - // dat.u.str holds the actual pointer to the data, must be duplicated. - // It will be freed later. Tested. - push_str(st->stack, C_STR, buffer); - }*/ - - // Push the 'pointer' so it's more flexible [Lance] - push_val(st->stack,C_NAME, - (elem<<24) | add_str((unsigned char *) varname)); - - return 0; -} - -// <--- [zBuffer] List of dynamic var commands -// Pet stat [Lance] -int buildin_petstat(struct script_state *st){ - struct map_session_data *sd = NULL; - char *tmp; - int flag = conv_num(st, & (st->stack->stack_data[st->start+2])); - sd = script_rid2sd(st); - if(!sd || !sd->pet.pet_id){ - if(flag == 2) - push_str(st->stack, C_CONSTSTR, ""); - else - push_val(st->stack, C_INT, 0); - } - else { - switch(flag){ - case 1: - push_val(st->stack, C_INT, (int)sd->pet.class_); - break; - case 2: - tmp = aStrdup(sd->pet.name); - push_str(st->stack, C_STR, tmp); - break; - case 3: - push_val(st->stack, C_INT, (int)sd->pet.level); - break; - case 4: - push_val(st->stack, C_INT, (int)sd->pet.hungry); - break; - case 5: - push_val(st->stack, C_INT, (int)sd->pet.intimate); - break; - default: - push_val(st->stack, C_INT, 0); - break; - } - } - return 0; -} - -int buildin_callshop(struct script_state *st) -{ - struct map_session_data *sd = NULL; - struct npc_data *nd; - char *shopname; - int flag = 0; - sd = script_rid2sd(st); - if (!sd) { - push_val(st->stack,C_INT,0); - return 0; - } - shopname = conv_str(st, & (st->stack->stack_data[st->start+2])); - if( st->end>st->start+3 ) - flag = conv_num(st, & (st->stack->stack_data[st->start+3])); - nd = npc_name2id(shopname); - if (!nd || nd->bl.type!=BL_NPC || nd->bl.subtype!=SHOP) { - ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)", shopname); - push_val(st->stack,C_INT,0); - return 1; - } - - switch (flag) { - case 1: //Buy window - npc_buysellsel(sd,nd->bl.id,0); - break; - case 2: //Sell window - npc_buysellsel(sd,nd->bl.id,1); - break; - default: //Show menu - clif_npcbuysell(sd,nd->bl.id); - break; - } - sd->npc_shopid = nd->bl.id; - push_val(st->stack,C_INT,1); - return 0; -} - -int buildin_npcshopitem(struct script_state *st) -{ - struct npc_data *nd= NULL; - int n = 0; - int i = 3; - int itemid, value; - - char* npcname = conv_str(st, & (st->stack->stack_data[st->start + 2])); - nd = npc_name2id(npcname); - -#ifndef MAX_SHOPITEM - #define MAX_SHOPITEM 100 -#endif - - if(nd && nd->bl.subtype==SHOP){ - nd = (struct npc_data *)aRealloc(nd,sizeof(struct npc_data) + - sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1)); - - // Reset sell list. - for(;nd->u.shop_item[n].nameid && n < MAX_SHOPITEM; n++){ - nd->u.shop_item[n].nameid = 0; - nd->u.shop_item[n].value = 0; - } - - n = 0; - - while (st->end > st->start + i) { - itemid = conv_num(st, & (st->stack->stack_data[st->start+i])); - nd->u.shop_item[n].nameid = itemid; - i++; - value = conv_num(st, & (st->stack->stack_data[st->start+i])); - nd->u.shop_item[n].value = value; - i++; - n++; - } - - nd = (struct npc_data *)aRealloc(nd,sizeof(struct npc_data) + - sizeof(nd->u.shop_item[0]) * n); - - map_addiddb(&nd->bl); - - nd->master_nd = ((struct npc_data *)map_id2bl(st->oid)); - } else { - ShowError("buildin_npcshopitem: shop not found.\n"); - } - - return 0; -} - -/*========================================== - * Returns some values of an item [Lupus] - * Price, Weight, etc... - setiteminfo(itemID,"{new item bonus script}"); - *------------------------------------------ - */ -int buildin_setitemscript(struct script_state *st) -{ - int item_id; - char *script; - struct item_data *i_data; - - item_id = conv_num(st,& (st->stack->stack_data[st->start+2])); - script = conv_str(st,& (st->stack->stack_data[st->start+3])); - i_data = itemdb_exists(item_id); - - if (i_data && script!=NULL && script[0]=='{') { - if(i_data->script!=NULL) - script_free_code(i_data->script); - i_data->script = parse_script((unsigned char *) script, 0); - push_val(st->stack,C_INT,1); - } else - push_val(st->stack,C_INT,0); - return 0; -} - -/* Work In Progress [Lupus] -int buildin_addmonsterdrop(struct script_state *st) -{ - int class_,item_id,chance; - class_=conv_num(st,& (st->stack->stack_data[st->start+2])); - item_id=conv_num(st,& (st->stack->stack_data[st->start+3])); - chance=conv_num(st,& (st->stack->stack_data[st->start+4])); - if(class_>1000 && item_id>500 && chance>0) { - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } -} - -int buildin_delmonsterdrop(struct script_state *st) -{ - int class_,item_id; - class_=conv_num(st,& (st->stack->stack_data[st->start+2])); - item_id=conv_num(st,& (st->stack->stack_data[st->start+3])); - if(class_>1000 && item_id>500) { - push_val(st->stack,C_INT,1); - } else { - push_val(st->stack,C_INT,0); - } -} -*/ -/*========================================== - * Returns some values of a monster [Lupus] - * Name, Level, race, size, etc... - getmonsterinfo(monsterID,queryIndex); - *------------------------------------------ - */ -int buildin_getmonsterinfo(struct script_state *st) -{ - struct mob_db *mob; - int mob_id; - - mob_id = conv_num(st,& (st->stack->stack_data[st->start+2])); - if (mob_id <= 1000 || (mob = mob_db(mob_id))==NULL) { - ShowError("buildin_getmonsterinfo: Wrong Monster ID: %i", mob_id); - push_val(st->stack, C_INT, -1); - return -1; - } - - switch ( conv_num(st,& (st->stack->stack_data[st->start+3])) ) { - case 0: //Name - push_str(st->stack,C_CONSTSTR, (unsigned char *) mob->jname); - break; - case 1: //Lvl - push_val(st->stack,C_INT, mob->lv); - break; - case 2: //MaxHP - push_val(st->stack,C_INT, mob->max_hp); - break; - case 3: //Base EXP - push_val(st->stack,C_INT, mob->base_exp); - break; - case 4: //Job EXP - push_val(st->stack,C_INT, mob->job_exp); - break; - case 5: //Atk1 - push_val(st->stack,C_INT, mob->atk1); - break; - case 6: //Atk2 - push_val(st->stack,C_INT, mob->atk2); - break; - case 7: //Def - push_val(st->stack,C_INT, mob->def); - break; - case 8: //Mdef - push_val(st->stack,C_INT, mob->mdef); - break; - case 9: //Str - push_val(st->stack,C_INT, mob->str); - break; - case 10: //Agi - push_val(st->stack,C_INT, mob->agi); - break; - case 11: //Vit - push_val(st->stack,C_INT, mob->vit); - break; - case 12: //Int - push_val(st->stack,C_INT, mob->int_); - break; - case 13: //Dex - push_val(st->stack,C_INT, mob->dex); - break; - case 14: //Luk - push_val(st->stack,C_INT, mob->luk); - break; - case 15: //Range - push_val(st->stack,C_INT, mob->range); - break; - case 16: //Range2 - push_val(st->stack,C_INT, mob->range2); - break; - case 17: //Range3 - push_val(st->stack,C_INT, mob->range3); - break; - case 18: //Size - push_val(st->stack,C_INT, mob->size); - break; - case 19: //Race - push_val(st->stack,C_INT, mob->race); - break; - case 20: //Element - push_val(st->stack,C_INT, mob->element); - break; - case 21: //Mode - push_val(st->stack,C_INT, mob->mode); - break; - default: //wrong Index - push_val(st->stack,C_INT,-1); - } - return 0; -} - -// [zBuffer] List of player cont commands ---> -int buildin_rid2name(struct script_state *st){ - struct block_list *bl = NULL; - int rid = conv_num(st, & (st->stack->stack_data[st->start + 2])); - if((bl = map_id2bl(rid))){ - switch(bl->type){ - case BL_MOB: - push_str(st->stack,C_CONSTSTR,((struct mob_data *)bl)->name); - break; - case BL_PC: - push_str(st->stack,C_CONSTSTR,((struct map_session_data *)bl)->status.name); - break; - case BL_NPC: - push_str(st->stack,C_CONSTSTR,((struct npc_data *)bl)->exname); - break; - case BL_PET: - push_str(st->stack,C_CONSTSTR,((struct pet_data *)bl)->name); - break; - case BL_HOMUNCULUS: - push_str(st->stack,C_CONSTSTR,((struct homun_data *)bl)->name); - break; - default: - ShowError("buildin_rid2name: BL type unknown.\n"); - push_str(st->stack,C_CONSTSTR,""); - break; - } - } - return 0; -} - -int buildin_pcwalkxy(struct script_state *st){ - int id, x, y = 0; - struct map_session_data *sd = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start + 2])); - x = conv_num(st, & (st->stack->stack_data[st->start + 3])); - if(st->end > st->start + 4) - y = conv_num(st, & (st->stack->stack_data[st->start + 4])); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd){ - if(y) - unit_walktoxy(&sd->bl, x, y, 0); - else - unit_walktobl(&sd->bl, map_id2bl(x), 65535, 1); - } - - return 0; -} - -int buildin_pcblockmove(struct script_state *st){ - int id, flag; - struct map_session_data *sd = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start + 2])); - flag = conv_num(st, & (st->stack->stack_data[st->start + 3])); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd) - sd->state.blockedmove = flag > 0; - - return 0; -} - -int buildin_pctalk(struct script_state *st){ - int id; - char *str; - char message[255]; - struct map_session_data *sd = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start + 2])); - str = conv_str(st, & (st->stack->stack_data[st->start + 3])); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd){ - memcpy(message, sd->status.name, NAME_LENGTH); - strcat(message," : "); - strncat(message,str, 254); //Prevent overflow possibility. [Skotlex] - clif_message(&(sd->bl), message); - clif_displaymessage(sd->fd, message); - } - - return 0; -} - -int buildin_pcemote(struct script_state *st) { - int id, emo; - struct map_session_data *sd = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start + 2])); - emo = conv_num(st, & (st->stack->stack_data[st->start + 3])); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd) - clif_emotion(&sd->bl,emo); - - return 0; - -} - -int buildin_pcfollow(struct script_state *st) { - int id, targetid; - struct map_session_data *sd = NULL; - - - id = conv_num(st, & (st->stack->stack_data[st->start + 2])); - targetid = conv_num(st, & (st->stack->stack_data[st->start + 3])); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd) - pc_follow(sd, targetid); - - return 0; -} - -int buildin_pcstopfollow(struct script_state *st) { - int id; - struct map_session_data *sd = NULL; - - - id = conv_num(st, & (st->stack->stack_data[st->start + 2])); - - if(id) - sd = map_id2sd(id); - else - sd = script_rid2sd(st); - - if(sd) - pc_stop_following(sd); - - return 0; -} -// <--- [zBuffer] List of player cont commands -// [zBuffer] List of mob control commands ---> -int buildin_spawnmob(struct script_state *st){ - int class_,x,y,id; - char *str,*map,*event=""; - struct mob_data *md = NULL; - - // Who? - str =conv_str(st,& (st->stack->stack_data[st->start+2])); - // What? - class_ =conv_num(st,& (st->stack->stack_data[st->start+3])); - // Where? - map =conv_str(st,& (st->stack->stack_data[st->start+4])); - x =conv_num(st,& (st->stack->stack_data[st->start+5])); - y =conv_num(st,& (st->stack->stack_data[st->start+6])); - // When? - if( st->end > st->start+8 ){ - event=conv_str(st,& (st->stack->stack_data[st->start+7])); - check_event(st, event); - } - - id = mob_once_spawn(map_id2sd(st->rid),map,x,y,str,class_,1,event); - if(id){ - md = (struct mob_data *)map_id2bl(id); - if(md){ - md->mode = md->db->mode; - } - push_val(st->stack,C_INT,id); - } - - return 0; -} - -int buildin_removemob(struct script_state *st) { - int id; - struct block_list *bl = NULL; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - - bl = map_id2bl(id); - if (bl && bl->type == BL_MOB) - unit_free(bl); - - return 0; -} - -int buildin_mobwalk(struct script_state *st){ - int id,x,y = 0; - struct block_list *bl = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - x = conv_num(st, & (st->stack->stack_data[st->start+3])); - if(st->end > st->start+4) - y = conv_num(st, & (st->stack->stack_data[st->start+4])); - - bl = map_id2bl(id); - if(bl && bl->type == BL_MOB){ - if(y) - push_val(st->stack,C_INT,unit_walktoxy(bl,x,y,0)); // We'll use harder calculations. - else - push_val(st->stack,C_INT,unit_walktobl(bl,map_id2bl(x),1,65025)); - } else { - push_val(st->stack,C_INT,0); - } - - return 0; -} - -int buildin_getmobdata(struct script_state *st) { - int num, id; - char *name; - struct mob_data *md = NULL; - struct map_session_data *sd = st->rid?map_id2sd(st->rid):NULL; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - if(!(md = (struct mob_data *)map_id2bl(id)) || st->stack->stack_data[st->start+3].type!=C_NAME ){ - ShowWarning("buildin_getmobdata: Error in argument!\n"); - } else { - num=st->stack->stack_data[st->start+3].u.num; - name=(char *)(str_buf+str_data[num&0x00ffffff].str); - setd_sub(st,sd,name,0,(void *)(int)md->class_,NULL); - setd_sub(st,sd,name,1,(void *)(int)md->level,NULL); - setd_sub(st,sd,name,2,(void *)(int)md->hp,NULL); - setd_sub(st,sd,name,3,(void *)(int)md->max_hp,NULL); - setd_sub(st,sd,name,4,(void *)(int)md->master_id,NULL); - setd_sub(st,sd,name,5,(void *)(int)md->bl.m,NULL); - setd_sub(st,sd,name,6,(void *)(int)md->bl.x,NULL); - setd_sub(st,sd,name,7,(void *)(int)md->bl.y,NULL); - setd_sub(st,sd,name,8,(void *)(int)md->speed,NULL); - setd_sub(st,sd,name,9,(void *)(int)(md->mode?md->mode:md->db->mode),NULL); - setd_sub(st,sd,name,10,(void *)(int)md->special_state.ai,NULL); - setd_sub(st,sd,name,11,(void *)(int)md->db->option,NULL); - setd_sub(st,sd,name,12,(void *)(int)md->vd->sex,NULL); - setd_sub(st,sd,name,13,(void *)(int)md->vd->class_,NULL); - setd_sub(st,sd,name,14,(void *)(int)md->vd->hair_style,NULL); - setd_sub(st,sd,name,15,(void *)(int)md->vd->hair_color,NULL); - setd_sub(st,sd,name,16,(void *)(int)md->vd->head_bottom,NULL); - setd_sub(st,sd,name,17,(void *)(int)md->vd->head_mid,NULL); - setd_sub(st,sd,name,18,(void *)(int)md->vd->head_top,NULL); - setd_sub(st,sd,name,19,(void *)(int)md->vd->cloth_color,NULL); - setd_sub(st,sd,name,20,(void *)(int)md->vd->shield,NULL); - setd_sub(st,sd,name,21,(void *)(int)md->vd->weapon,NULL); - setd_sub(st,sd,name,22,(void *)(int)md->vd->shield,NULL); - setd_sub(st,sd,name,23,(void *)(int)md->ud.dir,NULL); - setd_sub(st,sd,name,24,(void *)(int)md->state.killer,NULL); - } - return 0; -} - -int buildin_setmobdata(struct script_state *st){ - int id, value, value2; - struct mob_data *md = NULL; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - value = conv_num(st, & (st->stack->stack_data[st->start+3])); - value2 = conv_num(st, & (st->stack->stack_data[st->start+4])); - if(!(md = (struct mob_data *)map_id2bl(id))){ - ShowWarning("buildin_setmobdata: Error in argument!\n"); - } else { - switch(value){ - case 0: - md->class_ = (short)value2; - break; - case 1: - md->level = (unsigned short)value2; - break; - case 2: - md->hp = value2; - break; - case 3: - md->max_hp = value2; - break; - case 4: - md->master_id = value2; - break; - case 5: - md->bl.m = (short)value2; - break; - case 6: - md->bl.x = (short)value2; - break; - case 7: - md->bl.y = (short)value2; - break; - case 8: - md->speed = (short)value2; - break; - case 9: - md->mode = (short)value2; - break; - case 10: - md->special_state.ai = (unsigned int)value2; - break; - case 11: - md->db->option = (short)value2; - break; - case 12: - md->vd->sex = value2; - break; - case 13: - md->vd->class_ = value2; - break; - case 14: - md->vd->hair_style = (short)value2; - break; - case 15: - md->vd->hair_color = (short)value2; - break; - case 16: - md->vd->head_bottom = (short)value2; - break; - case 17: - md->vd->head_mid = (short)value2; - break; - case 18: - md->vd->head_top = (short)value2; - break; - case 19: - md->vd->cloth_color = (short)value2; - break; - case 20: - md->vd->shield = value2; - break; - case 21: - md->vd->weapon = (short)value2; - break; - case 22: - md->vd->shield = (short)value2; - break; - case 23: - md->ud.dir = (unsigned char)value2; - break; - case 24: - md->state.killer = value2>0?1:0; - break; - default: - ShowError("buildin_setmobdata: argument value2 is not identified."); - break; - } - } - return 0; -} - -int buildin_mobattack(struct script_state *st) { - int id = 0; - char *target = NULL; - struct mob_data *md = NULL; - struct map_session_data *sd = NULL; - struct block_list *bl = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - if(st->end > st->start + 3) - target = conv_str(st, & (st->stack->stack_data[st->start+3])); - - if(target){ - sd = map_nick2sd(target); - if(!sd) - bl = map_id2bl(atoi(target)); - else - bl = &sd->bl; - } - - if((md = (struct mob_data *)map_id2bl(id))){ - if (md && md->bl.type == BL_MOB) { - md->state.killer = 1; - md->special_state.ai = 1; - if(bl){ - md->target_id = bl->id; - unit_walktobl(&md->bl, bl, 65025, 2); - } - } - } - - return 0; -} - -int buildin_mobstop(struct script_state *st) { - int id; - struct block_list *bl = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - - bl = map_id2bl(id); - if(bl && bl->type == BL_MOB){ - unit_stop_attack(bl); - unit_stop_walking(bl,0); - ((TBL_MOB *)bl)->target_id = 0; - } - - return 0; -} - -int buildin_mobrandomwalk(struct script_state *st){ - int id = conv_num(st, &(st->stack->stack_data[st->start+2])); - int flag = conv_num(st, &(st->stack->stack_data[st->start+3])); - struct mob_data *md = (struct mob_data *)map_id2bl(id); - if(md->bl.type == BL_MOB){ - md->state.no_random_walk = flag>0?0:1; - } - return 0; -} - -int buildin_mobassist(struct script_state *st) { - int id; - char *target; - struct mob_data *md = NULL; - struct block_list *bl = NULL; - struct unit_data *ud; - - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - target = conv_str(st, & (st->stack->stack_data[st->start+3])); - - if((bl =&(map_nick2sd(target)->bl)) || (bl = map_id2bl(atoi(target)))) { - md = (struct mob_data *)map_id2bl(id); - if(md && md->bl.type == BL_MOB) { - ud = unit_bl2ud(bl); - md->master_id = bl->id; - md->state.killer = 1; - mob_convertslave(md); - if (ud) { - if (ud->target) - md->target_id = ud->target; - else if (ud->skilltarget) - md->target_id = ud->skilltarget; - if(md->target_id) - unit_walktobl(&md->bl, map_id2bl(md->target_id), 65025, 2); - } - } - } - return 0; -} - -int buildin_mobtalk(struct script_state *st) -{ - char *str; - int id; - char message[255]; - - struct mob_data *md = NULL; - - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - str=conv_str(st,& (st->stack->stack_data[st->start+3])); - - md = (struct mob_data *)map_id2bl(id); - if(md && md->bl.type == BL_MOB) { - memcpy(message, md->name, NAME_LENGTH); - strcat(message," : "); - strncat(message,str, 254); //Prevent overflow possibility. [Skotlex] - clif_message(&(md->bl), message); - } - - return 0; -} - -int buildin_mobemote(struct script_state *st) { - int id, emo; - struct mob_data *md = NULL; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - emo = conv_num(st, & (st->stack->stack_data[st->start+3])); - if((md = (struct mob_data *)map_id2bl(id)) && md->bl.type == BL_MOB) - clif_emotion(&md->bl,emo); - return 0; -} - -int buildin_mobattach(struct script_state *st){ - int id; - struct mob_data *md = NULL; - struct npc_data *nd = NULL; - char *npcname = NULL; - id = conv_num(st, & (st->stack->stack_data[st->start+2])); - if(st->end > st->start + 3){ - npcname = conv_str(st, & (st->stack->stack_data[st->start+3])); - } - - if(npcname) - nd = npc_name2id(npcname); - else - nd = (struct npc_data *)map_id2bl(st->oid); - - if(nd) - if((md = (struct mob_data *)map_id2bl(id)) && md->bl.type == BL_MOB) - md->nd = nd; - - return 0; -} - -// <--- [zBuffer] List of mob control commands - -// sleep -int buildin_sleep(struct script_state *st) { - int tick = conv_num(st,& (st->stack->stack_data[st->start+2])); - struct map_session_data *sd = map_id2sd(st->rid); - if(sd && sd->npc_id == st->oid) { - sd->npc_id = 0; - } - st->rid = 0; - if(tick <= 0) { - // 何もしない - } else if( !st->sleep.tick ) { - // 初回実行 - st->state = RERUNLINE; - st->sleep.tick = tick; - } else { - // 続行 - st->sleep.tick = 0; - } - return 0; -} - -// sleep2 -int buildin_sleep2(struct script_state *st) { - int tick = conv_num(st,& (st->stack->stack_data[st->start+2])); - if( tick <= 0 ) { - // 0ms の待機時間を指定された - push_val(st->stack,C_INT,map_id2sd(st->rid) != NULL); - } else if( !st->sleep.tick ) { - // 初回実行時 - st->state = RERUNLINE; - st->sleep.tick = tick; - } else { - push_val(st->stack,C_INT,map_id2sd(st->rid) != NULL); - st->sleep.tick = 0; - } - return 0; -} - -/*========================================== - * 指定NPCの全てのsleepを再開する - *------------------------------------------ - */ -int buildin_awake(struct script_state *st) -{ - struct npc_data *nd; - struct linkdb_node *node = (struct linkdb_node *)sleep_db; - - nd = npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); - if(nd == NULL) - return 0; - - while( node ) { - if( (int)node->key == nd->bl.id) { - struct script_state *tst = node->data; - struct map_session_data *sd = map_id2sd(tst->rid); - - if( tst->sleep.timer == -1 ) { - node = node->next; - continue; - } - if( sd && sd->char_id != tst->sleep.charid ) - tst->rid = 0; - - delete_timer(tst->sleep.timer, run_script_timer); - node = script_erase_sleepdb(node); - tst->sleep.timer = -1; - run_script_main(tst); - } else { - node = node->next; - } - } - return 0; -} - -// getvariableofnpc(, ); -int buildin_getvariableofnpc(struct script_state *st) -{ - if( st->stack->stack_data[st->start+2].type != C_NAME ) { - // 第一引数が変数名じゃない - printf("getvariableofnpc: param not name\n"); - push_val(st->stack,C_INT,0); - } else { - int num = st->stack->stack_data[st->start+2].u.num; - char *var_name = str_buf+str_data[num&0x00ffffff].str; - char *npc_name = conv_str(st,& (st->stack->stack_data[st->start+3])); - struct npc_data *nd = npc_name2id(npc_name); - if( var_name[0] != '.' || var_name[1] == '@' ) { - // ' 変数以外はダメ - printf("getvariableofnpc: invalid scope %s\n", var_name); - push_val(st->stack,C_INT,0); - } else if( nd == NULL || nd->bl.subtype != SCRIPT || !nd->u.scr.script) { - // NPC が見つからない or SCRIPT以外のNPC - printf("getvariableofnpc: can't find npc %s\n", npc_name); - push_val(st->stack,C_INT,0); - } else { - push_val2(st->stack,C_NAME,num, &nd->u.scr.script->script_vars ); - } - } - return 0; -} -// -// 実行部main -// -/*========================================== - * コマンドの読み取り - *------------------------------------------ - */ -static int unget_com_data=-1; -int get_com(unsigned char *script,int *pos) -{ - int i,j; - if(unget_com_data>=0){ - i=unget_com_data; - unget_com_data=-1; - return i; - } - if(script[*pos]>=0x80){ - return C_INT; - } - i=0; j=0; - while(script[*pos]>=0x40){ - i=script[(*pos)++]<=0xc0){ - i+=(script[(*pos)++]&0x7f)<stack->sp<=0) - return 0; - st->stack->sp--; - get_val(st,&(st->stack->stack_data[st->stack->sp])); - if(st->stack->stack_data[st->stack->sp].type==C_INT) - return st->stack->stack_data[st->stack->sp].u.num; - return 0; -} - -#define isstr(c) ((c).type==C_STR || (c).type==C_CONSTSTR) - -/*========================================== - * 加算演算子 - *------------------------------------------ - */ -void op_add(struct script_state* st) -{ - st->stack->sp--; - get_val(st,&(st->stack->stack_data[st->stack->sp])); - get_val(st,&(st->stack->stack_data[st->stack->sp-1])); - - if(isstr(st->stack->stack_data[st->stack->sp]) || isstr(st->stack->stack_data[st->stack->sp-1])){ - conv_str(st,&(st->stack->stack_data[st->stack->sp])); - conv_str(st,&(st->stack->stack_data[st->stack->sp-1])); - } - if(st->stack->stack_data[st->stack->sp].type==C_INT){ // ii - st->stack->stack_data[st->stack->sp-1].u.num += st->stack->stack_data[st->stack->sp].u.num; - } else { // ssの予定 - char *buf; - buf=(char *)aMallocA((strlen(st->stack->stack_data[st->stack->sp-1].u.str)+ - strlen(st->stack->stack_data[st->stack->sp].u.str)+1)*sizeof(char)); - strcpy(buf,st->stack->stack_data[st->stack->sp-1].u.str); - strcat(buf,st->stack->stack_data[st->stack->sp].u.str); - if(st->stack->stack_data[st->stack->sp-1].type==C_STR) - { - aFree(st->stack->stack_data[st->stack->sp-1].u.str); - st->stack->stack_data[st->stack->sp-1].type=C_INT; - } - if(st->stack->stack_data[st->stack->sp].type==C_STR) - { - aFree(st->stack->stack_data[st->stack->sp].u.str); - st->stack->stack_data[st->stack->sp].type=C_INT; - } - st->stack->stack_data[st->stack->sp-1].type=C_STR; - st->stack->stack_data[st->stack->sp-1].u.str=buf; - } -} - -/*========================================== - * 二項演算子(文字列) - *------------------------------------------ - */ -void op_2str(struct script_state *st,int op,int sp1,int sp2) -{ - char *s1=st->stack->stack_data[sp1].u.str, - *s2=st->stack->stack_data[sp2].u.str; - int a=0; - - switch(op){ - case C_EQ: - a= (strcmp(s1,s2)==0); - break; - case C_NE: - a= (strcmp(s1,s2)!=0); - break; - case C_GT: - a= (strcmp(s1,s2)> 0); - break; - case C_GE: - a= (strcmp(s1,s2)>=0); - break; - case C_LT: - a= (strcmp(s1,s2)< 0); - break; - case C_LE: - a= (strcmp(s1,s2)<=0); - break; - default: - ShowWarning("script: illegal string operator\n"); - break; - } - - // Because push_val() overwrite stack_data[sp1], C_STR on stack_data[sp1] won't be freed. - // So, call push_val() after freeing strings. [jA1783] - // push_val(st->stack,C_INT,a); - if(st->stack->stack_data[sp1].type==C_STR) - { - aFree(s1); - st->stack->stack_data[sp1].type=C_INT; - } - if(st->stack->stack_data[sp2].type==C_STR) - { - aFree(s2); - st->stack->stack_data[sp2].type=C_INT; - } - push_val(st->stack,C_INT,a); -} -/*========================================== - * 二項演算子(数値) - *------------------------------------------ - */ -void op_2num(struct script_state *st,int op,int i1,int i2) -{ - switch(op){ - case C_SUB: - i1-=i2; - break; - case C_MUL: - { - #ifndef _MSC_VER - long long res = i1 * i2; - #else - __int64 res = i1 * i2; - #endif - if (res > 2147483647 ) - i1 = 2147483647; - else - i1*=i2; - } - break; - case C_DIV: - if (i2 != 0) - i1/=i2; - else - ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_DIV)!\n"); - break; - case C_MOD: - if (i2 != 0) - i1%=i2; - else - ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_MOD)!\n"); - break; - case C_AND: - i1&=i2; - break; - case C_OR: - i1|=i2; - break; - case C_XOR: - i1^=i2; - break; - case C_LAND: - i1=i1&&i2; - break; - case C_LOR: - i1=i1||i2; - break; - case C_EQ: - i1=i1==i2; - break; - case C_NE: - i1=i1!=i2; - break; - case C_GT: - i1=i1>i2; - break; - case C_GE: - i1=i1>=i2; - break; - case C_LT: - i1=i1>i2; - break; - case C_L_SHIFT: - i1=i1<stack,C_INT,i1); -} -/*========================================== - * 二項演算子 - *------------------------------------------ - */ -void op_2(struct script_state *st,int op) -{ - int i1,i2; - char *s1=NULL,*s2=NULL; - - i2=pop_val(st); - if( isstr(st->stack->stack_data[st->stack->sp]) ) - s2=st->stack->stack_data[st->stack->sp].u.str; - - i1=pop_val(st); - if( isstr(st->stack->stack_data[st->stack->sp]) ) - s1=st->stack->stack_data[st->stack->sp].u.str; - - if( s1!=NULL && s2!=NULL ){ - // ss => op_2str - op_2str(st,op,st->stack->sp,st->stack->sp+1); - }else if( s1==NULL && s2==NULL ){ - // ii => op_2num - op_2num(st,op,i1,i2); - }else{ - // si,is => error - ShowWarning("script: op_2: int&str, str&int not allow."); - push_val(st->stack,C_INT,0); - } -} - -/*========================================== - * 単項演算子 - *------------------------------------------ - */ -void op_1num(struct script_state *st,int op) -{ - int i1; - i1=pop_val(st); - switch(op){ - case C_NEG: - i1=-i1; - break; - case C_NOT: - i1=~i1; - break; - case C_LNOT: - i1=!i1; - break; - } - push_val(st->stack,C_INT,i1); -} - - -/*========================================== - * 関数の実行 - *------------------------------------------ - */ -int run_func(struct script_state *st) -{ - int i,start_sp,end_sp,func; - - end_sp=st->stack->sp; - for(i=end_sp-1;i>=0 && st->stack->stack_data[i].type!=C_ARG;i--); - if(i==0){ - if(battle_config.error_log) - ShowError("function not found\n"); -// st->stack->sp=0; - st->state=END; - report_src(st); - return 1; - } - start_sp=i-1; - st->start=i-1; - st->end=end_sp; - - func=st->stack->stack_data[st->start].u.num; - if( st->stack->stack_data[st->start].type!=C_NAME || str_data[func].type!=C_FUNC ){ - ShowMessage ("run_func: '"CL_WHITE"%s"CL_RESET"' (type %d) is not function and command!\n", - str_buf + str_data[func].str, str_data[func].type); -// st->stack->sp=0; - st->state=END; - report_src(st); - return 1; - } -#ifdef DEBUG_RUN - if(battle_config.etc_log) { - ShowDebug("run_func : %s? (%d(%d)) sp=%d (%d...%d)\n",str_buf+str_data[func].str, func, str_data[func].type, st->stack->sp, st->start, st->end); - ShowDebug("stack dump :"); - for(i=0;istack->stack_data[i].type){ - case C_INT: - printf(" int(%d)",st->stack->stack_data[i].u.num); - break; - case C_NAME: - printf(" name(%s)",str_buf+str_data[st->stack->stack_data[i].u.num].str); - break; - case C_ARG: - printf(" arg"); - break; - case C_POS: - printf(" pos(%d)",st->stack->stack_data[i].u.num); - break; - case C_STR: - printf(" str(%s)",st->stack->stack_data[i].u.str); - break; - case C_CONSTSTR: - printf(" cstr(%s)",st->stack->stack_data[i].u.str); - break; - default: - printf(" %d,%d",st->stack->stack_data[i].type,st->stack->stack_data[i].u.num); - } - } - printf("\n"); - } -#endif - if(str_data[func].func){ - if (str_data[func].func(st)) //Report error - report_src(st); - } else { - if(battle_config.error_log) - ShowError("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type); - push_val(st->stack,C_INT,0); - report_src(st); - } - - // Stack's datum are used when re-run functions [Eoe] - if(st->state != RERUNLINE) { - pop_stack(st->stack,start_sp,end_sp); - } - - if(st->state==RETFUNC){ - // ユーザー定義関数からの復帰 - int olddefsp=st->stack->defsp; - int i; - - pop_stack(st->stack,st->stack->defsp,start_sp); // 復帰に邪魔なスタック削除 - if(st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO){ - ShowWarning("script:run_func(return) return without callfunc or callsub!\n"); - st->state=END; - report_src(st); - return 1; - } - script_free_vars( st->stack->var_function ); - aFree(st->stack->var_function); - i = conv_num(st,& (st->stack->stack_data[st->stack->defsp-5])); // 引数の数所得 - st->pos=conv_num(st,& (st->stack->stack_data[st->stack->defsp-1])); // スクリプト位置の復元 - st->script=(struct script_code *)conv_num(st,& (st->stack->stack_data[st->stack->defsp-3])); // スクリプトを復元 - st->stack->var_function = (struct linkdb_node**)st->stack->stack_data[st->stack->defsp-2].u.num; // 関数依存変数 - st->stack->defsp=conv_num(st,& (st->stack->stack_data[st->stack->defsp-4])); // 基準スタックポインタを復元 - - pop_stack(st->stack,olddefsp-5-i,olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 - - st->state=GOTO; - } - - return 0; -} - -/*========================================== - * スクリプトの実行メイン部分 - *------------------------------------------ - */ -int run_script_main(struct script_state *st) -{ - int c/*,rerun_pos*/; - int cmdcount=script_config.check_cmdcount; - int gotocount=script_config.check_gotocount; - struct script_stack *stack=st->stack; - - if(st->state == RERUNLINE) { - st->state = RUN; - run_func(st); - if(st->state == GOTO){ - st->state = RUN; - } - } else { - st->state = RUN; - } - while( st->state == RUN) { - c= get_com((unsigned char *) st->script->script_buf,&st->pos); - switch(c){ - case C_EOL: - if(stack->sp!=stack->defsp){ - if(stack->sp > stack->defsp) - { //sp > defsp is valid in cases when you invoke functions and don't use the returned value. [Skotlex] - //Since sp is supposed to be defsp in these cases, we could assume the extra stack elements are unneeded. - if (battle_config.etc_log) - ShowWarning("Clearing unused stack stack.sp(%d) -> default(%d)\n",stack->sp,stack->defsp); - pop_stack(stack, stack->defsp, stack->sp); //Clear out the unused stack-section. - } else if(battle_config.error_log) - ShowError("stack.sp(%d) != default(%d)\n",stack->sp,stack->defsp); - stack->sp=stack->defsp; - } - // rerun_pos=st->pos; - break; - case C_INT: - push_val(stack,C_INT,get_num((unsigned char *) st->script->script_buf,&st->pos)); - break; - case C_POS: - case C_NAME: - push_val(stack,c,(*(int*)(st->script->script_buf+st->pos))&0xffffff); - st->pos+=3; - break; - case C_ARG: - push_val(stack,c,0); - break; - case C_STR: - push_str(stack,C_CONSTSTR,(unsigned char *) (st->script->script_buf+st->pos)); - while(st->script->script_buf[st->pos++]); - break; - case C_FUNC: - run_func(st); - if(st->state==GOTO){ - // rerun_pos=st->pos; - st->state=0; - if( gotocount>0 && (--gotocount)<=0 ){ - ShowError("run_script: infinity loop !\n"); - st->state=END; - } - } - break; - - case C_ADD: - op_add(st); - break; - - case C_SUB: - case C_MUL: - case C_DIV: - case C_MOD: - case C_EQ: - case C_NE: - case C_GT: - case C_GE: - case C_LT: - case C_LE: - case C_AND: - case C_OR: - case C_XOR: - case C_LAND: - case C_LOR: - case C_R_SHIFT: - case C_L_SHIFT: - op_2(st,c); - break; - - case C_NEG: - case C_NOT: - case C_LNOT: - op_1num(st,c); - break; - - case C_NOP: - st->state=END; - break; - - default: - if(battle_config.error_log) - ShowError("unknown command : %d @ %d\n",c,pos); - st->state=END; - break; - } - if( cmdcount>0 && (--cmdcount)<=0 ){ - ShowError("run_script: infinity loop !\n"); - st->state=END; - } - } - switch(st->state){ - case STOP: - break; - case END: - { - struct map_session_data *sd=st->rid?map_id2sd(st->rid):NULL; - st->pos=-1; - if(sd && (sd->npc_id==st->oid || sd->state.using_fake_npc)){ - if(sd->state.using_fake_npc){ - clif_clearchar_id(sd->npc_id, 0, sd->fd); - sd->state.using_fake_npc = 0; - } - npc_event_dequeue(sd); - } - } - break; - case RERUNLINE: - // Do not call function of commands two time! [ Eoe / jA 1094 ] - // For example: select "1", "2", callsub(...); - // If current script position is changed, callsub will be called two time. - // - // { - // st->pos=rerun_pos; - // } - break; - } - - if(st->state == END) { - script_free_stack (st->stack); - st->stack = NULL; - aFree(st); - st = NULL; - return 0; - } - - return 1; -} - -/*========================================== - * スクリプトの実行 - *------------------------------------------ - */ -int run_script(struct script_code *rootscript,int pos,int rid,int oid) -{ - struct script_state *st; - struct map_session_data *sd=NULL; - - //Variables for backing up the previous script and restore it if needed. [Skotlex] - struct script_code *bck_script = NULL; - struct script_code *bck_scriptroot = NULL; - int bck_scriptstate = 0; - struct script_stack *bck_stack = NULL; - - if (rootscript == NULL || pos < 0) - return -1; - - st = aCalloc(sizeof(struct script_state), 1); - - if ((sd = map_id2sd(rid)) && sd->stack && sd->npc_scriptroot == rootscript){ - // we have a stack for the same script, should continue exec. - st->script = sd->npc_script; - st->stack = sd->stack; - st->state = sd->npc_scriptstate; - // and clear vars - sd->stack = NULL; - sd->npc_script = NULL; - sd->npc_scriptroot = NULL; - sd->npc_scriptstate = 0; - } else { - // the script is different, make new script_state and stack - st->stack = aMalloc (sizeof(struct script_stack)); - st->stack->sp = 0; - st->stack->sp_max = 64; - st->stack->stack_data = (struct script_data *) aCalloc (st->stack->sp_max,sizeof(st->stack->stack_data[0])); - st->stack->defsp = st->stack->sp; - st->stack->var_function = aCalloc(1, sizeof(struct linkdb_node*)); - st->state = RUN; - st->script = rootscript; - - if (sd && sd->stack) { // if there's a sd and a stack - back it up and restore it if possible. - bck_script = sd->npc_script; - bck_scriptroot = sd->npc_scriptroot; - bck_scriptstate = sd->npc_scriptstate; - bck_stack = sd->stack; - sd->stack = NULL; - } - } - st->pos = pos; - st->rid = rid; - st->oid = oid; - st->sleep.timer = -1; - - if(run_script_main(st)){ - if(st->state != END){ - pos = st->pos; - if(st->sleep.tick > 0) - { //Delay execution - st->sleep.charid = sd?sd->char_id:0; - st->sleep.timer = add_timer(gettick()+st->sleep.tick, - run_script_timer, st->sleep.charid, (int)st); - linkdb_insert(&sleep_db, (void*)st->oid, st); - } else if (sd) { - // script is not finished, store data in sd. - sd->npc_script = st->script; - sd->npc_scriptroot = rootscript; - sd->npc_scriptstate = st->state; - sd->stack = st->stack; - if (bck_stack) //Get rid of the backup as it can't be restored. - script_free_stack (bck_stack); - aFree(st); - } - return pos; - } else { - if(st->stack) - script_free_stack(st->stack); - aFree(st); - } - } else { - //Script finished. - if (sd) - { //Clear or restore previous script. - sd->npc_script = bck_script; - sd->npc_scriptroot = bck_scriptroot; - sd->npc_scriptstate = bck_scriptstate; - sd->stack = bck_stack; - //Since the script is done, save any changed account variables [Skotlex] - if (sd->state.reg_dirty&2) - intif_saveregistry(sd,2); - if (sd->state.reg_dirty&1) - intif_saveregistry(sd,1); - } - } - return 0; -} - -/*========================================== - * 指定ノードをsleep_dbから削除 - *------------------------------------------ - */ -struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n) -{ - struct linkdb_node *retnode; - - if( n == NULL) - return NULL; - if( n->prev == NULL ) - sleep_db = n->next; - else - n->prev->next = n->next; - if( n->next ) - n->next->prev = n->prev; - retnode = n->next; - aFree( n ); - return retnode; // 次のノードを返す -} - - -/*========================================== - * sleep用タイマー関数 - *------------------------------------------ - */ -int run_script_timer(int tid, unsigned int tick, int id, int data) -{ - struct script_state *st = (struct script_state *)data; - struct linkdb_node *node = (struct linkdb_node *)sleep_db; - struct map_session_data *sd = map_id2sd(st->rid); - - if( sd && sd->char_id != id ) { - st->rid = 0; - } - while( node && st->sleep.timer != -1 ) { - if( (int)node->key == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) { - script_erase_sleepdb(node); - st->sleep.timer = -1; - break; - } - node = node->next; - } - run_script_main(st); - return 0; -} - - -/*========================================== - * マップ変数の変更 - *------------------------------------------ - */ -int mapreg_setreg(int num,int val) -{ -#if !defined(TXT_ONLY) && defined(MAPREGSQL) - int i=num>>24; - char *name=str_buf+str_data[num&0x00ffffff].str; - char tmp_str[64]; -#endif - - if(val!=0) { - -#if !defined(TXT_ONLY) && defined(MAPREGSQL) - if(name[1] != '@' && idb_get(mapreg_db,num) == NULL) { - sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%d')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,val); - if(mysql_query(&mmysql_handle,tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } -#endif - idb_put(mapreg_db,num,(void*)val); - // else - } else { // [zBuffer] -#if !defined(TXT_ONLY) && defined(MAPREGSQL) - if(name[1] != '@') { // Remove from database because it is unused. - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i); - if(mysql_query(&mmysql_handle,tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } -#endif - idb_remove(mapreg_db,num); - } - - mapreg_dirty=1; - return 0; -} -/*========================================== - * 文字列型マップ変数の変更 - *------------------------------------------ - */ -int mapreg_setregstr(int num,const char *str) -{ - char *p; -#if !defined(TXT_ONLY) && defined(MAPREGSQL) - char tmp_str[64]; - char tmp_str2[512]; - int i=num>>24; // [zBuffer] - char *name=str_buf+str_data[num&0x00ffffff].str; -#endif - - if( str==NULL || *str==0 ){ -#if !defined(TXT_ONLY) && defined(MAPREGSQL) - if(name[1] != '@') { - sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i); - if(mysql_query(&mmysql_handle,tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } -#endif - idb_remove(mapregstr_db,num); - mapreg_dirty=1; - return 0; - } - p=(char *)aMallocA((strlen(str)+1)*sizeof(char)); - strcpy(p,str); - - if (idb_put(mapregstr_db,num,p)) - ; -#if !defined(TXT_ONLY) && defined(MAPREGSQL) - else { //put returned null, so we must insert. - sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%s')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,jstrescapecpy(tmp_str2,p)); - if(mysql_query(&mmysql_handle,tmp_sql)){ - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } -#endif - mapreg_dirty=1; - return 0; -} - -/*========================================== - * 永続的マップ変数の読み込み - *------------------------------------------ - */ -static int script_load_mapreg(void) -{ -#if defined(TXT_ONLY) || !defined(MAPREGSQL) - FILE *fp; - char line[1024]; - - if( (fp=fopen(mapreg_txt,"rt"))==NULL ) - return -1; - - while(fgets(line,sizeof(line),fp)){ - char buf1[256],buf2[1024],*p; - int n,v,s,i; - if( sscanf(line,"%255[^,],%d\t%n",buf1,&i,&n)!=2 && - (i=0,sscanf(line,"%[^\t]\t%n",buf1,&n)!=1) ) - continue; - if( buf1[strlen(buf1)-1]=='$' ){ - if( sscanf(line+n,"%[^\n\r]",buf2)!=1 ){ - ShowError("%s: %s broken data !\n",mapreg_txt,buf1); - continue; - } - p=(char *)aMallocA((strlen(buf2) + 1)*sizeof(char)); - strcpy(p,buf2); - s= add_str((unsigned char *) buf1); - idb_put(mapregstr_db,(i<<24)|s,p); - }else{ - if( sscanf(line+n,"%d",&v)!=1 ){ - ShowError("%s: %s broken data !\n",mapreg_txt,buf1); - continue; - } - s= add_str((unsigned char *) buf1); - idb_put(mapreg_db,(i<<24)|s,(void*)v); - } - } - fclose(fp); - mapreg_dirty=0; - return 0; -#else - // SQL mapreg code start [zBuffer] - /* - 0 1 2 - +-------------------------+ - | varname | index | value | - +-------------------------+ - */ - int perfomance = gettick_nocache(); - sprintf(tmp_sql,"SELECT * FROM `%s`",mapregsql_db); - ShowInfo("Querying script_load_mapreg ...\n"); - if(mysql_query(&mmysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - return -1; - } - ShowInfo("Success! Returning results ...\n"); - sql_res = mysql_store_result(&mmysql_handle); - if (sql_res) { - while ((sql_row = mysql_fetch_row(sql_res))) { - char buf1[33], *p = NULL; - int i,v,s; - strcpy(buf1,sql_row[0]); - if( buf1[strlen(buf1)-1]=='$' ){ - i = atoi(sql_row[1]); - p=(char *)aMallocA((strlen(sql_row[2]) + 1)*sizeof(char)); - strcpy(p,sql_row[2]); - s= add_str((unsigned char *) buf1); - idb_put(mapregstr_db,(i<<24)|s,p); - }else{ - s= add_str((unsigned char *) buf1); - v= atoi(sql_row[2]); - i = atoi(sql_row[1]); - idb_put(mapreg_db,(i<<24)|s,(void *)v); - } - } - } - ShowInfo("Freeing results...\n"); - mysql_free_result(sql_res); - mapreg_dirty=0; - perfomance = (gettick_nocache() - perfomance) / 1000; - ShowInfo("SQL Mapreg Loading Completed Under %d Seconds.\n",perfomance); - return 0; -#endif /* TXT_ONLY */ -} -/*========================================== - * 永続的マップ変数の書き込み - *------------------------------------------ - */ -static int script_save_mapreg_intsub(DBKey key,void *data,va_list ap) -{ -#if defined(TXT_ONLY) || !defined(MAPREGSQL) - FILE *fp=va_arg(ap,FILE*); - int num=key.i&0x00ffffff, i=key.i>>24; - char *name=str_buf+str_data[num].str; - if( name[1]!='@' ){ - if(i==0) - fprintf(fp,"%s\t%d\n", name, (int)data); - else - fprintf(fp,"%s,%d\t%d\n", name, i, (int)data); - } - return 0; -#else - int num=key.i&0x00ffffff, i=key.i>>24; // [zBuffer] - char *name=str_buf+str_data[num].str; - if ( name[1] != '@') { - sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%d' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,(int)data,mapregsql_db_varname,name,mapregsql_db_index,i); - if(mysql_query(&mmysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - return 0; -#endif -} -static int script_save_mapreg_strsub(DBKey key,void *data,va_list ap) -{ -#if defined(TXT_ONLY) || !defined(MAPREGSQL) - FILE *fp=va_arg(ap,FILE*); - int num=key.i&0x00ffffff, i=key.i>>24; - char *name=str_buf+str_data[num].str; - if( name[1]!='@' ){ - if(i==0) - fprintf(fp,"%s\t%s\n", name, (char *)data); - else - fprintf(fp,"%s,%d\t%s\n", name, i, (char *)data); - } - return 0; -#else - char tmp_str2[512]; - int num=key.i&0x00ffffff, i=key.i>>24; - char *name=str_buf+str_data[num].str; - if ( name[1] != '@') { - sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%s' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,jstrescapecpy(tmp_str2,(char *)data),mapregsql_db_varname,name,mapregsql_db_index,i); - if(mysql_query(&mmysql_handle, tmp_sql) ) { - ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); - ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); - } - } - return 0; -#endif -} -static int script_save_mapreg(void) -{ -#if defined(TXT_ONLY) || !defined(MAPREGSQL) - FILE *fp; - int lock; - - if( (fp=lock_fopen(mapreg_txt,&lock))==NULL ) { - ShowError("script_save_mapreg: Unable to lock-open file [%s]\n",mapreg_txt); - return -1; - } - mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub,fp); - mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub,fp); - lock_fclose(fp,mapreg_txt,&lock); -#else - int perfomance = gettick_nocache(); - mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub); // [zBuffer] - mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub); - perfomance = (gettick_nocache() - perfomance) / 1000; - ShowInfo("Mapreg saved in %d seconds.\n", perfomance); -#endif - mapreg_dirty=0; - return 0; -} -static int script_autosave_mapreg(int tid,unsigned int tick,int id,int data) -{ - if(mapreg_dirty) - if (script_save_mapreg() == -1) - ShowError("Failed to save the mapreg data!\n"); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -static int set_posword(char *p) -{ - char* np,* str[15]; - int i=0; - for(i=0;i<11;i++) { - if((np=strchr(p,','))!=NULL) { - str[i]=p; - *np=0; - p=np+1; - } else { - str[i]=p; - p+=strlen(p); - } - if(str[i]) - strcpy(pos[i],str[i]); - } - return 0; -} - -int script_config_read_sub(char *cfgName) -{ - int i; - char line[1024],w1[1024],w2[1024]; - FILE *fp; - - - fp = fopen(cfgName, "r"); - if (fp == NULL) { - ShowError("file not found: [%s]\n", cfgName); - return 1; - } - while (fgets(line, sizeof(line) - 1, fp)) { - if (line[0] == '/' && line[1] == '/') - continue; - i = sscanf(line,"%[^:]: %[^\r\n]",w1,w2); - if (i != 2) - continue; - if(strcmpi(w1,"refine_posword")==0) { - set_posword(w2); - } - else if(strcmpi(w1,"verbose_mode")==0) { - script_config.verbose_mode = battle_config_switch(w2); - } - else if(strcmpi(w1,"warn_func_no_comma")==0) { - script_config.warn_func_no_comma = battle_config_switch(w2); - } - else if(strcmpi(w1,"warn_cmd_no_comma")==0) { - script_config.warn_cmd_no_comma = battle_config_switch(w2); - } - else if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) { - script_config.warn_func_mismatch_paramnum = battle_config_switch(w2); - } - else if(strcmpi(w1,"warn_cmd_mismatch_paramnum")==0) { - script_config.warn_cmd_mismatch_paramnum = battle_config_switch(w2); - } - else if(strcmpi(w1,"check_cmdcount")==0) { - script_config.check_cmdcount = battle_config_switch(w2); - } - else if(strcmpi(w1,"check_gotocount")==0) { - script_config.check_gotocount = battle_config_switch(w2); - } - else if(strcmpi(w1,"event_script_type")==0) { - script_config.event_script_type = battle_config_switch(w2); - } - else if(strcmpi(w1,"event_requires_trigger")==0) { - script_config.event_requires_trigger = battle_config_switch(w2); - } - else if(strcmpi(w1,"die_event_name")==0) { - strncpy(script_config.die_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.die_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.die_event_name); - } - else if(strcmpi(w1,"kill_pc_event_name")==0) { - strncpy(script_config.kill_pc_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.kill_pc_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_pc_event_name); - } - else if(strcmpi(w1,"kill_mob_event_name")==0) { - strncpy(script_config.kill_mob_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.kill_mob_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_mob_event_name); - } - else if(strcmpi(w1,"login_event_name")==0) { - strncpy(script_config.login_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.login_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.login_event_name); - } - else if(strcmpi(w1,"logout_event_name")==0) { - strncpy(script_config.logout_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.logout_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.logout_event_name); - } - else if(strcmpi(w1,"loadmap_event_name")==0) { - strncpy(script_config.loadmap_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.loadmap_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.loadmap_event_name); - } - else if(strcmpi(w1,"baselvup_event_name")==0) { - strncpy(script_config.baselvup_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.baselvup_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.baselvup_event_name); - } - else if(strcmpi(w1,"joblvup_event_name")==0) { - strncpy(script_config.joblvup_event_name, w2, NAME_LENGTH-1); - if (strlen(script_config.joblvup_event_name) != strlen(w2)) - ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.joblvup_event_name); - } - else if(strcmpi(w1,"import")==0){ - script_config_read_sub(w2); - } - } - fclose(fp); - - return 0; -} - -int script_config_read(char *cfgName) -{ //Script related variables should be initialized once! [Skotlex] - - memset (&script_config, 0, sizeof(script_config)); - script_config.verbose_mode = 0; - script_config.warn_func_no_comma = 1; - script_config.warn_cmd_no_comma = 1; - script_config.warn_func_mismatch_paramnum = 1; - script_config.warn_cmd_mismatch_paramnum = 1; - script_config.check_cmdcount = 65535; - script_config.check_gotocount = 2048; - - script_config.event_script_type = 0; - script_config.event_requires_trigger = 1; - - return script_config_read_sub(cfgName); -} - -static int do_final_userfunc_sub (DBKey key,void *data,va_list ap){ - struct script_code *code = (struct script_code *)data; - if(code){ - script_free_vars( &code->script_vars ); - aFree( code->script_buf ); - } - return 0; -} - -/*========================================== - * 終了 - *------------------------------------------ - */ -int do_final_script() -{ - if(mapreg_dirty>=0) - script_save_mapreg(); - - mapreg_db->destroy(mapreg_db,NULL); - mapregstr_db->destroy(mapregstr_db,NULL); - scriptlabel_db->destroy(scriptlabel_db,NULL); - userfunc_db->destroy(userfunc_db,do_final_userfunc_sub); - if(sleep_db) { - struct linkdb_node *n = (struct linkdb_node *)sleep_db; - while(n) { - struct script_state *st = (struct script_state *)n->data; - script_free_stack(st->stack); - free(st); - n = n->next; - } - linkdb_final(&sleep_db); - } - - if (str_data) - aFree(str_data); - if (str_buf) - aFree(str_buf); - - return 0; -} -/*========================================== - * 初期化 - *------------------------------------------ - */ -int do_init_script() -{ - mapreg_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); - mapregstr_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); - userfunc_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_BOTH,50); - scriptlabel_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_ALLOW_NULL_DATA,50); - - script_load_mapreg(); - - add_timer_func_list(script_autosave_mapreg,"script_autosave_mapreg"); - add_timer_interval(gettick()+MAPREG_AUTOSAVE_INTERVAL, - script_autosave_mapreg,0,0,MAPREG_AUTOSAVE_INTERVAL); - - return 0; -} - -int script_reload() -{ - if(mapreg_dirty>=0) - script_save_mapreg(); - - mapreg_db->clear(mapreg_db, NULL); - mapregstr_db->clear(mapregstr_db, NULL); - userfunc_db->clear(userfunc_db,do_final_userfunc_sub); - scriptlabel_db->clear(scriptlabel_db, NULL); - - if(sleep_db) { - struct linkdb_node *n = (struct linkdb_node *)sleep_db; - while(n) { - struct script_state *st = (struct script_state *)n->data; - script_free_stack(st->stack); - free(st); - n = n->next; - } - linkdb_final(&sleep_db); - } - - script_load_mapreg(); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +//#define DEBUG_FUNCIN +//#define DEBUG_DISP +//#define DEBUG_RUN + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +#include + +#include "../common/socket.h" +#include "../common/timer.h" +#include "../common/malloc.h" +#include "../common/lock.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" +#include "../common/strlib.h" + +#include "map.h" +#include "clif.h" +#include "chrif.h" +#include "itemdb.h" +#include "pc.h" +#include "status.h" +#include "script.h" +#include "storage.h" +#include "mob.h" +#include "npc.h" +#include "pet.h" +#include "intif.h" +#include "skill.h" +#include "chat.h" +#include "battle.h" +#include "party.h" +#include "guild.h" +#include "atcommand.h" +#include "charcommand.h" +#include "log.h" +#include "unit.h" +#include "irc.h" + +#define SCRIPT_BLOCK_SIZE 256 + +#define FETCH(n, t) \ + if(st->end>st->start+(n)) \ + (t)=conv_num(st,&(st->stack->stack_data[st->start+(n)])); + +enum { LABEL_NEXTLINE=1,LABEL_START }; +static unsigned char * script_buf = NULL; +static int script_pos,script_size; + +char *str_buf; +int str_pos,str_size; +static struct str_data_struct { + int type; + int str; + int backpatch; + int label; + int (*func)(struct script_state *); + int val; + int next; +} *str_data = NULL; +int str_num=LABEL_START,str_data_size; +int str_hash[16]; + +static struct dbt *mapreg_db=NULL; +static struct dbt *mapregstr_db=NULL; +static int mapreg_dirty=-1; +char mapreg_txt[256]="save/mapreg.txt"; +#define MAPREG_AUTOSAVE_INTERVAL (10*1000) + +static struct dbt *scriptlabel_db=NULL; +static struct dbt *userfunc_db=NULL; + +struct dbt* script_get_label_db(){ return scriptlabel_db; } +struct dbt* script_get_userfunc_db(){ return userfunc_db; } + +static char pos[11][100] = {"頭","体","左手","右手","ローブ","靴","アクセサリー1","アクセサリー2","頭2","頭3","装着していない"}; + +struct Script_Config script_config; + +static int parse_cmd; + +// for advanced scripting support ( nested if, switch, while, for, do-while, function, etc ) +// [Eoe / jA 1080, 1081, 1094, 1164] +enum { TYPE_NULL = 0 , TYPE_IF , TYPE_SWITCH , TYPE_WHILE , TYPE_FOR , TYPE_DO , TYPE_USERFUNC}; +static struct { + struct { + int type; + int index; + int count; + int flag; + } curly[256]; // 右カッコの情報 + int curly_count; // 右カッコの数 + int index; // スクリプト内で使用した構文の数 +} syntax; +unsigned char* parse_curly_close(unsigned char *p); +unsigned char* parse_syntax_close(unsigned char *p); +unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag); +unsigned char* parse_syntax(unsigned char *p); +static int parse_syntax_for_flag = 0; + +extern int current_equip_item_index; //for New CARS Scripts. It contains Inventory Index of the EQUIP_SCRIPT caller item. [Lupus] +int potion_flag=0; //For use on Alchemist improved potions/Potion Pitcher. [Skotlex] +int potion_hp=0, potion_per_hp=0, potion_sp=0, potion_per_sp=0; +int potion_target=0; + +#if !defined(TXT_ONLY) && defined(MAPREGSQL) +// [zBuffer] SQL Mapreg Saving/Loading Database Declaration +char mapregsql_db[32] = "mapreg"; +char mapregsql_db_varname[32] = "varname"; +char mapregsql_db_index[32] = "index"; +char mapregsql_db_value[32] = "value"; +char tmp_sql[65535]; +// -------------------------------------------------------- +#endif + +static struct linkdb_node *sleep_db; +#define not_server_variable(prefix) (prefix != '$' && prefix != '.') + +/*========================================== + * ローカルプロトタイプ宣言 (必要な物のみ) + *------------------------------------------ + */ +unsigned char* parse_subexpr(unsigned char *,int); +#ifndef TXT_ONLY +int buildin_query_sql(struct script_state *st); +int buildin_escape_sql(struct script_state *st); +#endif +int buildin_atoi(struct script_state *st); +int buildin_axtoi(struct script_state *st); +int buildin_mes(struct script_state *st); +int buildin_goto(struct script_state *st); +int buildin_callsub(struct script_state *st); +int buildin_callfunc(struct script_state *st); +int buildin_return(struct script_state *st); +int buildin_getarg(struct script_state *st); +int buildin_next(struct script_state *st); +int buildin_close(struct script_state *st); +int buildin_close2(struct script_state *st); +int buildin_menu(struct script_state *st); +int buildin_rand(struct script_state *st); +int buildin_warp(struct script_state *st); +int buildin_areawarp(struct script_state *st); +int buildin_warpchar(struct script_state *st); // [LuzZza] +int buildin_warpparty(struct script_state *st); //[Fredzilla] +int buildin_warpguild(struct script_state *st); //[Fredzilla] +int buildin_heal(struct script_state *st); +int buildin_itemheal(struct script_state *st); +int buildin_percentheal(struct script_state *st); +int buildin_jobchange(struct script_state *st); +int buildin_input(struct script_state *st); +int buildin_setlook(struct script_state *st); +int buildin_set(struct script_state *st); +int buildin_setarray(struct script_state *st); +int buildin_cleararray(struct script_state *st); +int buildin_copyarray(struct script_state *st); +int buildin_getarraysize(struct script_state *st); +int buildin_deletearray(struct script_state *st); +int buildin_getelementofarray(struct script_state *st); +int buildin_getitem(struct script_state *st); +int buildin_getitem2(struct script_state *st); +int buildin_getnameditem(struct script_state *st); +int buildin_grouprandomitem(struct script_state *st); +int buildin_makeitem(struct script_state *st); +int buildin_delitem(struct script_state *st); +int buildin_delitem2(struct script_state *st); +int buildin_enableitemuse(struct script_state *st); +int buildin_disableitemuse(struct script_state *st); +int buildin_viewpoint(struct script_state *st); +int buildin_countitem(struct script_state *st); +int buildin_countitem2(struct script_state *st); +int buildin_checkweight(struct script_state *st); +int buildin_readparam(struct script_state *st); +int buildin_getcharid(struct script_state *st); +int buildin_getpartyname(struct script_state *st); +int buildin_getpartymember(struct script_state *st); +int buildin_getguildname(struct script_state *st); +int buildin_getguildmaster(struct script_state *st); +int buildin_getguildmasterid(struct script_state *st); +int buildin_strcharinfo(struct script_state *st); +int buildin_getequipid(struct script_state *st); +int buildin_getequipname(struct script_state *st); +int buildin_getbrokenid(struct script_state *st); // [Valaris] +int buildin_repair(struct script_state *st); // [Valaris] +int buildin_getequipisequiped(struct script_state *st); +int buildin_getequipisenableref(struct script_state *st); +int buildin_getequipisidentify(struct script_state *st); +int buildin_getequiprefinerycnt(struct script_state *st); +int buildin_getequipweaponlv(struct script_state *st); +int buildin_getequippercentrefinery(struct script_state *st); +int buildin_successrefitem(struct script_state *st); +int buildin_failedrefitem(struct script_state *st); +int buildin_cutin(struct script_state *st); +int buildin_cutincard(struct script_state *st); +int buildin_statusup(struct script_state *st); +int buildin_statusup2(struct script_state *st); +int buildin_bonus(struct script_state *st); +int buildin_bonus2(struct script_state *st); +int buildin_bonus3(struct script_state *st); +int buildin_bonus4(struct script_state *st); +int buildin_skill(struct script_state *st); +int buildin_addtoskill(struct script_state *st); // [Valaris] +int buildin_guildskill(struct script_state *st); +int buildin_getskilllv(struct script_state *st); +int buildin_getgdskilllv(struct script_state *st); +int buildin_basicskillcheck(struct script_state *st); +int buildin_getgmlevel(struct script_state *st); +int buildin_end(struct script_state *st); +int buildin_checkoption(struct script_state *st); +int buildin_setoption(struct script_state *st); +int buildin_setcart(struct script_state *st); +int buildin_checkcart(struct script_state *st); // check cart [Valaris] +int buildin_setfalcon(struct script_state *st); +int buildin_checkfalcon(struct script_state *st); // check falcon [Valaris] +int buildin_setriding(struct script_state *st); +int buildin_checkriding(struct script_state *st); // check for pecopeco [Valaris] +int buildin_savepoint(struct script_state *st); +int buildin_gettimetick(struct script_state *st); +int buildin_gettime(struct script_state *st); +int buildin_gettimestr(struct script_state *st); +int buildin_openstorage(struct script_state *st); +int buildin_guildopenstorage(struct script_state *st); +int buildin_itemskill(struct script_state *st); +int buildin_produce(struct script_state *st); +int buildin_monster(struct script_state *st); +int buildin_areamonster(struct script_state *st); +int buildin_killmonster(struct script_state *st); +int buildin_killmonsterall(struct script_state *st); +int buildin_clone(struct script_state *st); +int buildin_doevent(struct script_state *st); +int buildin_donpcevent(struct script_state *st); +int buildin_addtimer(struct script_state *st); +int buildin_deltimer(struct script_state *st); +int buildin_addtimercount(struct script_state *st); +int buildin_initnpctimer(struct script_state *st); +int buildin_stopnpctimer(struct script_state *st); +int buildin_startnpctimer(struct script_state *st); +int buildin_setnpctimer(struct script_state *st); +int buildin_getnpctimer(struct script_state *st); +int buildin_attachnpctimer(struct script_state *st); // [celest] +int buildin_detachnpctimer(struct script_state *st); // [celest] +int buildin_playerattached(struct script_state *st); // [Skotlex] +int buildin_announce(struct script_state *st); +int buildin_mapannounce(struct script_state *st); +int buildin_areaannounce(struct script_state *st); +int buildin_getusers(struct script_state *st); +int buildin_getmapusers(struct script_state *st); +int buildin_getareausers(struct script_state *st); +int buildin_getareadropitem(struct script_state *st); +int buildin_enablenpc(struct script_state *st); +int buildin_disablenpc(struct script_state *st); +int buildin_enablearena(struct script_state *st); // Added by RoVeRT +int buildin_disablearena(struct script_state *st); // Added by RoVeRT +int buildin_hideoffnpc(struct script_state *st); +int buildin_hideonnpc(struct script_state *st); +int buildin_sc_start(struct script_state *st); +int buildin_sc_start2(struct script_state *st); +int buildin_sc_start4(struct script_state *st); +int buildin_sc_end(struct script_state *st); +int buildin_getscrate(struct script_state *st); +int buildin_debugmes(struct script_state *st); +int buildin_catchpet(struct script_state *st); +int buildin_birthpet(struct script_state *st); +int buildin_resetlvl(struct script_state *st); +int buildin_resetstatus(struct script_state *st); +int buildin_resetskill(struct script_state *st); +int buildin_skillpointcount(struct script_state *st); +int buildin_changebase(struct script_state *st); +int buildin_changesex(struct script_state *st); +int buildin_waitingroom(struct script_state *st); +int buildin_delwaitingroom(struct script_state *st); +int buildin_enablewaitingroomevent(struct script_state *st); +int buildin_disablewaitingroomevent(struct script_state *st); +int buildin_getwaitingroomstate(struct script_state *st); +int buildin_warpwaitingpc(struct script_state *st); +int buildin_attachrid(struct script_state *st); +int buildin_detachrid(struct script_state *st); +int buildin_isloggedin(struct script_state *st); +int buildin_setmapflagnosave(struct script_state *st); +int buildin_setmapflag(struct script_state *st); +int buildin_removemapflag(struct script_state *st); +int buildin_pvpon(struct script_state *st); +int buildin_pvpoff(struct script_state *st); +int buildin_gvgon(struct script_state *st); +int buildin_gvgoff(struct script_state *st); +int buildin_emotion(struct script_state *st); +int buildin_maprespawnguildid(struct script_state *st); +int buildin_agitstart(struct script_state *st); // +int buildin_agitend(struct script_state *st); +int buildin_agitcheck(struct script_state *st); // +int buildin_flagemblem(struct script_state *st); // Flag Emblem +int buildin_getcastlename(struct script_state *st); +int buildin_getcastledata(struct script_state *st); +int buildin_setcastledata(struct script_state *st); +int buildin_requestguildinfo(struct script_state *st); +int buildin_getequipcardcnt(struct script_state *st); +int buildin_successremovecards(struct script_state *st); +int buildin_failedremovecards(struct script_state *st); +int buildin_marriage(struct script_state *st); +int buildin_wedding_effect(struct script_state *st); +int buildin_divorce(struct script_state *st); +int buildin_ispartneron(struct script_state *st); // MouseJstr +int buildin_getpartnerid(struct script_state *st); // MouseJstr +int buildin_getchildid(struct script_state *st); // Skotlex +int buildin_getmotherid(struct script_state *st); // Lupus +int buildin_getfatherid(struct script_state *st); // Lupus +int buildin_warppartner(struct script_state *st); // MouseJstr +int buildin_getitemname(struct script_state *st); +int buildin_getitemslots(struct script_state *st); +int buildin_makepet(struct script_state *st); +int buildin_getexp(struct script_state *st); +int buildin_getinventorylist(struct script_state *st); +int buildin_getskilllist(struct script_state *st); +int buildin_clearitem(struct script_state *st); +int buildin_classchange(struct script_state *st); +int buildin_misceffect(struct script_state *st); +int buildin_soundeffect(struct script_state *st); +int buildin_soundeffectall(struct script_state *st); +int buildin_setcastledata(struct script_state *st); +int buildin_mapwarp(struct script_state *st); +int buildin_inittimer(struct script_state *st); +int buildin_stoptimer(struct script_state *st); +int buildin_cmdothernpc(struct script_state *st); +int buildin_mobcount(struct script_state *st); +int buildin_strmobinfo(struct script_state *st); // Script for displaying mob info [Valaris] +int buildin_guardian(struct script_state *st); // Script for displaying mob info [Valaris] +int buildin_guardianinfo(struct script_state *st); // Script for displaying mob info [Valaris] +int buildin_petskillbonus(struct script_state *st); // petskillbonus [Valaris] +int buildin_petrecovery(struct script_state *st); // pet skill for curing status [Valaris] +int buildin_petloot(struct script_state *st); // pet looting [Valaris] +int buildin_petheal(struct script_state *st); // pet healing [Valaris] +//int buildin_petmag(struct script_state *st); // pet magnificat [Valaris] +int buildin_petskillattack(struct script_state *st); // pet skill attacks [Skotlex] +int buildin_petskillattack2(struct script_state *st); // pet skill attacks [Skotlex] +int buildin_petskillsupport(struct script_state *st); // pet support skill [Valaris] +int buildin_skilleffect(struct script_state *st); // skill effects [Celest] +int buildin_npcskilleffect(struct script_state *st); // skill effects for npcs [Valaris] +int buildin_specialeffect(struct script_state *st); // special effect script [Valaris] +int buildin_specialeffect2(struct script_state *st); // special effect script [Valaris] +int buildin_nude(struct script_state *st); // nude [Valaris] +int buildin_atcommand(struct script_state *st); // [MouseJstr] +int buildin_charcommand(struct script_state *st); // [MouseJstr] +int buildin_movenpc(struct script_state *st); // [MouseJstr] +int buildin_message(struct script_state *st); // [MouseJstr] +int buildin_npctalk(struct script_state *st); // [Valaris] +int buildin_hasitems(struct script_state *st); // [Valaris] +int buildin_getlook(struct script_state *st); //Lorky [Lupus] +int buildin_getsavepoint(struct script_state *st); //Lorky [Lupus] +int buildin_npcspeed(struct script_state *st); // [Valaris] +int buildin_npcwalkto(struct script_state *st); // [Valaris] +int buildin_npcstop(struct script_state *st); // [Valaris] +int buildin_getmapxy(struct script_state *st); //get map position for player/npc/pet/mob by Lorky [Lupus] +int buildin_checkoption1(struct script_state *st); // [celest] +int buildin_checkoption2(struct script_state *st); // [celest] +int buildin_guildgetexp(struct script_state *st); // [celest] +int buildin_guildchangegm(struct script_state *st); // [Skotlex] +int buildin_skilluseid(struct script_state *st); // originally by Qamera [celest] +int buildin_skillusepos(struct script_state *st); // originally by Qamera [celest] +int buildin_logmes(struct script_state *st); // [Lupus] +int buildin_summon(struct script_state *st); // [celest] +int buildin_isnight(struct script_state *st); // [celest] +int buildin_isday(struct script_state *st); // [celest] +int buildin_isequipped(struct script_state *st); // [celest] +int buildin_isequippedcnt(struct script_state *st); // [celest] +int buildin_cardscnt(struct script_state *st); // [Lupus] +int buildin_getrefine(struct script_state *st); // [celest] +int buildin_adopt(struct script_state *st); +int buildin_night(struct script_state *st); +int buildin_day(struct script_state *st); +int buildin_getusersname(struct script_state *st); //jA commands added [Lupus] +int buildin_dispbottom(struct script_state *st); +int buildin_recovery(struct script_state *st); +int buildin_getpetinfo(struct script_state *st); +int buildin_checkequipedcard(struct script_state *st); +int buildin_globalmes(struct script_state *st); +int buildin_jump_zero(struct script_state *st); +int buildin_select(struct script_state *st); +int buildin_getmapmobs(struct script_state *st); //jA addition end +int buildin_unequip(struct script_state *st); // unequip [Spectre] +int buildin_getstrlen(struct script_state *st); //strlen [valaris] +int buildin_charisalpha(struct script_state *st);//isalpha [valaris] +int buildin_fakenpcname(struct script_state *st); // [Lance] +int buildin_compare(struct script_state *st); // Lordalfa, to bring strstr to Scripting Engine +int buildin_getiteminfo(struct script_state *st); //[Lupus] returns Items Buy / sell Price, etc info +int buildin_getequipcardid(struct script_state *st); //[Lupus] returns card id from quipped item card slot N +// [zBuffer] List of mathematics commands ---> +int buildin_sqrt(struct script_state *st); +int buildin_pow(struct script_state *st); +int buildin_distance(struct script_state *st); +// <--- [zBuffer] List of mathematics commands +// [zBuffer] List of dynamic var commands ---> +int buildin_getd(struct script_state *st); +int buildin_setd(struct script_state *st); +// <--- [zBuffer] List of dynamic var commands +int buildin_petstat(struct script_state *st); // [Lance] Pet Stat Rq: Dubby +int buildin_callshop(struct script_state *st); // [Skotlex] +int buildin_npcshopitem(struct script_state *st); // [Lance] +int buildin_equip(struct script_state *st); +int buildin_autoequip(struct script_state *st); +int buildin_setbattleflag(struct script_state *st); +int buildin_getbattleflag(struct script_state *st); +// [zBuffer] List of player cont commands ---> +int buildin_rid2name(struct script_state *st); +int buildin_pcwalkxy(struct script_state *st); +int buildin_pctalk(struct script_state *st); +int buildin_pcemote(struct script_state *st); +int buildin_pcfollow(struct script_state *st); +int buildin_pcstopfollow(struct script_state *st); +int buildin_pcblockmove(struct script_state *st); +// <--- [zBuffer] List of player cont commands +// [zBuffer] List of mob control commands ---> +int buildin_spawnmob(struct script_state *st); +int buildin_removemob(struct script_state *st); +int buildin_mobwalk(struct script_state *st); +int buildin_getmobdata(struct script_state *st); +int buildin_setmobdata(struct script_state *st); +int buildin_mobattack(struct script_state *st); +int buildin_mobrandomwalk(struct script_state *st); +int buildin_mobstop(struct script_state *st); +int buildin_mobassist(struct script_state *st); +int buildin_mobtalk(struct script_state *st); +int buildin_mobemote(struct script_state *st); +int buildin_mobattach(struct script_state *st); +// <--- [zBuffer] List of mob control commands +int buildin_sleep(struct script_state *st); +int buildin_sleep2(struct script_state *st); +int buildin_awake(struct script_state *st); +int buildin_getvariableofnpc(struct script_state *st); +void push_val(struct script_stack *stack,int type,int val); +int run_func(struct script_state *st); + +int mapreg_setreg(int num,int val); +int mapreg_setregstr(int num,const char *str); + +int buildin_setitemscript(struct script_state *st); +int buildin_disguise(struct script_state *st); +int buildin_undisguise(struct script_state *st); +int buildin_getmonsterinfo(struct script_state *st); // [Lupus] + +#ifdef PCRE_SUPPORT +int buildin_defpattern(struct script_state *st); // MouseJstr +int buildin_activatepset(struct script_state *st); // MouseJstr +int buildin_deactivatepset(struct script_state *st); // MouseJstr +int buildin_deletepset(struct script_state *st); // MouseJstr +#endif + +struct { + int (*func)(struct script_state *); + char *name; + char *arg; +} buildin_func[]={ + {buildin_axtoi,"axtoi","s"}, +#ifndef TXT_ONLY + {buildin_query_sql, "query_sql", "s*"}, + {buildin_escape_sql, "escape_sql", "s"}, +#endif + {buildin_atoi,"atoi","s"}, + {buildin_mes,"mes","s"}, + {buildin_next,"next",""}, + {buildin_close,"close",""}, + {buildin_close2,"close2",""}, + {buildin_menu,"menu","*"}, + {buildin_goto,"goto","l"}, + {buildin_callsub,"callsub","i*"}, + {buildin_callfunc,"callfunc","s*"}, + {buildin_return,"return","*"}, + {buildin_getarg,"getarg","i"}, + {buildin_jobchange,"jobchange","i*"}, + {buildin_input,"input","*"}, + {buildin_warp,"warp","sii"}, + {buildin_areawarp,"areawarp","siiiisii"}, + {buildin_warpchar,"warpchar","siii"}, // [LuzZza] + {buildin_warpparty,"warpparty","siii"}, // [Fredzilla] + {buildin_warpguild,"warpguild","siii"}, // [Fredzilla] + {buildin_setlook,"setlook","ii"}, + {buildin_set,"set","ii"}, + {buildin_setarray,"setarray","ii*"}, + {buildin_cleararray,"cleararray","iii"}, + {buildin_copyarray,"copyarray","iii"}, + {buildin_getarraysize,"getarraysize","i"}, + {buildin_deletearray,"deletearray","ii"}, + {buildin_getelementofarray,"getelementofarray","ii"}, + {buildin_getitem,"getitem","ii**"}, + {buildin_getitem2,"getitem2","iiiiiiiii*"}, + {buildin_getnameditem,"getnameditem","is"}, + {buildin_grouprandomitem,"groupranditem","i"}, + {buildin_makeitem,"makeitem","iisii"}, + {buildin_delitem,"delitem","ii"}, + {buildin_delitem2,"delitem2","iiiiiiiii"}, + {buildin_enableitemuse,"enable_items",""}, + {buildin_disableitemuse,"disable_items",""}, + {buildin_cutin,"cutin","si"}, + {buildin_cutincard,"cutincard","i"}, + {buildin_viewpoint,"viewpoint","iiiii"}, + {buildin_heal,"heal","ii"}, + {buildin_itemheal,"itemheal","ii"}, + {buildin_percentheal,"percentheal","ii"}, + {buildin_rand,"rand","i*"}, + {buildin_countitem,"countitem","i"}, + {buildin_countitem2,"countitem2","iiiiiiii"}, + {buildin_checkweight,"checkweight","ii"}, + {buildin_readparam,"readparam","i*"}, + {buildin_getcharid,"getcharid","i*"}, + {buildin_getpartyname,"getpartyname","i"}, + {buildin_getpartymember,"getpartymember","i*"}, + {buildin_getguildname,"getguildname","i"}, + {buildin_getguildmaster,"getguildmaster","i"}, + {buildin_getguildmasterid,"getguildmasterid","i"}, + {buildin_strcharinfo,"strcharinfo","i"}, + {buildin_getequipid,"getequipid","i"}, + {buildin_getequipname,"getequipname","i"}, + {buildin_getbrokenid,"getbrokenid","i"}, // [Valaris] + {buildin_repair,"repair","i"}, // [Valaris] + {buildin_getequipisequiped,"getequipisequiped","i"}, + {buildin_getequipisenableref,"getequipisenableref","i"}, + {buildin_getequipisidentify,"getequipisidentify","i"}, + {buildin_getequiprefinerycnt,"getequiprefinerycnt","i"}, + {buildin_getequipweaponlv,"getequipweaponlv","i"}, + {buildin_getequippercentrefinery,"getequippercentrefinery","i"}, + {buildin_successrefitem,"successrefitem","i"}, + {buildin_failedrefitem,"failedrefitem","i"}, + {buildin_statusup,"statusup","i"}, + {buildin_statusup2,"statusup2","ii"}, + {buildin_bonus,"bonus","ii"}, + {buildin_bonus2,"bonus2","iii"}, + {buildin_bonus3,"bonus3","iiii"}, + {buildin_bonus4,"bonus4","iiiii"}, + {buildin_skill,"skill","ii*"}, + {buildin_addtoskill,"addtoskill","ii*"}, // [Valaris] + {buildin_guildskill,"guildskill","ii"}, + {buildin_getskilllv,"getskilllv","i"}, + {buildin_getgdskilllv,"getgdskilllv","ii"}, + {buildin_basicskillcheck,"basicskillcheck","*"}, + {buildin_getgmlevel,"getgmlevel","*"}, + {buildin_end,"end",""}, +// {buildin_end,"break",""}, this might confuse advanced scripting support [Eoe] + {buildin_checkoption,"checkoption","i"}, + {buildin_setoption,"setoption","i*"}, + {buildin_setcart,"setcart",""}, + {buildin_checkcart,"checkcart","*"}, //fixed by Lupus (added '*') + {buildin_setfalcon,"setfalcon",""}, + {buildin_checkfalcon,"checkfalcon","*"}, //fixed by Lupus (fixed wrong pointer, added '*') + {buildin_setriding,"setriding",""}, + {buildin_checkriding,"checkriding","*"}, //fixed by Lupus (fixed wrong pointer, added '*') + {buildin_savepoint,"save","sii"}, + {buildin_savepoint,"savepoint","sii"}, + {buildin_gettimetick,"gettimetick","i"}, + {buildin_gettime,"gettime","i"}, + {buildin_gettimestr,"gettimestr","si"}, + {buildin_openstorage,"openstorage",""}, + {buildin_guildopenstorage,"guildopenstorage","*"}, + {buildin_itemskill,"itemskill","iis"}, + {buildin_produce,"produce","i"}, + {buildin_monster,"monster","siisii*"}, + {buildin_areamonster,"areamonster","siiiisii*"}, + {buildin_killmonster,"killmonster","ss"}, + {buildin_killmonsterall,"killmonsterall","s"}, + {buildin_clone,"clone","siisi*"}, + {buildin_doevent,"doevent","s"}, + {buildin_donpcevent,"donpcevent","s"}, + {buildin_addtimer,"addtimer","is"}, + {buildin_deltimer,"deltimer","s"}, + {buildin_addtimercount,"addtimercount","si"}, + {buildin_initnpctimer,"initnpctimer","*"}, + {buildin_stopnpctimer,"stopnpctimer","*"}, + {buildin_startnpctimer,"startnpctimer","*"}, + {buildin_setnpctimer,"setnpctimer","*"}, + {buildin_getnpctimer,"getnpctimer","i*"}, + {buildin_attachnpctimer,"attachnpctimer","*"}, // attached the player id to the npc timer [Celest] + {buildin_detachnpctimer,"detachnpctimer","*"}, // detached the player id from the npc timer [Celest] + {buildin_playerattached,"playerattached",""}, // returns id of the current attached player. [Skotlex] + {buildin_announce,"announce","si*"}, + {buildin_mapannounce,"mapannounce","ssi*"}, + {buildin_areaannounce,"areaannounce","siiiisi*"}, + {buildin_getusers,"getusers","i"}, + {buildin_getmapusers,"getmapusers","s"}, + {buildin_getareausers,"getareausers","siiii"}, + {buildin_getareadropitem,"getareadropitem","siiiii"}, + {buildin_enablenpc,"enablenpc","s"}, + {buildin_disablenpc,"disablenpc","s"}, + {buildin_enablearena,"enablearena",""}, // Added by RoVeRT + {buildin_disablearena,"disablearena",""}, // Added by RoVeRT + {buildin_hideoffnpc,"hideoffnpc","s"}, + {buildin_hideonnpc,"hideonnpc","s"}, + {buildin_sc_start,"sc_start","iii*"}, + {buildin_sc_start2,"sc_start2","iiii*"}, + {buildin_sc_start4,"sc_start4","iiiiii*"}, + {buildin_sc_end,"sc_end","i"}, + {buildin_getscrate,"getscrate","ii*"}, + {buildin_debugmes,"debugmes","s"}, + {buildin_catchpet,"pet","i"}, + {buildin_birthpet,"bpet",""}, + {buildin_resetlvl,"resetlvl","i"}, + {buildin_resetstatus,"resetstatus",""}, + {buildin_resetskill,"resetskill",""}, + {buildin_skillpointcount,"skillpointcount",""}, + {buildin_changebase,"changebase","i"}, + {buildin_changesex,"changesex",""}, + {buildin_waitingroom,"waitingroom","si*"}, + {buildin_warpwaitingpc,"warpwaitingpc","sii"}, + {buildin_delwaitingroom,"delwaitingroom","*"}, + {buildin_enablewaitingroomevent,"enablewaitingroomevent","*"}, + {buildin_disablewaitingroomevent,"disablewaitingroomevent","*"}, + {buildin_getwaitingroomstate,"getwaitingroomstate","i*"}, + {buildin_warpwaitingpc,"warpwaitingpc","sii*"}, + {buildin_attachrid,"attachrid","i"}, + {buildin_detachrid,"detachrid",""}, + {buildin_isloggedin,"isloggedin","i"}, + {buildin_setmapflagnosave,"setmapflagnosave","ssii"}, + {buildin_setmapflag,"setmapflag","si*"}, + {buildin_removemapflag,"removemapflag","si"}, + {buildin_pvpon,"pvpon","s"}, + {buildin_pvpoff,"pvpoff","s"}, + {buildin_gvgon,"gvgon","s"}, + {buildin_gvgoff,"gvgoff","s"}, + {buildin_emotion,"emotion","i*"}, + {buildin_maprespawnguildid,"maprespawnguildid","sii"}, + {buildin_agitstart,"agitstart",""}, // + {buildin_agitend,"agitend",""}, + {buildin_agitcheck,"agitcheck","i"}, // + {buildin_flagemblem,"flagemblem","i"}, // Flag Emblem + {buildin_getcastlename,"getcastlename","s"}, + {buildin_getcastledata,"getcastledata","si*"}, + {buildin_setcastledata,"setcastledata","sii"}, + {buildin_requestguildinfo,"requestguildinfo","i*"}, + {buildin_getequipcardcnt,"getequipcardcnt","i"}, + {buildin_successremovecards,"successremovecards","i"}, + {buildin_failedremovecards,"failedremovecards","ii"}, + {buildin_marriage,"marriage","s"}, + {buildin_wedding_effect,"wedding",""}, + {buildin_divorce,"divorce",""}, + {buildin_ispartneron,"ispartneron",""}, + {buildin_getpartnerid,"getpartnerid",""}, + {buildin_getchildid,"getchildid",""}, + {buildin_getmotherid,"getmotherid",""}, + {buildin_getfatherid,"getfatherid",""}, + {buildin_warppartner,"warppartner","sii"}, + {buildin_getitemname,"getitemname","i"}, + {buildin_getitemslots,"getitemslots","i"}, + {buildin_makepet,"makepet","i"}, + {buildin_getexp,"getexp","ii"}, + {buildin_getinventorylist,"getinventorylist",""}, + {buildin_getskilllist,"getskilllist",""}, + {buildin_clearitem,"clearitem",""}, + {buildin_classchange,"classchange","ii"}, + {buildin_misceffect,"misceffect","i"}, + {buildin_soundeffect,"soundeffect","si"}, + {buildin_soundeffectall,"soundeffectall","si*"}, // SoundEffectAll [Codemaster] + {buildin_strmobinfo,"strmobinfo","ii"}, // display mob data [Valaris] + {buildin_guardian,"guardian","siisii*i"}, // summon guardians + {buildin_guardianinfo,"guardianinfo","i"}, // display guardian data [Valaris] + {buildin_petskillbonus,"petskillbonus","iiii"}, // [Valaris] + {buildin_petrecovery,"petrecovery","ii"}, // [Valaris] + {buildin_petloot,"petloot","i"}, // [Valaris] + {buildin_petheal,"petheal","iiii"}, // [Valaris] +// {buildin_petmag,"petmag","iiii"}, // [Valaris] + {buildin_petskillattack,"petskillattack","iiii"}, // [Skotlex] + {buildin_petskillattack2,"petskillattack2","iiiii"}, // [Valaris] + {buildin_petskillsupport,"petskillsupport","iiiii"}, // [Skotlex] + {buildin_skilleffect,"skilleffect","ii"}, // skill effect [Celest] + {buildin_npcskilleffect,"npcskilleffect","iiii"}, // npc skill effect [Valaris] + {buildin_specialeffect,"specialeffect","i"}, // npc skill effect [Valaris] + {buildin_specialeffect2,"specialeffect2","i"}, // skill effect on players[Valaris] + {buildin_nude,"nude",""}, // nude command [Valaris] + {buildin_mapwarp,"mapwarp","ssii"}, // Added by RoVeRT + {buildin_inittimer,"inittimer",""}, + {buildin_stoptimer,"stoptimer",""}, + {buildin_cmdothernpc,"cmdothernpc","ss"}, + {buildin_atcommand,"atcommand","*"}, // [MouseJstr] + {buildin_charcommand,"charcommand","*"}, // [MouseJstr] +// {buildin_movenpc,"movenpc","siis"}, // [MouseJstr] + {buildin_message,"message","s*"}, // [MouseJstr] + {buildin_npctalk,"npctalk","*"}, // [Valaris] + {buildin_hasitems,"hasitems","*"}, // [Valaris] + {buildin_mobcount,"mobcount","ss"}, + {buildin_getlook,"getlook","i"}, + {buildin_getsavepoint,"getsavepoint","i"}, + {buildin_npcspeed,"npcspeed","i"}, // [Valaris] + {buildin_npcwalkto,"npcwalkto","ii"}, // [Valaris] + {buildin_npcstop,"npcstop",""}, // [Valaris] + {buildin_getmapxy,"getmapxy","siii*"}, //by Lorky [Lupus] + {buildin_checkoption1,"checkoption1","i"}, + {buildin_checkoption2,"checkoption2","i"}, + {buildin_guildgetexp,"guildgetexp","i"}, + {buildin_guildchangegm,"guildchangegm","is"}, + {buildin_skilluseid,"skilluseid","ii"}, // originally by Qamera [Celest] + {buildin_skilluseid,"doskill","ii"}, // since a lot of scripts would already use 'doskill'... + {buildin_skillusepos,"skillusepos","iiii"}, // [Celest] + {buildin_logmes,"logmes","s"}, //this command actls as MES but rints info into LOG file either SQL/TXT [Lupus] + {buildin_summon,"summon","si*"}, // summons a slave monster [Celest] + {buildin_isnight,"isnight",""}, // check whether it is night time [Celest] + {buildin_isday,"isday",""}, // check whether it is day time [Celest] + {buildin_isequipped,"isequipped","i*"}, // check whether another item/card has been equipped [Celest] + {buildin_isequippedcnt,"isequippedcnt","i*"}, // check how many items/cards are being equipped [Celest] + {buildin_cardscnt,"cardscnt","i*"}, // check how many items/cards are being equipped in the same arm [Lupus] + {buildin_getrefine,"getrefine","*"}, // returns the refined number of the current item, or an item with index specified [celest] + {buildin_adopt,"adopt","sss"}, // allows 2 parents to adopt a child + {buildin_night,"night",""}, // sets the server to night time + {buildin_day,"day",""}, // sets the server to day time +#ifdef PCRE_SUPPORT + {buildin_defpattern, "defpattern", "iss"}, // Define pattern to listen for [MouseJstr] + {buildin_activatepset, "activatepset", "i"}, // Activate a pattern set [MouseJstr] + {buildin_deactivatepset, "deactivatepset", "i"}, // Deactive a pattern set [MouseJstr] + {buildin_deletepset, "deletepset", "i"}, // Delete a pattern set [MouseJstr] +#endif + {buildin_dispbottom,"dispbottom","s"}, //added from jA [Lupus] + {buildin_getusersname,"getusersname","*"}, + {buildin_recovery,"recovery",""}, + {buildin_getpetinfo,"getpetinfo","i"}, + {buildin_checkequipedcard,"checkequipedcard","i"}, + {buildin_jump_zero,"jump_zero","ii"}, //for future jA script compatibility + {buildin_select,"select","*"}, //for future jA script compatibility + {buildin_globalmes,"globalmes","s*"}, + {buildin_getmapmobs,"getmapmobs","s"}, //end jA addition + {buildin_unequip,"unequip","i"}, // unequip command [Spectre] + {buildin_getstrlen,"getstrlen","s"}, //strlen [Valaris] + {buildin_charisalpha,"charisalpha","si"}, //isalpha [Valaris] + {buildin_fakenpcname,"fakenpcname","ssi"}, // [Lance] + {buildin_compare,"compare","ss"}, // Lordalfa - To bring strstr to scripting Engine. + {buildin_getiteminfo,"getiteminfo","ii"}, //[Lupus] returns Items Buy / sell Price, etc info + {buildin_getequipcardid,"getequipcardid","ii"}, //[Lupus] returns CARD ID or other info from CARD slot N of equipped item + // [zBuffer] List of mathematics commands ---> + {buildin_sqrt,"sqrt","i"}, + {buildin_pow,"pow","ii"}, + {buildin_distance,"distance","iiii"}, + // <--- [zBuffer] List of mathematics commands + // [zBuffer] List of dynamic var commands ---> + {buildin_getd,"getd","*"}, + {buildin_setd,"setd","*"}, + // <--- [zBuffer] List of dynamic var commands + {buildin_petstat,"petstat","i"}, + {buildin_callshop,"callshop","si"}, // [Skotlex] + {buildin_npcshopitem,"npcshopitem","*"}, // [Lance] + {buildin_equip,"equip","i"}, + {buildin_autoequip,"autoequip","ii"}, + {buildin_setbattleflag,"setbattleflag","ss"}, + {buildin_getbattleflag,"getbattleflag","s"}, + {buildin_setitemscript,"setitemscript","is"}, //Set NEW item bonus script. Lupus + {buildin_disguise,"disguise","i"}, //disguise player. Lupus + {buildin_undisguise,"undisguise","i"}, //undisguise player. Lupus + {buildin_getmonsterinfo,"getmonsterinfo","ii"}, //Lupus + // [zBuffer] List of player cont commands ---> + {buildin_rid2name,"rid2name","i"}, + {buildin_pcwalkxy,"pcwalkxy","iii"}, + {buildin_pctalk,"pctalk","is"}, + {buildin_pcemote,"pcemote","ii"}, + {buildin_pcfollow,"pcfollow","ii"}, + {buildin_pcstopfollow,"pcstopfollow","i"}, + {buildin_pcblockmove,"pcblockmove","ii"}, + // <--- [zBuffer] List of player cont commands + // [zBuffer] List of mob control commands ---> + {buildin_spawnmob,"spawnmob","*"}, + {buildin_removemob,"removemob","i"}, + {buildin_mobwalk,"mobwalk","i*"}, + {buildin_mobrandomwalk,"mobrandomwalk","ii"}, + {buildin_getmobdata,"getmobdata","i*"}, + {buildin_setmobdata,"setmobdata","iii"}, + {buildin_mobattack,"mobattack","i*"}, + {buildin_mobstop,"mobstop","i"}, + {buildin_mobassist,"mobassist","i*"}, + {buildin_mobtalk,"mobtalk","is"}, + {buildin_mobemote,"mobemote","ii"}, + {buildin_mobattach,"mobattach","i*"}, +// <--- [zBuffer] List of mob control commands +{buildin_sleep,"sleep","i"}, + {buildin_sleep2,"sleep2","i"}, + {buildin_awake,"awake","s"}, + {buildin_getvariableofnpc,"getvariableofnpc","is"}, + {NULL,NULL,NULL}, +}; + +enum { + C_NOP,C_POS,C_INT,C_PARAM,C_FUNC,C_STR,C_CONSTSTR,C_ARG, + C_NAME,C_EOL, C_RETINFO, + C_USERFUNC, C_USERFUNC_POS, // user defined functions + + C_LOR,C_LAND,C_LE,C_LT,C_GE,C_GT,C_EQ,C_NE, //operator + C_XOR,C_OR,C_AND,C_ADD,C_SUB,C_MUL,C_DIV,C_MOD,C_NEG,C_LNOT,C_NOT,C_R_SHIFT,C_L_SHIFT +}; + +//Reports on the console the src of an script error. +static void report_src(struct script_state *st) { + struct block_list *bl; + if (!st->oid) return; //Can't report source. + bl = map_id2bl(st->oid); + if (!bl) return; + switch (bl->type) { + case BL_NPC: + if (bl->m >=0) + ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y); + else + ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name); + + break; + default: + if (bl->m >=0) + ShowDebug("Source (Non-NPC): type %d at %s (%d,%d)\n", bl->type, map[bl->m].name, bl->x, bl->y); + else + ShowDebug("Source (Non-NPC): type %d (invisible/not on a map)\n", bl->type); + break; + } +} + +static void check_event(struct script_state *st, unsigned char *event){ + if(event != NULL && event[0] != '\0' && !strstr(event,"::")){ + ShowError("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n",event); + report_src(st); + } + return; +} +/*========================================== + * 文字列のハッシュを計算 + *------------------------------------------ + */ +static int calc_hash(const unsigned char *p) +{ + int h=0; + while(*p){ + h=(h<<1)+(h>>3)+(h>>5)+(h>>8); + h+=*p++; + } + return h&15; +} + +/*========================================== + * str_dataの中に名前があるか検索する + *------------------------------------------ + */ +// 既存のであれば番号、無ければ-1 +static int search_str(const unsigned char *p) +{ + int i; + i=str_hash[calc_hash(p)]; + while(i){ + if(strcmp(str_buf+str_data[i].str,(char *) p)==0){ + return i; + } + i=str_data[i].next; + } + return -1; +} + +/*========================================== + * str_dataに名前を登録 + *------------------------------------------ + */ +// 既存のであれば番号、無ければ登録して新規番号 +static int add_str(const unsigned char *p) +{ + int i; + char *lowcase; + + lowcase=aStrdup((char *) p); + for(i=0;lowcase[i];i++) + lowcase[i]=tolower(lowcase[i]); + if((i=search_str((unsigned char *) lowcase))>=0){ + aFree(lowcase); + return i; + } + aFree(lowcase); + + i=calc_hash(p); + if(str_hash[i]==0){ + str_hash[i]=str_num; + } else { + i=str_hash[i]; + for(;;){ + if(strcmp(str_buf+str_data[i].str,(char *) p)==0){ + return i; + } + if(str_data[i].next==0) + break; + i=str_data[i].next; + } + str_data[i].next=str_num; + } + if(str_num>=str_data_size){ + str_data_size+=128; + str_data=(struct str_data_struct *) aRealloc(str_data,sizeof(str_data[0])*str_data_size); + memset(str_data + (str_data_size - 128), '\0', 128); + } + while(str_pos+(int)strlen((char *) p)+1>=str_size){ + str_size+=256; + str_buf=(char *)aRealloc(str_buf,str_size); + memset(str_buf + (str_size - 256), '\0', 256); + } + strcpy(str_buf+str_pos, (char *) p); + str_data[str_num].type=C_NOP; + str_data[str_num].str=str_pos; + str_data[str_num].next=0; + str_data[str_num].func=NULL; + str_data[str_num].backpatch=-1; + str_data[str_num].label=-1; + str_pos+=(int)strlen( (char *) p)+1; + return str_num++; +} + + +/*========================================== + * スクリプトバッファサイズの確認と拡張 + *------------------------------------------ + */ +static void check_script_buf(int size) +{ + if(script_pos+size>=script_size){ + script_size+=SCRIPT_BLOCK_SIZE; + script_buf=(unsigned char *)aRealloc(script_buf,script_size); + memset(script_buf + script_size - SCRIPT_BLOCK_SIZE, '\0', + SCRIPT_BLOCK_SIZE); + } +} + +/*========================================== + * スクリプトバッファに1バイト書き込む + *------------------------------------------ + */ +static void add_scriptb(int a) +{ + check_script_buf(1); + script_buf[script_pos++]=a; +} + +/*========================================== + * スクリプトバッファにデータタイプを書き込む + *------------------------------------------ + */ +static void add_scriptc(int a) +{ + while(a>=0x40){ + add_scriptb((a&0x3f)|0x40); + a=(a-0x40)>>6; + } + add_scriptb(a&0x3f); +} + +/*========================================== + * スクリプトバッファに整数を書き込む + *------------------------------------------ + */ +static void add_scripti(int a) +{ + while(a>=0x40){ + add_scriptb(a|0xc0); + a=(a-0x40)>>6; + } + add_scriptb(a|0x80); +} + +/*========================================== + * スクリプトバッファにラベル/変数/関数を書き込む + *------------------------------------------ + */ +// 最大16Mまで +static void add_scriptl(int l) +{ + int backpatch = str_data[l].backpatch; + + switch(str_data[l].type){ + case C_POS: + case C_USERFUNC_POS: + add_scriptc(C_POS); + add_scriptb(str_data[l].label); + add_scriptb(str_data[l].label>>8); + add_scriptb(str_data[l].label>>16); + break; + case C_NOP: + case C_USERFUNC: + // ラベルの可能性があるのでbackpatch用データ埋め込み + add_scriptc(C_NAME); + str_data[l].backpatch=script_pos; + add_scriptb(backpatch); + add_scriptb(backpatch>>8); + add_scriptb(backpatch>>16); + break; + case C_INT: + add_scripti(str_data[l].val); + break; + default: + // もう他の用途と確定してるので数字をそのまま + add_scriptc(C_NAME); + add_scriptb(l); + add_scriptb(l>>8); + add_scriptb(l>>16); + break; + } +} + +/*========================================== + * ラベルを解決する + *------------------------------------------ + */ +void set_label(int l,int pos) +{ + int i,next; + + str_data[l].type=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); + str_data[l].label=pos; + for(i=str_data[l].backpatch;i>=0 && i!=0x00ffffff;){ + next=(*(int*)(script_buf+i)) & 0x00ffffff; + script_buf[i-1]=(str_data[l].type == C_USERFUNC ? C_USERFUNC_POS : C_POS); + script_buf[i]=pos; + script_buf[i+1]=pos>>8; + script_buf[i+2]=pos>>16; + i=next; + } +} + +/*========================================== + * スペース/コメント読み飛ばし + *------------------------------------------ + */ +static unsigned char *skip_space(unsigned char *p) +{ + while(1){ + while(isspace(*p)) + p++; + if(p[0]=='/' && p[1]=='/'){ + while(*p && *p!='\n') + p++; + } else if(p[0]=='/' && p[1]=='*'){ + p++; + while(*p && (p[-1]!='*' || p[0]!='/')) + p++; + if(*p) p++; + } else + break; + } + return p; +} + +/*========================================== + * 1単語スキップ + *------------------------------------------ + */ +static unsigned char *skip_word(unsigned char *p) +{ + // prefix + if(*p=='.') p++; + if(*p=='$') p++; // MAP鯖内共有変数用 + if(*p=='@') p++; // 一時的変数用(like weiss) + if(*p=='#') p++; // account変数用 + if(*p=='#') p++; // ワールドaccount変数用 + + while(isalnum(*p)||*p=='_'|| *p>=0x81) + if(*p>=0x81 && p[1]){ + p+=2; + } else + p++; + + // postfix + if(*p=='$') p++; // 文字列変数 + + return p; +} + +static unsigned char *startptr; +static int startline; + +/*========================================== + * エラーメッセージ出力 + *------------------------------------------ + */ +static void disp_error_message(const char *mes,const unsigned char *pos) +{ + int line,c=0,i; + unsigned char *p,*linestart,*lineend; + + for(line=startline,p=startptr;p && *p;line++){ + linestart=p; + lineend=(unsigned char *) strchr((char *) p,'\n'); + if(lineend){ + c=*lineend; + *lineend=0; + } + if(lineend==NULL || pos getelementofarray(name,i) ) + add_scriptl(search_str((unsigned char *) "getelementofarray")); + add_scriptc(C_ARG); + add_scriptl(l); + p=parse_subexpr(p+1,-1); + p=skip_space(p); + if((*p++)!=']'){ + disp_error_message("unmatch ']'",p); + exit(1); + } + add_scriptc(C_FUNC); + } else if(str_data[l].type == C_USERFUNC || str_data[l].type == C_USERFUNC_POS) { + add_scriptl(search_str((unsigned char*)"callsub")); + add_scriptc(C_ARG); + add_scriptl(l); + }else + add_scriptl(l); + + } + +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + ShowDebug("parse_simpleexpr end %s\n",p); +#endif + return p; +} + +/*========================================== + * 式の解析 + *------------------------------------------ + */ +unsigned char* parse_subexpr(unsigned char *p,int limit) +{ + int op,opl,len; + char *tmpp; + +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + ShowDebug("parse_subexpr %s\n",p); +#endif + p=skip_space(p); + + if(*p=='-'){ + tmpp=(char *) skip_space((unsigned char *) (p+1)); + if(*tmpp==';' || *tmpp==','){ + add_scriptl(LABEL_NEXTLINE); + p++; + return p; + } + } + tmpp=(char *) p; + if((op=C_NEG,*p=='-') || (op=C_LNOT,*p=='!') || (op=C_NOT,*p=='~')){ + p=parse_subexpr(p+1,8); + add_scriptc(op); + } else + p=parse_simpleexpr(p); + p=skip_space(p); + while(((op=C_ADD,opl=6,len=1,*p=='+') || + (op=C_SUB,opl=6,len=1,*p=='-') || + (op=C_MUL,opl=7,len=1,*p=='*') || + (op=C_DIV,opl=7,len=1,*p=='/') || + (op=C_MOD,opl=7,len=1,*p=='%') || + (op=C_FUNC,opl=9,len=1,*p=='(') || + (op=C_LAND,opl=1,len=2,*p=='&' && p[1]=='&') || + (op=C_AND,opl=5,len=1,*p=='&') || + (op=C_LOR,opl=0,len=2,*p=='|' && p[1]=='|') || + (op=C_OR,opl=4,len=1,*p=='|') || + (op=C_XOR,opl=3,len=1,*p=='^') || + (op=C_EQ,opl=2,len=2,*p=='=' && p[1]=='=') || + (op=C_NE,opl=2,len=2,*p=='!' && p[1]=='=') || + (op=C_R_SHIFT,opl=5,len=2,*p=='>' && p[1]=='>') || + (op=C_GE,opl=2,len=2,*p=='>' && p[1]=='=') || + (op=C_GT,opl=2,len=1,*p=='>') || + (op=C_L_SHIFT,opl=5,len=2,*p=='<' && p[1]=='<') || + (op=C_LE,opl=2,len=2,*p=='<' && p[1]=='=') || + (op=C_LT,opl=2,len=1,*p=='<')) && opl>limit){ + p+=len; + if(op==C_FUNC){ + int i=0,func=parse_cmd; + const char *plist[128]; + + if(str_data[parse_cmd].type == C_FUNC){ + // 通常の関数 + add_scriptc(C_ARG); + } else if(str_data[parse_cmd].type == C_USERFUNC || str_data[parse_cmd].type == C_USERFUNC_POS) { + // ユーザー定義関数呼び出し + parse_cmd = search_str((unsigned char*)"callsub"); + i++; + } else { + disp_error_message( + "expect command, missing function name or calling undeclared function",(unsigned char *) tmpp + ); + exit(0); + } + func=parse_cmd; + + do { + plist[i]=(char *) p; + p=parse_subexpr(p,-1); + p=skip_space(p); + if(*p==',') p++; + else if(*p!=')' && script_config.warn_func_no_comma){ + disp_error_message("expect ',' or ')' at func params",p); + } + p=skip_space(p); + i++; + } while(*p && *p!=')' && i<128); + plist[i]=(char *) p; + if(*(p++)!=')'){ + disp_error_message("func request '(' ')'",p); + exit(1); + } + + if (str_data[func].type == C_FUNC && script_config.warn_func_mismatch_paramnum) { + const char *arg = buildin_func[str_data[func].val].arg; + int j = 0; + for (; arg[j]; j++) if (arg[j] == '*') break; + if (!(i <= 1 && j == 0) && ((arg[j] == 0 && i != j) || (arg[j] == '*' && i < j))) { + disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i= 0) { + if(syntax.curly[pos].type == TYPE_DO) { + sprintf(label,"goto __DO%x_FIN;",syntax.curly[pos].index); + break; + } else if(syntax.curly[pos].type == TYPE_FOR) { + sprintf(label,"goto __FR%x_FIN;",syntax.curly[pos].index); + break; + } else if(syntax.curly[pos].type == TYPE_WHILE) { + sprintf(label,"goto __WL%x_FIN;",syntax.curly[pos].index); + break; + } else if(syntax.curly[pos].type == TYPE_SWITCH) { + sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index); + break; + } + pos--; + } + if(pos < 0) { + disp_error_message("unexpected 'break'",p); + } else { + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + } + p = skip_word(p); + p++; + // if, for , while の閉じ判定 + p = parse_syntax_close(p + 1); + return p; + } + break; + case 'c': + if(!strncmp(p,"case",4) && !isalpha(*(p + 4))) { + // case の処理 + if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) { + disp_error_message("unexpected 'case' ",p); + return p+1; + } else { + char *p2; + char label[256]; + int l; + int pos = syntax.curly_count-1; + if(syntax.curly[pos].count != 1) { + // FALLTHRU 用のジャンプ + sprintf(label,"goto __SW%x_%xJ;",syntax.curly[pos].index,syntax.curly[pos].count); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // 現在地のラベルを付ける + sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + } + // switch 判定文 + p = skip_word(p); + p = skip_space(p); + p2 = p; + p = skip_word(p); + p = skip_space(p); + if(*p != ':') { + disp_error_message("expect ':'",p); + exit(1); + } + *p = 0; + sprintf(label,"if(%s != $@__SW%x_VAL) goto __SW%x_%x;", + p2,syntax.curly[pos].index,syntax.curly[pos].index,syntax.curly[pos].count+1); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + *p = ':'; + // 2回parse しないとダメ + p2 = parse_line(label); + parse_line(p2); + syntax.curly_count--; + if(syntax.curly[pos].count != 1) { + // FALLTHRU 終了後のラベル + sprintf(label,"__SW%x_%xJ",syntax.curly[pos].index,syntax.curly[pos].count); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + } + // 一時変数を消す + sprintf(label,"set $@__SW%x_VAL,0;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + syntax.curly[pos].count++; + } + return p + 1; + } else if(!strncmp(p,"continue",8) && !isalpha(*(p + 8))) { + // continue の処理 + char label[256]; + int pos = syntax.curly_count - 1; + while(pos >= 0) { + if(syntax.curly[pos].type == TYPE_DO) { + sprintf(label,"goto __DO%x_NXT;",syntax.curly[pos].index); + syntax.curly[pos].flag = 1; // continue 用のリンク張るフラグ + break; + } else if(syntax.curly[pos].type == TYPE_FOR) { + sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); + break; + } else if(syntax.curly[pos].type == TYPE_WHILE) { + sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); + break; + } + pos--; + } + if(pos < 0) { + disp_error_message("unexpected 'continue'",p); + } else { + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + } + p = skip_word(p); + p++; + // if, for , while の閉じ判定 + p = parse_syntax_close(p + 1); + return p; + } + break; + case 'd': + if(!strncmp(p,"default",7) && !isalpha(*(p + 7))) { + // switch - default の処理 + if(syntax.curly_count <= 0 || syntax.curly[syntax.curly_count - 1].type != TYPE_SWITCH) { + disp_error_message("unexpected 'delault'",p); + return p+1; + } else if(syntax.curly[syntax.curly_count - 1].flag) { + disp_error_message("dup 'delault'",p); + return p+1; + } else { + char label[256]; + int l; + int pos = syntax.curly_count-1; + // 現在地のラベルを付ける + p = skip_word(p); + p = skip_space(p); + if(*p != ':') { + disp_error_message("need ':'",p); + } + p++; + sprintf(label,"__SW%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + + // 無条件で次のリンクに飛ばす + sprintf(label,"goto __SW%x_%x;",syntax.curly[pos].index,syntax.curly[pos].count+1); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // default のラベルを付ける + sprintf(label,"__SW%x_DEF",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + + syntax.curly[syntax.curly_count - 1].flag = 1; + syntax.curly[pos].count++; + + p = skip_word(p); + return p + 1; + } + } else if(!strncmp(p,"do",2) && !isalpha(*(p + 2))) { + int l; + char label[256]; + p=skip_word(p); + p=skip_space(p); + + syntax.curly[syntax.curly_count].type = TYPE_DO; + syntax.curly[syntax.curly_count].count = 1; + syntax.curly[syntax.curly_count].index = syntax.index++; + syntax.curly[syntax.curly_count].flag = 0; + // 現在地のラベル形成する + sprintf(label,"__DO%x_BGN",syntax.curly[syntax.curly_count].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + syntax.curly_count++; + return p; + } + break; + case 'f': + if(!strncmp(p,"for",3) && !isalpha(*(p + 3))) { + int l; + unsigned char label[256]; + int pos = syntax.curly_count; + syntax.curly[syntax.curly_count].type = TYPE_FOR; + syntax.curly[syntax.curly_count].count = 1; + syntax.curly[syntax.curly_count].index = syntax.index++; + syntax.curly[syntax.curly_count].flag = 0; + syntax.curly_count++; + + p=skip_word(p); + p=skip_space(p); + + if(*p != '(') { + disp_error_message("need '('",p); + return p+1; + } + p++; + + // 初期化文を実行する + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + p=parse_line(p); + syntax.curly_count--; + + // 条件判断開始のラベル形成する + sprintf(label,"__FR%x_J",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + + if(*p == ';') { + // for(;;) のパターンなので必ず真 + ; + } else { + // 条件が偽なら終了地点に飛ばす + sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); + add_scriptl(add_str("jump_zero")); + add_scriptc(C_ARG); + p=parse_expr(p); + p=skip_space(p); + add_scriptl(add_str(label)); + add_scriptc(C_FUNC); + } + if(*p != ';') { + disp_error_message("need ';'",p); + return p+1; + } + p++; + + // ループ開始に飛ばす + sprintf(label,"goto __FR%x_BGN;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // 次のループへのラベル形成する + sprintf(label,"__FR%x_NXT",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + + // 次のループに入る時の処理 + // for 最後の '(' を ';' として扱うフラグ + parse_syntax_for_flag = 1; + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + p=parse_line(p); + syntax.curly_count--; + parse_syntax_for_flag = 0; + + // 条件判定処理に飛ばす + sprintf(label,"goto __FR%x_J;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // ループ開始のラベル付け + sprintf(label,"__FR%x_BGN",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + return p; + } else if(!strncmp(p,"function",8) && !isalpha(*(p + 8))) { + unsigned char *func_name; + // function + p=skip_word(p); + p=skip_space(p); + // function - name + func_name = p; + p=skip_word(p); + if(*skip_space(p) == ';') { + // 関数の宣言 - 名前を登録して終わり + unsigned char c = *p; + int l; + *p = 0; + l=add_str(func_name); + *p = c; + if(str_data[l].type == C_NOP) { + str_data[l].type = C_USERFUNC; + } + return skip_space(p) + 1; + } else { + // 関数の中身 + char label[256]; + unsigned char c = *p; + int l; + syntax.curly[syntax.curly_count].type = TYPE_USERFUNC; + syntax.curly[syntax.curly_count].count = 1; + syntax.curly[syntax.curly_count].index = syntax.index++; + syntax.curly[syntax.curly_count].flag = 0; + syntax.curly_count++; + + // 関数終了まで飛ばす + sprintf(label,"goto __FN%x_FIN;",syntax.curly[syntax.curly_count-1].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // 関数名のラベルを付ける + *p = 0; + l=add_str(func_name); + if(str_data[l].type == C_NOP) { + str_data[l].type = C_USERFUNC; + } + if(str_data[l].label!=-1){ + *p=c; + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + strdb_put(scriptlabel_db,func_name,(void*)script_pos); // 外部用label db登録 + *p = c; + return skip_space(p); + } + } + break; + case 'i': + if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) { + // if() の処理 + char label[256]; + p=skip_word(p); + p=skip_space(p); + + syntax.curly[syntax.curly_count].type = TYPE_IF; + syntax.curly[syntax.curly_count].count = 1; + syntax.curly[syntax.curly_count].index = syntax.index++; + syntax.curly[syntax.curly_count].flag = 0; + sprintf(label,"__IF%x_%x",syntax.curly[syntax.curly_count].index,syntax.curly[syntax.curly_count].count); + syntax.curly_count++; + add_scriptl(add_str("jump_zero")); + add_scriptc(C_ARG); + p=parse_expr(p); + p=skip_space(p); + add_scriptl(add_str(label)); + add_scriptc(C_FUNC); + return p; + } + break; + case 's': + if(!strncmp(p,"switch",6) && !isalpha(*(p + 6))) { + // switch() の処理 + char label[256]; + syntax.curly[syntax.curly_count].type = TYPE_SWITCH; + syntax.curly[syntax.curly_count].count = 1; + syntax.curly[syntax.curly_count].index = syntax.index++; + syntax.curly[syntax.curly_count].flag = 0; + sprintf(label,"$@__SW%x_VAL",syntax.curly[syntax.curly_count].index); + syntax.curly_count++; + add_scriptl(add_str((unsigned char*)"set")); + add_scriptc(C_ARG); + add_scriptl(add_str(label)); + p=skip_word(p); + p=skip_space(p); + p=parse_expr(p); + p=skip_space(p); + if(*p != '{') { + disp_error_message("need '{'",p); + } + add_scriptc(C_FUNC); + return p + 1; + } + break; + case 'w': + if(!strncmp(p,"while",5) && !isalpha(*(p + 5))) { + int l; + char label[256]; + p=skip_word(p); + p=skip_space(p); + + syntax.curly[syntax.curly_count].type = TYPE_WHILE; + syntax.curly[syntax.curly_count].count = 1; + syntax.curly[syntax.curly_count].index = syntax.index++; + syntax.curly[syntax.curly_count].flag = 0; + // 条件判断開始のラベル形成する + sprintf(label,"__WL%x_NXT",syntax.curly[syntax.curly_count].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + + // 条件が偽なら終了地点に飛ばす + sprintf(label,"__WL%x_FIN",syntax.curly[syntax.curly_count].index); + add_scriptl(add_str("jump_zero")); + add_scriptc(C_ARG); + p=parse_expr(p); + p=skip_space(p); + add_scriptl(add_str(label)); + add_scriptc(C_FUNC); + syntax.curly_count++; + return p; + } + break; + } + return NULL; +} + +unsigned char* parse_syntax_close(unsigned char *p) { + // if(...) for(...) hoge(); のように、1度閉じられたら再度閉じられるか確認する + int flag; + + do { + p = parse_syntax_close_sub(p,&flag); + } while(flag); + return p; +} + +// if, for , while , do の閉じ判定 +// flag == 1 : 閉じられた +// flag == 0 : 閉じられない +unsigned char* parse_syntax_close_sub(unsigned char *p,int *flag) { + unsigned char label[256]; + int pos = syntax.curly_count - 1; + int l; + *flag = 1; + + if(syntax.curly_count <= 0) { + *flag = 0; + return p; + } else if(syntax.curly[pos].type == TYPE_IF) { + char *p2 = p; + // if 最終場所へ飛ばす + sprintf(label,"goto __IF%x_FIN;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // 現在地のラベルを付ける + sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + + syntax.curly[pos].count++; + p = skip_space(p); + if(!syntax.curly[pos].flag && !strncmp(p,"else",4) && !isalpha(*(p + 4))) { + // else or else - if + p = skip_word(p); + p = skip_space(p); + if(!strncmp(p,"if",2) && !isalpha(*(p + 2))) { + // else - if + p=skip_word(p); + p=skip_space(p); + sprintf(label,"__IF%x_%x",syntax.curly[pos].index,syntax.curly[pos].count); + add_scriptl(add_str("jump_zero")); + add_scriptc(C_ARG); + p=parse_expr(p); + p=skip_space(p); + add_scriptl(add_str(label)); + add_scriptc(C_FUNC); + *flag = 0; + return p; + } else { + // else + if(!syntax.curly[pos].flag) { + syntax.curly[pos].flag = 1; + *flag = 0; + return p; + } + } + } + // if 閉じ + syntax.curly_count--; + // 最終地のラベルを付ける + sprintf(label,"__IF%x_FIN",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + if(syntax.curly[pos].flag == 1) { + // このifに対するelseじゃないのでポインタの位置は同じ + return p2; + } + return p; + } else if(syntax.curly[pos].type == TYPE_DO) { + int l; + char label[256]; + unsigned char *p2; + + if(syntax.curly[pos].flag) { + // 現在地のラベル形成する(continue でここに来る) + sprintf(label,"__DO%x_NXT",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + } + + // 条件が偽なら終了地点に飛ばす + p = skip_space(p); + p2 = skip_word(p); + if(p2 - p != 5 || strncmp("while",p,5)) { + disp_error_message("need 'while'",p); + } + p = p2; + + sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); + add_scriptl(add_str("jump_zero")); + add_scriptc(C_ARG); + p=parse_expr(p); + p=skip_space(p); + add_scriptl(add_str(label)); + add_scriptc(C_FUNC); + + // 開始地点に飛ばす + sprintf(label,"goto __DO%x_BGN;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // 条件終了地点のラベル形成する + sprintf(label,"__DO%x_FIN",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + p = skip_space(p); + if(*p != ';') { + disp_error_message("need ';'",p); + return p+1; + } + p++; + syntax.curly_count--; + return p; + } else if(syntax.curly[pos].type == TYPE_FOR) { + // 次のループに飛ばす + sprintf(label,"goto __FR%x_NXT;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // for 終了のラベル付け + sprintf(label,"__FR%x_FIN",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + syntax.curly_count--; + return p; + } else if(syntax.curly[pos].type == TYPE_WHILE) { + // while 条件判断へ飛ばす + sprintf(label,"goto __WL%x_NXT;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // while 終了のラベル付け + sprintf(label,"__WL%x_FIN",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + syntax.curly_count--; + return p; + } else if(syntax.curly[syntax.curly_count-1].type == TYPE_USERFUNC) { + int pos = syntax.curly_count-1; + char label[256]; + int l; + // 戻す + sprintf(label,"return;"); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + + // 現在地のラベルを付ける + sprintf(label,"__FN%x_FIN",syntax.curly[pos].index); + l=add_str(label); + if(str_data[l].label!=-1){ + disp_error_message("dup label ",p); + exit(1); + } + set_label(l,script_pos); + syntax.curly_count--; + return p + 1; + } else { + *flag = 0; + return p; + } +} + +/*========================================== + * 組み込み関数の追加 + *------------------------------------------ + */ +static void add_buildin_func(void) +{ + int i,n; + for(i=0;buildin_func[i].func;i++){ + n=add_str((unsigned char *) buildin_func[i].name); + str_data[n].type=C_FUNC; + str_data[n].val=i; + str_data[n].func=buildin_func[i].func; + } +} + +/*========================================== + * 定数データベースの読み込み + *------------------------------------------ + */ +static void read_constdb(void) +{ + FILE *fp; + char line[1024],name[1024]; + int val,n,i,type; + + sprintf(line, "%s/const.txt", db_path); + fp=fopen(line, "r"); + if(fp==NULL){ + ShowError("can't read %s\n", line); + return ; + } + while(fgets(line,1020,fp)){ + if(line[0]=='/' && line[1]=='/') + continue; + type=0; + if(sscanf(line,"%[A-Za-z0-9_],%d,%d",name,&val,&type)>=2 || + sscanf(line,"%[A-Za-z0-9_] %d %d",name,&val,&type)>=2){ + for(i=0;name[i];i++) + name[i]=tolower(name[i]); + n=add_str((const unsigned char *) name); + if(type==0) + str_data[n].type=C_INT; + else + str_data[n].type=C_PARAM; + str_data[n].val=val; + } + } + fclose(fp); +} + +/*========================================== + * スクリプトの解析 + *------------------------------------------ + */ +struct script_code* parse_script(unsigned char *src,int line) +{ + unsigned char *p, *tmpp; + int i; + struct script_code *code; + static int first = 1; + + if (first) { + add_buildin_func(); + read_constdb(); + } + first = 0; + +////////////////////////////////////////////// +// additional check on the input to filter empty scripts ("{}" and "{ }") + p = src; + p = skip_space(p); + if (*p != '{') { + disp_error_message("not found '{'", p); + return NULL; + } + p++; + p = skip_space(p); + if (*p == '}') { + // an empty function, just return + return NULL; + } + script_buf = (unsigned char *) aCallocA(SCRIPT_BLOCK_SIZE, sizeof(unsigned char)); + script_pos = 0; + script_size = SCRIPT_BLOCK_SIZE; + str_data[LABEL_NEXTLINE].type = C_NOP; + str_data[LABEL_NEXTLINE].backpatch = -1; + str_data[LABEL_NEXTLINE].label = -1; + for (i = LABEL_START; i < str_num; i++) { + if ( + str_data[i].type == C_POS || str_data[i].type == C_NAME || + str_data[i].type == C_USERFUNC || str_data[i].type == C_USERFUNC_POS + ) { + str_data[i].type = C_NOP; + str_data[i].backpatch = -1; + str_data[i].label = -1; + } + } + + //Labels must be reparsed for the script.... + scriptlabel_db->clear(scriptlabel_db, NULL); + + // for error message + startptr = src; + startline = line; + + while (p && *p && (*p != '}' || syntax.curly_count != 0)) { + p = skip_space(p); + // labelだけ特殊処理 + tmpp = skip_space(skip_word(p)); + if (*tmpp == ':' && !(!strncmp(p,"default",7) && !isalpha(*(p + 7)))) { + int l, c; + c = *skip_word(p); + *skip_word(p) = 0; + l = add_str(p); + if (str_data[l].label != -1) { + *skip_word(p) = c; + disp_error_message("dup label ", p); + exit(1); + } + set_label(l, script_pos); + strdb_put(scriptlabel_db, p, (void*)script_pos); // 外部用label db登録 + *skip_word(p) = c; + p = tmpp + 1; + continue; + } + + // 他は全部一緒くた + p = parse_line(p); + p = skip_space(p); + add_scriptc(C_EOL); + + set_label(LABEL_NEXTLINE, script_pos); + str_data[LABEL_NEXTLINE].type = C_NOP; + str_data[LABEL_NEXTLINE].backpatch = -1; + str_data[LABEL_NEXTLINE].label = -1; + } + + add_scriptc(C_NOP); + + script_size = script_pos; + script_buf = (unsigned char *)aRealloc(script_buf, script_pos + 1); + + // 未解決のラベルを解決 + for (i = LABEL_START; i < str_num; i++) { + if (str_data[i].type == C_NOP) { + int j, next; + str_data[i].type = C_NAME; + str_data[i].label = i; + for (j = str_data[i].backpatch; j >= 0 && j != 0x00ffffff; ) { + next = (*(int*)(script_buf+j)) & 0x00ffffff; + script_buf[j] = i; + script_buf[j+1] = i>>8; + script_buf[j+2] = i>>16; + j = next; + } + } + } + +#ifdef DEBUG_DISP + for (i = 0; i < script_pos; i++) { + if ((i & 15) == 0) printf("%04x : ", i); + printf("%02x ", script_buf[i]); + if((i&15) == 15) printf("\n"); + } + printf("\n"); +#endif + + startptr = NULL; //Clear pointer to prevent future references to a src that may be free'd. [Skotlex] + code = aCalloc(1, sizeof(struct script_code)); + code->script_buf = script_buf; + code->script_size = script_size; + code->script_vars = NULL; + return code; +} + +// +// 実行系 +// +enum {RUN = 0,STOP,END,RERUNLINE,GOTO,RETFUNC}; + +/*========================================== + * ridからsdへの解決 + *------------------------------------------ + */ +struct map_session_data *script_rid2sd(struct script_state *st) +{ + struct map_session_data *sd=map_id2sd(st->rid); + if(!sd){ + ShowError("script_rid2sd: fatal error ! player not attached!\n"); + report_src(st); + } + return sd; +} + + +/*========================================== + * 変数の読み取り + *------------------------------------------ + */ +int get_val(struct script_state*st,struct script_data* data) +{ + struct map_session_data *sd=NULL; + if(data->type==C_NAME){ + char *name=str_buf+str_data[data->u.num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if(not_server_variable(prefix)){ + if((sd=script_rid2sd(st))==NULL) + ShowError("get_val error name?:%s\n",name); + } + if(postfix=='$'){ + + data->type=C_CONSTSTR; + if( prefix=='@'){ + if(sd) + data->u.str = pc_readregstr(sd,data->u.num); + }else if(prefix=='$'){ + data->u.str = (char *)idb_get(mapregstr_db,data->u.num); + }else if(prefix=='#'){ + if( name[1]=='#'){ + if(sd) + data->u.str = pc_readaccountreg2str(sd,name); + }else{ + if(sd) + data->u.str = pc_readaccountregstr(sd,name); + } + }else if(prefix=='.') { + struct linkdb_node **n; + if( data->ref ) { + n = data->ref; + } else if( name[1] == '@' ) { + n = st->stack->var_function; + } else { + n = &st->script->script_vars; + } + data->u.str = linkdb_search(n, (void*)data->u.num ); + }else{ + if(sd) + data->u.str = pc_readglobalreg_str(sd,name); + } // [zBuffer] + /*else{ + ShowWarning("script: get_val: illegal scope string variable.\n"); + data->u.str = "!!ERROR!!"; + }*/ + if( data->u.str == NULL ) + data->u.str =""; + + }else{ + + data->type=C_INT; + if(str_data[data->u.num&0x00ffffff].type==C_INT){ + data->u.num = str_data[data->u.num&0x00ffffff].val; + }else if(str_data[data->u.num&0x00ffffff].type==C_PARAM){ + if(sd) + data->u.num = pc_readparam(sd,str_data[data->u.num&0x00ffffff].val); + }else if(prefix=='@'){ + if(sd) + data->u.num = pc_readreg(sd,data->u.num); + }else if(prefix=='$'){ + data->u.num = (int)idb_get(mapreg_db,data->u.num); + }else if(prefix=='#'){ + if( name[1]=='#'){ + if(sd) + data->u.num = pc_readaccountreg2(sd,name); + }else{ + if(sd) + data->u.num = pc_readaccountreg(sd,name); + } + }else if(prefix=='.'){ + struct linkdb_node **n; + if( data->ref ) { + n = data->ref; + } else if( name[1] == '@' ) { + n = st->stack->var_function; + } else { + n = &st->script->script_vars; + } + data->u.num = (int)linkdb_search(n, (void*)data->u.num); + }else{ + if(sd) + data->u.num = pc_readglobalreg(sd,name); + } + } + } + return 0; +} +/*========================================== + * 変数の読み取り2 + *------------------------------------------ + */ +void* get_val2(struct script_state*st,int num,struct linkdb_node **ref) +{ + struct script_data dat; + dat.type=C_NAME; + dat.u.num=num; + dat.ref = ref; + get_val(st,&dat); + if( dat.type==C_INT ) return (void*)dat.u.num; + else return (void*)dat.u.str; +} + +/*========================================== + * 変数設定用 + *------------------------------------------ + */ +static int set_reg(struct script_state*st,struct map_session_data *sd,int num,char *name,void *v,struct linkdb_node** ref) +{ + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if( postfix=='$' ){ + char *str=(char*)v; + if( prefix=='@'){ + pc_setregstr(sd,num,str); + }else if(prefix=='$') { + mapreg_setregstr(num,str); + }else if(prefix=='#') { + if( name[1]=='#' ) + pc_setaccountreg2str(sd,name,str); + else + pc_setaccountregstr(sd,name,str); + }else if(prefix=='.') { + char *p; + struct linkdb_node **n; + if( ref ) { + n = ref; + } else if( name[1] == '@' ) { + n = st->stack->var_function; + } else { + n = &st->script->script_vars; + } + p = linkdb_search(n, (void*)num); + if(p) { + linkdb_erase(n, (void*)num); + aFree(p); + } + if( ((char*)v)[0] ) + linkdb_insert(n, (void*)num, aStrdup(v)); + }else{ + pc_setglobalreg_str(sd,name,str); + } // [zBuffer] + + /*else{ + ShowWarning("script: set_reg: illegal scope string variable !"); + }*/ + }else{ + // 数値 + int val = (int)v; + if(str_data[num&0x00ffffff].type==C_PARAM){ + pc_setparam(sd,str_data[num&0x00ffffff].val,val); + }else if(prefix=='@') { + pc_setreg(sd,num,val); + }else if(prefix=='$') { + mapreg_setreg(num,val); + }else if(prefix=='#') { + if( name[1]=='#' ) + pc_setaccountreg2(sd,name,val); + else + pc_setaccountreg(sd,name,val); + }else if(prefix == '.') { + struct linkdb_node **n; + if( ref ) { + n = ref; + } else if( name[1] == '@' ) { + n = st->stack->var_function; + } else { + n = &st->script->script_vars; + } + if( val == 0 ) { + linkdb_erase(n, (void*)num); + } else { + linkdb_replace(n, (void*)num, (void*)val); + } + }else{ + pc_setglobalreg(sd,name,val); + } + } + return 0; +} + +int set_var(struct map_session_data *sd, char *name, void *val) +{ + return set_reg(NULL, sd, add_str((unsigned char *) name), name, val, NULL); +} + +/*========================================== + * 文字列への変換 + *------------------------------------------ + */ +char* conv_str(struct script_state *st,struct script_data *data) +{ + get_val(st,data); + if(data->type==C_INT){ + char *buf; + buf=(char *)aMallocA(ITEM_NAME_LENGTH*sizeof(char)); + snprintf(buf,ITEM_NAME_LENGTH, "%d",data->u.num); + data->type=C_STR; + data->u.str=buf; +#if 1 + } else if(data->type==C_NAME){ + // テンポラリ。本来無いはず + data->type=C_CONSTSTR; + data->u.str=str_buf+str_data[data->u.num].str; +#endif + } + return data->u.str; +} + +/*========================================== + * 数値へ変換 + *------------------------------------------ + */ +int conv_num(struct script_state *st,struct script_data *data) +{ + char *p; + get_val(st,data); + if(data->type==C_STR || data->type==C_CONSTSTR){ + p=data->u.str; + data->u.num = atoi(p); + if(data->type==C_STR) + aFree(p); + data->type=C_INT; + } + return data->u.num; +} + +/*========================================== + * スタックへ数値をプッシュ + *------------------------------------------ + */ +void push_val(struct script_stack *stack,int type,int val) +{ + if(stack->sp >= stack->sp_max){ + stack->sp_max += 64; + stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, + sizeof(stack->stack_data[0]) * stack->sp_max); + memset(stack->stack_data + (stack->sp_max - 64), 0, + 64 * sizeof(*(stack->stack_data))); + } +// if(battle_config.etc_log) +// printf("push (%d,%d)-> %d\n",type,val,stack->sp); + stack->stack_data[stack->sp].type=type; + stack->stack_data[stack->sp].u.num=val; + stack->stack_data[stack->sp].ref = NULL; + stack->sp++; +} + +/*========================================== + * スタックへ数値+リファレンスをプッシュ + *------------------------------------------ + */ + +void push_val2(struct script_stack *stack,int type,int val,struct linkdb_node** ref) { + push_val(stack,type,val); + stack->stack_data[stack->sp-1].ref = ref; +} + +/*========================================== + * スタックへ文字列をプッシュ + *------------------------------------------ + */ +void push_str(struct script_stack *stack,int type,unsigned char *str) +{ + if(stack->sp>=stack->sp_max){ + stack->sp_max += 64; + stack->stack_data = (struct script_data *)aRealloc(stack->stack_data, + sizeof(stack->stack_data[0]) * stack->sp_max); + memset(stack->stack_data + (stack->sp_max - 64), '\0', + 64 * sizeof(*(stack->stack_data))); + } +// if(battle_config.etc_log) +// printf("push (%d,%x)-> %d\n",type,str,stack->sp); + stack->stack_data[stack->sp].type=type; + stack->stack_data[stack->sp].u.str=(char *) str; + stack->stack_data[stack->sp].ref = NULL; + stack->sp++; +} + +/*========================================== + * スタックへ複製をプッシュ + *------------------------------------------ + */ +void push_copy(struct script_stack *stack,int pos) +{ + switch(stack->stack_data[pos].type){ + case C_CONSTSTR: + push_str(stack,C_CONSTSTR,(unsigned char *) stack->stack_data[pos].u.str); + break; + case C_STR: + push_str(stack,C_STR,(unsigned char *) aStrdup(stack->stack_data[pos].u.str)); + break; + default: + push_val2( + stack,stack->stack_data[pos].type,stack->stack_data[pos].u.num, + stack->stack_data[pos].ref + ); + break; + } +} + +/*========================================== + * スタックからポップ + *------------------------------------------ + */ +void pop_stack(struct script_stack* stack,int start,int end) +{ + int i; + for(i=start;istack_data[i].type==C_STR){ + aFree(stack->stack_data[i].u.str); + stack->stack_data[i].type=C_INT; //Might not be correct, but it's done in case to prevent pointer errors later on. [Skotlex] + } + } + if(stack->sp>end){ + memmove(&stack->stack_data[start],&stack->stack_data[end],sizeof(stack->stack_data[0])*(stack->sp-end)); + } + stack->sp-=end-start; +} + +/*========================================== + * スクリプト依存変数、関数依存変数の解放 + *------------------------------------------ + */ +void script_free_vars(struct linkdb_node **node) { + struct linkdb_node *n = *node; + while(n) { + char *name = str_buf + str_data[(int)(n->key)&0x00ffffff].str; + char postfix = name[strlen(name)-1]; + if( postfix == '$' ) { + // 文字型変数なので、データ削除 + aFree(n->data); + } + n = n->next; + } + linkdb_final( node ); +} + +/*========================================== + * Free's the whole stack. Invoked when clearing a character. [Skotlex] + *------------------------------------------ + */ +void script_free_stack(struct script_stack* stack) +{ + int i; + for (i = 0; i < stack->sp; i++) + { + if(stack->stack_data[i].type==C_STR) + { + //ShowDebug ("script_free_stack: freeing %p at sp=%d.\n", stack->stack_data[i].u.str, i); + aFree(stack->stack_data[i].u.str); + stack->stack_data[i].type = C_INT; + }else if( i > 0 && stack->stack_data[i].type == C_RETINFO ) { + struct linkdb_node** n = (struct linkdb_node**)stack->stack_data[i-1].u.num; + script_free_vars( n ); + aFree( n ); + } + } + script_free_vars( stack->var_function ); + aFree(stack->var_function); + aFree (stack->stack_data); + aFree (stack); +} + +void script_free_code(struct script_code* code) { + script_free_vars( &code->script_vars ); + aFree( code->script_buf ); + aFree( code ); +} + +int axtoi(char *hexStg) { + int n = 0; // position in string + int m = 0; // position in digit[] to shift + int count; // loop index + int intValue = 0; // integer value of hex string + int digit[11]; // hold values to convert + while (n < 10) { + if (hexStg[n]=='\0') + break; + if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9 + digit[n] = hexStg[n] & 0x0f; //convert to int + else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f + digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int + else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F + digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int + else break; + n++; + } + count = n; + m = n - 1; + n = 0; + while(n < count) { + // digit[n] is value of hex digit at position n + // (m << 2) is the number of positions to shift + // OR the bits into return value + intValue = intValue | (digit[n] << (m << 2)); + m--; // adjust the position to set + n++; // next digit to process + } + return (intValue); +} + +// [Lance] Hex string to integer converter +int buildin_axtoi(struct script_state *st) +{ + char *hex = conv_str(st,& (st->stack->stack_data[st->start+2])); + push_val(st->stack, C_INT, axtoi(hex)); + return 0; +} + +// +// 埋め込み関数 +// +/*========================================== + * + *------------------------------------------ + */ +int buildin_mes(struct script_state *st) +{ + conv_str(st,& (st->stack->stack_data[st->start+2])); + clif_scriptmes(script_rid2sd(st),st->oid,st->stack->stack_data[st->start+2].u.str); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_goto(struct script_state *st) +{ + int pos; + + if (st->stack->stack_data[st->start+2].type != C_POS){ + int func = st->stack->stack_data[st->start+2].u.num; + ShowMessage("script: goto '"CL_WHITE"%s"CL_RESET"': not label!\n", str_buf + str_data[func].str); + st->state = END; + return 1; + } + + pos = conv_num(st,& (st->stack->stack_data[st->start+2])); + st->pos = pos; + st->state = GOTO; + return 0; +} + +/*========================================== + * ユーザー定義関数の呼び出し + *------------------------------------------ + */ +int buildin_callfunc(struct script_state *st) +{ + struct script_code *scr, *oldscr; + char *str=conv_str(st,& (st->stack->stack_data[st->start+2])); + + if( (scr=(struct script_code *) strdb_get(userfunc_db,(unsigned char*)str)) ){ + int i,j; + struct linkdb_node **oldval = st->stack->var_function; + for(i=st->start+3,j=0;iend;i++,j++) + push_copy(st->stack,i); + + push_val(st->stack,C_INT,j); // 引数の数をプッシュ + push_val(st->stack,C_INT,st->stack->defsp); // 現在の基準スタックポインタをプッシュ + push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ + push_val(st->stack,C_INT,(int)st->stack->var_function); // 現在の関数依存変数をプッシュ + push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ + + oldscr = st->script; + st->pos=0; + st->script=scr; + st->stack->defsp=st->start+5+j; + st->state=GOTO; + st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*)); + + // ' 変数の引き継ぎ + for(i = 0; i < j; i++) { + struct script_data *s = &st->stack->stack_data[st->stack->sp-6-i]; + if( s->type == C_NAME && !s->ref ) { + char *name = str_buf+str_data[s->u.num&0x00ffffff].str; + // '@ 変数の引き継ぎ + if( name[0] == '.' && name[1] == '@' ) { + s->ref = oldval; + } else if( name[0] == '.' ) { + s->ref = &oldscr->script_vars; + } + } + } + }else{ + ShowWarning("script:callfunc: function not found! [%s]\n",str); + st->state=END; + return 1; + } + return 0; +} +/*========================================== + * サブルーティンの呼び出し + *------------------------------------------ + */ +int buildin_callsub(struct script_state *st) +{ + int pos=conv_num(st,& (st->stack->stack_data[st->start+2])); + int i,j; + if(st->stack->stack_data[st->start+2].type != C_POS && st->stack->stack_data[st->start+2].type != C_USERFUNC_POS) { + ShowError("script: callsub: not label !\n"); + st->state=END; + return 1; + } else { + struct linkdb_node **oldval = st->stack->var_function; + for(i=st->start+3,j=0;iend;i++,j++) + push_copy(st->stack,i); + + push_val(st->stack,C_INT,j); // 引数の数をプッシュ + push_val(st->stack,C_INT,st->stack->defsp); // 現在の基準スタックポインタをプッシュ + push_val(st->stack,C_INT,(int)st->script); // 現在のスクリプトをプッシュ + push_val(st->stack,C_INT,(int)st->stack->var_function); // 現在の関数依存変数をプッシュ + push_val(st->stack,C_RETINFO,st->pos); // 現在のスクリプト位置をプッシュ + + st->pos=pos; + st->stack->defsp=st->start+5+j; + st->state=GOTO; + st->stack->var_function = (struct linkdb_node**)aCalloc(1, sizeof(struct linkdb_node*)); + + // ' 変数の引き継ぎ + for(i = 0; i < j; i++) { + struct script_data *s = &st->stack->stack_data[st->stack->sp-6-i]; + if( s->type == C_NAME && !s->ref ) { + char *name = str_buf+str_data[s->u.num&0x00ffffff].str; + // '@ 変数の引き継ぎ + if( name[0] == '.' && name[1] == '@' ) { + s->ref = oldval; + } + } + } + } + return 0; +} + +/*========================================== + * 引数の所得 + *------------------------------------------ + */ +int buildin_getarg(struct script_state *st) +{ + int num=conv_num(st,& (st->stack->stack_data[st->start+2])); + int max,stsp; + if( st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO ){ + ShowWarning("script:getarg without callfunc or callsub!\n"); + st->state=END; + return 1; + } + max=conv_num(st,& (st->stack->stack_data[st->stack->defsp-5])); + stsp=st->stack->defsp - max -5; + if( num >= max ){ + ShowWarning("script:getarg arg1(%d) out of range(%d) !\n",num,max); + st->state=END; + return 1; + } + push_copy(st->stack,stsp+num); + return 0; +} + +/*========================================== + * サブルーチン/ユーザー定義関数の終了 + *------------------------------------------ + */ +int buildin_return(struct script_state *st) +{ + if(st->end>st->start+2){ // 戻り値有り + struct script_data *sd; + push_copy(st->stack,st->start+2); + sd = &st->stack->stack_data[st->stack->sp-1]; + if(sd->type == C_NAME) { + char *name = str_buf + str_data[sd->u.num&0x00ffffff].str; + if( name[0] == '.' && name[1] == '@') { + // '@ 変数を参照渡しにすると危険なので値渡しにする + get_val(st,sd); + } else if( name[0] == '.' && !sd->ref) { + // ' 変数は参照渡しでも良いが、参照元が設定されていないと + // 元のスクリプトの値を差してしまうので補正する。 + sd->ref = &st->script->script_vars; + } + } + } + st->state=RETFUNC; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_next(struct script_state *st) +{ + st->state=STOP; + clif_scriptnext(script_rid2sd(st),st->oid); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_close(struct script_state *st) +{ + st->state=END; + clif_scriptclose(script_rid2sd(st),st->oid); + return 0; +} +int buildin_close2(struct script_state *st) +{ + st->state=STOP; + clif_scriptclose(script_rid2sd(st),st->oid); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_menu(struct script_state *st) +{ + char *buf; + int len,i; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(sd->state.menu_or_input==0){ + st->state=RERUNLINE; + sd->state.menu_or_input=1; + for(i=st->start+2,len=16;iend;i+=2){ + conv_str(st,& (st->stack->stack_data[i])); + len+=(int)strlen(st->stack->stack_data[i].u.str)+1; + } + buf=(char *)aMallocA((len+1)*sizeof(char)); + buf[0]=0; + for(i=st->start+2,len=0;iend;i+=2){ + strcat(buf,st->stack->stack_data[i].u.str); + strcat(buf,":"); + } + clif_scriptmenu(script_rid2sd(st),st->oid,buf); + aFree(buf); + } else if(sd->npc_menu==0xff){ // cansel + sd->state.menu_or_input=0; + st->state=END; + } else { // goto動作 + sd->state.menu_or_input=0; + if(sd->npc_menu>0){ + //Skip empty menu entries which weren't displayed on the client (blackhole89) + for(i=st->start+2;i<=(st->start+sd->npc_menu*2) && sd->npc_menu<(st->end-st->start)/2;i+=2) + { + conv_str(st,& (st->stack->stack_data[i])); // we should convert variables to strings before access it [jA1983] [EoE] + if((int)strlen(st->stack->stack_data[i].u.str) < 1) + sd->npc_menu++; //Empty selection which wasn't displayed on the client. + } + if(sd->npc_menu >= (st->end-st->start)/2) { + //Invalid selection. + st->state=END; + return 0; + } + if( st->stack->stack_data[st->start+sd->npc_menu*2+1].type!=C_POS ){ + ShowError("script: menu: not label !\n"); + st->state=END; + return 1; + } + pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu); + st->pos= conv_num(st,& (st->stack->stack_data[st->start+sd->npc_menu*2+1])); + st->state=GOTO; + } + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_rand(struct script_state *st) +{ + int range; + + if (st->end > st->start+3){ + int min, max; + min = conv_num(st,& (st->stack->stack_data[st->start+2])); + max = conv_num(st,& (st->stack->stack_data[st->start+3])); + if (max == min){ //Why would someone do this? + push_val(st->stack,C_INT,min); + return 0; + } + if (max < min){ + int tmp = min; + min = max; + max = tmp; + } + range = max - min + 1; + if (range == 0) range = 1; + push_val(st->stack,C_INT,rand()%range+min); + } else { + range = conv_num(st,& (st->stack->stack_data[st->start+2])); + if (range == 0) range = 1; + push_val(st->stack,C_INT,rand()%range); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_warp(struct script_state *st) +{ + int x,y; + char *str; + struct map_session_data *sd=script_rid2sd(st); + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + if(strcmp(str,"Random")==0) + pc_randomwarp(sd,3); + else if(strcmp(str,"SavePoint")==0){ + if(map[sd->bl.m].flag.noreturn) // 蝶禁止 + return 0; + + pc_setpos(sd,sd->status.save_point.map, + sd->status.save_point.x,sd->status.save_point.y,3); + }else if(strcmp(str,"Save")==0){ + if(map[sd->bl.m].flag.noreturn) // 蝶禁止 + return 0; + + pc_setpos(sd,sd->status.save_point.map, + sd->status.save_point.x,sd->status.save_point.y,3); + }else + pc_setpos(sd,mapindex_name2id(str),x,y,0); + return 0; +} +/*========================================== + * エリア指定ワープ + *------------------------------------------ + */ +int buildin_areawarp_sub(struct block_list *bl,va_list ap) +{ + int x,y; + unsigned int map; + map=va_arg(ap, unsigned int); + x=va_arg(ap,int); + y=va_arg(ap,int); + if(map == 0) + pc_randomwarp((struct map_session_data *)bl,3); + else + pc_setpos((struct map_session_data *)bl,map,x,y,0); + return 0; +} +int buildin_areawarp(struct script_state *st) +{ + int x,y,m; + unsigned int index; + char *str; + char *mapname; + int x0,y0,x1,y1; + + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + str=conv_str(st,& (st->stack->stack_data[st->start+7])); + x=conv_num(st,& (st->stack->stack_data[st->start+8])); + y=conv_num(st,& (st->stack->stack_data[st->start+9])); + + if( (m=map_mapname2mapid(mapname))< 0) + return 0; + + if(strcmp(str,"Random")==0) + index = 0; + else if(!(index=mapindex_name2id(str))) + return 0; + + map_foreachinarea(buildin_areawarp_sub, + m,x0,y0,x1,y1,BL_PC, index,x,y ); + return 0; +} + +/*========================================== + * warpchar [LuzZza] + * Useful for warp one player from + * another player npc-session. + * Using: warpchar "mapname.gat",x,y,Char_ID; + *------------------------------------------ + */ +int buildin_warpchar(struct script_state *st) +{ + int x,y,a,i; + char *str; + struct map_session_data *sd, **pl_allsd; + int users; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + a=conv_num(st,& (st->stack->stack_data[st->start+5])); + + pl_allsd = map_getallusers(&users); + + for(i=0; istatus.char_id == a) { + + if(strcmp(str, "Random") == 0) + pc_randomwarp(sd, 3); + + else if(strcmp(str, "SavePoint") == 0) + pc_setpos(sd, sd->status.save_point.map, + sd->status.save_point.x, sd->status.save_point.y, 3); + + else + pc_setpos(sd, mapindex_name2id(str), x, y, 3); + } + } + + return 0; +} + +/*========================================== + * Warpparty - [Fredzilla] + * Syntax: warpparty "mapname.gat",x,y,Party_ID; + *------------------------------------------ + */ +int buildin_warpparty(struct script_state *st) +{ + int x,y; + char *str; + int p_id; + int i; + unsigned short mapindex; + struct map_session_data *pl_sd; + struct party *p=NULL; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + p_id=conv_num(st,& (st->stack->stack_data[st->start+5])); + if(p_id < 1) + return 0; + p = party_search(p_id); + if (!p) + return 0; + if(strcmp(str,"Random")==0) + { + for (i = 0; i < MAX_PARTY; i++) + { + if ((pl_sd = p->member[i].sd)) + { + if(map[pl_sd->bl.m].flag.nowarp) + continue; + pc_randomwarp(pl_sd,3); + } + } + } + else if(strcmp(str,"SavePointAll")==0) + { + for (i = 0; i < MAX_PARTY; i++) + { + if ((pl_sd = p->member[i].sd)) + { + if(map[pl_sd->bl.m].flag.noreturn) + continue; + pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); + } + } + } + else if(strcmp(str,"SavePoint")==0) + { + pl_sd=script_rid2sd(st); + if (!pl_sd) return 0; + + mapindex=pl_sd->status.save_point.map; + x=pl_sd->status.save_point.x; + y=pl_sd->status.save_point.y; + + for (i = 0; i < MAX_PARTY; i++) + { + if ((pl_sd = p->member[i].sd)) + { + if(map[pl_sd->bl.m].flag.noreturn) + continue; + pc_setpos(pl_sd,mapindex,x,y,3); + } + } + } + else + { + mapindex = mapindex_name2id(str); + if (!mapindex) //Show source of npc error. + return 1; + for (i = 0; i < MAX_PARTY; i++) + { + if ((pl_sd = p->member[i].sd)) + { + if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp) + continue; + pc_setpos(pl_sd,mapindex,x,y,3); + } + } + } + return 0; +} +/*========================================== + * Warpguild - [Fredzilla] + * Syntax: warpguild "mapname.gat",x,y,Guild_ID; + *------------------------------------------ + */ +int buildin_warpguild(struct script_state *st) +{ + int x,y; + unsigned short mapindex; + char *str; + int g; + int i; + struct map_session_data *pl_sd, **pl_allsd; + int users; + struct map_session_data *sd; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + g=conv_num(st,& (st->stack->stack_data[st->start+5])); + sd=script_rid2sd(st); + + if(map[sd->bl.m].flag.noreturn || map[sd->bl.m].flag.nowarpto) + return 0; + + if(g < 1) + return 0; + + pl_allsd = map_getallusers(&users); + + if(strcmp(str,"Random")==0) + { + + for (i = 0; i < users; i++) + { + if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) + { + if(map[pl_sd->bl.m].flag.nowarp) + continue; + pc_randomwarp(pl_sd,3); + } + } + } + else if(strcmp(str,"SavePointAll")==0) + { + if(map[sd->bl.m].flag.noreturn) + return 0; + + for (i = 0; i < users; i++) + { + if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) + { + if(map[pl_sd->bl.m].flag.noreturn) + continue; + pc_setpos(pl_sd,pl_sd->status.save_point.map,pl_sd->status.save_point.x,pl_sd->status.save_point.y,3); + } + } + } + else if(strcmp(str,"SavePoint")==0) + { + if(map[sd->bl.m].flag.noreturn) + return 0; + + mapindex=sd->status.save_point.map; + x=sd->status.save_point.x; + y=sd->status.save_point.y; + for (i = 0; i < users; i++) + { + if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) + { + if(map[pl_sd->bl.m].flag.noreturn) + continue; + pc_setpos(pl_sd,mapindex,x,y,3); + } + } + } + else + { + mapindex = mapindex_name2id(str); + for (i = 0; i < users; i++) + { + if ((pl_sd = pl_allsd[i]) && pl_sd->status.guild_id == g) + { + if(map[pl_sd->bl.m].flag.noreturn || map[pl_sd->bl.m].flag.nowarp) + continue; + pc_setpos(pl_sd,mapindex,x,y,3); + } + } + } + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_heal(struct script_state *st) +{ + struct map_session_data *sd; + int hp,sp; + + sd = script_rid2sd(st); + if (!sd) return 0; + + hp=conv_num(st,& (st->stack->stack_data[st->start+2])); + sp=conv_num(st,& (st->stack->stack_data[st->start+3])); + status_heal(&sd->bl, hp, sp, 1); + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_itemheal(struct script_state *st) +{ + int hp,sp; + + hp=conv_num(st,& (st->stack->stack_data[st->start+2])); + sp=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if(potion_flag==1) { + potion_hp = hp; + potion_sp = sp; + return 0; + } + + pc_itemheal(script_rid2sd(st),hp,sp); + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_percentheal(struct script_state *st) +{ + int hp,sp; + + hp=conv_num(st,& (st->stack->stack_data[st->start+2])); + sp=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if(potion_flag==1) { + potion_per_hp = hp; + potion_per_sp = sp; + return 0; + } + + pc_percentheal(script_rid2sd(st),hp,sp); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_jobchange(struct script_state *st) +{ + int job, upper=-1; + + job=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end>st->start+3 ) + upper=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if ((job >= 0 && job < MAX_PC_CLASS)){ + pc_jobchange(script_rid2sd(st),job, upper); + if(use_irc && irc_announce_jobchange_flag) + irc_announce_jobchange(script_rid2sd(st)); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_input(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=(st->end>st->start+2)?st->stack->stack_data[st->start+2].u.num:0; + char *name=(char *) ((st->end>st->start+2)?str_buf+str_data[num&0x00ffffff].str:""); +// char prefix=*name; + char postfix=name[strlen(name)-1]; + + sd=script_rid2sd(st); + if(sd->state.menu_or_input){ + sd->state.menu_or_input=0; + if( postfix=='$' ){ + // 文字列 + if(st->end>st->start+2){ // 引数1個 + set_reg(st,sd,num,name,(void*)sd->npc_str,st->stack->stack_data[st->start+2].ref); + }else{ + ShowError("buildin_input: string discarded !!\n"); + return 1; + } + return 0; + } + // commented by Lupus (check Value Number Input fix in clif.c) + // readded by Yor: set ammount to 0 instead of cancel trade. + // ** Fix by fritz :X keeps people from abusing old input bugs + if (sd->npc_amount < 0) { //** If input amount is less then 0 +// clif_tradecancelled(sd); // added "Deal has been cancelled" message by Valaris +// buildin_close(st); // ** close + sd->npc_amount = 0; + } else if ((unsigned int)sd->npc_amount > battle_config.vending_max_value) // new fix by Yor + sd->npc_amount = battle_config.vending_max_value; + + // 数値 + if(st->end>st->start+2){ // 引数1個 + set_reg(st,sd,num,name,(void*)sd->npc_amount,st->stack->stack_data[st->start+2].ref); + } else { + // ragemu互換のため + //pc_setreg(sd,add_str((unsigned char *) "l14"),sd->npc_amount); + } + return 0; + } + //state.menu_or_input = 0 + st->state=RERUNLINE; + if(postfix=='$') + clif_scriptinputstr(sd,st->oid); + else + clif_scriptinput(sd,st->oid); + sd->state.menu_or_input=1; + return 0; +} + +/*========================================== + * 変数設定 + *------------------------------------------ + */ +int buildin_set(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if( st->stack->stack_data[st->start+2].type!=C_NAME ){ + ShowError("script: buildin_set: not name\n"); + return 1; + } + + if(not_server_variable(prefix)) + sd=script_rid2sd(st); + + + if( postfix=='$' ){ + // 文字列 + char *str = conv_str(st,& (st->stack->stack_data[st->start+3])); + set_reg(st,sd,num,name,(void*)str,st->stack->stack_data[st->start+2].ref); + }else{ + // 数値 + int val = conv_num(st,& (st->stack->stack_data[st->start+3])); + set_reg(st,sd,num,name,(void*)val,st->stack->stack_data[st->start+2].ref); + } + + return 0; +} +/*========================================== + * 配列変数設定 + *------------------------------------------ + */ +int buildin_setarray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int i,j; + + if( prefix!='$' && prefix!='@' && prefix!='.'){ + ShowWarning("buildin_setarray: illegal scope !\n"); + return 1; + } + if(not_server_variable(prefix)) + sd=script_rid2sd(st); + + for(j=0,i=st->start+3; iend && j<128;i++,j++){ + void *v; + if( postfix=='$' ) + v=(void*)conv_str(st,& (st->stack->stack_data[i])); + else + v=(void*)conv_num(st,& (st->stack->stack_data[i])); + set_reg(st, sd, num+(j<<24), name, v, st->stack->stack_data[st->start+2].ref); + } + return 0; +} +/*========================================== + * 配列変数クリア + *------------------------------------------ + */ +int buildin_cleararray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int sz=conv_num(st,& (st->stack->stack_data[st->start+4])); + int i; + void *v; + + if( prefix!='$' && prefix!='@' && prefix!='.'){ + ShowWarning("buildin_cleararray: illegal scope !\n"); + return 1; + } + if( not_server_variable(prefix) ) + sd=script_rid2sd(st); + + if( postfix=='$' ) + v=(void*)conv_str(st,& (st->stack->stack_data[st->start+3])); + else + v=(void*)conv_num(st,& (st->stack->stack_data[st->start+3])); + + for(i=0;istack->stack_data[st->start+2].ref); + return 0; +} +/*========================================== + * 配列変数コピー + *------------------------------------------ + */ +int buildin_copyarray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int num2=st->stack->stack_data[st->start+3].u.num; + char *name2=str_buf+str_data[num2&0x00ffffff].str; + char prefix2=*name2; + char postfix2=name2[strlen(name2)-1]; + int sz=conv_num(st,& (st->stack->stack_data[st->start+4])); + int i; + + if( prefix!='$' && prefix!='@' && prefix!='.' ){ + printf("buildin_copyarray: illeagal scope !\n"); + return 0; + } + if( prefix2!='$' && prefix2!='@' && prefix2!='.' ) { + printf("buildin_copyarray: illeagal scope !\n"); + return 0; + } + if( (postfix=='$' || postfix2=='$') && postfix!=postfix2 ){ + printf("buildin_copyarray: type mismatch !\n"); + return 0; + } + if( not_server_variable(prefix) || not_server_variable(prefix2) ) + sd=script_rid2sd(st); + + if((num & 0x00FFFFFF) == (num2 & 0x00FFFFFF) && (num & 0xFF000000) > (num2 & 0xFF000000)) { + // 同じ配列で、num > num2 の場合大きい方からコピーしないといけない + for(i=sz-1;i>=0;i--) + set_reg( + st,sd,num+(i<<24),name, + get_val2(st,num2+(i<<24),st->stack->stack_data[st->start+3].ref), + st->stack->stack_data[st->start+2].ref + ); + } else { + for(i=0;istack->stack_data[st->start+3].ref), + st->stack->stack_data[st->start+2].ref + ); + } + return 0; +} +/*========================================== + * 配列変数のサイズ所得 + *------------------------------------------ + */ +static int getarraysize(struct script_state *st,int num,int postfix,struct linkdb_node** ref) +{ + int i=(num>>24),c=(i==0? -1:i); // Moded to -1 because even if the first element is 0, it will still report as 1 [Lance] + if(postfix == '$'){ + for(;i<128;i++){ + void *v=get_val2(st,(num & 0x00FFFFFF)+(i<<24),ref); + if(*((char*)v)) c=i; + } + }else{ + for(;i<128;i++){ + void *v=get_val2(st,(num & 0x00FFFFFF)+(i<<24),ref); + if((int)v) c=i; + } + } + return c+1; +} + +int buildin_getarraysize(struct script_state *st) +{ + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + + if( prefix!='$' && prefix!='@' && prefix!='.' ){ + ShowWarning("buildin_copyarray: illegal scope !\n"); + return 1; + } + + push_val(st->stack,C_INT,getarraysize(st,num,postfix,st->stack->stack_data[st->start+2].ref)); + return 0; +} +/*========================================== + * 配列変数から要素削除 + *------------------------------------------ + */ +int buildin_deletearray(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int num=st->stack->stack_data[st->start+2].u.num; + char *name=str_buf+str_data[num&0x00ffffff].str; + char prefix=*name; + char postfix=name[strlen(name)-1]; + int count=1; + int i,sz=getarraysize(st,num,postfix,st->stack->stack_data[st->start+2].ref)-(num>>24)-count+1; + + + if( (st->end > st->start+3) ) + count=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if( prefix!='$' && prefix!='@' && prefix!='.' ){ + ShowWarning("buildin_deletearray: illegal scope !\n"); + return 1; + } + if( not_server_variable(prefix) ) + sd=script_rid2sd(st); + + for(i=0;istack->stack_data[st->start+2].ref), + st->stack->stack_data[st->start+2].ref + ); + } + + if(postfix != '$'){ + for(;i<(128-(num>>24));i++) + set_reg(st,sd,num+(i<<24),name, 0,st->stack->stack_data[st->start+2].ref); + } else { + for(;i<(128-(num>>24));i++) + set_reg(st,sd,num+(i<<24),name, (void *) "",st->stack->stack_data[st->start+2].ref); + } + return 0; +} + +/*========================================== + * 指定要素を表す値(キー)を所得する + *------------------------------------------ + */ +int buildin_getelementofarray(struct script_state *st) +{ + if( st->stack->stack_data[st->start+2].type==C_NAME ){ + int i=conv_num(st,& (st->stack->stack_data[st->start+3])); + if(i>127 || i<0){ + ShowWarning("script: getelementofarray (operator[]): param2 illegal number %d\n",i); + push_val(st->stack,C_INT,0); + return 1; + }else{ + push_val(st->stack,C_NAME, + (i<<24) | st->stack->stack_data[st->start+2].u.num ); + } + }else{ + ShowError("script: getelementofarray (operator[]): param1 not name !\n"); + push_val(st->stack,C_INT,0); + } + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_setlook(struct script_state *st) +{ + int type,val; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=conv_num(st,& (st->stack->stack_data[st->start+3])); + + pc_changelook(script_rid2sd(st),type,val); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_cutin(struct script_state *st) +{ + int type; + + conv_str(st,& (st->stack->stack_data[st->start+2])); + type=conv_num(st,& (st->stack->stack_data[st->start+3])); + + clif_cutin(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str,type); + + return 0; +} +/*========================================== + * カードのイラストを表示する + *------------------------------------------ + */ +int buildin_cutincard(struct script_state *st) +{ + int itemid; + struct item_data *i_data; + + itemid=conv_num(st,& (st->stack->stack_data[st->start+2])); + + i_data = itemdb_exists(itemid); + if (i_data) + clif_cutin(script_rid2sd(st),i_data->cardillustname,4); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_viewpoint(struct script_state *st) +{ + int type,x,y,id,color; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + id=conv_num(st,& (st->stack->stack_data[st->start+5])); + color=conv_num(st,& (st->stack->stack_data[st->start+6])); + + clif_viewpoint(script_rid2sd(st),st->oid,type,x,y,id,color); + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_countitem(struct script_state *st) +{ + int nameid=0,count=0,i; + struct map_session_data *sd; + + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data; + if( (item_data = itemdb_searchname(name)) != NULL) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + if (nameid>=500) //if no such ID then skip this iteration + for(i=0;istatus.inventory[i].nameid==nameid) + count+=sd->status.inventory[i].amount; + } + else{ + if(battle_config.error_log) + ShowError("wrong item ID : countitem(%i)\n",nameid); + push_val(st->stack,C_INT,0); + return 1; + } + push_val(st->stack,C_INT,count); + return 0; +} + +/*========================================== + * countitem2(nameID,Identified,Refine,Attribute,Card0,Card1,Card2,Card3) [Lupus] + * returns number of items that met the conditions + *------------------------------------------ + */ +int buildin_countitem2(struct script_state *st) +{ + int nameid=0,count=0,i; + int iden,ref,attr,c1,c2,c3,c4; + struct map_session_data *sd; + + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data; + if( (item_data = itemdb_searchname(name)) != NULL) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + iden=conv_num(st,& (st->stack->stack_data[st->start+3])); + ref=conv_num(st,& (st->stack->stack_data[st->start+4])); + attr=conv_num(st,& (st->stack->stack_data[st->start+5])); + c1=conv_num(st,& (st->stack->stack_data[st->start+6])); + c2=conv_num(st,& (st->stack->stack_data[st->start+7])); + c3=conv_num(st,& (st->stack->stack_data[st->start+8])); + c4=conv_num(st,& (st->stack->stack_data[st->start+9])); + + if (nameid>=500) //if no such ID then skip this iteration + for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || + sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid || + sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref || + sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 || + sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 || + sd->status.inventory[i].card[3]!=c4) + continue; + + count+=sd->status.inventory[i].amount; + } + else{ + if(battle_config.error_log) + ShowError("wrong item ID : countitem2(%i)\n",nameid); + push_val(st->stack,C_INT,0); + return 1; + } + push_val(st->stack,C_INT,count); + + return 0; +} + +/*========================================== + * 重量チェック + *------------------------------------------ + */ +int buildin_checkweight(struct script_state *st) +{ + int nameid=0,amount,i; + unsigned long weight; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + if ( amount<=0 || nameid<500 ) { //if get wrong item ID or amount<=0, don't count weight of non existing items + push_val(st->stack,C_INT,0); + ShowError("buildin_checkweight: Wrong item ID or amount.\n"); + return 1; + } + + weight = itemdb_weight(nameid)*amount; + if(amount > MAX_AMOUNT || weight + sd->weight > sd->max_weight){ + push_val(st->stack,C_INT,0); + } else { + //Check if the inventory ain't full. + //TODO: Currently does not checks if you can just stack it on top of another item you already have.... + + i = pc_search_inventory(sd,0); + if (i >= 0) //Empty slot available. + push_val(st->stack,C_INT,1); + else //Inventory full + push_val(st->stack,C_INT,0); + + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_getitem(struct script_state *st) +{ + int nameid,nameidsrc,amount,flag = 0; + struct item item_tmp; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + nameid=512; //Apple item ID + if( item_data != NULL) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + if ( ( amount=conv_num(st,& (st->stack->stack_data[st->start+3])) ) <= 0) { + return 0; //return if amount <=0, skip the useles iteration + } + //Violet Box, Blue Box, etc - random item pick + if((nameidsrc = nameid)<0) { // Save real ID of the source Box [Lupus] + nameid=itemdb_searchrandomid(-nameid); + + flag = 1; + } + + if(nameid > 0) { + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid=nameid; + if(!flag) + item_tmp.identify=1; + else + item_tmp.identify=!itemdb_isequip3(nameid); + if( st->end>st->start+5 ) //アイテムを指定したIDに渡す + sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+5]))); + if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り + return 0; + if((flag = pc_additem(sd,&item_tmp,amount))) { + clif_additem(sd,0,0,flag); + if(pc_candrop(sd,nameid)) + map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, nameid, amount, NULL); + } + //Logs + + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_getitem2(struct script_state *st) +{ + int nameid,amount,flag = 0; + int iden,ref,attr,c1,c2,c3,c4; + struct item_data *item_data; + struct item item_tmp; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + nameid=512; //Apple item ID + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + iden=conv_num(st,& (st->stack->stack_data[st->start+4])); + ref=conv_num(st,& (st->stack->stack_data[st->start+5])); + attr=conv_num(st,& (st->stack->stack_data[st->start+6])); + c1=conv_num(st,& (st->stack->stack_data[st->start+7])); + c2=conv_num(st,& (st->stack->stack_data[st->start+8])); + c3=conv_num(st,& (st->stack->stack_data[st->start+9])); + c4=conv_num(st,& (st->stack->stack_data[st->start+10])); + if( st->end>st->start+11 ) //アイテムを指定したIDに渡す + sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+11]))); + if(sd == NULL) //アイテムを渡す相手がいなかったらお帰り + return 0; + + if(nameid<0) { // ランダム + nameid=itemdb_searchrandomid(-nameid); + flag = 1; + } + + if(nameid > 0) { + memset(&item_tmp,0,sizeof(item_tmp)); + item_data=itemdb_exists(nameid); + if (item_data == NULL) + return -1; + if(item_data->type==4 || item_data->type==5){ + if(ref > 10) ref = 10; + } + else if(item_data->type==7) { + iden = 1; + ref = 0; + } + else { + iden = 1; + ref = attr = 0; + } + + item_tmp.nameid=nameid; + if(!flag) + item_tmp.identify=iden; + else if(item_data->type==4 || item_data->type==5) + item_tmp.identify=0; + item_tmp.refine=ref; + item_tmp.attribute=attr; + item_tmp.card[0]=c1; + item_tmp.card[1]=c2; + item_tmp.card[2]=c3; + item_tmp.card[3]=c4; + if((flag = pc_additem(sd,&item_tmp,amount))) { + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, nameid, amount, &item_tmp); + } + //Logs + } + + return 0; +} + +/*========================================== + * gets an item with someone's name inscribed [Skotlex] + * getinscribeditem item_num, character_name + * Returned Qty is always 1, only works on equip-able + * equipment + *------------------------------------------ + */ +int buildin_getnameditem(struct script_state *st) +{ + int nameid; + struct item item_tmp; + struct map_session_data *sd, *tsd; + struct script_data *data; + + sd = script_rid2sd(st); + if (sd == NULL) + { //Player not attached! + push_val(st->stack,C_INT,0); + return 0; + } + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + if( item_data == NULL) + { //Failed + push_val(st->stack,C_INT,0); + return 0; + } + nameid = item_data->nameid; + }else + nameid = conv_num(st,data); + + if(!itemdb_exists(nameid) || !itemdb_isequip3(nameid)) + { //We don't allow non-equipable/stackable items to be named + //to avoid any qty exploits that could happen because of it. + push_val(st->stack,C_INT,0); + return 0; + } + + data=&(st->stack->stack_data[st->start+3]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ) //Char Name + tsd=map_nick2sd(conv_str(st,data)); + else //Char Id was given + tsd=map_charid2sd(conv_num(st,data)); + + if( tsd == NULL ) + { //Failed + push_val(st->stack,C_INT,0); + return 0; + } + + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid=nameid; + item_tmp.amount=1; + item_tmp.identify=1; + item_tmp.card[0]=254; //we don't use 255! because for example SIGNED WEAPON shouldn't get TOP10 BS Fame bonus [Lupus] + item_tmp.card[2]=tsd->status.char_id; + item_tmp.card[3]=tsd->status.char_id >> 16; + if(pc_additem(sd,&item_tmp,1)) { + push_val(st->stack,C_INT,0); + return 0; //Failed to add item, we will not drop if they don't fit + } + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, item_tmp.nameid, item_tmp.amount, &item_tmp); + } + //Logs + + push_val(st->stack,C_INT,1); + return 0; +} + +/*========================================== + * gets a random item ID from an item group [Skotlex] + * groupranditem group_num + *------------------------------------------ + */ +int buildin_grouprandomitem(struct script_state *st) +{ + int group; + + group = conv_num(st,& (st->stack->stack_data[st->start+2])); + push_val(st->stack, C_INT, itemdb_searchrandomid(group)); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_makeitem(struct script_state *st) +{ + int nameid,amount,flag = 0; + int x,y,m; + char *mapname; + struct item item_tmp; + struct script_data *data; + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + nameid=512; //Apple Item ID + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + mapname =conv_str(st,& (st->stack->stack_data[st->start+4])); + x =conv_num(st,& (st->stack->stack_data[st->start+5])); + y =conv_num(st,& (st->stack->stack_data[st->start+6])); + + if(strcmp(mapname,"this")==0) + { + struct map_session_data *sd; + sd = script_rid2sd(st); + if (!sd) return 0; //Failed... + m=sd->bl.m; + } else + m=map_mapname2mapid(mapname); + + if(nameid<0) { // ランダム + nameid=itemdb_searchrandomid(-nameid); + flag = 1; + } + + if(nameid > 0) { + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid=nameid; + if(!flag) + item_tmp.identify=1; + else + item_tmp.identify=!itemdb_isequip3(nameid); + + map_addflooritem(&item_tmp,amount,m,x,y,NULL,NULL,NULL,0); + } + + return 0; +} +/*========================================== + * script DELITEM command (fixed 2 bugs by Lupus, added deletion priority by Lupus) + *------------------------------------------ + */ +int buildin_delitem(struct script_state *st) +{ + int nameid=0,amount,i,important_item=0; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + //nameid=512; + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0 + //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); + return 0; + } + //1st pass + //here we won't delete items with CARDS, named items but we count them + for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || + sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ) + continue; + //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle + if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){ + intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) ); + //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^) + sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0; + //now this egg'll be deleted as a common unimportant item + } + //is this item important? does it have cards? or Player's name? or Refined/Upgraded + if( sd->status.inventory[i].card[0] || sd->status.inventory[i].card[1] || + sd->status.inventory[i].card[2] || sd->status.inventory[i].card[3] || sd->status.inventory[i].refine) { + //this is important item, count it + important_item++; + continue; + } + + if(sd->status.inventory[i].amount>=amount){ + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd,i,amount,0); + return 0; //we deleted exact amount of items. now exit + } else { + amount-=sd->status.inventory[i].amount; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd,i,sd->status.inventory[i].amount,0); + } + } + //2nd pass + //now if there WERE items with CARDs/REFINED/NAMED... and if we still have to delete some items. we'll delete them finally + if (important_item>0 && amount>0) + for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || + sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid ) + continue; + + if(sd->status.inventory[i].amount>=amount){ + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd,i,amount,0); + return 0; //we deleted exact amount of items. now exit + } else { + amount-=sd->status.inventory[i].amount; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd,i,sd->status.inventory[i].amount,0); + } + } + + return 0; +} + +/*========================================== +* advanced version of delitem [modified by Mihilion] +*------------------------------------------ +*/ +int buildin_delitem2(struct script_state *st) +{ + int nameid=0,amount,i=0; + int iden,ref,attr,c1,c2,c3,c4; + struct map_session_data *sd; + struct script_data *data; + + sd = script_rid2sd(st); + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + //nameid=512; + if( item_data ) + nameid=item_data->nameid; + }else + nameid=conv_num(st,data); + + amount=conv_num(st,& (st->stack->stack_data[st->start+3])); + iden=conv_num(st,& (st->stack->stack_data[st->start+4])); + ref=conv_num(st,& (st->stack->stack_data[st->start+5])); + attr=conv_num(st,& (st->stack->stack_data[st->start+6])); + c1=conv_num(st,& (st->stack->stack_data[st->start+7])); + c2=conv_num(st,& (st->stack->stack_data[st->start+8])); + c3=conv_num(st,& (st->stack->stack_data[st->start+9])); + c4=conv_num(st,& (st->stack->stack_data[st->start+10])); + + if (nameid<500 || amount<=0 ) {//by Lupus. Don't run FOR if u got wrong item ID or amount<=0 + //eprintf("wrong item ID or amount<=0 : delitem %i,\n",nameid,amount); + return 0; + } + + for(i=0;istatus.inventory[i].nameid<=0 || sd->inventory_data[i] == NULL || + sd->status.inventory[i].amount<=0 || sd->status.inventory[i].nameid!=nameid || + sd->status.inventory[i].identify!=iden || sd->status.inventory[i].refine!=ref || + sd->status.inventory[i].attribute!=attr || sd->status.inventory[i].card[0]!=c1 || + sd->status.inventory[i].card[1]!=c2 || sd->status.inventory[i].card[2]!=c3 || + sd->status.inventory[i].card[3]!=c4) + continue; + //1 egg uses 1 cell in the inventory. so it's ok to delete 1 pet / per cycle + if(sd->inventory_data[i]->type==7 && sd->status.inventory[i].card[0] == (short)0xff00 && search_petDB_index(nameid, PET_EGG) >= 0 ){ + intif_delete_petdata( MakeDWord(sd->status.inventory[i].card[1], sd->status.inventory[i].card[2]) ); + //clear egg flag. so it won't be put in IMPORTANT items (eggs look like item with 2 cards ^_^) + sd->status.inventory[i].card[1] = sd->status.inventory[i].card[0] = 0; + //now this egg'll be deleted as a common unimportant item + } + + if(sd->status.inventory[i].amount>=amount){ + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -amount, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd,i,amount,0); + return 0; //we deleted exact amount of items. now exit + } else { + amount-=sd->status.inventory[i].amount; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd,i,sd->status.inventory[i].amount,0); + } + } + return 0; +} + +/*========================================== + * Enables/Disables use of items while in an NPC [Skotlex] + *------------------------------------------ + */ +int buildin_enableitemuse(struct script_state *st) { + struct map_session_data *sd; + sd=script_rid2sd(st); + if (sd) + sd->npc_item_flag = st->oid; + return 0; +} + +int buildin_disableitemuse(struct script_state *st) { + struct map_session_data *sd; + sd=script_rid2sd(st); + if (sd) + sd->npc_item_flag = 0; + return 0; +} + +/*========================================== + *キャラ関係のパラメータ取得 + *------------------------------------------ + */ +int buildin_readparam(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end>st->start+3 ) + sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + sd=script_rid2sd(st); + + if(sd==NULL){ + push_val(st->stack,C_INT,-1); + return 0; + } + + push_val(st->stack,C_INT,pc_readparam(sd,type)); + + return 0; +} +/*========================================== + *キャラ関係のID取得 + *------------------------------------------ + */ +int buildin_getcharid(struct script_state *st) +{ + int num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end>st->start+3 ) + sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + sd=script_rid2sd(st); + if(sd==NULL){ + push_val(st->stack,C_INT,-1); + return 0; + } + if(num==0) + push_val(st->stack,C_INT,sd->status.char_id); + if(num==1) + push_val(st->stack,C_INT,sd->status.party_id); + if(num==2) + push_val(st->stack,C_INT,sd->status.guild_id); + if(num==3) + push_val(st->stack,C_INT,sd->status.account_id); + return 0; +} +/*========================================== + *指定IDのPT名取得 + *------------------------------------------ + */ +char *buildin_getpartyname_sub(int party_id) +{ + struct party *p; + + p=NULL; + p=party_search(party_id); + + if(p!=NULL){ + char *buf; + buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); + memcpy(buf, p->name, NAME_LENGTH-1); + buf[NAME_LENGTH-1] = '\0'; + return buf; + } + + return 0; +} +int buildin_getpartyname(struct script_state *st) +{ + char *name; + int party_id; + + party_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + name=buildin_getpartyname_sub(party_id); + if(name != NULL) + push_str(st->stack,C_STR,(unsigned char *)name); + else + push_str(st->stack,C_CONSTSTR, (unsigned char *) "null"); + + return 0; +} +/*========================================== + *指定IDのPT人数とメンバーID取得 + *------------------------------------------ + */ +int buildin_getpartymember(struct script_state *st) +{ + struct party *p; + int i,j=0,type=0; + + p=NULL; + p=party_search(conv_num(st,& (st->stack->stack_data[st->start+2]))); + + if( st->end>st->start+3 ) + type=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if(p!=NULL){ + for(i=0;imember[i].account_id){ + switch (type) { + case 2: + mapreg_setreg(add_str((unsigned char *) "$@partymemberaid")+(j<<24),p->member[i].account_id); + break; + case 1: + mapreg_setreg(add_str((unsigned char *) "$@partymembercid")+(j<<24),p->member[i].char_id); + break; + default: + mapreg_setregstr(add_str((unsigned char *) "$@partymembername$")+(j<<24),p->member[i].name); + } + j++; + } + } + } + mapreg_setreg(add_str((unsigned char *) "$@partymembercount"),j); + + return 0; +} +/*========================================== + *指定IDのギルド名取得 + *------------------------------------------ + */ +char *buildin_getguildname_sub(int guild_id) +{ + struct guild *g=NULL; + g=guild_search(guild_id); + + if(g!=NULL){ + char *buf; + buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); + memcpy(buf, g->name, NAME_LENGTH-1); + buf[NAME_LENGTH-1] = '\0'; + return buf; + } + return NULL; +} +int buildin_getguildname(struct script_state *st) +{ + char *name; + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + name=buildin_getguildname_sub(guild_id); + if(name != NULL) + push_str(st->stack,C_STR,(unsigned char *) name); + else + push_str(st->stack,C_CONSTSTR,(unsigned char *) "null"); + return 0; +} + +/*========================================== + *指定IDのGuildMaster名取得 + *------------------------------------------ + */ +char *buildin_getguildmaster_sub(int guild_id) +{ + struct guild *g=NULL; + g=guild_search(guild_id); + + if(g!=NULL){ + char *buf; + buf=(char *)aMallocA(NAME_LENGTH*sizeof(char)); + memcpy(buf, g->master, NAME_LENGTH-1); + buf[NAME_LENGTH-1] = '\0'; + return buf; + } + + return 0; +} +int buildin_getguildmaster(struct script_state *st) +{ + char *master; + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + master=buildin_getguildmaster_sub(guild_id); + if(master!=0) + push_str(st->stack,C_STR,(unsigned char *) master); + else + push_str(st->stack,C_CONSTSTR,(unsigned char *) "null"); + return 0; +} + +int buildin_getguildmasterid(struct script_state *st) +{ + char *master; + struct map_session_data *sd=NULL; + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + master=buildin_getguildmaster_sub(guild_id); + if(master!=0){ + if((sd=map_nick2sd(master)) == NULL){ + push_val(st->stack,C_INT,0); + return 0; + } + push_val(st->stack,C_INT,sd->status.char_id); + }else{ + push_val(st->stack,C_INT,0); + } + return 0; +} + +/*========================================== + * キャラクタの名前 + *------------------------------------------ + */ +int buildin_strcharinfo(struct script_state *st) +{ + struct map_session_data *sd; + int num; + char *buf; + + sd=script_rid2sd(st); + if (!sd) { //Avoid crashing.... + push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); + return 0; + } + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + switch(num){ + case 0: + push_str(st->stack,C_CONSTSTR,(unsigned char *) sd->status.name); + break; + case 1: + buf=buildin_getpartyname_sub(sd->status.party_id); + if(buf!=0) + push_str(st->stack,C_STR,(unsigned char *) buf); + else + push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); + break; + case 2: + buf=buildin_getguildname_sub(sd->status.guild_id); + if(buf != NULL) + push_str(st->stack,C_STR,(unsigned char *) buf); + else + push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); + break; + default: + ShowWarning("buildin_strcharinfo: unknown parameter."); + push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); + break; + } + + return 0; +} + +unsigned int equip[10]={0x0100,0x0010,0x0020,0x0002,0x0004,0x0040,0x0008,0x0080,0x0200,0x0001}; + +/*========================================== + * GetEquipID(Pos); Pos: 1-10 + *------------------------------------------ + */ +int buildin_getequipid(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + struct item_data* item; + + sd=script_rid2sd(st); + if(sd == NULL) + { + ShowError("getequipid: sd == NULL\n"); + return 0; + } + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0){ + item=sd->inventory_data[i]; + if(item) + push_val(st->stack,C_INT,item->nameid); + else + push_val(st->stack,C_INT,0); + }else{ + push_val(st->stack,C_INT,-1); + } + return 0; +} + +/*========================================== + * 装備名文字列(精錬メニュー用) + *------------------------------------------ + */ +int buildin_getequipname(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + struct item_data* item; + char *buf; + + buf=(char *)aMallocA(64*sizeof(char)); + sd=script_rid2sd(st); + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0){ + item=sd->inventory_data[i]; + if(item) + sprintf(buf,"%s-[%s]",pos[num-1],item->jname); + else + sprintf(buf,"%s-[%s]",pos[num-1],pos[10]); + }else{ + sprintf(buf,"%s-[%s]",pos[num-1],pos[10]); + } + push_str(st->stack,C_STR,(unsigned char *) buf); + + return 0; +} + +/*========================================== + * getbrokenid [Valaris] + *------------------------------------------ + */ +int buildin_getbrokenid(struct script_state *st) +{ + int i,num,id=0,brokencounter=0; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + for(i=0; istatus.inventory[i].attribute==1){ + brokencounter++; + if(num==brokencounter){ + id=sd->status.inventory[i].nameid; + break; + } + } + } + + push_val(st->stack,C_INT,id); + + return 0; +} + +/*========================================== + * repair [Valaris] + *------------------------------------------ + */ +int buildin_repair(struct script_state *st) +{ + int i,num; + int repaircounter=0; + struct map_session_data *sd; + + + sd=script_rid2sd(st); + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + for(i=0; istatus.inventory[i].attribute==1){ + repaircounter++; + if(num==repaircounter){ + sd->status.inventory[i].attribute=0; + clif_equiplist(sd); + clif_produceeffect(sd, 0, sd->status.inventory[i].nameid); + clif_misceffect(&sd->bl, 3); + clif_displaymessage(sd->fd,"Item has been repaired."); + break; + } + } + } + + return 0; +} + +/*========================================== + * 装備チェック + *------------------------------------------ + */ +int buildin_getequipisequiped(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + + if ((num - 1) >= (sizeof(equip) / sizeof(equip[0]))) + i = -1; + else + i=pc_checkequip(sd,equip[num-1]); + + if(i >= 0) + push_val(st->stack,C_INT,1); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 装備品精錬可能チェック + *------------------------------------------ + */ +int buildin_getequipisenableref(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0 && num<7 && sd->inventory_data[i] && !sd->inventory_data[i]->flag.no_refine) + { + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + +/*========================================== + * 装備品鑑定チェック + *------------------------------------------ + */ +int buildin_getequipisidentify(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) + push_val(st->stack,C_INT,sd->status.inventory[i].identify); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 装備品精錬度 + *------------------------------------------ + */ +int buildin_getequiprefinerycnt(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) + push_val(st->stack,C_INT,sd->status.inventory[i].refine); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 装備品武器LV + *------------------------------------------ + */ +int buildin_getequipweaponlv(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0 && sd->inventory_data[i]) + push_val(st->stack,C_INT,sd->inventory_data[i]->wlv); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 装備品精錬成功率 + *------------------------------------------ + */ +int buildin_getequippercentrefinery(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0 && sd->status.inventory[i].nameid && sd->status.inventory[i].refine < MAX_REFINE) + push_val(st->stack,C_INT,percentrefinery[itemdb_wlv(sd->status.inventory[i].nameid)][(int)sd->status.inventory[i].refine]); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * 精錬成功 + *------------------------------------------ + */ +int buildin_successrefitem(struct script_state *st) +{ + int i,num,ep; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) { + ep=sd->status.inventory[i].equip; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); + } + //Logs + + sd->status.inventory[i].refine++; + pc_unequipitem(sd,i,2); + + clif_refine(sd->fd,sd,0,i,sd->status.inventory[i].refine); + clif_delitem(sd,i,1); + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, 1, &sd->status.inventory[i]); + } + //Logs + + clif_additem(sd,i,1,0); + pc_equipitem(sd,i,ep); + clif_misceffect(&sd->bl,3); + if(sd->status.inventory[i].refine == 10 && sd->status.inventory[i].card[0] == 0x00ff && sd->char_id == MakeDWord(sd->status.inventory[i].card[2],sd->status.inventory[i].card[3])){ // Fame point system [DracoRPG] + switch (sd->inventory_data[i]->wlv){ + case 1: + pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point + break; + case 2: + pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point + break; + case 3: + pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point + break; + } + } + } + + return 0; +} + +/*========================================== + * 精錬失敗 + *------------------------------------------ + */ +int buildin_failedrefitem(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0) { + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); + } + //Logs + + sd->status.inventory[i].refine = 0; + pc_unequipitem(sd,i,3); + // 精錬失敗エフェクトのパケット + clif_refine(sd->fd,sd,1,i,sd->status.inventory[i].refine); + + pc_delitem(sd,i,1,0); + // 他の人にも失敗を通知 + clif_misceffect(&sd->bl,2); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_statusup(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + pc_statusup(sd,type); + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_statusup2(struct script_state *st) +{ + int type,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + pc_statusup2(sd,type,val); + + return 0; +} +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +int buildin_bonus(struct script_state *st) +{ + int type,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + pc_bonus(sd,type,val); + + return 0; +} +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +int buildin_bonus2(struct script_state *st) +{ + int type,type2,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + type2=conv_num(st,& (st->stack->stack_data[st->start+3])); + val=conv_num(st,& (st->stack->stack_data[st->start+4])); + sd=script_rid2sd(st); + pc_bonus2(sd,type,type2,val); + + return 0; +} +/*========================================== + * 装備品による能力値ボーナス + *------------------------------------------ + */ +int buildin_bonus3(struct script_state *st) +{ + int type,type2,type3,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + type2=conv_num(st,& (st->stack->stack_data[st->start+3])); + type3=conv_num(st,& (st->stack->stack_data[st->start+4])); + val=conv_num(st,& (st->stack->stack_data[st->start+5])); + sd=script_rid2sd(st); + pc_bonus3(sd,type,type2,type3,val); + + return 0; +} + +int buildin_bonus4(struct script_state *st) +{ + int type,type2,type3,type4,val; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + type2=conv_num(st,& (st->stack->stack_data[st->start+3])); + type3=conv_num(st,& (st->stack->stack_data[st->start+4])); + type4=conv_num(st,& (st->stack->stack_data[st->start+5])); + val=conv_num(st,& (st->stack->stack_data[st->start+6])); + sd=script_rid2sd(st); + pc_bonus4(sd,type,type2,type3,type4,val); + + return 0; +} +/*========================================== + * スキル所得 + *------------------------------------------ + */ +int buildin_skill(struct script_state *st) +{ + int id,level,flag=1; + struct map_session_data *sd; + + id=conv_num(st,& (st->stack->stack_data[st->start+2])); + level=conv_num(st,& (st->stack->stack_data[st->start+3])); + if( st->end>st->start+4 ) + flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); + sd=script_rid2sd(st); + pc_skill(sd,id,level,flag); + + return 0; +} + +// add x levels of skill (stackable) [Valaris] +int buildin_addtoskill(struct script_state *st) +{ + int id,level,flag=2; + struct map_session_data *sd; + + id=conv_num(st,& (st->stack->stack_data[st->start+2])); + level=conv_num(st,& (st->stack->stack_data[st->start+3])); + if( st->end>st->start+4 ) + flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); + sd=script_rid2sd(st); + pc_skill(sd,id,level,flag); + + return 0; +} + +/*========================================== + * ギルドスキル取得 + *------------------------------------------ + */ +int buildin_guildskill(struct script_state *st) +{ + int id,level,flag=0; + struct map_session_data *sd; + int i=0; + + id=conv_num(st,& (st->stack->stack_data[st->start+2])); + level=conv_num(st,& (st->stack->stack_data[st->start+3])); + if( st->end>st->start+4 ) + flag=conv_num(st,&(st->stack->stack_data[st->start+4]) ); + sd=script_rid2sd(st); + for(i=0;istack->stack_data[st->start+2])); + push_val(st->stack,C_INT, pc_checkskill( script_rid2sd(st) ,id) ); + return 0; +} +/*========================================== + * getgdskilllv(Guild_ID, Skill_ID); + * skill_id = 10000 : GD_APPROVAL + * 10001 : GD_KAFRACONTRACT + * 10002 : GD_GUARDIANRESEARCH + * 10003 : GD_GUARDUP + * 10004 : GD_EXTENSION + *------------------------------------------ + */ +int buildin_getgdskilllv(struct script_state *st) +{ + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + int skill_id=conv_num(st,& (st->stack->stack_data[st->start+3])); + struct guild *g=guild_search(guild_id); + push_val(st->stack,C_INT, (g==NULL)?-1:guild_checkskill(g,skill_id) ); + return 0; +/* + struct map_session_data *sd=NULL; + struct guild *g=NULL; + int skill_id; + + skill_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + if(sd && sd->status.guild_id > 0) g=guild_search(sd->status.guild_id); + if(sd && g) { + push_val(st->stack,C_INT, guild_checkskill(g,skill_id+9999) ); + } else { + push_val(st->stack,C_INT,-1); + } + return 0; +*/ +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_basicskillcheck(struct script_state *st) +{ + push_val(st->stack,C_INT, battle_config.basic_skill_check); + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_getgmlevel(struct script_state *st) +{ + push_val(st->stack,C_INT, pc_isGM(script_rid2sd(st))); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_end(struct script_state *st) +{ + st->state = END; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_checkoption(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + + if(sd->sc.option & type){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_checkoption1(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + + if(sd->sc.opt1 & type){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int buildin_checkoption2(struct script_state *st) +{ + int type; + struct map_session_data *sd; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + + if(sd->sc.opt2 & type){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_setoption(struct script_state *st) +{ + int type; + struct map_session_data *sd; + int flag=1; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(st->end>st->start+3 ) + flag=conv_num(st,&(st->stack->stack_data[st->start+3]) ); + + sd=script_rid2sd(st); + if (!sd) return 0; + + if (flag) {//Add option + if (type&OPTION_WEDDING && !battle_config.wedding_modifydisplay) + type&=~OPTION_WEDDING; //Do not show the wedding sprites + pc_setoption(sd,sd->sc.option|type); + } else//Remove option + pc_setoption(sd,sd->sc.option&~type); + return 0; +} + +/*========================================== + * Checkcart [Valaris] + *------------------------------------------ + */ + +int buildin_checkcart(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(pc_iscarton(sd)){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + return 0; +} + +/*========================================== + * カートを付ける + *------------------------------------------ + */ +int buildin_setcart(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + pc_setcart(sd,1); + + return 0; +} + +/*========================================== + * checkfalcon [Valaris] + *------------------------------------------ + */ + +int buildin_checkfalcon(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(pc_isfalcon(sd)){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + + +/*========================================== + * 鷹を付ける + *------------------------------------------ + */ +int buildin_setfalcon(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + pc_setfalcon(sd); + + return 0; +} + +/*========================================== + * Checkcart [Valaris] + *------------------------------------------ + */ + +int buildin_checkriding(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(pc_isriding(sd)){ + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + + +/*========================================== + * ペコペコ乗り + *------------------------------------------ + */ +int buildin_setriding(struct script_state *st) +{ + struct map_session_data *sd; + + sd=script_rid2sd(st); + pc_setriding(sd); + + return 0; +} + +/*========================================== + * セーブポイントの保存 + *------------------------------------------ + */ +int buildin_savepoint(struct script_state *st) +{ + int x,y; + short map; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + map = mapindex_name2id(str); + if (map) + pc_setsavepoint(script_rid2sd(st),map,x,y); + return 0; +} + +/*========================================== + * GetTimeTick(0: System Tick, 1: Time Second Tick) + *------------------------------------------ + */ +int buildin_gettimetick(struct script_state *st) /* Asgard Version */ +{ + int type; + time_t timer; + struct tm *t; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + + switch(type){ + case 2: + //type 2:(Get the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC + // from the system clock.) + push_val(st->stack,C_INT,(int)time(NULL)); + break; + case 1: + //type 1:(Second Ticks: 0-86399, 00:00:00-23:59:59) + time(&timer); + t=localtime(&timer); + push_val(st->stack,C_INT,((t->tm_hour)*3600+(t->tm_min)*60+t->tm_sec)); + break; + case 0: + default: + //type 0:(System Ticks) + push_val(st->stack,C_INT,gettick()); + break; + } + return 0; +} + +/*========================================== + * GetTime(Type); + * 1: Sec 2: Min 3: Hour + * 4: WeekDay 5: MonthDay 6: Month + * 7: Year + *------------------------------------------ + */ +int buildin_gettime(struct script_state *st) /* Asgard Version */ +{ + int type; + time_t timer; + struct tm *t; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + + time(&timer); + t=localtime(&timer); + + switch(type){ + case 1://Sec(0~59) + push_val(st->stack,C_INT,t->tm_sec); + break; + case 2://Min(0~59) + push_val(st->stack,C_INT,t->tm_min); + break; + case 3://Hour(0~23) + push_val(st->stack,C_INT,t->tm_hour); + break; + case 4://WeekDay(0~6) + push_val(st->stack,C_INT,t->tm_wday); + break; + case 5://MonthDay(01~31) + push_val(st->stack,C_INT,t->tm_mday); + break; + case 6://Month(01~12) + push_val(st->stack,C_INT,t->tm_mon+1); + break; + case 7://Year(20xx) + push_val(st->stack,C_INT,t->tm_year+1900); + break; + case 8://Year Day(01~366) + push_val(st->stack,C_INT,t->tm_yday+1); + break; + default://(format error) + push_val(st->stack,C_INT,-1); + break; + } + return 0; +} + +/*========================================== + * GetTimeStr("TimeFMT", Length); + *------------------------------------------ + */ +int buildin_gettimestr(struct script_state *st) +{ + char *tmpstr; + char *fmtstr; + int maxlen; + time_t now = time(NULL); + + fmtstr=conv_str(st,& (st->stack->stack_data[st->start+2])); + maxlen=conv_num(st,& (st->stack->stack_data[st->start+3])); + + tmpstr=(char *)aMallocA((maxlen+1)*sizeof(char)); + strftime(tmpstr,maxlen,fmtstr,localtime(&now)); + tmpstr[maxlen]='\0'; + + push_str(st->stack,C_STR,(unsigned char *) tmpstr); + return 0; +} + +/*========================================== + * カプラ倉庫を開く + *------------------------------------------ + */ +int buildin_openstorage(struct script_state *st) +{ + storage_storageopen(script_rid2sd(st)); + return 0; +} + +int buildin_guildopenstorage(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int ret; + ret = storage_guild_storageopen(sd); + push_val(st->stack,C_INT,ret); + return 0; +} + +/*========================================== + * アイテムによるスキル発動 + *------------------------------------------ + */ +int buildin_itemskill(struct script_state *st) +{ + int id,lv; + char *str; + struct map_session_data *sd=script_rid2sd(st); + + id=conv_num(st,& (st->stack->stack_data[st->start+2])); + lv=conv_num(st,& (st->stack->stack_data[st->start+3])); + str=conv_str(st,& (st->stack->stack_data[st->start+4])); + + // 詠唱中にスキルアイテムは使用できない + if(sd->ud.skilltimer != -1) + return 0; + + sd->skillitem=id; + sd->skillitemlv=lv; + clif_item_skill(sd,id,lv,str); + return 0; +} +/*========================================== + * アイテム作成 + *------------------------------------------ + */ +int buildin_produce(struct script_state *st) +{ + int trigger; + struct map_session_data *sd=script_rid2sd(st); + + trigger=conv_num(st,& (st->stack->stack_data[st->start+2])); + clif_skill_produce_mix_list(sd, trigger); + return 0; +} +/*========================================== + * NPCでペット作る + *------------------------------------------ + */ +int buildin_makepet(struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd(st); + struct script_data *data; + int id,pet_id; + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + + id=conv_num(st,data); + + pet_id = search_petDB_index(id, PET_CLASS); + + if (pet_id < 0) + pet_id = search_petDB_index(id, PET_EGG); + if (pet_id >= 0 && sd) { + 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 0; +} +/*========================================== + * NPCで経験値上げる + *------------------------------------------ + */ +int buildin_getexp(struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd(st); + int base=0,job=0; + + base=conv_num(st,& (st->stack->stack_data[st->start+2])); + job =conv_num(st,& (st->stack->stack_data[st->start+3])); + if(base<0 || job<0) + return 0; + if(sd) + pc_gainexp(sd,base,job); + + return 0; +} + +/*========================================== + * Gain guild exp [Celest] + *------------------------------------------ + */ +int buildin_guildgetexp(struct script_state *st) +{ + struct map_session_data *sd = script_rid2sd(st); + int exp; + + exp = conv_num(st,& (st->stack->stack_data[st->start+2])); + if(exp < 0) + return 0; + if(sd && sd->status.guild_id > 0) + guild_getexp (sd, exp); + + return 0; +} + +/*========================================== + * Changes the guild master of a guild [Skotlex] + *------------------------------------------ + */ +int buildin_guildchangegm(struct script_state *st) +{ + struct map_session_data *sd; + int guild_id; + char *name; + + guild_id = conv_num(st,& (st->stack->stack_data[st->start+2])); + name = conv_str(st,& (st->stack->stack_data[st->start+3])); + sd=map_nick2sd(name); + + if (!sd) + push_val(st->stack,C_INT,0); + else + push_val(st->stack,C_INT,guild_gm_change(guild_id, sd)); + + return 0; +} + +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +int buildin_monster(struct script_state *st) +{ + int class_,amount,x,y; + char *str,*map,*event=""; + + map =conv_str(st,& (st->stack->stack_data[st->start+2])); + x =conv_num(st,& (st->stack->stack_data[st->start+3])); + y =conv_num(st,& (st->stack->stack_data[st->start+4])); + str =conv_str(st,& (st->stack->stack_data[st->start+5])); + class_=conv_num(st,& (st->stack->stack_data[st->start+6])); + amount=conv_num(st,& (st->stack->stack_data[st->start+7])); + if( st->end>st->start+8 ){ + event=conv_str(st,& (st->stack->stack_data[st->start+8])); + check_event(st, event); + } + + if (class_ >= 0 && !mobdb_checkid(class_)) { + ShowWarning("buildin_monster: Attempted to spawn non-existing monster class %d\n", class_); + return 1; + } + mob_once_spawn(map_id2sd(st->rid),map,x,y,str,class_,amount,event); + return 0; +} +/*========================================== + * モンスター発生 + *------------------------------------------ + */ +int buildin_areamonster(struct script_state *st) +{ + int class_,amount,x0,y0,x1,y1; + char *str,*map,*event=""; + + map =conv_str(st,& (st->stack->stack_data[st->start+2])); + x0 =conv_num(st,& (st->stack->stack_data[st->start+3])); + y0 =conv_num(st,& (st->stack->stack_data[st->start+4])); + x1 =conv_num(st,& (st->stack->stack_data[st->start+5])); + y1 =conv_num(st,& (st->stack->stack_data[st->start+6])); + str =conv_str(st,& (st->stack->stack_data[st->start+7])); + class_=conv_num(st,& (st->stack->stack_data[st->start+8])); + amount=conv_num(st,& (st->stack->stack_data[st->start+9])); + if( st->end>st->start+10 ){ + event=conv_str(st,& (st->stack->stack_data[st->start+10])); + check_event(st, event); + } + + mob_once_spawn_area(map_id2sd(st->rid),map,x0,y0,x1,y1,str,class_,amount,event); + return 0; +} +/*========================================== + * モンスター削除 + *------------------------------------------ + */ +int buildin_killmonster_sub(struct block_list *bl,va_list ap) +{ + TBL_MOB* md = (TBL_MOB*)bl; + char *event=va_arg(ap,char *); + int allflag=va_arg(ap,int); + + if(!allflag){ + if(strcmp(event,md->npc_event)==0) + unit_remove_map(bl,1); + return 0; + }else{ + if(!md->spawn) + unit_remove_map(bl,1); + return 0; + } + return 0; +} +int buildin_killmonster(struct script_state *st) +{ + char *mapname,*event; + int m,allflag=0; + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + if(strcmp(event,"All")==0) + allflag = 1; + else + check_event(st, event); + + if( (m=map_mapname2mapid(mapname))<0 ) + return 0; + map_foreachinmap(buildin_killmonster_sub, m, BL_MOB, event ,allflag); + return 0; +} + +int buildin_killmonsterall_sub(struct block_list *bl,va_list ap) +{ + unit_remove_map(bl,1); + return 0; +} +int buildin_killmonsterall(struct script_state *st) +{ + char *mapname; + int m; + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + + if( (m=map_mapname2mapid(mapname))<0 ) + return 0; + map_foreachinmap(buildin_killmonsterall_sub, + m,BL_MOB); + return 0; +} + +/*========================================== + * Creates a clone of a player. + * clone map, x, y, event, char_id, master_id, mode, flag, duration + *------------------------------------------ + */ +int buildin_clone(struct script_state *st) { + struct map_session_data *sd, *msd=NULL; + int char_id,master_id=0,x,y, mode = 0, flag = 0, m; + unsigned int duration = 0; + char *map,*event=""; + + map=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + event=conv_str(st,& (st->stack->stack_data[st->start+5])); + char_id=conv_num(st,& (st->stack->stack_data[st->start+6])); + + if( st->end>st->start+7 ) + master_id=conv_num(st,& (st->stack->stack_data[st->start+7])); + + if( st->end>st->start+8 ) + mode=conv_num(st,& (st->stack->stack_data[st->start+8])); + + if( st->end>st->start+9 ) + flag=conv_num(st,& (st->stack->stack_data[st->start+9])); + + if( st->end>st->start+10 ) + duration=conv_num(st,& (st->stack->stack_data[st->start+10])); + + check_event(st, event); + + m = map_mapname2mapid(map); + if (m < 0) return 0; + + sd = map_charid2sd(char_id); + + if (master_id) { + msd = map_charid2sd(master_id); + if (msd) + master_id = msd->bl.id; + else + master_id = 0; + } + if (sd) //Return ID of newly crafted clone. + push_val(st->stack,C_INT,mob_clone_spawn(sd, m, x, y, event, master_id, mode, flag, 1000*duration)); + else //Failed to create clone. + push_val(st->stack,C_INT,0); + + return 0; +} +/*========================================== + * イベント実行 + *------------------------------------------ + */ +int buildin_doevent(struct script_state *st) +{ + char *event; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + check_event(st, event); + npc_event(map_id2sd(st->rid),event,0); + return 0; +} +/*========================================== + * NPC主体イベント実行 + *------------------------------------------ + */ +int buildin_donpcevent(struct script_state *st) +{ + char *event; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + check_event(st, event); + npc_event_do(event); + return 0; +} +/*========================================== + * イベントタイマー追加 + *------------------------------------------ + */ +int buildin_addtimer(struct script_state *st) +{ + char *event; + int tick; + tick=conv_num(st,& (st->stack->stack_data[st->start+2])); + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + check_event(st, event); + pc_addeventtimer(script_rid2sd(st),tick,event); + return 0; +} +/*========================================== + * イベントタイマー削除 + *------------------------------------------ + */ +int buildin_deltimer(struct script_state *st) +{ + char *event; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + check_event(st, event); + pc_deleventtimer(script_rid2sd(st),event); + return 0; +} +/*========================================== + * イベントタイマーのカウント値追加 + *------------------------------------------ + */ +int buildin_addtimercount(struct script_state *st) +{ + char *event; + int tick; + event=conv_str(st,& (st->stack->stack_data[st->start+2])); + tick=conv_num(st,& (st->stack->stack_data[st->start+3])); + check_event(st, event); + pc_addeventtimercount(script_rid2sd(st),event,tick); + return 0; +} + +/*========================================== + * NPCタイマー初期化 + *------------------------------------------ + */ +int buildin_initnpctimer(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + //nd->u.scr.rid = st->rid; //NO, use npcattachtimer if you want a player-attached timer! [Skotlex] + npc_settimerevent_tick(nd,0); + npc_timerevent_start(nd, st->rid); + return 0; +} +/*========================================== + * NPCタイマー開始 + *------------------------------------------ + */ +int buildin_startnpctimer(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + npc_timerevent_start(nd, st->rid); + return 0; +} +/*========================================== + * NPCタイマー停止 + *------------------------------------------ + */ +int buildin_stopnpctimer(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + npc_timerevent_stop(nd); + return 0; +} +/*========================================== + * NPCタイマー情報所得 + *------------------------------------------ + */ +int buildin_getnpctimer(struct script_state *st) +{ + struct npc_data *nd; + struct map_session_data *sd; + int type=conv_num(st,& (st->stack->stack_data[st->start+2])); + int val=0; + if( st->end > st->start+3 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + switch(type){ + case 0: val=npc_gettimerevent_tick(nd); break; + case 1: + if (nd->u.scr.rid) { + sd = map_id2sd(nd->u.scr.rid); + if (!sd) { + if(battle_config.error_log) + ShowError("buildin_getnpctimer: Attached player not found!\n"); + break; + } + val = (sd->npc_timer_id != -1); + } else + val= (nd->u.scr.timerid !=-1); + break; + case 2: val= nd->u.scr.timeramount; break; + } + push_val(st->stack,C_INT,val); + return 0; +} +/*========================================== + * NPCタイマー値設定 + *------------------------------------------ + */ +int buildin_setnpctimer(struct script_state *st) +{ + int tick; + struct npc_data *nd; + tick=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end > st->start+3 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + npc_settimerevent_tick(nd,tick); + return 0; +} + +/*========================================== + * attaches the player rid to the timer [Celest] + *------------------------------------------ + */ +int buildin_attachnpctimer(struct script_state *st) +{ + struct map_session_data *sd; + struct npc_data *nd; + + nd=(struct npc_data *)map_id2bl(st->oid); + if( st->end > st->start+2 ) { + char *name = conv_str(st,& (st->stack->stack_data[st->start+2])); + sd=map_nick2sd(name); + } else { + sd = script_rid2sd(st); + } + + if (sd==NULL) + return 0; + + nd->u.scr.rid = sd->bl.id; + return 0; +} + +/*========================================== + * detaches a player rid from the timer [Celest] + *------------------------------------------ + */ +int buildin_detachnpctimer(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + nd->u.scr.rid = 0; + return 0; +} + +/*========================================== + * To avoid "player not attached" script errors, this function is provided, + * it checks if there is a player attached to the current script. [Skotlex] + * If no, returns 0, if yes, returns the char_id of the attached player. + *------------------------------------------ + */ +int buildin_playerattached(struct script_state *st) +{ + struct map_session_data *sd; + if (st->rid == 0 || (sd = map_id2sd(st->rid)) == NULL) + push_val(st->stack,C_INT,0); + else + push_val(st->stack,C_INT,st->rid); + return 0; +} + +/*========================================== + * 天の声アナウンス + *------------------------------------------ + */ +int buildin_announce(struct script_state *st) +{ + char *str, *color=NULL; + int flag; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + flag=conv_num(st,& (st->stack->stack_data[st->start+3])); + if (st->end>st->start+4) + color=conv_str(st,& (st->stack->stack_data[st->start+4])); + + if(flag&0x0f){ + struct block_list *bl=(flag&0x08)? map_id2bl(st->oid) : + (struct block_list *)script_rid2sd(st); + if (color) + clif_announce(bl,str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0),flag); + else + clif_GMmessage(bl,str,(int)strlen(str)+1,flag); + }else { + if (color) + intif_announce(str,(int)strlen(str)+1, strtol(color, (char **)NULL, 0), flag); + else + intif_GMmessage(str,(int)strlen(str)+1,flag); + } + return 0; +} +/*========================================== + * 天の声アナウンス(特定マップ) + *------------------------------------------ + */ +int buildin_mapannounce_sub(struct block_list *bl,va_list ap) +{ + char *str, *color; + int len,flag; + str=va_arg(ap,char *); + len=va_arg(ap,int); + flag=va_arg(ap,int); + color=va_arg(ap,char *); + if (color) + clif_announce(bl,str,len, strtol(color, (char **)NULL, 0), flag|3); + else + clif_GMmessage(bl,str,len,flag|3); + return 0; +} +int buildin_mapannounce(struct script_state *st) +{ + char *mapname,*str, *color=NULL; + int flag,m; + + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + str=conv_str(st,& (st->stack->stack_data[st->start+3])); + flag=conv_num(st,& (st->stack->stack_data[st->start+4])); + if (st->end>st->start+5) + color=conv_str(st,& (st->stack->stack_data[st->start+5])); + + if( (m=map_mapname2mapid(mapname))<0 ) + return 0; + + map_foreachinmap(buildin_mapannounce_sub, + m, BL_PC, str,strlen(str)+1,flag&0x10, color); + return 0; +} +/*========================================== + * 天の声アナウンス(特定エリア) + *------------------------------------------ + */ +int buildin_areaannounce(struct script_state *st) +{ + char *map,*str,*color=NULL; + int flag,m; + int x0,y0,x1,y1; + + map=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + str=conv_str(st,& (st->stack->stack_data[st->start+7])); + flag=conv_num(st,& (st->stack->stack_data[st->start+8])); + if (st->end>st->start+9) + color=conv_str(st,& (st->stack->stack_data[st->start+9])); + + if( (m=map_mapname2mapid(map))<0 ) + return 0; + + map_foreachinarea(buildin_mapannounce_sub, + m,x0,y0,x1,y1,BL_PC, str,strlen(str)+1,flag&0x10, color); + return 0; +} + +/*========================================== + * ユーザー数所得 + *------------------------------------------ + */ +int buildin_getusers(struct script_state *st) +{ + int flag=conv_num(st,& (st->stack->stack_data[st->start+2])); + struct block_list *bl=map_id2bl((flag&0x08)?st->oid:st->rid); + int val=0; + switch(flag&0x07){ + case 0: val=map[bl->m].users; break; + case 1: val=map_getusers(); break; + } + push_val(st->stack,C_INT,val); + return 0; +} +/*========================================== + * Works like @WHO - displays all online users names in window + *------------------------------------------ + */ +int buildin_getusersname(struct script_state *st) +{ + struct map_session_data *pl_sd = NULL, **pl_allsd; + int i=0,disp_num=1, users; + + pl_allsd = map_getallusers(&users); + + for (i=0;ioid); + clif_scriptmes(script_rid2sd(st),st->oid,pl_sd->status.name); + } + } + return 0; +} +/*========================================== + * マップ指定ユーザー数所得 + *------------------------------------------ + */ +int buildin_getmapusers(struct script_state *st) +{ + char *str; + int m; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + if( (m=map_mapname2mapid(str))< 0){ + push_val(st->stack,C_INT,-1); + return 0; + } + push_val(st->stack,C_INT,map[m].users); + return 0; +} +/*========================================== + * エリア指定ユーザー数所得 + *------------------------------------------ + */ +int buildin_getareausers_sub(struct block_list *bl,va_list ap) +{ + int *users=va_arg(ap,int *); + (*users)++; + return 0; +} +int buildin_getareausers(struct script_state *st) +{ + char *str; + int m,x0,y0,x1,y1,users=0; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + if( (m=map_mapname2mapid(str))< 0){ + push_val(st->stack,C_INT,-1); + return 0; + } + map_foreachinarea(buildin_getareausers_sub, + m,x0,y0,x1,y1,BL_PC,&users); + push_val(st->stack,C_INT,users); + return 0; +} + +/*========================================== + * エリア指定ドロップアイテム数所得 + *------------------------------------------ + */ +int buildin_getareadropitem_sub(struct block_list *bl,va_list ap) +{ + int item=va_arg(ap,int); + int *amount=va_arg(ap,int *); + struct flooritem_data *drop=(struct flooritem_data *)bl; + + if(drop->item_data.nameid==item) + (*amount)+=drop->item_data.amount; + + return 0; +} +int buildin_getareadropitem(struct script_state *st) +{ + char *str; + int m,x0,y0,x1,y1,item,amount=0; + struct script_data *data; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x0=conv_num(st,& (st->stack->stack_data[st->start+3])); + y0=conv_num(st,& (st->stack->stack_data[st->start+4])); + x1=conv_num(st,& (st->stack->stack_data[st->start+5])); + y1=conv_num(st,& (st->stack->stack_data[st->start+6])); + + data=&(st->stack->stack_data[st->start+7]); + get_val(st,data); + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + item=512; + if( item_data ) + item=item_data->nameid; + }else + item=conv_num(st,data); + + if( (m=map_mapname2mapid(str))< 0){ + push_val(st->stack,C_INT,-1); + return 0; + } + map_foreachinarea(buildin_getareadropitem_sub, + m,x0,y0,x1,y1,BL_ITEM,item,&amount); + push_val(st->stack,C_INT,amount); + return 0; +} +/*========================================== + * NPCの有効化 + *------------------------------------------ + */ +int buildin_enablenpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,1); + return 0; +} +/*========================================== + * NPCの無効化 + *------------------------------------------ + */ +int buildin_disablenpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,0); + return 0; +} + +int buildin_enablearena(struct script_state *st) // Added by RoVeRT +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + struct chat_data *cd; + + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL) + return 0; + + npc_enable(nd->name,1); + nd->arenaflag=1; + + if(cd->users>=cd->trigger && cd->npc_event[0]) + npc_timer_event(cd->npc_event); + + return 0; +} +int buildin_disablearena(struct script_state *st) // Added by RoVeRT +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + nd->arenaflag=0; + + return 0; +} +/*========================================== + * 隠れているNPCの表示 + *------------------------------------------ + */ +int buildin_hideoffnpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,2); + return 0; +} +/*========================================== + * NPCをハイディング + *------------------------------------------ + */ +int buildin_hideonnpc(struct script_state *st) +{ + char *str; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + npc_enable(str,4); + return 0; +} +/*========================================== + * 状態異常にかかる + *------------------------------------------ + */ +int buildin_sc_start(struct script_state *st) +{ + struct block_list *bl; + int type,tick,val1,val4=0; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + tick=conv_num(st,& (st->stack->stack_data[st->start+3])); + val1=conv_num(st,& (st->stack->stack_data[st->start+4])); + if( st->end>st->start+5 ) //指定したキャラを状態異常にする + bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+5]))); + else + bl = map_id2bl(st->rid); + + if (potion_flag==1 && potion_target) { + bl = map_id2bl(potion_target); + tick/=2; //Thrown potions only last half. + val4 = 1; //Mark that this was a thrown sc_effect + } + if (bl) + status_change_start(bl,type,10000,val1,0,0,val4,tick,11); + return 0; +} + +/*========================================== + * 状態異常にかかる(確率指定) + *------------------------------------------ + */ +int buildin_sc_start2(struct script_state *st) +{ + struct block_list *bl; + int type,tick,val1,val4=0,per; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + tick=conv_num(st,& (st->stack->stack_data[st->start+3])); + val1=conv_num(st,& (st->stack->stack_data[st->start+4])); + per=conv_num(st,& (st->stack->stack_data[st->start+5])); + if( st->end>st->start+6 ) //指定したキャラを状態異常にする + bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6]))); + else + bl = map_id2bl(st->rid); + + if (potion_flag==1 && potion_target) { + bl = map_id2bl(potion_target); + tick/=2; + val4 = 1; + } + + if(bl) + status_change_start(bl,type,per,val1,0,0,val4,tick,11); + return 0; +} + +/*========================================== + * Starts a SC_ change with the four values passed. [Skotlex] + * Final optional argument is the ID of player to affect. + * sc_start4 type, duration, val1, val2, val3, val4, ; + *------------------------------------------ + */ +int buildin_sc_start4(struct script_state *st) +{ + struct block_list *bl; + int type,tick,val1,val2,val3,val4; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + tick=conv_num(st,& (st->stack->stack_data[st->start+3])); + val1=conv_num(st,& (st->stack->stack_data[st->start+4])); + val2=conv_num(st,& (st->stack->stack_data[st->start+5])); + val3=conv_num(st,& (st->stack->stack_data[st->start+6])); + val4=conv_num(st,& (st->stack->stack_data[st->start+7])); + if( st->end>st->start+8 ) + bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+8]))); + else + bl = map_id2bl(st->rid); + + if (potion_flag==1 && potion_target) { + bl = map_id2bl(potion_target); + tick/=2; + } + if (bl) + status_change_start(bl,type,10000,val1,val2,val3,val4,tick,11); + return 0; +} + +/*========================================== + * 状態異常が直る + *------------------------------------------ + */ +int buildin_sc_end(struct script_state *st) +{ + struct block_list *bl; + int type; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + bl = map_id2bl(st->rid); + + if (potion_flag==1 && potion_target) + bl = map_id2bl(potion_target); + + if (bl) + status_change_end(bl,type,-1); + return 0; +} +/*========================================== + * 状態異常耐性を計算した確率を返す + *------------------------------------------ + */ +int buildin_getscrate(struct script_state *st) +{ + struct block_list *bl; + int sc_def=0,type,rate; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + rate=conv_num(st,& (st->stack->stack_data[st->start+3])); + if( st->end>st->start+4 ) //指定したキャラの耐性を計算する + bl = map_id2bl(conv_num(st,& (st->stack->stack_data[st->start+6]))); + else + bl = map_id2bl(st->rid); + + if (bl) + sc_def = status_get_sc_def(bl,type); + + rate = rate*(10000-sc_def)/10000; + push_val(st->stack,C_INT,rate<0?0:rate); + + return 0; + +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_debugmes(struct script_state *st) +{ + conv_str(st,& (st->stack->stack_data[st->start+2])); + ShowDebug("script debug : %d %d : %s\n",st->rid,st->oid,st->stack->stack_data[st->start+2].u.str); + return 0; +} + +/*========================================== + *捕獲アイテム使用 + *------------------------------------------ + */ +int buildin_catchpet(struct script_state *st) +{ + int pet_id; + struct map_session_data *sd; + pet_id= conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + pet_catch_process1(sd,pet_id); + return 0; +} + +/*========================================== + *携帯卵孵化機使用 + *------------------------------------------ + */ +int buildin_birthpet(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + clif_sendegg(sd); + return 0; +} + +/*========================================== + * Added - AppleGirl For Advanced Classes, (Updated for Cleaner Script Purposes) + *------------------------------------------ + */ +int buildin_resetlvl(struct script_state *st) +{ + struct map_session_data *sd; + + int type=conv_num(st,& (st->stack->stack_data[st->start+2])); + + sd=script_rid2sd(st); + pc_resetlvl(sd,type); + return 0; +} +/*========================================== + * ステータスリセット + *------------------------------------------ + */ +int buildin_resetstatus(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + pc_resetstate(sd); + return 0; +} + +/*========================================== + * script command resetskill + *------------------------------------------ + */ +int buildin_resetskill(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + pc_resetskill(sd,1); + return 0; +} + +/*========================================== + * Counts total amount of skill points. + *------------------------------------------ + */ +int buildin_skillpointcount(struct script_state *st) +{ + struct map_session_data *sd; + sd=script_rid2sd(st); + push_val(st->stack,C_INT,sd->status.skill_point + pc_resetskill(sd,2)); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int buildin_changebase(struct script_state *st) +{ + struct map_session_data *sd=NULL; + int vclass; + + if( st->end>st->start+3 ) + sd=map_id2sd(conv_num(st,& (st->stack->stack_data[st->start+3]))); + else + sd=script_rid2sd(st); + + if(sd == NULL) + return 0; + + vclass = conv_num(st,& (st->stack->stack_data[st->start+2])); + if(vclass == JOB_WEDDING) + { + if (!battle_config.wedding_modifydisplay || //Do not show the wedding sprites + sd->class_&JOBL_BABY //Baby classes screw up when showing wedding sprites. [Skotlex] They don't seem to anymore. + ) + return 0; + } + + if(!sd->disguise && vclass != sd->vd.class_) { + status_set_viewdata(&sd->bl, vclass); + //Updated client view. Base, Weapon and Cloth Colors. + clif_changelook(&sd->bl,LOOK_BASE,sd->vd.class_); + clif_changelook(&sd->bl,LOOK_WEAPON,sd->status.weapon); + if (sd->vd.cloth_color) + clif_changelook(&sd->bl,LOOK_CLOTHES_COLOR,sd->vd.cloth_color); + } + + return 0; +} + +/*========================================== + * 性別変換 + *------------------------------------------ + */ +int buildin_changesex(struct script_state *st) { + struct map_session_data *sd = NULL; + sd = script_rid2sd(st); + + if (sd->status.sex == 0) { + sd->status.sex = 1; + sd->sex = 1; + if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) + sd->status.class_ -= 1; + } else if (sd->status.sex == 1) { + sd->status.sex = 0; + sd->sex = 0; + if ((sd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER) + sd->status.class_ += 1; + } + chrif_char_ask_name(-1, sd->status.name, 5, 0, 0, 0, 0, 0, 0); // type: 5 - changesex + chrif_save(sd,0); + return 0; +} + +/*========================================== + * npcチャット作成 + *------------------------------------------ + */ +int buildin_waitingroom(struct script_state *st) +{ + char *name,*ev=""; + int limit, trigger = 0,pub=1; + name=conv_str(st,& (st->stack->stack_data[st->start+2])); + limit= conv_num(st,& (st->stack->stack_data[st->start+3])); + if(limit==0) + pub=3; + + if( (st->end > st->start+5) ){ + struct script_data* data=&(st->stack->stack_data[st->start+5]); + get_val(st,data); + if(data->type==C_INT){ + // 新Athena仕様(旧Athena仕様と互換性あり) + ev=conv_str(st,& (st->stack->stack_data[st->start+4])); + trigger=conv_num(st,& (st->stack->stack_data[st->start+5])); + }else{ + // eathena仕様 + trigger=conv_num(st,& (st->stack->stack_data[st->start+4])); + ev=conv_str(st,& (st->stack->stack_data[st->start+5])); + } + }else{ + // 旧Athena仕様 + if( st->end > st->start+4 ) + ev=conv_str(st,& (st->stack->stack_data[st->start+4])); + } + chat_createnpcchat( (struct npc_data *)map_id2bl(st->oid), + limit,pub,trigger,name,(int)strlen(name)+1,ev); + return 0; +} +/*========================================== + * Works like 'announce' but outputs in the common chat window + *------------------------------------------ + */ +int buildin_globalmes(struct script_state *st) +{ + struct block_list *bl = map_id2bl(st->oid); + struct npc_data *nd = (struct npc_data *)bl; + char *name=NULL,*mes; + + mes=conv_str(st,& (st->stack->stack_data[st->start+2])); // メッセージの取得 + if(mes==NULL) return 0; + + if(st->end>st->start+3){ // NPC名の取得(123#456) + name=conv_str(st,& (st->stack->stack_data[st->start+3])); + } else { + name=nd->name; + } + + npc_globalmessage(name,mes); // グローバルメッセージ送信 + + return 0; +} +/*========================================== + * npcチャット削除 + *------------------------------------------ + */ +int buildin_delwaitingroom(struct script_state *st) +{ + struct npc_data *nd; + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + chat_deletenpcchat(nd); + return 0; +} +/*========================================== + * npcチャット全員蹴り出す + *------------------------------------------ + */ +int buildin_waitingroomkickall(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + chat_npckickall(cd); + return 0; +} + +/*========================================== + * npcチャットイベント有効化 + *------------------------------------------ + */ +int buildin_enablewaitingroomevent(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + chat_enableevent(cd); + return 0; +} + +/*========================================== + * npcチャットイベント無効化 + *------------------------------------------ + */ +int buildin_disablewaitingroomevent(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + + if( st->end > st->start+2 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + chat_disableevent(cd); + return 0; +} +/*========================================== + * npcチャット状態所得 + *------------------------------------------ + */ +int buildin_getwaitingroomstate(struct script_state *st) +{ + struct npc_data *nd; + struct chat_data *cd; + int val=0,type; + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if( st->end > st->start+3 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+3]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ){ + push_val(st->stack,C_INT,-1); + return 0; + } + + switch(type){ + case 0: val=cd->users; break; + case 1: val=cd->limit; break; + case 2: val=cd->trigger&0x7f; break; + case 3: val=((cd->trigger&0x80)>0); break; + case 32: val=(cd->users >= cd->limit); break; + case 33: val=(cd->users >= cd->trigger); break; + + case 4: + push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->title); + return 0; + case 5: + push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->pass); + return 0; + case 16: + push_str(st->stack,C_CONSTSTR,(unsigned char *) cd->npc_event); + return 0; + } + push_val(st->stack,C_INT,val); + return 0; +} + +/*========================================== + * チャットメンバー(規定人数)ワープ + *------------------------------------------ + */ +int buildin_warpwaitingpc(struct script_state *st) +{ + int x,y,i,n; + char *str; + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + struct chat_data *cd; + + if(nd==NULL || (cd=(struct chat_data *)map_id2bl(nd->chat_id))==NULL ) + return 0; + + n=cd->trigger&0x7f; + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + + if( st->end > st->start+5 ) + n=conv_num(st,& (st->stack->stack_data[st->start+5])); + + for(i=0;iusersd[0]; // リスト先頭のPCを次々に。 + + mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpc")+(i<<24),sd->bl.id); + + if(strcmp(str,"Random")==0) + pc_randomwarp(sd,3); + else if(strcmp(str,"SavePoint")==0){ + if(map[sd->bl.m].flag.noteleport) // テレポ禁止 + return 0; + + pc_setpos(sd,sd->status.save_point.map, + sd->status.save_point.x,sd->status.save_point.y,3); + }else + pc_setpos(sd,mapindex_name2id(str),x,y,0); + } + mapreg_setreg(add_str((unsigned char *) "$@warpwaitingpcnum"),n); + return 0; +} +/*========================================== + * RIDのアタッチ + *------------------------------------------ + */ +int buildin_attachrid(struct script_state *st) +{ + st->rid=conv_num(st,& (st->stack->stack_data[st->start+2])); + push_val(st->stack,C_INT, (map_id2sd(st->rid)!=NULL)); + return 0; +} +/*========================================== + * RIDのデタッチ + *------------------------------------------ + */ +int buildin_detachrid(struct script_state *st) +{ + st->rid=0; + return 0; +} +/*========================================== + * 存在チェック + *------------------------------------------ + */ +int buildin_isloggedin(struct script_state *st) +{ + push_val(st->stack,C_INT, map_id2sd( + conv_num(st,& (st->stack->stack_data[st->start+2])) )!=NULL ); + return 0; +} + + +/*========================================== + * + *------------------------------------------ + */ +enum { MF_NOMEMO,MF_NOTELEPORT,MF_NOSAVE,MF_NOBRANCH,MF_NOPENALTY,MF_NOZENYPENALTY, + MF_PVP,MF_PVP_NOPARTY,MF_PVP_NOGUILD,MF_GVG,MF_GVG_NOPARTY,MF_NOTRADE,MF_NOSKILL, + MF_NOWARP,MF_FREE,MF_NOICEWALL,MF_SNOW,MF_FOG,MF_SAKURA,MF_LEAVES,MF_RAIN, + MF_INDOORS,MF_NOGO,MF_CLOUDS,MF_CLOUDS2,MF_FIREWORKS,MF_GVG_CASTLE,MF_GVG_DUNGEON,MF_NIGHTENABLED, + MF_NOBASEEXP, MF_NOJOBEXP, MF_NOMOBLOOT, MF_NOMVPLOOT, MF_NORETURN, MF_NOWARPTO, MF_NIGHTMAREDROP, + MF_RESTRICTED, MF_NOCOMMAND, MF_NODROP, MF_JEXP, MF_BEXP, MF_NOVENDING }; + +int buildin_setmapflagnosave(struct script_state *st) +{ + int m,x,y; + unsigned short mapindex; + char *str,*str2; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + str2=conv_str(st,& (st->stack->stack_data[st->start+3])); + x=conv_num(st,& (st->stack->stack_data[st->start+4])); + y=conv_num(st,& (st->stack->stack_data[st->start+5])); + m = map_mapname2mapid(str); + mapindex = mapindex_name2id(str2); + + if(m >= 0 && mapindex) { + map[m].flag.nosave=1; + map[m].save.map=mapindex; + map[m].save.x=x; + map[m].save.y=y; + } + + return 0; +} + +int buildin_setmapflag(struct script_state *st) +{ + int m,i; + char *str; + char *val=NULL; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + i=conv_num(st,& (st->stack->stack_data[st->start+3])); + if(st->end>st->start+4){ + val=conv_str(st,& (st->stack->stack_data[st->start+4])); + } + m = map_mapname2mapid(str); + if(m >= 0) { + switch(i) { + case MF_NOMEMO: + map[m].flag.nomemo=1; + break; + case MF_NOTELEPORT: + map[m].flag.noteleport=1; + break; + case MF_NOBRANCH: + map[m].flag.nobranch=1; + break; + case MF_NOPENALTY: + map[m].flag.nopenalty=1; + break; + case MF_NOZENYPENALTY: + map[m].flag.nozenypenalty=1; + break; + case MF_PVP: + map[m].flag.pvp=1; + break; + case MF_PVP_NOPARTY: + map[m].flag.pvp_noparty=1; + break; + case MF_PVP_NOGUILD: + map[m].flag.pvp_noguild=1; + break; + case MF_GVG: + map[m].flag.gvg=1; + break; + case MF_GVG_NOPARTY: + map[m].flag.gvg_noparty=1; + break; + case MF_GVG_DUNGEON: + map[m].flag.gvg_dungeon=1; + break; + case MF_GVG_CASTLE: + map[m].flag.gvg_castle=1; + break; + case MF_NOTRADE: + map[m].flag.notrade=1; + break; + case MF_NODROP: + map[m].flag.nodrop=1; + break; + case MF_NOSKILL: + map[m].flag.noskill=1; + break; + case MF_NOWARP: + map[m].flag.nowarp=1; + break; + case MF_NOICEWALL: // [Valaris] + map[m].flag.noicewall=1; + break; + case MF_SNOW: // [Valaris] + map[m].flag.snow=1; + break; + case MF_CLOUDS: + map[m].flag.clouds=1; + break; + case MF_CLOUDS2: // [Valaris] + map[m].flag.clouds2=1; + break; + case MF_FOG: // [Valaris] + map[m].flag.fog=1; + break; + case MF_FIREWORKS: + map[m].flag.fireworks=1; + break; + case MF_SAKURA: // [Valaris] + map[m].flag.sakura=1; + break; + case MF_LEAVES: // [Valaris] + map[m].flag.leaves=1; + break; + case MF_RAIN: // [Valaris] + map[m].flag.rain=1; + break; + case MF_INDOORS: // celest + map[m].flag.indoors=1; + break; + case MF_NIGHTENABLED: + map[m].flag.nightenabled=1; + break; + case MF_NOGO: // celest + map[m].flag.nogo=1; + break; + case MF_NOBASEEXP: + map[m].flag.nobaseexp=1; + break; + case MF_NOJOBEXP: + map[m].flag.nojobexp=1; + break; + case MF_NOMOBLOOT: + map[m].flag.nomobloot=1; + break; + case MF_NOMVPLOOT: + map[m].flag.nomvploot=1; + break; + case MF_NORETURN: + map[m].flag.noreturn=1; + break; + case MF_NOWARPTO: + map[m].flag.nowarpto=1; + break; + case MF_NIGHTMAREDROP: + map[m].flag.pvp_nightmaredrop=1; + break; + case MF_RESTRICTED: + map[m].flag.restricted=1; + break; + case MF_NOCOMMAND: + map[m].flag.nocommand=1; + break; + case MF_JEXP: + map[m].jexp = (!val || atoi(val) < 0) ? 100 : atoi(val); + break; + case MF_BEXP: + map[m].bexp = (!val || atoi(val) < 0) ? 100 : atoi(val); + break; + case MF_NOVENDING: + map[m].flag.novending=1; + break; + } + } + + return 0; +} + +int buildin_removemapflag(struct script_state *st) +{ + int m,i; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + i=conv_num(st,& (st->stack->stack_data[st->start+3])); + m = map_mapname2mapid(str); + if(m >= 0) { + switch(i) { + case MF_NOMEMO: + map[m].flag.nomemo=0; + break; + case MF_NOTELEPORT: + map[m].flag.noteleport=0; + break; + case MF_NOSAVE: + map[m].flag.nosave=0; + break; + case MF_NOBRANCH: + map[m].flag.nobranch=0; + break; + case MF_NOPENALTY: + map[m].flag.nopenalty=0; + break; + case MF_PVP: + map[m].flag.pvp=0; + break; + case MF_PVP_NOPARTY: + map[m].flag.pvp_noparty=0; + break; + case MF_PVP_NOGUILD: + map[m].flag.pvp_noguild=0; + break; + case MF_GVG: + map[m].flag.gvg=0; + break; + case MF_GVG_NOPARTY: + map[m].flag.gvg_noparty=0; + break; + case MF_GVG_DUNGEON: + map[m].flag.gvg_dungeon=0; + break; + case MF_GVG_CASTLE: + map[m].flag.gvg_castle=0; + break; + case MF_NOZENYPENALTY: + map[m].flag.nozenypenalty=0; + break; + case MF_NOTRADE: + map[m].flag.notrade=0; + break; + case MF_NODROP: + map[m].flag.nodrop=0; + break; + case MF_NOSKILL: + map[m].flag.noskill=0; + break; + case MF_NOWARP: + map[m].flag.nowarp=0; + break; + case MF_NOICEWALL: // [Valaris] + map[m].flag.noicewall=0; + break; + case MF_SNOW: // [Valaris] + map[m].flag.snow=0; + break; + case MF_CLOUDS: + map[m].flag.clouds=0; + break; + case MF_CLOUDS2: // [Valaris] + map[m].flag.clouds2=0; + break; + case MF_FOG: // [Valaris] + map[m].flag.fog=0; + break; + case MF_FIREWORKS: + map[m].flag.fireworks=0; + break; + case MF_SAKURA: // [Valaris] + map[m].flag.sakura=0; + break; + case MF_LEAVES: // [Valaris] + map[m].flag.leaves=0; + break; + case MF_RAIN: // [Valaris] + map[m].flag.rain=0; + break; + case MF_INDOORS: // celest + map[m].flag.indoors=0; + break; + case MF_NIGHTENABLED: + map[m].flag.nightenabled=0; + break; + case MF_NOGO: // celest + map[m].flag.nogo=0; + break; + case MF_NOBASEEXP: + map[m].flag.nobaseexp=0; + break; + case MF_NOJOBEXP: + map[m].flag.nojobexp=0; + break; + case MF_NOMOBLOOT: + map[m].flag.nomobloot=0; + break; + case MF_NOMVPLOOT: + map[m].flag.nomvploot=0; + break; + case MF_NORETURN: + map[m].flag.noreturn=0; + break; + case MF_NOWARPTO: + map[m].flag.nowarpto=0; + break; + case MF_NIGHTMAREDROP: + map[m].flag.pvp_nightmaredrop=0; + break; + case MF_RESTRICTED: + map[m].flag.restricted=0; + break; + case MF_NOCOMMAND: + map[m].flag.nocommand=0; + break; + case MF_JEXP: + map[m].jexp=100; + break; + case MF_BEXP: + map[m].bexp=100; + break; + case MF_NOVENDING: + map[m].flag.novending=0; + break; + } + } + + return 0; +} + +int buildin_pvpon(struct script_state *st) +{ + int m,i,users; + char *str; + struct map_session_data *pl_sd=NULL, **pl_allsd; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && !map[m].flag.pvp) { + map[m].flag.pvp = 1; + clif_send0199(m,1); + + if(battle_config.pk_mode) // disable ranking functions if pk_mode is on [Valaris] + return 0; + + pl_allsd = map_getallusers(&users); + + for(i=0;ibl.m && pl_sd->pvp_timer == -1) + { + pl_sd->pvp_timer=add_timer(gettick()+200,pc_calc_pvprank_timer,pl_sd->bl.id,0); + pl_sd->pvp_rank=0; + pl_sd->pvp_lastusers=0; + pl_sd->pvp_point=5; + pl_sd->pvp_won = 0; + pl_sd->pvp_lost = 0; + } + } + } + return 0; +} + +int buildin_pvpoff(struct script_state *st) +{ + int m,i,users; + char *str; + struct map_session_data *pl_sd=NULL, **pl_allsd; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && map[m].flag.pvp) { //fixed Lupus + map[m].flag.pvp = 0; + clif_send0199(m,0); + + if(battle_config.pk_mode) // disable ranking options if pk_mode is on [Valaris] + return 0; + + pl_allsd = map_getallusers(&users); + + for(i=0;ibl.m) + { + clif_pvpset(pl_sd,0,0,2); + if(pl_sd->pvp_timer != -1) { + delete_timer(pl_sd->pvp_timer,pc_calc_pvprank_timer); + pl_sd->pvp_timer = -1; + } + } + } + } + + return 0; +} + +int buildin_gvgon(struct script_state *st) +{ + int m; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && !map[m].flag.gvg) { + map[m].flag.gvg = 1; + clif_send0199(m,3); + } + + return 0; +} +int buildin_gvgoff(struct script_state *st) +{ + int m; + char *str; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + m = map_mapname2mapid(str); + if(m >= 0 && map[m].flag.gvg) { + map[m].flag.gvg = 0; + clif_send0199(m,0); + } + + return 0; +} +/*========================================== + * Shows an emoticon on top of the player/npc + * emotion emotion#, + *------------------------------------------ + */ +//Optional second parameter added by [Skotlex] +int buildin_emotion(struct script_state *st) +{ + int type; + int player=0; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(type < 0 || type > 100) + return 0; + + if( st->end>st->start+3 ) + player=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if (player) { + struct map_session_data *sd = script_rid2sd(st); + if (sd) + clif_emotion(&sd->bl,type); + } else + clif_emotion(map_id2bl(st->oid),type); + return 0; +} + +int buildin_maprespawnguildid_sub(struct block_list *bl,va_list ap) +{ + int g_id=va_arg(ap,int); + int flag=va_arg(ap,int); + struct map_session_data *sd=NULL; + struct mob_data *md=NULL; + + if(bl->type == BL_PC) + sd=(struct map_session_data*)bl; + if(bl->type == BL_MOB) + md=(struct mob_data *)bl; + + if(sd){ + if((sd->status.guild_id == g_id) && (flag&1)) + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + else if((sd->status.guild_id != g_id) && (flag&2)) + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); + else if (sd->status.guild_id == 0) // Warp out players not in guild [Valaris] + pc_setpos(sd,sd->status.save_point.map,sd->status.save_point.x,sd->status.save_point.y,3); // end addition [Valaris] + } + if(md && flag&4){ + if(!md->guardian_data && md->class_ != MOBID_EMPERIUM) + unit_remove_map(bl,1); + } + return 0; +} +int buildin_maprespawnguildid(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + int g_id=conv_num(st,& (st->stack->stack_data[st->start+3])); + int flag=conv_num(st,& (st->stack->stack_data[st->start+4])); + + int m=map_mapname2mapid(mapname); + + if(m) map_foreachinmap(buildin_maprespawnguildid_sub,m,BL_CHAR,g_id,flag); + return 0; +} + +int buildin_agitstart(struct script_state *st) +{ + if(agit_flag==1) return 0; // Agit already Start. + agit_flag=1; + guild_agit_start(); + return 0; +} + +int buildin_agitend(struct script_state *st) +{ + if(agit_flag==0) return 0; // Agit already End. + agit_flag=0; + guild_agit_end(); + return 0; +} +/*========================================== + * agitcheck 1; // choice script + * if(@agit_flag == 1) goto agit; + * if(agitcheck(0) == 1) goto agit; + *------------------------------------------ + */ +int buildin_agitcheck(struct script_state *st) +{ + struct map_session_data *sd; + int cond; + + cond=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(cond == 0) { + if (agit_flag==1) push_val(st->stack,C_INT,1); + if (agit_flag==0) push_val(st->stack,C_INT,0); + } else { + sd=script_rid2sd(st); + if (agit_flag==1) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),1); + if (agit_flag==0) pc_setreg(sd,add_str((unsigned char *) "@agit_flag"),0); + } + return 0; +} +int buildin_flagemblem(struct script_state *st) +{ + int g_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(g_id < 0) return 0; + +// printf("Script.c: [FlagEmblem] GuildID=%d, Emblem=%d.\n", g->guild_id, g->emblem_id); + ((struct npc_data *)map_id2bl(st->oid))->u.scr.guild_id = g_id; + return 0; +} + +int buildin_getcastlename(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + struct guild_castle *gc; + int i; + for(i=0;imap_name)==0){ + break; + } + } + } + if(gc) + push_str(st->stack,C_CONSTSTR,(unsigned char *) gc->castle_name); + else + push_str(st->stack,C_CONSTSTR,(unsigned char *) ""); + return 0; +} + +int buildin_getcastledata(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + int index=conv_num(st,& (st->stack->stack_data[st->start+3])); + char *event=NULL; + struct guild_castle *gc; + int i,j; + + if( st->end>st->start+4 && index==0){ + for(i=0,j=-1;imap_name)==0 ) + j=i; + if(j>=0){ + event=conv_str(st,& (st->stack->stack_data[st->start+4])); + check_event(st, event); + guild_addcastleinfoevent(j,17,event); + } + } + + for(i=0;imap_name)==0){ + switch(index){ + case 0: for(j=1;j<26;j++) guild_castledataload(gc->castle_id,j); break; // Initialize[AgitInit] + case 1: push_val(st->stack,C_INT,gc->guild_id); break; + case 2: push_val(st->stack,C_INT,gc->economy); break; + case 3: push_val(st->stack,C_INT,gc->defense); break; + case 4: push_val(st->stack,C_INT,gc->triggerE); break; + case 5: push_val(st->stack,C_INT,gc->triggerD); break; + case 6: push_val(st->stack,C_INT,gc->nextTime); break; + case 7: push_val(st->stack,C_INT,gc->payTime); break; + case 8: push_val(st->stack,C_INT,gc->createTime); break; + case 9: push_val(st->stack,C_INT,gc->visibleC); break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + push_val(st->stack,C_INT,gc->guardian[index-10].visible); break; + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + push_val(st->stack,C_INT,gc->guardian[index-18].hp); break; + default: + push_val(st->stack,C_INT,0); break; + } + return 0; + } + } + } + push_val(st->stack,C_INT,0); + return 0; +} + +int buildin_setcastledata(struct script_state *st) +{ + char *mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + int index=conv_num(st,& (st->stack->stack_data[st->start+3])); + int value=conv_num(st,& (st->stack->stack_data[st->start+4])); + struct guild_castle *gc; + int i; + + for(i=0;imap_name)==0){ + // Save Data byself First + 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: return 0; + } + guild_castledatasave(gc->castle_id,index,value); + return 0; + } + } + } + return 0; +} + +/* ===================================================================== + * ギルド情報を要求する + * --------------------------------------------------------------------- + */ +int buildin_requestguildinfo(struct script_state *st) +{ + int guild_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + char *event=NULL; + + if( st->end>st->start+3 ){ + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + check_event(st, event); + } + + if(guild_id>0) + guild_npc_request_info(guild_id,event); + return 0; +} + +/* ===================================================================== + * カードの数を得る + * --------------------------------------------------------------------- + */ +int buildin_getequipcardcnt(struct script_state *st) +{ + int i,num; + struct map_session_data *sd; + int c=MAX_SLOTS; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(sd->status.inventory[i].card[0] == 0x00ff){ // 製造武器はカードなし + push_val(st->stack,C_INT,0); + return 0; + } + do{ + if( (sd->status.inventory[i].card[c-1] > 4000 && + sd->status.inventory[i].card[c-1] < 5000) || + itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest] + push_val(st->stack,C_INT,(c)); + return 0; + } + }while(c--); + push_val(st->stack,C_INT,0); + return 0; +} + +/* ================================================================ + * カード取り外し成功 + * ---------------------------------------------------------------- + */ +int buildin_successremovecards(struct script_state *st) +{ + int i,j,num,cardflag=0,flag; + struct map_session_data *sd; + struct item item_tmp; + int c=MAX_SLOTS; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない + return 0; + } + do{ + if( (sd->status.inventory[i].card[c-1] > 4000 && + sd->status.inventory[i].card[c-1] < 5000) || + itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest] + + cardflag = 1; + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1]; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0; + item_tmp.attribute=0; + for (j = 0; j < MAX_SLOTS; j++) + item_tmp.card[j]=0; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL); + } + //Logs + + if((flag=pc_additem(sd,&item_tmp,1))){ // 持てないならドロップ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + }while(c--); + + if(cardflag == 1){ // カードを取り除いたアイテム所得 + flag=0; + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine; + item_tmp.attribute=sd->status.inventory[i].attribute; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); + } + //Logs + + for (j = 0; j < MAX_SLOTS; j++) + item_tmp.card[j]=0; + pc_delitem(sd,i,1,0); + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp); + } + //Logs + + if((flag=pc_additem(sd,&item_tmp,1))){ // もてないならドロップ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + clif_misceffect(&sd->bl,3); + return 0; + } + return 0; +} + +/* ================================================================ + * カード取り外し失敗 slot,type + * type=0: 両方損失、1:カード損失、2:武具損失、3:損失無し + * ---------------------------------------------------------------- + */ +int buildin_failedremovecards(struct script_state *st) +{ + int i,j,num,cardflag=0,flag,typefail; + struct map_session_data *sd; + struct item item_tmp; + int c=MAX_SLOTS; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + typefail=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(sd->status.inventory[i].card[0]==0x00ff){ // 製造武器は処理しない + return 0; + } + do{ + if( (sd->status.inventory[i].card[c-1] > 4000 && + sd->status.inventory[i].card[c-1] < 5000) || + itemdb_type(sd->status.inventory[i].card[c-1]) == 6){ // [Celest] + + cardflag = 1; + + if(typefail == 2){ // 武具のみ損失なら、カードは受け取らせる + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].card[c-1]; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=0; + item_tmp.attribute=0; + for (j = 0; j < MAX_SLOTS; j++) + item_tmp.card[j]=0; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, item_tmp.nameid, 1, NULL); + } + //Logs + + if((flag=pc_additem(sd,&item_tmp,1))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + } + }while(c--); + + if(cardflag == 1){ + + if(typefail == 0 || typefail == 2){ // 武具損失 + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd,i,1,0); + clif_misceffect(&sd->bl,2); + return 0; + } + if(typefail == 1){ // カードのみ損失(武具を返す) + flag=0; + item_tmp.id=0,item_tmp.nameid=sd->status.inventory[i].nameid; + item_tmp.equip=0,item_tmp.identify=1,item_tmp.refine=sd->status.inventory[i].refine; + item_tmp.attribute=sd->status.inventory[i].attribute; + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -1, &sd->status.inventory[i]); + } + //Logs + + for (j = 0; j < MAX_SLOTS; j++) + item_tmp.card[j]=0; + pc_delitem(sd,i,1,0); + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, item_tmp.nameid, 1, &item_tmp); + } + //Logs + + if((flag=pc_additem(sd,&item_tmp,1))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + clif_misceffect(&sd->bl,2); + return 0; + } + return 0; +} + +int buildin_mapwarp(struct script_state *st) // Added by RoVeRT +{ + int x,y,m; + char *str; + char *mapname; + unsigned int index; + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + str=conv_str(st,& (st->stack->stack_data[st->start+3])); + x=conv_num(st,& (st->stack->stack_data[st->start+4])); + y=conv_num(st,& (st->stack->stack_data[st->start+5])); + + if( (m=map_mapname2mapid(mapname))< 0) + return 0; + + if(!(index=mapindex_name2id(str))) + return 0; + map_foreachinmap(buildin_areawarp_sub, + m,BL_PC,index,x,y); + return 0; +} + +int buildin_cmdothernpc(struct script_state *st) // Added by RoVeRT +{ + char *npc,*command; + + npc=conv_str(st,& (st->stack->stack_data[st->start+2])); + command=conv_str(st,& (st->stack->stack_data[st->start+3])); + + npc_command(map_id2sd(st->rid),npc,command); + return 0; +} + +int buildin_inittimer(struct script_state *st) // Added by RoVeRT +{ +// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid); +// nd->lastaction=nd->timer=gettick(); + + npc_do_ontimer(st->oid, 1); + + return 0; +} + +int buildin_stoptimer(struct script_state *st) // Added by RoVeRT +{ +// struct npc_data *nd=(struct npc_data*)map_id2bl(st->oid); +// nd->lastaction=nd->timer=-1; + + npc_do_ontimer(st->oid, 0); + + return 0; +} + +int buildin_mobcount_sub(struct block_list *bl,va_list ap) // Added by RoVeRT +{ + char *event=va_arg(ap,char *); + int *c=va_arg(ap,int *); + + if(strcmp(event,((struct mob_data *)bl)->npc_event)==0) + (*c)++; + return 0; +} + +int buildin_mobcount(struct script_state *st) // Added by RoVeRT +{ + char *mapname,*event; + int m,c=0; + mapname=conv_str(st,& (st->stack->stack_data[st->start+2])); + event=conv_str(st,& (st->stack->stack_data[st->start+3])); + check_event(st, event); + + if( (m=map_mapname2mapid(mapname))<0 ) { + push_val(st->stack,C_INT,-1); + return 0; + } + map_foreachinmap(buildin_mobcount_sub, m, BL_MOB, event,&c ); + + push_val(st->stack,C_INT, (c)); + + return 0; +} +int buildin_marriage(struct script_state *st) +{ + char *partner=conv_str(st,& (st->stack->stack_data[st->start+2])); + struct map_session_data *sd=script_rid2sd(st); + struct map_session_data *p_sd=map_nick2sd(partner); + + if(sd==NULL || p_sd==NULL || pc_marriage(sd,p_sd) < 0){ + push_val(st->stack,C_INT,0); + return 0; + } + push_val(st->stack,C_INT,1); + return 0; +} +int buildin_wedding_effect(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + struct block_list *bl; + + if(sd==NULL) { + bl=map_id2bl(st->oid); + } else + bl=&sd->bl; + clif_wedding_effect(bl); + return 0; +} +int buildin_divorce(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + if(sd==NULL || pc_divorce(sd) < 0){ + push_val(st->stack,C_INT,0); + return 0; + } + push_val(st->stack,C_INT,1); + return 0; +} + +int buildin_ispartneron(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + struct map_session_data *p_sd=NULL; + + if(sd==NULL || !pc_ismarried(sd) || + (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) { + push_val(st->stack,C_INT,0); + return 0; + } + + push_val(st->stack,C_INT,1); + return 0; +} + +int buildin_getpartnerid(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + if (sd == NULL) { + push_val(st->stack,C_INT,0); + return 0; + } + + push_val(st->stack,C_INT,sd->status.partner_id); + return 0; +} + +int buildin_getchildid(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + if (sd == NULL) { + push_val(st->stack,C_INT,0); + return 0; + } + + push_val(st->stack,C_INT,sd->status.child); + return 0; +} + +int buildin_getmotherid(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + if (sd == NULL) { + push_val(st->stack,C_INT,0); + return 0; + } + + push_val(st->stack,C_INT,sd->status.mother); + return 0; +} + +int buildin_getfatherid(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + if (sd == NULL) { + push_val(st->stack,C_INT,0); + return 0; + } + + push_val(st->stack,C_INT,sd->status.father); + return 0; +} + +int buildin_warppartner(struct script_state *st) +{ + int x,y; + unsigned short mapindex; + char *str; + struct map_session_data *sd=script_rid2sd(st); + struct map_session_data *p_sd=NULL; + + if(sd==NULL || !pc_ismarried(sd) || + (p_sd=map_charid2sd(sd->status.partner_id)) == NULL) { + push_val(st->stack,C_INT,0); + return 0; + } + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + x=conv_num(st,& (st->stack->stack_data[st->start+3])); + y=conv_num(st,& (st->stack->stack_data[st->start+4])); + + mapindex = mapindex_name2id(str); + if (mapindex) { + pc_setpos(p_sd,mapindex,x,y,0); + push_val(st->stack,C_INT,1); + } else + push_val(st->stack,C_INT,0); + return 0; +} + +/*================================================ + * Script for Displaying MOB Information [Valaris] + *------------------------------------------------ + */ +int buildin_strmobinfo(struct script_state *st) +{ + + int num=conv_num(st,& (st->stack->stack_data[st->start+2])); + int class_=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if((class_>=0 && class_<=1000) || class_ >2000) + return 0; + + switch (num) { + case 1: + push_str(st->stack,C_CONSTSTR,(unsigned char *) mob_db(class_)->name); + break; + case 2: + push_str(st->stack,C_CONSTSTR,(unsigned char *) mob_db(class_)->jname); + break; + case 3: + push_val(st->stack,C_INT,mob_db(class_)->lv); + break; + case 4: + push_val(st->stack,C_INT,mob_db(class_)->status.max_hp); + break; + case 5: + push_val(st->stack,C_INT,mob_db(class_)->status.max_sp); + break; + case 6: + push_val(st->stack,C_INT,mob_db(class_)->base_exp); + break; + case 7: + push_val(st->stack,C_INT,mob_db(class_)->job_exp); + break; + } + return 0; +} + +/*========================================== + * Summon guardians [Valaris] + *------------------------------------------ + */ +int buildin_guardian(struct script_state *st) +{ + int class_=0,amount=1,x=0,y=0,guardian=0; + char *str,*map,*event=""; + + map =conv_str(st,& (st->stack->stack_data[st->start+2])); + x =conv_num(st,& (st->stack->stack_data[st->start+3])); + y =conv_num(st,& (st->stack->stack_data[st->start+4])); + str =conv_str(st,& (st->stack->stack_data[st->start+5])); + class_=conv_num(st,& (st->stack->stack_data[st->start+6])); + amount=conv_num(st,& (st->stack->stack_data[st->start+7])); + event=conv_str(st,& (st->stack->stack_data[st->start+8])); + if( st->end>st->start+9 ) + guardian=conv_num(st,& (st->stack->stack_data[st->start+9])); + + check_event(st, event); + + mob_spawn_guardian(map_id2sd(st->rid),map,x,y,str,class_,amount,event,guardian); + + return 0; +} + +/*================================================ + * Script for Displaying Guardian Info [Valaris] + *------------------------------------------------ + */ +int buildin_guardianinfo(struct script_state *st) +{ + int guardian=conv_num(st,& (st->stack->stack_data[st->start+2])); + struct map_session_data *sd=script_rid2sd(st); + struct guild_castle *gc=guild_mapname2gc(map[sd->bl.m].name); + + if (guardian < 0 || guardian >= MAX_GUARDIANS || gc==NULL) + { + push_val(st->stack,C_INT,-1); + return 0; + } + + if(gc->guardian[guardian].visible) + push_val(st->stack,C_INT,gc->guardian[guardian].hp); + else push_val(st->stack,C_INT,-1); + + return 0; +} +/*========================================== + * IDからItem名 + *------------------------------------------ + */ +int buildin_getitemname(struct script_state *st) +{ + int item_id=0; + struct item_data *i_data; + char *item_name; + struct script_data *data; + + data=&(st->stack->stack_data[st->start+2]); + get_val(st,data); + + if( data->type==C_STR || data->type==C_CONSTSTR ){ + const char *name=conv_str(st,data); + struct item_data *item_data = itemdb_searchname(name); + if( item_data ) + item_id=item_data->nameid; + }else + item_id=conv_num(st,data); + + i_data = itemdb_exists(item_id); + if (i_data == NULL) + { + push_str(st->stack,C_CONSTSTR,(unsigned char *) "null"); + return 0; + } + item_name=(char *)aMallocA(ITEM_NAME_LENGTH*sizeof(char)); + + memcpy(item_name, i_data->jname, ITEM_NAME_LENGTH); + push_str(st->stack,C_STR,(unsigned char *) item_name); + return 0; +} +/*========================================== + * Returns number of slots an item has. [Skotlex] + *------------------------------------------ + */ +int buildin_getitemslots(struct script_state *st) +{ + int item_id; + struct item_data *i_data; + + item_id=conv_num(st,& (st->stack->stack_data[st->start+2])); + + i_data = itemdb_exists(item_id); + + if (i_data) + push_val(st->stack,C_INT,i_data->slot); + else + push_val(st->stack,C_INT,-1); + return 0; +} + +/*========================================== + * Returns some values of an item [Lupus] + * Price, Weight, etc... + getiteminfo(itemID,n), where n + 0 value_buy; + 1 value_sell; + 2 type; + 3 maxchance = Max drop chance of this item e.g. 1 = 0.01% , etc.. + if = 0, then monsters don't drop it at all (rare or a quest item) + if = 10000, then this item is sold in NPC shops only + 4 sex; + 5 equip; + 6 weight; + 7 atk; + 8 def; + 9 range; + 10 slot; + 11 look; + 12 elv; + 13 wlv; + *------------------------------------------ + */ +int buildin_getiteminfo(struct script_state *st) +{ + int item_id,n; + int *item_arr; + struct item_data *i_data; + + item_id = conv_num(st,& (st->stack->stack_data[st->start+2])); + n = conv_num(st,& (st->stack->stack_data[st->start+3])); + i_data = itemdb_exists(item_id); + + if (i_data && n>=0 && n<14) { + item_arr = (int*)&i_data->value_buy; + push_val(st->stack,C_INT,item_arr[n]); + } else + push_val(st->stack,C_INT,-1); + return 0; +} + +/*========================================== + * Returns value from equipped item slot n [Lupus] + getequipcardid(num,slot) + where + num = eqip position slot + slot = 0,1,2,3 (Card Slot N) + + This func returns CARD ID, 255,254,-255 (for card 0, if the item is produced) + it's useful when you want to check item cards or if it's signed + Useful for such quests as "Sign this refined item with players name" etc + Hat[0] +4 -> Player's Hat[0] +4 + *------------------------------------------ + */ +int buildin_getequipcardid(struct script_state *st) +{ + int i,num,slot; + struct map_session_data *sd; + + num=conv_num(st,& (st->stack->stack_data[st->start+2])); + slot=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + i=pc_checkequip(sd,equip[num-1]); + if(i >= 0 && slot>=0 && slot<4) + push_val(st->stack,C_INT,sd->status.inventory[i].card[slot]); + else + push_val(st->stack,C_INT,0); + + return 0; +} + +/*========================================== + * petskillbonus [Valaris] //Rewritten by [Skotlex] + *------------------------------------------ + */ + +int buildin_petskillbonus(struct script_state *st) +{ + struct pet_data *pd; + + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL || sd->pd==NULL) + return 0; + + pd=sd->pd; + if (pd->bonus) + { //Clear previous bonus + if (pd->bonus->timer != -1) + delete_timer(pd->bonus->timer, pet_skill_bonus_timer); + } else //init + pd->bonus = (struct pet_bonus *) aMalloc(sizeof(struct pet_bonus)); + + pd->bonus->type=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->bonus->val=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->bonus->duration=conv_num(st,& (st->stack->stack_data[st->start+4])); + pd->bonus->delay=conv_num(st,& (st->stack->stack_data[st->start+5])); + + if (pd->state.skillbonus == -1) + pd->state.skillbonus=0; // waiting state + + // wait for timer to start + if (battle_config.pet_equip_required && pd->equip == 0) + pd->bonus->timer=-1; + else + pd->bonus->timer=add_timer(gettick()+pd->bonus->delay*1000, pet_skill_bonus_timer, sd->bl.id, 0); + + return 0; +} + +/*========================================== + * pet looting [Valaris] //Rewritten by [Skotlex] + *------------------------------------------ + */ +int buildin_petloot(struct script_state *st) +{ + int max; + struct pet_data *pd; + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL || sd->pd==NULL) + return 0; + + max=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(max < 1) + max = 1; //Let'em loot at least 1 item. + else if (max > MAX_PETLOOT_SIZE) + max = MAX_PETLOOT_SIZE; + + pd = sd->pd; + if (pd->loot != NULL) + { //Release whatever was there already and reallocate memory + pet_lootitem_drop(pd, pd->msd); + aFree(pd->loot->item); + } + else + pd->loot = (struct pet_loot *)aMalloc(sizeof(struct pet_loot)); + + pd->loot->item = (struct item *)aCalloc(max,sizeof(struct item)); + + pd->loot->max=max; + pd->loot->count = 0; + pd->loot->weight = 0; + + return 0; +} +/*========================================== + * PCの所持品情報読み取り + *------------------------------------------ + */ +int buildin_getinventorylist(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + unsigned char card_var[NAME_LENGTH]; + + int i,j=0,k; + if(!sd) return 0; + for(i=0;istatus.inventory[i].nameid > 0 && sd->status.inventory[i].amount > 0){ + pc_setreg(sd,add_str((unsigned char *) "@inventorylist_id")+(j<<24),sd->status.inventory[i].nameid); + pc_setreg(sd,add_str((unsigned char *) "@inventorylist_amount")+(j<<24),sd->status.inventory[i].amount); + pc_setreg(sd,add_str((unsigned char *) "@inventorylist_equip")+(j<<24),sd->status.inventory[i].equip); + pc_setreg(sd,add_str((unsigned char *) "@inventorylist_refine")+(j<<24),sd->status.inventory[i].refine); + pc_setreg(sd,add_str((unsigned char *) "@inventorylist_identify")+(j<<24),sd->status.inventory[i].identify); + pc_setreg(sd,add_str((unsigned char *) "@inventorylist_attribute")+(j<<24),sd->status.inventory[i].attribute); + for (k = 0; k < MAX_SLOTS; k++) + { + sprintf(card_var, "@inventorylist_card%d",k+1); + pc_setreg(sd,add_str(card_var)+(j<<24),sd->status.inventory[i].card[k]); + } + j++; + } + } + pc_setreg(sd,add_str((unsigned char *) "@inventorylist_count"),j); + return 0; +} + +int buildin_getskilllist(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int i,j=0; + if(!sd) return 0; + for(i=0;istatus.skill[i].id > 0 && sd->status.skill[i].lv > 0){ + pc_setreg(sd,add_str((unsigned char *) "@skilllist_id")+(j<<24),sd->status.skill[i].id); + pc_setreg(sd,add_str((unsigned char *)"@skilllist_lv")+(j<<24),sd->status.skill[i].lv); + pc_setreg(sd,add_str((unsigned char *)"@skilllist_flag")+(j<<24),sd->status.skill[i].flag); + j++; + } + } + pc_setreg(sd,add_str((unsigned char *) "@skilllist_count"),j); + return 0; +} + +int buildin_clearitem(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int i; + if(sd==NULL) return 0; + for (i=0; istatus.inventory[i].amount) { + + //Logs items, got from (N)PC scripts [Lupus] + if(log_config.pick > 0 ) { + log_pick(sd, "N", 0, sd->status.inventory[i].nameid, -sd->status.inventory[i].amount, &sd->status.inventory[i]); + } + //Logs + + pc_delitem(sd, i, sd->status.inventory[i].amount, 0); + } + } + return 0; +} + +/*========================================== + Disguise Player (returns Mob/NPC ID if success, 0 on fail) [Lupus] + *------------------------------------------ + */ +int buildin_disguise(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int id; + + id = conv_num(st,& (st->stack->stack_data[st->start+2])); + + if (!mobdb_checkid(id) && !npcdb_checkid(id)) { + push_val(st->stack,C_INT,0); + return 0; + } + + pc_disguise(sd, id); + push_val(st->stack,C_INT,id); + return 0; +} + +/*========================================== + Undisguise Player (returns 1 if success, 0 on fail) [Lupus] + *------------------------------------------ + */ +int buildin_undisguise(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + + if (sd->disguise) { + pc_disguise(sd, 0); + push_val(st->stack,C_INT,0); + } else { + push_val(st->stack,C_INT,1); + } + return 0; +} + +/*========================================== + * NPCクラスチェンジ + * classは変わりたいclass + * typeは通常0なのかな? + *------------------------------------------ + */ +int buildin_classchange(struct script_state *st) +{ + int _class,type; + struct block_list *bl=map_id2bl(st->oid); + + if(bl==NULL) return 0; + + _class=conv_num(st,& (st->stack->stack_data[st->start+2])); + type=conv_num(st,& (st->stack->stack_data[st->start+3])); + clif_class_change(bl,_class,type); + return 0; +} + +/*========================================== + * NPCから発生するエフェクト + *------------------------------------------ + */ +int buildin_misceffect(struct script_state *st) +{ + int type; + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(st->oid) { + struct block_list *bl = map_id2bl(st->oid); + if (bl) + clif_misceffect2(bl,type); + } else{ + struct map_session_data *sd=script_rid2sd(st); + if(sd) + clif_misceffect2(&sd->bl,type); + } + return 0; +} +/*========================================== + * サウンドエフェクト + *------------------------------------------ + */ +int buildin_soundeffect(struct script_state *st) +{ + + // Redundn + struct map_session_data *sd=script_rid2sd(st); + char *name; + int type=0; + + + name=conv_str(st,& (st->stack->stack_data[st->start+2])); + type=conv_num(st,& (st->stack->stack_data[st->start+3])); + if(sd){ + if(!st->rid) + clif_soundeffect(sd,map_id2bl(st->oid),name,type); + else{ + clif_soundeffect(sd,&sd->bl,name,type); + } + } + return 0; +} + +int soundeffect_sub(struct block_list* bl,va_list ap) +{ + char *name; + int type; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + name = va_arg(ap,char *); + type = va_arg(ap,int); + + clif_soundeffect((struct map_session_data *)bl, bl, name, type); + + return 0; +} + +int buildin_soundeffectall(struct script_state *st) +{ + // [Lance] - Improved. + char *name, *map = NULL; + struct block_list *bl; + int type, coverage, x0, y0, x1, y1; + + name=conv_str(st,& (st->stack->stack_data[st->start+2])); + type=conv_num(st,& (st->stack->stack_data[st->start+3])); + coverage=conv_num(st,& (st->stack->stack_data[st->start+4])); + + if(!st->rid) + bl = map_id2bl(st->oid); + else + bl = &(script_rid2sd(st)->bl); + + if(bl){ + if(coverage < 23){ + clif_soundeffectall(bl,name,type,coverage); + }else { + if(st->end > st->start+9){ + map=conv_str(st,& (st->stack->stack_data[st->start+5])); + x0 = conv_num(st,& (st->stack->stack_data[st->start+6])); + y0 = conv_num(st,& (st->stack->stack_data[st->start+7])); + x1 = conv_num(st,& (st->stack->stack_data[st->start+8])); + y1 = conv_num(st,& (st->stack->stack_data[st->start+9])); + map_foreachinarea(soundeffect_sub,map_mapname2mapid(map),x0,y0,x1,y1,BL_PC,name,type); + } else { + ShowError("buildin_soundeffectall: insufficient arguments for specific area broadcast.\n"); + } + } + } + return 0; +} +/*========================================== + * pet status recovery [Valaris] / Rewritten by [Skotlex] + *------------------------------------------ + */ +int buildin_petrecovery(struct script_state *st) +{ + struct pet_data *pd; + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL || sd->pd==NULL) + return 0; + + pd=sd->pd; + + if (pd->recovery) + { //Halt previous bonus + if (pd->recovery->timer != -1) + delete_timer(pd->recovery->timer, pet_recovery_timer); + } else //Init + pd->recovery = (struct pet_recovery *)aMalloc(sizeof(struct pet_recovery)); + + pd->recovery->type=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->recovery->delay=conv_num(st,& (st->stack->stack_data[st->start+3])); + + pd->recovery->timer=-1; + + return 0; +} + +/*========================================== + * pet healing [Valaris] //Rewritten by [Skotlex] + *------------------------------------------ + */ +int buildin_petheal(struct script_state *st) +{ + struct pet_data *pd; + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL || sd->pd==NULL) + return 0; + + pd=sd->pd; + if (pd->s_skill) + { //Clear previous skill + if (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); + } + } else //init memory + pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support)); + + pd->s_skill->id=0; //This id identifies that it IS petheal rather than pet_skillsupport + //Use the lv as the amount to heal + pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+4])); + pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+5])); + + //Use delay as initial offset to avoid skill/heal exploits + if (battle_config.pet_equip_required && pd->equip == 0) + pd->s_skill->timer=-1; + else + pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_heal_timer,sd->bl.id,0); + + return 0; +} + +/*========================================== + * pet attack skills [Valaris] //Rewritten by [Skotlex] + *------------------------------------------ + */ +int buildin_petskillattack(struct script_state *st) +{ + struct pet_data *pd; + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL || sd->pd==NULL) + return 0; + + pd=sd->pd; + if (pd->a_skill == NULL) + pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack)); + + pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->a_skill->div_ = 0; + pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+4])); + pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+5])); + + return 0; +} + +/*========================================== + * pet attack skills [Valaris] + *------------------------------------------ + */ +int buildin_petskillattack2(struct script_state *st) +{ + struct pet_data *pd; + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL || sd->pd==NULL) + return 0; + + pd=sd->pd; + if (pd->a_skill == NULL) + pd->a_skill = (struct pet_skill_attack *)aMalloc(sizeof(struct pet_skill_attack)); + + pd->a_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->a_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->a_skill->div_ = conv_num(st,& (st->stack->stack_data[st->start+4])); + pd->a_skill->rate=conv_num(st,& (st->stack->stack_data[st->start+5])); + pd->a_skill->bonusrate=conv_num(st,& (st->stack->stack_data[st->start+6])); + + return 0; +} + +/*========================================== + * pet support skills [Skotlex] + *------------------------------------------ + */ +int buildin_petskillsupport(struct script_state *st) +{ + struct pet_data *pd; + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL || sd->pd==NULL) + return 0; + + pd=sd->pd; + if (pd->s_skill) + { //Clear previous skill + if (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); + } + } else //init memory + pd->s_skill = (struct pet_skill_support *) aMalloc(sizeof(struct pet_skill_support)); + + pd->s_skill->id=conv_num(st,& (st->stack->stack_data[st->start+2])); + pd->s_skill->lv=conv_num(st,& (st->stack->stack_data[st->start+3])); + pd->s_skill->delay=conv_num(st,& (st->stack->stack_data[st->start+4])); + pd->s_skill->hp=conv_num(st,& (st->stack->stack_data[st->start+5])); + pd->s_skill->sp=conv_num(st,& (st->stack->stack_data[st->start+6])); + + //Use delay as initial offset to avoid skill/heal exploits + if (battle_config.pet_equip_required && pd->equip == 0) + pd->s_skill->timer=-1; + else + pd->s_skill->timer=add_timer(gettick()+pd->s_skill->delay*1000,pet_skill_support_timer,sd->bl.id,0); + + return 0; +} + +/*========================================== + * Scripted skill effects [Celest] + *------------------------------------------ + */ +int buildin_skilleffect(struct script_state *st) +{ + struct map_session_data *sd; + + int skillid=conv_num(st,& (st->stack->stack_data[st->start+2])); + int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + + clif_skill_nodamage(&sd->bl,&sd->bl,skillid,skilllv,1); + + return 0; +} + +/*========================================== + * NPC skill effects [Valaris] + *------------------------------------------ + */ +int buildin_npcskilleffect(struct script_state *st) +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + + int skillid=conv_num(st,& (st->stack->stack_data[st->start+2])); + int skilllv=conv_num(st,& (st->stack->stack_data[st->start+3])); + int x=conv_num(st,& (st->stack->stack_data[st->start+4])); + int y=conv_num(st,& (st->stack->stack_data[st->start+5])); + + clif_skill_poseffect(&nd->bl,skillid,skilllv,x,y,gettick()); + + return 0; +} + +/*========================================== + * Special effects [Valaris] + *------------------------------------------ + */ +int buildin_specialeffect(struct script_state *st) +{ + struct block_list *bl=map_id2bl(st->oid); + + if(bl==NULL) + return 0; + + clif_specialeffect(bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0); + + return 0; +} + +int buildin_specialeffect2(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + + if(sd==NULL) + return 0; + + clif_specialeffect(&sd->bl,conv_num(st,& (st->stack->stack_data[st->start+2])), 0); + + return 0; +} + +/*========================================== + * Nude [Valaris] + *------------------------------------------ + */ + +int buildin_nude(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int i,calcflag=0; + + if(sd==NULL) + return 0; + + for(i=0;i<11;i++) + if(sd->equip_index[i] >= 0) { + if(!calcflag) + calcflag=1; + pc_unequipitem(sd,sd->equip_index[i],2); + } + + if(calcflag) + status_calc_pc(sd,0); + + return 0; +} + +/*========================================== + * gmcommand [MouseJstr] + * + * suggested on the forums... + * splitted into atcommand & charcommand by [Skotlex] + *------------------------------------------ + */ + +int buildin_atcommand(struct script_state *st) +{ + struct map_session_data *sd=NULL; + char *cmd; + + cmd = conv_str(st,& (st->stack->stack_data[st->start+2])); + if (st->rid) + sd = script_rid2sd(st); + + if (sd) is_atcommand(sd->fd, sd, cmd, 99); + else { //Use a dummy character. + struct map_session_data dummy_sd; + struct block_list *bl = NULL; + memset(&dummy_sd, 0, sizeof(struct map_session_data)); + if (st->oid) bl = map_id2bl(st->oid); + if (bl) { + memcpy(&dummy_sd.bl, bl, sizeof(struct block_list)); + if (bl->type == BL_NPC) + strncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH); + } + is_atcommand(0, &dummy_sd, cmd, 99); + } + + return 0; +} + +int buildin_charcommand(struct script_state *st) +{ + struct map_session_data *sd=NULL; + char *cmd; + + cmd = conv_str(st,& (st->stack->stack_data[st->start+2])); + + if (st->rid) + sd = script_rid2sd(st); + + if (sd) is_charcommand(sd->fd, sd, cmd, 99); + else { //Use a dummy character. + struct map_session_data dummy_sd; + struct block_list *bl = NULL; + memset(&dummy_sd, 0, sizeof(struct map_session_data)); + if (st->oid) bl = map_id2bl(st->oid); + if (bl) { + memcpy(&dummy_sd.bl, bl, sizeof(struct block_list)); + if (bl->type == BL_NPC) + strncpy(dummy_sd.status.name, ((TBL_NPC*)bl)->name, NAME_LENGTH); + } + is_charcommand(0, &dummy_sd, cmd, 99); + } + + return 0; +} + + +/*========================================== + * Displays a message for the player only (like system messages like "you got an apple" ) + *------------------------------------------ + */ +int buildin_dispbottom(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + char *message; + message=conv_str(st,& (st->stack->stack_data[st->start+2])); + if(sd) + clif_disp_onlyself(sd,message,(int)strlen(message)); + return 0; +} + +/*========================================== + * All The Players Full Recovery + (HP/SP full restore and resurrect if need) + *------------------------------------------ + */ +int buildin_recovery(struct script_state *st) +{ + struct map_session_data *sd, **all_sd; + int i = 0, users; + + all_sd = map_getallusers(&users); + + for (i = 0; i < users; i++) + { + sd = all_sd[i]; + sd->status.hp = sd->status.max_hp; + sd->status.sp = sd->status.max_sp; + clif_updatestatus(sd, SP_HP); + clif_updatestatus(sd, SP_SP); + if(pc_isdead(sd)){ + pc_setstand(sd); + clif_resurrection(&sd->bl, 1); + } + clif_displaymessage(sd->fd,"You have been recovered!"); + } + return 0; +} +/*========================================== + * Get your pet info: getpetinfo(n) + * n -> 0:pet_id 1:pet_class 2:pet_name + 3:friendly 4:hungry + *------------------------------------------ + */ +int buildin_getpetinfo(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int type=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(sd && sd->status.pet_id){ + switch(type){ + case 0: + push_val(st->stack,C_INT,sd->status.pet_id); + break; + case 1: + push_val(st->stack,C_INT,sd->pet.class_); + break; + case 2: + if(sd->pet.name) + { + push_str(st->stack,C_CONSTSTR,(unsigned char *) sd->pet.name); + } + else + push_str(st->stack,C_CONSTSTR, (unsigned char *) "null"); + break; + case 3: + //if(sd->pet.intimate) + push_val(st->stack,C_INT,sd->pet.intimate); + break; + case 4: + //if(sd->pet.hungry) + push_val(st->stack,C_INT,sd->pet.hungry); + break; + default: + push_val(st->stack,C_INT,0); + break; + } + }else{ + push_val(st->stack,C_INT,0); + } + return 0; +} +/*========================================== + * Shows wether your inventory(and equips) contain + selected card or not. + checkequipedcard(4001); + *------------------------------------------ + */ +int buildin_checkequipedcard(struct script_state *st) +{ + struct map_session_data *sd=script_rid2sd(st); + int n,i,c=0; + c=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(sd){ + for(i=0;istatus.inventory[i].nameid > 0 && sd->status.inventory[i].amount){ + for(n=0;nstatus.inventory[i].card[n]==c){ + push_val(st->stack,C_INT,1); + return 0; + } + } + } + } + } + push_val(st->stack,C_INT,0); + return 0; +} + +int buildin_jump_zero(struct script_state *st) { + int sel; + sel=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(!sel) { + int pos; + if( st->stack->stack_data[st->start+3].type!=C_POS ){ + ShowError("script: jump_zero: not label !\n"); + st->state=END; + return 0; + } + + pos=conv_num(st,& (st->stack->stack_data[st->start+3])); + st->pos=pos; + st->state=GOTO; + // printf("script: jump_zero: jumpto : %d\n",pos); + } else { + // printf("script: jump_zero: fail\n"); + } + return 0; +} + +int buildin_select(struct script_state *st) +{ + char *buf; + int len,i; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + if(sd->state.menu_or_input==0){ + st->state=RERUNLINE; + sd->state.menu_or_input=1; + for(i=st->start+2,len=16;iend;i++){ + conv_str(st,& (st->stack->stack_data[i])); + len+=(int)strlen(st->stack->stack_data[i].u.str)+1; + } + buf=(char *)aMalloc((len+1)*sizeof(char)); + buf[0]=0; + for(i=st->start+2,len=0;iend;i++){ + strcat(buf,st->stack->stack_data[i].u.str); + strcat(buf,":"); + } + clif_scriptmenu(script_rid2sd(st),st->oid,buf); + aFree(buf); + } else if(sd->npc_menu==0xff){ // cansel + sd->state.menu_or_input=0; + st->state=END; + } else { +// pc_setreg(sd,add_str((unsigned char *) "l15"),sd->npc_menu); + pc_setreg(sd,add_str((unsigned char *) "@menu"),sd->npc_menu); + sd->state.menu_or_input=0; + push_val(st->stack,C_INT,sd->npc_menu); + } + return 0; +} + +/*========================================== + * GetMapMobs + returns mob counts on a set map: + e.g. GetMapMobs("prontera.gat") + use "this" - for player's map + *------------------------------------------ + */ +int buildin_getmapmobs(struct script_state *st) +{ + char *str=NULL; + int m=-1,bx,by,i; + int count=0,c; + struct block_list *bl; + + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + + if(strcmp(str,"this")==0){ + struct map_session_data *sd=script_rid2sd(st); + if(sd) + m=sd->bl.m; + else{ + push_val(st->stack,C_INT,-1); + return 0; + } + }else + m=map_mapname2mapid(str); + + if(m < 0){ + push_val(st->stack,C_INT,-1); + return 0; + } + + for(by=0;by<=(map[m].ys-1)/BLOCK_SIZE;by++){ + for(bx=0;bx<=(map[m].xs-1)/BLOCK_SIZE;bx++){ + bl = map[m].block_mob[bx+by*map[m].bxs]; + c = map[m].block_mob_count[bx+by*map[m].bxs]; + for(i=0;inext){ + if(bl->x>=0 && bl->x<=map[m].xs-1 && bl->y>=0 && bl->y<=map[m].ys-1) + count++; + } + } + } + push_val(st->stack,C_INT,count); + return 0; +} + +/*========================================== + * movenpc [MouseJstr] + *------------------------------------------ + */ + +int buildin_movenpc(struct script_state *st) +{ + struct map_session_data *sd; + char *map,*npc; + int x,y; + + sd = script_rid2sd(st); + + map = conv_str(st,& (st->stack->stack_data[st->start+2])); + x = conv_num(st,& (st->stack->stack_data[st->start+3])); + y = conv_num(st,& (st->stack->stack_data[st->start+4])); + npc = conv_str(st,& (st->stack->stack_data[st->start+5])); + + return 0; +} + +/*========================================== + * message [MouseJstr] + *------------------------------------------ + */ + +int buildin_message(struct script_state *st) +{ + struct map_session_data *sd; + char *msg,*player; + struct map_session_data *pl_sd = NULL; + + sd = script_rid2sd(st); + + player = conv_str(st,& (st->stack->stack_data[st->start+2])); + msg = conv_str(st,& (st->stack->stack_data[st->start+3])); + + if((pl_sd=map_nick2sd((char *) player)) == NULL) + return 0; + clif_displaymessage(pl_sd->fd, msg); + + return 0; +} + +/*========================================== + * npctalk (sends message to surrounding + * area) [Valaris] + *------------------------------------------ + */ + +int buildin_npctalk(struct script_state *st) +{ + char *str; + char message[255]; + + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + str=conv_str(st,& (st->stack->stack_data[st->start+2])); + + if(nd) { + memcpy(message, nd->name, NAME_LENGTH); + strcat(message," : "); + strncat(message,str, 254); //Prevent overflow possibility. [Skotlex] + clif_message(&(nd->bl), message); + } + + return 0; +} + +/*========================================== + * hasitems (checks to see if player has any + * items on them, if so will return a 1) + * [Valaris] + *------------------------------------------ + */ + +int buildin_hasitems(struct script_state *st) +{ + int i; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + for(i=0; istatus.inventory[i].amount && sd->status.inventory[i].nameid!=2364 && sd->status.inventory[i].nameid!=2365) { + push_val(st->stack,C_INT,1); + return 0; + } + } + + push_val(st->stack,C_INT,0); + + return 0; +} +// change npc walkspeed [Valaris] +int buildin_npcspeed(struct script_state *st) +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + int x=0; + + x=conv_num(st,& (st->stack->stack_data[st->start+2])); + + if(nd) { + nd->speed=x; + } + + return 0; +} +// make an npc walk to a position [Valaris] +int buildin_npcwalkto(struct script_state *st) +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + int x=0,y=0; + + x=conv_num(st,& (st->stack->stack_data[st->start+2])); + y=conv_num(st,& (st->stack->stack_data[st->start+3])); + + if(nd) { + unit_walktoxy(&nd->bl,x,y,0); + } + + return 0; +} +// stop an npc's movement [Valaris] +int buildin_npcstop(struct script_state *st) +{ + struct npc_data *nd=(struct npc_data *)map_id2bl(st->oid); + + if(nd) { + unit_stop_walking(&nd->bl,1); + } + + return 0; +} + + +/*========================================== + * getlook char info. getlook(arg) + *------------------------------------------ + */ +int buildin_getlook(struct script_state *st){ + int type,val; + struct map_session_data *sd; + sd=script_rid2sd(st); + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + val=-1; + switch(type){ + case LOOK_HAIR: //1 + val=sd->status.hair; + break; + case LOOK_WEAPON: //2 + val=sd->status.weapon; + break; + case LOOK_HEAD_BOTTOM: //3 + val=sd->status.head_bottom; + break; + case LOOK_HEAD_TOP: //4 + val=sd->status.head_top; + break; + case LOOK_HEAD_MID: //5 + val=sd->status.head_mid; + break; + case LOOK_HAIR_COLOR: //6 + val=sd->status.hair_color; + break; + case LOOK_CLOTHES_COLOR: //7 + val=sd->status.clothes_color; + break; + case LOOK_SHIELD: //8 + val=sd->status.shield; + break; + case LOOK_SHOES: //9 + break; + } + + push_val(st->stack,C_INT,val); + return 0; +} + +/*========================================== + * get char save point. argument: 0- map name, 1- x, 2- y + *------------------------------------------ +*/ +int buildin_getsavepoint(struct script_state *st) +{ + int x,y,type; + char *mapname; + struct map_session_data *sd; + + sd=script_rid2sd(st); + + type=conv_num(st,& (st->stack->stack_data[st->start+2])); + + x=sd->status.save_point.x; + y=sd->status.save_point.y; + switch(type){ + case 0: + mapname=(char *) aMallocA((MAP_NAME_LENGTH+1)*sizeof(char)); + memcpy(mapname, mapindex_id2name(sd->status.save_point.map), MAP_NAME_LENGTH); + mapname[MAP_NAME_LENGTH]='\0'; + push_str(st->stack,C_STR,(unsigned char *) mapname); + break; + case 1: + push_val(st->stack,C_INT,x); + break; + case 2: + push_val(st->stack,C_INT,y); + break; + } + return 0; +} + +/*========================================== + * Get position for char/npc/pet/mob objects. Added by Lorky + * + * int getMapXY(MapName$,MaxX,MapY,type,[CharName$]); + * where type: + * MapName$ - String variable for output map name + * MapX - Integer variable for output coord X + * MapY - Integer variable for output coord Y + * type - type of object + * 0 - Character coord + * 1 - NPC coord + * 2 - Pet coord + * 3 - Mob coord (not released) + * CharName$ - Name object. If miss or "this" the current object + * + * Return: + * 0 - success + * -1 - some error, MapName$,MapX,MapY contains unknown value. + *------------------------------------------ +*/ +int buildin_getmapxy(struct script_state *st){ + struct map_session_data *sd=NULL; + struct npc_data *nd; + struct pet_data *pd; + + int num; + char *name; + char prefix; + + int x,y,type; + char mapname[MAP_NAME_LENGTH+1]; + memset(mapname, 0, sizeof(mapname)); + + if( st->stack->stack_data[st->start+2].type!=C_NAME ){ + ShowWarning("script: buildin_getmapxy: not mapname variable\n"); + push_val(st->stack,C_INT,-1); + return 0; + } + if( st->stack->stack_data[st->start+3].type!=C_NAME ){ + ShowWarning("script: buildin_getmapxy: not mapx variable\n"); + push_val(st->stack,C_INT,-1); + return 0; + } + if( st->stack->stack_data[st->start+4].type!=C_NAME ){ + ShowWarning("script: buildin_getmapxy: not mapy variable\n"); + push_val(st->stack,C_INT,-1); + return 0; + } + +//??????????? >>> Possible needly check function parameters on C_STR,C_INT,C_INT <<< ???????????// + type=conv_num(st,& (st->stack->stack_data[st->start+5])); + + switch (type){ + case 0: //Get Character Position + if( st->end>st->start+6 ) + sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6]))); + else + sd=script_rid2sd(st); + + if ( sd==NULL ) { //wrong char name or char offline + push_val(st->stack,C_INT,-1); + return 0; + } + + + x=sd->bl.x; + y=sd->bl.y; + memcpy(mapname,mapindex_id2name(sd->mapindex), MAP_NAME_LENGTH); + break; + case 1: //Get NPC Position + if( st->end > st->start+6 ) + nd=npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+6]))); + else + nd=(struct npc_data *)map_id2bl(st->oid); + + if ( nd==NULL ) { //wrong npc name or char offline + push_val(st->stack,C_INT,-1); + return 0; + } + + x=nd->bl.x; + y=nd->bl.y; + memcpy(mapname, map[nd->bl.m].name, MAP_NAME_LENGTH); + break; + case 2: //Get Pet Position + if( st->end>st->start+6 ) + sd=map_nick2sd(conv_str(st,& (st->stack->stack_data[st->start+6]))); + else + sd=script_rid2sd(st); + + if ( sd==NULL ) { //wrong char name or char offline + push_val(st->stack,C_INT,-1); + return 0; + } + + pd=sd->pd; + + if(pd==NULL){ //pet data not found + push_val(st->stack,C_INT,-1); + return 0; + } + x=pd->bl.x; + y=pd->bl.y; + memcpy(mapname, map[pd->bl.m].name, MAP_NAME_LENGTH); + break; + + case 3: //Get Mob Position + push_val(st->stack,C_INT,-1); + return 0; + default: //Wrong type parameter + push_val(st->stack,C_INT,-1); + return 0; + } + + //Set MapName$ + num=st->stack->stack_data[st->start+2].u.num; + name=(char *)(str_buf+str_data[num&0x00ffffff].str); + prefix=*name; + + if(not_server_variable(prefix)) + sd=script_rid2sd(st); + else + sd=NULL; + + set_reg(st,sd,num,name,(void*)mapname,NULL); + + //Set MapX + num=st->stack->stack_data[st->start+3].u.num; + name=(char *)(str_buf+str_data[num&0x00ffffff].str); + prefix=*name; + + if(not_server_variable(prefix)) + sd=script_rid2sd(st); + else + sd=NULL; + set_reg(st,sd,num,name,(void*)x,NULL); + + + //Set MapY + num=st->stack->stack_data[st->start+4].u.num; + name=(char *)(str_buf+str_data[num&0x00ffffff].str); + prefix=*name; + + if(not_server_variable(prefix)) + sd=script_rid2sd(st); + else + sd=NULL; + + set_reg(st,sd,num,name,(void*)y,NULL); + + //Return Success value + push_val(st->stack,C_INT,0); + return 0; +} + +/*===================================================== + * Allows players to use a skill - by Qamera + *----------------------------------------------------- + */ +int buildin_skilluseid (struct script_state *st) +{ + int skid,sklv; + struct map_session_data *sd; + + skid=conv_num(st,& (st->stack->stack_data[st->start+2])); + sklv=conv_num(st,& (st->stack->stack_data[st->start+3])); + sd=script_rid2sd(st); + if (sd) + unit_skilluse_id(&sd->bl,sd->bl.id,skid,sklv); + + return 0; +} + +/*===================================================== + * Allows players to use a skill on a position [Celest] + *----------------------------------------------------- + */ +int buildin_skillusepos(struct script_state *st) +{ + int skid,sklv,x,y; + struct map_session_data *sd; + + skid=conv_num(st,& (st->stack->stack_data[st->start+2])); + sklv=conv_num(st,& (st->stack->stack_data[st->start+3])); + x=conv_num(st,& (st->stack->stack_data[st->start+4])); + y=conv_num(st,& (st->stack->stack_data[st->start+5])); + + sd=script_rid2sd(st); + if (sd) + unit_skilluse_pos(&sd->bl,x,y,skid,sklv); + + return 0; +} + +/*========================================== + * Allows player to write NPC logs (i.e. Bank NPC, etc) [Lupus] + *------------------------------------------ + */ +int buildin_logmes(struct script_state *st) +{ + if (log_config.npc <= 0 ) return 0; + conv_str(st,& (st->stack->stack_data[st->start+2])); + log_npc(script_rid2sd(st),st->stack->stack_data[st->start+2].u.str); + return 0; +} + +int buildin_summon(struct script_state *st) +{ + int _class, id, timeout=0; + char *str,*event=""; + struct map_session_data *sd; + struct mob_data *md; + int tick = gettick(); + + sd=script_rid2sd(st); + if (!sd) return 0; + + str =conv_str(st,& (st->stack->stack_data[st->start+2])); + _class=conv_num(st,& (st->stack->stack_data[st->start+3])); + if( st->end>st->start+4 ) + timeout=conv_num(st,& (st->stack->stack_data[st->start+4])); + if( st->end>st->start+5 ){ + event=conv_str(st,& (st->stack->stack_data[st->start+5])); + check_event(st, event); + } + + clif_skill_poseffect(&sd->bl,AM_CALLHOMUN,1,sd->bl.x,sd->bl.y,tick); + id=mob_once_spawn(sd, "this", 0, 0, str,_class,1,event); + md=(struct mob_data *)map_id2bl(id); + if (!md) return 0; + md->master_id=sd->bl.id; + md->special_state.ai=1; + md->deletetimer=add_timer(tick+(timeout>0?timeout*1000:60000),mob_timer_delete,id,0); + clif_misceffect2(&md->bl,344); + sc_start4(&md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 60000); + return 0; +} + +/*========================================== + * Checks whether it is daytime/nighttime + *------------------------------------------ + */ +int buildin_isnight(struct script_state *st) +{ + push_val(st->stack,C_INT, (night_flag == 1)); + return 0; +} + +int buildin_isday(struct script_state *st) +{ + push_val(st->stack,C_INT, (night_flag == 0)); + return 0; +} + +/*================================================ + * Check whether another item/card has been + * equipped - used for 2/15's cards patch [celest] + *------------------------------------------------ + */ +// leave this here, just in case +#if 0 +int buildin_isequipped(struct script_state *st) +{ + struct map_session_data *sd; + int i, j, k, id = 1; + int ret = -1; + + sd = script_rid2sd(st); + + for (i=0; id!=0; i++) { + int flag = 0; + + FETCH (i+2, id) else id = 0; + if (id <= 0) + continue; + + for (j=0; j<10; j++) { + int index, type; + index = sd->equip_index[j]; + if(index < 0) continue; + if(j == 9 && sd->equip_index[8] == index) continue; + if(j == 5 && sd->equip_index[4] == index) continue; + if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue; + type = itemdb_type(id); + + if(sd->inventory_data[index]) { + if (type == 4 || type == 5) { + if (sd->inventory_data[index]->nameid == id) + flag = 1; + } else if (type == 6) { + for(k=0; kinventory_data[index]->slot; k++) { + if (sd->status.inventory[index].card[0]!=0x00ff && + sd->status.inventory[index].card[0]!=0x00fe && + sd->status.inventory[index].card[0]!=(short)0xff00 && + sd->status.inventory[index].card[k] == id) { + flag = 1; + break; + } + } + } + if (flag) break; + } + } + if (ret == -1) + ret = flag; + else + ret &= flag; + if (!ret) break; + } + + push_val(st->stack,C_INT,ret); + return 0; +} +#endif + +/*================================================ + * Check how many items/cards in the list are + * equipped - used for 2/15's cards patch [celest] + *------------------------------------------------ + */ +int buildin_isequippedcnt(struct script_state *st) +{ + struct map_session_data *sd; + int i, j, k, id = 1; + int ret = 0; + + sd = script_rid2sd(st); + + for (i=0; id!=0; i++) { + FETCH (i+2, id) else id = 0; + if (id <= 0) + continue; + + for (j=0; j<10; j++) { + int index, type; + index = sd->equip_index[j]; + if(index < 0) continue; + if(j == 9 && sd->equip_index[8] == index) continue; + if(j == 5 && sd->equip_index[4] == index) continue; + if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue; + type = itemdb_type(id); + + if(sd->inventory_data[index]) { + if (type == 4 || type == 5) { + if (sd->inventory_data[index]->nameid == id) + ret++; //[Lupus] + } else if (type == 6) { + for(k=0; kinventory_data[index]->slot; k++) { + if (sd->status.inventory[index].card[0]!=0x00ff && + sd->status.inventory[index].card[0]!=0x00fe && + sd->status.inventory[index].card[0]!=(short)0xff00 && + sd->status.inventory[index].card[k] == id) { + ret++; //[Lupus] + } + } + } + } + } + } + + push_val(st->stack,C_INT,ret); + return 0; +} + +/*================================================ + * Check whether another card has been + * equipped - used for 2/15's cards patch [celest] + * -- Items checked cannot be reused in another + * card set to prevent exploits + *------------------------------------------------ + */ +int buildin_isequipped(struct script_state *st) +{ + struct map_session_data *sd; + int i, j, k, id = 1; + int index, type, flag; + int ret = -1; + + sd = script_rid2sd(st); + + if (!sd) { //If the player is not attached it is a script error anyway... but better prevent the map server from crashing... + push_val(st->stack,C_INT,0); + return 0; + } + + for (i=0; id!=0; i++) + { + FETCH (i+2, id) else id = 0; + if (id <= 0) + continue; + + type = itemdb_type(id); + flag = 0; + for (j=0; j<10; j++) + { + index = sd->equip_index[j]; + if(index < 0) continue; + if(j == 9 && sd->equip_index[8] == index) continue; + if(j == 5 && sd->equip_index[4] == index) continue; + if(j == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) continue; + + if(!sd->inventory_data[index]) + continue; + + switch (type) + { + case 4: + case 5: + if (sd->inventory_data[index]->nameid == id) + flag = 1; + break; + case 6: + if ( + sd->inventory_data[index]->slot == 0 || + sd->status.inventory[index].card[0] == 0x00ff || + sd->status.inventory[index].card[0] == 0x00fe || + sd->status.inventory[index].card[0] == (short)0xff00) + continue; + + for (k = 0; k < sd->inventory_data[index]->slot; k++) + { //New hash system which should support up to 4 slots on any equipment. [Skotlex] + unsigned int hash = 0; + if (sd->status.inventory[index].card[k] != id) + continue; + + hash = 1<<((j<5?j:j-5)*4 + k); + // check if card is already used by another set + if ((j<5?sd->setitem_hash:sd->setitem_hash2) & hash) + continue; + + // We have found a match + flag = 1; + // Set hash so this card cannot be used by another + if (j<5) + sd->setitem_hash |= hash; + else + sd->setitem_hash2 |= hash; + break; + } + //Case 6 end + break; + } + if (flag) break; + } + if (ret == -1) + ret = flag; + else + ret &= flag; + if (!ret) break; + } + + push_val(st->stack,C_INT,ret); + return 0; +} + +/*================================================ + * Check how many given inserted cards in the CURRENT + * weapon - used for 2/15's cards patch [Lupus] + *------------------------------------------------ + */ +int buildin_cardscnt(struct script_state *st) +{ + struct map_session_data *sd; + int i, k, id = 1; + int ret = 0; + int index, type; + + sd = script_rid2sd(st); + + for (i=0; id!=0; i++) { + FETCH (i+2, id) else id = 0; + if (id <= 0) + continue; + + index = current_equip_item_index; //we get CURRENT WEAPON inventory index from status.c [Lupus] + if(index < 0) continue; + + type = itemdb_type(id); + + if(sd->inventory_data[index]) { + if (type == 4 || type == 5) { + if (sd->inventory_data[index]->nameid == id) + ret++; + } else if (type == 6) { + for(k=0; kinventory_data[index]->slot; k++) { + if (sd->status.inventory[index].card[0]!=0x00ff && + sd->status.inventory[index].card[0]!=0x00fe && + sd->status.inventory[index].card[0]!=(short)0xff00 && + sd->status.inventory[index].card[k] == id) { + ret++; + } + } + } + } + } + push_val(st->stack,C_INT,ret); +// push_val(st->stack,C_INT,current_equip_item_index); + return 0; +} + +/*======================================================= + * Returns the refined number of the current item, or an + * item with inventory index specified + *------------------------------------------------------- + */ +int buildin_getrefine(struct script_state *st) +{ + struct map_session_data *sd; + if ((sd = script_rid2sd(st))!= NULL) + push_val(st->stack, C_INT, sd->status.inventory[current_equip_item_index].refine); + return 0; +} + +/*======================================================= + * Allows 2 Parents to adopt a character as a Baby + *------------------------------------------------------- + */ +int buildin_adopt(struct script_state *st) +{ + int ret; + + char *parent1 = conv_str(st,& (st->stack->stack_data[st->start+2])); + char *parent2 = conv_str(st,& (st->stack->stack_data[st->start+3])); + char *child = conv_str(st,& (st->stack->stack_data[st->start+4])); + + struct map_session_data *p1_sd = map_nick2sd(parent1); + struct map_session_data *p2_sd = map_nick2sd(parent2); + struct map_session_data *c_sd = map_nick2sd(child); + + if (!p1_sd || !p2_sd || !c_sd || + p1_sd->status.base_level < 70 || + p2_sd->status.base_level < 70) + return 0; + + ret = pc_adoption(p1_sd, p2_sd, c_sd); + push_val(st->stack, C_INT, ret); + + return 0; +} + +/*======================================================= + * Day/Night controls + *------------------------------------------------------- + */ +int buildin_night(struct script_state *st) +{ + if (night_flag != 1) map_night_timer(night_timer_tid, 0, 0, 1); + return 0; +} +int buildin_day(struct script_state *st) +{ + if (night_flag != 0) map_day_timer(day_timer_tid, 0, 0, 1); + return 0; +} + +//======================================================= +// Unequip [Spectre] +//------------------------------------------------------- +int buildin_unequip(struct script_state *st) +{ + int i; + size_t num; + struct map_session_data *sd; + + num = conv_num(st,& (st->stack->stack_data[st->start+2])) - 1; + sd=script_rid2sd(st); + if(sd!=NULL && num<10) + { + i=pc_checkequip(sd,equip[num]); + pc_unequipitem(sd,i,2); + return 0; + } + return 0; +} + +int buildin_equip(struct script_state *st) +{ + int nameid=0,count=0,i; + struct map_session_data *sd; + struct item_data *item_data; + + sd = script_rid2sd(st); + + nameid=conv_num(st,& (st->stack->stack_data[st->start+2])); + if(nameid>=500 && (item_data = itemdb_search(nameid)) != NULL) + for(i=0;istatus.inventory[i].nameid==nameid) + count+=sd->status.inventory[i].amount; + } + else{ + if(battle_config.error_log) + ShowError("wrong item ID : equipitem(%i)\n",nameid); + return 1; + } + + if(count){ + pc_equipitem(sd,nameid,item_data->equip); + } + + return 0; +} + +int buildin_autoequip(struct script_state *st){ + int nameid, flag; + struct item_data *item_data; + nameid=conv_num(st,& (st->stack->stack_data[st->start+2])); + flag=conv_num(st,& (st->stack->stack_data[st->start+3])); + if(nameid>=500 && (item_data = itemdb_search(nameid)) != NULL){ + item_data->flag.autoequip = flag>0?1:0; + } + return 0; +} + +int buildin_setbattleflag(struct script_state *st){ + char *flag, *value; + + flag = conv_str(st,& (st->stack->stack_data[st->start+2])); + value = conv_str(st,& (st->stack->stack_data[st->start+3])); + + if (battle_set_value(flag, value) == 0) + ShowWarning("buildin_setbattleflag: unknown battle_config flag '%s'",flag); + else + ShowInfo("buildin_setbattleflag: battle_config flag '%s' is now set to '%s'.",flag,value); + + return 0; +} + +int buildin_getbattleflag(struct script_state *st){ + char *flag; + flag = conv_str(st,& (st->stack->stack_data[st->start+2])); + push_val(st->stack,C_INT,battle_get_value(flag)); + return 0; +} + +//======================================================= +// strlen [Valaris] +//------------------------------------------------------- +int buildin_getstrlen(struct script_state *st) { + + char *str = conv_str(st,& (st->stack->stack_data[st->start+2])); + int len = (str) ? (int)strlen(str) : 0; + + push_val(st->stack,C_INT,len); + return 0; +} + +//======================================================= +// isalpha [Valaris] +//------------------------------------------------------- +int buildin_charisalpha(struct script_state *st) { + + char *str=conv_str(st,& (st->stack->stack_data[st->start+2])); + int pos=conv_num(st,& (st->stack->stack_data[st->start+3])); + + int val = ( str && pos>0 && (unsigned int)posstack,C_INT,val); + return 0; +} + +// [Lance] +int buildin_fakenpcname(struct script_state *st) +{ + char *name; + char *newname; + int look; + name = conv_str(st,& (st->stack->stack_data[st->start+2])); + newname = conv_str(st,& (st->stack->stack_data[st->start+3])); + look = conv_num(st,& (st->stack->stack_data[st->start+4])); + if(look > 32767 || look < -32768) { + ShowError("buildin_fakenpcname: Invalid look value %d\n",look); + return 1; // Safety measure to prevent runtime errors + } + npc_changename(name,newname,(short)look); + return 0; +} + +int buildin_atoi(struct script_state *st) { + char *value; + value = conv_str(st,& (st->stack->stack_data[st->start+2])); + push_val(st->stack, C_INT, atoi(value)); + return 0; +} + +//-----------------------------------------------------------------------// +// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA START // +//-----------------------------------------------------------------------// +int buildin_compare(struct script_state *st) { + char *message; + char *cmpstring; + int j; + message = conv_str(st,& (st->stack->stack_data[st->start+2])); + cmpstring = conv_str(st,& (st->stack->stack_data[st->start+3])); + for (j = 0; message[j]; j++) + message[j] = tolower(message[j]); + for (j = 0; cmpstring[j]; j++) + cmpstring[j] = tolower(cmpstring[j]); + push_val(st->stack,C_INT,(strstr(message,cmpstring) != NULL)); + return 0; +} + +//-----------------------------------------------------------------------// +// BRING STRSTR TO SCRIPTING ENGINE - LORDALFA END // +//-----------------------------------------------------------------------// +// [zBuffer] List of mathematics commands ---> +int buildin_sqrt(struct script_state *st){ + double i, a; + i = conv_num(st, &(st->stack->stack_data[st->start+2])); + a = sqrt(i); + push_val(st->stack, C_INT, (int)a); + return 0; +} + +int buildin_pow(struct script_state *st){ + double i, a, b; + a = conv_num(st, &(st->stack->stack_data[st->start+2])); + b = conv_num(st, &(st->stack->stack_data[st->start+3])); + i = pow(a,b); + push_val(st->stack, C_INT, (int)i); + return 0; +} +int buildin_distance(struct script_state *st){ + int x0, y0, x1, y1; + + x0 = conv_num(st, &(st->stack->stack_data[st->start+2])); + y0 = conv_num(st, &(st->stack->stack_data[st->start+3])); + x1 = conv_num(st, &(st->stack->stack_data[st->start+4])); + y1 = conv_num(st, &(st->stack->stack_data[st->start+5])); + + push_val(st->stack, C_INT, distance(x0-x1, y0-y1)); + return 0; +} + +// <--- [zBuffer] List of mathematics commands +// [zBuffer] List of dynamic var commands ---> +void setd_sub(struct script_state *st, struct map_session_data *sd, char *varname, int elem, void *value, struct linkdb_node **ref) +{ + set_reg(st, sd, add_str((unsigned char *) varname)+(elem<<24), varname, value, ref); + return; +} + +int buildin_setd(struct script_state *st) +{ + struct map_session_data *sd=NULL; + char varname[100], *buffer; + char *value; + int elem; + buffer = conv_str(st, & (st->stack->stack_data[st->start+2])); + value = conv_str(st, & (st->stack->stack_data[st->start+3])); + + if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2) + elem = 0; + + if(st->rid) + sd = script_rid2sd(st); + + if(varname[strlen(varname)-1] != '$') { + setd_sub(st,sd, varname, elem, (void *)atoi(value),NULL); + } else { + setd_sub(st,sd, varname, elem, (void *)value,NULL); + } + + return 0; +} + +#ifndef TXT_ONLY +int buildin_query_sql(struct script_state *st) { + char *name, *query; + int num, i = 0; + struct map_session_data *sd = (st->rid)? script_rid2sd(st) : NULL; + + query = conv_str(st,& (st->stack->stack_data[st->start+2])); + strcpy(tmp_sql, query); + if(mysql_query(&mmysql_handle,tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return 1; + } + + if(st->end > st->start+3) { + if(st->stack->stack_data[st->start+3].type != C_NAME){ + ShowWarning("buildin_query_sql: 2nd parameter is not a variable!\n"); + } else { + num=st->stack->stack_data[st->start+3].u.num; + name=(char *)(str_buf+str_data[num&0x00ffffff].str); + if((sql_res = mysql_store_result(&mmysql_handle))){ + if(name[strlen(name)-1] != '$') { + while(i<128 && (sql_row = mysql_fetch_row(sql_res))){ + setd_sub(st,sd, name, i, (void *)atoi(sql_row[0]),NULL); + i++; + } + } else { + while(i<128 && (sql_row = mysql_fetch_row(sql_res))){ + setd_sub(st,sd, name, i, (void *)sql_row[0],NULL); + i++; + } + } + mysql_free_result(sql_res); + } + } + } + + return 0; +} + +//Allows escaping of a given string. +int buildin_escape_sql(struct script_state *st) { + char *t_query, *query; + query = conv_str(st,& (st->stack->stack_data[st->start+2])); + + t_query = aMallocA((strlen(query)*2+1)*sizeof(char)); + jstrescapecpy(t_query,query); + push_str(st->stack,C_STR,(unsigned char *)t_query); + return 0; +} +#endif + +int buildin_getd (struct script_state *st) +{ + char varname[100], *buffer; + //struct script_data dat; + int elem; + + buffer = conv_str(st, & (st->stack->stack_data[st->start+2])); + + if(sscanf(buffer, "%[^[][%d]", varname, &elem) < 2) + elem = 0; + + /*dat.type=C_NAME; + dat.u.num=add_str((unsigned char *) varname)+(elem<<24); + get_val(st,&dat); + + if(dat.type == C_INT) + push_val(st->stack, C_INT, dat.u.num); + else if(dat.type == C_CONSTSTR){ + buffer = aStrdup((char *)dat.u.str); + // dat.u.str holds the actual pointer to the data, must be duplicated. + // It will be freed later. Tested. + push_str(st->stack, C_STR, buffer); + }*/ + + // Push the 'pointer' so it's more flexible [Lance] + push_val(st->stack,C_NAME, + (elem<<24) | add_str((unsigned char *) varname)); + + return 0; +} + +// <--- [zBuffer] List of dynamic var commands +// Pet stat [Lance] +int buildin_petstat(struct script_state *st){ + struct map_session_data *sd = NULL; + char *tmp; + int flag = conv_num(st, & (st->stack->stack_data[st->start+2])); + sd = script_rid2sd(st); + if(!sd || !sd->pet.pet_id){ + if(flag == 2) + push_str(st->stack, C_CONSTSTR, ""); + else + push_val(st->stack, C_INT, 0); + } + else { + switch(flag){ + case 1: + push_val(st->stack, C_INT, (int)sd->pet.class_); + break; + case 2: + tmp = aStrdup(sd->pet.name); + push_str(st->stack, C_STR, tmp); + break; + case 3: + push_val(st->stack, C_INT, (int)sd->pet.level); + break; + case 4: + push_val(st->stack, C_INT, (int)sd->pet.hungry); + break; + case 5: + push_val(st->stack, C_INT, (int)sd->pet.intimate); + break; + default: + push_val(st->stack, C_INT, 0); + break; + } + } + return 0; +} + +int buildin_callshop(struct script_state *st) +{ + struct map_session_data *sd = NULL; + struct npc_data *nd; + char *shopname; + int flag = 0; + sd = script_rid2sd(st); + if (!sd) { + push_val(st->stack,C_INT,0); + return 0; + } + shopname = conv_str(st, & (st->stack->stack_data[st->start+2])); + if( st->end>st->start+3 ) + flag = conv_num(st, & (st->stack->stack_data[st->start+3])); + nd = npc_name2id(shopname); + if (!nd || nd->bl.type!=BL_NPC || nd->bl.subtype!=SHOP) { + ShowError("buildin_callshop: Shop [%s] not found (or NPC is not shop type)", shopname); + push_val(st->stack,C_INT,0); + return 1; + } + + switch (flag) { + case 1: //Buy window + npc_buysellsel(sd,nd->bl.id,0); + break; + case 2: //Sell window + npc_buysellsel(sd,nd->bl.id,1); + break; + default: //Show menu + clif_npcbuysell(sd,nd->bl.id); + break; + } + sd->npc_shopid = nd->bl.id; + push_val(st->stack,C_INT,1); + return 0; +} + +int buildin_npcshopitem(struct script_state *st) +{ + struct npc_data *nd= NULL; + int n = 0; + int i = 3; + int itemid, value; + + char* npcname = conv_str(st, & (st->stack->stack_data[st->start + 2])); + nd = npc_name2id(npcname); + +#ifndef MAX_SHOPITEM + #define MAX_SHOPITEM 100 +#endif + + if(nd && nd->bl.subtype==SHOP){ + nd = (struct npc_data *)aRealloc(nd,sizeof(struct npc_data) + + sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1)); + + // Reset sell list. + for(;nd->u.shop_item[n].nameid && n < MAX_SHOPITEM; n++){ + nd->u.shop_item[n].nameid = 0; + nd->u.shop_item[n].value = 0; + } + + n = 0; + + while (st->end > st->start + i) { + itemid = conv_num(st, & (st->stack->stack_data[st->start+i])); + nd->u.shop_item[n].nameid = itemid; + i++; + value = conv_num(st, & (st->stack->stack_data[st->start+i])); + nd->u.shop_item[n].value = value; + i++; + n++; + } + + nd = (struct npc_data *)aRealloc(nd,sizeof(struct npc_data) + + sizeof(nd->u.shop_item[0]) * n); + + map_addiddb(&nd->bl); + + nd->master_nd = ((struct npc_data *)map_id2bl(st->oid)); + } else { + ShowError("buildin_npcshopitem: shop not found.\n"); + } + + return 0; +} + +/*========================================== + * Returns some values of an item [Lupus] + * Price, Weight, etc... + setiteminfo(itemID,"{new item bonus script}"); + *------------------------------------------ + */ +int buildin_setitemscript(struct script_state *st) +{ + int item_id; + char *script; + struct item_data *i_data; + + item_id = conv_num(st,& (st->stack->stack_data[st->start+2])); + script = conv_str(st,& (st->stack->stack_data[st->start+3])); + i_data = itemdb_exists(item_id); + + if (i_data && script!=NULL && script[0]=='{') { + if(i_data->script!=NULL) + script_free_code(i_data->script); + i_data->script = parse_script((unsigned char *) script, 0); + push_val(st->stack,C_INT,1); + } else + push_val(st->stack,C_INT,0); + return 0; +} + +/* Work In Progress [Lupus] +int buildin_addmonsterdrop(struct script_state *st) +{ + int class_,item_id,chance; + class_=conv_num(st,& (st->stack->stack_data[st->start+2])); + item_id=conv_num(st,& (st->stack->stack_data[st->start+3])); + chance=conv_num(st,& (st->stack->stack_data[st->start+4])); + if(class_>1000 && item_id>500 && chance>0) { + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } +} + +int buildin_delmonsterdrop(struct script_state *st) +{ + int class_,item_id; + class_=conv_num(st,& (st->stack->stack_data[st->start+2])); + item_id=conv_num(st,& (st->stack->stack_data[st->start+3])); + if(class_>1000 && item_id>500) { + push_val(st->stack,C_INT,1); + } else { + push_val(st->stack,C_INT,0); + } +} +*/ +/*========================================== + * Returns some values of a monster [Lupus] + * Name, Level, race, size, etc... + getmonsterinfo(monsterID,queryIndex); + *------------------------------------------ + */ +int buildin_getmonsterinfo(struct script_state *st) +{ + struct mob_db *mob; + int mob_id; + + mob_id = conv_num(st,& (st->stack->stack_data[st->start+2])); + if (!mobdb_checkid(mob_id)) { + ShowError("buildin_getmonsterinfo: Wrong Monster ID: %i", mob_id); + push_val(st->stack, C_INT, -1); + return -1; + } + mob = mob_db(mob_id); + switch ( conv_num(st,& (st->stack->stack_data[st->start+3])) ) { + case 0: //Name + push_str(st->stack,C_CONSTSTR, (unsigned char *) mob->jname); + break; + case 1: //Lvl + push_val(st->stack,C_INT, mob->lv); + break; + case 2: //MaxHP + push_val(st->stack,C_INT, mob->status.max_hp); + break; + case 3: //Base EXP + push_val(st->stack,C_INT, mob->base_exp); + break; + case 4: //Job EXP + push_val(st->stack,C_INT, mob->job_exp); + break; + case 5: //Atk1 + push_val(st->stack,C_INT, mob->status.rhw.atk); + break; + case 6: //Atk2 + push_val(st->stack,C_INT, mob->status.rhw.atk2); + break; + case 7: //Def + push_val(st->stack,C_INT, mob->status.def); + break; + case 8: //Mdef + push_val(st->stack,C_INT, mob->status.mdef); + break; + case 9: //Str + push_val(st->stack,C_INT, mob->status.str); + break; + case 10: //Agi + push_val(st->stack,C_INT, mob->status.agi); + break; + case 11: //Vit + push_val(st->stack,C_INT, mob->status.vit); + break; + case 12: //Int + push_val(st->stack,C_INT, mob->status.int_); + break; + case 13: //Dex + push_val(st->stack,C_INT, mob->status.dex); + break; + case 14: //Luk + push_val(st->stack,C_INT, mob->status.luk); + break; + case 15: //Range + push_val(st->stack,C_INT, mob->status.rhw.range); + break; + case 16: //Range2 + push_val(st->stack,C_INT, mob->range2); + break; + case 17: //Range3 + push_val(st->stack,C_INT, mob->range3); + break; + case 18: //Size + push_val(st->stack,C_INT, mob->status.size); + break; + case 19: //Race + push_val(st->stack,C_INT, mob->status.race); + break; + case 20: //Element + push_val(st->stack,C_INT, mob->status.def_ele); + break; + case 21: //Mode + push_val(st->stack,C_INT, mob->status.mode); + break; + default: //wrong Index + push_val(st->stack,C_INT,-1); + } + return 0; +} + +// [zBuffer] List of player cont commands ---> +int buildin_rid2name(struct script_state *st){ + struct block_list *bl = NULL; + int rid = conv_num(st, & (st->stack->stack_data[st->start + 2])); + if((bl = map_id2bl(rid))){ + switch(bl->type){ + case BL_MOB: + push_str(st->stack,C_CONSTSTR,((struct mob_data *)bl)->name); + break; + case BL_PC: + push_str(st->stack,C_CONSTSTR,((struct map_session_data *)bl)->status.name); + break; + case BL_NPC: + push_str(st->stack,C_CONSTSTR,((struct npc_data *)bl)->exname); + break; + case BL_PET: + push_str(st->stack,C_CONSTSTR,((struct pet_data *)bl)->name); + break; + case BL_HOMUNCULUS: + push_str(st->stack,C_CONSTSTR,((struct homun_data *)bl)->name); + break; + default: + ShowError("buildin_rid2name: BL type unknown.\n"); + push_str(st->stack,C_CONSTSTR,""); + break; + } + } + return 0; +} + +int buildin_pcwalkxy(struct script_state *st){ + int id, x, y = 0; + struct map_session_data *sd = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start + 2])); + x = conv_num(st, & (st->stack->stack_data[st->start + 3])); + if(st->end > st->start + 4) + y = conv_num(st, & (st->stack->stack_data[st->start + 4])); + + if(id) + sd = map_id2sd(id); + else + sd = script_rid2sd(st); + + if(sd){ + if(y) + unit_walktoxy(&sd->bl, x, y, 0); + else + unit_walktobl(&sd->bl, map_id2bl(x), 65535, 1); + } + + return 0; +} + +int buildin_pcblockmove(struct script_state *st){ + int id, flag; + struct map_session_data *sd = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start + 2])); + flag = conv_num(st, & (st->stack->stack_data[st->start + 3])); + + if(id) + sd = map_id2sd(id); + else + sd = script_rid2sd(st); + + if(sd) + sd->state.blockedmove = flag > 0; + + return 0; +} + +int buildin_pctalk(struct script_state *st){ + int id; + char *str; + char message[255]; + struct map_session_data *sd = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start + 2])); + str = conv_str(st, & (st->stack->stack_data[st->start + 3])); + + if(id) + sd = map_id2sd(id); + else + sd = script_rid2sd(st); + + if(sd){ + memcpy(message, sd->status.name, NAME_LENGTH); + strcat(message," : "); + strncat(message,str, 254); //Prevent overflow possibility. [Skotlex] + clif_message(&(sd->bl), message); + clif_displaymessage(sd->fd, message); + } + + return 0; +} + +int buildin_pcemote(struct script_state *st) { + int id, emo; + struct map_session_data *sd = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start + 2])); + emo = conv_num(st, & (st->stack->stack_data[st->start + 3])); + + if(id) + sd = map_id2sd(id); + else + sd = script_rid2sd(st); + + if(sd) + clif_emotion(&sd->bl,emo); + + return 0; + +} + +int buildin_pcfollow(struct script_state *st) { + int id, targetid; + struct map_session_data *sd = NULL; + + + id = conv_num(st, & (st->stack->stack_data[st->start + 2])); + targetid = conv_num(st, & (st->stack->stack_data[st->start + 3])); + + if(id) + sd = map_id2sd(id); + else + sd = script_rid2sd(st); + + if(sd) + pc_follow(sd, targetid); + + return 0; +} + +int buildin_pcstopfollow(struct script_state *st) { + int id; + struct map_session_data *sd = NULL; + + + id = conv_num(st, & (st->stack->stack_data[st->start + 2])); + + if(id) + sd = map_id2sd(id); + else + sd = script_rid2sd(st); + + if(sd) + pc_stop_following(sd); + + return 0; +} +// <--- [zBuffer] List of player cont commands +// [zBuffer] List of mob control commands ---> +int buildin_spawnmob(struct script_state *st){ + int class_,x,y,id; + char *str,*map,*event=""; + + // Who? + str =conv_str(st,& (st->stack->stack_data[st->start+2])); + // What? + class_ =conv_num(st,& (st->stack->stack_data[st->start+3])); + // Where? + map =conv_str(st,& (st->stack->stack_data[st->start+4])); + x =conv_num(st,& (st->stack->stack_data[st->start+5])); + y =conv_num(st,& (st->stack->stack_data[st->start+6])); + // When? + if( st->end > st->start+8 ){ + event=conv_str(st,& (st->stack->stack_data[st->start+7])); + check_event(st, event); + } + + id = mob_once_spawn(map_id2sd(st->rid),map,x,y,str,class_,1,event); + push_val(st->stack,C_INT,id); + + return 0; +} + +int buildin_removemob(struct script_state *st) { + int id; + struct block_list *bl = NULL; + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + + bl = map_id2bl(id); + if (bl && bl->type == BL_MOB) + unit_free(bl); + + return 0; +} + +int buildin_mobwalk(struct script_state *st){ + int id,x,y = 0; + struct block_list *bl = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + x = conv_num(st, & (st->stack->stack_data[st->start+3])); + if(st->end > st->start+4) + y = conv_num(st, & (st->stack->stack_data[st->start+4])); + + bl = map_id2bl(id); + if(bl && bl->type == BL_MOB){ + if(y) + push_val(st->stack,C_INT,unit_walktoxy(bl,x,y,0)); // We'll use harder calculations. + else + push_val(st->stack,C_INT,unit_walktobl(bl,map_id2bl(x),1,65025)); + } else { + push_val(st->stack,C_INT,0); + } + + return 0; +} + +int buildin_getmobdata(struct script_state *st) { + int num, id; + char *name; + struct mob_data *md = NULL; + struct map_session_data *sd = st->rid?map_id2sd(st->rid):NULL; + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + + if(!(md = (struct mob_data *)map_id2bl(id)) || md->bl.type != BL_MOB || st->stack->stack_data[st->start+3].type!=C_NAME ){ + ShowWarning("buildin_getmobdata: Error in argument!\n"); + return -1; + } + + num=st->stack->stack_data[st->start+2].u.num; + name=(char *)(str_buf+str_data[num&0x00ffffff].str); + setd_sub(st,sd,name,0,(void *)(int)md->class_,NULL); + setd_sub(st,sd,name,1,(void *)(int)md->level,NULL); + setd_sub(st,sd,name,2,(void *)(int)md->status.hp,NULL); + setd_sub(st,sd,name,3,(void *)(int)md->status.max_hp,NULL); + setd_sub(st,sd,name,4,(void *)(int)md->master_id,NULL); + setd_sub(st,sd,name,5,(void *)(int)md->bl.m,NULL); + setd_sub(st,sd,name,6,(void *)(int)md->bl.x,NULL); + setd_sub(st,sd,name,7,(void *)(int)md->bl.y,NULL); + setd_sub(st,sd,name,8,(void *)(int)md->status.speed,NULL); + setd_sub(st,sd,name,9,(void *)(int)md->status.mode,NULL); + setd_sub(st,sd,name,10,(void *)(int)md->special_state.ai,NULL); + setd_sub(st,sd,name,11,(void *)(int)md->sc.option,NULL); + setd_sub(st,sd,name,12,(void *)(int)md->vd->sex,NULL); + setd_sub(st,sd,name,13,(void *)(int)md->vd->class_,NULL); + setd_sub(st,sd,name,14,(void *)(int)md->vd->hair_style,NULL); + setd_sub(st,sd,name,15,(void *)(int)md->vd->hair_color,NULL); + setd_sub(st,sd,name,16,(void *)(int)md->vd->head_bottom,NULL); + setd_sub(st,sd,name,17,(void *)(int)md->vd->head_mid,NULL); + setd_sub(st,sd,name,18,(void *)(int)md->vd->head_top,NULL); + setd_sub(st,sd,name,19,(void *)(int)md->vd->cloth_color,NULL); + setd_sub(st,sd,name,20,(void *)(int)md->vd->shield,NULL); + setd_sub(st,sd,name,21,(void *)(int)md->vd->weapon,NULL); + setd_sub(st,sd,name,22,(void *)(int)md->vd->shield,NULL); + setd_sub(st,sd,name,23,(void *)(int)md->ud.dir,NULL); + setd_sub(st,sd,name,24,(void *)(int)md->state.killer,NULL); + return 0; +} + +int buildin_setmobdata(struct script_state *st){ + int id, value, value2; + struct mob_data *md = NULL; + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + value = conv_num(st, & (st->stack->stack_data[st->start+3])); + value2 = conv_num(st, & (st->stack->stack_data[st->start+4])); + if(!(md = (struct mob_data *)map_id2bl(id)) || md->bl.type != BL_MOB){ + ShowWarning("buildin_setmobdata: Error in argument!\n"); + return -1; + } + switch(value){ + case 0: + md->class_ = (short)value2; + break; + case 1: + md->level = (unsigned short)value2; + break; + case 2: + md->status.hp = value2; + break; + case 3: + md->status.max_hp = value2; + break; + case 4: + md->master_id = value2; + break; + case 5: + md->bl.m = (short)value2; + break; + case 6: + md->bl.x = (short)value2; + break; + case 7: + md->bl.y = (short)value2; + break; + case 8: + md->status.speed = (short)value2; + break; + case 9: + md->status.mode = (short)value2; + break; + case 10: + md->special_state.ai = (unsigned int)value2; + break; + case 11: + md->sc.option = (short)value2; + break; + case 12: + md->vd->sex = value2; + break; + case 13: + md->vd->class_ = value2; + break; + case 14: + md->vd->hair_style = (short)value2; + break; + case 15: + md->vd->hair_color = (short)value2; + break; + case 16: + md->vd->head_bottom = (short)value2; + break; + case 17: + md->vd->head_mid = (short)value2; + break; + case 18: + md->vd->head_top = (short)value2; + break; + case 19: + md->vd->cloth_color = (short)value2; + break; + case 20: + md->vd->shield = value2; + break; + case 21: + md->vd->weapon = (short)value2; + break; + case 22: + md->vd->shield = (short)value2; + break; + case 23: + md->ud.dir = (unsigned char)value2; + break; + case 24: + md->state.killer = value2>0?1:0; + break; + default: + ShowError("buildin_setmobdata: argument id is not identified."); + return -1; + } + return 0; +} + +int buildin_mobattack(struct script_state *st) { + int id = 0; + char *target = NULL; + struct mob_data *md = NULL; + struct map_session_data *sd = NULL; + struct block_list *bl = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + if(st->end > st->start + 3) + target = conv_str(st, & (st->stack->stack_data[st->start+3])); + + if(target){ + sd = map_nick2sd(target); + if(!sd) + bl = map_id2bl(atoi(target)); + else + bl = &sd->bl; + } + + if((md = (struct mob_data *)map_id2bl(id))){ + if (md && md->bl.type == BL_MOB) { + md->state.killer = 1; + md->special_state.ai = 1; + if(bl){ + md->target_id = bl->id; + unit_walktobl(&md->bl, bl, 65025, 2); + } + } + } + + return 0; +} + +int buildin_mobstop(struct script_state *st) { + int id; + struct block_list *bl = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + + bl = map_id2bl(id); + if(bl && bl->type == BL_MOB){ + unit_stop_attack(bl); + unit_stop_walking(bl,0); + ((TBL_MOB *)bl)->target_id = 0; + } + + return 0; +} + +int buildin_mobrandomwalk(struct script_state *st){ + int id = conv_num(st, &(st->stack->stack_data[st->start+2])); + int flag = conv_num(st, &(st->stack->stack_data[st->start+3])); + struct mob_data *md = (struct mob_data *)map_id2bl(id); + if(md->bl.type == BL_MOB){ + md->state.no_random_walk = flag>0?0:1; + } + return 0; +} + +int buildin_mobassist(struct script_state *st) { + int id; + char *target; + struct mob_data *md = NULL; + struct block_list *bl = NULL; + struct unit_data *ud; + + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + target = conv_str(st, & (st->stack->stack_data[st->start+3])); + + if((bl =&(map_nick2sd(target)->bl)) || (bl = map_id2bl(atoi(target)))) { + md = (struct mob_data *)map_id2bl(id); + if(md && md->bl.type == BL_MOB) { + ud = unit_bl2ud(bl); + md->master_id = bl->id; + md->state.killer = 1; + mob_convertslave(md); + if (ud) { + if (ud->target) + md->target_id = ud->target; + else if (ud->skilltarget) + md->target_id = ud->skilltarget; + if(md->target_id) + unit_walktobl(&md->bl, map_id2bl(md->target_id), 65025, 2); + } + } + } + return 0; +} + +int buildin_mobtalk(struct script_state *st) +{ + char *str; + int id; + char message[255]; + + struct mob_data *md = NULL; + + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + str=conv_str(st,& (st->stack->stack_data[st->start+3])); + + md = (struct mob_data *)map_id2bl(id); + if(md && md->bl.type == BL_MOB) { + memcpy(message, md->name, NAME_LENGTH); + strcat(message," : "); + strncat(message,str, 254); //Prevent overflow possibility. [Skotlex] + clif_message(&(md->bl), message); + } + + return 0; +} + +int buildin_mobemote(struct script_state *st) { + int id, emo; + struct mob_data *md = NULL; + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + emo = conv_num(st, & (st->stack->stack_data[st->start+3])); + if((md = (struct mob_data *)map_id2bl(id)) && md->bl.type == BL_MOB) + clif_emotion(&md->bl,emo); + return 0; +} + +int buildin_mobattach(struct script_state *st){ + int id; + struct mob_data *md = NULL; + struct npc_data *nd = NULL; + char *npcname = NULL; + id = conv_num(st, & (st->stack->stack_data[st->start+2])); + if(st->end > st->start + 3){ + npcname = conv_str(st, & (st->stack->stack_data[st->start+3])); + } + + if(npcname) + nd = npc_name2id(npcname); + else + nd = (struct npc_data *)map_id2bl(st->oid); + + if(nd) + if((md = (struct mob_data *)map_id2bl(id)) && md->bl.type == BL_MOB) + md->nd = nd; + + return 0; +} + +// <--- [zBuffer] List of mob control commands + +// sleep +int buildin_sleep(struct script_state *st) { + int tick = conv_num(st,& (st->stack->stack_data[st->start+2])); + struct map_session_data *sd = map_id2sd(st->rid); + if(sd && sd->npc_id == st->oid) { + sd->npc_id = 0; + } + st->rid = 0; + if(tick <= 0) { + // 何もしない + } else if( !st->sleep.tick ) { + // 初回実行 + st->state = RERUNLINE; + st->sleep.tick = tick; + } else { + // 続行 + st->sleep.tick = 0; + } + return 0; +} + +// sleep2 +int buildin_sleep2(struct script_state *st) { + int tick = conv_num(st,& (st->stack->stack_data[st->start+2])); + if( tick <= 0 ) { + // 0ms の待機時間を指定された + push_val(st->stack,C_INT,map_id2sd(st->rid) != NULL); + } else if( !st->sleep.tick ) { + // 初回実行時 + st->state = RERUNLINE; + st->sleep.tick = tick; + } else { + push_val(st->stack,C_INT,map_id2sd(st->rid) != NULL); + st->sleep.tick = 0; + } + return 0; +} + +/*========================================== + * 指定NPCの全てのsleepを再開する + *------------------------------------------ + */ +int buildin_awake(struct script_state *st) +{ + struct npc_data *nd; + struct linkdb_node *node = (struct linkdb_node *)sleep_db; + + nd = npc_name2id(conv_str(st,& (st->stack->stack_data[st->start+2]))); + if(nd == NULL) + return 0; + + while( node ) { + if( (int)node->key == nd->bl.id) { + struct script_state *tst = node->data; + struct map_session_data *sd = map_id2sd(tst->rid); + + if( tst->sleep.timer == -1 ) { + node = node->next; + continue; + } + if( sd && sd->char_id != tst->sleep.charid ) + tst->rid = 0; + + delete_timer(tst->sleep.timer, run_script_timer); + node = script_erase_sleepdb(node); + tst->sleep.timer = -1; + run_script_main(tst); + } else { + node = node->next; + } + } + return 0; +} + +// getvariableofnpc(, ); +int buildin_getvariableofnpc(struct script_state *st) +{ + if( st->stack->stack_data[st->start+2].type != C_NAME ) { + // 第一引数が変数名じゃない + printf("getvariableofnpc: param not name\n"); + push_val(st->stack,C_INT,0); + } else { + int num = st->stack->stack_data[st->start+2].u.num; + char *var_name = str_buf+str_data[num&0x00ffffff].str; + char *npc_name = conv_str(st,& (st->stack->stack_data[st->start+3])); + struct npc_data *nd = npc_name2id(npc_name); + if( var_name[0] != '.' || var_name[1] == '@' ) { + // ' 変数以外はダメ + printf("getvariableofnpc: invalid scope %s\n", var_name); + push_val(st->stack,C_INT,0); + } else if( nd == NULL || nd->bl.subtype != SCRIPT || !nd->u.scr.script) { + // NPC が見つからない or SCRIPT以外のNPC + printf("getvariableofnpc: can't find npc %s\n", npc_name); + push_val(st->stack,C_INT,0); + } else { + push_val2(st->stack,C_NAME,num, &nd->u.scr.script->script_vars ); + } + } + return 0; +} +// +// 実行部main +// +/*========================================== + * コマンドの読み取り + *------------------------------------------ + */ +static int unget_com_data=-1; +int get_com(unsigned char *script,int *pos) +{ + int i,j; + if(unget_com_data>=0){ + i=unget_com_data; + unget_com_data=-1; + return i; + } + if(script[*pos]>=0x80){ + return C_INT; + } + i=0; j=0; + while(script[*pos]>=0x40){ + i=script[(*pos)++]<=0xc0){ + i+=(script[(*pos)++]&0x7f)<stack->sp<=0) + return 0; + st->stack->sp--; + get_val(st,&(st->stack->stack_data[st->stack->sp])); + if(st->stack->stack_data[st->stack->sp].type==C_INT) + return st->stack->stack_data[st->stack->sp].u.num; + return 0; +} + +#define isstr(c) ((c).type==C_STR || (c).type==C_CONSTSTR) + +/*========================================== + * 加算演算子 + *------------------------------------------ + */ +void op_add(struct script_state* st) +{ + st->stack->sp--; + get_val(st,&(st->stack->stack_data[st->stack->sp])); + get_val(st,&(st->stack->stack_data[st->stack->sp-1])); + + if(isstr(st->stack->stack_data[st->stack->sp]) || isstr(st->stack->stack_data[st->stack->sp-1])){ + conv_str(st,&(st->stack->stack_data[st->stack->sp])); + conv_str(st,&(st->stack->stack_data[st->stack->sp-1])); + } + if(st->stack->stack_data[st->stack->sp].type==C_INT){ // ii + st->stack->stack_data[st->stack->sp-1].u.num += st->stack->stack_data[st->stack->sp].u.num; + } else { // ssの予定 + char *buf; + buf=(char *)aMallocA((strlen(st->stack->stack_data[st->stack->sp-1].u.str)+ + strlen(st->stack->stack_data[st->stack->sp].u.str)+1)*sizeof(char)); + strcpy(buf,st->stack->stack_data[st->stack->sp-1].u.str); + strcat(buf,st->stack->stack_data[st->stack->sp].u.str); + if(st->stack->stack_data[st->stack->sp-1].type==C_STR) + { + aFree(st->stack->stack_data[st->stack->sp-1].u.str); + st->stack->stack_data[st->stack->sp-1].type=C_INT; + } + if(st->stack->stack_data[st->stack->sp].type==C_STR) + { + aFree(st->stack->stack_data[st->stack->sp].u.str); + st->stack->stack_data[st->stack->sp].type=C_INT; + } + st->stack->stack_data[st->stack->sp-1].type=C_STR; + st->stack->stack_data[st->stack->sp-1].u.str=buf; + } +} + +/*========================================== + * 二項演算子(文字列) + *------------------------------------------ + */ +void op_2str(struct script_state *st,int op,int sp1,int sp2) +{ + char *s1=st->stack->stack_data[sp1].u.str, + *s2=st->stack->stack_data[sp2].u.str; + int a=0; + + switch(op){ + case C_EQ: + a= (strcmp(s1,s2)==0); + break; + case C_NE: + a= (strcmp(s1,s2)!=0); + break; + case C_GT: + a= (strcmp(s1,s2)> 0); + break; + case C_GE: + a= (strcmp(s1,s2)>=0); + break; + case C_LT: + a= (strcmp(s1,s2)< 0); + break; + case C_LE: + a= (strcmp(s1,s2)<=0); + break; + default: + ShowWarning("script: illegal string operator\n"); + break; + } + + // Because push_val() overwrite stack_data[sp1], C_STR on stack_data[sp1] won't be freed. + // So, call push_val() after freeing strings. [jA1783] + // push_val(st->stack,C_INT,a); + if(st->stack->stack_data[sp1].type==C_STR) + { + aFree(s1); + st->stack->stack_data[sp1].type=C_INT; + } + if(st->stack->stack_data[sp2].type==C_STR) + { + aFree(s2); + st->stack->stack_data[sp2].type=C_INT; + } + push_val(st->stack,C_INT,a); +} +/*========================================== + * 二項演算子(数値) + *------------------------------------------ + */ +void op_2num(struct script_state *st,int op,int i1,int i2) +{ + switch(op){ + case C_SUB: + i1-=i2; + break; + case C_MUL: + { + #ifndef _MSC_VER + long long res = i1 * i2; + #else + __int64 res = i1 * i2; + #endif + if (res > 2147483647 ) + i1 = 2147483647; + else + i1*=i2; + } + break; + case C_DIV: + if (i2 != 0) + i1/=i2; + else + ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_DIV)!\n"); + break; + case C_MOD: + if (i2 != 0) + i1%=i2; + else + ShowWarning("op_2num: Attempted to divide by 0 in a script (operation C_MOD)!\n"); + break; + case C_AND: + i1&=i2; + break; + case C_OR: + i1|=i2; + break; + case C_XOR: + i1^=i2; + break; + case C_LAND: + i1=i1&&i2; + break; + case C_LOR: + i1=i1||i2; + break; + case C_EQ: + i1=i1==i2; + break; + case C_NE: + i1=i1!=i2; + break; + case C_GT: + i1=i1>i2; + break; + case C_GE: + i1=i1>=i2; + break; + case C_LT: + i1=i1>i2; + break; + case C_L_SHIFT: + i1=i1<stack,C_INT,i1); +} +/*========================================== + * 二項演算子 + *------------------------------------------ + */ +void op_2(struct script_state *st,int op) +{ + int i1,i2; + char *s1=NULL,*s2=NULL; + + i2=pop_val(st); + if( isstr(st->stack->stack_data[st->stack->sp]) ) + s2=st->stack->stack_data[st->stack->sp].u.str; + + i1=pop_val(st); + if( isstr(st->stack->stack_data[st->stack->sp]) ) + s1=st->stack->stack_data[st->stack->sp].u.str; + + if( s1!=NULL && s2!=NULL ){ + // ss => op_2str + op_2str(st,op,st->stack->sp,st->stack->sp+1); + }else if( s1==NULL && s2==NULL ){ + // ii => op_2num + op_2num(st,op,i1,i2); + }else{ + // si,is => error + ShowWarning("script: op_2: int&str, str&int not allow."); + push_val(st->stack,C_INT,0); + } +} + +/*========================================== + * 単項演算子 + *------------------------------------------ + */ +void op_1num(struct script_state *st,int op) +{ + int i1; + i1=pop_val(st); + switch(op){ + case C_NEG: + i1=-i1; + break; + case C_NOT: + i1=~i1; + break; + case C_LNOT: + i1=!i1; + break; + } + push_val(st->stack,C_INT,i1); +} + + +/*========================================== + * 関数の実行 + *------------------------------------------ + */ +int run_func(struct script_state *st) +{ + int i,start_sp,end_sp,func; + + end_sp=st->stack->sp; + for(i=end_sp-1;i>=0 && st->stack->stack_data[i].type!=C_ARG;i--); + if(i==0){ + if(battle_config.error_log) + ShowError("function not found\n"); +// st->stack->sp=0; + st->state=END; + report_src(st); + return 1; + } + start_sp=i-1; + st->start=i-1; + st->end=end_sp; + + func=st->stack->stack_data[st->start].u.num; + if( st->stack->stack_data[st->start].type!=C_NAME || str_data[func].type!=C_FUNC ){ + ShowMessage ("run_func: '"CL_WHITE"%s"CL_RESET"' (type %d) is not function and command!\n", + str_buf + str_data[func].str, str_data[func].type); +// st->stack->sp=0; + st->state=END; + report_src(st); + return 1; + } +#ifdef DEBUG_RUN + if(battle_config.etc_log) { + ShowDebug("run_func : %s? (%d(%d)) sp=%d (%d...%d)\n",str_buf+str_data[func].str, func, str_data[func].type, st->stack->sp, st->start, st->end); + ShowDebug("stack dump :"); + for(i=0;istack->stack_data[i].type){ + case C_INT: + printf(" int(%d)",st->stack->stack_data[i].u.num); + break; + case C_NAME: + printf(" name(%s)",str_buf+str_data[st->stack->stack_data[i].u.num].str); + break; + case C_ARG: + printf(" arg"); + break; + case C_POS: + printf(" pos(%d)",st->stack->stack_data[i].u.num); + break; + case C_STR: + printf(" str(%s)",st->stack->stack_data[i].u.str); + break; + case C_CONSTSTR: + printf(" cstr(%s)",st->stack->stack_data[i].u.str); + break; + default: + printf(" %d,%d",st->stack->stack_data[i].type,st->stack->stack_data[i].u.num); + } + } + printf("\n"); + } +#endif + if(str_data[func].func){ + if (str_data[func].func(st)) //Report error + report_src(st); + } else { + if(battle_config.error_log) + ShowError("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type); + push_val(st->stack,C_INT,0); + report_src(st); + } + + // Stack's datum are used when re-run functions [Eoe] + if(st->state != RERUNLINE) { + pop_stack(st->stack,start_sp,end_sp); + } + + if(st->state==RETFUNC){ + // ユーザー定義関数からの復帰 + int olddefsp=st->stack->defsp; + int i; + + pop_stack(st->stack,st->stack->defsp,start_sp); // 復帰に邪魔なスタック削除 + if(st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO){ + ShowWarning("script:run_func(return) return without callfunc or callsub!\n"); + st->state=END; + report_src(st); + return 1; + } + script_free_vars( st->stack->var_function ); + aFree(st->stack->var_function); + i = conv_num(st,& (st->stack->stack_data[st->stack->defsp-5])); // 引数の数所得 + st->pos=conv_num(st,& (st->stack->stack_data[st->stack->defsp-1])); // スクリプト位置の復元 + st->script=(struct script_code *)conv_num(st,& (st->stack->stack_data[st->stack->defsp-3])); // スクリプトを復元 + st->stack->var_function = (struct linkdb_node**)st->stack->stack_data[st->stack->defsp-2].u.num; // 関数依存変数 + st->stack->defsp=conv_num(st,& (st->stack->stack_data[st->stack->defsp-4])); // 基準スタックポインタを復元 + + pop_stack(st->stack,olddefsp-5-i,olddefsp); // 要らなくなったスタック(引数と復帰用データ)削除 + + st->state=GOTO; + } + + return 0; +} + +/*========================================== + * スクリプトの実行メイン部分 + *------------------------------------------ + */ +int run_script_main(struct script_state *st) +{ + int c/*,rerun_pos*/; + int cmdcount=script_config.check_cmdcount; + int gotocount=script_config.check_gotocount; + struct script_stack *stack=st->stack; + + if(st->state == RERUNLINE) { + st->state = RUN; + run_func(st); + if(st->state == GOTO){ + st->state = RUN; + } + } else { + st->state = RUN; + } + while( st->state == RUN) { + c= get_com((unsigned char *) st->script->script_buf,&st->pos); + switch(c){ + case C_EOL: + if(stack->sp!=stack->defsp){ + if(stack->sp > stack->defsp) + { //sp > defsp is valid in cases when you invoke functions and don't use the returned value. [Skotlex] + //Since sp is supposed to be defsp in these cases, we could assume the extra stack elements are unneeded. + if (battle_config.etc_log) + ShowWarning("Clearing unused stack stack.sp(%d) -> default(%d)\n",stack->sp,stack->defsp); + pop_stack(stack, stack->defsp, stack->sp); //Clear out the unused stack-section. + } else if(battle_config.error_log) + ShowError("stack.sp(%d) != default(%d)\n",stack->sp,stack->defsp); + stack->sp=stack->defsp; + } + // rerun_pos=st->pos; + break; + case C_INT: + push_val(stack,C_INT,get_num((unsigned char *) st->script->script_buf,&st->pos)); + break; + case C_POS: + case C_NAME: + push_val(stack,c,(*(int*)(st->script->script_buf+st->pos))&0xffffff); + st->pos+=3; + break; + case C_ARG: + push_val(stack,c,0); + break; + case C_STR: + push_str(stack,C_CONSTSTR,(unsigned char *) (st->script->script_buf+st->pos)); + while(st->script->script_buf[st->pos++]); + break; + case C_FUNC: + run_func(st); + if(st->state==GOTO){ + // rerun_pos=st->pos; + st->state=0; + if( gotocount>0 && (--gotocount)<=0 ){ + ShowError("run_script: infinity loop !\n"); + st->state=END; + } + } + break; + + case C_ADD: + op_add(st); + break; + + case C_SUB: + case C_MUL: + case C_DIV: + case C_MOD: + case C_EQ: + case C_NE: + case C_GT: + case C_GE: + case C_LT: + case C_LE: + case C_AND: + case C_OR: + case C_XOR: + case C_LAND: + case C_LOR: + case C_R_SHIFT: + case C_L_SHIFT: + op_2(st,c); + break; + + case C_NEG: + case C_NOT: + case C_LNOT: + op_1num(st,c); + break; + + case C_NOP: + st->state=END; + break; + + default: + if(battle_config.error_log) + ShowError("unknown command : %d @ %d\n",c,pos); + st->state=END; + break; + } + if( cmdcount>0 && (--cmdcount)<=0 ){ + ShowError("run_script: infinity loop !\n"); + st->state=END; + } + } + switch(st->state){ + case STOP: + break; + case END: + { + struct map_session_data *sd=st->rid?map_id2sd(st->rid):NULL; + st->pos=-1; + if(sd && (sd->npc_id==st->oid || sd->state.using_fake_npc)){ + if(sd->state.using_fake_npc){ + clif_clearchar_id(sd->npc_id, 0, sd->fd); + sd->state.using_fake_npc = 0; + } + npc_event_dequeue(sd); + } + } + break; + case RERUNLINE: + // Do not call function of commands two time! [ Eoe / jA 1094 ] + // For example: select "1", "2", callsub(...); + // If current script position is changed, callsub will be called two time. + // + // { + // st->pos=rerun_pos; + // } + break; + } + + if(st->state == END) { + script_free_stack (st->stack); + st->stack = NULL; + aFree(st); + st = NULL; + return 0; + } + + return 1; +} + +/*========================================== + * スクリプトの実行 + *------------------------------------------ + */ +int run_script(struct script_code *rootscript,int pos,int rid,int oid) +{ + struct script_state *st; + struct map_session_data *sd=NULL; + + //Variables for backing up the previous script and restore it if needed. [Skotlex] + struct script_code *bck_script = NULL; + struct script_code *bck_scriptroot = NULL; + int bck_scriptstate = 0; + struct script_stack *bck_stack = NULL; + + if (rootscript == NULL || pos < 0) + return -1; + + st = aCalloc(sizeof(struct script_state), 1); + + if ((sd = map_id2sd(rid)) && sd->stack && sd->npc_scriptroot == rootscript){ + // we have a stack for the same script, should continue exec. + st->script = sd->npc_script; + st->stack = sd->stack; + st->state = sd->npc_scriptstate; + // and clear vars + sd->stack = NULL; + sd->npc_script = NULL; + sd->npc_scriptroot = NULL; + sd->npc_scriptstate = 0; + } else { + // the script is different, make new script_state and stack + st->stack = aMalloc (sizeof(struct script_stack)); + st->stack->sp = 0; + st->stack->sp_max = 64; + st->stack->stack_data = (struct script_data *) aCalloc (st->stack->sp_max,sizeof(st->stack->stack_data[0])); + st->stack->defsp = st->stack->sp; + st->stack->var_function = aCalloc(1, sizeof(struct linkdb_node*)); + st->state = RUN; + st->script = rootscript; + + if (sd && sd->stack) { // if there's a sd and a stack - back it up and restore it if possible. + bck_script = sd->npc_script; + bck_scriptroot = sd->npc_scriptroot; + bck_scriptstate = sd->npc_scriptstate; + bck_stack = sd->stack; + sd->stack = NULL; + } + } + st->pos = pos; + st->rid = rid; + st->oid = oid; + st->sleep.timer = -1; + + if(run_script_main(st)){ + if(st->state != END){ + pos = st->pos; + if(st->sleep.tick > 0) + { //Delay execution + st->sleep.charid = sd?sd->char_id:0; + st->sleep.timer = add_timer(gettick()+st->sleep.tick, + run_script_timer, st->sleep.charid, (int)st); + linkdb_insert(&sleep_db, (void*)st->oid, st); + } else if (sd) { + // script is not finished, store data in sd. + sd->npc_script = st->script; + sd->npc_scriptroot = rootscript; + sd->npc_scriptstate = st->state; + sd->stack = st->stack; + if (bck_stack) //Get rid of the backup as it can't be restored. + script_free_stack (bck_stack); + aFree(st); + } + return pos; + } else { + if(st->stack) + script_free_stack(st->stack); + aFree(st); + } + } else { + //Script finished. + if (sd) + { //Clear or restore previous script. + sd->npc_script = bck_script; + sd->npc_scriptroot = bck_scriptroot; + sd->npc_scriptstate = bck_scriptstate; + sd->stack = bck_stack; + //Since the script is done, save any changed account variables [Skotlex] + if (sd->state.reg_dirty&2) + intif_saveregistry(sd,2); + if (sd->state.reg_dirty&1) + intif_saveregistry(sd,1); + } + } + return 0; +} + +/*========================================== + * 指定ノードをsleep_dbから削除 + *------------------------------------------ + */ +struct linkdb_node* script_erase_sleepdb(struct linkdb_node *n) +{ + struct linkdb_node *retnode; + + if( n == NULL) + return NULL; + if( n->prev == NULL ) + sleep_db = n->next; + else + n->prev->next = n->next; + if( n->next ) + n->next->prev = n->prev; + retnode = n->next; + aFree( n ); + return retnode; // 次のノードを返す +} + + +/*========================================== + * sleep用タイマー関数 + *------------------------------------------ + */ +int run_script_timer(int tid, unsigned int tick, int id, int data) +{ + struct script_state *st = (struct script_state *)data; + struct linkdb_node *node = (struct linkdb_node *)sleep_db; + struct map_session_data *sd = map_id2sd(st->rid); + + if( sd && sd->char_id != id ) { + st->rid = 0; + } + while( node && st->sleep.timer != -1 ) { + if( (int)node->key == st->oid && ((struct script_state *)node->data)->sleep.timer == st->sleep.timer ) { + script_erase_sleepdb(node); + st->sleep.timer = -1; + break; + } + node = node->next; + } + run_script_main(st); + return 0; +} + + +/*========================================== + * マップ変数の変更 + *------------------------------------------ + */ +int mapreg_setreg(int num,int val) +{ +#if !defined(TXT_ONLY) && defined(MAPREGSQL) + int i=num>>24; + char *name=str_buf+str_data[num&0x00ffffff].str; + char tmp_str[64]; +#endif + + if(val!=0) { + +#if !defined(TXT_ONLY) && defined(MAPREGSQL) + if(name[1] != '@' && idb_get(mapreg_db,num) == NULL) { + sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%d')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,val); + if(mysql_query(&mmysql_handle,tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } +#endif + idb_put(mapreg_db,num,(void*)val); + // else + } else { // [zBuffer] +#if !defined(TXT_ONLY) && defined(MAPREGSQL) + if(name[1] != '@') { // Remove from database because it is unused. + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i); + if(mysql_query(&mmysql_handle,tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } +#endif + idb_remove(mapreg_db,num); + } + + mapreg_dirty=1; + return 0; +} +/*========================================== + * 文字列型マップ変数の変更 + *------------------------------------------ + */ +int mapreg_setregstr(int num,const char *str) +{ + char *p; +#if !defined(TXT_ONLY) && defined(MAPREGSQL) + char tmp_str[64]; + char tmp_str2[512]; + int i=num>>24; // [zBuffer] + char *name=str_buf+str_data[num&0x00ffffff].str; +#endif + + if( str==NULL || *str==0 ){ +#if !defined(TXT_ONLY) && defined(MAPREGSQL) + if(name[1] != '@') { + sprintf(tmp_sql,"DELETE FROM `%s` WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_varname,name,mapregsql_db_index,i); + if(mysql_query(&mmysql_handle,tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } +#endif + idb_remove(mapregstr_db,num); + mapreg_dirty=1; + return 0; + } + p=(char *)aMallocA((strlen(str)+1)*sizeof(char)); + strcpy(p,str); + + if (idb_put(mapregstr_db,num,p)) + ; +#if !defined(TXT_ONLY) && defined(MAPREGSQL) + else { //put returned null, so we must insert. + sprintf(tmp_sql,"INSERT INTO `%s`(`%s`,`%s`,`%s`) VALUES ('%s','%d','%s')",mapregsql_db,mapregsql_db_varname,mapregsql_db_index,mapregsql_db_value,jstrescapecpy(tmp_str,name),i,jstrescapecpy(tmp_str2,p)); + if(mysql_query(&mmysql_handle,tmp_sql)){ + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } +#endif + mapreg_dirty=1; + return 0; +} + +/*========================================== + * 永続的マップ変数の読み込み + *------------------------------------------ + */ +static int script_load_mapreg(void) +{ +#if defined(TXT_ONLY) || !defined(MAPREGSQL) + FILE *fp; + char line[1024]; + + if( (fp=fopen(mapreg_txt,"rt"))==NULL ) + return -1; + + while(fgets(line,sizeof(line),fp)){ + char buf1[256],buf2[1024],*p; + int n,v,s,i; + if( sscanf(line,"%255[^,],%d\t%n",buf1,&i,&n)!=2 && + (i=0,sscanf(line,"%[^\t]\t%n",buf1,&n)!=1) ) + continue; + if( buf1[strlen(buf1)-1]=='$' ){ + if( sscanf(line+n,"%[^\n\r]",buf2)!=1 ){ + ShowError("%s: %s broken data !\n",mapreg_txt,buf1); + continue; + } + p=(char *)aMallocA((strlen(buf2) + 1)*sizeof(char)); + strcpy(p,buf2); + s= add_str((unsigned char *) buf1); + idb_put(mapregstr_db,(i<<24)|s,p); + }else{ + if( sscanf(line+n,"%d",&v)!=1 ){ + ShowError("%s: %s broken data !\n",mapreg_txt,buf1); + continue; + } + s= add_str((unsigned char *) buf1); + idb_put(mapreg_db,(i<<24)|s,(void*)v); + } + } + fclose(fp); + mapreg_dirty=0; + return 0; +#else + // SQL mapreg code start [zBuffer] + /* + 0 1 2 + +-------------------------+ + | varname | index | value | + +-------------------------+ + */ + int perfomance = gettick_nocache(); + sprintf(tmp_sql,"SELECT * FROM `%s`",mapregsql_db); + ShowInfo("Querying script_load_mapreg ...\n"); + if(mysql_query(&mmysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + return -1; + } + ShowInfo("Success! Returning results ...\n"); + sql_res = mysql_store_result(&mmysql_handle); + if (sql_res) { + while ((sql_row = mysql_fetch_row(sql_res))) { + char buf1[33], *p = NULL; + int i,v,s; + strcpy(buf1,sql_row[0]); + if( buf1[strlen(buf1)-1]=='$' ){ + i = atoi(sql_row[1]); + p=(char *)aMallocA((strlen(sql_row[2]) + 1)*sizeof(char)); + strcpy(p,sql_row[2]); + s= add_str((unsigned char *) buf1); + idb_put(mapregstr_db,(i<<24)|s,p); + }else{ + s= add_str((unsigned char *) buf1); + v= atoi(sql_row[2]); + i = atoi(sql_row[1]); + idb_put(mapreg_db,(i<<24)|s,(void *)v); + } + } + } + ShowInfo("Freeing results...\n"); + mysql_free_result(sql_res); + mapreg_dirty=0; + perfomance = (gettick_nocache() - perfomance) / 1000; + ShowInfo("SQL Mapreg Loading Completed Under %d Seconds.\n",perfomance); + return 0; +#endif /* TXT_ONLY */ +} +/*========================================== + * 永続的マップ変数の書き込み + *------------------------------------------ + */ +static int script_save_mapreg_intsub(DBKey key,void *data,va_list ap) +{ +#if defined(TXT_ONLY) || !defined(MAPREGSQL) + FILE *fp=va_arg(ap,FILE*); + int num=key.i&0x00ffffff, i=key.i>>24; + char *name=str_buf+str_data[num].str; + if( name[1]!='@' ){ + if(i==0) + fprintf(fp,"%s\t%d\n", name, (int)data); + else + fprintf(fp,"%s,%d\t%d\n", name, i, (int)data); + } + return 0; +#else + int num=key.i&0x00ffffff, i=key.i>>24; // [zBuffer] + char *name=str_buf+str_data[num].str; + if ( name[1] != '@') { + sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%d' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,(int)data,mapregsql_db_varname,name,mapregsql_db_index,i); + if(mysql_query(&mmysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + return 0; +#endif +} +static int script_save_mapreg_strsub(DBKey key,void *data,va_list ap) +{ +#if defined(TXT_ONLY) || !defined(MAPREGSQL) + FILE *fp=va_arg(ap,FILE*); + int num=key.i&0x00ffffff, i=key.i>>24; + char *name=str_buf+str_data[num].str; + if( name[1]!='@' ){ + if(i==0) + fprintf(fp,"%s\t%s\n", name, (char *)data); + else + fprintf(fp,"%s,%d\t%s\n", name, i, (char *)data); + } + return 0; +#else + char tmp_str2[512]; + int num=key.i&0x00ffffff, i=key.i>>24; + char *name=str_buf+str_data[num].str; + if ( name[1] != '@') { + sprintf(tmp_sql,"UPDATE `%s` SET `%s`='%s' WHERE `%s`='%s' AND `%s`='%d'",mapregsql_db,mapregsql_db_value,jstrescapecpy(tmp_str2,(char *)data),mapregsql_db_varname,name,mapregsql_db_index,i); + if(mysql_query(&mmysql_handle, tmp_sql) ) { + ShowSQL("DB error - %s\n",mysql_error(&mmysql_handle)); + ShowDebug("at %s:%d - %s\n", __FILE__,__LINE__,tmp_sql); + } + } + return 0; +#endif +} +static int script_save_mapreg(void) +{ +#if defined(TXT_ONLY) || !defined(MAPREGSQL) + FILE *fp; + int lock; + + if( (fp=lock_fopen(mapreg_txt,&lock))==NULL ) { + ShowError("script_save_mapreg: Unable to lock-open file [%s]\n",mapreg_txt); + return -1; + } + mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub,fp); + mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub,fp); + lock_fclose(fp,mapreg_txt,&lock); +#else + int perfomance = gettick_nocache(); + mapreg_db->foreach(mapreg_db,script_save_mapreg_intsub); // [zBuffer] + mapregstr_db->foreach(mapregstr_db,script_save_mapreg_strsub); + perfomance = (gettick_nocache() - perfomance) / 1000; + ShowInfo("Mapreg saved in %d seconds.\n", perfomance); +#endif + mapreg_dirty=0; + return 0; +} +static int script_autosave_mapreg(int tid,unsigned int tick,int id,int data) +{ + if(mapreg_dirty) + if (script_save_mapreg() == -1) + ShowError("Failed to save the mapreg data!\n"); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +static int set_posword(char *p) +{ + char* np,* str[15]; + int i=0; + for(i=0;i<11;i++) { + if((np=strchr(p,','))!=NULL) { + str[i]=p; + *np=0; + p=np+1; + } else { + str[i]=p; + p+=strlen(p); + } + if(str[i]) + strcpy(pos[i],str[i]); + } + return 0; +} + +int script_config_read_sub(char *cfgName) +{ + int i; + char line[1024],w1[1024],w2[1024]; + FILE *fp; + + + fp = fopen(cfgName, "r"); + if (fp == NULL) { + ShowError("file not found: [%s]\n", cfgName); + return 1; + } + while (fgets(line, sizeof(line) - 1, fp)) { + if (line[0] == '/' && line[1] == '/') + continue; + i = sscanf(line,"%[^:]: %[^\r\n]",w1,w2); + if (i != 2) + continue; + if(strcmpi(w1,"refine_posword")==0) { + set_posword(w2); + } + else if(strcmpi(w1,"verbose_mode")==0) { + script_config.verbose_mode = battle_config_switch(w2); + } + else if(strcmpi(w1,"warn_func_no_comma")==0) { + script_config.warn_func_no_comma = battle_config_switch(w2); + } + else if(strcmpi(w1,"warn_cmd_no_comma")==0) { + script_config.warn_cmd_no_comma = battle_config_switch(w2); + } + else if(strcmpi(w1,"warn_func_mismatch_paramnum")==0) { + script_config.warn_func_mismatch_paramnum = battle_config_switch(w2); + } + else if(strcmpi(w1,"warn_cmd_mismatch_paramnum")==0) { + script_config.warn_cmd_mismatch_paramnum = battle_config_switch(w2); + } + else if(strcmpi(w1,"check_cmdcount")==0) { + script_config.check_cmdcount = battle_config_switch(w2); + } + else if(strcmpi(w1,"check_gotocount")==0) { + script_config.check_gotocount = battle_config_switch(w2); + } + else if(strcmpi(w1,"event_script_type")==0) { + script_config.event_script_type = battle_config_switch(w2); + } + else if(strcmpi(w1,"event_requires_trigger")==0) { + script_config.event_requires_trigger = battle_config_switch(w2); + } + else if(strcmpi(w1,"die_event_name")==0) { + strncpy(script_config.die_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.die_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.die_event_name); + } + else if(strcmpi(w1,"kill_pc_event_name")==0) { + strncpy(script_config.kill_pc_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.kill_pc_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_pc_event_name); + } + else if(strcmpi(w1,"kill_mob_event_name")==0) { + strncpy(script_config.kill_mob_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.kill_mob_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.kill_mob_event_name); + } + else if(strcmpi(w1,"login_event_name")==0) { + strncpy(script_config.login_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.login_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.login_event_name); + } + else if(strcmpi(w1,"logout_event_name")==0) { + strncpy(script_config.logout_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.logout_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.logout_event_name); + } + else if(strcmpi(w1,"loadmap_event_name")==0) { + strncpy(script_config.loadmap_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.loadmap_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.loadmap_event_name); + } + else if(strcmpi(w1,"baselvup_event_name")==0) { + strncpy(script_config.baselvup_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.baselvup_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.baselvup_event_name); + } + else if(strcmpi(w1,"joblvup_event_name")==0) { + strncpy(script_config.joblvup_event_name, w2, NAME_LENGTH-1); + if (strlen(script_config.joblvup_event_name) != strlen(w2)) + ShowWarning("script_config_read: Event label truncated (max length is 23 chars): %d\n", script_config.joblvup_event_name); + } + else if(strcmpi(w1,"import")==0){ + script_config_read_sub(w2); + } + } + fclose(fp); + + return 0; +} + +int script_config_read(char *cfgName) +{ //Script related variables should be initialized once! [Skotlex] + + memset (&script_config, 0, sizeof(script_config)); + script_config.verbose_mode = 0; + script_config.warn_func_no_comma = 1; + script_config.warn_cmd_no_comma = 1; + script_config.warn_func_mismatch_paramnum = 1; + script_config.warn_cmd_mismatch_paramnum = 1; + script_config.check_cmdcount = 65535; + script_config.check_gotocount = 2048; + + script_config.event_script_type = 0; + script_config.event_requires_trigger = 1; + + return script_config_read_sub(cfgName); +} + +static int do_final_userfunc_sub (DBKey key,void *data,va_list ap){ + struct script_code *code = (struct script_code *)data; + if(code){ + script_free_vars( &code->script_vars ); + aFree( code->script_buf ); + } + return 0; +} + +/*========================================== + * 終了 + *------------------------------------------ + */ +int do_final_script() +{ + if(mapreg_dirty>=0) + script_save_mapreg(); + + mapreg_db->destroy(mapreg_db,NULL); + mapregstr_db->destroy(mapregstr_db,NULL); + scriptlabel_db->destroy(scriptlabel_db,NULL); + userfunc_db->destroy(userfunc_db,do_final_userfunc_sub); + if(sleep_db) { + struct linkdb_node *n = (struct linkdb_node *)sleep_db; + while(n) { + struct script_state *st = (struct script_state *)n->data; + script_free_stack(st->stack); + free(st); + n = n->next; + } + linkdb_final(&sleep_db); + } + + if (str_data) + aFree(str_data); + if (str_buf) + aFree(str_buf); + + return 0; +} +/*========================================== + * 初期化 + *------------------------------------------ + */ +int do_init_script() +{ + mapreg_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int)); + mapregstr_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int)); + userfunc_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_BOTH,50); + scriptlabel_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_ALLOW_NULL_DATA,50); + + script_load_mapreg(); + + add_timer_func_list(script_autosave_mapreg,"script_autosave_mapreg"); + add_timer_interval(gettick()+MAPREG_AUTOSAVE_INTERVAL, + script_autosave_mapreg,0,0,MAPREG_AUTOSAVE_INTERVAL); + + return 0; +} + +int script_reload() +{ + if(mapreg_dirty>=0) + script_save_mapreg(); + + mapreg_db->clear(mapreg_db, NULL); + mapregstr_db->clear(mapregstr_db, NULL); + userfunc_db->clear(userfunc_db,do_final_userfunc_sub); + scriptlabel_db->clear(scriptlabel_db, NULL); + + if(sleep_db) { + struct linkdb_node *n = (struct linkdb_node *)sleep_db; + while(n) { + struct script_state *st = (struct script_state *)n->data; + script_free_stack(st->stack); + free(st); + n = n->next; + } + linkdb_final(&sleep_db); + } + + script_load_mapreg(); + return 0; +} diff --git a/src/map/skill.c b/src/map/skill.c index 638d22756..7bd2f737d 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -1,11100 +1,10980 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include - -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/malloc.h" -#include "../common/showmsg.h" -#include "../common/grfio.h" -#include "../common/ers.h" - -#include "skill.h" -#include "map.h" -#include "clif.h" -#include "pc.h" -#include "status.h" -#include "pet.h" -#include "mob.h" -#include "battle.h" -#include "party.h" -#include "itemdb.h" -#include "script.h" -#include "intif.h" -#include "log.h" -#include "chrif.h" -#include "guild.h" -#include "date.h" -#include "unit.h" - -#define SKILLUNITTIMER_INVERVAL 100 -//Guild Skills are shifted to these to make them stick into the skill array. -#define GD_SKILLRANGEMIN 900 -#define GD_SKILLRANGEMAX GD_SKILLRANGEMIN+MAX_GUILDSKILL - -int skill_names_id[MAX_SKILL_DB]; -const struct skill_name_db skill_names[] = { - { AC_CHARGEARROW, "AC_CHARGEARROW", "Arrow_Repel" } , - { AC_CONCENTRATION, "AC_CONCENTRATION", "Improve_Concentration" } , - { AC_DOUBLE, "AC_DOUBLE", "Double_Strafe" } , - { AC_MAKINGARROW, "AC_MAKINGARROW", "Arrow_Crafting" } , - { AC_OWL, "AC_OWL", "Owl's_Eye" } , - { AC_SHOWER, "AC_SHOWER", "Arrow_Shower" } , - { AC_VULTURE, "AC_VULTURE", "Vulture's_Eye" } , - { ALL_RESURRECTION, "ALL_RESURRECTION", "Resurrection" } , - { AL_ANGELUS, "AL_ANGELUS", "Angelus" } , - { AL_BLESSING, "AL_BLESSING", "Blessing" } , - { AL_CRUCIS, "AL_CRUCIS", "Signum_Crusis" } , - { AL_CURE, "AL_CURE", "Cure" } , - { AL_DECAGI, "AL_DECAGI", "Decrease_AGI" } , - { AL_DEMONBANE, "AL_DEMONBANE", "Demon_Bane" } , - { AL_DP, "AL_DP", "Divine_Protection" } , - { AL_HEAL, "AL_HEAL", "Heal" } , - { AL_HOLYLIGHT, "AL_HOLYLIGHT", "Holy_Light" } , - { AL_HOLYWATER, "AL_HOLYWATER", "Aqua_Benedicta" } , - { AL_INCAGI, "AL_INCAGI", "Increase_AGI" } , - { AL_PNEUMA, "AL_PNEUMA", "Pneuma" } , - { AL_RUWACH, "AL_RUWACH", "Ruwach" } , - { AL_TELEPORT, "AL_TELEPORT", "Teleport" } , - { AL_WARP, "AL_WARP", "Warp_Portal" } , - { AM_ACIDTERROR, "AM_ACIDTERROR", "Acid_Terror" } , - { AM_AXEMASTERY, "AM_AXEMASTERY", "Axe_Mastery" } , - { AM_BERSERKPITCHER, "AM_BERSERKPITCHER", "Aid_Berserk_Potion" } , - { AM_BIOETHICS, "AM_BIOETHICS", "Bioethics" } , - { AM_CALLHOMUN, "AM_CALLHOMUN", "Call_Homunculus" } , - { AM_CANNIBALIZE, "AM_CANNIBALIZE", "Summon_Flora" } , - { AM_CP_ARMOR, "AM_CP_ARMOR", "Synthetic_Armor" } , - { AM_CP_HELM, "AM_CP_HELM", "Biochemical_Helm" } , - { AM_CP_SHIELD, "AM_CP_SHIELD", "Synthetized_Shield" } , - { AM_CP_WEAPON, "AM_CP_WEAPON", "Alchemical_Weapon" } , - { AM_CULTIVATION, "AM_CULTIVATION", "Cultivation" } , - { AM_DEMONSTRATION, "AM_DEMONSTRATION", "Bomb" } , - { AM_LEARNINGPOTION, "AM_LEARNINGPOTION", "Potion_Research" } , - { AM_PHARMACY, "AM_PHARMACY", "Prepare_Potion" } , - { AM_POTIONPITCHER, "AM_POTIONPITCHER", "Aid_Potion" } , - { AM_REST, "AM_REST", "Vaporize" } , - { AM_RESURRECTHOMUN, "AM_RESURRECTHOMUN", "Homunculus_Resurrection" } , - { AM_SPHEREMINE, "AM_SPHEREMINE", "Summon_Marine_Sphere" } , - { AM_TWILIGHT1, "AM_TWILIGHT1", "Twilight_Pharmacy_1" } , - { AM_TWILIGHT2, "AM_TWILIGHT2", "Twilight_Pharmacy_2" } , - { AM_TWILIGHT3, "AM_TWILIGHT3", "Twilight_Pharmacy_3" } , - { ASC_BREAKER, "ASC_BREAKER", "Soul_Destroyer" } , - { ASC_CDP, "ASC_CDP", "Create_Deadly_Poison" } , - { ASC_EDP, "ASC_EDP", "Enchant_Deadly_Poison" } , - { ASC_KATAR, "ASC_KATAR", "Advanced_Katar_Mastery" } , - { ASC_METEORASSAULT, "ASC_METEORASSAULT", "Meteor_Assault" } , - { AS_CLOAKING, "AS_CLOAKING", "Cloaking" } , - { AS_ENCHANTPOISON, "AS_ENCHANTPOISON", "Enchant_Poison" } , - { AS_GRIMTOOTH, "AS_GRIMTOOTH", "Grimtooth" } , - { AS_KATAR, "AS_KATAR", "Katar_Mastery" } , - { AS_LEFT, "AS_LEFT", "Lefthand_Mastery" } , - { AS_POISONREACT, "AS_POISONREACT", "Poison_React" } , - { AS_RIGHT, "AS_RIGHT", "Righthand_Mastery" } , - { AS_SONICACCEL, "AS_SONICACCEL", "Sonic_Acceleration" } , - { AS_SONICBLOW, "AS_SONICBLOW", "Sonic_Blow" } , - { AS_SPLASHER, "AS_SPLASHER", "Venom_Splasher" } , - { AS_VENOMDUST, "AS_VENOMDUST", "Venom_Dust" } , - { AS_VENOMKNIFE, "AS_VENOMKNIFE", "Throw_Venom_Knife" } , - { BA_APPLEIDUN, "BA_APPLEIDUN", "Song_of_Lutie" } , - { BA_ASSASSINCROSS, "BA_ASSASSINCROSS", "Impressive_Riff" } , - { BA_DISSONANCE, "BA_DISSONANCE", "Unchained_Serenade" } , - { BA_FROSTJOKE, "BA_FROSTJOKE", "Unbarring_Octave" } , - { BA_MUSICALLESSON, "BA_MUSICALLESSON", "Music_Lessons" } , - { BA_MUSICALSTRIKE, "BA_MUSICALSTRIKE", "Melody_Strike" } , - { BA_PANGVOICE, "BA_PANGVOICE", "Pang_Voice" } , - { BA_POEMBRAGI, "BA_POEMBRAGI", "Magic_Strings" } , - { BA_WHISTLE, "BA_WHISTLE", "Perfect_Tablature" } , - { BD_ADAPTATION, "BD_ADAPTATION", "Amp" } , - { BD_DRUMBATTLEFIELD, "BD_DRUMBATTLEFIELD", "Battle_Theme" } , - { BD_ENCORE, "BD_ENCORE", "Encore" } , - { BD_ETERNALCHAOS, "BD_ETERNALCHAOS", "Down_Tempo" } , - { BD_INTOABYSS, "BD_INTOABYSS", "Power_Cord" } , - { BD_LULLABY, "BD_LULLABY", "Lullaby" } , - { BD_RICHMANKIM, "BD_RICHMANKIM", "Mental_Sensing" } , - { BD_RINGNIBELUNGEN, "BD_RINGNIBELUNGEN", "Harmonic_Lick" } , - { BD_ROKISWEIL, "BD_ROKISWEIL", "Classical_Pluck" } , - { BD_SIEGFRIED, "BD_SIEGFRIED", "Acoustic_Rhythm" } , - { BS_ADRENALINE, "BS_ADRENALINE", "Adrenaline_Rush" } , - { BS_ADRENALINE2, "BS_ADRENALINE2", "Advanced_Adrenaline_Rush" } , - { BS_AXE, "BS_AXE", "Smith_Axe" } , - { BS_DAGGER, "BS_DAGGER", "Smith_Dagger" } , - { BS_ENCHANTEDSTONE, "BS_ENCHANTEDSTONE", "Enchantedstone_Craft" } , - { BS_FINDINGORE, "BS_FINDINGORE", "Ore_Discovery" } , - { BS_GREED, "BS_GREED", "Greed" } , - { BS_HAMMERFALL, "BS_HAMMERFALL", "Hammer_Fall" } , - { BS_HILTBINDING, "BS_HILTBINDING", "Hilt_Binding" } , - { BS_IRON, "BS_IRON", "Iron_Tempering" } , - { BS_KNUCKLE, "BS_KNUCKLE", "Smith_Knucklebrace" } , - { BS_MACE, "BS_MACE", "Smith_Mace" } , - { BS_MAXIMIZE, "BS_MAXIMIZE", "Power_Maximize" } , - { BS_ORIDEOCON, "BS_ORIDEOCON", "Oridecon_Research" } , - { BS_OVERTHRUST, "BS_OVERTHRUST", "Power-Thrust" } , - { BS_REPAIRWEAPON, "BS_REPAIRWEAPON", "Weapon_Repair" } , - { BS_SKINTEMPER, "BS_SKINTEMPER", "Skin_Tempering" } , - { BS_SPEAR, "BS_SPEAR", "Smith_Spear" } , - { BS_STEEL, "BS_STEEL", "Steel_Tempering" } , - { BS_SWORD, "BS_SWORD", "Smith_Sword" } , - { BS_TWOHANDSWORD, "BS_TWOHANDSWORD", "Smith_Two-handed_Sword" } , - { BS_UNFAIRLYTRICK, "BS_UNFAIRLYTRICK", "Unfair_Trick" } , - { BS_WEAPONPERFECT, "BS_WEAPONPERFECT", "Weapon_Perfection" } , - { BS_WEAPONRESEARCH, "BS_WEAPONRESEARCH", "Weaponry_Research" } , - { CG_ARROWVULCAN, "CG_ARROWVULCAN", "Vulcan_Arrow" } , - { CG_HERMODE, "CG_HERMODE", "Wand_of_Hermode" } , - { CG_LONGINGFREEDOM, "CG_LONGINGFREEDOM", "Longing_for_Freedom" } , - { CG_MARIONETTE, "CG_MARIONETTE", "Marionette_Control" } , - { CG_MOONLIT, "CG_MOONLIT", "Sheltering_Bliss" } , - { CG_TAROTCARD, "CG_TAROTCARD", "Tarot_Card_of_Fate" } , - { CH_CHAINCRUSH, "CH_CHAINCRUSH", "Chain_Crush_Combo" } , - { CH_PALMSTRIKE, "CH_PALMSTRIKE", "Raging_Palm_Strike" } , - { CH_SOULCOLLECT, "CH_SOULCOLLECT", "Zen" } , - { CH_TIGERFIST, "CH_TIGERFIST", "Glacier_Fist" } , - { CR_ACIDDEMONSTRATION, "CR_ACIDDEMONSTRATION", "Acid_Demonstration" } , - { CR_ALCHEMY, "CR_ALCHEMY", "Alchemy" } , - { CR_AUTOGUARD, "CR_AUTOGUARD", "Guard" } , - { CR_CULTIVATION, "CR_CULTIVATION", "Plant_Cultivation" } , - { CR_DEFENDER, "CR_DEFENDER", "Defending_Aura" } , - { CR_DEVOTION, "CR_DEVOTION", "Sacrifice" } , - { CR_FULLPROTECTION, "CR_FULLPROTECTION", "Full_Protection" } , - { CR_GRANDCROSS, "CR_GRANDCROSS", "Grand_Cross" } , - { CR_HOLYCROSS, "CR_HOLYCROSS", "Holy_Cross" } , - { CR_PROVIDENCE, "CR_PROVIDENCE", "Resistant_Souls" } , - { CR_REFLECTSHIELD, "CR_REFLECTSHIELD", "Shield_Reflect" } , - { CR_SHIELDBOOMERANG, "CR_SHIELDBOOMERANG", "Shield_Boomerang" } , - { CR_SHIELDCHARGE, "CR_SHIELDCHARGE", "Smite" } , - { CR_SHRINK, "CR_SHRINK", "Shrink" } , - { CR_SLIMPITCHER, "CR_SLIMPITCHER", "Slim_Pitcher" } , - { CR_SPEARQUICKEN, "CR_SPEARQUICKEN", "Spear_Quicken" } , - { CR_SYNTHESISPOTION, "CR_SYNTHESISPOTION", "Potion_Synthesis" } , - { CR_TRUST, "CR_TRUST", "Faith" } , - { DC_DANCINGLESSON, "DC_DANCINGLESSON", "Dance_Lessons" } , - { DC_DONTFORGETME, "DC_DONTFORGETME", "Slow_Grace" } , - { DC_FORTUNEKISS, "DC_FORTUNEKISS", "Lady_Luck" } , - { DC_HUMMING, "DC_HUMMING", "Focus_Ballet" } , - { DC_SCREAM, "DC_SCREAM", "Dazzler" } , - { DC_SERVICEFORYOU, "DC_SERVICEFORYOU", "Gypsy's_Kiss" } , - { DC_THROWARROW, "DC_THROWARROW", "Slinging_Arrow" } , - { DC_UGLYDANCE, "DC_UGLYDANCE", "Hip_Shaker" } , - { DC_WINKCHARM, "DC_WINKCHARM", "Sexy_Wink" } , - { GD_APPROVAL, "GD_APPROVAL", "Official_Guild_Approval" } , - { GD_BATTLEORDER, "GD_BATTLEORDER", "Battle_Command" } , - { GD_DEVELOPMENT, "GD_DEVELOPMENT", "Permanent_Development" } , - { GD_EMERGENCYCALL, "GD_EMERGENCYCALL", "Urgent_Call" } , - { GD_EXTENSION, "GD_EXTENSION", "Guild_Extension" } , - { GD_GLORYGUILD, "GD_GLORYGUILD", "Glory_of_Guild" } , - { GD_GLORYWOUNDS, "GD_GLORYWOUNDS", "Glorious_Wounds" } , - { GD_GUARDUP, "GD_GUARDUP", "Strengthen_Guardian" } , - { GD_HAWKEYES, "GD_HAWKEYES", "Sharp_Gaze" } , - { GD_KAFRACONTRACT, "GD_KAFRACONTRACT", "Contract_with_Kafra" } , - { GD_LEADERSHIP, "GD_LEADERSHIP", "Great_Leadership" } , - { GD_REGENERATION, "GD_REGENERATION", "Regeneration" } , - { GD_RESTORE, "GD_RESTORE", "Restoration" } , - { GD_SOULCOLD, "GD_SOULCOLD", "Cold_Heart" } , - { GS_ADJUSTMENT, "GS_ADJUSTMENT", "Adjustment" } , - { GS_BULLSEYE, "GS_BULLSEYE", "Bulls_Eye" } , - { GS_CHAINACTION, "GS_CHAINACTION", "Chain_Action" } , - { GS_CRACKER, "GS_CRACKER", "Cracker" } , - { GS_DESPERADO, "GS_DESPERADO", "Desperado" } , - { GS_DISARM, "GS_DISARM", "Disarm" } , - { GS_DUST, "GS_DUST", "Dust" } , - { GS_FLING, "GS_FLING", "Fling" } , - { GS_FULLBUSTER, "GS_FULLBUSTER", "Full_Buster" } , - { GS_GATLINGFEVER, "GS_GATLINGFEVER", "Gatling_Fever" } , - { GS_GLITTERING, "GS_GLITTERING", "Flip_the_Coin" } , - { GS_GROUNDDRIFT, "GS_GROUNDDRIFT", "Ground_Drift" } , - { GS_INCREASING, "GS_INCREASING", "Increasing_Accuracy" } , - { GS_MADNESSCANCEL, "GS_MADNESSCANCEL", "Madness_Canceler" } , - { GS_MAGICALBULLET, "GS_MAGICALBULLET", "Magical_Bullet" } , - { GS_PIERCINGSHOT, "GS_PIERCINGSHOT", "Piercing_Shot" } , - { GS_RAPIDSHOWER, "GS_RAPIDSHOWER", "Rapid_Shower" } , - { GS_SINGLEACTION, "GS_SINGLEACTION", "Single_Action" } , - { GS_SNAKEEYE, "GS_SNAKEEYE", "Snake_Eye" } , - { GS_SPREADATTACK, "GS_SPREADATTACK", "Spread_Attack" } , - { GS_TRACKING, "GS_TRACKING", "Tracking" } , - { GS_TRIPLEACTION, "GS_TRIPLEACTION", "Triple_Action" } , - { HP_ASSUMPTIO, "HP_ASSUMPTIO", "Assumptio" } , - { HP_BASILICA, "HP_BASILICA", "Basilica" } , - { HP_MANARECHARGE, "HP_MANARECHARGE", "Mana_Recharge" } , - { HP_MEDITATIO, "HP_MEDITATIO", "Meditatio" } , - { HT_ANKLESNARE, "HT_ANKLESNARE", "Ankle_Snare" } , - { HT_BEASTBANE, "HT_BEASTBANE", "Beast_Bane" } , - { HT_BLASTMINE, "HT_BLASTMINE", "Blast_Mine" } , - { HT_BLITZBEAT, "HT_BLITZBEAT", "Blitz_Beat" } , - { HT_CLAYMORETRAP, "HT_CLAYMORETRAP", "Claymore_Trap" } , - { HT_DETECTING, "HT_DETECTING", "Detect" } , - { HT_FALCON, "HT_FALCON", "Falconry_Mastery" } , - { HT_FLASHER, "HT_FLASHER", "Flasher" } , - { HT_FREEZINGTRAP, "HT_FREEZINGTRAP", "Freezing_Trap" } , - { HT_LANDMINE, "HT_LANDMINE", "Land_Mine" } , - { HT_PHANTASMIC, "HT_PHANTASMIC", "Phantasmic_Arrow" } , - { HT_POWER, "HT_POWER", "Beast_Strafing" } , - { HT_REMOVETRAP, "HT_REMOVETRAP", "Remove_Trap" } , - { HT_SANDMAN, "HT_SANDMAN", "Sandman" } , - { HT_SHOCKWAVE, "HT_SHOCKWAVE", "Shockwave_Trap" } , - { HT_SKIDTRAP, "HT_SKIDTRAP", "Skid_Trap" } , - { HT_SPRINGTRAP, "HT_SPRINGTRAP", "Spring_Trap" } , - { HT_STEELCROW, "HT_STEELCROW", "Steel_Crow" } , - { HT_TALKIEBOX, "HT_TALKIEBOX", "Talkie_Box" } , - { HW_GANBANTEIN, "HW_GANBANTEIN", "Ganbantein" } , - { HW_GRAVITATION, "HW_GRAVITATION", "Gravitation_Field" } , - { HW_MAGICCRASHER, "HW_MAGICCRASHER", "Stave_Crasher" } , - { HW_MAGICPOWER, "HW_MAGICPOWER", "Mystical_Amplification" } , - { HW_NAPALMVULCAN, "HW_NAPALMVULCAN", "Napalm_Vulcan" } , - { HW_SOULDRAIN, "HW_SOULDRAIN", "Soul_Drain" } , - { ITM_TOMAHAWK, "ITM_TOMAHAWK", "Tomahawk_Throwing" } , - { KN_AUTOCOUNTER, "KN_AUTOCOUNTER", "Counter_Attack" } , - { KN_BOWLINGBASH, "KN_BOWLINGBASH", "Bowling_Bash" } , - { KN_BRANDISHSPEAR, "KN_BRANDISHSPEAR", "Brandish_Spear" } , - { KN_CAVALIERMASTERY, "KN_CAVALIERMASTERY", "Cavalier_Mastery" } , - { KN_CHARGEATK, "KN_CHARGEATK", "Charge_Attack" } , - { KN_ONEHAND, "KN_ONEHAND", "Onehand_Quicken" } , - { KN_PIERCE, "KN_PIERCE", "Pierce" } , - { KN_RIDING, "KN_RIDING", "Peco_Peco_Ride" } , - { KN_SPEARBOOMERANG, "KN_SPEARBOOMERANG", "Spear_Boomerang" } , - { KN_SPEARMASTERY, "KN_SPEARMASTERY", "Spear_Mastery" } , - { KN_SPEARSTAB, "KN_SPEARSTAB", "Spear_Stab" } , - { KN_TWOHANDQUICKEN, "KN_TWOHANDQUICKEN", "Twohand_Quicken" } , - { LK_AURABLADE, "LK_AURABLADE", "Aura_Blade" } , - { LK_BERSERK, "LK_BERSERK", "Frenzy" } , - { LK_CONCENTRATION, "LK_CONCENTRATION", "Spear_Dynamo" } , - { LK_HEADCRUSH, "LK_HEADCRUSH", "Traumatic_Blow" } , - { LK_JOINTBEAT, "LK_JOINTBEAT", "Vital_Strike" } , - { LK_PARRYING, "LK_PARRYING", "Parry" } , - { LK_SPIRALPIERCE, "LK_SPIRALPIERCE", "Clashing_Spiral" } , - { LK_TENSIONRELAX, "LK_TENSIONRELAX", "Relax" } , - { MC_CARTREVOLUTION, "MC_CARTREVOLUTION", "Cart_Revolution" } , - { MC_CHANGECART, "MC_CHANGECART", "Change_Cart" } , - { MC_DISCOUNT, "MC_DISCOUNT", "Discount" } , - { MC_IDENTIFY, "MC_IDENTIFY", "Item_Appraisal" } , - { MC_INCCARRY, "MC_INCCARRY", "Enlarge_Weight_Limit" } , - { MC_LOUD, "MC_LOUD", "Crazy_Uproar" } , - { MC_MAMMONITE, "MC_MAMMONITE", "Mammonite" } , - { MC_OVERCHARGE, "MC_OVERCHARGE", "Overcharge" } , - { MC_PUSHCART, "MC_PUSHCART", "Pushcart" } , - { MC_VENDING, "MC_VENDING", "Vending" } , - { MG_COLDBOLT, "MG_COLDBOLT", "Cold_Bolt" } , - { MG_ENERGYCOAT, "MG_ENERGYCOAT", "Energy_Coat" } , - { MG_FIREBALL, "MG_FIREBALL", "Fire_Ball" } , - { MG_FIREBOLT, "MG_FIREBOLT", "Fire_Bolt" } , - { MG_FIREWALL, "MG_FIREWALL", "Fire_Wall" } , - { MG_FROSTDIVER, "MG_FROSTDIVER", "Frost_Diver" } , - { MG_LIGHTNINGBOLT, "MG_LIGHTNINGBOLT", "Lightening_Bolt" } , - { MG_NAPALMBEAT, "MG_NAPALMBEAT", "Napalm_Beat" } , - { MG_SAFETYWALL, "MG_SAFETYWALL", "Safety_Wall" } , - { MG_SIGHT, "MG_SIGHT", "Sight" } , - { MG_SOULSTRIKE, "MG_SOULSTRIKE", "Soul_Strike" } , - { MG_SRECOVERY, "MG_SRECOVERY", "Increase_SP_Recovery" } , - { MG_STONECURSE, "MG_STONECURSE", "Stone_Curse" } , - { MG_THUNDERSTORM, "MG_THUNDERSTORM", "Thunderstorm" } , - { MO_ABSORBSPIRITS, "MO_ABSORBSPIRITS", "Spiritual_Sphere_Absorption" } , - { MO_BALKYOUNG, "MO_BALKYOUNG", "Ki_Explosion" } , - { MO_BLADESTOP, "MO_BLADESTOP", "Root" } , - { MO_BODYRELOCATION, "MO_BODYRELOCATION", "Snap" } , - { MO_CALLSPIRITS, "MO_CALLSPIRITS", "Summon_Spirit_Sphere" } , - { MO_CHAINCOMBO, "MO_CHAINCOMBO", "Raging_Quadruple_Blow" } , - { MO_COMBOFINISH, "MO_COMBOFINISH", "Raging_Thrust" } , - { MO_DODGE, "MO_DODGE", "Flee" } , - { MO_EXPLOSIONSPIRITS, "MO_EXPLOSIONSPIRITS", "Fury" } , - { MO_EXTREMITYFIST, "MO_EXTREMITYFIST", "Guillotine_Fist" } , - { MO_FINGEROFFENSIVE, "MO_FINGEROFFENSIVE", "Throw_Spirit_Sphere" } , - { MO_INVESTIGATE, "MO_INVESTIGATE", "Occult_Impaction" } , - { MO_IRONHAND, "MO_IRONHAND", "Iron_Fists" } , - { MO_KITRANSLATION, "MO_KITRANSLATION", "Ki_Translation" } , - { MO_SPIRITSRECOVERY, "MO_SPIRITSRECOVERY", "Spiritual_Cadence" } , - { MO_STEELBODY, "MO_STEELBODY", "Mental_Strength" } , - { MO_TRIPLEATTACK, "MO_TRIPLEATTACK", "Raging_Trifecta_Blow" } , - { NJ_BAKUENRYU, "NJ_BAKUENRYU", "NJ_BAKUENRYU" } , - { NJ_BUNSINJYUTSU, "NJ_BUNSINJYUTSU", "NJ_BUNSINJYUTSU" } , - { NJ_HUUJIN, "NJ_HUUJIN", "NJ_HUUJIN" } , - { NJ_HUUMA, "NJ_HUUMA", "NJ_HUUMA" } , - { NJ_HYOUSENSOU, "NJ_HYOUSENSOU", "NJ_HYOUSENSOU" } , - { NJ_HYOUSYOURAKU, "NJ_HYOUSYOURAKU", "NJ_HYOUSYOURAKU" } , - { NJ_ISSEN, "NJ_ISSEN", "NJ_ISSEN" } , - { NJ_KAENSIN, "NJ_KAENSIN", "NJ_KAENSIN" } , - { NJ_KAMAITACHI, "NJ_KAMAITACHI", "NJ_KAMAITACHI" } , - { NJ_KASUMIKIRI, "NJ_KASUMIKIRI", "NJ_KASUMIKIRI" } , - { NJ_KIRIKAGE, "NJ_KIRIKAGE", "NJ_KIRIKAGE" } , - { NJ_KOUENKA, "NJ_KOUENKA", "NJ_KOUENKA" } , - { NJ_KUNAI, "NJ_KUNAI", "NJ_KUNAI" } , - { NJ_NEN, "NJ_NEN", "NJ_NEN" } , - { NJ_NINPOU, "NJ_NINPOU", "NJ_NINPOU" } , - { NJ_RAIGEKISAI, "NJ_RAIGEKISAI", "NJ_RAIGEKISAI" } , - { NJ_SHADOWJUMP, "NJ_SHADOWJUMP", "NJ_SHADOWJUMP" } , - { NJ_SUITON, "NJ_SUITON", "NJ_SUITON" } , - { NJ_SYURIKEN, "NJ_SYURIKEN", "NJ_SYURIKEN" } , - { NJ_TATAMIGAESHI, "NJ_TATAMIGAESHI", "NJ_TATAMIGAESHI" } , - { NJ_TOBIDOUGU, "NJ_TOBIDOUGU", "NJ_TOBIDOUGU" } , - { NJ_UTSUSEMI, "NJ_UTSUSEMI", "NJ_UTSUSEMI" } , - { NJ_ZENYNAGE, "NJ_ZENYNAGE", "NJ_ZENYNAGE" } , - { NPC_AGIUP, "NPC_AGIUP", "NPC_AGIUP" } , - { NPC_ATTRICHANGE, "NPC_ATTRICHANGE", "NPC_ATTRICHANGE" } , - { NPC_BARRIER, "NPC_BARRIER", "NPC_BARRIER" } , - { NPC_BLINDATTACK, "NPC_BLINDATTACK", "NPC_BLINDATTACK" } , - { NPC_BLOODDRAIN, "NPC_BLOODDRAIN", "NPC_BLOODDRAIN" } , - { NPC_BREAKARMOR, "NPC_BREAKARMOR", "NPC_BREAKARMOR" } , - { NPC_BREAKHELM, "NPC_BREAKHELM", "NPC_BREAKHELM" } , - { NPC_BREAKSHIELD, "NPC_BREAKSHIELD", "NPC_BREAKSHIELD" } , - { NPC_BREAKWEAPON, "NPC_BREAKWEAPON", "NPC_BREAKWEAPON" } , - { NPC_CALLSLAVE, "NPC_CALLSLAVE", "NPC_CALLSLAVE" } , - { NPC_CHANGEDARKNESS, "NPC_CHANGEDARKNESS", "NPC_CHANGEDARKNESS" } , - { NPC_CHANGEFIRE, "NPC_CHANGEFIRE", "NPC_CHANGEFIRE" } , - { NPC_CHANGEGROUND, "NPC_CHANGEGROUND", "NPC_CHANGEGROUND" } , - { NPC_CHANGEHOLY, "NPC_CHANGEHOLY", "NPC_CHANGEHOLY" } , - { NPC_CHANGEPOISON, "NPC_CHANGEPOISON", "NPC_CHANGEPOISON" } , - { NPC_CHANGETELEKINESIS, "NPC_CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS" } , - { NPC_CHANGEUNDEAD, "NPC_CHANGEUNDEAD", "NPC_CHANGEUNDEAD" } , - { NPC_CHANGEWATER, "NPC_CHANGEWATER", "NPC_CHANGEWATER" } , - { NPC_CHANGEWIND, "NPC_CHANGEWIND", "NPC_CHANGEWIND" } , - { NPC_COMBOATTACK, "NPC_COMBOATTACK", "NPC_COMBOATTACK" } , - { NPC_CRITICALSLASH, "NPC_CRITICALSLASH", "NPC_CRITICALSLASH" } , - { NPC_CURSEATTACK, "NPC_CURSEATTACK", "NPC_CURSEATTACK" } , - { NPC_DARKBLESSING, "NPC_DARKBLESSING", "NPC_DARKBLESSING" } , - { NPC_DARKBREATH, "NPC_DARKBREATH", "NPC_DARKBREATH" } , - { NPC_DARKCROSS, "NPC_DARKCROSS", "NPC_DARKCROSS" } , - { NPC_DARKNESSATTACK, "NPC_DARKNESSATTACK", "NPC_DARKNESSATTACK" } , - { NPC_DARKSTRIKE, "NPC_DARKSTRIKE", "NPC_DARKSTRIKE" } , - { NPC_DARKTHUNDER, "NPC_DARKTHUNDER", "NPC_DARKTHUNDER" } , - { NPC_DEFENDER, "NPC_DEFENDER", "NPC_DEFENDER" } , - { NPC_EMOTION, "NPC_EMOTION", "NPC_EMOTION" } , - { NPC_EMOTION_ON, "NPC_EMOTION_ON", "NPC_EMOTION_ON" } , - { NPC_ENERGYDRAIN, "NPC_ENERGYDRAIN", "NPC_ENERGYDRAIN" } , - { NPC_FIREATTACK, "NPC_FIREATTACK", "NPC_FIREATTACK" } , - { NPC_GRANDDARKNESS, "NPC_GRANDDARKNESS", "NPC_GRANDDARKNESS" } , - { NPC_GROUNDATTACK, "NPC_GROUNDATTACK", "NPC_GROUNDATTACK" } , - { NPC_GUIDEDATTACK, "NPC_GUIDEDATTACK", "NPC_GUIDEDATTACK" } , - { NPC_HALLUCINATION, "NPC_HALLUCINATION", "NPC_HALLUCINATION" } , - { NPC_HOLYATTACK, "NPC_HOLYATTACK", "NPC_HOLYATTACK" } , - { NPC_INVISIBLE, "NPC_INVISIBLE", "NPC_INVISIBLE" } , - { NPC_KEEPING, "NPC_KEEPING", "NPC_KEEPING" } , - { NPC_LICK, "NPC_LICK", "NPC_LICK" } , - { NPC_MAGICALATTACK, "NPC_MAGICALATTACK", "NPC_MAGICALATTACK" } , - { NPC_MENTALBREAKER, "NPC_MENTALBREAKER", "NPC_MENTALBREAKER" } , - { NPC_METAMORPHOSIS, "NPC_METAMORPHOSIS", "NPC_METAMORPHOSIS" } , - { NPC_PETRIFYATTACK, "NPC_PETRIFYATTACK", "NPC_PETRIFYATTACK" } , - { NPC_PIERCINGATT, "NPC_PIERCINGATT", "NPC_PIERCINGATT" } , - { NPC_POISON, "NPC_POISON", "NPC_POISON" } , - { NPC_POISONATTACK, "NPC_POISONATTACK", "NPC_POISONATTACK" } , - { NPC_POWERUP, "NPC_POWERUP", "NPC_POWERUP" } , - { NPC_PROVOCATION, "NPC_PROVOCATION", "NPC_PROVOCATION" } , - { NPC_RANDOMATTACK, "NPC_RANDOMATTACK", "NPC_RANDOMATTACK" } , - { NPC_RANDOMMOVE, "NPC_RANDOMMOVE", "NPC_RANDOMMOVE" } , - { NPC_RANGEATTACK, "NPC_RANGEATTACK", "NPC_RANGEATTACK" } , - { NPC_REBIRTH, "NPC_REBIRTH", "NPC_REBIRTH" } , - { NPC_REVENGE, "NPC_REVENGE", "NPC_REVENGE" } , - { NPC_RUN, "NPC_RUN", "NPC_RUN" }, - { NPC_SELFDESTRUCTION, "NPC_SELFDESTRUCTION", "Kabooooom!" } , - { NPC_SIEGEMODE, "NPC_SIEGEMODE", "NPC_SIEGEMODE" } , - { NPC_SILENCEATTACK, "NPC_SILENCEATTACK", "NPC_SILENCEATTACK" } , - { NPC_SLEEPATTACK, "NPC_SLEEPATTACK", "NPC_SLEEPATTACK" } , - { NPC_SMOKING, "NPC_SMOKING", "NPC_SMOKING" } , - { NPC_SPEEDUP, "NPC_SPEEDUP", "NPC_SPEEDUP" } , - { NPC_SPLASHATTACK, "NPC_SPLASHATTACK", "NPC_SPLASHATTACK" } , - { NPC_STOP, "NPC_STOP", "NPC_STOP" } , - { NPC_STUNATTACK, "NPC_STUNATTACK", "NPC_STUNATTACK" } , - { NPC_SUICIDE, "NPC_SUICIDE", "NPC_SUICIDE" } , - { NPC_SUMMONMONSTER, "NPC_SUMMONMONSTER", "NPC_SUMMONMONSTER" } , - { NPC_SUMMONSLAVE, "NPC_SUMMONSLAVE", "NPC_SUMMONSLAVE" } , - { NPC_TELEKINESISATTACK, "NPC_TELEKINESISATTACK", "NPC_TELEKINESISATTACK" } , - { NPC_TRANSFORMATION, "NPC_TRANSFORMATION", "NPC_TRANSFORMATION" } , - { NPC_UNDEADATTACK, "NPC_UNDEADATTACK", "NPC_UNDEADATTACK" } , - { NPC_WATERATTACK, "NPC_WATERATTACK", "NPC_WATERATTACK" } , - { NPC_WINDATTACK, "NPC_WINDATTACK", "NPC_WINDATTACK" } , - { NV_BASIC, "NV_BASIC", "Basic_Skill" } , - { NV_FIRSTAID, "NV_FIRSTAID", "First Aid" } , - { NV_TRICKDEAD, "NV_TRICKDEAD", "Play_Dead" } , - { PA_GOSPEL, "PA_GOSPEL", "Battle_Chant" } , - { PA_PRESSURE, "PA_PRESSURE", "Gloria_Domini" } , - { PA_SACRIFICE, "PA_SACRIFICE", "Martyr's_Reckoning" } , - { PA_SHIELDCHAIN, "PA_SHIELDCHAIN", "Shield_Chain" } , - { PF_DOUBLECASTING, "PF_DOUBLECASTING", "Double_Casting" } , - { PF_FOGWALL, "PF_FOGWALL", "Blinding_Mist" } , - { PF_HPCONVERSION, "PF_HPCONVERSION", "Indulge" } , - { PF_MEMORIZE, "PF_MEMORIZE", "Foresight" } , - { PF_MINDBREAKER, "PF_MINDBREAKER", "Mind_Breaker" } , - { PF_SOULBURN, "PF_SOULBURN", "Soul_Siphon" } , - { PF_SOULCHANGE, "PF_SOULCHANGE", "Soul_Exhale" } , - { PF_SPIDERWEB, "PF_SPIDERWEB", "Fiber_Lock" } , - { PR_ASPERSIO, "PR_ASPERSIO", "Aspersio" } , - { PR_BENEDICTIO, "PR_BENEDICTIO", "B.S_Sacramenti" } , - { PR_GLORIA, "PR_GLORIA", "Gloria" } , - { PR_IMPOSITIO, "PR_IMPOSITIO", "Impositio_Manus" } , - { PR_KYRIE, "PR_KYRIE", "Kyrie_Eleison" } , - { PR_LEXAETERNA, "PR_LEXAETERNA", "Lex_Aeterna" } , - { PR_LEXDIVINA, "PR_LEXDIVINA", "Lex_Divina" } , - { PR_MACEMASTERY, "PR_MACEMASTERY", "Mace_Mastery" } , - { PR_MAGNIFICAT, "PR_MAGNIFICAT", "Magnificat" } , - { PR_MAGNUS, "PR_MAGNUS", "Magnus_Exorcismus" } , - { PR_REDEMPTIO, "PR_REDEMPTIO", "Redemptio" } , - { PR_SANCTUARY, "PR_SANCTUARY", "Sanctuary" } , - { PR_SLOWPOISON, "PR_SLOWPOISON", "Slow_Poison" } , - { PR_STRECOVERY, "PR_STRECOVERY", "Status_Recovery" } , - { PR_SUFFRAGIUM, "PR_SUFFRAGIUM", "Suffragium" } , - { PR_TURNUNDEAD, "PR_TURNUNDEAD", "Turn_Undead" } , - { RG_BACKSTAP, "RG_BACKSTAP", "Back_Stab" } , - { RG_CLEANER, "RG_CLEANER", "Remover" } , - { RG_CLOSECONFINE, "RG_CLOSECONFINE", "Close_Confine"} , - { RG_COMPULSION, "RG_COMPULSION", "Haggle" } , - { RG_FLAGGRAFFITI, "RG_FLAGGRAFFITI", "Piece" } , - { RG_GANGSTER, "RG_GANGSTER", "Slyness" } , - { RG_GRAFFITI, "RG_GRAFFITI", "Scribble" } , - { RG_INTIMIDATE, "RG_INTIMIDATE", "Snatch" } , - { RG_PLAGIARISM, "RG_PLAGIARISM", "Intimidate" } , - { RG_RAID, "RG_RAID", "Sightless_Mind" } , - { RG_SNATCHER, "RG_SNATCHER", "Gank" } , - { RG_STEALCOIN, "RG_STEALCOIN", "Mug" } , - { RG_STRIPARMOR, "RG_STRIPARMOR", "Divest_Armor" } , - { RG_STRIPHELM, "RG_STRIPHELM", "Divest_Helm" } , - { RG_STRIPSHIELD, "RG_STRIPSHIELD", "Divest_Shield" } , - { RG_STRIPWEAPON, "RG_STRIPWEAPON", "Divest_Weapon" } , - { RG_TUNNELDRIVE, "RG_TUNNELDRIVE", "Stalk" } , - { SA_ABRACADABRA, "SA_ABRACADABRA", "Hocus-pocus" } , - { SA_ADVANCEDBOOK, "SA_ADVANCEDBOOK", "Advanced_Book" } , - { SA_AUTOSPELL, "SA_AUTOSPELL", "Hindsight" } , - { SA_CASTCANCEL, "SA_CASTCANCEL", "Cast_Cancel" } , - { SA_CLASSCHANGE, "SA_CLASSCHANGE", "Class_Change" } , - { SA_COMA, "SA_COMA", "Coma" } , - { SA_CREATECON, "SA_CREATECON", "Create_Elemental_Converter" } , - { SA_DEATH, "SA_DEATH", "Grim_Reaper" } , - { SA_DELUGE, "SA_DELUGE", "Deluge" } , - { SA_DISPELL, "SA_DISPELL", "Dispell" } , - { SA_DRAGONOLOGY, "SA_DRAGONOLOGY", "Dragonology" } , - { SA_ELEMENTFIRE, "SA_ELEMENTFIRE", "Elemental_Change_Fire" } , - { SA_ELEMENTGROUND, "SA_ELEMENTGROUND", "Elemental_Change_Earth" } , - { SA_ELEMENTWATER, "SA_ELEMENTWATER", "Elemental_Change_Water" } , - { SA_ELEMENTWIND, "SA_ELEMENTWIND", "Elemental_Change_Wind" } , - { SA_FLAMELAUNCHER, "SA_FLAMELAUNCHER", "Endow_Blaze" } , - { SA_FORTUNE, "SA_FORTUNE", "Gold_Digger" } , - { SA_FREECAST, "SA_FREECAST", "Free_Cast" } , - { SA_FROSTWEAPON, "SA_FROSTWEAPON", "Endow_Tsunami" } , - { SA_FULLRECOVERY, "SA_FULLRECOVERY", "Rejuvenation" } , - { SA_GRAVITY, "SA_GRAVITY", "Gravity" } , - { SA_INSTANTDEATH, "SA_INSTANTDEATH", "Suicide" } , - { SA_LANDPROTECTOR, "SA_LANDPROTECTOR", "Magnetic_Earth" } , - { SA_LEVELUP, "SA_LEVELUP", "Leveling" } , - { SA_LIGHTNINGLOADER, "SA_LIGHTNINGLOADER", "Endow_Tornado" } , - { SA_MAGICROD, "SA_MAGICROD", "Magic_Rod" } , - { SA_MONOCELL, "SA_MONOCELL", "Mono_Cell" } , - { SA_QUESTION, "SA_QUESTION", "Questioning" } , - { SA_REVERSEORCISH, "SA_REVERSEORCISH", "Grampus_Morph" } , - { SA_SEISMICWEAPON, "SA_SEISMICWEAPON", "Endow_Quake" } , - { SA_SPELLBREAKER, "SA_SPELLBREAKER", "Spell_Breaker" } , - { SA_SUMMONMONSTER, "SA_SUMMONMONSTER", "Monster_Chant" } , - { SA_TAMINGMONSTER, "SA_TAMINGMONSTER", "Beastly_Hypnosis" } , - { SA_VIOLENTGALE, "SA_VIOLENTGALE", "Whirlwind" } , - { SA_VOLCANO, "SA_VOLCANO", "Volcano" } , - { SG_DEVIL, "SG_DEVIL", "Devil_of_the_Sun,_Moon_and_Stars" } , - { SG_FEEL, "SG_FEEL", "Feeling_of_the_Sun,_Moon_and_Star" } , - { SG_FRIEND, "SG_FRIEND", "Companion_of_the_Sun_and_Moon" } , - { SG_FUSION, "SG_FUSION", "Union_of_the_Sun,_Moon_and_Stars" } , - { SG_HATE, "SG_HATE", "Hatred_of_the_Sun,_Moon_and_Stars" } , - { SG_KNOWLEDGE, "SG_KNOWLEDGE", "Knowledge_of_the_Sun,_Moon_and_Stars" } , - { SG_MOON_ANGER, "SG_MOON_ANGER", "Fury_of_the_Moon" } , - { SG_MOON_BLESS, "SG_MOON_BLESS", "Bless_of_the_Moon" } , - { SG_MOON_COMFORT, "SG_MOON_COMFORT", "Comfort_of_the_Moon" } , - { SG_MOON_WARM, "SG_MOON_WARM", "Warmth_of_the_Moon" } , - { SG_STAR_ANGER, "SG_STAR_ANGER", "Fury_of_the_Stars" } , - { SG_STAR_BLESS, "SG_STAR_BLESS", "Bless_of_the_Stars" } , - { SG_STAR_COMFORT, "SG_STAR_COMFORT", "Comfort_of_the_Stars" } , - { SG_STAR_WARM, "SG_STAR_WARM", "Warmth_of_the_Stars" } , - { SG_SUN_ANGER, "SG_SUN_ANGER", "Fury_of_the_Sun" } , - { SG_SUN_BLESS, "SG_SUN_BLESS", "Bless_of_the_Sun" } , - { SG_SUN_COMFORT, "SG_SUN_COMFORT", "Comfort_of_the_Sun" } , - { SG_SUN_WARM, "SG_SUN_WARM", "Warmth_of_the_Sun" } , - { SL_ALCHEMIST, "SL_ALCHEMIST", "Spirit_of_Alchemist" } , - { SL_ASSASIN, "SL_ASSASIN", "Spirit_of_Assassin" } , - { SL_BARDDANCER, "SL_BARDDANCER", "Spirit_of_Bard_and_Dancer" } , - { SL_BLACKSMITH, "SL_BLACKSMITH", "Spirit_of_Blacksmith" } , - { SL_CRUSADER, "SL_CRUSADER", "Spirit_of_Crusader" } , - { SL_HIGH, "SL_HIGH", "Spirit_of_Advanced_1st_Class" } , - { SL_HUNTER, "SL_HUNTER", "Spirit_of_Hunter" } , - { SL_KAAHI, "SL_KAAHI", "Kaahi" } , - { SL_KAINA, "SL_KAINA", "Kaina" } , - { SL_KAITE, "SL_KAITE", "Kaite" } , - { SL_KAIZEL, "SL_KAIZEL", "Kaizel" } , - { SL_KAUPE, "SL_KAUPE", "Kaupe" } , - { SL_KNIGHT, "SL_KNIGHT", "Spirit_of_Knight" } , - { SL_MONK, "SL_MONK", "Spirit_of_Monk" } , - { SL_PRIEST, "SL_PRIEST", "Spirit_of_Priest" } , - { SL_ROGUE, "SL_ROGUE", "Spirit_of_Rogue" } , - { SL_SAGE, "SL_SAGE", "Spirit_of_Sage" } , - { SL_SKA, "SL_SKA", "Eska" } , - { SL_SKE, "SL_SKE", "Eske" } , - { SL_SMA, "SL_SMA", "Esma" } , - { SL_SOULLINKER, "SL_SOULLINKER", "Spirit_of_Soul_Linker" } , - { SL_STAR, "SL_STAR", "Spirit_of_Stars" } , - { SL_STIN, "SL_STIN", "Estin" } , - { SL_STUN, "SL_STUN", "Estun" } , - { SL_SUPERNOVICE, "SL_SUPERNOVICE", "Spirit_of_Super_Novice" } , - { SL_SWOO, "SL_SWOO", "Eswoo" } , - { SL_WIZARD, "SL_WIZARD", "Spirit_of_Wizard" } , - { SM_AUTOBERSERK, "SM_AUTOBERSERK", "Berserk" } , - { SM_BASH, "SM_BASH", "Bash" } , - { SM_ENDURE, "SM_ENDURE", "Endure" } , - { SM_FATALBLOW, "SM_FATALBLOW", "Fatal_Blow" } , - { SM_MAGNUM, "SM_MAGNUM", "Magnum_Break" } , - { SM_MOVINGRECOVERY, "SM_MOVINGRECOVERY", "HP_Recovery_While_Moving" } , - { SM_PROVOKE, "SM_PROVOKE", "Provoke" } , - { SM_RECOVERY, "SM_RECOVERY", "Increase_HP_Recovery" } , - { SM_SWORD, "SM_SWORD", "Sword_Mastery" } , - { SM_TWOHAND, "SM_TWOHAND", "Two-Handed_Sword_Mastery" } , - { SN_FALCONASSAULT, "SN_FALCONASSAULT", "Falcon_Assault" } , - { SN_SHARPSHOOTING, "SN_SHARPSHOOTING", "Focused_Arrow_Strike" } , - { SN_SIGHT, "SN_SIGHT", "Falcon_Eyes" } , - { SN_WINDWALK, "SN_WINDWALK", "Wind_Walker" } , - { ST_CHASEWALK, "ST_CHASEWALK", "Stealth" } , - { ST_FULLSTRIP, "ST_FULLSTRIP", "Full_Divestment" } , - { ST_PRESERVE, "ST_PRESERVE", "Preserve" } , - { ST_REJECTSWORD, "ST_REJECTSWORD", "Counter_Instinct" } , - { TF_BACKSLIDING, "TF_BACKSLIDING", "Back_Slide" } , - { TF_DETOXIFY, "TF_DETOXIFY", "Detoxify" } , - { TF_DOUBLE, "TF_DOUBLE", "Double_Attack" } , - { TF_HIDING, "TF_HIDING", "Hiding" } , - { TF_MISS, "TF_MISS", "Improve_Dodge" } , - { TF_PICKSTONE, "TF_PICKSTONE", "Find_Stone" } , - { TF_POISON, "TF_POISON", "Envenom" } , - { TF_SPRINKLESAND, "TF_SPRINKLESAND", "Sand_Attack" } , - { TF_STEAL, "TF_STEAL", "Steal" } , - { TF_THROWSTONE, "TF_THROWSTONE", "Stone_Fling" } , - { TK_COUNTER, "TK_COUNTER", "Spin_Kick" } , - { TK_DODGE, "TK_DODGE", "Sprint" } , - { TK_DOWNKICK, "TK_DOWNKICK", "Heel_Drop" } , - { TK_HIGHJUMP, "TK_HIGHJUMP", "Taekwon_Jump" } , - { TK_HPTIME, "TK_HPTIME", "Peaceful_Break" } , - { TK_JUMPKICK, "TK_JUMPKICK", "Flying_Kick" } , - { TK_MISSION, "TK_MISSION", "Mission" } , - { TK_POWER, "TK_POWER", "Kihop" } , - { TK_READYCOUNTER, "TK_READYCOUNTER", "Spin_Kick_Stance" } , - { TK_READYDOWN, "TK_READYDOWN", "Heel_Drop_Stance" } , - { TK_READYSTORM, "TK_READYSTORM", "Tornado_Stance" } , - { TK_READYTURN, "TK_READYTURN", "Roundhouse_Stance" } , - { TK_RUN, "TK_RUN", "Sprint" } , - { TK_SEVENWIND, "TK_SEVENWIND", "Mild_Wind" } , - { TK_SPTIME, "TK_SPTIME", "Happy_Break" } , - { TK_STORMKICK, "TK_STORMKICK", "Storm_Kick" } , - { TK_TURNKICK, "TK_TURNKICK", "Turn_Kick" } , - { WE_BABY, "WE_BABY", "Mom,_Dad,_I_love_you!" } , - { WE_CALLBABY, "WE_CALLBABY", "Come_to_me,_honey~" } , - { WE_CALLPARENT, "WE_CALLPARENT", "Mom,_Dad,_I_miss_you!" } , - { WE_CALLPARTNER, "WE_CALLPARTNER", "Romantic_Rendezvous" } , - { WE_FEMALE, "WE_FEMALE", "Loving_Touch" } , - { WE_MALE, "WE_MALE", "Undying_Love" } , - { WS_CARTBOOST, "WS_CARTBOOST", "Cart_Boost" } , - { WS_CARTTERMINATION, "WS_CARTTERMINATION", "Cart_Termination" } , - { WS_CREATECOIN, "WS_CREATECOIN", "Coin_Craft" } , - { WS_CREATENUGGET, "WS_CREATENUGGET", "Nugget_Craft" } , - { WS_MELTDOWN, "WS_MELTDOWN", "Shattering_Strike" } , - { WS_OVERTHRUSTMAX, "WS_OVERTHRUSTMAX", "Max_Power-Thust" } , - { WS_SYSTEMCREATE, "WS_SYSTEMCREATE", "Auto_Attacking_Machine_Craft" } , - { WS_WEAPONREFINE, "WS_WEAPONREFINE", "Weapon_Refine" } , - { WZ_EARTHSPIKE, "WZ_EARTHSPIKE", "Earth_Spike" } , - { WZ_ESTIMATION, "WZ_ESTIMATION", "Sense" } , - { WZ_FIREPILLAR, "WZ_FIREPILLAR", "Fire_Pillar" } , - { WZ_FROSTNOVA, "WZ_FROSTNOVA", "Frost_Nova" } , - { WZ_HEAVENDRIVE, "WZ_HEAVENDRIVE", "Heaven's_Drive" } , - { WZ_ICEWALL, "WZ_ICEWALL", "Ice_Wall" } , - { WZ_JUPITEL, "WZ_JUPITEL", "Jupitel_Thunder" } , - { WZ_METEOR, "WZ_METEOR", "Meteor_Storm" } , - { WZ_QUAGMIRE, "WZ_QUAGMIRE", "Quagmire" } , - { WZ_SIGHTBLASTER, "WZ_SIGHTBLASTER", "Sight_Blaster" } , - { WZ_SIGHTRASHER, "WZ_SIGHTRASHER", "Sightrasher" } , - { WZ_STORMGUST, "WZ_STORMGUST", "Storm_Gust" } , - { WZ_VERMILION, "WZ_VERMILION", "Lord_of_Vermilion" } , - { WZ_WATERBALL, "WZ_WATERBALL", "Water_Ball" } , - //[blackhole89] - { HLIF_HEAL, "HLIF_HEAL", "Healing_Touch" }, - { HLIF_AVOID, "HLIF_AVOID", "Avoid" }, - { HLIF_BRAIN, "HLIF_BRAIN", "Brain_Surgery" }, - { HLIF_CHANGE, "HLIF_CHANGE", "Change" }, - { HAMI_CASTLE, "HAMI_CASTLE", "Castling" }, - { HAMI_DEFENCE, "HAMI_DEFENCE", "Defense" }, - { HAMI_SKIN, "HAMI_SKIN", "Adamantium_Skin" }, - { HAMI_BLOODLUST, "HAMI_BLOODLUST", "Bloodlust" }, - { HFLI_MOON, "HFLI_MOON", "Moonlight" }, - { HFLI_FLEET, "HFLI_FLEET", "Fleeting_Move" }, - { HFLI_SPEED, "HFLI_SPEED", "Speed" }, - { HFLI_SBR44, "HFLI_SBR44", "S.B.R.44" }, - { HVAN_CAPRICE, "HVAN_CAPRICE", "Caprice" }, - { HVAN_CHAOTIC, "HVAN_CHAOTIC", "Benediction_of_Chaos" }, - { HVAN_INSTRUCT, "HVAN_INSTRUCT", "Instruct" }, - { HVAN_EXPLOSION, "HVAN_EXPLOSION", "Bio_Explosion" }, - { 0, "UNKNOWN_SKILL", "Unknown_Skill" } -}; - -static const int dirx[8]={0,-1,-1,-1,0,1,1,1}; -static const int diry[8]={1,1,0,-1,-1,-1,0,1}; - -static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex] -static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex] - -/* スキルデ?タベ?ス */ -struct skill_db skill_db[MAX_SKILL_DB]; - -/* アイテム??ャデ?タベ?ス */ -struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; - -/* 矢??ャスキルデ?タベ?ス */ -struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; - -/* アブラカダブラ?動スキルデ?タベ?ス */ -struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; - -// macros to check for out of bounds errors [celest] -// i: Skill ID, l: Skill Level, var: Value to return after checking -// for values that don't require level just put a one (putting 0 will trigger return 0; instead -// for values that might need to use a different function just skill_chk would suffice. -#define skill_chk(i, l) \ - if (i >= GD_SKILLRANGEMIN && i <= GD_SKILLRANGEMAX) { return 0; } \ - if (i >= GD_SKILLBASE) {i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;} \ - if (i < 1 || i >= MAX_SKILL_DB) {return 0;} \ - if (l <= 0 || l > MAX_SKILL_LEVEL) {return 0;} -#define skill_get(var, i, l) \ - { skill_chk(i, l); return var; } - -// Skill DB -int skill_get_hit( int id ){ skill_get (skill_db[id].hit, id, 1); } -int skill_get_inf( int id ){ skill_get (skill_db[id].inf, id, 1); } -int skill_get_pl( int id ){ skill_get (skill_db[id].pl, id, 1); } -int skill_get_nk( int id ){ skill_get (skill_db[id].nk, id, 1); } -int skill_get_max( int id ){ skill_get (skill_db[id].max, id, 1); } -int skill_get_range( int id , int lv ){ skill_get(skill_db[id].range[lv-1], id, lv); } -int skill_get_splash( int id , int lv ){ skill_chk (id, lv); return (skill_db[id].splash[lv-1]>=0?skill_db[id].splash[lv-1]:AREA_SIZE); } -int skill_get_hp( int id ,int lv ){ skill_get (skill_db[id].hp[lv-1], id, lv); } -int skill_get_sp( int id ,int lv ){ skill_get (skill_db[id].sp[lv-1], id, lv); } -int skill_get_hp_rate(int id, int lv ){ skill_get (skill_db[id].hp_rate[lv-1], id, lv); } -int skill_get_sp_rate(int id, int lv ){ skill_get (skill_db[id].sp_rate[lv-1], id, lv); } -int skill_get_state(int id) { skill_get (skill_db[id].state, id, 1); } -int skill_get_spiritball(int id, int lv) { skill_get (skill_db[id].spiritball[lv-1], id, lv); } -int skill_get_itemid(int id, int idx) { skill_get (skill_db[id].itemid[idx], id, 1); } -int skill_get_itemqty(int id, int idx) { skill_get (skill_db[id].amount[idx], id, 1); } -int skill_get_zeny( int id ,int lv ){ skill_get (skill_db[id].zeny[lv-1], id, lv); } -int skill_get_num( int id ,int lv ){ skill_get (skill_db[id].num[lv-1], id, lv); } -int skill_get_cast( int id ,int lv ){ skill_get (skill_db[id].cast[lv-1], id, lv); } -int skill_get_delay( int id ,int lv ){ skill_get (skill_db[id].delay[lv-1], id, lv); } -int skill_get_walkdelay( int id ,int lv ){ skill_get (skill_db[id].walkdelay[lv-1], id, lv); } -int skill_get_time( int id ,int lv ){ skill_get (skill_db[id].upkeep_time[lv-1], id, lv); } -int skill_get_time2( int id ,int lv ){ skill_get (skill_db[id].upkeep_time2[lv-1], id, lv); } -int skill_get_castdef( int id ){ skill_get (skill_db[id].cast_def_rate, id, 1); } -int skill_get_weapontype( int id ){ skill_get (skill_db[id].weapon, id, 1); } -int skill_get_ammotype( int id ){ skill_get (skill_db[id].ammo, id, 1); } -int skill_get_ammo_qty( int id, int lv ){ skill_get (skill_db[id].ammo_qty[lv-1], id, lv); } -int skill_get_inf2( int id ){ skill_get (skill_db[id].inf2, id, 1); } -int skill_get_castcancel( int id ){ skill_get (skill_db[id].castcancel, id, 1); } -int skill_get_maxcount( int id ){ skill_get (skill_db[id].maxcount, id, 1); } -int skill_get_blewcount( int id ,int lv ){ skill_get (skill_db[id].blewcount[lv-1], id, lv); } -int skill_get_mhp( int id ,int lv ){ skill_get (skill_db[id].mhp[lv-1], id, lv); } -int skill_get_castnodex( int id ,int lv ){ skill_get (skill_db[id].castnodex[lv-1], id, lv); } -int skill_get_delaynodex( int id ,int lv ){ skill_get (skill_db[id].delaynodex[lv-1], id, lv); } -int skill_get_nocast ( int id ){ skill_get (skill_db[id].nocast, id, 1); } -int skill_get_type( int id ){ skill_get (skill_db[id].skill_type, id, 1); } -int skill_get_unit_id ( int id, int flag ){ skill_get (skill_db[id].unit_id[flag], id, 1); } -int skill_get_unit_layout_type( int id ,int lv ){ skill_get (skill_db[id].unit_layout_type[lv-1], id, lv); } -int skill_get_unit_interval( int id ){ skill_get (skill_db[id].unit_interval, id, 1); } -int skill_get_unit_range( int id, int lv ){ skill_get (skill_db[id].unit_range[lv-1], id, lv); } -int skill_get_unit_target( int id ){ skill_get ((skill_db[id].unit_target&BCT_ALL), id, 1); } -int skill_get_unit_bl_target( int id ){ skill_get ((skill_db[id].unit_target&BL_ALL), id, 1); } -int skill_get_unit_flag( int id ){ skill_get (skill_db[id].unit_flag, id, 1); } -const char* skill_get_name( int id ){ - if (id >= GD_SKILLRANGEMIN && id <= GD_SKILLRANGEMAX) - return "UNKNOWN_SKILL"; - if (id >= GD_SKILLBASE) - id = GD_SKILLRANGEMIN + id - GD_SKILLBASE; - if (id < 1 || id > MAX_SKILL_DB || skill_db[id].name==NULL) - return "UNKNOWN_SKILL"; //Can't use skill_chk because we return a string. - return skill_db[id].name; -} - -int skill_tree_get_max(int id, int b_class){ - int i, skillid; - for(i=0;(skillid=skill_tree[b_class][i].id)>0;i++) - if (id == skillid) return skill_tree[b_class][i].max; - return skill_get_max (id); -} - -/* プ?トタイプ */ -int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); -int skill_frostjoke_scream(struct block_list *bl,va_list ap); -int status_change_timer_sub(struct block_list *bl, va_list ap); -int skill_attack_area(struct block_list *bl,va_list ap); -struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex] -int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris] -int skill_greed(struct block_list *bl, va_list ap); -int skill_landprotector(struct block_list *bl, va_list ap); -int skill_ganbatein(struct block_list *bl, va_list ap); -int skill_trap_splash(struct block_list *bl, va_list ap); -int skill_count_target(struct block_list *bl, va_list ap); -struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick); -static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick); -static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick); -int skill_unit_effect(struct block_list *bl,va_list ap); -static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv); - -int enchant_eff[5] = { 10, 14, 17, 19, 20 }; -int deluge_eff[5] = { 5, 9, 12, 14, 15 }; - -int skill_get_casttype(int id) -{ - int inf = skill_get_inf(id); - if (inf&(INF_GROUND_SKILL)) - return CAST_GROUND; - if (inf&INF_SUPPORT_SKILL) - return CAST_NODAMAGE; - if (inf&INF_SELF_SKILL) { - if(skill_get_inf2(id)&INF2_NO_TARGET_SELF) - return CAST_DAMAGE; //Combo skill. - return CAST_NODAMAGE; - } - if (skill_get_nk(id)&NK_NO_DAMAGE) - return CAST_NODAMAGE; - return CAST_DAMAGE; -}; - -//Returns actual skill range taking into account attack range and AC_OWL [Skotlex] -int skill_get_range2(struct block_list *bl, int id, int lv) { - int range = skill_get_range(id, lv); - if(range < 0) { - if (battle_config.use_weapon_skill_range) - return status_get_range(bl); - range *=-1; - } - //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE - switch (id) { - case AC_SHOWER: - case AC_DOUBLE: - case HT_BLITZBEAT: - case AC_CHARGEARROW: - case SN_FALCONASSAULT: - case SN_SHARPSHOOTING: - case HT_POWER: - if (bl->type == BL_PC) - range += pc_checkskill((struct map_session_data *)bl, AC_VULTURE); - else - range += 10; //Assume level 10? - break; - // added to allow GS skills to be effected by the range of Snake Eyes [Reddozen] - case GS_RAPIDSHOWER: - case GS_TRACKING: - case GS_PIERCINGSHOT: - case GS_FULLBUSTER: - case GS_SPREADATTACK: - case GS_GROUNDDRIFT: - if (bl->type == BL_PC) - range += pc_checkskill((struct map_session_data *)bl, GS_SNAKEEYE); - else - range += 10; //Assume level 10? - break; - } - - return range; -} - -// Making plagiarize check its own function [Aru] -int can_copy(struct map_session_data *sd, int skillid) -{ - // Never copy NPC/Wedding Skills - if (skill_get_inf2(skillid)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL)) - return 0; - - // High-class skills - if((skillid >= LK_AURABLADE && skillid <= ASC_CDP) || (skillid >= ST_PRESERVE && skillid <= CR_CULTIVATION)) - { - if(battle_config.copyskill_restrict == 2) - return 0; - else if(battle_config.copyskill_restrict) - return (sd->status.class_ == JOB_STALKER); - } - - return 1; -} - -// [MouseJstr] - skill ok to cast? and when? -int skillnotok(int skillid, struct map_session_data *sd) -{ - int i = skillid; - nullpo_retr (1, sd); - //if (sd == 0) - //return 0; - //return 1; - // I think it was meant to be "no skills allowed when not a valid sd" - - if (skillid >= GD_SKILLRANGEMIN && skillid <= GD_SKILLRANGEMAX) - return 1; - - if (i >= GD_SKILLBASE) - i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; - - if (i > MAX_SKILL || i < 0) - return 1; - - if (sd->blockskill[i] > 0) - return 1; - - if (battle_config.gm_skilluncond && pc_isGM(sd) >= battle_config.gm_skilluncond) - return 0; // gm's can do anything damn thing they want - - // Check skill restrictions [Celest] - if(!map_flag_vs(sd->bl.m) && skill_get_nocast (skillid) & 1) - return 1; - if(map[sd->bl.m].flag.pvp) { - if(!battle_config.pk_mode && skill_get_nocast (skillid) & 2) - return 1; - if(battle_config.pk_mode && skill_get_nocast (skillid) & 16) - return 1; - } - if(map_flag_gvg(sd->bl.m) && skill_get_nocast (skillid) & 4) - return 1; - if(agit_flag && skill_get_nocast (skillid) & 8) - return 1; - if(map[sd->bl.m].flag.restricted && map[sd->bl.m].zone && skill_get_nocast (skillid) & (8*map[sd->bl.m].zone)) - return 1; - - switch (skillid) { - case AL_WARP: - if(map[sd->bl.m].flag.nowarp) { - clif_skill_teleportmessage(sd,0); - return 1; - } - return 0; - break; - case AL_TELEPORT: - if(map[sd->bl.m].flag.noteleport) { - clif_skill_teleportmessage(sd,0); - return 1; - } - return 0; - case TK_HIGHJUMP: - if(map[sd->bl.m].flag.noteleport && !map_flag_gvg(sd->bl.m)) - { //Can't be used on noteleport maps, except for gvg maps [Skotlex] - clif_skill_fail(sd,skillid,0,0); - return 1; - } - break; - case WE_CALLPARTNER: - case WE_CALLPARENT: - case WE_CALLBABY: - if (map[sd->bl.m].flag.nomemo) { - clif_skill_teleportmessage(sd,1); - return 1; - } - break; - case MC_VENDING: - case MC_IDENTIFY: - return 0; // always allowed - case WZ_ICEWALL: - // noicewall flag [Valaris] - if (map[sd->bl.m].flag.noicewall) { - clif_skill_fail(sd,skillid,0,0); - return 1; - } - } - return (map[sd->bl.m].flag.noskill); -} - -/* スキルユニットの配置?報を返す */ -struct skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT]; -int firewall_unit_pos; -int icewall_unit_pos; - -struct skill_unit_layout *skill_get_unit_layout (int skillid, int skilllv, struct block_list *src, int x, int y) -{ - int pos = skill_get_unit_layout_type(skillid,skilllv); - int dir; - - if (pos != -1) - return &skill_unit_layout[pos]; - - if (src->x == x && src->y == y) - dir = 2; - else - dir = map_calc_dir(src,x,y); - - if (skillid == MG_FIREWALL) - return &skill_unit_layout [firewall_unit_pos + dir]; - else if (skillid == WZ_ICEWALL) - return &skill_unit_layout [icewall_unit_pos + dir]; - - ShowError("Unknown unit layout for skill %d, %d\n",skillid,skilllv); - return &skill_unit_layout[0]; -} - -/*========================================== - * スキル追加?果 - *------------------------------------------ - */ -int skill_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick) -{ - struct map_session_data *sd=NULL; - struct map_session_data *dstsd=NULL; - struct status_change *sc; - struct status_change *tsc; - struct mob_data *md=NULL; - struct mob_data *dstmd=NULL; - struct pet_data *pd=NULL; - - int skill,skill2; - int rate; - - nullpo_retr(0, src); - nullpo_retr(0, bl); - - if(skillid < 0) - { // remove the debug print when this case is finished - ShowDebug("skill_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid, - src, bl,skillid,skilllv,attack_type,tick); - return 0; - } - if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest - - switch (src->type) { - case BL_PC: - sd = (struct map_session_data *)src; - break; - case BL_MOB: - md = (struct mob_data *)src; - break; - case BL_PET: - pd = (struct pet_data *)src; // [Valaris] - break; - } - - switch (bl->type) { - case BL_PC: - dstsd=(struct map_session_data *)bl; - break; - case BL_MOB: - dstmd=(struct mob_data *)bl; - break; - } - sc = status_get_sc(src); - tsc = status_get_sc(bl); - - if (!tsc) //skill additional effect is about adding effects to the target... - //So if the target can't be inflicted with statuses, this is pointless. - return 0; - - switch(skillid){ - case 0: // Normal attacks (no skill used) - { - if(sd) { - // Automatic trigger of Blitz Beat - if (pc_isfalcon(sd) && sd->status.weapon == W_BOW && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 && - rand()%1000 <= sd->paramc[5]*10/3+1 ) { - int lv=(sd->status.job_level+9)/10; - skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skillstate.steal_flagstatus.weapon != W_BOW && (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 && - (skill*15 + 55) + (skill2 = pc_checkskill(sd,TF_STEAL))*10 > rand()%1000) { - if(pc_steal_item(sd,bl)) - clif_skill_nodamage(src,bl,TF_STEAL,skill2,1); - else if (battle_config.display_snatcher_skill_fail) - clif_skill_fail(sd,skillid,0,0); - } - // Chance to trigger Taekwon kicks [Dralnu] - if(sd->sc.count && sd->sc.data[SC_COMBO].timer == -1) { - if(sd->sc.data[SC_READYSTORM].timer != -1 && - sc_start4(src,SC_COMBO, 15, TK_STORMKICK,0,0,0, - (2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src)))) - ; //Stance triggered - else if(sd->sc.data[SC_READYDOWN].timer != -1 && - sc_start4(src,SC_COMBO, 15, TK_DOWNKICK,0,0,0, - (2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src)))) - ; //Stance triggered - else if(sd->sc.data[SC_READYTURN].timer != -1 && - sc_start4(src,SC_COMBO, 15, TK_TURNKICK,0,0,0, - (2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src)))) - ; //Stance triggered - else if(sd->sc.data[SC_READYCOUNTER].timer != -1) - { //additional chance from SG_FRIEND [Komurka] - rate = 20; - if (sd->sc.data[SC_SKILLRATE_UP].timer != -1 && sd->sc.data[SC_SKILLRATE_UP].val1 == TK_COUNTER) { - rate += rate*sd->sc.data[SC_SKILLRATE_UP].val2/100; - status_change_end(src,SC_SKILLRATE_UP,-1); - } - sc_start4(src,SC_COMBO, rate, TK_COUNTER, bl->id,0,0, - (2000 - 4 * status_get_agi(src) - 2 * status_get_dex(src))); - } - } - } - - if (sc && sc->count) { - // Enchant Poison gives a chance to poison attacked enemies - if(sc->data[SC_ENCPOISON].timer != -1) - sc_start4(bl,SC_POISON,sc->data[SC_ENCPOISON].val1, - sc->data[SC_ENCPOISON].val1,0,0,0,skill_get_time2(AS_ENCHANTPOISON,sc->data[SC_ENCPOISON].val1)); - // Enchant Deadly Poison gives a chance to deadly poison attacked enemies - if(sc->data[SC_EDP].timer != -1) - sc_start4(bl,SC_DPOISON,sc->data[SC_EDP].val2, - sc->data[SC_EDP].val1,0,0,0,skill_get_time2(ASC_EDP,sc->data[SC_EDP].val1)); - } - if (tsc->count) { - if (tsc->data[SC_SPLASHER].timer != -1) - sc_start4(bl,SC_POISON,2*tsc->data[SC_SPLASHER].val1+10, - tsc->data[SC_SPLASHER].val1,0,0,0, - skill_get_time2(tsc->data[SC_SPLASHER].val2,tsc->data[SC_SPLASHER].val1)); - } - } - break; - - case SM_BASH: /* バッシュ?i急??U??j */ - if( sd && skilllv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){ - //TODO: How much % per base level it actually is? - sc_start(bl,SC_STUN,(5*(skilllv-5)+(int)sd->status.base_level/10), - skilllv,skill_get_time2(SM_FATALBLOW,skilllv)); - } - break; - - case AS_VENOMKNIFE: - if (sd) //Poison chance must be that of Envenom. [Skotlex] - skilllv = pc_checkskill(sd, TF_POISON); - case TF_POISON: /* インベナム */ - case AS_SPLASHER: /* ベナムスプラッシャ? */ - if(!sc_start(bl,SC_POISON,(2*skilllv+10),skilllv,skill_get_time2(skillid,skilllv)) - && sd && skillid==TF_POISON - ) - clif_skill_fail(sd,skillid,0,0); - break; - - case AS_SONICBLOW: /* ソニックブ?? */ - sc_start(bl,SC_STUN,(2*skilllv+10),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case AS_GRIMTOOTH: - { - int type = dstsd?SC_SLOWDOWN:SC_STOP; - if (tsc->data[type].timer == -1) - sc_start(bl,type,100,skilllv,skill_get_time2(skillid, skilllv)); - break; - } - case MG_FROSTDIVER: /* フ?ストダイバ? */ - case WZ_FROSTNOVA: /* フ?ストノヴァ */ - { - rate = (skilllv*3+35)-(status_get_int(bl)+status_get_luk(bl))/15; - if (rate <= 5) - rate = 5; - sc_start(bl,SC_FREEZE,rate,skilllv,skill_get_time2(skillid,skilllv)); - } - break; - - case WZ_STORMGUST: /* スト?ムガスト */ - tsc->data[SC_FREEZE].val3++; - if(tsc->data[SC_FREEZE].val3 >= 3) - status_change_start(bl,SC_FREEZE,10000, - skilllv,0,0,0,skill_get_time2(skillid,skilllv),8); - break; - - case WZ_METEOR: - sc_start(bl,SC_STUN,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case WZ_VERMILION: - sc_start(bl,SC_BLIND,4*skilllv,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case HT_FREEZINGTRAP: /* フリ?ジングトラップ */ - sc_start(bl,SC_FREEZE,(3*skilllv+35),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case HT_FLASHER: /* Flasher */ - sc_start(bl,SC_BLIND,(10*skilllv+30),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case HT_LANDMINE: /* ランドマイン */ - sc_start(bl,SC_STUN,(5*skilllv+30),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case HT_SHOCKWAVE: //it can't affect mobs, because they have no SP... - if(dstsd) - pc_damage_sp(dstsd, 0, 15*skilllv+5); - break; - - case HT_SANDMAN: /* サンドマン */ - sc_start(bl,SC_SLEEP,(10*skilllv+40),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case TF_SPRINKLESAND: /* ?サまき */ - sc_start(bl,SC_BLIND,20,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case TF_THROWSTONE: /* ?ホ投げ */ - sc_start(bl,SC_STUN,3,skilllv,skill_get_time(skillid,skilllv)); - sc_start(bl,SC_BLIND,3,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case NPC_DARKCROSS: - case CR_HOLYCROSS: /* ホ?リ?クロス */ - sc_start(bl,SC_BLIND,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case CR_GRANDCROSS: /* グランドク?ス */ - case NPC_GRANDDARKNESS: /*闇グランドク?ス*/ - { - int race = status_get_race(bl); - if(battle_check_undead(race,status_get_elem_type(bl)) || race == RC_DEMON) - sc_start(bl,SC_BLIND,100,skilllv,skill_get_time2(skillid,skilllv)); - } - break; - - case AM_ACIDTERROR: - sc_start(bl,SC_BLEEDING,(skilllv*3),skilllv,skill_get_time2(skillid,skilllv)); - if (skill_break_equip(bl, EQP_ARMOR, 100*skill_get_time(skillid,skilllv), BCT_ENEMY)) - clif_emotion(bl,23); - break; - - case AM_DEMONSTRATION: - skill_break_equip(bl, EQP_WEAPON, 100*skilllv, BCT_ENEMY); - break; - - case CR_SHIELDCHARGE: /* シ?ルドチャ?ジ */ - sc_start(bl,SC_STUN,(15+skilllv*5),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case PA_PRESSURE: /* プレッシャ? */ - if (dstsd) - pc_damage_sp(dstsd, 0, 15 +5*skilllv); - break; - - case RG_RAID: /* サプライズアタック */ - sc_start(bl,SC_STUN,(10+3*skilllv),skilllv,skill_get_time(skillid,skilllv)); - sc_start(bl,SC_BLIND,(10+3*skilllv),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case BA_FROSTJOKE: - sc_start(bl,SC_FREEZE,(15+5*skilllv),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case DC_SCREAM: - sc_start(bl,SC_STUN,(25+5*skilllv),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case BD_LULLABY: /* 子守唄 */ - sc_start(bl,SC_SLEEP,15,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case DC_UGLYDANCE: - if (dstsd) { - int skill, sp = 5+5*skilllv; - if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON))) - sp += 5+skill; - pc_damage_sp(dstsd, sp, 0); - } - break; - case SL_STUN: - if (status_get_size(bl)==1) //Only stuns mid-sized mobs. - sc_start(bl,SC_STUN,(30+10*skilllv),skilllv,skill_get_time(skillid,skilllv)); - break; - - /* MOBの追加?果付きスキル */ - case NPC_PETRIFYATTACK: - case NPC_CURSEATTACK: - case NPC_SLEEPATTACK: - case NPC_BLINDATTACK: - case NPC_POISON: - case NPC_SILENCEATTACK: - case NPC_STUNATTACK: - sc_start(bl,SkillStatusChangeTable[skillid],50+10*skilllv,skilllv,src->type==BL_PET?skilllv*1000:skill_get_time2(skillid,skilllv)); - break; - - case NPC_MENTALBREAKER: - if(dstsd) { - int sp = dstsd->status.max_sp*(10+skilllv)/100; - if(sp < 1) sp = 1; - pc_damage_sp(dstsd,sp,0); - } - break; - // Equipment breaking monster skills [Celest] - case NPC_BREAKWEAPON: - skill_break_equip(bl, EQP_WEAPON, 150*skilllv, BCT_ENEMY); - break; - case NPC_BREAKARMOR: - skill_break_equip(bl, EQP_ARMOR, 150*skilllv, BCT_ENEMY); - break; - case NPC_BREAKHELM: - skill_break_equip(bl, EQP_HELM, 150*skilllv, BCT_ENEMY); - break; - case NPC_BREAKSHIELD: - skill_break_equip(bl, EQP_SHIELD, 150*skilllv, BCT_ENEMY); - break; - - case CH_TIGERFIST: - sc_start(bl,SC_STOP,(10+skilllv*10),0,skill_get_time2(skillid,skilllv)); - break; - - case LK_SPIRALPIERCE: - sc_start(bl,SC_STOP,(15+skilllv*5),0,skill_get_time2(skillid,skilllv)); - break; - - case ST_REJECTSWORD: /* フリ?ジングトラップ */ - sc_start(bl,SC_AUTOCOUNTER,(skilllv*15),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case PF_FOGWALL: /* ホ?リ?ク?ス */ - if (src != bl && tsc->data[SC_DELUGE].timer == -1) - status_change_start(bl,SC_BLIND,10000,skilllv,0,0,0,skill_get_time2(skillid,skilllv),8); - break; - - case LK_HEADCRUSH: /* ヘッドクラッシュ */ - { - //??が良く分からないので適?に - int race = status_get_race(bl); - if (!(battle_check_undead(race, status_get_elem_type(bl)) || race == RC_DEMON)) - sc_start(bl, SC_BLEEDING,50, skilllv, skill_get_time2(skillid,skilllv)); - } - break; - - case LK_JOINTBEAT: /* ジョイントビ?ト */ - //??が良く分からないので適?に - sc_start(bl,SkillStatusChangeTable[skillid],(5*skilllv+5),skilllv,skill_get_time2(skillid,skilllv)); - break; - - case ASC_METEORASSAULT: /* ?テオアサルト */ - //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*SkillLV% chance. - switch(rand()%3) { - case 0: - sc_start(bl,SC_BLIND,(5+skilllv*5),skilllv,skill_get_time2(skillid,1)); - break; - case 1: - sc_start(bl,SC_STUN,(5+skilllv*5),skilllv,skill_get_time2(skillid,2)); - break; - default: - sc_start(bl,SC_BLEEDING,(5+skilllv*5),skilllv,skill_get_time2(skillid,3)); - } - break; - - case HW_NAPALMVULCAN: /* ナパ?ムバルカン */ - // skilllv*5%の確率で呪い - sc_start(bl,SC_CURSE,5*skilllv,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case WS_CARTTERMINATION: // Cart termination - sc_start(bl,SC_STUN,5*skilllv,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case CR_ACIDDEMONSTRATION: - skill_break_equip(bl, EQP_WEAPON|EQP_ARMOR, 100*skilllv, BCT_ENEMY); - break; - - case TK_DOWNKICK: - sc_start(bl,SC_STUN,100,skilllv,skill_get_time2(skillid,skilllv)); - break; - - case TK_JUMPKICK: - //Cancel out Soul Linker status of the target. [Skotlex] - if (tsc->count) { - if (tsc->data[SC_PRESERVE].timer != -1) //preserve blocks the cleaning - break; - //Remove pitched potions effect. - if (tsc->data[SC_ASPDPOTION0].timer != -1 && tsc->data[SC_ASPDPOTION0].val4) - status_change_end(bl, SC_ASPDPOTION0, -1); - if (tsc->data[SC_ASPDPOTION1].timer != -1 && tsc->data[SC_ASPDPOTION1].val4) - status_change_end(bl, SC_ASPDPOTION1, -1); - if (tsc->data[SC_ASPDPOTION2].timer != -1 && tsc->data[SC_ASPDPOTION2].val4) - status_change_end(bl, SC_ASPDPOTION2, -1); - if (tsc->data[SC_ASPDPOTION3].timer != -1 && tsc->data[SC_ASPDPOTION3].val4) - status_change_end(bl, SC_ASPDPOTION3, -1); - if (tsc->data[SC_SPIRIT].timer != -1) - status_change_end(bl, SC_SPIRIT, -1); - if (tsc->data[SC_ONEHAND].timer != -1) - status_change_end(bl, SC_ONEHAND, -1); - if (tsc->data[SC_ADRENALINE2].timer != -1) - status_change_end(bl, SC_ADRENALINE2, -1); - } - break; - case TK_TURNKICK: - case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs. - if(attack_type == BF_MISC) //70% base stun chance... - sc_start(bl,SC_STUN,70,skilllv,skill_get_time2(skillid,skilllv)); - break; - //Until they're at right position - gs_statuschange- [Vicious] - case GS_BULLSEYE: //0.1% coma rate. - status_change_start(bl,SC_COMA,10,skilllv,0,0,0,0,0); - break; - case GS_CRACKER: - if (!dstsd) // according to latest patch, should not work on players [Reddozen] - sc_start(bl,SC_STUN,(100 - 10*distance_bl(src, bl)),skilllv,skill_get_time2(skillid,skilllv)); //Temp stun rate - break; - case GS_PIERCINGSHOT: - sc_start(bl,SC_BLEEDING,(skilllv*3),skilllv,skill_get_time2(skillid,skilllv)); - break; - case GS_FULLBUSTER: - sc_start(bl,SC_BLIND,(2*skilllv),skilllv,skill_get_time2(skillid,1)); - break; - case NJ_HYOUSYOURAKU: - sc_start(bl,SC_FREEZE,(10+10*skilllv),skilllv,skill_get_time2(skillid,skilllv)); - break; - - //case GS_FLING: // this needs to be looked at [Reddozen] - // if (skill == GS_FLING) { // gunslinger [marquis007] - // int spiritball = (sd->spiritball > 5 ? 5 : sd->spiritball); - // } else { - // int spiritball = 1; - // } - // - // if (spiritball <= sd->spiritball && sd->spiritball != 0){ - // pc_delspiritball(sd,spiritball,0); - // status_change_start(bl,SC_FLING,10000,spiritball*5,0,0,0,skill_get_time(skillid,skilllv))); - // } - // break; - } - - if (md && battle_config.summons_inherit_effects && md->master_id && md->special_state.ai) - { //Pass heritage to Master for status causing effects. [Skotlex] - sd = map_id2sd(md->master_id); - } - - if(sd && skillid != MC_CARTREVOLUTION && skillid != AM_DEMONSTRATION && skillid != CR_REFLECTSHIELD && attack_type&BF_WEAPON){ /* カ?ドによる追加?果 */ - int i, type; - for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){ - type=i-SC_COMMON_MIN; - rate = sd->addeff[type]+(sd->state.arrow_atk?sd->arrow_addeff[type]:0); - if (!rate) - continue; //Code Speedup. - status_change_start(bl,i,rate,7,0,0,0,skill_get_time2(StatusSkillChangeTable[type],7),0); - } - } - - //Reports say that autospell effects get triggered on skills and pretty much everything including splash attacks. [Skotlex] - //No need to check the NK value as this function is only called on attacks - //(or stuff that should invoke these things. - if(sd && !status_isdead(bl) && src != bl/* && - !(skillid && skill_get_nk(skillid)&NK_NO_DAMAGE)*/) { - struct block_list *tbl; - struct unit_data *ud; - int i; - for (i = 0; i < MAX_PC_BONUS && sd->autospell[i].id; i++) { - - skill = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id; - - if (skillnotok(skill, sd)) - continue; - - //skill2 reused to store skilllv. - skill2 = (sd->autospell[i].lv > 0) ? sd->autospell[i].lv : 1; - rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2; - - if (rand()%1000 > rate) - continue; - if (sd->autospell[i].id < 0) - tbl = src; - else - tbl = bl; - - if (tbl != src && !battle_check_range(src, tbl, skill_get_range2(src, skill, skill2))) - continue; //Autoskills DO check for target-src range. [Skotlex] - rate = skill_get_inf(skill); - switch (skill_get_casttype(skill)) { - case CAST_GROUND: - skill_castend_pos2(src, tbl->x, tbl->y, skill, skill2, tick, 0); - break; - case CAST_NODAMAGE: - skill_castend_nodamage_id(src, tbl, skill, skill2, tick, 0); - break; - case CAST_DAMAGE: - skill_castend_damage_id(src, tbl, skill, skill2, tick, 0); - break; - } - //Set canact delay. [Skotlex] - ud = unit_bl2ud(src); - if (ud) { - rate = skill_delayfix(src, skill, skill2)/2; - if (DIFF_TICK(ud->canact_tick, tick + rate) < 0) - ud->canact_tick = tick+rate; - } - break; //Only one auto skill comes off at a time. - } - } - return 0; -} - -/* Splitted off from skill_additional_effect, which is never called when the - * attack skill kills the enemy. Place in this function counter status effects - * when using skills (eg: Asura's sp regen penalty, or counter-status effects - * from cards) that will take effect on the source, not the target. [Skotlex] - * Note: Currently this function only applies to Extremity Fist and BF_WEAPON - * type of skills, so not every instance of skill_additional_effect needs a call - * to this one. - */ -int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick) -{ - int rate; - struct map_session_data *sd=NULL; - struct map_session_data *dstsd=NULL; - struct status_change *tsc; - - nullpo_retr(0, src); - nullpo_retr(0, bl); - - if(skillid < 0) - { // remove the debug print when this case is finished - ShowDebug("skill_counter_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid, - src, bl,skillid,skilllv,attack_type,tick); - return 0; - } - if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest - - tsc = status_get_sc(bl); - if (tsc && !tsc->count) - tsc = NULL; - - BL_CAST(BL_PC, src, sd); - BL_CAST(BL_PC, bl, dstsd); - - switch(skillid){ - case 0: //Normal Attack - if(tsc && tsc->data[SC_KAAHI].timer != -1 && tsc->data[SC_KAAHI].val4 == -1) - tsc->data[SC_KAAHI].val4 = add_timer( - tick+skill_get_time2(SL_KAAHI,tsc->data[SC_KAAHI].val1), - kaahi_heal_timer, bl->id, SC_KAAHI); //Activate heal. - break; - case MO_EXTREMITYFIST: /* 阿?C羅覇凰? */ - //阿?C羅を使うと5分間自然回復しないようになる - sc_start(src,SkillStatusChangeTable[skillid],100,skilllv,skill_get_time2(skillid,skilllv)); - break; - } - - if((sd||dstsd) && skillid != MC_CARTREVOLUTION && attack_type&BF_WEAPON){ /* カ?ドによる追加?果 */ - int i, type; - - for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){ - type=i-SC_COMMON_MIN; - - - rate = sd?(sd->addeff2[type]+(sd->state.arrow_atk?sd->arrow_addeff2[type]:0)):0; - if (rate) //Self infliced status from attacking. - status_change_start(src,i,rate,7,0,0,0,skill_get_time2(StatusSkillChangeTable[type],7),0); - - rate = dstsd?dstsd->addeff3[type]:0; - if (rate && (dstsd->addeff3_type[type] == 1 || ((sd && sd->state.arrow_atk) || (status_get_range(src)>2)))) - status_change_start(src,i,rate,7,0,0,0,skill_get_time2(StatusSkillChangeTable[type],7),0); - } - } - - if(sd && bl->type == BL_MOB && status_isdead(bl) && - skillid && skill_get_type(skillid)==BF_MAGIC && - skill_get_inf(skillid)!=INF_GROUND_SKILL && - (rate=pc_checkskill(sd,HW_SOULDRAIN))>0) - { //Soul Drain should only work on targetted spells [Skotlex] - int sp; - if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex] - clif_skill_nodamage(src,bl,HW_SOULDRAIN,rate,1); - sp = (status_get_lv(bl))*(95+15*rate)/100; - if(sp > sd->status.max_sp - sd->status.sp) - sp = sd->status.max_sp - sd->status.sp; - if (sp) { - sd->status.sp += sp; - clif_heal(sd->fd,SP_SP,sp); - } - } - - //Trigger counter-spells to retaliate against damage causing skills. [Skotlex] - if(dstsd && !status_isdead(bl) && src != bl && !(skillid && skill_get_nk(skillid)&NK_NO_DAMAGE)) - { - struct block_list *tbl; - struct unit_data *ud; - int i, skillid, skilllv, rate; - - for (i = 0; i < MAX_PC_BONUS; i++) { - if (dstsd->autospell2[i].id == 0) - break; - - skillid = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id; - skilllv = (dstsd->autospell2[i].lv > 0) ? dstsd->autospell2[i].lv : 1; - rate = ((sd && !sd->state.arrow_atk) || (status_get_range(src)<=2)) ? - dstsd->autospell2[i].rate : dstsd->autospell2[i].rate / 2; - - if (skillnotok(skillid, dstsd)) - continue; - if (rand()%1000 > rate) - continue; - if (dstsd->autospell2[i].id < 0) - tbl = bl; - else - tbl = src; - - if (tbl != bl && !battle_check_range(bl, tbl, skill_get_range2(bl, skillid, skilllv))) - continue; //Autoskills DO check for target-src range. [Skotlex] - - switch (skill_get_casttype(skillid)) { - case CAST_GROUND: - skill_castend_pos2(bl, tbl->x, tbl->y, skillid, skilllv, tick, 0); - break; - case CAST_NODAMAGE: - skill_castend_nodamage_id(bl, tbl, skillid, skilllv, tick, 0); - break; - case CAST_DAMAGE: - skill_castend_damage_id(bl, tbl, skillid, skilllv, tick, 0); - break; - } - //Set canact delay. [Skotlex] - ud = unit_bl2ud(bl); - if (ud) { - rate = skill_delayfix(bl, skillid, skilllv)/2; - if (DIFF_TICK(ud->canact_tick, tick + rate) < 0) - ud->canact_tick = tick+rate; - } - break; //trigger only one auto-spell per hit. - } - } - return 0; -} -/*========================================================================= - Breaks equipment. On-non players causes the corresponding strip effect. - - rate goes from 0 to 10000 (100.00%) - - flag is a BCT_ flag to indicate which type of adjustment should be used - (BCT_ENEMY/BCT_PARTY/BCT_SELF) are the valid values. ---------------------------------------------------------------------------*/ -int skill_break_equip(struct block_list *bl, unsigned short where, int rate, int flag) { - static int where_list[4] = {EQP_WEAPON, EQP_ARMOR, EQP_SHIELD, EQP_HELM}; - static int scatk[4] = {SC_STRIPWEAPON, SC_STRIPARMOR, SC_STRIPSHIELD, SC_STRIPHELM }; - static int scdef[4] = {SC_CP_WEAPON, SC_CP_ARMOR, SC_CP_SHIELD, SC_CP_HELM}; - struct status_change *sc = status_get_sc(bl); - int i,j; - TBL_PC *sd; - BL_CAST(BL_PC, bl, sd); - if (sc && !sc->count) - sc = NULL; - - if (sd) { - if (sd->unbreakable_equip) - where &= ~sd->unbreakable_equip; - if (sd->unbreakable) - rate -= rate*sd->unbreakable/100; - if (where&EQP_WEAPON) { - switch (sd->status.weapon) { - case W_FIST: //Bare fists should not break :P - case W_1HAXE: - case W_2HAXE: - case W_MACE: // Axes and Maces can't be broken [DracoRPG] - case W_STAFF: - case W_BOOK: //Rods and Books can't be broken [Skotlex] - where &= ~EQP_WEAPON; - } - } - } - if (flag&BCT_ENEMY) { - if (battle_config.equip_skill_break_rate != 100) - rate = rate*battle_config.equip_skill_break_rate/100; - } else if (flag&(BCT_PARTY|BCT_SELF)) { - if (battle_config.equip_self_break_rate != 100) - rate = rate*battle_config.equip_self_break_rate/100; - } - - for (i = 0; i < 4; i++) { - if (where&where_list[i]) { - if (sc && sc->count && sc->data[scdef[i]].timer != -1) - where&=~where_list[i]; - else if (rand()%10000 >= rate) - where&=~where_list[i]; - else if (!sd) //Cause Strip effect. - sc_start(bl,scatk[i],100,0,skill_get_time(StatusSkillChangeTable[scatk[i]],1)); - } - } - if (!where) //Nothing to break. - return 0; - if (sd) { - for (i = 0; i < 11; i++) { - j = sd->equip_index[i]; - if (j <= 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j]) - continue; - flag = 0; - switch(i) { - case 6: //Upper Head - flag = (where&EQP_HELM); - break; - case 7: //Body - flag = (where&EQP_ARMOR); - break; - case 8: //Left/Right hands - case 9: - flag = ( - (where&EQP_WEAPON && sd->inventory_data[j]->type == 4) || - (where&EQP_SHIELD && sd->inventory_data[j]->type == 5)); - break; - default: - continue; - } - if (flag) { - sd->status.inventory[j].attribute = 1; - pc_unequipitem(sd, j, 3); - } - } - clif_equiplist(sd); - } - - return where; //Return list of pieces broken. -} -/*========================================================================= - Used to knock back players, monsters, traps, etc - If count&0xf00000, the direction is send in the 6th byte. - If count&0x10000, the direction is to the back of the target, otherwise is away from the src. - If count&0x20000, position update packets must not be sent. - IF count&0X40000, direction is random. ---------------------------------------------------------------------------*/ -int skill_blown( struct block_list *src, struct block_list *target,int count) -{ - int dx=0,dy=0,nx,ny; - int x=target->x,y=target->y; - int dir,ret; - struct skill_unit *su=NULL; - - nullpo_retr(0, src); - - if (src != target && map_flag_gvg(target->m)) - return 0; //No knocking back in WoE - if (!count&0xffff) - return 0; //Actual knockback distance is 0. - - switch (target->type) { - case BL_MOB: - if (((TBL_MOB*)target)->class_ == MOBID_EMPERIUM) - return 0; - break; - case BL_SKILL: - su=(struct skill_unit *)target; - break; - } - - if (count&0xf00000) - dir = (count>>20)&0xf; - else if (count&0x10000 || (target->x==src->x && target->y==src->y)) - dir = unit_getdir(target); - else if (count&0x40000) //Flag for random pushing. - dir = rand()%8; - else - dir = map_calc_dir(target,src->x,src->y); - if (dir>=0 && dir<8){ - dx = -dirx[dir]; - dy = -diry[dir]; - } - - ret=path_blownpos(target->m,x,y,dx,dy,count&0xffff); - nx=ret>>16; - ny=ret&0xffff; - - if (!su) - unit_stop_walking(target,0); - - dx = nx - x; - dy = ny - y; - - if (!dx && !dy) //Could not knockback. - return 0; - - map_foreachinmovearea(clif_outsight,target->m, - x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE, - dx,dy,target->type==BL_PC?BL_ALL:BL_PC,target); - - if(su) - skill_unit_move_unit_group(su->group,target->m,dx,dy); - else - map_moveblock(target, nx, ny, gettick()); - - map_foreachinmovearea(clif_insight,target->m, - nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE, - -dx,-dy,target->type==BL_PC?BL_ALL:BL_PC,target); - - if(!(count&0x20000)) - clif_blown(target); - - return 0; -} - -/* - * ========================================================================= - * スキル?U??果??まとめ - * flagの?明?B16?i? - * 00XRTTff - * ff = magicで計算に渡される?j - * TT = パケットのtype部分(0でデフォルト?j - * X = パケットのスキルLv - * R = 予約?iskill_area_subで使用する?j - *------------------------------------------------------------------------- - */ - -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 ) -{ - struct Damage dmg; - struct status_change *sc; - struct map_session_data *sd=NULL, *tsd=NULL; - int type,lv,damage,rdamage=0; - - if(skillid > 0 && skilllv <= 0) return 0; - - nullpo_retr(0, src); //Source is the master behind the attack (player/mob/pet) - nullpo_retr(0, dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src. - nullpo_retr(0, bl); //Target to be attacked. - - if (src != dsrc) { - //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex] - if (!status_check_skilluse(battle_config.skill_caster_check?src:NULL, bl, skillid, 2)) - return 0; - } else if (flag && skill_get_nk(skillid)&NK_SPLASH) { - //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex] - if (!status_check_skilluse(dsrc, bl, skillid, 2)) - return 0; - } - - if (dsrc->type == BL_PC) - sd = (struct map_session_data *)dsrc; - if (bl->type == BL_PC) - tsd = (struct map_session_data *)bl; - -// Is this check really needed? FrostNova won't hurt you if you step right where the caster is? - if(skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //使用スキルがフ?ストノヴァで?Adsrcとblが同じ??鰍ネら何もしない - return 0; - - type=-1; - lv=(flag>>20)&0xf; - dmg=battle_calc_attack(attack_type,src,bl,skillid,skilllv,flag&0xff ); - - //Skotlex: Adjusted to the new system - if(src->type==BL_PET && (struct pet_data *)src) - { // [Valaris] - struct pet_data *pd = (struct pet_data *)src; - if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skillid) - { - int element = skill_get_pl(skillid); - if (skillid == -1) - element = status_get_attack_element(src); - dmg.damage=battle_attr_fix(src, bl, skilllv, element, status_get_element(bl)); - dmg.damage2=0; - dmg.div_= pd->a_skill->div_; - } - } - - sc= status_get_sc(bl); - if (sc && !sc->count) sc = NULL; //Don't need it. - - if (attack_type&BF_MAGIC) { - if(sc && sc->data[SC_KAITE].timer != -1 && (dmg.damage || dmg.damage2) - && !(status_get_mode(src)&MD_BOSS) && (sd || status_get_lv(dsrc) <= 80) - ) { //Works on players or mobs with level under 80. - clif_skill_nodamage(bl,bl,SL_KAITE,sc->data[SC_KAITE].val1,1); - if (--sc->data[SC_KAITE].val2 <= 0) - status_change_end(bl, SC_KAITE, -1); - bl = src; //Just make the skill attack yourself @.@ - sc = status_get_sc(bl); - tsd = (bl->type == BL_PC)?(TBL_PC*)bl:NULL; - if (sc && !sc->count) - sc = NULL; //Don't need it. - if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_WIZARD) - { //Spirit of Wizard blocks bounced back spells. - dmg.damage = dmg.damage2 = 0; - dmg.dmg_lv = ATK_FLEE; - } - } - - if(sc && sc->data[SC_MAGICROD].timer != -1 && src == dsrc) { - dmg.damage = dmg.damage2 = 0; - dmg.dmg_lv = ATK_FLEE; //This will prevent skill additional effect from taking effect. [Skotlex] - if(tsd) { - int sp = skill_get_sp(skillid,skilllv); - sp = sp * sc->data[SC_MAGICROD].val2 / 100; - if(skillid == WZ_WATERBALL && skilllv > 1) - sp = sp/((skilllv|1)*(skilllv|1)); //Estimate SP cost of a single water-ball - if(sp > SHRT_MAX) sp = SHRT_MAX; - else if(sp < 1) sp = 1; - if(sp > tsd->status.max_sp - tsd->status.sp) - sp = tsd->status.max_sp - tsd->status.sp; - tsd->status.sp += sp; - clif_heal(tsd->fd,SP_SP,sp); - tsd->ud.canact_tick = tick + skill_delayfix(bl, SA_MAGICROD, sc->data[SC_MAGICROD].val1); - } - clif_skill_nodamage(bl,bl,SA_MAGICROD,sc->data[SC_MAGICROD].val1,1); - } - } - - damage = dmg.damage + dmg.damage2; - - if (damage > 0 && src != bl && src == dsrc) - rdamage = battle_calc_return_damage(bl, &damage, dmg.flag); - - if(lv==15) - lv=-1; - - if( flag&0xff00 ) - type=(flag&0xff00)>>8; - - if((damage <= 0 || damage < dmg.div_) - && skillid != CH_PALMSTRIKE) //Palm Strike is the only skill that will knockback even if it misses. [Skotlex] - dmg.blewcount = 0; - - if(skillid == CR_GRANDCROSS||skillid == NPC_GRANDDARKNESS) {//グランドクロス - if(battle_config.gx_disptype) dsrc = src; // 敵ダメ?ジ白文字表示 - if(src == bl) type = 4; // 反動はダメ?ジモ?ションなし - } - -//使用者がPCの??の??ここから - if(sd) { - //Sorry for removing the Japanese comments, but they were actually distracting - //from the actual code and I couldn't understand a thing anyway >.< [Skotlex] - if (sd->sc.data[SC_COMBO].timer!=-1) - { //End combo state after skill is invoked. [Skotlex] - switch (skillid) { - case TK_TURNKICK: - case TK_STORMKICK: - case TK_DOWNKICK: - case TK_COUNTER: - //set this skill as previous one. - sd->skillid_old = skillid; - sd->skilllv_old = skilllv; - if (pc_famerank(sd->char_id,MAPID_TAEKWON)) - break; //Do not end combo state. - default: - status_change_end(src,SC_COMBO,-1); - } - } - switch(skillid) - { - case MO_TRIPLEATTACK: - { - int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src); - if (damage < status_get_hp(bl) && - pc_checkskill(sd, MO_CHAINCOMBO) > 0) - delay += 300 * battle_config.combo_delay_rate / 100; - sc_start4(src,SC_COMBO,100,MO_TRIPLEATTACK,skilllv,0,0,delay); - clif_combo_delay(src, delay); - - if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka] - party_skill_check(sd, sd->status.party_id, MO_TRIPLEATTACK, skilllv); - break; - } - case MO_CHAINCOMBO: - { - int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src); - if(damage < status_get_hp(bl) && - (pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0)) - delay += 300 * battle_config.combo_delay_rate /100; - sc_start4(src,SC_COMBO,100,MO_CHAINCOMBO,skilllv,0,0,delay); - clif_combo_delay(src,delay); - break; - } - case MO_COMBOFINISH: - { - int delay = 700 - 4 * status_get_agi(src) - 2 * status_get_dex(src); - if(damage < status_get_hp(bl) && - ( - (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc.data[SC_EXPLOSIONSPIRITS].timer != -1) || - (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0) || - (pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1) - )) - delay += 300 * battle_config.combo_delay_rate /100; - sc_start4(src,SC_COMBO,100,MO_COMBOFINISH,skilllv,0,0,delay); - clif_combo_delay(src,delay); - break; - } - case CH_TIGERFIST: - { //Tigerfist is now a combo-only skill. [Skotlex] - int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src); - if(damage < status_get_hp(bl) && - ( - (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 3 && sd->sc.data[SC_EXPLOSIONSPIRITS].timer != -1) || - (pc_checkskill(sd, CH_CHAINCRUSH) > 0) - )) - delay += 300 * battle_config.combo_delay_rate /100; - sc_start4(src,SC_COMBO,100,CH_TIGERFIST,skilllv,0,0,delay); - clif_combo_delay(src,delay); - break; - } - case CH_CHAINCRUSH: - { - int delay = 1000 - 4 * status_get_agi(src) - 2 * status_get_dex(src); - if(damage < status_get_hp(bl)) - delay += 300 * battle_config.combo_delay_rate /100; - sc_start4(src,SC_COMBO,100,CH_CHAINCRUSH,skilllv,0,0,delay); - clif_combo_delay(src,delay); - break; - } - case AC_DOUBLE: - { - int race = status_get_race(bl); - if((race == RC_BRUTE || race == RC_INSECT) && damage < status_get_hp(bl) && pc_checkskill(sd, HT_POWER)) { - //TODO: This code was taken from Triple Blows,is this even how it should be? [Skotlex] - sc_start4(src,SC_COMBO,100,HT_POWER,bl->id,0,0,2000); - clif_combo_delay(src,2000); - } - break; - } - case TK_COUNTER: - { //bonus from SG_FRIEND [Komurka] - int level; - if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND))) - party_skill_check(sd, sd->status.party_id, TK_COUNTER,level); - } - break; - case SL_STIN: - case SL_STUN: - if (skilllv >= 7 && sd->sc.data[SC_SMA].timer == -1) - sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA, skilllv)); - break; - case GS_FULLBUSTER: - //Can't attack nor use items until skill's delay expires. [Skotlex] - sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick; - break; - } //Switch End - } - -//武器スキル?ここまで - switch(skillid){ - //Skills who's damage should't show any skill-animation. - case SM_MAGNUM: - case AS_SPLASHER: - case ASC_METEORASSAULT: - case GS_SPREADATTACK: - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); - break; - case KN_BRANDISHSPEAR: - { //Only display skill animation for skill's target. - struct unit_data *ud = unit_bl2ud(src); - if (ud && ud->skilltarget == bl->id) - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, type); - else - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); - break; - } - case PA_GOSPEL: //Should look like Holy Cross [Skotlex] - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5); - break; - - case NPC_SELFDESTRUCTION: - if(src->type==BL_PC) - dmg.blewcount = 10; - break; - case KN_AUTOCOUNTER: //Skills that need be passed as a normal attack for the client to display correctly. - case TF_DOUBLE: - case GS_CHAINACTION: - case SN_SHARPSHOOTING: - dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2); - break; - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - //Only show animation when hitting yourself. [Skotlex] - if (src!=bl) { - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); - break; - } - default: - dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, (skillid==0)? 5:type ); - } - - map_freeblock_lock(); - - if(damage > 0 && dmg.flag&BF_SKILL && tsd - && pc_checkskill(tsd,RG_PLAGIARISM) - && (!sc || sc->data[SC_PRESERVE].timer == -1) - && damage < tsd->status.hp) - { //Updated to not be able to copy skills if the blow will kill you. [Skotlex] - if ((!tsd->status.skill[skillid].id || tsd->status.skill[skillid].flag >= 13) && - can_copy(tsd,skillid)) // Split all the check into their own function [Aru] - { - //?に?んでいるスキルが れば該?スキルを消す - if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == 13){ - tsd->status.skill[tsd->cloneskill_id].id = 0; - tsd->status.skill[tsd->cloneskill_id].lv = 0; - tsd->status.skill[tsd->cloneskill_id].flag = 0; - } - tsd->cloneskill_id = skillid; - tsd->status.skill[skillid].id = skillid; - tsd->status.skill[skillid].lv = skilllv; - if ((lv = pc_checkskill(tsd,RG_PLAGIARISM)) < skilllv) - tsd->status.skill[skillid].lv = lv; - tsd->status.skill[skillid].flag = 13;//cloneskill flag - pc_setglobalreg(tsd, "CLONE_SKILL", tsd->cloneskill_id); - pc_setglobalreg(tsd, "CLONE_SKILL_LV", tsd->status.skill[skillid].lv); - clif_skillinfoblock(tsd); - } - } - if (skillid != WZ_HEAVENDRIVE && bl->type == BL_SKILL && damage > 0) { - struct skill_unit* su = (struct skill_unit*)bl; - if (su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP) - damage = 0; //Only Heaven's drive may damage traps. [Skotlex] - } - if (!dmg.amotion) { - battle_damage(src,bl,damage,dmg.dmotion,0); //Deal damage before knockback to allow stuff like firewall+storm gust combo. - if (dmg.dmg_lv == ATK_DEF || damage > 0) { - if (!status_isdead(bl)) - skill_additional_effect(src,bl,skillid,skilllv,attack_type,tick); - //Counter status effects [Skotlex] - skill_counter_additional_effect(dsrc,bl,skillid,skilllv,attack_type,tick); - } - } - - //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex] - if (dmg.blewcount > 0 && !status_isdead(bl)) - skill_blown(dsrc,bl,dmg.blewcount); - - //Delayed damage must be dealt after the knockback (it needs to know actual position of target) - if (dmg.amotion) - battle_delay_damage(tick+dmg.amotion,src,bl,attack_type,skillid,skilllv,damage,dmg.dmg_lv,dmg.dmotion,0); - - if(skillid == RG_INTIMIDATE && damage > 0 && !(status_get_mode(bl)&MD_BOSS)/* && !map_flag_gvg(src->m)*/) { - int s_lv = status_get_lv(src),t_lv = status_get_lv(bl); - int rate = 50 + skilllv * 5; - rate = rate + (s_lv - t_lv); - if(rand()%100 < rate) - skill_addtimerskill(src,tick + 800,bl->id,0,0,skillid,skilllv,0,flag); - } - - if(sd && dmg.flag&BF_WEAPON && src != bl && src == dsrc && damage > 0) { - if (battle_config.left_cardfix_to_right) - battle_drain(sd, tsd, dmg.damage, dmg.damage, status_get_race(bl), status_get_mode(bl)&MD_BOSS); - else - battle_drain(sd, tsd, dmg.damage, dmg.damage2, status_get_race(bl), status_get_mode(bl)&MD_BOSS); - } - - if (rdamage>0) { - if (dmg.amotion) - battle_delay_damage(tick+dmg.amotion,bl,src,0,0,0,rdamage,ATK_DEF,0,0); - else - battle_damage(bl,src,rdamage,0,0); - clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0); - //Use Reflect Shield to signal this kind of skill trigger. [Skotlex] - skill_additional_effect(bl,src,CR_REFLECTSHIELD, 1,BF_WEAPON,tick); - } - - if (!(flag & 1) && - ( - skillid == MG_COLDBOLT || skillid == MG_FIREBOLT || skillid == MG_LIGHTNINGBOLT - ) && - (sc = status_get_sc(src)) && - sc->count && sc->data[SC_DOUBLECAST].timer != -1 && - rand() % 100 < 40+10*sc->data[SC_DOUBLECAST].val1) - { -// skill_addtimerskill(src, tick + dmg.div_*dmg.amotion, bl->id, 0, 0, skillid, skilllv, BF_MAGIC, flag|1); - skill_addtimerskill(src, tick + dmg.amotion, bl->id, 0, 0, skillid, skilllv, BF_MAGIC, flag|1); - } - - map_freeblock_unlock(); - - return damage; /* ?ダ?を返す */ -} - -/*========================================== - * スキル範??U?用(map_foreachinareaから呼ばれる) - * flagについて?F16?i?を確認 - * MSB <- 00fTffff ->LSB - * T =タ?ゲット選?用(BCT_*) - * ffff=自由に使用可能 - * 0 =予約?B0に固定 - *------------------------------------------ - */ -static int skill_area_temp[8]; /* 一時???B必要なら使う?B */ -static int skill_unit_temp[8]; /* For storing skill_unit ids as players move in/out of them. [Skotlex] */ -static int skill_unit_index=0; //Well, yeah... am too lazy to pass pointers around :X -typedef int (*SkillFunc)(struct block_list *,struct block_list *,int,int,unsigned int,int); -int skill_area_sub( struct block_list *bl,va_list ap ) -{ - struct block_list *src; - int skill_id,skill_lv,flag; - unsigned int tick; - SkillFunc func; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - - src=va_arg(ap,struct block_list *); //ここではsrcの値を??ニしていないのでNULLチェックはしない - skill_id=va_arg(ap,int); - skill_lv=va_arg(ap,int); - tick=va_arg(ap,unsigned int); - flag=va_arg(ap,int); - func=va_arg(ap,SkillFunc); - - if(battle_check_target(src,bl,flag) > 0) - func(src,bl,skill_id,skill_lv,tick,flag); - return 0; -} - -static int skill_check_unit_range_sub( struct block_list *bl,va_list ap ) -{ - struct skill_unit *unit; - int skillid,g_skillid; - - unit = (struct skill_unit *)bl; - - if(bl->prev == NULL || bl->type != BL_SKILL) - return 0; - - if(!unit->alive) - return 0; - - skillid = va_arg(ap,int); - g_skillid = unit->group->skill_id; - - switch (skillid) - { - case MG_SAFETYWALL: - case AL_PNEUMA: - if(g_skillid != MG_SAFETYWALL && g_skillid != AL_PNEUMA) - return 0; - break; - case AL_WARP: - case HT_SKIDTRAP: - case HT_LANDMINE: - case HT_ANKLESNARE: - case HT_SHOCKWAVE: - case HT_SANDMAN: - case HT_FLASHER: - case HT_FREEZINGTRAP: - case HT_BLASTMINE: - case HT_CLAYMORETRAP: - case HT_TALKIEBOX: - case HP_BASILICA: - //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set) - if (skillid != g_skillid && !(skill_get_inf2(g_skillid)&INF2_TRAP) && g_skillid != AS_VENOMDUST) - return 0; - break; - default: //Avoid stacking with same kind of trap. [Skotlex] - if (g_skillid != skillid) - return 0; - break; - } - - return 1; -} - -static int skill_check_unit_range(struct block_list *bl,int x,int y,int skillid,int skilllv) -{ - //Non players do not check for the skill's splash-trigger area. - int range = bl->type==BL_PC?skill_get_unit_range(skillid, skilllv):0; - int layout_type = skill_get_unit_layout_type(skillid,skilllv); - if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) { - ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skillid); - return 0; - } - - range += layout_type; - return map_foreachinarea(skill_check_unit_range_sub,bl->m, - x-range,y-range,x+range,y+range,BL_SKILL,skillid); -} - -static int skill_check_unit_range2_sub( struct block_list *bl,va_list ap ) -{ - int skillid; - - if(bl->prev == NULL) - return 0; - - if(status_isdead(bl)) - return 0; - - skillid = va_arg(ap,int); - if (skillid==HP_BASILICA && bl->type==BL_PC) - return 0; - - if (skillid==AM_DEMONSTRATION && bl->type==BL_MOB && ((TBL_MOB*)bl)->class_ == MOBID_EMPERIUM) - return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex] - return 1; -} - -static int skill_check_unit_range2(struct block_list *bl, int x,int y,int skillid, int skilllv) -{ - int range, type; - - switch (skillid) { // to be expanded later - case WZ_ICEWALL: - range = 2; - break; - default: - { - int layout_type = skill_get_unit_layout_type(skillid,skilllv); - if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) { - ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skillid); - return 0; - } - range = skill_get_unit_range(skillid,skilllv) + layout_type; - } - break; - } - - // if the caster is a monster/NPC, only check for players - // otherwise just check characters - if (bl->type == BL_PC) - type = BL_CHAR; - else - type = BL_PC; - - return map_foreachinarea(skill_check_unit_range2_sub, bl->m, - x - range, y - range, x + range, y + range, - type, skillid); -} - -int skill_guildaura_sub (struct block_list *bl,va_list ap) -{ - struct map_session_data *sd; - int gid, id, *flag; - - nullpo_retr(0, sd = (struct map_session_data *)bl); - nullpo_retr(0, ap); - - id = va_arg(ap,int); - gid = va_arg(ap,int); - if (sd->status.guild_id != gid) - return 0; - nullpo_retr(0, flag = va_arg(ap,int *)); - - if (flag && *flag > 0) { - if (sd->sc.count && sd->sc.data[SC_GUILDAURA].timer != -1) { - if (sd->sc.data[SC_GUILDAURA].val4 != *flag) { - sd->sc.data[SC_GUILDAURA].val4 = *flag; - status_calc_pc (sd, 0); - } - return 0; - } - sc_start4(&sd->bl, SC_GUILDAURA,100, 1, id, 0, *flag, 0); - } - - return 0; -} - -/*========================================================================= - * 範?スキル使用???ャ分けここから - */ -/* ??ロの?をカウントする?B?iskill_area_temp[0]を?炎化しておくこと?j */ -int skill_area_sub_count(struct block_list *src,struct block_list *target,int skillid,int skilllv,unsigned int tick,int flag) -{ - if(skill_area_temp[0] < 0xffff) - skill_area_temp[0]++; - return 1; -} - -int skill_count_water(struct block_list *src,int range) -{ - int i,x,y,cnt = 0,size = range*2+1; - struct skill_unit *unit; - - for (i=0;ix+(i%size-range); - y = src->y+(i/size-range); - if (map_getcell(src->m,x,y,CELL_CHKWATER)) { - cnt++; - continue; - } - unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL); - if (unit) { - cnt++; - skill_delunit(unit); - } - } - return cnt; -} - -/*========================================== - * - *------------------------------------------ - */ -static int skill_timerskill(int tid, unsigned int tick, int id,int data ) -{ - struct block_list *src = map_id2bl(id),*target; - struct unit_data *ud = unit_bl2ud(src); - struct skill_timerskill *skl = NULL; - int range; - - nullpo_retr(0, src); - nullpo_retr(0, ud); - skl = ud->skilltimerskill[data]; - nullpo_retr(0, skl); - ud->skilltimerskill[data] = NULL; - - do { - if(src->prev == NULL) - break; - if(skl->target_id) { - target = map_id2bl(skl->target_id); - if(!target && skl->skill_id == RG_INTIMIDATE) - target = src; //Required since it has to warp. - if(target == NULL) - break; - if(target->prev == NULL) - break; - if(src->m != target->m) - break; - if(status_isdead(src)) - break; - if(status_isdead(target) && skl->skill_id != RG_INTIMIDATE && skl->skill_id != WZ_WATERBALL) - break; - - switch(skl->skill_id) { - case RG_INTIMIDATE: - if (unit_warp(src,-1,-1,-1,3) == 0) { - short x,y; - map_search_freecell(src, 0, &x, &y, 1, 1, 0); - if (target != src && !status_isdead(target)) - unit_warp(target, -1, x, y, 3); - } - break; - case BA_FROSTJOKE: /* 寒いジョ?ク */ - case DC_SCREAM: /* スクリ?ム */ - range= skill_get_splash(skl->skill_id, skl->skill_lv); - map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range, - skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick); - break; - - case WZ_WATERBALL: - if (!status_isdead(target)) - skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); - if (skl->type>1 && !status_isdead(target)) { - skill_addtimerskill(src,tick+150,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag); - } else { - struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_MAGICPOWER].timer != -1) - status_change_end(src,SC_MAGICPOWER,-1); - } - break; - default: - skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); - break; - } - } - else { - if(src->m != skl->map) - break; - switch(skl->skill_id) { - case WZ_METEOR: - if(skl->type >= 0) { - skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->type>>16,skl->type&0xFFFF,skl->flag); - clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick); - } - else - skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag); - break; - } - } - } while (0); - //Free skl now that it is no longer needed. - ers_free(skill_timer_ers, skl); - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -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 i; - struct unit_data *ud; - nullpo_retr(1, src); - ud = unit_bl2ud(src); - nullpo_retr(1, ud); - - for(i=0;iskilltimerskill[i]; i++); - if (i==MAX_SKILLTIMERSKILL) return 1; - - ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill); - ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i); - ud->skilltimerskill[i]->src_id = src->id; - ud->skilltimerskill[i]->target_id = target; - ud->skilltimerskill[i]->skill_id = skill_id; - ud->skilltimerskill[i]->skill_lv = skill_lv; - ud->skilltimerskill[i]->map = src->m; - ud->skilltimerskill[i]->x = x; - ud->skilltimerskill[i]->y = y; - ud->skilltimerskill[i]->type = type; - ud->skilltimerskill[i]->flag = flag; - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int skill_cleartimerskill(struct block_list *src) -{ - int i; - struct unit_data *ud; - nullpo_retr(0, src); - ud = unit_bl2ud(src); - nullpo_retr(0, ud); - - for(i=0;iskilltimerskill[i]) { - delete_timer(ud->skilltimerskill[i]->timer, skill_timerskill); - ers_free(skill_timer_ers, ud->skilltimerskill[i]); - ud->skilltimerskill[i]=NULL; - } - } - return 1; -} - -static int skill_reveal_trap( struct block_list *bl,va_list ap ) -{ - TBL_SKILL *su = (TBL_SKILL*)bl; - if (su->alive && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP) - { //Reveal trap. - //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex] - //clif_changetraplook(bl, su->group->unit_id); - clif_skill_setunit(su); - return 1; - } - return 0; -} - -/*========================================== - * スキル使用?i詠?・完了?AID指定?U?系?j - * ?iスパゲッティに向けて1?前?i?I(ダ?ポ)?j - *------------------------------------------ - */ -int skill_castend_damage_id (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag) -{ - struct map_session_data *sd = NULL, *tsd = NULL; - struct status_change *sc; - - if (skillid > 0 && skilllv <= 0) return 0; - - nullpo_retr(1, src); - nullpo_retr(1, bl); - - if (src->m != bl->m) - return 1; - - if (bl->prev == NULL) - return 1; - - if (src->type == BL_PC) - sd = (struct map_session_data *)src; - if (bl->type == BL_PC) - tsd = (struct map_session_data *)bl; - - if (status_isdead(src) || (src != bl && status_isdead(bl))) - return 1; - - if (skillid && skill_get_type(skillid) == BF_MAGIC && - !battle_config.gtb_pvp_only && status_isimmune(bl)) { - if (sd) clif_skill_fail(sd,skillid,0,0); - //GTB makes all targetted skills silently fail. - return 1; - } - - sc = status_get_sc(src); - if (sc && !sc->count) - sc = NULL; //Unneeded - - map_freeblock_lock(); - - switch(skillid) - { - /* ?器?U?系スキル */ - case SM_BASH: /* バッシュ */ - case MC_MAMMONITE: /* ?マ?ナイト */ - case TF_DOUBLE: - case AC_DOUBLE: /* ダブルストレイフィング */ - case AS_SONICBLOW: /* ソニックブ?? */ - case KN_PIERCE: /* ピア?ス */ - case KN_SPEARBOOMERANG: /* スピアブ??ラン */ - case KN_BRANDISHSPEAR: /* ブランディッシュスピア */ - case TF_POISON: /* インベナム */ - case TF_SPRINKLESAND: /* ?サまき */ - case AC_CHARGEARROW: /* チャ?ジア?? */ - case RG_RAID: /* サプライズアタック */ - case RG_INTIMIDATE: /* インティミデイト */ - case AM_ACIDTERROR: /* アシッドテラ? */ - case BA_MUSICALSTRIKE: /* ミュ?ジカルストライク */ - case DC_THROWARROW: /* 矢?ち */ - case BA_DISSONANCE: /* 不協和音 */ - case CR_HOLYCROSS: /* ホ?リ?ク?ス */ - case NPC_DARKCROSS: - case CR_SHIELDCHARGE: - case CR_SHIELDBOOMERANG: - /* 以下MOB?用 */ - /* ???U??ASP減?ュ?U??A遠距離?U??A防御無視?U??A多段?U? */ - case NPC_PIERCINGATT: - case NPC_MENTALBREAKER: - case NPC_RANGEATTACK: - case NPC_CRITICALSLASH: - case NPC_COMBOATTACK: - /* 必中?U??A毒?U??A暗??U??A沈??U??Aスタン?U? */ - case NPC_GUIDEDATTACK: - case NPC_POISON: - case NPC_BLINDATTACK: - case NPC_SILENCEATTACK: - case NPC_STUNATTACK: - /* ?ホ化?U??A呪い?U??A?眠?U??AランダムATK?U? */ - case NPC_PETRIFYATTACK: - case NPC_CURSEATTACK: - case NPC_SLEEPATTACK: - case NPC_RANDOMATTACK: - /* ???ォ?U??A地??ォ?U??A火??ォ?U??A風??ォ?U? */ - case NPC_WATERATTACK: - case NPC_GROUNDATTACK: - case NPC_FIREATTACK: - case NPC_WINDATTACK: - /* 毒??ォ?U??A?ケ??ォ?U??A闇??ォ?U??A念??ォ?U??ASP減?ュ?U? */ - case NPC_POISONATTACK: - case NPC_HOLYATTACK: - case NPC_DARKNESSATTACK: - case NPC_TELEKINESISATTACK: - case NPC_UNDEADATTACK: - case NPC_BREAKARMOR: - case NPC_BREAKWEAPON: - case NPC_BREAKHELM: - case NPC_BREAKSHIELD: - case LK_AURABLADE: /* オ?ラブレ?ド */ - case LK_SPIRALPIERCE: /* スパイラルピア?ス */ - case LK_HEADCRUSH: /* ヘッドクラッシュ */ - case LK_JOINTBEAT: /* ジョイントビ?ト */ - case CG_ARROWVULCAN: /* ア??バルカン */ - case HW_MAGICCRASHER: /* マジッククラッシャ? */ - case ASC_METEORASSAULT: /* ?テオアサルト */ - case ITM_TOMAHAWK: - case MO_TRIPLEATTACK: - case CH_CHAINCRUSH: /* 連柱崩? */ - case CH_TIGERFIST: /* 伏虎? */ - case PA_SHIELDCHAIN: // Shield Chain - case PA_SACRIFICE: // Sacrifice, Aru's style. - case WS_CARTTERMINATION: // Cart Termination - case AS_VENOMKNIFE: - case HT_PHANTASMIC: - case HT_POWER: - case TK_DOWNKICK: - case TK_COUNTER: - case ASC_BREAKER: - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - break; - - case MO_COMBOFINISH: - if (!(flag&1) && sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_MONK) - { //Becomes a splash attack when Soul Linked. - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - } else - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - break; - - case TK_STORMKICK: // Taekwon kicks [Dralnu] - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_attack_area, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - BF_WEAPON, src, src, skillid, skilllv, tick, flag, BCT_ENEMY); - break; - - case NJ_SHADOWJUMP: //[blackhole89] - case TK_JUMPKICK: - if (skillid == TK_JUMPKICK) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - if (unit_movepos(src, bl->x, bl->y, 0, 0)) - clif_slide(src,bl->x,bl->y); - break; - - case SN_SHARPSHOOTING: /* シャ?プシュ?ティング */ - // Does it stop if touch an obstacle? it shouldn't shoot trough walls - map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y, - skill_get_splash(skillid, skilllv),BL_CHAR, - BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY); // varargs - break; - - case MO_INVESTIGATE: /* ?勁 */ - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - if (sc && sc->data[SC_BLADESTOP].timer != -1) - status_change_end(src,SC_BLADESTOP,-1); - break; - - case RG_BACKSTAP: /* バックスタブ */ - { - int dir = map_calc_dir(src, bl->x, bl->y), t_dir = unit_getdir(bl); - if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) { - if (sc && sc->data[SC_HIDING].timer != -1) - status_change_end(src, SC_HIDING, -1); // ハイディング解? - skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); - dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest] - unit_setdir(bl,dir); - clif_changed_dir(bl); - } - else if (sd) - clif_skill_fail(sd,skillid,0,0); - } - break; - - case MO_FINGEROFFENSIVE: /* 指? */ - { - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - if (battle_config.finger_offensive_type && sd) { - int i; - for (i = 1; i < sd->spiritball_old; i++) - skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag); -// sd->canmove_tick = tick + (sd->spiritball_old - 1) * 200; Should be handled by the canmove delay on skill_cast_db [Skotlex] - } - if (sc && sc->data[SC_BLADESTOP].timer != -1) - status_change_end(src,SC_BLADESTOP,-1); - } - break; - - case MO_CHAINCOMBO: /* 連打?カ */ - { - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - if (sc && sc->data[SC_BLADESTOP].timer != -1) - status_change_end(src,SC_BLADESTOP,-1); - } - break; - - case KN_CHARGEATK: - case MO_EXTREMITYFIST: /* 阿?C羅覇鳳? */ - if (skillid == MO_EXTREMITYFIST && sc && sc->count) - { - if (sc->data[SC_EXPLOSIONSPIRITS].timer != -1) - status_change_end(src, SC_EXPLOSIONSPIRITS, -1); - if (sc->data[SC_BLADESTOP].timer != -1) - status_change_end(src,SC_BLADESTOP,-1); - } - if(!check_distance_bl(src, bl, 2)) { //Need to move to target. - int dx,dy; - - dx = bl->x - src->x; - dy = bl->y - src->y; - if(dx > 0) dx++; - else if(dx < 0) dx--; - if (dy > 0) dy++; - else if(dy < 0) dy--; - - if (skillid == KN_CHARGEATK) //Store distance in flag [Skotlex] - flag = distance_bl(src, bl); - - if (!unit_movepos(src, src->x+dx, src->y+dy, 1, 1)) { - if (sd) clif_skill_fail(sd,skillid,0,0); - break; - } - clif_slide(src,src->x,src->y); - if (skillid != MO_EXTREMITYFIST || battle_check_target(src, bl, BCT_ENEMY) > 0) //Check must be done here because EF should be broken this way.. [Skotlex] - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - else if (sd) - clif_skill_fail(sd,skillid,0,0); - } - else //Assume minimum distance of 1 for Charge. - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,skillid == KN_CHARGEATK?1:flag); - break; - - /* ?器系範??U?スキル */ - case AS_GRIMTOOTH: /* グリムトゥ?ス */ - case MC_CARTREVOLUTION: /* カ?トレヴォリュ?ション */ - case NPC_SPLASHATTACK: /* スプラッシュアタック */ - case AC_SHOWER: //Targetted skill implementation. - if(flag&1){ - /* 個別にダ??ジを?える */ - if(bl->id!=skill_area_temp[1]){ - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick, - 0x0500); - } - } else { - skill_area_temp[1]=bl->id; - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - //Skill-attack at the end in case it has knockback. [Skotlex] - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0); - } - break; - - case AS_SPLASHER: - if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by. - if (bl->id != skill_area_temp[1]) - skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); - else - skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0); - } else { - skill_area_temp[0] = 0; - skill_area_temp[1] = bl->id; - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count); - skill_area_temp[0]--; //Substract one, the original target shouldn't count. [Skotlex] - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, BCT_ENEMY|1, - skill_castend_damage_id); - } - break; - - - case SM_MAGNUM: - if(flag&1) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - else { - //If we get here, someone changed magnum to be a enemy targetted skill, - //so treat it as such. - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - //Initiate 10% of your damage becomes fire element. - clif_skill_nodamage (src,bl,skillid,skilllv,1); - sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skillid, skilllv)); - if (sd) skill_blockpc_start (sd, skillid, skill_get_time(skillid, skilllv)); - } - break; - - case KN_BOWLINGBASH: /* ボウリングバッシュ */ - if(flag&1){ - /* 個別にダ??ジを?える */ - if(bl->id!=skill_area_temp[1]) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500); - } else { - int i,c; /* 他?lから聞いた動きなので間違ってる可能?ォ大??率が?いっす?? */ - /* まずタ?[ゲットに?U撃を加える */ - c = skill_get_blewcount(skillid,skilllv); - if(map_flag_gvg(bl->m) || status_get_mexp(bl)) - c = 0; - for(i=0;i1) break; - } - clif_blown(bl); //Update target pos. - skill_area_temp[1]=bl->id; - /* その後タ?ゲット以外の範??の敵全?に??を?sう */ - map_foreachinrange(skill_area_sub,bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0); - } - break; - - case KN_SPEARSTAB: /* スピアスタブ */ - if(flag&1){ - /* 個別にダ??[ジを与える */ - if (bl->id==skill_area_temp[1]) - break; - if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500) && !status_get_mexp(bl)) - skill_blown(src,bl,skill_area_temp[2]); - } else { - int x=bl->x,y=bl->y,i,dir; - /* まずタ?[ゲットに?U撃を加える */ - dir = map_calc_dir(bl,src->x,src->y); - skill_area_temp[1] = bl->id; - skill_area_temp[2] = skill_get_blewcount(skillid,skilllv)|dir<<20; - if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0) && !status_get_mexp(bl)) - skill_blown(src,bl,skill_area_temp[2]); - for (i=0;i<4;i++) { - map_foreachincell(skill_area_sub,bl->m,x,y,BL_CHAR, - src,skillid,skilllv,tick,flag|BCT_ENEMY|1, - skill_castend_damage_id); - x += dirx[dir]; - y += diry[dir]; - } - } - break; - - case TK_TURNKICK: - case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex] - { - skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target. - if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0)) - map_foreachinrange(skill_area_sub,bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick,flag|BCT_ENEMY|1, - skill_castend_nodamage_id); - } - break; - case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex] - // clif_skill_nodamage(src,bl,skillid,skilllv,0); //Can't make this one display the correct attack animation delay :/ - clif_damage(src,bl,tick,status_get_amotion(src),0,0,1,4,0); //Displays MISS, but better than nothing :X - skill_addtimerskill(src, tick + 1000, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag); - break; - - case ALL_RESURRECTION: /* リザレクション */ - case PR_TURNUNDEAD: /* タ?ンアンデッド */ - if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); - break; - - /* 魔法系スキル */ - case MG_SOULSTRIKE: /* ソウルストライク */ - case NPC_DARKSTRIKE: /*闇ソウルストライク*/ - case MG_COLDBOLT: /* コ?[ルドボルト */ - case MG_FIREBOLT: /* ファイア?[ボルト */ - case MG_LIGHTNINGBOLT: /* ライトニングボルト */ - case WZ_EARTHSPIKE: /* ア?[ススパイク */ - case AL_HEAL: /* ヒ?[ル */ - case AL_HOLYLIGHT: /* ホ?[リ?[ライト */ - case WZ_JUPITEL: /* ユピテルサンダ?[ */ - case NPC_DARKTHUNDER: /*闇ユピテル*/ - case NPC_MAGICALATTACK: /* MOB:魔法打??U? */ - case PR_ASPERSIO: /* アスペルシオ */ - case MG_FROSTDIVER: /* フ?ストダイバ?[ */ - case WZ_SIGHTBLASTER: - case WZ_SIGHTRASHER: /* サイトラッシャ?[ */ - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); - break; - - case HVAN_CAPRICE: //[blackhole89] - { - int ran=rand()%4; - int sid; - switch(ran) - { - case 0: sid=MG_COLDBOLT; break; - case 1: sid=MG_FIREBOLT; break; - case 2: sid=MG_LIGHTNINGBOLT; break; - case 3: sid=WZ_EARTHSPIKE; break; - } - // if(sd && sd->hd) - // skill_attack(BF_MAGIC,(struct block_list*)sd->hd,(struct block_list*)sd->hd,bl,sid,skilllv,tick,flag); - // else if(sd) clif_skill_fail(sd,skillid,0,0); - skill_attack(BF_MAGIC,src,src,bl,sid,skilllv,tick,flag); - } - break; - case HFLI_MOON: - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - break; - - case WZ_WATERBALL: /* ウォ?タ?ボ?ル */ - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); - if (skilllv>1) { - int range = skilllv/2; - int cnt; - if (sd) - cnt = skill_count_water(src,range); - else { - range = 2*range+1; - cnt = range*range; - } - cnt--; - if (cnt > 0) - skill_addtimerskill(src,tick+150,bl->id,0,0, - skillid,skilllv,cnt,flag); - } else if (sd) //Eat up deluge tiles. - skill_count_water(src,0); - - break; - - case PR_BENEDICTIO: /* ?ケ??~福 */ - { //Should attack undead and demons. [Skotlex] - int race = status_get_race(bl); - if (battle_check_undead(race, status_get_elem_type(bl)) || race == RC_DEMON) - skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); - } - break; - - /* 魔法系範??U?スキル */ - case MG_NAPALMBEAT: /* ナパ?ムビ?ト */ - case MG_FIREBALL: /* ファイヤ?ボ?ル */ - if (flag & 1) { - /* 個別にダ??ジを?える */ - if (bl->id == skill_area_temp[1]) - break; - if(skillid == MG_FIREBALL) //Store distance. - skill_area_temp[0] = distance_blxy(bl, skill_area_temp[2], skill_area_temp[3]); - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, skill_area_temp[0]| 0x0500); - } else { - skill_area_temp[0]=0; - skill_area_temp[1]=bl->id; - switch (skillid) { - case MG_NAPALMBEAT: - /* ナパ?[ムビ?[トは分散ダ??[ジなので敵の?狽?狽ヲる */ - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick,flag|BCT_ENEMY, - skill_area_sub_count); - break; - case MG_FIREBALL: - skill_area_temp[2]=bl->x; - skill_area_temp[3]=bl->y; - break; - } - /* タ?[ゲットに?U撃を加える(スキルエフェクト表示) */ - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, skill_area_temp[0]); - /* タ?[ゲット以外の範囲内の敵全体に??を?sう */ - map_foreachinrange(skill_area_sub,bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - } - break; - - case HW_NAPALMVULCAN: // Fixed By SteelViruZ - if (flag & 1) { - if (bl->id != skill_area_temp[1]) - skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); - } else { - skill_area_temp[0] = 0; - skill_area_temp[1] = bl->id; - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, flag|BCT_ENEMY, - skill_area_sub_count); - skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - } - break; - - case SL_STIN: - case SL_STUN: - case SL_SMA: - if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { - status_change_start(src,SC_STUN,10000,skilllv,0,0,0,500,10); - clif_skill_fail(sd,skillid,0,0); - break; - } - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); - break; - - /* その他 */ - case HT_BLITZBEAT: /* ブリッツビ?ト */ - if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by. - skill_attack(BF_MISC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); - } else { - skill_area_temp[0] = 0; - skill_area_temp[1] = bl->id; - if (flag & 0xf00000) //Warning, 0x100000 is currently BCT_NEUTRAL, so don't mix it when asking for the enemy. [Skotlex] - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, BCT_ENEMY|1, - skill_castend_damage_id); - } - break; - - case NPC_DARKBREATH: - clif_emotion(src,7); - skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); - break; - - case SN_FALCONASSAULT: /* ファルコンアサルト */ - case PA_PRESSURE: /* プレッシャ? */ - case CR_ACIDDEMONSTRATION: // Acid Demonstration - case TF_THROWSTONE: /* ?ホ投げ */ - case NPC_SMOKING: /* スモ?キング */ - case NPC_SELFDESTRUCTION: /* 自爆 */ - skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); - break; - - // Celest - case PF_SOULBURN: - if (rand()%100 < (skilllv < 5 ? 30 + skilllv * 10 : 70)) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if (skilllv == 5) - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,0 ); - if (tsd) { - tsd->status.sp = 0; - clif_updatestatus(tsd,SP_SP); - } - } else { - clif_skill_nodamage(src,src,skillid,skilllv,1); - if (skilllv == 5) - skill_attack(BF_MAGIC,src,src,src,skillid,skilllv,tick,0 ); - if (sd) { - sd->status.sp = 0; - clif_updatestatus(sd,SP_SP); - } - } - if (sd) skill_blockpc_start (sd, skillid, (skilllv < 5 ? 10000: 15000)); - break; - - /* HP吸?/HP吸?魔法 */ - case NPC_BLOODDRAIN: - case NPC_ENERGYDRAIN: - { - int heal = skill_attack( (skillid == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC, - src, src, bl, skillid, skilllv, tick, flag); - if (heal > 0){ - clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1); - battle_heal(NULL, src, heal, 0, 0); - } - } - break; - - //Until they're at right position - gs_damage- [Vicious] - case GS_TRIPLEACTION: - case GS_MAGICALBULLET: - case GS_CRACKER: - case GS_TRACKING: - case GS_PIERCINGSHOT: - case GS_RAPIDSHOWER: - case GS_DUST: - case GS_FULLBUSTER: - case GS_FLING: - case NJ_SYURIKEN: - case NJ_KUNAI: - case NJ_HUUMA: - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - break; - case GS_BULLSEYE: - { - int race = status_get_race(bl); - if(race == RC_BRUTE || race == RC_DEMIHUMAN) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - else - clif_skill_fail(sd,skillid,0,0); - } - break; - case GS_DESPERADO: - case GS_SPREADATTACK: - if(flag&1) - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - else { - //If we get here, someone changed it to be a enemy targetted skill, - //so treat it as such. - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - } - break; - case NJ_ZENYNAGE: - if(sd->status.zeny < skilllv*1000) - clif_skill_fail(sd,skillid,5,0); - else - skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); - break; - case NJ_KASUMIKIRI: - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - sc_start(src,SC_HIDING,100,skilllv,skill_get_time(skillid,skilllv)); - break; - case NJ_KIRIKAGE: - status_change_end(src, SC_HIDING, -1); - skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); - break; - case NJ_KOUENKA: - case NJ_HYOUSENSOU: - case NJ_HYOUSYOURAKU: - case NJ_HUUJIN: - case NJ_RAIGEKISAI: - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); - break; - case NJ_KAMAITACHI: - // Does it stop if touch an obstacle? it shouldn't shoot trough walls - map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y, - skill_get_splash(skillid, skilllv),BL_CHAR, - BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY); // varargs - break; - //Not implemented yet [Vicious] - case GS_GROUNDDRIFT: - - //case NJ_SYURIKEN: - //case NJ_KUNAI: - //case NJ_HUUMA: - //case NJ_ZENYNAGE: - case NJ_TATAMIGAESHI: - //case NJ_KASUMIKIRI: - //case NJ_KIRIKAGE: - //case NJ_KOUENKA: - case NJ_KAENSIN: - //case NJ_HYOUSENSOU: - //case NJ_HYOUSYOURAKU: - //case NJ_HUUJIN: - //case NJ_RAIGEKISAI: - //case NJ_KAMAITACHI: - case NJ_ISSEN: - skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); - break; - - case 0: - if(sd) { - if (flag & 3){ - if (bl->id != skill_area_temp[1]) - skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0x0500); - } else { - skill_area_temp[1] = bl->id; - map_foreachinrange(skill_area_sub, bl, - sd->splash_range, BL_CHAR, - src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, - skill_castend_damage_id); - } - } - break; - - default: - ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n",skillid); - map_freeblock_unlock(); - return 1; - } - - map_freeblock_unlock(); - - if (sd && !(flag&1) && sd->state.arrow_atk) //Consume arrow on last invocation to this skill. - battle_consume_ammo(sd, skillid, skilllv); - return 0; -} - -/*========================================== - * スキル使用?i詠?・完了?AID指定支援系?j - *------------------------------------------ - */ -int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag) -{ - struct map_session_data *sd = NULL; - struct map_session_data *dstsd = NULL; - struct status_change *tsc; - struct mob_data *md = NULL; - struct mob_data *dstmd = NULL; - int i,type=-1; - - if(skillid > 0 && skilllv <= 0) return 0; // celest - - nullpo_retr(1, src); - nullpo_retr(1, bl); - - if (src->m != bl->m) - return 1; - - if (src->type == BL_PC) { - sd = (struct map_session_data *)src; - } else if (src->type == BL_MOB) { - md = (struct mob_data *)src; - } - - if (bl->type == BL_PC){ - dstsd = (struct map_session_data *)bl; - } else if (bl->type == BL_MOB){ - dstmd = (struct mob_data *)bl; - } - - if(bl->prev == NULL) - return 1; - if(status_isdead(src) && skillid != NPC_REBIRTH) - return 1; - if(status_isdead(bl) && skillid != NPC_REBIRTH && skillid != ALL_RESURRECTION && skillid != PR_REDEMPTIO) - return 1; - - //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex] - switch (skillid) { - case AL_HEAL: - case ALL_RESURRECTION: - case PR_ASPERSIO: - if (battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) { - if (battle_check_target(src, bl, BCT_ENEMY) < 1) { - //Offensive heal does not works on non-enemies. [Skotlex] - if (sd) clif_skill_fail(sd,skillid,0,0); - return 0; - } - if(!sd) { - //Prevent non-players from casting offensive heal. [Skotlex] - clif_emotion(src, 4); - return 0; - } - return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag); - } - break; - case NPC_SMOKING: //Since it is a self skill, this one ends here rather than in damage_id. [Skotlex] - return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag); - //These are actually ground placed. - case CR_GRANDCROSS: - case NPC_GRANDDARKNESS: - //Until they're at right position - gs_ground- [Vicious] - case GS_DESPERADO: - case NJ_KAENSIN: /*火炎陣*/ - case NJ_HYOUSYOURAKU: - case NJ_RAIGEKISAI: - return skill_castend_pos2(src,src->x,src->y,skillid,skilllv,tick,0); - } - - //Self skill with target changed? We assume these are offensive auto-select-target skills. [Skotlex] - //But only do this on the first call (flag&~1) - if (!(flag&1) && skill_get_inf(skillid)&INF_SELF_SKILL && src != bl && !(skill_get_nk(skillid)&NK_NO_DAMAGE)) - return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag); - - if (skillid > 0 && skillid < MAX_SKILL) - type = SkillStatusChangeTable[skillid]; - - tsc = status_get_sc(bl); - - map_freeblock_lock(); - switch(skillid) - { - case AL_HEAL: /* ヒ?ル */ - { - int heal = skill_calc_heal(src, skilllv); - int heal_get_jobexp; - int skill; - - if (skilllv > 10) - heal = 9999; //9999ヒ?[ル - if (status_isimmune(bl) || (dstmd && dstmd->class_ == MOBID_EMPERIUM)) - heal=0; /* ?金蟲カ?ド?iヒ?ル量0?j */ - if (sd) { - if ((skill = pc_checkskill(sd, HP_MEDITATIO)) > 0) // ?ディテイティオ - heal += heal * skill * 2 / 100; - if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id && - (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0) //自分も?象もPC、?象が自分のパ?トナ?、自分がスパノビ、自分が♀なら - heal = heal*2; //スパノビの嫁が旦那にヒ?ルすると2倍になる - } - - if (tsc && tsc->count && tsc->data[SC_KAITE].timer != -1 - && !(status_get_mode(src)&MD_BOSS) - ) { //Bounce back heal - if (--tsc->data[SC_KAITE].val2 <= 0) - status_change_end(bl, SC_KAITE, -1); - if (src == bl) heal=0; //When you try to heal yourself and you are under Kaite, the heal is voided. - clif_skill_nodamage (src, src, skillid, heal, 1); - heal_get_jobexp = battle_heal(NULL,src,heal,0,0); - } else { - clif_skill_nodamage (src, bl, skillid, heal, 1); - heal_get_jobexp = battle_heal(NULL,bl,heal,0,0); - } - - // JOB??値獲得 - if(sd && dstsd && heal > 0 && sd != dstsd && battle_config.heal_exp > 0){ - heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100; - if (heal_get_jobexp <= 0) - heal_get_jobexp = 1; - if (bl->type == BL_PC) // Give heal experience only when healing players [Harbin] - pc_gainexp (sd, 0, heal_get_jobexp); - } - } - break; - - case PR_REDEMPTIO: - if (sd && !(flag&1)) { - if (sd->status.party_id == 0) { - clif_skill_fail(sd,skillid,0,0); - break; - } - skill_area_temp[0] = 0; - party_foreachsamemap(skill_area_sub, - sd,skill_get_splash(skillid, skilllv), - src,skillid,skilllv,tick, flag|BCT_PARTY|1, - skill_castend_nodamage_id); - if (skill_area_temp[0] == 0) { - clif_skill_fail(sd,skillid,0,0); - break; - } - skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty... - if (skill_area_temp[0] > 0 && !map[src->m].flag.nopenalty) { //Apply penalty - sd->status.base_exp -= pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000; //0.2% penalty per each. - sd->status.job_exp -= pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000; - clif_updatestatus(sd,SP_BASEEXP); - clif_updatestatus(sd,SP_JOBEXP); - } - pc_heal(sd, 1-sd->status.hp, 1-sd->status.sp); - break; - } else if (dstsd && pc_isdead(dstsd) && flag&1) { //Revive - skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code. - skilllv = 3; //Resurrection level 3 is used - } else //Invalid target, skip resurrection. - break; - - case ALL_RESURRECTION: /* リザレクション */ - if(sd && map_flag_gvg(bl->m)) - { //No reviving in WoE grounds! - clif_skill_fail(sd,skillid,0,0); - break; - } - if(dstsd) { - int per = 0; - if (map[bl->m].flag.pvp && dstsd->pvp_point < 0) - break; /* PVPで復活不可能?態 */ - - if (pc_isdead(dstsd)) { /* 死亡判定 */ - clif_skill_nodamage(src,bl,ALL_RESURRECTION,skilllv,1); //Both Redemption and Res show this skill-animation. - switch(skilllv){ - case 1: per=10; break; - case 2: per=30; break; - case 3: per=50; break; - case 4: per=80; break; - } - dstsd->status.hp = dstsd->status.max_hp * per / 100; - if (dstsd->status.hp <= 0) dstsd->status.hp = 1; - if (dstsd->special_state.restart_full_recover) { /* オシリスカ?ド */ - dstsd->status.hp = dstsd->status.max_hp; - dstsd->status.sp = dstsd->status.max_sp; - } - pc_setstand(dstsd); - if(battle_config.pc_invincible_time > 0) - pc_setinvincibletimer(dstsd, battle_config.pc_invincible_time); - clif_updatestatus(dstsd, SP_HP); - clif_resurrection(bl, 1); - if(sd && sd != dstsd && battle_config.resurrection_exp > 0) { - int exp = 0,jexp = 0; - int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level; - if(lv > 0) { - exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); - if (exp < 1) exp = 1; - } - if(jlv > 0) { - jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); - if (jexp < 1) jexp = 1; - } - if(exp > 0 || jexp > 0) - pc_gainexp (sd, exp, jexp); - } - } - } - break; - - case AL_DECAGI: /* 速度減?ュ */ - clif_skill_nodamage (src, bl, skillid, skilllv, - sc_start(bl, type, - (40 + skilllv * 2 + (status_get_lv(src) + status_get_int(src))/5), - skilllv, skill_get_time(skillid,skilllv))); - break; - - case AL_CRUCIS: - if (flag & 1) { - if (battle_check_target (src, bl, BCT_ENEMY)) - sc_start(bl,type, - 23+skilllv*4 +status_get_lv(src) -status_get_lv(bl), - skilllv,0); - } else { - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, flag|BCT_ENEMY|1, - skill_castend_nodamage_id); - clif_skill_nodamage(src, bl, skillid, skilllv, 1); - } - break; - - case PR_LEXDIVINA: /* レックスディビ?ナ */ - if (tsc && tsc->count && tsc->data[type].timer != -1) { - status_change_end(bl,type, -1); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - } else - clif_skill_nodamage (src, bl, skillid, skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case SA_ABRACADABRA: - { - int abra_skillid = 0, abra_skilllv; - if (sd) - { //Crash-fix [Skotlex] - //require 1 yellow gemstone even with mistress card or Into the Abyss - if ((i = pc_search_inventory(sd, 715)) < 0 ) - { //bug fixed by Lupus (item pos can be 0, too!) - clif_skill_fail(sd,skillid,0,0); - break; - } - pc_delitem(sd, i, 1, 0); - } - do { - abra_skillid = rand() % MAX_SKILL_ABRA_DB; - if (skill_abra_db[abra_skillid].req_lv > skilllv || - rand()%10000 >= skill_abra_db[abra_skillid].per || //dbに基づくレベル?確率判定 - (abra_skillid >= NPC_PIERCINGATT && abra_skillid <= NPC_SUMMONMONSTER) || //NPCスキルはダ? - skill_get_unit_flag(abra_skillid) & UF_DANCE) //演奏スキルはダ? - abra_skillid = 0; // reset to get a new id - } while (abra_skillid == 0); - abra_skilllv = skill_get_max(abra_skillid) > skilllv ? skilllv : skill_get_max(abra_skillid); - clif_skill_nodamage (src, bl, skillid, skilllv, 1); - - if (sd) - { //Crash-protection against Abracadabra casting pets - sd->skillitem = abra_skillid; - sd->skillitemlv = abra_skilllv; - sd->state.abra_flag = 1; - clif_item_skill (sd, abra_skillid, abra_skilllv, "Abracadabra"); - } else - { // [Skotlex] - struct unit_data *ud = unit_bl2ud(src); - int inf = skill_get_inf(abra_skillid); - int target_id = 0; - if (!ud) break; - if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) { - if (src->type == BL_PET) - bl = (struct block_list*)((TBL_PET*)src)->msd; - if (!bl) bl = src; - unit_skilluse_id(src, bl->id, abra_skillid, abra_skilllv); - } else { //Assume offensive skills - if (ud->target) - target_id = ud->target; - else switch (src->type) { - case BL_MOB: - target_id = ((TBL_MOB*)src)->target_id; - break; - case BL_PET: - target_id = ((TBL_PET*)src)->target_id; - break; - } - if (!target_id) - break; - if (skill_get_casttype(abra_skillid) == CAST_GROUND) { - bl = map_id2bl(target_id); - if (!bl) bl = src; - unit_skilluse_pos(src, bl->x, bl->y, abra_skillid, abra_skilllv); - } else - unit_skilluse_id(src, target_id, abra_skillid, abra_skilllv); - } - } - } - break; - - case SA_COMA: - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time2(skillid,skilllv))); - break; - case SA_FULLRECOVERY: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if (status_isimmune(bl)) - break; - battle_heal(src, bl, status_get_max_hp(bl), dstsd?dstsd->status.max_sp:0,0); - break; - case SA_SUMMONMONSTER: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if (sd) mob_once_spawn(sd,map[src->m].name,src->x,src->y,"--ja--",-1,1,""); - break; - case SA_LEVELUP: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, pc_nextbaseexp(sd) * 10 / 100, 0); - break; - case SA_INSTANTDEATH: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - battle_damage(NULL,src,status_get_hp(src)-1,0,1); - break; - case SA_QUESTION: - case SA_GRAVITY: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - break; - case SA_CLASSCHANGE: - { - //クラスチェンジ用ボスモンスタ?ID - static int changeclass[]={1038,1039,1046,1059,1086,1087,1112,1115 - ,1157,1159,1190,1272,1312,1373,1492}; - int class_ = mob_random_class (changeclass,sizeof(changeclass)/sizeof(changeclass[0])); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(dstmd) mob_class_change(dstmd,class_); - } - break; - case SA_MONOCELL: - { - static int poringclass[]={1002}; - int class_ = mob_random_class (poringclass,sizeof(poringclass)/sizeof(poringclass[0])); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(dstmd) mob_class_change(dstmd,class_); - } - break; - case SA_DEATH: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - battle_damage(NULL,bl,status_get_max_hp(bl),0,1); - break; - case SA_REVERSEORCISH: - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid, skilllv))); - break; - case SA_FORTUNE: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(sd) pc_getzeny(sd,status_get_lv(bl)*100); - break; - case SA_TAMINGMONSTER: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if (sd && dstmd) { - for (i = 0; i < MAX_PET_DB; i++) { - if (dstmd->class_ == pet_db[i].class_) { - pet_catch_process1 (sd, dstmd->class_); - break; - } - } - } - break; - - case AL_INCAGI: /* 速度?加 */ - case AL_BLESSING: /* ブレッシング */ - case PR_SLOWPOISON: - case PR_IMPOSITIO: /* イムポシティオマヌス */ - case PR_LEXAETERNA: /* レックスエ?テルナ */ - case PR_SUFFRAGIUM: /* サフラギウム */ - case PR_BENEDICTIO: /* ?ケ??~福 */ - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case CR_PROVIDENCE: /* プ?ヴィデンス */ - if(sd && dstsd){ //Check they are not another crusader [Skotlex] - if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) { - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 1; - } - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case CG_MARIONETTE: /* マリオネットコント??ル */ - { - struct status_change *sc= status_get_sc(src); - int type2 = SC_MARIONETTE2; - - if(sc && tsc){ - if (sc->data[type].timer == -1 && tsc->data[type2].timer == -1) { - sc_start4(src,type,100,skilllv,0,bl->id,0,skill_get_time(skillid,skilllv)); - sc_start4(bl,type2,100,skilllv,0,src->id,0,skill_get_time(skillid,skilllv)); - clif_marionette(src, bl); - } - else if (sc->data[type].timer != -1 && tsc->data[type2].timer != -1 && - sc->data[type].val3 == bl->id && tsc->data[type2].val3 == src->id) { - status_change_end(src, type, -1); - status_change_end(bl, type2, -1); - clif_marionette(src, 0); - } - else { - if (sd) clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 1; - } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - } - break; - - case RG_CLOSECONFINE: - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start4(bl,type,100,skilllv,src->id,0,0,skill_get_time(skillid,skilllv))); - break; - case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris] - case SA_FROSTWEAPON: - case SA_LIGHTNINGLOADER: - case SA_SEISMICWEAPON: - if (dstsd) { - if(dstsd->status.weapon == W_FIST || - (dstsd->sc.count && dstsd->sc.data[type].timer == -1 && - ( //Allow re-enchanting to lenghten time. [Skotlex] - dstsd->sc.data[SC_FIREWEAPON].timer != -1 || - dstsd->sc.data[SC_WATERWEAPON].timer != -1 || - dstsd->sc.data[SC_WINDWEAPON].timer != -1 || - dstsd->sc.data[SC_EARTHWEAPON].timer != -1 || - dstsd->sc.data[SC_SHADOWWEAPON].timer != -1 || - dstsd->sc.data[SC_GHOSTWEAPON].timer != -1 || - dstsd->sc.data[SC_ENCPOISON].timer != -1 - )) - ) { - if (sd) clif_skill_fail(sd,skillid,0,0); - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - } - } - if (sd) { - int i = pc_search_inventory (sd, skill_db[skillid].itemid[0]); - if(i < 0 || sd->status.inventory[i].amount < skill_db[skillid].amount[0]) { - clif_skill_fail(sd,skillid,0,0); - break; - } - pc_delitem(sd, i, skill_db[skillid].amount[0], 0); - } - // 100% success rate at lv4 & 5, but lasts longer at lv5 - i = skilllv <4?(60+skilllv*10):100; - i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - if(!i) { - if (sd) clif_skill_fail(sd,skillid,0,0); - if (skill_break_equip(bl, EQP_WEAPON, 10000, BCT_PARTY) && - sd && sd != dstsd) - clif_displaymessage(sd->fd,"You broke target's weapon"); - } - clif_skill_nodamage(src,bl,skillid,skilllv,i); - break; - - case PR_ASPERSIO: /* アスペルシオ */ - if (sd && dstmd) { - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case TK_SEVENWIND: - switch(skilllv){ - case 1: - type=SC_EARTHWEAPON; - break; - case 2: - type=SC_WINDWEAPON; - break; - case 3: - type=SC_WATERWEAPON; - break; - case 4: - type=SC_FIREWEAPON; - break; - case 5: - type=SC_GHOSTWEAPON; - break; - case 6: - type=SC_SHADOWWEAPON; - break; - case 7: - type=SC_ASPERSIO; - break; - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case PR_KYRIE: /* キリエエレイソン */ - clif_skill_nodamage(bl,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - //Passive Magnum, should had been casted on yourself. - case SM_MAGNUM: - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - //Initiate 10% of your damage becomes fire element. - clif_skill_nodamage (src,src,skillid,skilllv,1); - sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skillid, skilllv)); - if (sd) skill_blockpc_start (sd, skillid, skill_get_time(skillid, skilllv)); - break; - case LK_BERSERK: /* バ?サ?ク */ - case KN_AUTOCOUNTER: /* オ?トカウンタ? */ - case KN_TWOHANDQUICKEN: /* ツ?ハンドクイッケン */ - case KN_ONEHAND: - case CR_SPEARQUICKEN: /* スピアクイッケン */ - case CR_REFLECTSHIELD: - case AS_POISONREACT: /* ポイズンリアクト */ - case MC_LOUD: /* ラウドボイス */ - case MG_ENERGYCOAT: /* エナジ?コ?ト */ - case MG_SIGHT: /* サイト */ - case AL_RUWACH: /* ルアフ */ - case MO_EXPLOSIONSPIRITS: // 爆裂波動 - case MO_STEELBODY: // 金? - case MO_BLADESTOP: // 白?n取り - case LK_AURABLADE: /* オ?ラブレ?ド */ - case LK_PARRYING: /* パリイング */ - case LK_CONCENTRATION: /* コンセントレ?ション */ - case WS_CARTBOOST: /* カ?トブ?スト */ - case SN_SIGHT: /* トゥル?サイト */ - case WS_MELTDOWN: /* ?ルトダウン */ - case WS_OVERTHRUSTMAX: // Overthrust Max [Celest] - case ST_REJECTSWORD: /* リジェクトソ?ド */ - case HW_MAGICPOWER: /* 魔法力?? */ - case PF_MEMORIZE: /* ?モライズ */ - case PA_SACRIFICE: - case ASC_EDP: // [Celest] - case NPC_STOP: - case WZ_SIGHTBLASTER: - case SG_SUN_COMFORT: - case SG_MOON_COMFORT: - case SG_STAR_COMFORT: - case NPC_HALLUCINATION: - case HP_ASSUMPTIO: - case GS_MADNESSCANCEL: - case GS_ADJUSTMENT: - case GS_INCREASING: - case GS_CRACKER: - case GS_GROUNDDRIFT: - case NJ_TATAMIGAESHI: - case NJ_KASUMIKIRI: - case NJ_UTSUSEMI: - case NJ_BUNSINJYUTSU: - case NJ_NEN: - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case SG_SUN_WARM: - case SG_MOON_WARM: - case SG_STAR_WARM: - { - struct skill_unit_group *sg; - if (!tsc) break; - sg = skill_unitsetting(bl,skillid,skilllv,src->x,src->y,0); - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start4(bl,type,100,skilllv,0,0,(int)sg,skill_get_time(skillid,skilllv))); - break; - } - - case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if (sd && battle_config.player_skill_partner_check && - (!battle_config.gm_skilluncond || pc_isGM(sd) < battle_config.gm_skilluncond)) { - skill_check_pc_partner(sd, skillid, &skilllv, 1, 1); - } else - skill_moonlit(bl, NULL, skilllv); //The knockback must be invoked before starting the effect which places down the map cells. [Skotlex] - - break; -/* Was modified to only affect targetted char. [Skotlex] - case HP_ASSUMPTIO: - if (flag&1) - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - else - { - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_PC, - src, skillid, skilllv, tick, flag|BCT_ALL|1, - skill_castend_nodamage_id); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; -*/ - case SM_ENDURE: /* インデュア */ - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - if (sd) - skill_blockpc_start (sd, skillid, skill_get_time2(skillid,skilllv)); - break; - - case AS_ENCHANTPOISON: // Prevent spamming [Valaris] - if (sd && dstsd && dstsd->sc.count) { - if(dstsd->sc.data[SC_FIREWEAPON].timer != -1 || - dstsd->sc.data[SC_WATERWEAPON].timer != -1 || - dstsd->sc.data[SC_WINDWEAPON].timer != -1 || - dstsd->sc.data[SC_EARTHWEAPON].timer != -1 || - dstsd->sc.data[SC_SHADOWWEAPON].timer != -1 || - dstsd->sc.data[SC_GHOSTWEAPON].timer != -1 - // dstsd->sc.data[SC_ENCPOISON].timer != -1 //People say you should be able to recast to lengthen the timer. [Skotlex] - ) { - clif_skill_nodamage(src,bl,skillid,skilllv,0); - clif_skill_fail(sd,skillid,0,0); - break; - } - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case LK_TENSIONRELAX: /* テンションリラックス */ - if (sd) { - pc_setsit(sd); - clif_sitting(sd); - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - break; - - case MC_CHANGECART: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - break; - - case TK_MISSION: - if (sd) { - int id; - if (sd->mission_mobid && (sd->mission_count || rand()%100)) { //Cannot change target when already have one - clif_mission_mob(sd, sd->mission_mobid, sd->mission_count); - clif_skill_fail(sd,skillid,0,0); - break; - } - id = mob_get_random_id(0,0, sd->status.base_level); - if (!id) { - clif_skill_fail(sd,skillid,0,0); - break; - } - sd->mission_mobid = id; - sd->mission_count = 0; - pc_setglobalreg(sd,"TK_MISSION_ID", id); - clif_mission_mob(sd, id, 0); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; - - case AC_CONCENTRATION: /* ?W中力向? */ - { - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - map_foreachinrange( status_change_timer_sub, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - src,status_get_sc(src),type,tick); - } - break; - - case SM_PROVOKE: /* プ?ボック */ - /* MVPmobと不死には?かない */ - if((status_get_mode(bl)&MD_BOSS) || battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) { //不死には?かない - map_freeblock_unlock(); - return 1; - } - //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex] - clif_skill_nodamage(src,bl,skillid,skilllv, - (i=sc_start(bl,type, - 50 +3*skilllv +status_get_lv(src) -status_get_lv(bl), - skilllv,skill_get_time(skillid,skilllv)))); - if (!i) - { - if (sd) - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 0; - } - unit_skillcastcancel(bl, 2); - - if(tsc && tsc->count){ - if(tsc->data[SC_FREEZE].timer!=-1) - status_change_end(bl,SC_FREEZE,-1); - if(tsc->data[SC_STONE].timer!=-1 && tsc->data[SC_STONE].val2==0) - status_change_end(bl,SC_STONE,-1); - if(tsc->data[SC_SLEEP].timer!=-1) - status_change_end(bl,SC_SLEEP,-1); - } - - if(dstmd) { - dstmd->state.provoke_flag = src->id; - mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv)); - } - break; - - case CR_DEVOTION: /* ディボ?ション */ - if(sd && dstsd) - { - //??カや養子の??の元の?E業を算?oする - - int lv = sd->status.base_level - dstsd->status.base_level; - if (lv < 0) lv = -lv; - if (lv > battle_config.devotion_level_difference || - (dstsd->sc.data[type].timer != -1 && dstsd->sc.data[type].val1 != src->id) || //Avoid overriding [Skotlex] - (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) { - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 1; - } - //Look for an empty slot (or reuse in case you cast it twice in the same char. [Skotlex] - for (i = 0; i < skilllv && i < 5 && sd->devotion[i]!=bl->id && sd->devotion[i]; i++); - if (i == skilllv) - { - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 1; - } - sd->devotion[i] = bl->id; - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start4(bl,type,100,src->id,i,skill_get_range2(src,skillid,skilllv),skill_get_time2(skillid, skilllv),1000)); - clif_devotion(sd); - } - else - if (sd) - clif_skill_fail(sd,skillid,0,0); - break; - - case MO_CALLSPIRITS: // ?功 - if(sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - pc_addspiritball(sd,skill_get_time(skillid,skilllv),skilllv); - } - break; - - case CH_SOULCOLLECT: // 狂?功 - if(sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - for (i = 0; i < 5; i++) - pc_addspiritball(sd,skill_get_time(skillid,skilllv),5); - } - break; - - case MO_KITRANSLATION: - if(dstsd && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) { - pc_addspiritball(dstsd,skill_get_time(skillid,skilllv),5); - } - break; - - case TK_TURNKICK: - case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex] - if (skill_area_temp[1] != bl->id) { - skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)); - skill_additional_effect(src,bl,skillid,skilllv,BF_MISC,tick); //Use Misc rather than weapon to signal passive pushback - } - break; - - case MO_ABSORBSPIRITS: // ?奪 - i = 0; - if (dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) - { // split the if for readability, and included gunslingers in the check so that their coins cannot be removed [Reddozen] - i = dstsd->spiritball * 10; - pc_delspiritball(dstsd,dstsd->spiritball,0); - } else if (dstmd && !(status_get_mode(bl)&MD_BOSS) && rand() % 100 < 20) - { // check if target is a monster and not a Boss, for the 20% chance to absorb 2 SP per monster's level [Reddozen] - i = 2 * dstmd->db->lv; - mob_target(dstmd,src,0); - } - if (sd){ - if (i > 0x7FFF) - i = 0x7FFF; - if (sd->status.sp + i > sd->status.max_sp) - i = sd->status.max_sp - sd->status.sp; - if (i) { - sd->status.sp += i; - clif_heal(sd->fd,SP_SP,i); - } - } - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - - case AC_MAKINGARROW: /* 矢??ャ */ - if(sd) { - clif_arrow_create_list(sd); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; - - case AM_PHARMACY: /* ポ?ション??ャ */ - if(sd) { - clif_skill_produce_mix_list(sd,22); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; - - case SA_CREATECON: - if(sd) { - clif_skill_produce_mix_list(sd,23); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; - - case BS_HAMMERFALL: /* ハンマ?フォ?ル */ - if(dstsd && dstsd->special_state.no_weapon_damage) { - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,SC_STUN,(20 + 10 * skilllv),skilllv,skill_get_time2(skillid,skilllv))); - break; - case RG_RAID: /* サプライズアタック */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - status_change_end(src, SC_HIDING, -1); // ハイディング解? - break; - - case ASC_METEORASSAULT: /* ?テオアサルト */ - case GS_SPREADATTACK: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - break; - - case KN_BRANDISHSPEAR: /*ブランディッシュスピア*/ - { - int c,n=4,ar; - int dir = map_calc_dir(src,bl->x,bl->y); - struct square tc; - int x=bl->x,y=bl->y; - ar=skilllv/3; - skill_brandishspear_first(&tc,dir,x,y); - skill_brandishspear_dir(&tc,dir,4); - /* 範?C */ - if(skilllv == 10){ - for(c=1;c<4;c++){ - map_foreachincell(skill_area_sub, - bl->m,tc.val1[c],tc.val2[c],BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|n, - skill_castend_damage_id); - } - } - /* 範?BA */ - if(skilllv > 6){ - skill_brandishspear_dir(&tc,dir,-1); - n--; - }else{ - skill_brandishspear_dir(&tc,dir,-2); - n-=2; - } - - if(skilllv > 3){ - for(c=0;c<5;c++){ - map_foreachincell(skill_area_sub, - bl->m,tc.val1[c],tc.val2[c],BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|n, - skill_castend_damage_id); - if(skilllv > 6 && n==3 && c==4){ - skill_brandishspear_dir(&tc,dir,-1); - n--;c=-1; - } - } - } - /* 範?@ */ - for(c=0;c<10;c++){ - if(c==0||c==5) skill_brandishspear_dir(&tc,dir,-1); - map_foreachincell(skill_area_sub, - bl->m,tc.val1[c%5],tc.val2[c%5],BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - } - } - break; - - case WZ_SIGHTRASHER: - //Passive side of the attack. - status_change_end(src,SC_SIGHT,-1); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub,src, - skill_get_splash(skillid, skilllv),BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - break; - - case WZ_FROSTNOVA: - map_foreachinrange(skill_attack_area, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - BF_MAGIC, src, src, skillid, skilllv, tick, flag, BCT_ENEMY); - break; - - case NPC_SELFDESTRUCTION: - clif_skill_nodamage(src, src, skillid, -1, 1); - map_foreachinrange(skill_area_sub, bl, - skill_get_splash(skillid, skilllv), BL_CHAR, - src, skillid, skilllv, tick, flag|BCT_ENEMY, - skill_castend_damage_id); - battle_damage(src, src, status_get_max_hp(src),0,1); - break; - - /* パ?ティスキル */ - case AL_ANGELUS: /* エンジェラス */ - case PR_MAGNIFICAT: /* マグニフィカ?ト */ - case PR_GLORIA: /* グ?リア */ - case SN_WINDWALK: /* ウインドウォ?ク */ - if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { - clif_skill_nodamage(bl,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - } else if (sd) { - /* パ?ティ全?への?? */ - party_foreachsamemap (skill_area_sub, - sd,skill_get_splash(skillid, skilllv), - src,skillid,skilllv,tick, flag|BCT_PARTY|1, - skill_castend_nodamage_id); - } - break; - - case BS_ADRENALINE: /* アドレナリンラッシュ */ - case BS_ADRENALINE2: - case BS_WEAPONPERFECT: /* ウェポンパ?フェクション */ - case BS_OVERTHRUST: /* オ?バ?トラスト */ - if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { - /* 個別の?? */ - clif_skill_nodamage(bl,bl,skillid,skilllv, - sc_start4(bl,type,100,skilllv,(src == bl)? 1:0,0,0,skill_get_time(skillid,skilllv))); - } else if (sd) { - /* パ?ティ全?への?? */ - party_foreachsamemap(skill_area_sub, - sd,skill_get_splash(skillid, skilllv), - src,skillid,skilllv,tick, flag|BCT_PARTY|1, - skill_castend_nodamage_id); - } - break; - - case BS_MAXIMIZE: - case NV_TRICKDEAD: - case CR_DEFENDER: - case CR_AUTOGUARD: - case TK_READYSTORM: - case TK_READYDOWN: - case TK_READYTURN: - case TK_READYCOUNTER: - case TK_DODGE: - case CR_SHRINK: - case ST_PRESERVE: - case SG_FUSION: - case GS_GATLINGFEVER: - if (tsc && tsc->data[type].timer != -1) - i = status_change_end(bl, type, -1); - else - i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - clif_skill_nodamage(src,bl,skillid,skilllv,i); - break; - case SL_KAITE: - case SL_KAAHI: - case SL_KAIZEL: - case SL_KAUPE: - if (sd) { - if (!dstsd || !( - (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_SOULLINKER) || - (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER || - dstsd->char_id == sd->char_id || - dstsd->char_id == sd->status.partner_id || - dstsd->char_id == sd->status.child - )) { - status_change_start(src,SC_STUN,10000,skilllv,0,0,0,500,8); - clif_skill_fail(sd,skillid,0,0); - break; - } - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid, skilllv))); - break; - case SM_AUTOBERSERK: // Celest - if (tsc && tsc->data[type].timer != -1) - i = status_change_end(bl, type, -1); - else - i = sc_start(bl,type,100,skilllv,0); - clif_skill_nodamage(src,bl,skillid,skilllv,i); - break; - case TF_HIDING: /* ハイディング */ - case ST_CHASEWALK: /* ハイディング */ - if (tsc && tsc->data[type].timer != -1) - i = status_change_end(bl, type, -1); - else - i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - clif_skill_nodamage(src,bl,skillid,-1,i); // Don't display the skill name as it is a hiding skill - break; - case TK_RUN: - if (tsc && tsc->data[type].timer != -1) - i = status_change_end(bl, type, -1); - else - i = sc_start4(bl,type,100,skilllv,unit_getdir(bl),0,0,0); -// If the client receives a skill-use packet inmediately before -// a walkok packet, it will discard the walk packet! [Skotlex] -// clif_skill_nodamage(src,bl,skillid,skilllv,i); - break; - case AS_CLOAKING: /* ク??キング */ - if(tsc && tsc->data[type].timer!=-1 ) - /* 解?怩キる */ - i = status_change_end(bl, type, -1); - else - i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - clif_skill_nodamage(src,bl,skillid,-1,i); - if (!i && sd) - clif_skill_fail(sd,skillid,0,0); - break; - - /* ?地スキル */ - case BD_LULLABY: /* 子守唄 */ - case BD_RICHMANKIM: /* ニヨルドの宴 */ - case BD_ETERNALCHAOS: /* 永遠の?ャ沌 */ - case BD_DRUMBATTLEFIELD: /* ?太鼓の響き */ - case BD_RINGNIBELUNGEN: /* ニ?ベルングの指輪 */ - case BD_ROKISWEIL: /* ?キの叫び */ - case BD_INTOABYSS: /* ?[淵の中に */ - case BD_SIEGFRIED: /* 不死?gのジ?クフリ?ド */ - case BA_DISSONANCE: /* 不協和音 */ - case BA_POEMBRAGI: /* ブラギの? */ - case BA_WHISTLE: /* 口笛 */ - case BA_ASSASSINCROSS: /* 夕陽のアサシンク?ス */ - case BA_APPLEIDUN: /* イドゥンの林檎 */ - case DC_UGLYDANCE: /* 自分?沁閧ネダンス */ - case DC_HUMMING: /* ハミング */ - case DC_DONTFORGETME: /* 私を忘れないで?c */ - case DC_FORTUNEKISS: /* ?K運のキス */ - case DC_SERVICEFORYOU: /* サ?ビスフォ?ユ? */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); - break; - - case HP_BASILICA: /* バジリカ */ - case CG_HERMODE: // Wand of Hermod - { - struct skill_unit_group *sg; - unit_stop_walking(src,1); - skill_clear_unitgroup(src); - sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); - if(skillid == CG_HERMODE) - i = sc_start4(src,SC_DANCING,100, - skillid,0,0,sg->group_id,skill_get_time(skillid,skilllv)); - else - i = sc_start4(src,type,100, - skilllv,0,BCT_SELF,sg->group_id, - skill_get_time(skillid,skilllv)); - clif_skill_nodamage(src,bl,skillid,skilllv,i); - } - break; - - case PA_GOSPEL: /* ゴスペル */ - if (!tsc) break; - if (tsc->data[type].timer != -1 && tsc->data[type].val4 == BCT_SELF) { - i = status_change_end(bl,SC_GOSPEL,-1); - } else { - struct skill_unit_group *sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); - if (tsc->data[type].timer != -1) - status_change_end(bl,type,-1); //Was under someone else's Gospel. [Skotlex] - i = sc_start4(bl,type,100,skilllv,0,(int)sg,BCT_SELF,skill_get_time(skillid,skilllv)); - } - clif_skill_nodamage(src,bl,skillid,skilllv,i); - break; - - case BD_ADAPTATION: /* アドリブ */ - if(tsc && tsc->data[SC_DANCING].timer!=-1){ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - skill_stop_dancing(bl); - } - break; - - case BA_FROSTJOKE: /* 寒いジョ?ク */ - case DC_SCREAM: /* スクリ?ム */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skillid,skilllv,0,flag); - if (md) { // Mobは?れないから?Aスキル名を叫ばせてみる - char temp[128]; - if (strlen(md->name) + strlen(skill_db[skillid].desc) > 120) - break; //Message won't fit on buffer. [Skotlex] - sprintf(temp,"%s : %s !!",md->name,skill_db[skillid].desc); - clif_message(&md->bl,temp); - } - break; - - - case BA_PANGVOICE://パンボイス - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,SC_CONFUSION,50,7,skill_get_time(skillid,skilllv))); - break; - - case DC_WINKCHARM://魅惑のウィンク - if(dstsd){ - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,SC_CONFUSION,30,7,skill_get_time2(skillid,skilllv))); - }else if(dstmd) - { - int race = status_get_race(bl); - if(status_get_lv(src)>status_get_lv(bl) && (race == RC_DEMON || race == RC_DEMIHUMAN || race == RC_ANGEL)) { - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,70,skilllv,skill_get_time(skillid,skilllv))); - } else{ - clif_skill_nodamage(src,bl,skillid,skilllv,0); - if(sd) - clif_skill_fail(sd,skillid,0,0); - } - } - break; - - case TF_STEAL: // スティ?ル - if(sd) { - if(pc_steal_item(sd,bl)) - clif_skill_nodamage(src,bl,skillid,skilllv,1); - else - clif_skill_fail(sd,skillid,0x0a,0); - } - break; - - case RG_STEALCOIN: // スティ?ルコイン - if(sd) { - if(pc_steal_coin(sd,bl)) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - mob_target((struct mob_data *)bl,src,skill_get_range2(src,skillid,skilllv)); - } - else - clif_skill_fail(sd,skillid,0,0); - } - break; - - case MG_STONECURSE: /* スト?ンカ?ス */ - { - if (status_get_mode(bl)&MD_BOSS) { - if (sd) clif_skill_fail(sd,skillid,0,0); - break; - } - if(status_isimmune(bl) || !tsc) - break; - if (dstmd) - mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv)); - - if (tsc->data[SC_STONE].timer != -1) { - status_change_end(bl,SC_STONE,-1); - if (sd) clif_skill_fail(sd,skillid,0,0); - break; - } - if (sc_start(bl,SC_STONE,(skilllv*4+20),skilllv,skill_get_time2(skillid,skilllv))) - clif_skill_nodamage(src,bl,skillid,skilllv,1); - else if(sd) { - clif_skill_fail(sd,skillid,0,0); - // Level 6-10 doesn't consume a red gem if it fails [celest] - if (skilllv > 5) break; - } - if (sd) { - if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_WIZARD) - break; //Do not delete the gemstone. - if ((i=pc_search_inventory(sd, skill_db[skillid].itemid[0])) >= 0 ) - pc_delitem(sd, i, skill_db[skillid].amount[0], 0); - } - } - break; - - case NV_FIRSTAID: /* ?急手? */ - clif_skill_nodamage(src,bl,skillid,5,1); - battle_heal(NULL,bl,5,0,0); - break; - - case AL_CURE: /* キュア? */ - if(status_isimmune(bl)) { - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - } - status_change_end(bl, SC_SILENCE , -1 ); - status_change_end(bl, SC_BLIND , -1 ); - status_change_end(bl, SC_CONFUSION, -1 ); - if( battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) - sc_start(bl, SC_CONFUSION,100,1,skill_get_time2(skillid, skilllv)); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - break; - - case TF_DETOXIFY: /* 解毒 */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - status_change_end(bl, SC_POISON , -1 ); - status_change_end(bl, SC_DPOISON , -1 ); - break; - - case PR_STRECOVERY: /* リカバリ? */ - if(status_isimmune(bl)) { - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - } - status_change_end(bl, SC_FREEZE , -1 ); - status_change_end(bl, SC_STONE , -1 ); - status_change_end(bl, SC_SLEEP , -1 ); - status_change_end(bl, SC_STUN , -1 ); - //Is this equation really right? It looks so... special. - if( battle_check_undead(status_get_race(bl),status_get_elem_type(bl)) ){//アンデッドなら暗闇?果 - status_change_start(bl, SC_BLIND, - 100*(100-(status_get_int(bl)/2+status_get_vit(bl)/3+status_get_luk(bl)/10)), - 1,0,0,0, - skill_get_time2(skillid, skilllv) * (100-(status_get_int(bl)+status_get_vit(bl))/2)/100,10); - } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(dstmd) - mob_unlocktarget(dstmd,tick); - break; - - case WZ_ESTIMATION: /* モンスタ??報 */ - if(sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - clif_skill_estimation((struct map_session_data *)src,bl); - } - break; - - case BS_REPAIRWEAPON: /* ?器?C? */ - if(sd && dstsd) - clif_item_repair_list(sd,dstsd); - break; - - case MC_IDENTIFY: /* アイテム鑑定 */ - if(sd) - clif_item_identify_list(sd); - break; - - // Weapon Refining [Celest] - case WS_WEAPONREFINE: - if(sd) - clif_item_refine_list(sd); - break; - - case MC_VENDING: /* 露店開?ン */ - if(sd) - { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex] - if ( pc_can_give_items(pc_isGM(sd)) ) - clif_skill_fail(sd,skillid,0,0); - else - clif_openvendingreq(sd,2+skilllv); - } - break; - - case AL_TELEPORT: /* テレポ?ト */ - if(sd) { - if (map[bl->m].flag.noteleport) { /* テレポ禁止 */ - clif_skill_teleportmessage(sd,0); - break; - } - if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza] - clif_displaymessage(sd->fd, "Duel: Can't use teleport in duel."); - break; - } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(skilllv == 1) { - // possibility to skip menu [LuzZza] - if(!battle_config.skip_teleport_lv1_menu && - sd->skillitem != AL_TELEPORT) //If skillid is not teleport, this was auto-casted! [Skotlex] - clif_skill_warppoint(sd,skillid,skilllv,"Random","","",""); - else - pc_randomwarp(sd,3); - } else { - if (sd->skillitem != AL_TELEPORT) - clif_skill_warppoint(sd,skillid,skilllv,"Random", - mapindex_id2name(sd->status.save_point.map),"",""); - else //Autocasted Teleport level 2?? - pc_setpos(sd,sd->status.save_point.map, - sd->status.save_point.x,sd->status.save_point.y,3); - } - } else - unit_warp(bl,-1,-1,-1,3); - break; - - case AL_HOLYWATER: /* アクアベネディクタ */ - if(sd) { - if (skill_produce_mix(sd, skillid, 523, 0, 0, 0, 1)) - clif_skill_nodamage(src,bl,skillid,skilllv,1); - else - clif_skill_fail(sd,skillid,0,0); - } - break; - - case TF_PICKSTONE: - if(sd) { - int eflag; - struct item item_tmp; - struct block_list tbl; - clif_skill_nodamage(src,bl,skillid,skilllv,1); - memset(&item_tmp,0,sizeof(item_tmp)); - memset(&tbl,0,sizeof(tbl)); // [MouseJstr] - item_tmp.nameid = 7049; - item_tmp.identify = 1; - tbl.id = 0; - clif_takeitem(&sd->bl,&tbl); - eflag = pc_additem(sd,&item_tmp,1); - if(eflag) { - clif_additem(sd,0,0,eflag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - } - break; - case ASC_CDP: - if(sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - skill_produce_mix(sd, skillid, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle. - } - break; - - case RG_STRIPWEAPON: /* ストリップウェポン */ - case RG_STRIPSHIELD: /* ストリップシ?[ルド */ - case RG_STRIPARMOR: /* ストリップア?[マ?[ */ - case RG_STRIPHELM: /* ストリップヘルム */ - case ST_FULLSTRIP: // Rewritten most of the code [DracoRPG] - case GS_DISARM: // Added disarm. [Reddozen] - { - int strip_fix, equip = 0; - int sclist[4] = {0,0,0,0}; - - if (skillid == RG_STRIPWEAPON || skillid == ST_FULLSTRIP || skillid == GS_DISARM) - equip |= EQP_WEAPON; - if (skillid == RG_STRIPSHIELD || skillid == ST_FULLSTRIP) - equip |= EQP_SHIELD; - if (skillid == RG_STRIPARMOR || skillid == ST_FULLSTRIP) - equip |= EQP_ARMOR; - if (skillid == RG_STRIPHELM || skillid == ST_FULLSTRIP) - equip |= EQP_HELM; - - strip_fix = status_get_dex(src) - status_get_dex(bl); - if(strip_fix < 0) - strip_fix=0; - if (rand()%100 >= 5+2*skilllv+strip_fix/5) - { - if (sd) - clif_skill_fail(sd,skillid,0,0); - break; - } - if (dstsd) { - for (i=0;i<11;i++) { - if (dstsd->equip_index[i]<0 || !dstsd->inventory_data[dstsd->equip_index[i]]) - continue; - switch (i) { - case 8: //Shield / left-hand weapon - if(dstsd->inventory_data[dstsd->equip_index[8]]->type == 5) - { //Shield - if (equip&EQP_SHIELD && - !(dstsd->unstripable_equip&EQP_SHIELD) && - !(tsc && tsc->data[SC_CP_SHIELD].timer != -1) - ){ - sclist[1] = SC_STRIPSHIELD; // Okay, we found a shield to strip - It is really a shield, not a two-handed weapon or a left-hand weapon - pc_unequipitem(dstsd,dstsd->equip_index[i],3); - } - continue; - } - //Continue to weapon - case 9: - if (equip &EQP_WEAPON && - !(dstsd->unstripable_equip&EQP_WEAPON) && - !(tsc && tsc->data[SC_CP_WEAPON].timer != -1) - ) { - sclist[0] = SC_STRIPWEAPON; // Okay, we found a weapon to strip - It can be a right-hand, left-hand or two-handed weapon - pc_unequipitem(dstsd,dstsd->equip_index[i],3); - } - break; - case 7: //Armor - if (equip &EQP_ARMOR && - !(dstsd->unstripable_equip &EQP_ARMOR) && - !(tsc && tsc->data[SC_CP_ARMOR].timer != -1) - ) { - sclist[2] = SC_STRIPARMOR; // Okay, we found an armor to strip - pc_unequipitem(dstsd,dstsd->equip_index[i],3); - } - break; - case 6: //Helm - if (equip &EQP_HELM && - !(dstsd->unstripable_equip &EQP_HELM) && - !(tsc && tsc->data[SC_CP_HELM].timer != -1) - ) { - sclist[3] = SC_STRIPHELM; // Okay, we found a helm to strip - pc_unequipitem(dstsd,dstsd->equip_index[i],3); - } - break; - } - } - } else if (!(status_get_mode(bl)&MD_BOSS)) { - if (equip&EQP_WEAPON && !(tsc && tsc->data[SC_CP_WEAPON].timer != -1)) - sclist[0] = SC_STRIPWEAPON; - if (equip&EQP_SHIELD && !(tsc && tsc->data[SC_CP_SHIELD].timer != -1)) - sclist[1] = SC_STRIPSHIELD; - if (equip&EQP_ARMOR && !(tsc && tsc->data[SC_CP_ARMOR].timer != -1)) - sclist[2] = SC_STRIPARMOR; - if (equip&EQP_HELM && !(tsc && tsc->data[SC_CP_HELM].timer != -1)) - sclist[3] = SC_STRIPHELM; - } - equip = 0; //Reuse equip to hold how many stats are invoked. - for (i=0;i<4;i++) { - if (sclist[i]) // Start the SC only if an equipment was stripped from this location - equip+=sc_start(bl,sclist[i],100,skilllv,skill_get_time(skillid,skilllv)+strip_fix/2); - } - if (equip) - clif_skill_nodamage(src,bl,skillid,skilllv,1); - else if (sd) //Nothing stripped. - clif_skill_fail(sd,skillid,0,0); - break; - } - - /* PotionPitcher */ - case AM_BERSERKPITCHER: - case AM_POTIONPITCHER: /* ポ?ションピッチャ? */ - { - int i,x,hp = 0,sp = 0,bonus=100; - if(sd) { - x = skilllv%11 - 1; - i = pc_search_inventory(sd,skill_db[skillid].itemid[x]); - if(i < 0 || skill_db[skillid].itemid[x] <= 0) { - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 1; - } - if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skillid].amount[x]) { - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 1; - } - if(skillid == AM_BERSERKPITCHER) { //Does not override use-level, and cannot be used on bows. - if (dstsd && (dstsd->status.base_level<(unsigned int)sd->inventory_data[i]->elv || dstsd->weapontype1 == W_BOW)) { - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 1; - } - } - potion_flag = 1; - potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0; - potion_target = bl->id; - run_script(sd->inventory_data[i]->script,0,sd->bl.id,0); - pc_delitem(sd,i,skill_db[skillid].amount[x],0); - potion_flag = potion_target = 0; - if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_ALCHEMIST) - bonus += sd->status.base_level; - if(potion_per_hp > 0 || potion_per_sp > 0) { - hp = status_get_max_hp(bl) * potion_per_hp / 100; - hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - if(dstsd) { - sp = dstsd->status.max_sp * potion_per_sp / 100; - sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - } - } - else { - if(potion_hp > 0) { - hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - hp = hp * (100 + (status_get_vit(bl)<<1)) / 100; - if(dstsd) - hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; - } - if(potion_sp > 0) { - sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; - sp = sp * (100 + (status_get_int(bl)<<1)) / 100; - if(dstsd) - sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100; - } - } - } - else { - hp = (1 + rand()%400) * (100 + skilllv*10) / 100; - hp = hp * (100 + (status_get_vit(bl)<<1)) / 100; - if(dstsd) - hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; - } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(hp > 0 || (skillid == AM_POTIONPITCHER && hp <= 0 && sp <= 0)) - clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); - if(sp > 0) - clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1); - battle_heal(src,bl,hp,sp,0); - } - break; - case AM_CP_WEAPON: - case AM_CP_SHIELD: - case AM_CP_ARMOR: - case AM_CP_HELM: - { - int scid = SC_STRIPWEAPON + (skillid - AM_CP_WEAPON); - if(tsc && tsc->data[scid].timer != -1) - status_change_end(bl, scid, -1 ); - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - } - break; - case AM_TWILIGHT1: - if (sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - //Prepare 200 White Potions. - if (!skill_produce_mix(sd, skillid, 504, 0, 0, 0, 200)) - clif_skill_fail(sd,skillid,0,0); - } - break; - case AM_TWILIGHT2: - if (sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - //Prepare 200 Slim White Potions. - if (!skill_produce_mix(sd, skillid, 547, 0, 0, 0, 200)) - clif_skill_fail(sd,skillid,0,0); - } - break; - case AM_TWILIGHT3: - if (sd) { - //check if you can produce all three, if not, then fail: - if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol - || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle - || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle - ) { - clif_skill_fail(sd,skillid,0,0); - break; - } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - skill_produce_mix(sd, skillid, 970, 0, 0, 0, 100); - skill_produce_mix(sd, skillid, 7136, 0, 0, 0, 50); - skill_produce_mix(sd, skillid, 7135, 0, 0, 0, 50); - } - break; - case SA_DISPELL: /* ディスペル */ - { - int i; - clif_skill_nodamage(src,bl,skillid,skilllv,1); - i = status_get_sc_def_mdef(bl); - if (i >= 10000 || - tsc == NULL || (tsc->data[SC_SPIRIT].timer != -1 && tsc->data[SC_SPIRIT].val2 == SL_ROGUE) || //Rogue's spirit defends againt dispel. - //Fixed & changed to use a proportionnal reduction (no info, but seems far more logical) [DracoRPG] - rand()%10000 >= (10000-i)*(50+10*skilllv)/100) - { - if (sd) - clif_skill_fail(sd,skillid,0,0); - break; - } - if(status_isimmune(bl) || !tsc->count) - break; - for(i=0;idata[i].timer == -1) - continue; - if(i==SC_HALLUCINATION || i==SC_WEIGHT50 || i==SC_WEIGHT90 - || i==SC_STRIPWEAPON || i==SC_STRIPSHIELD || i==SC_STRIPARMOR || i==SC_STRIPHELM - || i==SC_CP_WEAPON || i==SC_CP_SHIELD || i==SC_CP_ARMOR || i==SC_CP_HELM - || i==SC_COMBO || i==SC_DANCING || i==SC_GUILDAURA || i==SC_EDP - || i==SC_AUTOBERSERK || i==SC_CARTBOOST || i==SC_MELTDOWN || i==SC_MOONLIT - || i==SC_SAFETYWALL || i==SC_SMA - ) - continue; - if(i==SC_BERSERK) tsc->data[i].val4=1; //Mark a dispelled berserk to avoid setting hp to 100. - status_change_end(bl,i,-1); - } - } - break; - - case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex] - clif_skill_nodamage(src,bl,skillid,skilllv,1); - skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)|0x10000); - break; - - case TK_HIGHJUMP: - { - int x,y, dir = unit_getdir(src); - - x = src->x + dirx[dir]*skilllv*2; - y = src->y + diry[dir]*skilllv*2; - - clif_skill_nodamage(src,bl,TK_HIGHJUMP,skilllv,1); - if(map_getcell(src->m,x,y,CELL_CHKPASS)) { - unit_movepos(src, x, y, 1, 0); - clif_slide(src,x,y); - } - } - break; - - case SA_CASTCANCEL: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - unit_skillcastcancel(src,1); - if(sd) { - int sp = skill_get_sp(sd->skillid_old,sd->skilllv_old); - sp = sp * (90 - (skilllv-1)*20) / 100; - if(sp < 0) sp = 0; - pc_heal(sd,0,-sp); - } - break; - case SA_SPELLBREAKER: // スペルブレイカ? - { - int sp; - if(tsc && tsc->data[SC_MAGICROD].timer != -1) { - if(dstsd) { - sp = skill_get_sp(skillid,skilllv); - sp = sp * tsc->data[SC_MAGICROD].val2 / 100; - if(sp > SHRT_MAX) sp = SHRT_MAX; - else if(sp < 1) sp = 1; - clif_heal(dstsd->fd,SP_SP,pc_heal(dstsd, 0, sp)); - } - clif_skill_nodamage(bl,bl,SA_MAGICROD,tsc->data[SC_MAGICROD].val1,1); - if(sd) { - sp = sd->status.max_sp/5; - if(sp < 1) sp = 1; - pc_damage_sp(sd,sp,0); - } - } else { - struct unit_data *ud = unit_bl2ud(bl); - int bl_skillid=0,bl_skilllv=0,hp = 0; - if (!ud || ud->skilltimer == -1) break; //Nothing to cancel. - bl_skillid = ud->skillid; - bl_skilllv = ud->skilllv; - if (status_get_mode(bl) & MD_BOSS) - { //Only 10% success chance against bosses. [Skotlex] - if (rand()%100 < 90) - { - if (sd) clif_skill_fail(sd,skillid,0,0); - break; - } - } else if (!dstsd || map_flag_vs(bl->m)) //HP damage only on pvp-maps when against players. - hp = status_get_max_hp(bl)/50; //Recover 2% HP [Skotlex] - - clif_skill_nodamage(src,bl,skillid,skilllv,1); - unit_skillcastcancel(bl,0); - sp = skill_get_sp(bl_skillid,bl_skilllv); - if (dstsd) - pc_damage_sp(dstsd, sp, 0); - battle_damage(NULL, bl, hp, 0, 1); - if(sd && sp) { - sp = sp*(25*(skilllv-1))/100; - if(skilllv > 1 && sp < 1) sp = 1; - else if(sp > SHRT_MAX) sp = SHRT_MAX; - clif_heal(sd->fd,SP_SP,pc_heal(sd, 0, sp)); - } - if (hp && skilllv >= 5) - { //Recover half damaged HP at level 5 [Skotlex] - hp = battle_heal(bl, src, hp/2, 0, 0); - if (sd && sd->fd) - clif_heal(sd->fd,SP_HP,hp); - } - } - } - break; - case SA_MAGICROD: - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - break; - case SA_AUTOSPELL: /* オ?トスペル */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(sd) - clif_autospell(sd,skilllv); - else { - int maxlv=1,spellid=0; - static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT }; - if(skilllv >= 10) { - spellid = MG_FROSTDIVER; -// if (tsc && tsc->data[SC_SPIRIT].timer != -1 && tsc->data[SC_SPIRIT].val2 == SA_SAGE) -// maxlv = 10; -// else - maxlv = skilllv - 9; - } - else if(skilllv >=8) { - spellid = MG_FIREBALL; - maxlv = skilllv - 7; - } - else if(skilllv >=5) { - spellid = MG_SOULSTRIKE; - maxlv = skilllv - 4; - } - else if(skilllv >=2) { - int i = rand()%3; - spellid = spellarray[i]; - maxlv = skilllv - 1; - } - else if(skilllv > 0) { - spellid = MG_NAPALMBEAT; - maxlv = 3; - } - if(spellid > 0) - sc_start4(src,SC_AUTOSPELL,100,skilllv,spellid,maxlv,0, - skill_get_time(SA_AUTOSPELL,skilllv)); - } - break; - - case BS_GREED: - if(sd){ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_greed,bl, - skill_get_splash(skillid, skilllv),BL_ITEM,bl); - } - break; - - case SA_ELEMENTWATER: - case SA_ELEMENTFIRE: - case SA_ELEMENTGROUND: - case SA_ELEMENTWIND: - if(dstmd && !(status_get_mode(bl)&MD_BOSS)){ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - dstmd->def_ele = skill_get_pl(skillid); - dstmd->def_ele += (1+rand()%4)*20; - } - break; - - /* ランダム??ォ?化?A???ォ?化?A地?A火?A風 */ - case NPC_ATTRICHANGE: - case NPC_CHANGEWATER: - case NPC_CHANGEGROUND: - case NPC_CHANGEFIRE: - case NPC_CHANGEWIND: - /* 毒?A?ケ?A念?A闇 */ - case NPC_CHANGEPOISON: - case NPC_CHANGEHOLY: - case NPC_CHANGEDARKNESS: - case NPC_CHANGETELEKINESIS: - case NPC_CHANGEUNDEAD: - if(md){ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - md->def_ele = skill_get_pl(skillid); - if (md->def_ele == 0) /* ランダム?化?Aただし?A*/ - md->def_ele = rand()%10; /* 不死??ォは?怩ュ */ - md->def_ele += (1+rand()%4)*20; /* ??ォレベルはランダム */ - } - break; - - case NPC_PROVOCATION: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(md && md->skillidx >= 0) - clif_pet_performance(src,md->db->skill[md->skillidx].val[0]); - break; - - case NPC_KEEPING: - case NPC_BARRIER: - { - int skill_time = skill_get_time(skillid,skilllv); - struct unit_data *ud = unit_bl2ud(bl); - if (clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_time)) - && ud) { //Disable attacking/acting/moving for skill's duration. - ud->attackabletime = - ud->canact_tick = - ud->canmove_tick = tick + skill_time; - } - } - break; - - case NPC_REBIRTH: - //New rebirth System uses Kaizel lv1. [Skotlex] - sc_start(bl,type,100,1,skill_get_time(SL_KAIZEL,skilllv)); - break; - - case NPC_DARKBLESSING: - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,(50+skilllv*5),skilllv,skill_get_time2(skillid,skilllv))); - break; - - case NPC_LICK: - if (dstsd) { - if (dstsd->special_state.no_weapon_damage ) { - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - } - pc_damage_sp(dstsd,100,0); - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,(skilllv*5),skilllv,skill_get_time2(skillid,skilllv))); - break; - - case NPC_SUICIDE: /* 自決 */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - battle_damage(NULL, src,status_get_hp(src),0,3); //Suicidal Mobs should give neither exp nor items. (flag&2 passed to battle_damage) [Skotlex] - break; - - case NPC_SUMMONSLAVE: /* 手下?「喚 */ - case NPC_SUMMONMONSTER: /* MOB?「喚 */ - if(md && md->skillidx >= 0) - mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid); - break; - - case NPC_CALLSLAVE: //取り巻き呼び戻し - mob_warpslave(src,AREA_SIZE/2); - break; - - case NPC_RANDOMMOVE: - if (md) { - md->next_walktime = tick - 1; - mob_randomwalk(md,tick); - } - break; - - case NPC_SPEEDUP: - { - // or does it increase casting rate? just a guess xD - int i = SC_ASPDPOTION0 + skilllv - 1; - if (i > SC_ASPDPOTION3) - i = SC_ASPDPOTION3; - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,i,100,skilllv,skilllv * 60000)); - } - break; - - case NPC_REVENGE: - // not really needed... but adding here anyway ^^ - if (md && md->master_id > 0) { - struct block_list *mbl, *tbl; - if ((mbl = map_id2bl(md->master_id)) == NULL || - (tbl = battle_gettargeted(mbl)) == NULL) - break; - md->state.provoke_flag = tbl->id; - mob_target(md, tbl, md->db->range); - } - break; - - case NPC_RUN: //後退 - { - const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}}; - int dir = (bl == src)?unit_getdir(src):map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away. - unit_stop_attack(src); - //Run skillv tiles. - unit_walktoxy(src, bl->x + skilllv * mask[dir][0], bl->y + skilllv * mask[dir][1], 0); - } - break; - - case NPC_TRANSFORMATION: - case NPC_METAMORPHOSIS: - if(md && md->skillidx >= 0) { - if (skilllv > 1) - { //Multiply skilllv times, the original instance must be silently killed. [Skotlex] - mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid); - unit_remove_map(src,1); - } - else - { //Transform into another class. - int class_ = mob_random_class (md->db->skill[md->skillidx].val,0); - if (class_) mob_class_change(md, class_); - } - } - break; - - case NPC_EMOTION_ON: - case NPC_EMOTION: - if(md && md->skillidx >= 0) - { - clif_emotion(&md->bl,md->db->skill[md->skillidx].val[0]); - if(!md->special_state.ai && (md->db->skill[md->skillidx].val[1] || md->db->skill[md->skillidx].val[2])) - { //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex] - //val[1] 'sets' the mode, val[2] can add/remove from the current mode based on skill used: - //NPC_EMOTION_ON adds a mode / NPC_EMOTION removes it. - int mode, mode2; - mode = status_get_mode(src); - mode2 = (md->db->skill[md->skillidx].val[1])?(md->db->skill[md->skillidx].val[1]):mode; - if (md->db->skill[md->skillidx].val[2]) { //Alter the mode. - if (skillid == NPC_EMOTION_ON) //Add a mode - mode2|= md->db->skill[md->skillidx].val[2]; - else //Remove a mode - mode2&= ~(md->db->skill[md->skillidx].val[2]); - } - if (mode == mode2) - break; //No change - md->mode = mode2; - if (md->mode == md->db->mode) - md->mode = 0; //Fallback to the db's mode. - //Since mode changed, reset their state. - mob_stop_attack(md); - mob_stop_walking(md,0); - } - } - break; - - case NPC_DEFENDER: - clif_skill_nodamage(src,bl,skillid,skilllv,1); - break; - - case NPC_POWERUP: - sc_start(bl,SC_INCATKRATE,100,40*skilllv,skill_get_time(skillid, skilllv)); - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,20*skilllv,skill_get_time(skillid, skilllv))); - break; - - case NPC_AGIUP: - sc_start(bl,SC_SPEEDUP1,100,skilllv,skill_get_time(skillid, skilllv)); - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,20*skilllv,skill_get_time(skillid, skilllv))); - break; - - case NPC_INVISIBLE: - //val4 passed as 1 is for "infinite cloak". - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start4(bl,type,100,skilllv,0,0,1,skill_get_time(skillid,skilllv))); - break; - - case NPC_SIEGEMODE: - // not sure what it does - clif_skill_nodamage(src,bl,skillid,skilllv,1); - break; - - case WE_MALE: /* 君だけは護るよ */ - if(sd && dstsd){ - int hp_rate=(skilllv <= 0)? 0:skill_db[skillid].hp_rate[skilllv-1]; - int gain_hp=dstsd->status.max_hp*abs(hp_rate)/100;// The earned is the same % of the target HP than it costed the caster. [Skotlex] - gain_hp = battle_heal(NULL,bl,gain_hp,0,0); - clif_skill_nodamage(src,bl,skillid,gain_hp,1); - } - break; - case WE_FEMALE: /* なたの?に??オになります */ - if(sd && dstsd){ - int sp_rate=(skilllv <= 0)? 0:skill_db[skillid].sp_rate[skilllv-1]; - int gain_sp=dstsd->status.max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex] - gain_sp = battle_heal(NULL,bl,0,gain_sp,0); - clif_skill_nodamage(src,bl,skillid,gain_sp,1); - } - break; - -// parent-baby skills - case WE_BABY: - if(sd){ - struct map_session_data *f_sd = pc_get_father(sd); - struct map_session_data *m_sd = pc_get_mother(sd); - // if neither was found - if(!f_sd && !m_sd){ - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 0; - } - status_change_start(bl,SC_STUN,10000,skilllv,0,0,0,skill_get_time2(skillid,skilllv),8); - if (f_sd) sc_start(&f_sd->bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - if (m_sd) sc_start(&m_sd->bl,type,100,skilllv,skill_get_time(skillid,skilllv)); - } - break; - - case PF_HPCONVERSION: /* ライフ置き換え */ - { - int hp, sp; - hp = status_get_max_hp(src) / 10; //基本はHPの10% - sp = hp * 10 * skilllv / 100; - if (hp >= status_get_hp(bl)) { - if (sd) clif_skill_fail(sd,skillid,0,0); - break; - } - clif_skill_nodamage(src, bl, skillid, skilllv, 1); - if (dstsd) { - if (sp > dstsd->status.max_sp - dstsd->status.sp) - sp = dstsd->status.max_sp - dstsd->status.sp; - // we need to check with the sp that was taken away when casting too - if (skill_get_sp(skillid, skilllv) >= dstsd->status.max_sp - dstsd->status.sp) - hp = sp = 0; - } - battle_heal(src,bl,-hp, sp, 0); - if (dstsd && dstsd->fd) - { - clif_heal(dstsd->fd, SP_SP, sp); - clif_updatestatus(dstsd, SP_SP); - } - } - break; - case HT_REMOVETRAP: /* リム?ブトラップ */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - { - struct skill_unit *su=NULL; - struct item item_tmp; - int flag; - if((bl->type==BL_SKILL) && - (su=(struct skill_unit *)bl) && - (su->group->src_id == src->id || map_flag_vs(bl->m)) && - (skill_get_inf2(su->group->skill_id) & INF2_TRAP)) - { - if(sd && !su->group->state.into_abyss) - { //Avoid collecting traps when it does not costs to place them down. [Skotlex] - if(battle_config.skill_removetrap_type){ - for(i=0;i<10;i++) { - if(skill_db[su->group->skill_id].itemid[i] > 0){ - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = skill_db[su->group->skill_id].itemid[i]; - item_tmp.identify = 1; - if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i]))){ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - } - } - }else{ - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid = 1065; - item_tmp.identify = 1; - if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1))){ - clif_additem(sd,0,0,flag); - map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - } - } - if(su->group->unit_id == UNT_ANKLESNARE && su->group->val2){ - struct block_list *target=map_id2bl(su->group->val2); - if(target) - status_change_end(target,SC_ANKLE,-1); - } - skill_delunit(su); - } - } - break; - case HT_SPRINGTRAP: /* スプリングトラップ */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - { - struct skill_unit *su=NULL; - if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){ - switch(su->group->unit_id){ - case UNT_ANKLESNARE: // ankle snare - if (su->group->val2 != 0) - // if it is already trapping something don't spring it, - // remove trap should be used instead - break; - // otherwise fallthrough to below - case UNT_BLASTMINE: - case UNT_SKIDTRAP: - case UNT_LANDMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_FREEZINGTRAP: - case UNT_CLAYMORETRAP: - case UNT_TALKIEBOX: - su->group->unit_id = UNT_USED_TRAPS; - clif_changetraplook(bl, UNT_USED_TRAPS); - su->group->limit=DIFF_TICK(tick+1500,su->group->tick); - su->limit=DIFF_TICK(tick+1500,su->group->tick); - } - } - } - break; - case BD_ENCORE: /* アンコ?ル */ - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(sd) - unit_skilluse_id(src,src->id,sd->skillid_dance,sd->skilllv_dance); - break; - - case AS_SPLASHER: /* ベナムスプラッシャ? */ - if(status_get_max_hp(bl)*3/4 < status_get_hp(bl)) { //HPが2/3以??っていたら失敗 - map_freeblock_unlock(); - return 1; - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start4(bl,type,100, - skilllv,skillid,src->id,skill_get_time(skillid,skilllv),1000)); - break; - - case PF_MINDBREAKER: /* プ?ボック */ - { - /* MVPmobと不死には?かない */ - if(status_get_mode(bl)&MD_BOSS || battle_check_undead(status_get_race(bl),status_get_elem_type(bl))) //不死には?かない - { - map_freeblock_unlock(); - return 1; - } - - //Has a 55% + skilllv*5% success chance. - if (!clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,55+5*skilllv,skilllv,skill_get_time(skillid,skilllv)))) - { - if (sd) clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 0; - } - - unit_skillcastcancel(bl,0); - - if(tsc && tsc->count){ - if(tsc->data[SC_FREEZE].timer!=-1) - status_change_end(bl,SC_FREEZE,-1); - if(tsc->data[SC_STONE].timer!=-1 && tsc->data[SC_STONE].val2==0) - status_change_end(bl,SC_STONE,-1); - if(tsc->data[SC_SLEEP].timer!=-1) - status_change_end(bl,SC_SLEEP,-1); - } - - if(dstmd) - mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv)); - } - break; - - case PF_SOULCHANGE: - { - int sp1 = 0, sp2 = 0; - if (sd) { - if (dstsd) { - sp1 = sd->status.sp > dstsd->status.max_sp ? dstsd->status.max_sp : sd->status.sp; - sp2 = dstsd->status.sp > sd->status.max_sp ? sd->status.max_sp : dstsd->status.sp; - sd->status.sp = sp2; - dstsd->status.sp = sp1; - clif_heal(sd->fd,SP_SP,sp2); - clif_updatestatus(sd,SP_SP); - clif_heal(dstsd->fd,SP_SP,sp1); - clif_updatestatus(dstsd,SP_SP); - } else if (dstmd) { - if (dstmd->state.soul_change_flag) { - clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 0; - } - sp2 = sd->status.max_sp * 3 /100; - if (sd->status.sp + sp2 > sd->status.max_sp) - sp2 = sd->status.max_sp - sd->status.sp; - sd->status.sp += sp2; - clif_heal(sd->fd,SP_SP,sp2); - clif_updatestatus(sd,SP_SP); - dstmd->state.soul_change_flag = 1; - } - } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; - - // Slim Pitcher - case CR_SLIMPITCHER: - if (potion_hp) { - int hp = potion_hp; - hp = hp * (100 + (status_get_vit(bl)<<1))/100; - if (dstsd) { - hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10)/100; - } - clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); - battle_heal(NULL,bl,hp,0,0); - } - break; - // Full Chemical Protection - case CR_FULLPROTECTION: - { - int i, skilltime; - skilltime = skill_get_time(skillid,skilllv); - if (!tsc) { - clif_skill_nodamage(src,bl,skillid,skilllv,0); - break; - } - for (i=0; i<4; i++) { - if(tsc->data[SC_STRIPWEAPON + i].timer != -1) - status_change_end(bl, SC_STRIPWEAPON + i, -1 ); - sc_start(bl,SC_CP_WEAPON + i,100,skilllv,skilltime); - } - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; - - case RG_CLEANER: //AppleGirl - clif_skill_nodamage(src,bl,skillid,skilllv,1); - break; - - - case PF_DOUBLECASTING: - if (!clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,30+ 10*skilllv,skilllv,skill_get_time(skillid,skilllv)))) - if (sd) clif_skill_fail(sd,skillid,0,0); - break; - - case CG_LONGINGFREEDOM: - { - if (tsc && tsc->data[SC_LONGING].timer == -1 && tsc->data[SC_DANCING].timer != -1 && tsc->data[SC_DANCING].val4 - && tsc->data[SC_DANCING].val1 != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex] - { - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - } - } - break; - - case CG_TAROTCARD: - { - int eff, count = -1; - if (rand() % 100 > skilllv * 8) { - if (sd) clif_skill_fail(sd,skillid,0,0); - map_freeblock_unlock(); - return 0; - } - do { - eff = rand() % 14; - clif_specialeffect(bl, 523 + eff, 0); - switch (eff) - { - case 0: // heals SP to 0 - if (dstsd) pc_damage_sp(dstsd,0,100); - break; - case 1: // matk halved - sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skillid,skilllv)); - break; - case 2: // all buffs removed - status_change_clear_buffs(bl,1); - break; - case 3: // 1000 damage, random armor destroyed - { - int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM }; - battle_damage(src, bl, 1000, 0, 0); - clif_damage(src,bl,tick,0,0,1000,0,0,0); - skill_break_equip(bl, where[rand()%3], 10000, BCT_ENEMY); - } - break; - case 4: // atk halved - sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skillid,skilllv)); - break; - case 5: // 2000HP heal, random teleported - battle_heal(src, src, 2000, 0, 0); - unit_warp(src, -1,-1,-1, 3); - break; - case 6: // random 2 other effects - if (count == -1) - count = 3; - else - count++; //Should not retrigger this one. - break; - case 7: // stop freeze or stoned - { - int sc[] = { SC_STOP, SC_FREEZE, SC_STONE }; - sc_start(bl,sc[rand()%3],100,skilllv,skill_get_time2(skillid,skilllv)); - } - break; - case 8: // curse coma and poison - sc_start(bl,SC_COMA,100,skilllv,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_CURSE,100,skilllv,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_POISON,100,skilllv,skill_get_time2(skillid,skilllv)); - break; - case 9: // chaos - sc_start(bl,SC_CONFUSION,100,skilllv,skill_get_time2(skillid,skilllv)); - break; - case 10: // 6666 damage, atk matk halved, cursed - battle_damage(src, bl, 6666, 0, 0); - clif_damage(src,bl,tick,0,0,6666,0,0,0); - sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_CURSE,skilllv,100,skill_get_time2(skillid,skilllv)); - break; - case 11: // 4444 damage - battle_damage(src, bl, 4444, 0, 0); - clif_damage(src,bl,tick,0,0,4444,0,0,0); - break; - case 12: // stun - sc_start(bl,SC_STUN,100,skilllv,5000); - break; - case 13: // atk,matk,hit,flee,def reduced - sc_start(bl,SC_INCATKRATE,100,-20,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_INCMATKRATE,100,-20,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_INCHITRATE,100,-20,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_INCFLEERATE,100,-20,skill_get_time2(skillid,skilllv)); - sc_start(bl,SC_INCDEFRATE,100,-20,skill_get_time2(skillid,skilllv)); - break; - default: - break; - } - } while ((--count) > 0); - clif_skill_nodamage(src,bl,skillid,skilllv,1); - } - break; - - case SL_ALCHEMIST: - case SL_ASSASIN: - case SL_BARDDANCER: - case SL_BLACKSMITH: - case SL_CRUSADER: - case SL_HUNTER: - case SL_KNIGHT: - case SL_MONK: - case SL_PRIEST: - case SL_ROGUE: - case SL_SAGE: - case SL_SOULLINKER: - case SL_STAR: - case SL_SUPERNOVICE: - case SL_WIZARD: - if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == type)) { - clif_skill_fail(sd,skillid,0,0); - break; - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start4(bl,SC_SPIRIT,100,skilllv,skillid,0,0,skill_get_time(skillid,skilllv))); - sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA,skilllv)); - break; - case SL_HIGH: - if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) { - clif_skill_fail(sd,skillid,0,0); - break; - } - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start4(bl,type,100,skilllv,skillid,0,0,skill_get_time(skillid,skilllv))); - sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA,skilllv)); - break; - - case SL_SWOO: - if (tsc && tsc->data[type].timer != -1) { - sc_start(src,SC_STUN,100,skilllv,10000); - break; - } - case SL_SKA: // [marquis007] - case SL_SKE: - if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { - clif_skill_fail(sd,skillid,0,0); - status_change_start(src,SC_STUN,10000,skilllv,0,0,0,500,10); - } else - clif_skill_nodamage(src,bl,skillid,skilllv, - sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); - - if (skillid == SL_SKE) - sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA,skilllv)); - - break; - - // New guild skills [Celest] - case GD_BATTLEORDER: - if(flag&1) { - if (status_get_guild_id(src) == status_get_guild_id(bl)) - sc_start(bl,SC_BATTLEORDERS,100,skilllv,skill_get_time(skillid, skilllv)); - } else if (status_get_guild_id(src)) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_GUILD|1, - skill_castend_nodamage_id); - if (sd) - guild_block_skill(sd,skill_get_time2(skillid,skilllv)); - } - break; - case GD_REGENERATION: - if(flag&1) { - if (status_get_guild_id(src) == status_get_guild_id(bl)) - sc_start(bl,SC_REGENERATION,100,skilllv,skill_get_time(skillid, skilllv)); - } else if (status_get_guild_id(src)) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_GUILD|1, - skill_castend_nodamage_id); - if (sd) - guild_block_skill(sd,skill_get_time2(skillid,skilllv)); - } - break; - case GD_RESTORE: - if(flag&1) { - if (status_get_guild_id(src) == status_get_guild_id(bl)) { - int hp, sp; - hp = status_get_max_hp(bl)*9/10; - sp = dstsd?dstsd->status.max_sp*9/10:0; - clif_skill_nodamage(src,bl,AL_HEAL,hp,1); - battle_heal(NULL,bl,hp,sp,0); - } - } else if (status_get_guild_id(src)) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - map_foreachinrange(skill_area_sub, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - src,skillid,skilllv,tick, flag|BCT_GUILD|1, - skill_castend_nodamage_id); - if (sd) - guild_block_skill(sd,skill_get_time2(skillid,skilllv)); - } - break; - case GD_EMERGENCYCALL: - { - int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0}; - int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0}; - int j = 0; - struct guild *g = NULL; - // i don't know if it actually summons in a circle, but oh well. ;P - g = sd?sd->state.gmaster_flag:guild_search(status_get_guild_id(src)); - if (!g) - break; - for(i = 0; i < g->max_member; i++, j++) { - if (j>8) j=0; - if ((dstsd = g->member[i].sd) != NULL && sd != dstsd) { - if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg(dstsd->bl.m)) - continue; - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH)) - dx[j] = dy[j] = 0; - pc_setpos(dstsd, map[src->m].index, src->x+dx[j], src->y+dy[j], 2); - } - } - if (sd) - guild_block_skill(sd,skill_get_time2(skillid,skilllv)); - } - break; - - case SG_FEEL: - if (sd) { - if(!sd->feel_map[skilllv-1].index) { - //AuronX reported you CAN memorize the same map as all three. [Skotlex] - clif_skill_nodamage(src,bl,skillid,skilllv,1); - clif_parse_ReqFeel(sd->fd,sd, skilllv); - } - else - clif_feel_info(sd, skilllv-1); - } - break; - - case SG_HATE: - if (sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(dstsd) //PC - { - sd->hate_mob[skilllv-1] = dstsd->status.class_; - pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[skilllv-1]+1); - clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); - } - else if(dstmd) // mob - { - switch(skilllv) - { - case 1: - if (status_get_size(bl)==0) - { - sd->hate_mob[0] = dstmd->class_; - pc_setglobalreg(sd,"PC_HATE_MOB_SUN",sd->hate_mob[0]+1); - clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); - } else clif_skill_fail(sd,skillid,0,0); - break; - case 2: - if (status_get_size(bl)==1 && status_get_max_hp(bl)>=6000) - { - sd->hate_mob[1] = dstmd->class_; - pc_setglobalreg(sd,"PC_HATE_MOB_MOON",sd->hate_mob[1]+1); - clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); - } else clif_skill_fail(sd,skillid,0,0); - break; - case 3: - if (status_get_size(bl)==2 && status_get_max_hp(bl)>=20000) - { - sd->hate_mob[2] = dstmd->class_; - pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[2]+1); - clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); - } else clif_skill_fail(sd,skillid,0,0); - break; - default: - clif_skill_fail(sd,skillid,0,0); - break; - } - } - } - break; - - //Until they're at right position - gs_nodamage- [Vicious] - //Not implemented yet [Vicious] - case GS_GLITTERING: - if(sd) { - clif_skill_nodamage(src,bl,skillid,skilllv,1); - if(rand()%100 < (50+10*skilllv)) - pc_addspiritball(sd,skill_get_time(skillid,skilllv),10); - else if(sd->spiritball > 0) - pc_delspiritball(sd,1,0); - } - break; - default: - ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid); - map_freeblock_unlock(); - return 1; - } - - if (dstmd) //Mob skill event for no damage skills (damage ones are handled in battle_calc_damage) [Skotlex] - mobskill_event(dstmd, src, tick, MSC_SKILLUSED|(skillid<<16)); - - if (sd && !(flag&1) && sd->state.arrow_atk) //Consume arrow on last invocation to this skill. - battle_consume_ammo(sd, skillid, skilllv); - - map_freeblock_unlock(); - return 0; -} -/*========================================== - * スキル使用(詠唱完了、ID指定) - *------------------------------------------ - */ -int skill_castend_id( int tid, unsigned int tick, int id,int data ) -{ - struct block_list *target, *src = map_id2bl(id); - struct map_session_data* sd = NULL; - struct mob_data* md = NULL; - struct unit_data* ud = unit_bl2ud(src); - struct status_change *sc; - int inf2; - - nullpo_retr(0, ud); - - BL_CAST( BL_PC, src, sd); - BL_CAST( BL_MOB, src, md); - - if( src->prev == NULL ) { - ud->skilltimer = -1; - return 0; - } - - switch (ud->skillid) { - //These three should become skill_castend_pos - case WE_CALLPARTNER: - case WE_CALLPARENT: - case WE_CALLBABY: - //Find a random spot to place the skill. [Skotlex] - inf2 = skill_get_splash(ud->skillid, ud->skilllv); - ud->skillx = src->x + inf2; - ud->skilly = src->y + inf2; - if (!map_random_dir(src, &ud->skillx, &ud->skilly)) { - ud->skillx = src->x; - ud->skilly = src->y; - } - return skill_castend_pos(tid,tick,id,data); - } - - if(ud->skillid != SA_CASTCANCEL ) { - if( ud->skilltimer != tid ) { - ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid); - ud->skilltimer = -1; - return 0; - } - if( sd && ud->skilltimer != -1 && (inf2 = pc_checkskill(sd,SA_FREECAST)) > 0) - status_quick_recalc_speed(sd, SA_FREECAST, inf2, 0); - ud->skilltimer=-1; - } - - if (ud->skilltarget == id) - target = src; - else - target = map_id2bl(ud->skilltarget); - - // Use a do so that you can break out of it when the skill fails. - do { - if(!target || target->prev==NULL) break; - - if(src->m != target->m || status_isdead(src)) break; - - if(ud->skillid == RG_BACKSTAP) { - int dir = map_calc_dir(src,target->x,target->y),t_dir = unit_getdir(target); - if(check_distance_bl(src, target, 0) || map_check_dir(dir,t_dir)) { - break; - } - } - if (ud->skillid == PR_LEXDIVINA) - { - sc = status_get_sc(target); - if (battle_check_target(src,target, BCT_ENEMY)<=0 && - (!sc || sc->data[SC_SILENCE].timer == -1)) - { //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex] - clif_skill_nodamage (src, target, ud->skillid, ud->skilllv, 0); - break; - } - } else { - inf2 = skill_get_inf(ud->skillid); - if((inf2&INF_ATTACK_SKILL || - (inf2&INF_SELF_SKILL && skill_get_inf2(ud->skillid)&INF2_NO_TARGET_SELF)) //Combo skills - && battle_check_target(src, target, BCT_ENEMY)<=0 - ) - break; - } - - //Avoid doing double checks for instant-cast skills. - if (tid != -1 && !status_check_skilluse(src, target, ud->skillid, 1)) - break; - - //沈黙や状態異常など - if(md) { - if(ud->skillid != NPC_EMOTION)//Set afterskill delay. - md->last_thinktime=tick + (tid==-1?status_get_adelay(src):status_get_amotion(src)); - if(md->skillidx >= 0) { - md->skilldelay[md->skillidx]=tick; - if (md->db->skill[md->skillidx].emotion >= 0) - clif_emotion(src, md->db->skill[md->skillidx].emotion); - } - } - - inf2 = skill_get_inf2(ud->skillid); - if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && src != target) { - int fail_flag = 1; - if(inf2 & INF2_PARTY_ONLY && battle_check_target(src, target, BCT_PARTY) > 0) - fail_flag = 0; - else if(inf2 & INF2_GUILD_ONLY && battle_check_target(src, target, BCT_GUILD) > 0) - fail_flag = 0; - - if (ud->skillid == PF_SOULCHANGE && map_flag_vs(target->m)) - //Soul Change overrides this restriction during pvp/gvg [Skotlex] - fail_flag = 0; - - if(fail_flag) - break; - } - - if(src != target && battle_config.skill_add_range && - !check_distance_bl(src, target, skill_get_range2(src,ud->skillid,ud->skilllv)+battle_config.skill_add_range)) - { - if (sd) { - clif_skill_fail(sd,ud->skillid,0,0); - if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex] - skill_check_condition(sd,ud->skillid, ud->skilllv,1); - } - break; - } - - if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv,1)) /* 使用条件チェック */ - break; - - if (ud->walktimer != -1 && ud->skillid != TK_RUN) - unit_stop_walking(src,1); - - if (ud->skillid == SA_MAGICROD) - ud->canact_tick = tick; - else - ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); - - if (skill_get_state(ud->skillid) != ST_MOVE_ENABLE) - unit_set_walkdelay(src, tick, battle_config.default_skill_delay+skill_get_walkdelay(ud->skillid, ud->skilllv), 1); - - if(battle_config.skill_log && battle_config.skill_log&src->type) - ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d)\n", - src->type, src->id, ud->skillid, ud->skilllv, target->id); - if (skill_get_casttype(ud->skillid) == CAST_NODAMAGE) - skill_castend_nodamage_id(src,target,ud->skillid,ud->skilllv,tick,0); - else - skill_castend_damage_id(src,target,ud->skillid,ud->skilllv,tick,0); - - sc = status_get_sc(src); - if(sc && sc->count && sc->data[SC_MAGICPOWER].timer != -1 && ud->skillid != HW_MAGICPOWER && ud->skillid != WZ_WATERBALL) - status_change_end(src,SC_MAGICPOWER,-1); - - if (ud->skilltimer == -1) { - if(md) md->skillidx = -1; - else ud->skillid = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill' - ud->skilllv = ud->skilltarget = 0; - } - return 1; - } while(0); - //Skill failed. - ud->skillid = ud->skilllv = ud->skilltarget = 0; - ud->canact_tick = tick; - if(sd) sd->skillitem = sd->skillitemlv = -1; - if(md) md->skillidx = -1; - return 0; -} - -/*========================================== - * スキル使用(詠唱完了、場所指定) - *------------------------------------------ - */ -int skill_castend_pos( int tid, unsigned int tick, int id,int data ) -{ - struct block_list* src = map_id2bl(id); - int maxcount; - struct map_session_data *sd = NULL; - struct unit_data *ud = unit_bl2ud(src); - struct mob_data *md = NULL; - - nullpo_retr(0, ud); - - BL_CAST( BL_PC , src, sd); - BL_CAST( BL_MOB, src, md); - - if( src->prev == NULL ) { - ud->skilltimer = -1; - return 0; - } - - if( ud->skilltimer != tid ) /* タイマIDの確認 */ - { - ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", ud->skilltimer, tid); - ud->skilltimer = -1; - return 0; - } - - if(sd && ud->skilltimer != -1 && (maxcount = pc_checkskill(sd,SA_FREECAST) > 0)) - status_quick_recalc_speed(sd, SA_FREECAST, maxcount, 0); - - ud->skilltimer=-1; - do { - if(status_isdead(src)) break; - - if (!(battle_config.skill_reiteration && src->type&battle_config.skill_reiteration) && - skill_get_unit_flag(ud->skillid)&UF_NOREITERATION && - skill_check_unit_range(src,ud->skillx,ud->skilly,ud->skillid,ud->skilllv) - ) - break; - - if (battle_config.skill_nofootset && src->type&battle_config.skill_nofootset && - skill_get_unit_flag(ud->skillid)&UF_NOFOOTSET && - skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skillid,ud->skilllv) - ) - break; - - if(battle_config.land_skill_limit && src->type&battle_config.land_skill_limit && - (maxcount = skill_get_maxcount(ud->skillid)) > 0 - ) { - int i; - for(i=0;iskillunit[i] && maxcount;i++) { - if(ud->skillunit[i]->skill_id == ud->skillid) - maxcount--; - } - if(!maxcount) - break; - } - - if(tid != -1) - { //Avoid double checks on instant cast skills. [Skotlex] - if (!status_check_skilluse(src, NULL, ud->skillid, 1)) - break; - if(battle_config.skill_add_range && - !check_distance_blxy(src, ud->skillx, ud->skilly, skill_get_range2(src,ud->skillid,ud->skilllv)+battle_config.skill_add_range)) { - if (sd && battle_config.skill_out_range_consume) //Consume items anyway. - skill_check_condition(sd,ud->skillid, ud->skilllv,1); - break; - } - } - - if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv, 1)) /* 使用条件チェック */ - break; - - if(md) { - md->last_thinktime=tick + (tid==-1?status_get_adelay(src):status_get_amotion(src)); - if(md->skillidx >= 0) { - md->skilldelay[md->skillidx]=tick; - if (md->db->skill[md->skillidx].emotion >= 0) - clif_emotion(src, md->db->skill[md->skillidx].emotion); - } - } - - if(battle_config.skill_log && battle_config.skill_log&src->type) - ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n", - src->type, src->id, ud->skillid, ud->skilllv, ud->skillx, ud->skilly); - unit_stop_walking(src,1); - ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); - unit_set_walkdelay(src, tick, battle_config.default_skill_delay+skill_get_walkdelay(ud->skillid, ud->skilllv), 1); - skill_castend_pos2(src,ud->skillx,ud->skilly,ud->skillid,ud->skilllv,tick,0); - - if (ud->skilltimer == -1) { - if (md) md->skillidx = -1; - else ud->skillid = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill' - ud->skilllv = ud->skillx = ud->skilly = 0; - } - return 1; - } while(0); - - ud->canact_tick = tick; - ud->skillid = ud->skilllv = 0; - if(sd) { - clif_skill_fail(sd,ud->skillid,0,0); - sd->skillitem = sd->skillitemlv = -1; - } - if(md) md->skillidx = -1; - return 0; - -} - -/*========================================== - * スキル使用?i詠?・完了?A??且w定の??ロの???j - *------------------------------------------ - */ -int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag) -{ - struct map_session_data *sd=NULL; - struct status_change *sc; - int i; - - //if(skilllv <= 0) return 0; - if(skillid > 0 && skilllv <= 0) return 0; // celest - - nullpo_retr(0, src); - - if(status_isdead(src)) - return 0; - - if(src->type==BL_PC) - sd=(struct map_session_data *)src; - - sc = status_get_sc(src); //Needed for Magic Power checks. - if (sc && !sc->count) - sc = NULL; //Unneeded. - - if(skillid != WZ_METEOR && - skillid != MO_BODYRELOCATION && - skillid != CR_CULTIVATION) - clif_skill_poseffect(src,skillid,skilllv,x,y,tick); - - switch(skillid) - { - case PR_BENEDICTIO: /* ?ケ??~福 */ - skill_area_temp[1] = src->id; - i = skill_get_splash(skillid, skilllv); - map_foreachinarea(skill_area_sub, - src->m, x-i, y-i, x+i, y+i, BL_PC, - src, skillid, skilllv, tick, flag|BCT_ALL|1, - skill_castend_nodamage_id); - map_foreachinarea(skill_area_sub, - src->m, x-i, y-i, x+i, y+i, BL_CHAR, - src, skillid, skilllv, tick, flag|BCT_ENEMY|1, - skill_castend_damage_id); - break; - - case BS_HAMMERFALL: - i = skill_get_splash(skillid, skilllv); - map_foreachinarea (skill_area_sub, - src->m, x-i, y-i, x+i, y+i, BL_CHAR, - src, skillid, skilllv, tick, flag|BCT_ENEMY|2, - skill_castend_nodamage_id); - break; - - case HT_DETECTING: /* ディテクティング */ - i = skill_get_splash(skillid, skilllv); - map_foreachinarea( status_change_timer_sub, - src->m, x-i, y-i, x+i,y+i,BL_CHAR, - src,status_get_sc(src),SC_SIGHT,tick); - if(battle_config.traps_setting&1) - map_foreachinarea( skill_reveal_trap, - src->m, x-i, y-i, x+i,y+i,BL_SKILL); - break; - - case MG_SAFETYWALL: /* セイフティウォ?ル */ - case MG_FIREWALL: /* ファイヤ?ウォ?ル */ - case MG_THUNDERSTORM: /* サンダ?スト?ム */ - case AL_PNEUMA: /* ニュ?マ */ - case WZ_ICEWALL: /* アイスウォ?ル */ - case WZ_FIREPILLAR: /* ファイアピラ? */ - case WZ_QUAGMIRE: /* クァグマイア */ - case WZ_VERMILION: /* ??ドオブヴァ?ミリオン */ - case WZ_STORMGUST: /* スト?ムガスト */ - case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ - case PR_SANCTUARY: /* サンクチュアリ */ - case PR_MAGNUS: /* マグヌスエクソシズム */ - case CR_GRANDCROSS: /* グランドク?ス */ - case NPC_GRANDDARKNESS: /*闇グランドク?ス*/ - case HT_SKIDTRAP: /* スキッドトラップ */ - case HT_LANDMINE: /* ランドマイン */ - case HT_ANKLESNARE: /* アンクルスネア */ - case HT_SHOCKWAVE: /* ショックウェ?ブトラップ */ - case HT_SANDMAN: /* サンドマン */ - case HT_FLASHER: /* フラッシャ? */ - case HT_FREEZINGTRAP: /* フリ?ジングトラップ */ - case HT_BLASTMINE: /* ブラストマイン */ - case HT_CLAYMORETRAP: /* クレイモア?トラップ */ - case AS_VENOMDUST: /* ベノムダスト */ - case AM_DEMONSTRATION: /* デモンストレ?ション */ - case PF_FOGWALL: /* フォグウォ?ル */ - case PF_SPIDERWEB: /* スパイダ?ウェッブ */ - case HT_TALKIEBOX: /* ト?キ?ボックス */ - case WE_CALLPARTNER: - case WE_CALLPARENT: - case WE_CALLBABY: - case AC_SHOWER: //Ground-placed skill implementation. - case GS_DESPERADO: - skill_unitsetting(src,skillid,skilllv,x,y,0); - flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete). - break; - - case RG_GRAFFITI: /* Graffiti [Valaris] */ - skill_clear_unitgroup(src); - skill_unitsetting(src,skillid,skilllv,x,y,0); - flag|=1; - break; - - case RG_CLEANER: // [Valaris] - i = skill_get_splash(skillid, skilllv); - map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL); - break; - case SA_VOLCANO: /* ボルケ?ノ */ - case SA_DELUGE: /* デリュ?ジ */ - case SA_VIOLENTGALE: /* バイオレントゲイル */ - case SA_LANDPROTECTOR: /* ランドプ?テクタ? */ - case NJ_SUITON: - skill_unitsetting(src,skillid,skilllv,x,y,0); - flag|=1; - break; - - case WZ_METEOR: //?テオスト?ム - { - int flag=0, area = skill_get_splash(skillid, skilllv); - short tmpx, tmpy, x1 = 0, y1 = 0; - if (sc && sc->data[SC_MAGICPOWER].timer != -1) - flag = flag|2; //Store the magic power flag for future use. [Skotlex] - for(i=0;i<2+(skilllv>>1);i++) { - tmpx = x; - tmpy = y; - if (!map_search_freecell(NULL, src->m, &tmpx, &tmpy, area, area, 1)) - continue; - if(!(flag&1)){ - clif_skill_poseffect(src,skillid,skilllv,tmpx,tmpy,tick); - flag=flag|1; - } - if(i > 0) - skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,(x1<<16)|y1,flag&2); //Only pass the Magic Power flag - x1 = tmpx; - y1 = tmpy; - } - skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,-1,flag&2); //Only pass the Magic Power flag - } - break; - - case AL_WARP: /* ??プポ?タル */ - if(sd) { - clif_skill_warppoint(sd,skillid,skilllv,mapindex_id2name(sd->status.save_point.map), - (skilllv>1 && sd->status.memo_point[0].map)?mapindex_id2name(sd->status.memo_point[0].map):"", - (skilllv>2 && sd->status.memo_point[1].map)?mapindex_id2name(sd->status.memo_point[1].map):"", - (skilllv>3 && sd->status.memo_point[2].map)?mapindex_id2name(sd->status.memo_point[2].map):""); - } - break; - - case MO_BODYRELOCATION: - if (unit_movepos(src, x, y, 1, 1)) { - clif_skill_poseffect(src,skillid,skilllv,src->x,src->y,tick); -// clif_slide(src, src->x, src->y); //Poseffect is the one that makes the char snap on the client... - if (sd) skill_blockpc_start (sd, MO_EXTREMITYFIST, 2000); - } - break; - case AM_CANNIBALIZE: // バイオプラント - if(sd) { - int id; - int summons[5] = { 1020, 1068, 1118, 1500, 1368 }; - struct mob_data *md; - - // Correct info, don't change any of this! [celest] - id = mob_once_spawn (sd, "this", x, y, sd->status.name, summons[skilllv-1] ,1,""); - - if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){ - md->master_id = sd->bl.id; - // different levels of HP according to skill level - md->hp = 1500 + skilllv * 200 + sd->status.base_level * 10; - md->max_hp = md->hp; //Update the max, too! [Skotlex] - md->special_state.ai = 1; - //非移動でアクティブで反撃する[0x0:非移動 0x1:移動 0x4:ACT 0x8:非ACT 0x40:反撃無 0x80:反撃有] - md->mode = MD_CANATTACK|MD_AGGRESSIVE; - md->deletetimer = add_timer (gettick() + skill_get_time(skillid,skilllv), mob_timer_delete, id, 0); - } - } - break; - case AM_SPHEREMINE: // スフィア?マイン - if(sd){ - int id; - struct mob_data *md; - - id = mob_once_spawn(sd, "this", x, y, sd->status.name, 1142, 1, ""); - if( (md=(struct mob_data *)map_id2bl(id)) !=NULL ){ - md->master_id = sd->bl.id; - md->hp = 2000 + skilllv * 400; - md->max_hp = md->hp; //Update the max, too! [Skotlex] - md->mode = md->db->mode|MD_CANMOVE; //Needed for the skill - md->special_state.ai = 2; - md->deletetimer = add_timer (gettick() + skill_get_time(skillid,skilllv), mob_timer_delete, id, 0); - } - } - break; - - // Slim Pitcher [Celest] - case CR_SLIMPITCHER: - { - if (sd) { - int i = skilllv%11 - 1; - int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]); - if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL || - sd->status.inventory[j].amount < skill_db[skillid].amount[i]) { - clif_skill_fail(sd,skillid,0,0); - return 1; - } - potion_flag = 1; - potion_hp = 0; - run_script(sd->inventory_data[j]->script,0,sd->bl.id,0); - pc_delitem(sd,j,skill_db[skillid].amount[i],0); - potion_flag = 0; - clif_skill_poseffect(src,skillid,skilllv,x,y,tick); - //Apply skill bonuses - potion_hp = potion_hp * (100 - + pc_checkskill(sd,CR_SLIMPITCHER)*10 - + pc_checkskill(sd,AM_POTIONPITCHER)*10 - + pc_checkskill(sd,AM_LEARNINGPOTION)*5 - )/100; - if(potion_hp > 0) { - i = skill_get_splash(skillid, skilllv); - map_foreachinarea(skill_area_sub, - src->m,x-i,y-i,x+i,y+i,BL_CHAR, - src,skillid,skilllv,tick,flag|BCT_PARTY|BCT_GUILD|1, - skill_castend_nodamage_id); - } - } - } - break; - - case HW_GANBANTEIN: - if (rand()%100 < 80) { - clif_skill_poseffect(src,skillid,skilllv,x,y,tick); - i = skill_get_splash(skillid, skilllv); - map_foreachinarea (skill_ganbatein, src->m, x-i, y-i, x+i, y+i, BL_SKILL); - } else { - clif_skill_fail(sd,skillid,0,0); - return 1; - } - break; - - case HW_GRAVITATION: - { - struct skill_unit_group *sg; - clif_skill_poseffect(src,skillid,skilllv,x,y,tick); - sg = skill_unitsetting(src,skillid,skilllv,x,y,0); - sc_start4(src,SkillStatusChangeTable[skillid],100, - skilllv,0,BCT_SELF,(int)sg,skill_get_time(skillid,skilllv)); - flag|=1; - } - break; - - // Plant Cultivation [Celest] - case CR_CULTIVATION: - { - if (sd) { - int i = skilllv - 1; - int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]); - if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL || - sd->status.inventory[j].amount < skill_db[skillid].amount[i]) { - clif_skill_fail(sd,skillid,0,0); - return 1; - } - pc_delitem(sd,j,skill_db[skillid].amount[i],0); - clif_skill_poseffect(src,skillid,skilllv,x,y,tick); - if (rand()%100 < 50) - mob_once_spawn(sd, "this", x, y, "--ja--",(skilllv < 2 ? 1084+rand()%2 : 1078+rand()%6), 1, ""); - else - clif_skill_fail(sd,skillid,0,0); - } - } - break; - - //Until they're at right position - gs_unit- [Vicious] - case GS_GROUNDDRIFT: /* グラウンドドリフト*/ - case NJ_KAENSIN: /* 火炎陣*/ - case NJ_BAKUENRYU: /* 爆炎龍*/ - case NJ_HYOUSYOURAKU: - skill_unitsetting(src,skillid,skilllv,x,y,0); - flag|=1; - break; - - case NJ_RAIGEKISAI: - map_foreachinrange(skill_attack_area, src, - skill_get_splash(skillid, skilllv), BL_CHAR, - BF_MAGIC, src, src, skillid, skilllv, tick, flag, BCT_ENEMY); - break; - } - - if (sc && sc->data[SC_MAGICPOWER].timer != -1) - status_change_end(&sd->bl,SC_MAGICPOWER,-1); - - if (sd && !(flag&1) && sd->state.arrow_atk) //Consume arrow if a ground skill was not invoked. [Skotlex] - battle_consume_ammo(sd, skillid, skilllv); - - return 0; -} - -/*========================================== - * スキル使用?i詠?・完了?Amap指定?j - *------------------------------------------ - */ -int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map) -{ - int x=0,y=0; - - nullpo_retr(0, sd); - -//Simplify skill_failed code. -#define skill_failed(sd) { sd->menuskill_id = sd->menuskill_lv = 0; } - - if( sd->bl.prev == NULL || pc_isdead(sd) ) - return 0; - - if(sd->sc.opt1 || sd->sc.option&OPTION_HIDE ) { - skill_failed(sd); - return 0; - } - //スキルが使えない?態異?中 - if(sd->sc.count && ( - sd->sc.data[SC_SILENCE].timer!=-1 || - sd->sc.data[SC_ROKISWEIL].timer!=-1 || - sd->sc.data[SC_AUTOCOUNTER].timer != -1 || - sd->sc.data[SC_STEELBODY].timer != -1 || - sd->sc.data[SC_DANCING].timer!=-1 || - sd->sc.data[SC_BERSERK].timer != -1 || - sd->sc.data[SC_MARIONETTE].timer != -1 - )) - return 0; - - if( skill_num != sd->menuskill_id) /* 不?ウパケットらしい */ - return 0; - - if (strlen(map) > MAP_NAME_LENGTH-1) - { //Map_length check, as it is sent by the client and we shouldn't trust it [Skotlex] - if (battle_config.error_log) - ShowError("skill_castend_map: Received map name '%s' too long!\n", map); - skill_failed(sd); - return 0; - } - - pc_stop_attack(sd); - pc_stop_walking(sd,0); - - if(battle_config.skill_log && battle_config.skill_log&BL_PC) - ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_num,map); - - if(strcmp(map,"cancel")==0) { - skill_failed(sd); - return 0; - } - - switch(skill_num){ - case AL_TELEPORT: /* テレポ?ト */ - if(strcmp(map,"Random")==0) - pc_randomwarp(sd,3); - else if (sd->menuskill_lv > 1) //Need lv2 to be able to warp here. - pc_setpos(sd,sd->status.save_point.map, - sd->status.save_point.x,sd->status.save_point.y,3); - break; - - case AL_WARP: /* ??プポ?タル */ - { - const struct point *p[4]; - struct skill_unit_group *group; - int i, lv, wx, wy; - int maxcount=0; - unsigned short mapindex; - mapindex = mapindex_name2id((char*)map); - if(!mapindex) { //Given map not found? - clif_skill_fail(sd,skill_num,0,0); - skill_failed(sd); - return 0; - } - p[0] = &sd->status.save_point; - p[1] = &sd->status.memo_point[0]; - p[2] = &sd->status.memo_point[1]; - p[3] = &sd->status.memo_point[2]; - - if((maxcount = skill_get_maxcount(skill_num)) > 0) { - for(i=0;iud.skillunit[i] && maxcount;i++) { - if(sd->ud.skillunit[i]->skill_id == skill_num) - maxcount--; - } - if(!maxcount) { - clif_skill_fail(sd,skill_num,0,0); - skill_failed(sd); - return 0; - } - } - - //When it's an item-used warp-portal, the skill-lv used is lost.. assume max level. - lv = sd->skillitem==skill_num?skill_get_max(skill_num):pc_checkskill(sd,skill_num); - wx = sd->menuskill_lv>>16; - wy = sd->menuskill_lv&0xffff; - - if(lv <= 0) return 0; - for(i=0;imap){ - x=p[i]->x; - y=p[i]->y; - break; - } - } - if(x==0 || y==0) { /* 不?ウパケット?H */ - skill_failed(sd); - return 0; - } - - if(!skill_check_condition(sd, sd->menuskill_id, lv,3)) //This checks versus skillid/skilllv... - { - skill_failed(sd); - return 0; - } - - if(skill_check_unit_range2(&sd->bl,wx,wy,skill_num,lv) > 0) { - clif_skill_fail(sd,0,0,0); - skill_failed(sd); - return 0; - } - if((group=skill_unitsetting(&sd->bl,skill_num,lv,wx,wy,0))==NULL) { - skill_failed(sd); - return 0; - } - //Now that there's a mapindex, use that in val3 rather than a string. [Skotlex] - group->val2=(x<<16)|y; - group->val3 = mapindex; - } - break; - } - - sd->menuskill_id = sd->menuskill_lv = 0; - return 0; -#undef skill_failed -} - -/*========================================== - * Initializes and sets a ground skill. - * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active) - * flag&2 is used to determine if this skill was casted with Magic Power active. - *------------------------------------------ - */ -struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag) -{ - struct skill_unit_group *group; - int i,limit,val1=0,val2=0,val3=0; - int count=0; - int target,interval,range,unit_flag; - struct skill_unit_layout *layout; - struct map_session_data *sd; - struct status_change *sc; - int active_flag=1; - - nullpo_retr(0, src); - - limit = skill_get_time(skillid,skilllv); - range = skill_get_unit_range(skillid,skilllv); - interval = skill_get_unit_interval(skillid); - target = skill_get_unit_target(skillid); - unit_flag = skill_get_unit_flag(skillid); - layout = skill_get_unit_layout(skillid,skilllv,src,x,y); - - BL_CAST(BL_PC, src, sd); - sc= status_get_sc(src); // for traps, firewall and fogwall - celest - if (sc && !sc->count) - sc = NULL; - - switch(skillid){ /* ?ン定 */ - - case MG_SAFETYWALL: /* セイフティウォ?ル */ - val2=skilllv+1; - break; - case MG_FIREWALL: /* ファイヤ?ウォ?ル */ - if(sc && sc->data[SC_VIOLENTGALE].timer!=-1) - limit = limit*3/2; - val2=4+skilllv; - break; - - case AL_WARP: /* ??プポ?タル */ - val1=skilllv+6; - if(!(flag&1)) - limit=2000; - active_flag=0; - break; - - case PR_SANCTUARY: /* サンクチュアリ */ - val1=(skilllv+3)*2; - val2=(skilllv>6)?777:skilllv*100; - break; - - case WZ_FIREPILLAR: /* ファイア?ピラ? */ - if((flag&1)!=0) - limit=1000; - val1=skilllv+2; - break; - case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex] - case AM_DEMONSTRATION: - if (map_flag_vs(src->m) && battle_config.vs_traps_bctall - && (src->type&battle_config.vs_traps_bctall)) - target = BCT_ALL; - break; - case HT_SHOCKWAVE: /* ショックウェ?ブトラップ */ - val1=skilllv*15+10; - case HT_SANDMAN: /* サンドマン */ - case HT_CLAYMORETRAP: /* クレイモア?トラップ */ - case HT_SKIDTRAP: /* スキッドトラップ */ - case HT_LANDMINE: /* ランドマイン */ - case HT_ANKLESNARE: /* アンクルスネア */ - case HT_FLASHER: /* フラッシャ? */ - case HT_FREEZINGTRAP: /* フリ?ジングトラップ */ - case HT_BLASTMINE: /* ブラストマイン */ - if (map_flag_gvg(src->m)) - limit *= 4; // longer trap times in WOE [celest] - if (battle_config.vs_traps_bctall && map_flag_vs(src->m) - && (src->type&battle_config.vs_traps_bctall)) - target = BCT_ALL; - break; - - case SA_LANDPROTECTOR: /* グランドク?ス */ - { - int aoe_diameter; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills - val1=skilllv*15+10; - aoe_diameter=skilllv+skilllv%2+5; - count=aoe_diameter*aoe_diameter; // -- this will not function if changed to ^2 (moonsoul) - } - //No break because we also have to check if we use gemstones. [Skotlex] - case SA_VOLCANO: - case SA_DELUGE: - case SA_VIOLENTGALE: - { - struct skill_unit_group *old_sg; - if ((old_sg = skill_locate_element_field(src)) != NULL) - { - if (old_sg->skill_id == skillid && old_sg->limit > 0) - { //Use the previous limit (minus the elapsed time) [Skotlex] - limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick); - if (limit < 0) //This can happen... - limit = skill_get_time(skillid,skilllv); - } - skill_clear_group(src,1); - } - break; - } - - case BA_DISSONANCE: - case DC_UGLYDANCE: - val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex] - break; - case BA_WHISTLE: - val1 = skilllv+(status_get_agi(src)/10); // Flee increase - val2 = ((skilllv+1)/2)+(status_get_luk(src)/10); // Perfect dodge increase - if(src->type == BL_PC){ - val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); - val2 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); - } - break; - case DC_HUMMING: - val1 = 2*skilllv+(status_get_dex(src)/10); // Hit increase - if(src->type == BL_PC) - val1 += 2*pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON); - break; - case BA_POEMBRAGI: - val1 = 3*skilllv+(status_get_dex(src)/10); // Casting time reduction - val2 = 3*skilllv+(status_get_int(src)/10); // After-cast delay reduction - if(src->type == BL_PC){ - val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); - val2 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); - } - break; - case DC_DONTFORGETME: - val1 = 3*skilllv+(status_get_dex(src)/10); // ASPD decrease - val2 = 2*skilllv+(status_get_agi(src)/10); // Movement speed decrease - if(src->type == BL_PC){ - val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON); - val2 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON); - } - break; - case BA_APPLEIDUN: - val1 = 5+2*skilllv+(status_get_vit(src)/10); // MaxHP percent increase - val2 = 30+5*skilllv+5*(status_get_vit(src)/10); // HP recovery - if(src->type == BL_PC){ - val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); - val2 += 5*pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); - } - break; - case DC_SERVICEFORYOU: - val1 = 10+skilllv+(status_get_int(src)/10); // MaxSP percent increase - val2 = 10+3*skilllv+(status_get_int(src)/10); // SP cost reduction - if(src->type == BL_PC){ - val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON); - val2 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON); - } - break; - case BA_ASSASSINCROSS: - val1 = 10+skilllv+(status_get_agi(src)/10); // ASPD increase - if(src->type == BL_PC) - val1 += pc_checkskill((struct map_session_data *)src,BA_MUSICALLESSON); - break; - case DC_FORTUNEKISS: - val1 = 10+skilllv+(status_get_luk(src)/10); // Critical increase - if(src->type == BL_PC) - val1 += pc_checkskill((struct map_session_data *)src,DC_DANCINGLESSON); - break; - case BD_LULLABY: - val1 = 11; //FIXME: This value is not used anywhere, what is it for? [Skotlex] - break; - case BD_DRUMBATTLEFIELD: - val1 = (skilllv+1)*25; //Watk increase - val2 = (skilllv+1)*2; //Def increase - break; - case BD_RINGNIBELUNGEN: - val1 = (skilllv+2)*25; //Watk increase - break; - case BD_SIEGFRIED: - val1 = 55 + skilllv*5; //Elemental Resistance - val2 = skilllv*10; //Status ailment resistance - break; - case PF_FOGWALL: /* フォグウォ?ル */ - if(sc && sc->data[SC_DELUGE].timer!=-1) limit *= 2; - break; - case RG_GRAFFITI: /* Graffiti */ - count=1; // Leave this at 1 [Valaris] - break; - case WE_CALLPARTNER: - if (sd) val1 = sd->status.partner_id; - break; - case WE_CALLPARENT: - if (sd) { - val1 = sd->status.father; - val2 = sd->status.mother; - } - break; - case WE_CALLBABY: - if (sd) val1 = sd->status.child; - break; - } - - nullpo_retr(NULL, group=skill_initunitgroup(src,(count > 0 ? count : layout->count), - skillid,skilllv,skill_get_unit_id(skillid,flag&1), limit, interval)); - group->val1=val1; - group->val2=val2; - group->val3=val3; - group->target_flag=target; - group->bl_flag= skill_get_unit_bl_target(skillid); - group->state.into_abyss = (sc && sc->data[SC_INTOABYSS].timer != -1); //Store into abyss state, to know it shouldn't give traps back. [Skotlex] - group->state.magic_power = (flag&2 || (sc && sc->data[SC_MAGICPOWER].timer != -1)); //Store the magic power flag. [Skotlex] - group->state.ammo_consume = (sd && sd->state.arrow_atk); //Store if this skill needs to consume ammo. - - if(skillid==HT_TALKIEBOX || - skillid==RG_GRAFFITI){ - group->valstr=(char *) aMallocA(MESSAGE_SIZE*sizeof(char)); - if(group->valstr==NULL){ - ShowFatalError("skill_castend_map: out of memory !\n"); - exit(1); - } - memcpy(group->valstr,talkie_mes,MESSAGE_SIZE-1); - group->valstr[MESSAGE_SIZE-1] = '\0'; - } - - //Why redefine local variables when the ones of the function can be reused? [Skotlex] - val1=skilllv; - val2=0; - limit=group->limit; - for(i=0;icount;i++){ - struct skill_unit *unit; - int ux,uy,alive=1; - ux = x + layout->dx[i]; - uy = y + layout->dy[i]; - switch (skillid) { - case MG_FIREWALL: /* ファイヤ?ウォ?ル */ - val2=group->val2; - break; - case WZ_ICEWALL: /* アイスウォ?ル */ - if(skilllv <= 1) - val1 = 500; - else - val1 = 200 + 200*skilllv; - break; - case RG_GRAFFITI: /* Graffiti [Valaris] */ - ux+=(i%5-2); - uy+=(i/5-2); - break; - } - //直?繝Xキルの???ン置?タ標?繧ノランドプ?テクタ?がないかチェック - if(range<=0) - map_foreachincell(skill_landprotector,src->m,ux,uy,BL_SKILL,skillid,&alive, src); - - if(alive && map_getcell(src->m,ux,uy,CELL_CHKWALL)) - alive = 0; - - if (alive && battle_config.skill_wall_check) { - //Check if there's a path between cell and center of casting. - if (!path_search_long(NULL,src->m,ux,uy,x,y)) - alive = 0; - } - - if(alive && skillid == WZ_ICEWALL) { - if(src->x == x && src->y==y) // Ice Wall not allowed on self [DracoRPG] - alive=0; - else { - val2=map_getcell(src->m,ux,uy,CELL_GETTYPE); - if(val2==5 || val2==1) - alive=0; - else - clif_changemapcell(src->m,ux,uy,5,0); - } - } - - if(alive){ - nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy)); - unit->val1=val1; - unit->val2=val2; - unit->limit=limit; - unit->range=range; - - if (range==0 && active_flag) - map_foreachincell(skill_unit_effect,unit->bl.m, - unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1); - } - } - - return group; -} - -/*========================================== - * スキルユニットの?動イベント - *------------------------------------------ - */ -int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick) -{ - struct skill_unit_group *sg; - struct block_list *ss; - struct status_change *sc; - int type,skillid; - - nullpo_retr(0, src); - nullpo_retr(0, bl); - - if(bl->prev==NULL || !src->alive || status_isdead(bl)) - return 0; - - nullpo_retr(0, sg=src->group); - nullpo_retr(0, ss=map_id2bl(sg->src_id)); - - if (skill_get_type(sg->skill_id) == BF_MAGIC && - map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR)) - return 0; //AoE skills are ineffective. [Skotlex] - - if (battle_check_target(&src->bl,bl,sg->target_flag)<=0) - return 0; - - sc = status_get_sc(bl); - - if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE) - return 0; //Hidden characters are inmune to AoE skills except Heaven's Drive. [Skotlex] - - type = SkillStatusChangeTable[sg->skill_id]; - skillid = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still. - switch (sg->unit_id) { - case UNT_SAFETYWALL: - //TODO: Find a more reliable way to handle the link to sg, this could cause dangling pointers. [Skotlex] - if (sc && sc->data[type].timer == -1) - sc_start4(bl,type,100,sg->skill_lv,sg->group_id,(int)sg,0,sg->limit); - break; - - case UNT_WARP_WAITING: - if(bl->type==BL_PC){ - struct map_session_data *sd = (struct map_session_data *)bl; - if((!sd->chatID || battle_config.chat_warpportal) - && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y) { - if (pc_setpos(sd,sg->val3,sg->val2>>16,sg->val2&0xffff,3) == 0) { - if (--sg->val1<=0 || sg->src_id == bl->id) - skill_delunitgroup(NULL, sg); - } - } - } else if(battle_config.mob_warpportal && bl->type != BL_PET) - unit_warp(bl,map_mapindex2mapid(sg->val3),sg->val2>>16,sg->val2&0xffff,3); - break; - - case UNT_QUAGMIRE: - if(sc && sc->data[type].timer==-1) - sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit); - break; - - case UNT_VOLCANO: - case UNT_DELUGE: - case UNT_VIOLENTGALE: - case UNT_SUITON: - if(sc && sc->data[type].timer==-1) - sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0, - skill_get_time2(sg->skill_id,sg->skill_lv)); - break; - - case UNT_RICHMANKIM: - case UNT_ETERNALCHAOS: - case UNT_DRUMBATTLEFIELD: - case UNT_RINGNIBELUNGEN: - case UNT_ROKISWEIL: - case UNT_INTOABYSS: - case UNT_SIEGFRIED: - case UNT_HERMODE: - //Needed to check when a dancer/bard leaves their ensemble area. - if (sg->src_id==bl->id && (!sc || sc->data[SC_SPIRIT].timer == -1 || sc->data[SC_SPIRIT].val2 != SL_BARDDANCER)) - return sg->skill_id; - if (sc && sc->data[type].timer==-1) - sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); - break; - case UNT_WHISTLE: - case UNT_ASSASSINCROSS: - case UNT_POEMBRAGI: - case UNT_APPLEIDUN: - case UNT_HUMMING: - case UNT_DONTFORGETME: - case UNT_FORTUNEKISS: - case UNT_SERVICEFORYOU: - if (sg->src_id==bl->id && (!sc || sc->data[SC_SPIRIT].timer == -1 || sc->data[SC_SPIRIT].val2 != SL_BARDDANCER)) - return 0; - if (!sc) - break; - if (sc->data[type].timer==-1) - sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); - else if (sc->data[type].val4 == 1) { - //Readjust timers since the effect will not last long. - sc->data[type].val4 = 0; - delete_timer(sc->data[type].timer, status_change_timer); - sc->data[type].timer = add_timer(tick+sg->limit, status_change_timer, bl->id, type); - } - break; -/* Basilica does not knocks back... - case UNT_BASILICA: - if (!(status_get_mode(bl)&MD_BOSS) && battle_check_target(&src->bl,bl,BCT_ENEMY)>0) - skill_blown(&src->bl,bl,1); - break; -*/ - case UNT_FOGWALL: - if (sc && sc->data[type].timer==-1) - { - sc_start4(bl, type, 100, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit); - if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) - skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, tick); - } - break; - - case UNT_GRAVITATION: - if (sc && sc->data[type].timer==-1) - sc_start4(bl,type,100,sg->skill_lv,5*sg->skill_lv,BCT_ENEMY,sg->group_id,sg->limit); - break; - - case UNT_ICEWALL: //Destroy the cell. [Skotlex] - src->val1 = 0; - if(src->limit + sg->tick > tick + 700) - src->limit = DIFF_TICK(tick+700,sg->tick); - break; - } - - return skillid; -} - -/*========================================== - * スキルユニットの発動イベント(タイマ?[発動) - *------------------------------------------ - */ -int skill_unit_onplace_timer(struct skill_unit *src,struct block_list *bl,unsigned int tick) -{ - struct skill_unit_group *sg; - struct block_list *ss; - struct map_session_data *sd = NULL; - struct status_change *tsc, *sc; - struct skill_unit_group_tickset *ts; - int type, skillid; - int diff=0; - - nullpo_retr(0, src); - nullpo_retr(0, bl); - - if (bl->prev==NULL || !src->alive || status_isdead(bl)) - return 0; - - nullpo_retr(0, sg=src->group); - nullpo_retr(0, ss=map_id2bl(sg->src_id)); - if (ss->type == BL_PC) sd = (struct map_session_data*)ss; - sc = status_get_sc(ss); //For magic power. - tsc = status_get_sc(bl); - type = SkillStatusChangeTable[sg->skill_id]; - skillid = sg->skill_id; - - if (sg->interval == -1) { - switch (sg->unit_id) { - case UNT_ANKLESNARE: //These happen when a trap is splash-triggered by multiple targets on the same cell. - case UNT_SPIDERWEB: - case UNT_FIREPILLAR_ACTIVE: - return 0; - default: - if (battle_config.error_log) - ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id); - return 0; - } - } - - if ((ts = skill_unitgrouptickset_search(bl,sg,tick))) - { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex] - diff = DIFF_TICK(tick,ts->tick); - if (diff < 0) - return 0; - ts->tick = tick+sg->interval; - - // GXは?dなっていたら3HITしない - if ((skillid==CR_GRANDCROSS || skillid==NPC_GRANDDARKNESS) && !battle_config.gx_allhit) - ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,0)-1); - } - //Temporarily set magic power to have it take effect. [Skotlex] - if (sg->state.magic_power && sc && sc->data[SC_MAGICPOWER].timer == -1 && sc->data[SC_MAGICPOWER].val1 > 0) - { - if (sd) - { //This is needed since we are not going to recall status_calc_pc... - sd->matk1 += sd->matk1 * 5*sc->data[SC_MAGICPOWER].val1/100; - sd->matk2 += sd->matk2 * 5*sc->data[SC_MAGICPOWER].val1/100; - } else - sc->data[SC_MAGICPOWER].timer = -2; //Note to NOT return from the function until this is unset! - } - - switch (sg->unit_id) { - case UNT_FIREWALL: - { - int count=0, t_ele = status_get_elem_type(bl); - if (t_ele == 3 || battle_check_undead(status_get_race(bl), t_ele)) { - //This is the best Aegis approximation we can do without - //changing the minimum skill unit interval. [Skotlex] - while (count++ < battle_config.firewall_hits_on_undead && src->val2-- && !status_isdead(bl)) - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*10,1); - } else { - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - src->val2--; - } - if (src->val2<=0) - skill_delunit(src); - break; - } - case UNT_SANCTUARY: - { - int race = status_get_race(bl); - - if (battle_check_undead(race, status_get_elem_type(bl)) || race==RC_DEMON) - { //Only damage enemies with offensive Sanctuary. [Skotlex] - if(battle_check_target(&src->bl,bl,BCT_ENEMY)>0 && - skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0)) - // reduce healing count if this was meant for damaging [hekate] - sg->val1 -= 2; - } else { - int heal = sg->val2; - if (status_get_hp(bl) >= status_get_max_hp(bl)) - break; - if (status_isimmune(bl)) - heal = 0; /* 黄金蟲カ?[ド?iヒ?[ル量0?j */ - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - battle_heal(NULL, bl, heal, 0, 0); - if (diff >= 500) - sg->val1--; // ?V規に入ったユニットだけカウント - } - if (sg->val1 <= 0) - skill_delunitgroup(NULL,sg); - break; - } - - case UNT_MAGNUS: - { - int race = status_get_race(bl); - if (!battle_check_undead(race,status_get_elem_type(bl)) && race!=RC_DEMON) - break; - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - } - - case UNT_ATTACK_SKILLS: - switch (sg->skill_id) - { - case SG_SUN_WARM: //SG skills [Komurka] - case SG_MOON_WARM: - case SG_STAR_WARM: - if(bl->type==BL_PC) - //Only damage SP [Skotlex] - pc_damage_sp((TBL_PC*)bl, 60, 0); - else if(!sd || pc_damage_sp(sd, 2, 0) >= 0) - //Otherwise, Knockback attack. - skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - default: - skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - } - break; - case UNT_DESPERADO: - if (!(rand()%10)) //Has a low chance of connecting. [Skotlex] - skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - - case UNT_FIREPILLAR_WAITING: - skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1); - skill_delunit(src); - break; - - case UNT_FIREPILLAR_ACTIVE: - map_foreachinrange(skill_attack_area,bl, - skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, - BF_MAGIC,ss,&src->bl,sg->skill_id,sg->skill_lv,tick,0,BCT_ENEMY); // area damage [Celest] - sg->interval = -1; //Mark it used up so others can't trigger it for massive splash damage. [Skotlex] - sg->limit=DIFF_TICK(tick,sg->tick) + 1500; - break; - - case UNT_SKIDTRAP: - { - skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv)|0x10000); - sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); - sg->limit=DIFF_TICK(tick,sg->tick)+1500; - sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] - } - break; - - case UNT_SPIDERWEB: - case UNT_ANKLESNARE: - if(sg->val2==0 && tsc && tsc->data[type].timer==-1){ - int sec = skill_get_time2(sg->skill_id,sg->skill_lv); - if (sc_start(bl,type,100,sg->skill_lv,sec)) - { - struct TimerData* td = get_timer(tsc->data[type].timer); - if (td) sec = DIFF_TICK(td->tick, tick); - map_moveblock(bl, src->bl.x, src->bl.y, tick); - clif_fixpos(bl); - sg->val2=bl->id; - } else - sec = 3000; //Couldn't trap it? - //clif_01ac(&src->bl); //Removed? Check the openkore description of this packet: [Skotlex] - // 01AC: long ID - // Indicates that an object is trapped, but ID is not a - // valid monster or player ID. - sg->limit = DIFF_TICK(tick,sg->tick)+sec; - sg->interval = -1; - src->range = 0; - } - break; - - case UNT_VENOMDUST: - if(tsc && tsc->data[type].timer==-1 ) - status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8); - break; - - case UNT_LANDMINE: - skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE); - sg->limit=DIFF_TICK(tick,sg->tick)+1500; - sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] - break; - - case UNT_BLASTMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_FREEZINGTRAP: - case UNT_CLAYMORETRAP: -// This ain't used anymore.... -// map_foreachinrange(skill_count_target,&src->bl, -// skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, -// &src->bl,&splash_count); - map_foreachinrange(skill_trap_splash,&src->bl, - skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, - &src->bl,tick); - sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); - sg->limit=DIFF_TICK(tick,sg->tick)+1500; - sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] - break; - - case UNT_TALKIEBOX: - if (sg->src_id == bl->id) //自分が踏んでも発動しない - break; - if (sg->val2 == 0){ - clif_talkiebox(&src->bl, sg->valstr); - sg->unit_id = UNT_USED_TRAPS; - clif_changetraplook(&src->bl, UNT_USED_TRAPS); - sg->limit = DIFF_TICK(tick, sg->tick) + 5000; - sg->val2 = -1; //踏んだ - sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] - } - break; - - case UNT_LULLABY: - if (ss->id == bl->id) - break; - skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick); - break; - - case UNT_UGLYDANCE: //Ugly Dance [Skotlex] - if (ss->id == bl->id) - break; - if (bl->type == BL_PC) - skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick); - break; - - case UNT_DISSONANCE: - skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - break; - - case UNT_APPLEIDUN: //Apple of Idun [Skotlex] - { - int heal; - if (sg->src_id == bl->id) - break; - heal = sg->val2; - clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); - battle_heal(NULL, bl, heal, 0, 0); - break; - } - - case UNT_DEMONSTRATION: - skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); - break; - - case UNT_GOSPEL: - if (rand()%100 > sg->skill_lv*10) - break; - if (ss != bl && battle_check_target(ss,bl,BCT_PARTY)>0) { // Support Effect only on party, not guild - int i = rand()%13; // Positive buff count - switch (i) - { - case 0: // Heal 1~9999 HP - { - int heal = rand() %9999+1; - clif_skill_nodamage(ss,bl,AL_HEAL,heal,1); - battle_heal(NULL,bl,heal,0,0); - } - break; - case 1: // End all negative status - status_change_clear_buffs(bl,2); - break; - case 2: // Level 10 Blessing - sc_start(bl,SC_BLESSING,100,10,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 3: // Level 10 Increase AGI - sc_start(bl,SC_INCREASEAGI,100,10,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 4: // Enchant weapon with Holy element - sc_start(bl,SC_ASPERSIO,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 5: // Enchant armor with Holy element - sc_start(bl,SC_BENEDICTIO,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 6: // MaxHP +100% - sc_start(bl,SC_INCMHPRATE,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 7: // MaxSP +100% - sc_start(bl,SC_INCMSPRATE,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 8: // All stats +20 - sc_start(bl,SC_INCALLSTATUS,100,20,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 9: // DEF +25% - sc_start(bl,SC_INCDEFRATE,100,25,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 10: // ATK +100% - sc_start(bl,SC_INCATKRATE,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 11: // HIT/Flee +50 - sc_start(bl,SC_INCHIT,100,50,skill_get_time2(sg->skill_id, sg->skill_lv)); - sc_start(bl,SC_INCFLEE,100,50,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 12: // Immunity to all status - sc_start(bl,SC_SCRESIST,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - } - } - else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) { // Offensive Effect - int i = rand()%9; // Negative buff count - switch (i) - { - case 0: // Deal 1~9999 damage - skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - case 1: // Curse - sc_start(bl,SC_CURSE,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 2: // Blind - sc_start(bl,SC_BLIND,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 3: // Poison - sc_start(bl,SC_POISON,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 4: // Level 10 Provoke - sc_start(bl,SC_PROVOKE,100,10,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 5: // DEF -100% - sc_start(bl,SC_INCDEFRATE,100,-100,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 6: // ATK -100% - sc_start(bl,SC_INCATKRATE,100,-100,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 7: // Flee -100% - sc_start(bl,SC_INCFLEERATE,100,-100,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - case 8: // Speed/ASPD -25% - sc_start4(bl,SC_GOSPEL,100,1,0,0,BCT_ENEMY,skill_get_time2(sg->skill_id, sg->skill_lv)); - break; - } - } - break; - - case UNT_GRAVITATION: - skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - } - if (sg->state.magic_power && sc && sc->data[SC_MAGICPOWER].timer < 0 && sc->data[SC_MAGICPOWER].val1 > 0) - { //Unset Magic Power. - if (sd) - { - sd->matk1 = 100*sd->matk1/(100 + 5*sc->data[SC_MAGICPOWER].val1); - sd->matk2 = 100*sd->matk2/(100 + 5*sc->data[SC_MAGICPOWER].val1); - } else - sc->data[SC_MAGICPOWER].timer = -1; - } - - if (bl->type == BL_MOB && ss != bl) - mobskill_event((TBL_MOB*)bl, ss, tick, MSC_SKILLUSED|(skillid<<16)); - - return skillid; -} -/*========================================== - * スキルユニットから離?する(もしくはしている)?? - *------------------------------------------ - */ -int skill_unit_onout(struct skill_unit *src,struct block_list *bl,unsigned int tick) -{ - struct skill_unit_group *sg; - struct status_change *sc; - int type; - - nullpo_retr(0, src); - nullpo_retr(0, bl); - nullpo_retr(0, sg=src->group); - sc = status_get_sc(bl); - if (sc && !sc->count) - sc = NULL; - - type = SkillStatusChangeTable[sg->skill_id]; - - if (bl->prev==NULL || !src->alive || //Need to delete the trap if the source died. - (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB)) - return 0; - - switch(sg->unit_id){ - case UNT_SAFETYWALL: - if (sc && sc->data[type].timer!=-1) - status_change_end(bl,type,-1); - break; - case UNT_ANKLESNARE: - { - struct block_list *target = map_id2bl(sg->val2); - if(target && target == bl){ - status_change_end(bl,SC_ANKLE,-1); - sg->limit=DIFF_TICK(tick,sg->tick)+1000; - sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] - } - else - return 0; - break; - } - case UNT_BASILICA: //Clear basilica if the owner moved [Skotlex] - case UNT_HERMODE: //Clear Hermode if the owner moved. - if (sc && sc->data[type].timer!=-1 && sc->data[type].val3 == BCT_SELF && sc->data[type].val4 == sg->group_id) - status_change_end(bl,type,-1); - break; - - case UNT_SPIDERWEB: /* スパイダ?ウェッブ */ - { - struct block_list *target = map_id2bl(sg->val2); - if (target && target==bl) - { - status_change_end(bl,SC_SPIDERWEB,-1); - sg->limit = DIFF_TICK(tick,sg->tick)+1000; - } - break; - } - } - return sg->skill_id; -} - -/*========================================== - * Triggered when a char steps out of a skill group [Skotlex] - *------------------------------------------ - */ -static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick) -{ - struct status_change *sc; - int type; - - sc = status_get_sc(bl); - if (sc && !sc->count) - sc = NULL; - - type = SkillStatusChangeTable[skill_id]; - - switch (skill_id) - { - case WZ_QUAGMIRE: - if (bl->type==BL_MOB) - break; - if (sc && sc->data[type].timer != -1) - status_change_end(bl, type, -1); - break; - - case BD_LULLABY: - case BD_RICHMANKIM: - case BD_ETERNALCHAOS: - case BD_DRUMBATTLEFIELD: - case BD_RINGNIBELUNGEN: - case BD_ROKISWEIL: - case BD_INTOABYSS: - case BD_SIEGFRIED: - if(sc && sc->data[SC_DANCING].timer != -1 && sc->data[SC_DANCING].val1 == skill_id) - { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex] - //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance. - //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner, - //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble - //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel - //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it. - skill_stop_dancing(bl); - } - case MG_SAFETYWALL: - case AL_PNEUMA: - case SA_VOLCANO: - case SA_DELUGE: - case SA_VIOLENTGALE: - case CG_HERMODE: - case HW_GRAVITATION: - case NJ_SUITON: - if (sc && sc->data[type].timer != -1) - status_change_end(bl, type, -1); - break; - - case BA_POEMBRAGI: - case BA_WHISTLE: - case BA_ASSASSINCROSS: - case BA_APPLEIDUN: - case DC_HUMMING: - case DC_DONTFORGETME: - case DC_FORTUNEKISS: - case DC_SERVICEFORYOU: - if (sc && sc->data[type].timer != -1) - { - delete_timer(sc->data[type].timer, status_change_timer); - //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas... - //not possible on our current implementation. - sc->data[type].val4 = 1; //Store the fact that this is a "reduced" duration effect. - sc->data[type].timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type); - } - break; - case PF_FOGWALL: - if (sc && sc->data[type].timer != -1) - { - status_change_end(bl,type,-1); - if (sc->data[SC_BLIND].timer!=-1) - { - if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex] - status_change_end(bl, SC_BLIND, -1); - else { - delete_timer(sc->data[SC_BLIND].timer, status_change_timer); - sc->data[SC_BLIND].timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND); - } - } - } - break; - case UNT_GOSPEL: - if (sc && sc->data[type].timer != -1 && sc->data[type].val4 == BCT_ALL) //End item-no-use Gospel Effect. [Skotlex] - status_change_end(bl, type, -1); - break; - - } - return skill_id; -} - -/*========================================== - * Invoked when a unit cell has been placed/removed/deleted. - * flag values: - * flag&1: Invoke onplace function (otherwise invoke onout) - * flag&4: Invoke a onleft call (the unit might be scheduled for deletion) - *------------------------------------------ - */ -int skill_unit_effect(struct block_list *bl,va_list ap) -{ - struct skill_unit *unit; - struct skill_unit_group *group; - int flag; - unsigned int tick; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, unit=va_arg(ap,struct skill_unit*)); - tick = va_arg(ap,unsigned int); - flag = va_arg(ap,unsigned int); - - if (!unit->alive || bl->prev==NULL) - return 0; - - nullpo_retr(0, group=unit->group); - - if (flag&1) - skill_unit_onplace(unit,bl,tick); - else - skill_unit_onout(unit,bl,tick); - - if (flag&4) skill_unit_onleft(group->skill_id, bl, tick); - return 0; -} - -/*========================================== - * スキルユニットの限界イベント - *------------------------------------------ - */ -int skill_unit_onlimit(struct skill_unit *src,unsigned int tick) -{ - struct skill_unit_group *sg; - nullpo_retr(0, src); - nullpo_retr(0, sg=src->group); - - switch(sg->unit_id){ - case UNT_WARP_ACTIVE: /* ??プポ?タル(?動前) */ - { - struct skill_unit_group *group= - skill_unitsetting(map_id2bl(sg->src_id),sg->skill_id,sg->skill_lv, - src->bl.x,src->bl.y,1); - if(group == NULL) - return 0; - group->val2=sg->val2; //Copy the (x,y) position you warp to - group->val3=sg->val3; //as well as the mapindex to warp to. - } - break; - - case UNT_ICEWALL: /* アイスウォ?ル */ - clif_changemapcell(src->bl.m,src->bl.x,src->bl.y,src->val2,1); - break; - case UNT_CALLFAMILY: /* なたに?いたい */ - { - struct map_session_data *sd = NULL; - if(sg->val1) { - sd = map_charid2sd(sg->val1); - sg->val1 = 0; - if (sd && !map[sd->bl.m].flag.nowarp) - pc_setpos(sd,map[src->bl.m].index,src->bl.x,src->bl.y,3); - } - if(sg->val2) { - sd = map_charid2sd(sg->val2); - sg->val2 = 0; - if (sd && !map[sd->bl.m].flag.nowarp) - pc_setpos(sd,map[src->bl.m].index,src->bl.x,src->bl.y,3); - } - } - break; - } - - return 0; -} -/*========================================== - * スキルユニットのダ??ジイベント - *------------------------------------------ - */ -int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl, - int damage,unsigned int tick) -{ - struct skill_unit_group *sg; - - nullpo_retr(0, src); - nullpo_retr(0, sg=src->group); - - if (skill_get_inf2(sg->skill_id)&INF2_TRAP && damage > 0) - skill_delunitgroup(NULL,sg); - else - switch(sg->unit_id){ - case UNT_ICEWALL: - src->val1-=damage; - break; - default: - damage = 0; - break; - } - return damage; -} - -static int skill_moonlit_sub(struct block_list *bl, va_list ap) { - struct block_list *src = va_arg(ap, struct block_list*); - struct block_list *partner = va_arg(ap, struct block_list*); - int blowcount = va_arg(ap, int); - if (bl == src || bl == partner) - return 0; - skill_blown(src, bl, blowcount); - return 1; -} - -/*========================================== - * Starts the moonlit effect by first knocking back all other characters in the vecinity. - * partner may be null, but src cannot be. - *------------------------------------------ - */ -static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv) -{ - int range = skill_get_range2(src, CG_MOONLIT, skilllv); - int blowcount = range+1, time = skill_get_time(CG_MOONLIT,skilllv); - - map_foreachinrange(skill_moonlit_sub,src, - skill_get_splash(CG_MOONLIT, skilllv), - BL_CHAR,src,partner,blowcount); - if(partner) - map_foreachinrange(skill_moonlit_sub,partner, - skill_get_splash(CG_MOONLIT, skilllv), - BL_CHAR,src,partner,blowcount); - - sc_start4(src,SC_DANCING,100,CG_MOONLIT,0,0,partner?partner->id:BCT_SELF,time+1000); - sc_start4(src,SkillStatusChangeTable[CG_MOONLIT],100,skilllv,0,0,0,time); - - if (partner) { - sc_start4(partner,SC_DANCING,100,CG_MOONLIT,0,0,src->id,time+1000); - sc_start4(partner,SkillStatusChangeTable[CG_MOONLIT],100,skilllv,0,0,0,time); - } - -} -/*========================================== - * 範??キャラ存?ン確認判定??(foreachinarea) - *------------------------------------------ - */ - -static int skill_check_condition_char_sub (struct block_list *bl, va_list ap) -{ - int *c, skillid; - struct block_list *src; - struct map_session_data *sd; - struct map_session_data *tsd; - int *p_sd; //Contains the list of characters found. - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, tsd=(struct map_session_data*)bl); - nullpo_retr(0, src=va_arg(ap,struct block_list *)); - nullpo_retr(0, sd=(struct map_session_data*)src); - - c=va_arg(ap,int *); - p_sd = va_arg(ap, int *); - skillid = va_arg(ap,int); - - if ((skillid != PR_BENEDICTIO && *c >=1) || *c >=2) - return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex] - - if (bl == src) - return 0; - - if(pc_isdead(tsd)) - return 0; - - if (tsd->sc.count && (tsd->sc.data[SC_SILENCE].timer != -1 || tsd->sc.data[SC_STUN].timer != -1)) - return 0; - - switch(skillid) - { - case PR_BENEDICTIO: /* ?ケ??~福 */ - { - int dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y); - dir = (unit_getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing. - if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest. - && sd->status.sp >= 10) - p_sd[(*c)++]=tsd->bl.id; - return 1; - } - default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex] - { - int skilllv; - if(pc_issit(tsd) || !unit_can_move(&tsd->bl)) - return 0; - if (sd->status.sex != tsd->status.sex && - (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && - (skilllv = pc_checkskill(tsd, skillid)) > 0 && - (tsd->weapontype1==W_MUSICAL || tsd->weapontype1==W_WHIP) && - sd->status.party_id && tsd->status.party_id && - sd->status.party_id == tsd->status.party_id && - tsd->sc.data[SC_DANCING].timer == -1) - { - p_sd[(*c)++]=tsd->bl.id; - return skilllv; - } else { - return 0; - } - } - break; - } - return 0; -} - -/*========================================== - * Checks and stores partners for ensemble skills [Skotlex] - *------------------------------------------ - */ -int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag) -{ - static int c=0; - static int p_sd[2] = { 0, 0 }; - int i; - if (cast_flag) - { //Execute the skill on the partners. - struct map_session_data* tsd; - switch (skill_id) - { - case PR_BENEDICTIO: - for (i = 0; i < c; i++) - { - if ((tsd = map_id2sd(p_sd[i])) != NULL) - pc_damage_sp(tsd, 10, 0); - } - return c; - case CG_MOONLIT: - if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL) - { - clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1); - skill_moonlit(&sd->bl, &tsd->bl, *skill_lv); - tsd->skillid_dance = skill_id; - tsd->skilllv_dance = *skill_lv; - } - return c; - default: //Warning: Assuming Ensemble skills here (for speed) - if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL) - { - sd->sc.data[SC_DANCING].val4= tsd->bl.id; - sc_start4(&tsd->bl,SC_DANCING,100,skill_id,sd->sc.data[SC_DANCING].val2,0,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000); - clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1); - tsd->skillid_dance = skill_id; - tsd->skilllv_dance = *skill_lv; - } - return c; - } - } - //Else: new search for partners. - c = 0; - memset (p_sd, 0, sizeof(p_sd)); - i = map_foreachinrange(skill_check_condition_char_sub, &sd->bl, - range, BL_PC, &sd->bl, &c, &p_sd, skill_id); - - if (skill_id != PR_BENEDICTIO) //Apply the average lv to encore skills. - *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners. - return c; -} - -/*========================================== - * 範??バイオプラント?Aスフィアマイン用Mob存?ン確認判定??(foreachinarea) - *------------------------------------------ - */ - -static int skill_check_condition_mob_master_sub(struct block_list *bl,va_list ap) -{ - int *c,src_id=0,mob_class=0; - struct mob_data *md; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, md=(struct mob_data*)bl); - nullpo_retr(0, src_id=va_arg(ap,int)); - nullpo_retr(0, mob_class=va_arg(ap,int)); - nullpo_retr(0, c=va_arg(ap,int *)); - - if(md->class_==mob_class && md->master_id==src_id) - (*c)++; - return 0; -} - -static int skill_check_condition_hermod_sub(struct block_list *bl,va_list ap) -{ - struct npc_data *nd; - nd=(struct npc_data*)bl; - - if (nd->bl.subtype == WARP) - return 1; - return 0; -} - -/*========================================== - * Determines if a given skill should be made to consume ammo - * when used by the player. [Skotlex] - *------------------------------------------ - */ -int skill_isammotype(TBL_PC *sd, int skill) -{ - return ( - (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) && - skill != HT_PHANTASMIC && skill != GS_MAGICALBULLET && - skill_get_type(skill) == BF_WEAPON && !(skill_get_nk(skill)&NK_NO_DAMAGE) - ); -} - -/*========================================== - * Checks that you have the requirements for casting a skill. - * Flag: - * &1: finished casting the skill (invoke hp/sp/item consumption) - * &2: picked menu entry (Warp Portal, Teleport and other menu based skills) - *------------------------------------------ - */ -int skill_check_condition(struct map_session_data *sd,int skill, int lv, int type) -{ - int i,j,hp,sp,hp_rate,sp_rate,zeny,weapon,ammo,ammo_qty,state,spiritball,mhp; - int index[10],itemid[10],amount[10]; - int force_gem_flag = 0; - int delitem_flag = 1, checkitem_flag = 1; - - nullpo_retr(0, sd); - - if (lv <= 0) return 0; - - if( battle_config.gm_skilluncond && - pc_isGM(sd)>= battle_config.gm_skilluncond && - sd->skillitem != skill) - { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] - sd->skillitem = sd->skillitemlv = -1; - return 1; - } - - if( sd->sc.opt1 ) { - clif_skill_fail(sd,skill,0,0); - sd->skillitem = sd->skillitemlv = -1; - return 0; - } - if(pc_is90overweight(sd)) { - clif_skill_fail(sd,skill,9,0); - sd->skillitem = sd->skillitemlv = -1; - return 0; - } - - if (sd->state.abra_flag) - { - sd->skillitem = sd->skillitemlv = -1; - if(type&1) sd->state.abra_flag = 0; - return 1; - } - - if (sd->menuskill_id == AM_PHARMACY && - (skill == AM_PHARMACY || skill == AC_MAKINGARROW || skill == BS_REPAIRWEAPON || - skill == AM_TWILIGHT1 || skill == AM_TWILIGHT2 || skill == AM_TWILIGHT3 - )) { - sd->skillitem = sd->skillitemlv = -1; - return 0; - } - - if(sd->skillitem == skill) { /* アイテムの??無???ャ功 */ - if(!type) //When a target was selected - { //Consume items that were skipped in pc_use_item [Skotlex] - if((i = sd->itemindex) == -1 || - sd->status.inventory[i].nameid != sd->itemid || - sd->inventory_data[i] == NULL || - !sd->inventory_data[i]->flag.delay_consume || - sd->status.inventory[i].amount < 1 - ) - { //Something went wrong, item exploit? - sd->itemid = sd->itemindex = -1; - return 0; - } - //Consume - sd->itemid = sd->itemindex = -1; - if(skill == WZ_EARTHSPIKE - && sd->sc.data[SC_TKREST].timer != -1 && rand()%100 > sd->sc.data[SC_TKREST].val2) // [marquis007] - ; //Do not consume item. - else - pc_delitem(sd,i,1,0); - } - if (type&1) //Casting finished - sd->skillitem = sd->skillitemlv = -1; - return 1; - } - // for the guild skills [celest] - if (skill >= GD_SKILLBASE) - j = GD_SKILLRANGEMIN + skill - GD_SKILLBASE; - else - j = skill; - if (j < 0 || j >= MAX_SKILL_DB) - return 0; - //Code speedup, rather than using skill_get_* over and over again. - if (lv < 1 || lv > MAX_SKILL_LEVEL) - return 0; - hp = skill_db[j].hp[lv-1]; /* ?チ費HP */ - sp = skill_db[j].sp[lv-1]; /* ?チ費SP */ - if((sd->skillid_old == BD_ENCORE) && skill == sd->skillid_dance) - sp=sp/2; //アンコ?ル時はSP?チ費が半分 - hp_rate = skill_db[j].hp_rate[lv-1]; - sp_rate = skill_db[j].sp_rate[lv-1]; - zeny = skill_db[j].zeny[lv-1]; - weapon = skill_db[j].weapon; - ammo = skill_db[j].ammo; - ammo_qty = skill_db[j].ammo_qty[lv-1]; - state = skill_db[j].state; - spiritball = skill_db[j].spiritball[lv-1]; - mhp = skill_db[j].mhp[lv-1]; /* ?チ費HP */ - for(i = 0; i < 10; i++) { - itemid[i] = skill_db[j].itemid[i]; - amount[i] = skill_db[j].amount[i]; - } - if(mhp > 0) - hp += (sd->status.max_hp * mhp)/100; - if(hp_rate > 0) - hp += (sd->status.hp * hp_rate)/100; - else - hp += (sd->status.max_hp * abs(hp_rate))/100; - if(sp_rate > 0) - sp += (sd->status.sp * sp_rate)/100; - else - sp += (sd->status.max_sp * abs(sp_rate))/100; - - if (!ammo && skill && skill_isammotype(sd, skill)) - { //Assume this skill is using the weapon, therefore it requires arrows. - ammo = 2; //1<<1 <- look 1 (arrows) moved right 1 times. - ammo_qty = skill_get_num(skill, lv); - if (ammo_qty < 0) ammo_qty *= -1; - } - - switch(skill) { // Check for cost reductions due to skills & SCs - case MC_MAMMONITE: - if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0) - zeny -= zeny*10/100; - break; - case AL_HOLYLIGHT: - if(sd->sc.data[SC_SPIRIT].timer!=-1 && sd->sc.data[SC_SPIRIT].val2 == SL_PRIEST) - sp *= 5; - break; - case SL_SMA: - case SL_STUN: - case SL_STIN: - { - int kaina_lv = pc_checkskill(sd,SL_KAINA); - - if(kaina_lv==0 || sd->status.base_level<70) - break; - if(sd->status.base_level>=90) - sp -= sp*7*kaina_lv/100; - else if(sd->status.base_level>=80) - sp -= sp*5*kaina_lv/100; - else if(sd->status.base_level>=70) - sp -= sp*3*kaina_lv/100; - } - break; - case MO_TRIPLEATTACK: - case MO_CHAINCOMBO: - case MO_COMBOFINISH: - case CH_TIGERFIST: - case CH_CHAINCRUSH: - if(sd->sc.data[SC_SPIRIT].timer!=-1 && sd->sc.data[SC_SPIRIT].val2 == SL_MONK) - sp -= sp*25/100; //FIXME: Need real data. this is a custom value. - break; - } - - if(sd->dsprate!=100) - sp=sp*sd->dsprate/100; /* ?チ費SP?C?ウ */ - - switch(skill) { - case SA_CASTCANCEL: - if(sd->ud.skilltimer == -1) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case BS_MAXIMIZE: /* マキシマイズパ?? */ - case NV_TRICKDEAD: /* 死んだふり */ - case TF_HIDING: /* ハイディング */ - case AS_CLOAKING: /* ク??キング */ - case CR_AUTOGUARD: /* オ?トガ?ド */ - case CR_DEFENDER: /* ディフェンダ? */ - case ST_CHASEWALK: - case PA_GOSPEL: - case CR_SHRINK: - case TK_RUN: - if(sd->sc.data[SkillStatusChangeTable[skill]].timer!=-1) - return 1; /* 解?怩キる??はSP?チ費しない */ - break; - - case AL_WARP: - if(!(type&2)) //Delete the item when the portal has been selected (type&2). [Skotlex] - delitem_flag = 0; - if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza] - clif_displaymessage(sd->fd, "Duel: Can't use warp in duel."); - return 0; - } - break; - case MO_CALLSPIRITS: /* ?功 */ - if(sd->spiritball >= lv) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case CH_SOULCOLLECT: /* 狂?功 */ - if(sd->spiritball >= 5) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case MO_FINGEROFFENSIVE: //指? - if (sd->spiritball > 0 && sd->spiritball < spiritball) { - spiritball = sd->spiritball; - sd->spiritball_old = sd->spiritball; - } - else sd->spiritball_old = lv; - break; - case MO_BODYRELOCATION: - if (sd->sc.data[SC_EXPLOSIONSPIRITS].timer!=-1) - spiritball = 0; - break; - case MO_CHAINCOMBO: //連打?カ - if(sd->sc.data[SC_BLADESTOP].timer==-1){ - if(sd->sc.data[SC_COMBO].timer == -1 || sd->sc.data[SC_COMBO].val1 != MO_TRIPLEATTACK) - return 0; - } - break; - case MO_COMBOFINISH: //猛龍? - if(sd->sc.data[SC_COMBO].timer == -1 || sd->sc.data[SC_COMBO].val1 != MO_CHAINCOMBO) - return 0; - break; - case CH_TIGERFIST: //伏虎? - if(sd->sc.data[SC_COMBO].timer == -1 || sd->sc.data[SC_COMBO].val1 != MO_COMBOFINISH) - return 0; - break; - case CH_CHAINCRUSH: //連柱崩? - if(sd->sc.data[SC_COMBO].timer == -1) - return 0; - if(sd->sc.data[SC_COMBO].val1 != MO_COMBOFINISH && sd->sc.data[SC_COMBO].val1 != CH_TIGERFIST) - return 0; - break; - case MO_EXTREMITYFIST: -// if(sd->sc.data[SC_EXTREMITYFIST].timer != -1) //To disable Asura during the 5 min skill block uncomment this... -// return 0; - if(sd->sc.data[SC_BLADESTOP].timer!=-1) - spiritball--; - else if (sd->sc.data[SC_COMBO].timer != -1) { - if (sd->sc.data[SC_COMBO].val1 == MO_COMBOFINISH) - spiritball = 4; - else if (sd->sc.data[SC_COMBO].val1 == CH_TIGERFIST) - spiritball = 3; - else if (sd->sc.data[SC_COMBO].val1 == CH_CHAINCRUSH) - spiritball = sd->spiritball?sd->spiritball:1; - //It should consume whatever is left as long as it's at least 1. - } else if(!type && !unit_can_move(&sd->bl)) //Check only on begin casting. - { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex] - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - - case TK_MISSION: //Does not works on Non-Taekwon - if ((sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - - case TK_READYCOUNTER: - case TK_READYDOWN: - case TK_READYSTORM: - case TK_READYTURN: - case TK_JUMPKICK: - if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) { - //They do not work on Soul Linkers. - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - - case TK_TURNKICK: - case TK_STORMKICK: - case TK_DOWNKICK: - case TK_COUNTER: - if(sd->sc.data[SC_COMBO].timer == -1) - return 0; //Combo needs to be ready - if (pc_famerank(sd->char_id,MAPID_TAEKWON)) - { //Unlimited Combo - if (skill == sd->skillid_old) { - status_change_end(&sd->bl, SC_COMBO, -1); - sd->skillid_old = sd->skilllv_old = 0; - return 0; //Can't repeat previous combo skill. - } - break; - } else - if(sd->sc.data[SC_COMBO].val1 == skill) - break; //Combo ready. - return 0; - case BD_ADAPTATION: /* アドリブ */ - { - struct skill_unit_group *group=NULL; - if(sd->sc.data[SC_DANCING].timer==-1 || - ((group=(struct skill_unit_group*)sd->sc.data[SC_DANCING].val2) && (skill_get_time(sd->sc.data[SC_DANCING].val1,group->skill_lv) - sd->sc.data[SC_DANCING].val3*1000) <= skill_get_time2(skill,lv))){ //ダンス中で使用後5秒以?繧フみ?H - clif_skill_fail(sd,skill,0,0); - return 0; - } - } - break; - case PR_BENEDICTIO: /* ?ケ??~福 */ - { - if (!battle_config.player_skill_partner_check || - (battle_config.gm_skilluncond && pc_isGM(sd) >= battle_config.gm_skilluncond) - ) - break; //No need to do any partner checking [Skotlex] - if (!(type&1)) - { //Started casting. - if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2) - { - clif_skill_fail(sd,skill,0,0); - return 0; - } - } - else - { //Done casting - //Should I repeat the check? If so, it would be best to only do this on cast-ending. [Skotlex] - skill_check_pc_partner(sd, skill, &lv, 1, 1); - } - } - break; - case AM_CANNIBALIZE: /* バイオプラント */ - case AM_SPHEREMINE: /* スフィア?マイン */ - if(type&1){ - int c=0; - int summons[5] = { 1020, 1068, 1118, 1500, 1368 }; - int maxcount = (skill==AM_CANNIBALIZE)? 6-lv : skill_get_maxcount(skill); - int mob_class = (skill==AM_CANNIBALIZE)? summons[lv-1] :1142; - if(battle_config.land_skill_limit && maxcount>0 && (battle_config.land_skill_limit&BL_PC)) { - map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class,&c ); - if(c >= maxcount){ - clif_skill_fail(sd,skill,0,0); - return 0; - } - } - } - break; - case WZ_FIREPILLAR: // celest - if (lv <= 5) // no gems required at level 1-5 - itemid[0] = 0; - break; - case SL_SMA: - if(type) break; //Only do the combo check when the target is selected (type == 0) - if(sd->sc.data[SC_SMA].timer == -1) - return 0; - break; - - case HT_POWER: - if(sd->sc.data[SC_COMBO].timer == -1 || sd->sc.data[SC_COMBO].val1 != skill) - return 0; - break; - case HW_GANBANTEIN: - force_gem_flag = 1; - break; - case AM_BERSERKPITCHER: - case AM_POTIONPITCHER: - case CR_SLIMPITCHER: - case MG_STONECURSE: - case CR_CULTIVATION: - case SA_FLAMELAUNCHER: - case SA_FROSTWEAPON: - case SA_LIGHTNINGLOADER: - case SA_SEISMICWEAPON: - delitem_flag = 0; - break; - case SA_DELUGE: - case SA_VOLCANO: - case SA_VIOLENTGALE: - case SA_LANDPROTECTOR: - { //Does not consumes if the skill is already active. [Skotlex] - struct skill_unit_group *sg; - if ((sg= skill_locate_element_field(&sd->bl)) != NULL && sg->skill_id == skill) - { - if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0) - checkitem_flag = delitem_flag = 0; - else sg->limit = 0; //Disable it. - } - break; - } - case CG_HERMODE: - if (map_foreachinrange (skill_check_condition_hermod_sub, &sd->bl, - skill_get_splash(skill, lv), BL_NPC) < 1) - { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex] - { - int i,x,y,range = skill_get_splash(skill, lv)+1; - int size = range*2+1; - for (i=0;ibl.x+(i%size-range); - y = sd->bl.y+(i/size-range); - if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - } - } - break; - case PR_REDEMPTIO: - { - int exp; - if(((exp = pc_nextbaseexp(sd)) > 0 && sd->status.base_exp*100/exp < 1) || - ((exp = pc_nextjobexp(sd)) > 0 && sd->status.job_exp*100/exp < 1)) { - clif_skill_fail(sd,skill,0,0); //Not enough exp. - return 0; - } - break; - } - case AM_TWILIGHT2: - case AM_TWILIGHT3: - if (!party_skill_check(sd, sd->status.party_id, skill, lv)) - { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - //SHOULD BE OPTIMALIZED [Komurka] - //Optimized #1. optimize comfort later. [Vicious] - case SG_SUN_WARM: - case SG_MOON_WARM: - case SG_STAR_WARM: - if ((sd->bl.m == sd->feel_map[skill-SG_SUN_WARM].m) || (sd->sc.data[SC_MIRACLE].timer!=-1)) - break; - clif_skill_fail(sd,skill,0,0); - return 0; - break; - case SG_SUN_COMFORT: - if ((sd->bl.m == sd->feel_map[0].m && (battle_config.allow_skill_without_day || is_day_of_sun())) || (sd->sc.data[SC_MIRACLE].timer!=-1)) - break; - clif_skill_fail(sd,skill,0,0); - return 0; - case SG_MOON_COMFORT: - if ((sd->bl.m == sd->feel_map[1].m && (battle_config.allow_skill_without_day || is_day_of_moon())) || (sd->sc.data[SC_MIRACLE].timer!=-1)) - break; - clif_skill_fail(sd,skill,0,0); - return 0; - case SG_STAR_COMFORT: - if ((sd->bl.m == sd->feel_map[2].m && (battle_config.allow_skill_without_day || is_day_of_star())) || (sd->sc.data[SC_MIRACLE].timer!=-1)) - break; - clif_skill_fail(sd,skill,0,0); - return 0; - case SG_FUSION: - if (sd->sc.data[SC_FUSION].timer!=-1) - return 1; - if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_STAR) - break; - return 0; - case GD_BATTLEORDER: - case GD_REGENERATION: - case GD_RESTORE: - case GD_EMERGENCYCALL: - if (!sd->status.guild_id || !sd->state.gmaster_flag) - return 0; - if (lv <= 0) - return 0; - - if (skill == GD_EMERGENCYCALL) { - if (!map_flag_gvg(sd->bl.m)) - { //if not allowed to warp to the map (castles are always allowed) - clif_skill_fail(sd,skill,0,0); - return 0; - } - } else if (!agit_flag) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - - //Until they're at right position - gs_skillcheck- [Vicious] - case GS_GLITTERING: - if(sd->spiritball >= 10) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - zeny = 1; - break; - - case GS_FLING: - if(sd->spiritball == 0) - clif_skill_fail(sd,skill,0,0); - break; - - case GS_TRIPLEACTION: - case GS_MAGICALBULLET: - case GS_CRACKER: - case GS_BULLSEYE: - spiritball = 1; - break; - - case GS_MADNESSCANCEL: - spiritball = 4; - if(sd->spiritball >= 4 && sd->sc.data[SC_ADJUSTMENT].timer!=-1) - status_change_end(&sd->bl,SC_ADJUSTMENT,-1); - break; - - case GS_ADJUSTMENT: - spiritball = 2; - if(sd->spiritball >= 2 && sd->sc.data[SC_MADNESSCANCEL].timer != -1) - status_change_end(&sd->bl,SC_MADNESSCANCEL,-1); - break; - - case GS_INCREASING: - spiritball = 2; - break; - - case NJ_KAENSIN: - case NJ_BAKUENRYU: - case NJ_SUITON: - case NJ_HYOUSYOURAKU: - case NJ_RAIGEKISAI: - case NJ_KAMAITACHI: - //delitem_flag = 0; <- don't need? - break; - - case NJ_ISSEN: - if (sd->sc.data[SC_NEN].timer!=-1) - return 0; - break; - - //Not implemented yet [Vicious] - case NJ_KASUMIKIRI: - case NJ_KIRIKAGE: - case NJ_UTSUSEMI: - case NJ_BUNSINJYUTSU: - - case NJ_NEN: - break; - } - - if(!(type&2)){ - if( hp>0 && sd->status.hp < hp) { /* HPチェック */ - clif_skill_fail(sd,skill,2,0); /* HP不足?F失敗通知 */ - return 0; - } - if( sp>0 && sd->status.sp < sp) { /* SPチェック */ - clif_skill_fail(sd,skill,1,0); /* SP不足?F失敗通知 */ - return 0; - } - if( zeny>0 && sd->status.zeny < zeny) { - clif_skill_fail(sd,skill,5,0); - return 0; - } - if(!(weapon & (1<status.weapon) ) ) { - clif_skill_fail(sd,skill,6,0); - return 0; - } - if(ammo) { //Skill requires stuff equipped in the arrow slot. - if((i=sd->equip_index[10]) < 0 || - !sd->inventory_data[i] || - sd->status.inventory[i].amount < ammo_qty - ) { - clif_arrow_fail(sd,0); - return 0; - } - if (!(ammo&1<inventory_data[i]->look)) - { //Ammo type check. Send the "wrong weapon type" message - //which is the closest we have to wrong ammo type. [Skotlex] - clif_arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex] - //clif_skill_fail(sd,skill,6,0); - return 0; - } - } - if( spiritball > 0 && sd->spiritball < spiritball) { - clif_skill_fail(sd,skill,0,0); // 氣球不足 - return 0; - } - } - - switch(state) { - case ST_HIDING: - if(!(sd->sc.option&OPTION_HIDE)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_CLOAKING: - if(!pc_iscloaking(sd)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_HIDDEN: - if(!pc_ishiding(sd)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_RIDING: - if(!pc_isriding(sd)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_FALCON: - if(!pc_isfalcon(sd)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_CART: - if(!pc_iscarton(sd)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_SHIELD: - if(sd->status.shield <= 0) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_SIGHT: - if(sd->sc.data[SC_SIGHT].timer == -1 && type&1) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_EXPLOSIONSPIRITS: - if(sd->sc.data[SC_EXPLOSIONSPIRITS].timer == -1) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_CARTBOOST: - if(!pc_iscarton(sd) || sd->sc.data[SC_CARTBOOST].timer == -1) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_RECOV_WEIGHT_RATE: - if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_MOVE_ENABLE: - //Check only on begin casting. [Skotlex] - if(!type && !unit_can_move(&sd->bl)) { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - case ST_WATER: - //??齡サ定 - //(!map[sd->bl.m].flag.rain) && //they have removed RAIN effect. [Lupus] - if (sd->sc.data[SC_DELUGE].timer == -1 && sd->sc.data[SC_SUITON].timer == -1 && - (!map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER))) - { - clif_skill_fail(sd,skill,0,0); - return 0; - } - break; - } - - if (checkitem_flag) { - for(i=0;i<10;i++) { - int x = lv%11 - 1; - index[i] = -1; - if(itemid[i] <= 0) - continue; - if(itemid[i] >= 715 && itemid[i] <= 717 && sd->special_state.no_gemstone && !force_gem_flag) - continue; - if(((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065) - && sd->sc.data[SC_INTOABYSS].timer != -1 && !force_gem_flag) - continue; - if((skill == AM_POTIONPITCHER || - skill == CR_SLIMPITCHER || - skill == CR_CULTIVATION) && i != x) - continue; - - index[i] = pc_search_inventory(sd,itemid[i]); - if(index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i]) { - if(itemid[i] == 716 || itemid[i] == 717) - clif_skill_fail(sd,skill,(7+(itemid[i]-716)),0); - else - clif_skill_fail(sd,skill,0,0); - return 0; - } - if((itemid[i] >= 715 && itemid[i] <= 717) && sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_WIZARD) - index[i] = -1; //Gemstones are checked, but not substracted from inventory. - - } - } - - if(!(type&1)) - return 1; - - sd->state.arrow_atk = ammo?1:0; //Update arrow-atk state on cast-end. - - if(delitem_flag) { - for(i=0;i<10;i++) { - if(index[i] >= 0) - pc_delitem(sd,index[i],amount[i],0); // アイテム?チ費 - } -// Ammo is now reduced in battle_calc_weapon_attack. [Skotlex] -// if (ammo && battle_config.arrow_decrement) -// pc_delitem(sd,sd->equip_index[10],ammo_qty,0); - } - - if(type&2) - return 1; - - if(sp > 0) { // SP?チ費 - sd->status.sp-=sp; - clif_updatestatus(sd,SP_SP); - } - if(hp > 0) { // HP?チ費 - sd->status.hp-=hp; - clif_updatestatus(sd,SP_HP); - } - if(zeny > 0) // Zeny?チ費 - pc_payzeny(sd,zeny); - if(spiritball > 0) // 氣球?チ費 - pc_delspiritball(sd,spiritball,0); - - return 1; -} - -/*========================================== - * 詠?・時間計算 - *------------------------------------------ - */ -int skill_castfix( struct block_list *bl, int skill_id, int skill_lv) -{ - int castnodex = skill_get_castnodex(skill_id, skill_lv); - int time = skill_get_cast(skill_id, skill_lv); - struct map_session_data *sd; - - nullpo_retr(0, bl); - BL_CAST(BL_PC, bl, sd); - - // calculate base cast time (reduced by dex) - if (!(castnodex&1)) { // castnodex&~1? wtf. [blackhole89] - int scale = battle_config.castrate_dex_scale - status_get_dex(bl); - if (scale > 0) // not instant cast - time = time * scale / battle_config.castrate_dex_scale; - else return 0; // instant cast - } - - // calculate cast time reduced by card bonuses - if (sd && sd->castrate != 100) - time = time * sd->castrate / 100; - - // config cast time multiplier - if (battle_config.cast_rate != 100) - time = time * battle_config.cast_rate / 100; - - // calculate cast time reduced by skill bonuses - if (!(castnodex&2)) - time = skill_castfix_sc(bl, time); - - // return final cast time - return (time > 0) ? time : 0; -} - -/*========================================== - * Does cast-time reductions based on sc data. - *------------------------------------------ - */ -int skill_castfix_sc(struct block_list *bl, int time) -{ - struct status_change *sc = status_get_sc(bl); - - if (time <= 0) return 0; - - if (sc && sc->count) { - if (sc->data[SC_SUFFRAGIUM].timer != -1) { - time -= time * (sc->data[SC_SUFFRAGIUM].val1 * 15) / 100; - status_change_end(bl, SC_SUFFRAGIUM, -1); - } - if (sc->data[SC_POEMBRAGI].timer != -1) - time -= time * sc->data[SC_POEMBRAGI].val2 / 100; - } - return (time > 0) ? time : 0; -} - -/*========================================== - * ディレイ計算 - *------------------------------------------ - */ -int skill_delayfix(struct block_list *bl, int skill_id, int skill_lv) -{ - int delaynodex = skill_get_delaynodex(skill_id, skill_lv); - int time = skill_get_delay(skill_id, skill_lv); - - nullpo_retr(0, bl); - - if (bl->type == BL_MOB) - return 0; //Mobs have no delay other than the skill-specific delay in their skill db. [Skotlex] - - // instant cast attack skills depend on aspd as delay [celest] - if (time == 0) { - if (skill_get_type(skill_id) == BF_WEAPON && !(skill_get_nk(skill_id)&NK_NO_DAMAGE)) - time = status_get_adelay(bl); //Use attack delay as default delay. - else - time = battle_config.default_skill_delay; - } else if (time < 0) - time = -time + status_get_adelay(bl); // if set to <0, the attack delay is added. - - if (battle_config.delay_dependon_dex && !(delaynodex&1)) - { // if skill casttime is allowed to be reduced by dex - int scale = battle_config.castrate_dex_scale - status_get_dex(bl); - if (scale > 0) - time = time * scale / battle_config.castrate_dex_scale; - else //To be capped later to minimum. - time = 0; - } - - if (bl->type == BL_PC && ((TBL_PC*)bl)->delayrate != 100) - time = time * ((TBL_PC*)bl)->delayrate / 100; - - if (battle_config.delay_rate != 100) - time = time * battle_config.delay_rate / 100; - - if (!(delaynodex&2)) - { /* ブラギの? */ - struct status_change *sc; - sc= status_get_sc(bl); - if (sc && sc->count) { - if (sc->data[SC_POEMBRAGI].timer != -1) - time -= time * sc->data[SC_POEMBRAGI].val3 / 100; - if (sc->data[SC_SPIRIT].timer != -1) - switch (skill_id) { - case CR_SHIELDBOOMERANG: - if (sc->data[SC_SPIRIT].val2 == SL_CRUSADER) - time /=2; - break; - case AS_SONICBLOW: - if (!map_flag_gvg(bl->m) && sc->data[SC_SPIRIT].val2 == SL_ASSASIN) - time /= 2; - break; - } - } - } - - return (time < battle_config.min_skill_delay_limit)? - battle_config.min_skill_delay_limit:time; -} - -/*========================================= - * ブランディッシュスピア ?炎範?決定 - *---------------------------------------- - */ -void skill_brandishspear_first(struct square *tc,int dir,int x,int y){ - - nullpo_retv(tc); - - if(dir == 0){ - tc->val1[0]=x-2; - tc->val1[1]=x-1; - tc->val1[2]=x; - tc->val1[3]=x+1; - tc->val1[4]=x+2; - tc->val2[0]= - tc->val2[1]= - tc->val2[2]= - tc->val2[3]= - tc->val2[4]=y-1; - } - else if(dir==2){ - tc->val1[0]= - tc->val1[1]= - tc->val1[2]= - tc->val1[3]= - tc->val1[4]=x+1; - tc->val2[0]=y+2; - tc->val2[1]=y+1; - tc->val2[2]=y; - tc->val2[3]=y-1; - tc->val2[4]=y-2; - } - else if(dir==4){ - tc->val1[0]=x-2; - tc->val1[1]=x-1; - tc->val1[2]=x; - tc->val1[3]=x+1; - tc->val1[4]=x+2; - tc->val2[0]= - tc->val2[1]= - tc->val2[2]= - tc->val2[3]= - tc->val2[4]=y+1; - } - else if(dir==6){ - tc->val1[0]= - tc->val1[1]= - tc->val1[2]= - tc->val1[3]= - tc->val1[4]=x-1; - tc->val2[0]=y+2; - tc->val2[1]=y+1; - tc->val2[2]=y; - tc->val2[3]=y-1; - tc->val2[4]=y-2; - } - else if(dir==1){ - tc->val1[0]=x-1; - tc->val1[1]=x; - tc->val1[2]=x+1; - tc->val1[3]=x+2; - tc->val1[4]=x+3; - tc->val2[0]=y-4; - tc->val2[1]=y-3; - tc->val2[2]=y-1; - tc->val2[3]=y; - tc->val2[4]=y+1; - } - else if(dir==3){ - tc->val1[0]=x+3; - tc->val1[1]=x+2; - tc->val1[2]=x+1; - tc->val1[3]=x; - tc->val1[4]=x-1; - tc->val2[0]=y-1; - tc->val2[1]=y; - tc->val2[2]=y+1; - tc->val2[3]=y+2; - tc->val2[4]=y+3; - } - else if(dir==5){ - tc->val1[0]=x+1; - tc->val1[1]=x; - tc->val1[2]=x-1; - tc->val1[3]=x-2; - tc->val1[4]=x-3; - tc->val2[0]=y+3; - tc->val2[1]=y+2; - tc->val2[2]=y+1; - tc->val2[3]=y; - tc->val2[4]=y-1; - } - else if(dir==7){ - tc->val1[0]=x-3; - tc->val1[1]=x-2; - tc->val1[2]=x-1; - tc->val1[3]=x; - tc->val1[4]=x+1; - tc->val2[1]=y; - tc->val2[0]=y+1; - tc->val2[2]=y-1; - tc->val2[3]=y-2; - tc->val2[4]=y-3; - } - -} - -/*========================================= - * ブランディッシュスピア 方向判定 範??張 - *----------------------------------------- - */ -void skill_brandishspear_dir(struct square *tc,int dir,int are){ - - int c; - - nullpo_retv(tc); - - for(c=0;c<5;c++){ - if(dir==0){ - tc->val2[c]+=are; - }else if(dir==1){ - tc->val1[c]-=are; tc->val2[c]+=are; - }else if(dir==2){ - tc->val1[c]-=are; - }else if(dir==3){ - tc->val1[c]-=are; tc->val2[c]-=are; - }else if(dir==4){ - tc->val2[c]-=are; - }else if(dir==5){ - tc->val1[c]+=are; tc->val2[c]-=are; - }else if(dir==6){ - tc->val1[c]+=are; - }else if(dir==7){ - tc->val1[c]+=are; tc->val2[c]+=are; - } - } -} - -/*========================================== - * Weapon Repair [Celest/DracoRPG] - *------------------------------------------ - */ -void skill_repairweapon(struct map_session_data *sd, int idx) -{ - int material; - int materials[4] = { 1002, 998, 999, 756 }; - struct item *item; - struct map_session_data *target_sd; - - nullpo_retv(sd); - target_sd = map_id2sd(sd->menuskill_lv); - if (!target_sd) //Failed.... - return; - if(idx==0xFFFF) // No item selected ('Cancel' clicked) - return; - if(idx < 0 || idx >= MAX_INVENTORY) - return; //Invalid index?? - - item = &target_sd->status.inventory[idx]; - if(item->nameid <= 0 || item->attribute == 0) - return; //Again invalid item.... - - if(sd!=target_sd && !battle_check_range(&sd->bl,&target_sd->bl,skill_get_range2(&sd->bl, sd->menuskill_id,pc_checkskill(sd, sd->menuskill_id)))){ - clif_item_repaireffect(sd,item->nameid,1); - return; - } - - if (itemdb_type(item->nameid)==4) - material = materials [itemdb_wlv(item->nameid)-1]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon - else - material = materials [2]; // Armors consume 1 Steel - if (pc_search_inventory(sd,material) < 0 ) { - clif_skill_fail(sd,sd->menuskill_id,0,0); - return; - } - item->attribute=0; - clif_equiplist(target_sd); - pc_delitem(sd,pc_search_inventory(sd,material),1,0); - clif_item_repaireffect(sd,item->nameid,0); - if(sd!=target_sd) - clif_item_repaireffect(target_sd,item->nameid,0); -} - -/*========================================== - * Item Appraisal - *------------------------------------------ - */ -void skill_identify(struct map_session_data *sd,int idx) -{ - int flag=1; - - nullpo_retv(sd); - - if(idx >= 0 && idx < MAX_INVENTORY) { - if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){ - flag=0; - sd->status.inventory[idx].identify=1; - } - } - clif_item_identified(sd,idx,flag); -} - -/*========================================== - * Weapon Refine [Celest] - *------------------------------------------ - */ -void skill_weaponrefine(struct map_session_data *sd,int idx) -{ - int i = 0, ep = 0, per; - int material[5] = { 0, 1010, 1011, 984, 984 }; - struct item *item; - - nullpo_retv(sd); - - if (idx >= 0 && idx < MAX_INVENTORY) { - struct item_data *ditem = sd->inventory_data[idx]; - item = &sd->status.inventory[idx]; - - if(item->nameid > 0 && ditem->type == 4) { - if (item->refine >= sd->menuskill_lv || - item->refine >= MAX_REFINE || // if it's no longer refineable - ditem->flag.no_refine || // if the item isn't refinable - (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 ) { //fixed by Lupus (item pos can be = 0!) - clif_skill_fail(sd,sd->menuskill_id,0,0); - return; - } - - per = percentrefinery [ditem->wlv][(int)item->refine]; - per += (sd->status.job_level-50)/2; //Updated per the new kro descriptions. [Skotlex] - - if (per > rand() % 100) { - item->refine++; - pc_delitem(sd, i, 1, 0); - if(item->equip) { - ep = item->equip; - pc_unequipitem(sd,idx,3); - } - clif_refine(sd->fd,sd,0,idx,item->refine); - clif_delitem(sd,idx,1); - clif_additem(sd,idx,1,0); - if (ep) - pc_equipitem(sd,idx,ep); - clif_misceffect(&sd->bl,3); - if(item->refine == MAX_REFINE && item->card[0] == 0x00ff && MakeDWord(item->card[2],item->card[3]) == sd->char_id){ // Fame point system [DracoRPG] - switch(ditem->wlv){ - case 1: - pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point - break; - case 2: - pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point - break; - case 3: - pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point - break; - } - } - } else { - pc_delitem(sd, i, 1, 0); - item->refine = 0; - if(item->equip) - pc_unequipitem(sd,idx,3); - clif_refine(sd->fd,sd,1,idx,item->refine); - pc_delitem(sd,idx,1,0); - clif_misceffect(&sd->bl,2); - clif_emotion(&sd->bl, 23); - } - } - } -} - -/*========================================== - * オ?トスペル - *------------------------------------------ - */ -int skill_autospell(struct map_session_data *sd,int skillid) -{ - int skilllv; - int maxlv=1,lv; - - nullpo_retr(0, sd); - - skilllv = sd->menuskill_lv; - lv=pc_checkskill(sd,skillid); - - if(skilllv <= 0 || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance] - - if(skillid==MG_NAPALMBEAT) maxlv=3; - else if(skillid==MG_COLDBOLT || skillid==MG_FIREBOLT || skillid==MG_LIGHTNINGBOLT){ - if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_SAGE) - maxlv =10; //Soul Linker bonus. [Skotlex] - else if(skilllv==2) maxlv=1; - else if(skilllv==3) maxlv=2; - else if(skilllv>=4) maxlv=3; - } - else if(skillid==MG_SOULSTRIKE){ - if(skilllv==5) maxlv=1; - else if(skilllv==6) maxlv=2; - else if(skilllv>=7) maxlv=3; - } - else if(skillid==MG_FIREBALL){ - if(skilllv==8) maxlv=1; - else if(skilllv>=9) maxlv=2; - } - else if(skillid==MG_FROSTDIVER) maxlv=1; - else return 0; - - if(maxlv > lv) - maxlv = lv; - - sc_start4(&sd->bl,SC_AUTOSPELL,100,skilllv,skillid,maxlv,0, // val1:スキルID val2:使用?ナ大Lv - skill_get_time(SA_AUTOSPELL,skilllv));// にしてみたけどbscriptが?曹ォ易い????H - return 0; -} - -/*========================================== - * ギャングスタ?パラダイス判定??(foreachinarea) - *------------------------------------------ - */ - -static int skill_gangster_count(struct block_list *bl,va_list ap) -{ - struct map_session_data *sd; - sd=(struct map_session_data*)bl; - - if(sd && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0) - return 1; - return 0; -} - -static int skill_gangster_in(struct block_list *bl,va_list ap) -{ - struct map_session_data *sd; - sd=(struct map_session_data*)bl; - if(sd && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0) - sd->state.gangsterparadise=1; - return 0; -} - -static int skill_gangster_out(struct block_list *bl,va_list ap) -{ - struct map_session_data *sd; - sd=(struct map_session_data*)bl; - if(sd && sd->state.gangsterparadise) - sd->state.gangsterparadise=0; - return 0; -} - -int skill_gangsterparadise(struct map_session_data *sd ,int type) -{ - int range; - nullpo_retr(0, sd); - - if((range = pc_checkskill(sd,RG_GANGSTER)) <= 0) - return 0; - range = skill_get_splash(RG_GANGSTER, range); - - if(type==1) {/* ?タった時の?? */ - if (map_foreachinrange(skill_gangster_count,&sd->bl, range, BL_PC) > 1) - { /*ギャングスタ??ャ功したら自分にもギャングスタ???ォ付?*/ - map_foreachinrange(skill_gangster_in,&sd->bl, range, BL_PC); - sd->state.gangsterparadise = 1; - } - return 0; - } - else if(type==0) {/* 立ち?繧ェったときの?? */ - if (map_foreachinrange(skill_gangster_count,&sd->bl, range, BL_PC) < 2) - map_foreachinrange(skill_gangster_out,&sd->bl, range, BL_PC); - sd->state.gangsterparadise = 0; - return 0; - } - return 0; -} -/*========================================== - * Taekwon TK_HPTIME and TK_SPTIME skills [Dralnu] - *------------------------------------------ - */ -static int skill_rest_count(struct block_list *bl,va_list ap) -{ - struct map_session_data *sd; - sd=(struct map_session_data*)bl; - - if(sd && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 )) - return 1; - return 0; -} - -static int skill_rest_in(struct block_list *bl,va_list ap) -{ - struct map_session_data *sd; - nullpo_retr(0, bl); - nullpo_retr(0, ap); - - sd=(struct map_session_data*)bl; - if(sd && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 )){ - sd->state.rest=1; - status_calc_pc(sd,0); - } - return 0; -} - -static int skill_rest_out(struct block_list *bl,va_list ap) -{ - struct map_session_data *sd; - sd=(struct map_session_data*)bl; - if(sd && sd->state.rest != 0) - sd->state.rest=0; - return 0; -} - -int skill_rest(struct map_session_data *sd ,int type) -{ - int range; - nullpo_retr(0, sd); - - if((range = pc_checkskill(sd,TK_HPTIME)) > 0) - range = skill_get_splash(TK_HPTIME, range); - else if ((range = pc_checkskill(sd,TK_SPTIME)) > 0) - range = skill_get_splash(TK_SPTIME, range); - else - return 0; - - - if(type==1) { //When you sit down - if (map_foreachinrange(skill_rest_count,&sd->bl, range, BL_PC) > 1) - { - map_foreachinrange(skill_rest_in,&sd->bl, range, BL_PC); - sd->state.rest = 1; - status_calc_pc(sd,0); - } - return 0; - } - else if(type==0) { //When you stand up - if (map_foreachinrange(skill_rest_count,&sd->bl, range, BL_PC) < 2) - map_foreachinrange(skill_rest_out,&sd->bl, range, BL_PC); - sd->state.rest = 0; - status_calc_pc(sd,0); - return 0; - } - return 0; -} -/*========================================== - * 寒いジョ?ク?スクリ?ム判定??(foreachinarea) - *------------------------------------------ - */ -int skill_frostjoke_scream(struct block_list *bl,va_list ap) -{ - struct block_list *src; - int skillnum,skilllv; - unsigned int tick; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, src=va_arg(ap,struct block_list*)); - - skillnum=va_arg(ap,int); - skilllv=va_arg(ap,int); - if(skilllv <= 0) return 0; - tick=va_arg(ap,unsigned int); - - if (src == bl || //自分には?かない - bl->prev == NULL || - status_isdead(bl)) - return 0; - if (bl->type == BL_PC) { - struct map_session_data *sd = (struct map_session_data *)bl; - if (sd && sd->sc.option&OPTION_INVISIBLE) - return 0; - } - //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex] - if(battle_check_target(src,bl,BCT_ENEMY) > 0) - skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick); - else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rand()%100 < 10) - skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick); - - return 0; -} - -/*========================================== - * バジリカのセルを?ン定する - *------------------------------------------ - */ -void skill_unitsetmapcell(struct skill_unit *src, int skill_num, int skill_lv, int flag) -{ - int i,x,y,range = skill_get_unit_range(skill_num,skill_lv); - int size = range*2+1; - - for (i=0;ibl.x+(i%size-range); - y = src->bl.y+(i/size-range); - map_setcell(src->bl.m,x,y,flag); - } -} - -/*========================================== - * Sets a map cell around the caster, according to the skill's range. - *------------------------------------------ - */ -void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag) -{ - int i,x,y,range = skill_get_range2(src, skill_num, skill_lv); - int size = range*2+1; - - for (i=0;ix+(i%size-range); - y = src->y+(i/size-range); - map_setcell(src->m,x,y,flag); - } -} - -/*========================================== - * - *------------------------------------------ - */ -int skill_attack_area(struct block_list *bl,va_list ap) -{ - struct block_list *src,*dsrc; - int atk_type,skillid,skilllv,flag,type; - unsigned int tick; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - - atk_type = va_arg(ap,int); - if((src=va_arg(ap,struct block_list*)) == NULL) - return 0; - if((dsrc=va_arg(ap,struct block_list*)) == NULL) - return 0; - skillid=va_arg(ap,int); - skilllv=va_arg(ap,int); - if(skillid > 0 && skilllv <= 0) return 0; // celest - tick=va_arg(ap,unsigned int); - flag=va_arg(ap,int); - type=va_arg(ap,int); - - if(battle_check_target(dsrc,bl,type) > 0) - skill_attack(atk_type,src,dsrc,bl,skillid,skilllv,tick,flag); - - return 0; -} -/*========================================== - * - *------------------------------------------ - */ -int skill_clear_group(struct block_list *bl, int flag) -{ - struct unit_data *ud = unit_bl2ud(bl); - struct skill_unit_group *group[MAX_SKILLUNITGROUP]; - int i, count=0; - - nullpo_retr(0, bl); - if (!ud) return 0; - - //All groups to be deleted are first stored on an array since the array elements shift around when you delete them. [Skotlex] - for (i=0;iskillunit[i];i++) - { - switch (ud->skillunit[i]->skill_id) { - case SA_DELUGE: - case SA_VOLCANO: - case SA_VIOLENTGALE: - case SA_LANDPROTECTOR: - case NJ_SUITON: - if (flag&1) - group[count++]= ud->skillunit[i]; - break; - default: - if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP) - group[count++]= ud->skillunit[i]; - break; - } - - } - for (i=0;iskillunit[i];i++) { - switch (ud->skillunit[i]->skill_id) { - case SA_DELUGE: - case SA_VOLCANO: - case SA_VIOLENTGALE: - case SA_LANDPROTECTOR: - case NJ_SUITON: - return ud->skillunit[i]; - } - } - return NULL; -} - -// for graffiti cleaner [Valaris] -int skill_graffitiremover(struct block_list *bl, va_list ap) -{ - struct skill_unit *unit=NULL; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - - if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL) - return 0; - - if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI)) - skill_delunit(unit); - - return 0; -} - -int skill_greed(struct block_list *bl, va_list ap) -{ - struct block_list *src; - struct map_session_data *sd=NULL; - struct flooritem_data *fitem=NULL; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, src = va_arg(ap,struct block_list *)); - - if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl)) - pc_takeitem(sd, fitem); - - return 0; -} - -/*========================================== - * ランドプ?テクタ?チェック(foreachinarea) - *------------------------------------------ - */ -int skill_landprotector(struct block_list *bl, va_list ap ) -{ - int skillid; - int *alive; - struct skill_unit *unit; - struct block_list *src; - - skillid = va_arg(ap,int); - alive = va_arg(ap,int *); - src = va_arg(ap,struct block_list *); - unit = (struct skill_unit *)bl; - if (unit == NULL || unit->group == NULL) - return 0; - - if (skillid == SA_LANDPROTECTOR && unit->group->skill_id == SA_LANDPROTECTOR - && battle_check_target(bl, src, BCT_ENEMY) > 0) - { //Check for offensive Land Protector to delete both. [Skotlex] - (*alive) = 0; - skill_delunit(unit); - return 1; - } - - if (skill_get_type(unit->group->skill_id) != BF_MAGIC) - return 0; //Only blocks out magical skills.```````` - - if (skillid == SA_LANDPROTECTOR || skillid == HW_GANBANTEIN ) { - skill_delunit(unit); - } else - if (unit->group->skill_id == SA_LANDPROTECTOR) { - (*alive) = 0; - } else - if (skillid == HP_BASILICA && unit->group->skill_id == HP_BASILICA) { - //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex] - (*alive) = 0; - } else - return 0; - return 1; -} - -/*========================================== - * variation of skill_landprotector - *------------------------------------------ - */ -int skill_ganbatein(struct block_list *bl, va_list ap ) -{ - struct skill_unit *unit; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - if ((unit = (struct skill_unit *)bl) == NULL || unit->group == NULL) - return 0; - -// Apparently, it REMOVES traps. -// if (skill_get_inf2(unit->group->skill_id)&INF2_TRAP) -// return 0; //Do not remove traps. - - if (unit->group->skill_id == SA_LANDPROTECTOR) - skill_delunit(unit); - else skill_delunitgroup(NULL, unit->group); - - return 1; -} - -/*========================================== - * 指定範??でsrcに?して有?なタ?ゲットのblの?を?える(foreachinarea) - *------------------------------------------ - */ -int skill_count_target (struct block_list *bl, va_list ap) -{ - struct block_list *src; - int *c; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - - if ((src = va_arg(ap,struct block_list *)) == NULL) - return 0; - if ((c = va_arg(ap,int *)) == NULL) - return 0; - if (battle_check_target(src,bl,BCT_ENEMY) > 0) - (*c)++; - return 0; -} -/*========================================== - * トラップ範???(foreachinarea) - *------------------------------------------ - */ -int skill_trap_splash (struct block_list *bl, va_list ap) -{ - struct block_list *src; - int tick; - struct skill_unit *unit; - struct skill_unit_group *sg; - struct block_list *ss; - - src = va_arg(ap,struct block_list *); - unit = (struct skill_unit *)src; - tick = va_arg(ap,int); - - nullpo_retr(0, sg = unit->group); - nullpo_retr(0, ss = map_id2bl(sg->src_id)); - - if(battle_check_target(src,bl,BCT_ENEMY) > 0){ - switch(sg->unit_id){ - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick); - break; - case UNT_BLASTMINE: - case UNT_CLAYMORETRAP: - skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - case UNT_FREEZINGTRAP: - skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); - break; - } - } - - return 0; -} - -/*========================================== - * ステ?タス異??I了 - *------------------------------------------ - */ -int skill_enchant_elemental_end (struct block_list *bl, int type) -{ - struct status_change *sc; - - nullpo_retr(0, bl); - nullpo_retr(0, sc= status_get_sc(bl)); - - if (!sc->count) return 0; - - if (type != SC_ENCPOISON && sc->data[SC_ENCPOISON].timer != -1) /* エンチャントポイズン解? */ - status_change_end(bl, SC_ENCPOISON, -1); - if (type != SC_ASPERSIO && sc->data[SC_ASPERSIO].timer != -1) /* アスペルシオ解? */ - status_change_end(bl, SC_ASPERSIO, -1); - if (type != SC_FIREWEAPON && sc->data[SC_FIREWEAPON].timer != -1) /* フレイムランチャ解? */ - status_change_end(bl, SC_FIREWEAPON, -1); - if (type != SC_WATERWEAPON && sc->data[SC_WATERWEAPON].timer != -1) /* フ?ストウェポン解? */ - status_change_end(bl, SC_WATERWEAPON, -1); - if (type != SC_WINDWEAPON && sc->data[SC_WINDWEAPON].timer != -1) /* ライトニング??ダ?解? */ - status_change_end(bl, SC_WINDWEAPON, -1); - if (type != SC_EARTHWEAPON && sc->data[SC_EARTHWEAPON].timer != -1) /* サイスミックウェポン解? */ - status_change_end(bl, SC_EARTHWEAPON, -1); - if (type != SC_SHADOWWEAPON && sc->data[SC_SHADOWWEAPON].timer != -1) - status_change_end(bl, SC_SHADOWWEAPON, -1); - if (type != SC_GHOSTWEAPON && sc->data[SC_GHOSTWEAPON].timer != -1) - status_change_end(bl, SC_GHOSTWEAPON, -1); - return 0; -} - -/* ク??キング??ク?i周りに移動不可能地?が るか?j */ -int skill_check_cloaking(struct block_list *bl) -{ - struct map_session_data *sd = NULL; - struct status_change *sc; - static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; //optimized by Lupus - static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; - int end = 1,i; - - nullpo_retr(1, bl); - - if (bl->type == BL_PC) - sd = (struct map_session_data *)bl; - - if ((bl->type == BL_PC && battle_config.pc_cloak_check_type&1) || - (bl->type != BL_PC && battle_config.monster_cloak_check_type&1)) - { //Check for walls. - for (i = 0; i < 8; i++) - if (map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS)) - { - end = 0; - break; - } - } else - end = 0; //No wall check. - - if(end){ - sc = status_get_sc(bl); - if (sc && sc->data[SC_CLOAKING].timer != -1 && sc->data[SC_CLOAKING].val1 < 3) { - status_change_end(bl, SC_CLOAKING, -1); - } else if (sd && sd->sc.data[SC_CLOAKING].val3 != 130) { - status_quick_recalc_speed (sd, AS_CLOAKING, 130, 1); - } - } - else { - if (sd && sd->sc.data[SC_CLOAKING].val3 != 103) { - status_quick_recalc_speed (sd, AS_CLOAKING, 103, 1); - } - } - - return end; -} - -/* - *---------------------------------------------------------------------------- - * スキルユニット - *---------------------------------------------------------------------------- - */ - -/*========================================== - * 演奏/ダンスをやめる - * flag 1で?奏中なら相方にユニットを任せる - * - *------------------------------------------ - */ -void skill_stop_dancing(struct block_list *src) -{ - struct status_change* sc; - struct skill_unit_group* group; - struct map_session_data* dsd = NULL; - - nullpo_retv(src); - nullpo_retv(sc = status_get_sc(src)); - - if(!sc->count || sc->data[SC_DANCING].timer == -1) - return; - - group = (struct skill_unit_group *)sc->data[SC_DANCING].val2; - sc->data[SC_DANCING].val2 = 0; - - if (sc->data[SC_DANCING].val4) - { - if (sc->data[SC_DANCING].val4 != BCT_SELF) - dsd = map_id2sd(sc->data[SC_DANCING].val4); - sc->data[SC_DANCING].val4 = 0; - } - - if (group) - skill_delunitgroup(NULL, group); - - if (dsd) - { - dsd->sc.data[SC_DANCING].val4 = dsd->sc.data[SC_DANCING].val2 = 0; - status_change_end(&dsd->bl, SC_DANCING, -1); - } - status_change_end(src, SC_DANCING, -1); -} - -/*========================================== - * スキルユニット?炎化 - *------------------------------------------ - */ -struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y) -{ - struct skill_unit *unit; - - nullpo_retr(NULL, group); - nullpo_retr(NULL, unit=&group->unit[idx]); - - if(!unit->alive) - group->alive_count++; - - unit->bl.id=map_addobject(&unit->bl); - unit->bl.type=BL_SKILL; - unit->bl.m=group->map; - unit->bl.x=x; - unit->bl.y=y; - unit->group=group; - unit->val1=unit->val2=0; - unit->alive=1; - - map_addblock(&unit->bl); - clif_skill_setunit(unit); - - switch (group->skill_id) { - case AL_PNEUMA: - skill_unitsetmapcell(unit,AL_PNEUMA,group->skill_lv,CELL_SETPNEUMA); - break; - case MG_SAFETYWALL: - skill_unitsetmapcell(unit,MG_SAFETYWALL,group->skill_lv,CELL_SETSAFETYWALL); - break; - case SA_LANDPROTECTOR: - skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_SETLANDPROTECTOR); - break; - case HP_BASILICA: - skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_SETBASILICA); - break; - case WZ_ICEWALL: - skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_SETICEWALL); - break; - } - return unit; -} - -/*========================================== - * スキルユニット?? - *------------------------------------------ - */ -int skill_delunit(struct skill_unit *unit) -{ - struct skill_unit_group *group; - - nullpo_retr(0, unit); - if(!unit->alive) - return 0; - nullpo_retr(0, group=unit->group); - - /* onlimitイベント呼び?oし */ - skill_unit_onlimit( unit,gettick() ); - - /* onoutイベント呼び?oし */ - if (!unit->range) { - map_foreachincell(skill_unit_effect,unit->bl.m, - unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4); - } - - switch (group->skill_id) { - case AL_PNEUMA: - skill_unitsetmapcell(unit,AL_PNEUMA,group->skill_lv,CELL_CLRPNEUMA); - break; - case MG_SAFETYWALL: - skill_unitsetmapcell(unit,MG_SAFETYWALL,group->skill_lv,CELL_CLRSAFETYWALL); - break; - case SA_LANDPROTECTOR: - skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_CLRLANDPROTECTOR); - break; - case HP_BASILICA: - skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_CLRBASILICA); - break; - case WZ_ICEWALL: - skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_CLRICEWALL); - break; - } - - clif_skill_delunit(unit); - - unit->group=NULL; - unit->alive=0; - map_delobjectnofree(unit->bl.id); - if(--group->alive_count==0) - skill_delunitgroup(NULL, group); - - return 0; -} -/*========================================== - * スキルユニットグル?プ?炎化 - *------------------------------------------ - */ -static int skill_unit_group_newid = MAX_SKILL_DB; -struct skill_unit_group *skill_initunitgroup(struct block_list *src, - int count,int skillid,int skilllv,int unit_id, int limit, int interval) -{ - struct unit_data *ud = unit_bl2ud(src); - struct skill_unit_group *group=NULL; - int i; - - if(skilllv <= 0) return 0; - - nullpo_retr(NULL, src); - nullpo_retr(NULL, ud); - - for(i=0;iskillunit[i]; i++); - - if(i == MAX_SKILLUNITGROUP) { - int j=0; - unsigned maxdiff=0,x,tick=gettick(); - for(i=0;iskillunit[i];i++) - if((x=DIFF_TICK(tick,ud->skillunit[i]->tick))>maxdiff){ - maxdiff=x; - j=i; - } - skill_delunitgroup(src, ud->skillunit[j]); - //Since elements must have shifted, we use the last slot. - i = MAX_SKILLUNITGROUP-1; - } - if (!ud->skillunit[i]) - ud->skillunit[i] = ers_alloc(skill_unit_ers, struct skill_unit_group); - group=ud->skillunit[i]; - - group->src_id=src->id; - group->party_id=status_get_party_id(src); - group->guild_id=status_get_guild_id(src); - group->group_id=skill_unit_group_newid++; - if(skill_unit_group_newid<=0) - skill_unit_group_newid = MAX_SKILL_DB; - group->unit=(struct skill_unit *)aCalloc(count,sizeof(struct skill_unit)); - group->unit_count=count; - group->alive_count=0; - group->val1=group->val2=group->val3=0; - group->skill_id=skillid; - group->skill_lv=skilllv; - group->unit_id=unit_id; - group->map=src->m; - group->limit=limit; - group->interval=interval; - group->tick=gettick(); - if (skillid == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex] - group->tick += 1500; - else if (skillid == PA_GOSPEL) //Prevent Gospel from triggering bonuses right away. [Skotlex] - group->tick += interval; - group->valstr=NULL; - - i = skill_get_unit_flag(skillid); //Reuse for faster access from here on. [Skotlex] - if (i&UF_DANCE) { - struct map_session_data *sd = NULL; - if(src->type==BL_PC && (sd=(struct map_session_data *)src) ){ - sd->skillid_dance=skillid; - sd->skilllv_dance=skilllv; - } - sc_start4(src,SC_DANCING,100,skillid,(int)group,0,(i&UF_ENSEMBLE?BCT_SELF:0),skill_get_time(skillid,skilllv)+1000); - //?奏スキルは相方をダンス?態にする - if (sd && i&UF_ENSEMBLE && - battle_config.player_skill_partner_check && - (!battle_config.gm_skilluncond || pc_isGM(sd) < battle_config.gm_skilluncond) - ) { - skill_check_pc_partner(sd, skillid, &skilllv, 1, 1); - } - } - return group; -} - -/*========================================== - * スキルユニットグル?プ?? - *------------------------------------------ - */ -int skill_delunitgroup(struct block_list *src, struct skill_unit_group *group) -{ - struct unit_data *ud; - int i,j; - - nullpo_retr(0, group); - - if (!src) src=map_id2bl(group->src_id); - ud = unit_bl2ud(src); - if(!src || !ud) { - ShowError("skill_delunitgroup: Group's source not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); - return 0; - } - if (skill_get_unit_flag(group->skill_id)&UF_DANCE) - { - struct status_change* sc = status_get_sc(src); - if (sc && sc->data[SC_DANCING].timer != -1) - { - sc->data[SC_DANCING].val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex] - status_change_end(src,SC_DANCING,-1); - } - } - - if (group->unit_id == UNT_GOSPEL) { //Clear Gospel [Skotlex] - struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_GOSPEL].timer != -1) { - sc->data[SC_GOSPEL].val3 = 0; //Remove reference to this group. [Skotlex] - status_change_end(src,SC_GOSPEL,-1); - } - } - if (group->skill_id == SG_SUN_WARM || - group->skill_id == SG_MOON_WARM || - group->skill_id == SG_STAR_WARM) { - struct status_change *sc = status_get_sc(src); - if(sc && sc->data[SC_WARM].timer != -1) { - sc->data[SC_WARM].val4 = 0; - status_change_end(src,SC_WARM,-1); - } - } - - if (src->type==BL_PC && group->state.ammo_consume) - battle_consume_ammo((TBL_PC*)src, group->skill_id, group->skill_lv); - - group->alive_count=0; - if(group->unit!=NULL){ - for(i=0;iunit_count;i++) - if(group->unit[i].alive) - skill_delunit(&group->unit[i]); - } - if(group->valstr!=NULL){ - aFree(group->valstr); - group->valstr=NULL; - } - - map_freeblock((struct block_list*)group->unit); /* aFree()の替わり */ - group->unit=NULL; - group->group_id=0; - group->unit_count=0; - - //Locate and clear this unit from the array. - for (i=0; iskillunit[i]!=group; i++); - for (j=i; jskillunit[j]; j++); - j--; - if (iskillunit[i] = ud->skillunit[j]; - ud->skillunit[j] = NULL; - ers_free(skill_unit_ers, group); - } else - ShowError("skill_delunitgroup: Group not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); - return 1; -} - -/*========================================== - * スキルユニットグル?プ全?? - *------------------------------------------ - */ -int skill_clear_unitgroup(struct block_list *src) -{ - struct unit_data *ud = unit_bl2ud(src); - - nullpo_retr(0, ud); - - while (ud->skillunit[0]) - skill_delunitgroup(src, ud->skillunit[0]); - return 1; -} - -/*========================================== - * スキルユニットグル?プの被影響tick?? - *------------------------------------------ - */ -struct skill_unit_group_tickset *skill_unitgrouptickset_search( - struct block_list *bl,struct skill_unit_group *group,int tick) -{ - int i,j=-1,k,s,id; - struct unit_data *ud; - struct skill_unit_group_tickset *set; - - nullpo_retr(0, bl); - if (group->interval==-1) - return NULL; - - ud = unit_bl2ud(bl); - if (!ud) return NULL; - - set = ud->skillunittick; - - if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP) - id = s = group->skill_id; - else - id = s = group->group_id; - - for (i=0; i0 || set[k].id==0)) - j=k; - } - - if (j == -1) { - if(battle_config.error_log) { - ShowWarning ("skill_unitgrouptickset_search: tickset is full\n"); - } - j = id % MAX_SKILLUNITGROUPTICKSET; - } - - set[j].id = id; - set[j].tick = tick; - return &set[j]; -} - -/*========================================== - * スキルユニットタイマ??動??用(foreachinarea) - *------------------------------------------ - */ -int skill_unit_timer_sub_onplace( struct block_list *bl, va_list ap ) -{ - struct skill_unit *unit; - struct skill_unit_group *group; - unsigned int tick; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - unit = va_arg(ap,struct skill_unit *); - tick = va_arg(ap,unsigned int); - - if (!unit->alive || bl->prev==NULL) - return 0; - - nullpo_retr(0, group=unit->group); - - if (skill_get_type(group->skill_id)==BF_MAGIC - && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR)) - return 0; //AoE skills are ineffective. [Skotlex] - - if (battle_check_target(&unit->bl,bl,group->target_flag)<=0) - return 0; - - skill_unit_onplace_timer(unit,bl,tick); - - return 0; -} - -/*========================================== - * スキルユニットタイマ???用(foreachobject) - *------------------------------------------ - */ -int skill_unit_timer_sub( struct block_list *bl, va_list ap ) -{ - struct skill_unit *unit; - struct skill_unit_group *group; - unsigned int tick; - - nullpo_retr(0, bl); - nullpo_retr(0, ap); - nullpo_retr(0, unit=(struct skill_unit *)bl); - tick=va_arg(ap,unsigned int); - - if(!unit->alive) - return 0; - group=unit->group; - - nullpo_retr(0, group); - - /* onplace_timerイベント呼び?oし */ - if (unit->range>=0 && group->interval!=-1) { - if (battle_config.skill_wall_check) - map_foreachinshootrange(skill_unit_timer_sub_onplace, bl, unit->range, - group->bl_flag,bl,tick); - else - map_foreachinrange(skill_unit_timer_sub_onplace, bl, unit->range, - group->bl_flag,bl,tick); - if (!unit->alive) - return 0; - } - /* 時間?リれ?? */ - if((DIFF_TICK(tick,group->tick)>=group->limit || DIFF_TICK(tick,group->tick)>=unit->limit)){ - switch(group->unit_id){ - case UNT_BLASTMINE: - group->unit_id = UNT_USED_TRAPS; - clif_changetraplook(bl, UNT_USED_TRAPS); - group->limit=DIFF_TICK(tick+1500,group->tick); - unit->limit=DIFF_TICK(tick+1500,group->tick); - break; - case UNT_SKIDTRAP: - case UNT_ANKLESNARE: - case UNT_LANDMINE: - case UNT_SHOCKWAVE: - case UNT_SANDMAN: - case UNT_FLASHER: - case UNT_FREEZINGTRAP: - case UNT_CLAYMORETRAP: - case UNT_TALKIEBOX: - { - struct block_list *src=map_id2bl(group->src_id); - if(group->unit_id == UNT_ANKLESNARE && group->val2); - else{ - if(src && src->type==BL_PC && !group->state.into_abyss) - { //Avoid generating trap items when it did not cost to create them. [Skotlex] - struct item item_tmp; - memset(&item_tmp,0,sizeof(item_tmp)); - item_tmp.nameid=1065; - item_tmp.identify=1; - map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,NULL,NULL,NULL,0); // ?返還 - } - } - skill_delunit(unit); - } - break; - - case 0xc1: - case 0xc2: - case 0xc3: - case 0xc4: - { - struct block_list *src=map_id2bl(group->src_id); - if (src) - group->tick = tick; - } - break; - - default: - skill_delunit(unit); - } - } - - if(group->unit_id == UNT_ICEWALL) { - unit->val1 -= 5; - if(unit->val1 <= 0 && unit->limit + group->tick > tick + 700) - unit->limit = DIFF_TICK(tick+700,group->tick); - } - - return 0; -} -/*========================================== - * スキルユニットタイマ??? - *------------------------------------------ - */ -int skill_unit_timer( int tid,unsigned int tick,int id,int data) -{ - map_freeblock_lock(); - - map_foreachobject( skill_unit_timer_sub, BL_SKILL, tick ); - - map_freeblock_unlock(); - - return 0; -} - -/*========================================== - * スキルユニット移動時??用(foreachinarea) - *------------------------------------------ - */ -int skill_unit_move_sub( struct block_list *bl, va_list ap ) -{ - struct skill_unit *unit = (struct skill_unit *)bl; - struct block_list *target; - unsigned int tick,flag,result; - int skill_id; - - target=va_arg(ap,struct block_list*); - tick = va_arg(ap,unsigned int); - flag = va_arg(ap,int); - - nullpo_retr(0, unit->group); - - if (!(unit->group->bl_flag&target->type)) - return 0; //we don't target this type of bl - - skill_id = unit->group->skill_id; //Necessary in case the group is deleted after calling on_place/on_out [Skotlex] - - if (unit->group->interval!=-1 && - !(skill_get_unit_flag(skill_id)&UF_DUALMODE)) //Skills in dual mode have to trigger both. [Skotlex] - return 0; - - if (!unit->alive || target->prev==NULL) - return 0; - - if (flag&1) - { - result = skill_unit_onplace(unit,target,tick); - if (flag&2 && result) - { //Clear skill ids we have stored in onout. - int i; - for(i=0; i<8 && skill_unit_temp[i]!=result; i++); - if (i<8) - skill_unit_temp[i] = 0; - } - } - else - { - result = skill_unit_onout(unit,target,tick); - if (flag&2 && skill_unit_index < 7 && result) //Store this unit id. - skill_unit_temp[skill_unit_index++] = result; - } - if (flag&4) - skill_unit_onleft(skill_id,target,tick); - - return 1; -} - -/*========================================== - * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft) - * Flag values: - * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout) - * flag&2: this function is being invoked twice as a bl moves, store in memory the affected - * units to figure out when they have left a group. - * flag&4: Force a onleft event (triggered when the bl is killed, for example) - *------------------------------------------ - */ -int skill_unit_move(struct block_list *bl,unsigned int tick,int flag) -{ - nullpo_retr(0, bl); - - if(bl->prev==NULL ) - return 0; - - if (flag&2 && !(flag&1)) - { //Onout, clear data - memset (&skill_unit_temp,0,sizeof(skill_unit_temp)); - skill_unit_index=0; - } - - map_foreachincell(skill_unit_move_sub, - bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag); - - if (flag&2 && flag&1) - { //Onplace, check any skill units you have left. - int i; - for (i=0; i< 8 && skill_unit_temp[i]>0; i++) - skill_unit_onleft(skill_unit_temp[i], bl, tick); - } - - return 0; -} - -/*========================================== - * スキルユニット自?の移動時?? - * 引?はグル?プと移動量 - *------------------------------------------ - */ -int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy) -{ - int i,j; - unsigned int tick = gettick(); - int *m_flag; - struct skill_unit *unit1; - struct skill_unit *unit2; - - nullpo_retr(0, group); - if (group->unit_count<=0) - return 0; - if (group->unit==NULL) - return 0; - - if (skill_get_unit_flag(group->skill_id)&UF_ENSEMBLE) //Ensembles may not be moved around. - return 0; - - m_flag = (int *) aMalloc(sizeof(int)*group->unit_count); - memset(m_flag,0,sizeof(int)*group->unit_count);// 移動フラグ - // m_flag - // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed) - // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed) - // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed) - // 3: Both 1+2. - for(i=0;iunit_count;i++){ - unit1=&group->unit[i]; - if (!unit1->alive || unit1->bl.m!=m) - continue; - for(j=0;junit_count;j++){ - unit2=&group->unit[j]; - if (!unit2->alive) - continue; - if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){ - m_flag[i] |= 0x1; - } - if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){ - m_flag[i] |= 0x2; - } - } - } - j = 0; - for (i=0;iunit_count;i++) { - unit1=&group->unit[i]; - if (!unit1->alive) - continue; - if (!(m_flag[i]&0x2)) { - map_foreachincell(skill_unit_effect,unit1->bl.m, - unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4); - } - //Move Cell using "smart" criteria (avoid useless moving around) - switch(m_flag[i]) - { - case 0: - //Cell moves independently, safely move it. - map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick); - clif_skill_setunit(unit1); - break; - case 1: - //Cell moves unto another cell, look for a replacement cell that won't collide - //and has no cell moving into it (flag == 2) - for(;junit_count;j++) - { - if(m_flag[j]!=2 || !group->unit[j].alive) - continue; - //Move to where this cell would had moved. - unit2 = &group->unit[j]; - map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick); - clif_skill_setunit(unit1); - j++; //Skip this cell as we have used it. - break; - } - break; - case 2: - case 3: - break; //Don't move the cell as a cell will end on this tile anyway. - } - if (!(m_flag[i]&2)) { //We only moved the cell in 0-1 - map_foreachincell(skill_unit_effect,unit1->bl.m, - unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1); - } - } - aFree(m_flag); - return 0; -} - -/*---------------------------------------------------------------------------- - * アイテム??ャ - *---------------------------------------------------------------------------- - */ - -/*========================================== - * アイテム??ャ可能判定 - *------------------------------------------ - */ -int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty) -{ - int i,j; - - nullpo_retr(0, sd); - - if(nameid<=0) - return 0; - - for(i=0;i= MAX_SKILL_PRODUCE_DB ) /* デ?タベ?スにない */ - return 0; - - if(trigger>=0){ - if(trigger>20) { // Non-weapon, non-food item (itemlv must match) - if(skill_produce_db[i].itemlv!=trigger) - return 0; - } else if(trigger>10) { // Food (itemlv must be higher or equal) - if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>trigger) - return 0; - } else { // Weapon (itemlv must be higher or equal) - if(skill_produce_db[i].itemlv>trigger) - return 0; - } - } - if( (j=skill_produce_db[i].req_skill)>0 && pc_checkskill(sd,j)<=0 ) - return 0; /* スキルが足りない */ - - for(j=0;jstatus.inventory[y].nameid == id ) - x+=sd->status.inventory[y].amount; - if(x=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */ - static const int ele_table[4]={3,1,4,2}; - pc_delitem(sd,j,1,1); - ele=ele_table[slot[i]-994]; - } - } - - /* ?゙料?チ費 */ - for(i=0;i= 0){ - y = sd->status.inventory[j].amount; - if(y>x)y=x; /* 足りている */ - pc_delitem(sd,j,y,0); - }else { - if(battle_config.error_log) - ShowError("skill_produce_mix: material item error\n"); - } - - x-=y; /* まだ足りない個?を計算 */ - }while( j>=0 && x>0 ); /* ?゙料を?チ費するか?Aエラ?になるまで繰り返す */ - } - - if((equip=itemdb_isequip(nameid))) - wlv = itemdb_wlv(nameid); - if(!equip) { - switch(skill_id){ - case BS_IRON: - case BS_STEEL: - case BS_ENCHANTEDSTONE: - { // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG] - int skill = pc_checkskill(sd,skill_id); - make_per = sd->status.job_level*20 + sd->paramc[4]*10 + sd->paramc[5]*10; //Base chance - switch(nameid){ - case 998: // Iron - make_per += 4000+skill*500; // Temper Iron bonus: +26/+32/+38/+44/+50 - break; - case 999: // Steel - make_per += 3000+skill*500; // Temper Steel bonus: +35/+40/+45/+50/+55 - break; - case 1000: //Star Crumb - make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex] - break; - default: // Enchanted Stones - make_per += 1000+skill*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35 - break; - } - break; - case ASC_CDP: - make_per = (2000 + 40*sd->paramc[4] + 20*sd->paramc[5]); - break; - case AL_HOLYWATER: - make_per = 100000; //100% success - break; - case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG] - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*100 - + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20 - + sd->paramc[3]*5 + sd->paramc[4]*10+sd->paramc[5]*10; - switch(nameid){ - case 501: // Red Potion - case 503: // Yellow Potion - case 504: // White Potion - case 605: // Anodyne - case 606: // Aloevera - make_per += 2000; - break; - case 505: // Blue Potion - make_per -= 500; - break; - case 545: // Condensed Red Potion - case 546: // Condensed Yellow Potion - case 547: // Condensed White Potion - make_per -= 1000; - break; - case 970: // Alcohol - make_per += 1000; - break; - case 7139: // Glistening Coat - make_per -= 1000; - break; - case 7135: // Bottle Grenade - case 7136: // Acid Bottle - case 7137: // Plant Bottle - case 7138: // Marine Sphere Bottle - default: - break; - } - if(battle_config.pp_rate != 100) - make_per = make_per * battle_config.pp_rate / 100; - break; - case SA_CREATECON: // Elemental Converter Creation - skill bonuses are from kRO [DracoRPG] - make_per = pc_checkskill(sd, SA_ADVANCEDBOOK)*100 + //TODO: Advanced Book bonus is custom! [Skotlex] - sd->status.job_level*20 + sd->paramc[3]*10 + sd->paramc[4]*10; - switch(nameid){ - case 12114: - flag = pc_checkskill(sd,SA_FLAMELAUNCHER); - if (flag > 0) - make_per += 1000*flag-500; - break; - case 12115: - flag = pc_checkskill(sd,SA_FROSTWEAPON); - if (flag > 0) - make_per += 1000*flag-500; - break; - case 12116: - flag = pc_checkskill(sd,SA_SEISMICWEAPON); - if (flag > 0) - make_per += 1000*flag-500; - break; - case 12117: - flag = pc_checkskill(sd,SA_LIGHTNINGLOADER); - if (flag > 0) - make_per += 1000*flag-500; - break; - } - break; - default: - make_per = 5000; - break; - } - } - } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG] - make_per = 5000 + sd->status.job_level*20 + sd->paramc[4]*10 + sd->paramc[5]*10; // Base - make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15 - make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5 - make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30 - if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10 - else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5 - else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3 - else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0? - if(battle_config.wp_rate != 100) - make_per = make_per * battle_config.wp_rate / 100; - } -// - Baby Class Penalty = 80% (from adult's chance) ----// - if (sd->class_&JOBL_BABY) //if it's a Baby Class - make_per = (make_per * 80) / 100; //Lupus - - if(make_per < 1) make_per = 1; - - - if(rand()%10000 < make_per || qty > 1){ //Success, or crafting multiple items. - struct item tmp_item; - memset(&tmp_item,0,sizeof(tmp_item)); - tmp_item.nameid=nameid; - tmp_item.amount=1; - tmp_item.identify=1; - if(equip){ - tmp_item.card[0]=0x00ff; - tmp_item.card[1]=((sc*5)<<8)+ele; - tmp_item.card[2]=GetWord(sd->char_id,0); // CharId - tmp_item.card[3]=GetWord(sd->char_id,1); - } else { - //Flag is only used on the end, so it can be used here. [Skotlex] - switch (skill_id) { - case AM_PHARMACY: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - flag = battle_config.produce_potion_name_input; - break; - case AL_HOLYWATER: - flag = battle_config.holywater_name_input; - break; - case ASC_CDP: - flag = battle_config.cdp_name_input; - break; - default: - flag = battle_config.produce_item_name_input; - break; - } - if (flag) { - tmp_item.card[0]=0x00fe; - tmp_item.card[1]=0; - tmp_item.card[2]=GetWord(sd->char_id,0); // CharId - tmp_item.card[3]=GetWord(sd->char_id,1); - } - } - -// if(log_config.produce > 0) -// log_produce(sd,nameid,slot1,slot2,slot3,1); -//TODO update PICKLOG - - if(equip){ - clif_produceeffect(sd,0,nameid); - clif_misceffect(&sd->bl,3); - if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG] - pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point - } else { - int fame = 0; - tmp_item.amount = 0; - for (i=0; i< qty; i++) - { //Apply quantity modifiers. - if (rand()%10000 < make_per || qty == 1) - { //Success - tmp_item.amount++; - if(nameid < 545 || nameid > 547) - continue; - if(skill_id != AM_PHARMACY && - skill_id != AM_TWILIGHT1 && - skill_id != AM_TWILIGHT2 && - skill_id != AM_TWILIGHT3) - continue; - //Add fame as needed. - switch(++sd->potion_success_counter) { - case 3: - fame+=1; // Success to prepare 3 Condensed Potions in a row - break; - case 5: - fame+=3; // Success to prepare 5 Condensed Potions in a row - break; - case 7: - fame+=10; // Success to prepare 7 Condensed Potions in a row - break; - case 10: - fame+=50; // Success to prepare 10 Condensed Potions in a row - sd->potion_success_counter = 0; - break; - } - } else //Failure - sd->potion_success_counter = 0; - } - if (fame) - pc_addfame(sd,fame); - //Visual effects and the like. - switch (skill_id) { - case AM_PHARMACY: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - case ASC_CDP: - clif_produceeffect(sd,2,nameid); - clif_misceffect(&sd->bl,5); - break; - case BS_IRON: - case BS_STEEL: - case BS_ENCHANTEDSTONE: - clif_produceeffect(sd,0,nameid); - clif_misceffect(&sd->bl,3); - break; - default: //Those that don't require a skill? - if (skill_produce_db[idx].itemlv==11) //Cooking items. - clif_specialeffect(&sd->bl, 608, 0); - break; - } - } - if (tmp_item.amount) { //Success - if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - return 1; - } - } - //Failure -// if(log_config.produce) -// log_produce(sd,nameid,slot1,slot2,slot3,0); -//TODO update PICKLOG - - if(equip){ - clif_produceeffect(sd,1,nameid); - clif_misceffect(&sd->bl,2); - } else { - switch (skill_id) { - case ASC_CDP: //Damage yourself, and display same effect as failed potion. - battle_damage(NULL, &sd->bl, sd->status.max_hp>>2, 0, 1); - case AM_PHARMACY: - case AM_TWILIGHT1: - case AM_TWILIGHT2: - case AM_TWILIGHT3: - clif_produceeffect(sd,3,nameid); - clif_misceffect(&sd->bl,6); - sd->potion_success_counter = 0; // Fame point system [DracoRPG] - break; - case BS_IRON: - case BS_STEEL: - case BS_ENCHANTEDSTONE: - clif_produceeffect(sd,1,nameid); - clif_misceffect(&sd->bl,2); - break; - default: - if (skill_produce_db[idx].itemlv==11) - clif_specialeffect(&sd->bl, 609, 0); - } - } - return 0; -} - -int skill_arrow_create( struct map_session_data *sd,int nameid) -{ - int i,j,flag,index=-1; - struct item tmp_item; - - nullpo_retr(0, sd); - - if(nameid <= 0) - return 1; - - for(i=0;ichar_id,0); // CharId - tmp_item.card[3]=GetWord(sd->char_id,1); - } - if(tmp_item.nameid <= 0 || tmp_item.amount <= 0) - continue; - if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) { - clif_additem(sd,0,0,flag); - map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); - } - } - - return 0; -} - -/*========================================== - * - *------------------------------------------ - */ -int skill_blockpc_end(int tid,unsigned int tick,int id,int data) -{ - struct map_session_data *sd = map_id2sd(id); - if (data <= 0 || data >= MAX_SKILL) - return 0; - if (sd) sd->blockskill[data] = 0; - - return 1; -} -int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick) -{ - nullpo_retr (-1, sd); - - if (skillid >= GD_SKILLBASE) - skillid = GD_SKILLRANGEMIN + skillid - GD_SKILLBASE; - if (skillid < 1 || skillid > MAX_SKILL) - return -1; - - sd->blockskill[skillid] = 1; - return add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,skillid); -} - - -/*---------------------------------------------------------------------------- - * ?炎化系 - */ - -/* - * 文字列?? - * ',' で区?リって val に戻す - */ -int skill_split_str(char *str,char **val,int num) -{ - int i; - - for (i=0; i= step; j--) - if ((val[j]-val[j-step]) != diff) - break; - - if (j>=step) //No match, try next step. - continue; - - for(; i < MAX_SKILL_LEVEL; i++) - { //Apply linear increase - val[i] = val[i-step]+diff; - if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases. - { val[i] = 1; diff = 0; step = 1; } - } - return i; - } - //Okay.. we can't figure this one out, just fill out the stuff with the previous value. - for (;i= GD_SKILLRANGEMIN && i <= GD_SKILLRANGEMAX) { - ShowWarning("read skill_db: Can't use skill id %d as guild skills are placed there!\n"); - continue; - } - if (i >= GD_SKILLBASE) - i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; - if(i<=0 || i>MAX_SKILL_DB) - continue; - - skill_split_atoi(split[1],skill_db[i].range); - skill_db[i].hit=atoi(split[2]); - skill_db[i].inf=atoi(split[3]); - skill_db[i].pl=atoi(split[4]); - skill_db[i].nk=atoi(split[5]); - skill_split_atoi(split[6],skill_db[i].splash); - skill_db[i].max=atoi(split[7]); - skill_split_atoi(split[8],skill_db[i].num); - - if(strcmpi(split[9],"yes") == 0) - skill_db[i].castcancel=1; - else - skill_db[i].castcancel=0; - skill_db[i].cast_def_rate=atoi(split[10]); - skill_db[i].inf2=atoi(split[11]); - skill_db[i].maxcount=atoi(split[12]); - if(strcmpi(split[13],"weapon") == 0) - skill_db[i].skill_type=BF_WEAPON; - else if(strcmpi(split[13],"magic") == 0) - skill_db[i].skill_type=BF_MAGIC; - else if(strcmpi(split[13],"misc") == 0) - skill_db[i].skill_type=BF_MISC; - else - skill_db[i].skill_type=0; - skill_split_atoi(split[14],skill_db[i].blewcount); - - for (j = 0; skill_names[j].id != 0; j++) - if (skill_names[j].id == i) { - skill_db[i].name = skill_names[j].name; - skill_db[i].desc = skill_names[j].desc; - break; - } - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); - - sprintf(path, "%s/skill_require_db.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; - } - while(fgets(line,1020,fp)){ - char *split[50]; - if(line[0]=='/' && line[1]=='/') - continue; - j = skill_split_str(line,split,32); - if(j < 32 || split[31]==NULL) - continue; - - i=atoi(split[0]); - if (i >= GD_SKILLBASE) - i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; - if(i<=0 || i>MAX_SKILL_DB) - continue; - - skill_split_atoi(split[1],skill_db[i].hp); - skill_split_atoi(split[2],skill_db[i].mhp); - skill_split_atoi(split[3],skill_db[i].sp); - skill_split_atoi(split[4],skill_db[i].hp_rate); - skill_split_atoi(split[5],skill_db[i].sp_rate); - skill_split_atoi(split[6],skill_db[i].zeny); - - p = split[7]; - for(j=0;j<32;j++){ - l = atoi(p); - if (l==99) { - skill_db[i].weapon = 0xffffffff; - break; - } - else - skill_db[i].weapon |= 1<= GD_SKILLBASE) - i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; - if(i<=0 || i>MAX_SKILL_DB) - continue; - - skill_split_atoi(split[1],skill_db[i].cast); - skill_split_atoi(split[2],skill_db[i].delay); - skill_split_atoi(split[3],skill_db[i].walkdelay); - skill_split_atoi(split[4],skill_db[i].upkeep_time); - skill_split_atoi(split[5],skill_db[i].upkeep_time2); - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); - - /* スキルユニットデ?[タベ?[ス */ - - sprintf(path, "%s/skill_unit_db.txt", db_path); - fp=fopen(path,"r"); - if (fp==NULL) { - ShowError("can't read %s\n", path); - return 1; - } - k = 0; - while (fgets(line,1020,fp)) { - char *split[50]; - if (line[0]=='/' && line[1]=='/') - continue; - j = skill_split_str(line,split,8); - if (split[7]==NULL || j<8) - continue; - - i=atoi(split[0]); - if (i >= GD_SKILLBASE) - i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; - if(i<=0 || i>MAX_SKILL_DB) - continue; - skill_db[i].unit_id[0] = strtol(split[1],NULL,16); - skill_db[i].unit_id[1] = strtol(split[2],NULL,16); - skill_split_atoi(split[3],skill_db[i].unit_layout_type); - skill_split_atoi(split[4],skill_db[i].unit_range); - skill_db[i].unit_interval = atoi(split[5]); - - if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target=BCT_NOENEMY; - else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target=BCT_NOENEMY; - else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target=BCT_PARTY; - else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target=BCT_PARTY|BCT_GUILD; - else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target=BCT_ALL; - else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target=BCT_ENEMY; - else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target=BCT_SELF; - else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target=BCT_NOONE; - else skill_db[i].unit_target = strtol(split[6],NULL,16); - - skill_db[i].unit_flag = strtol(split[7],NULL,16); - - if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) - skill_db[i].unit_target=BCT_NOENEMY; - - //By default, target just characters. - skill_db[i].unit_target |= BL_CHAR; - if (skill_db[i].unit_flag&UF_NOPC) - skill_db[i].unit_target &= ~BL_PC; - if (skill_db[i].unit_flag&UF_NOMOB) - skill_db[i].unit_target &= ~BL_MOB; - if (skill_db[i].unit_flag&UF_SKILL) - skill_db[i].unit_target |= BL_SKILL; - k++; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); - skill_init_unit_layout(); - - /* ?サ造系スキルデ?タベ?ス */ - memset(skill_produce_db,0,sizeof(skill_produce_db)); - for(m=0;m<2;m++){ - sprintf(path, "%s/%s", db_path, filename[m]); - fp=fopen(path,"r"); - if(fp==NULL){ - if(m>0) - continue; - ShowError("can't read %s\n",path); - return 1; - } - k=0; - while(fgets(line,1020,fp)){ - char *split[6 + MAX_PRODUCE_RESOURCE * 2]; - int x,y; - if(line[0]=='/' && line[1]=='/') - continue; - memset(split,0,sizeof(split)); - j = skill_split_str(line,split,(3 + MAX_PRODUCE_RESOURCE * 2)); - if(split[0]==0) //fixed by Lupus - continue; - i=atoi(split[0]); - if(i<=0) continue; - - skill_produce_db[k].nameid=i; - skill_produce_db[k].itemlv=atoi(split[1]); - skill_produce_db[k].req_skill=atoi(split[2]); - - for(x=3,y=0; split[x] && split[x+1] && y= MAX_SKILL_PRODUCE_DB) - break; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path); - } - - memset(skill_arrow_db,0,sizeof(skill_arrow_db)); - - sprintf(path, "%s/create_arrow_db.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; - } - k=0; - while(fgets(line,1020,fp)){ - char *split[16]; - int x,y; - if(line[0]=='/' && line[1]=='/') - continue; - memset(split,0,sizeof(split)); - j = skill_split_str(line,split,13); - if(split[0]==0) //fixed by Lupus - continue; - i=atoi(split[0]); - if(i<=0) - continue; - - skill_arrow_db[k].nameid=i; - - for(x=1,y=0;split[x] && split[x+1] && y<5;x+=2,y++){ - skill_arrow_db[k].cre_id[y]=atoi(split[x]); - skill_arrow_db[k].cre_amount[y]=atoi(split[x+1]); - } - k++; - if(k >= MAX_SKILL_ARROW_DB) - break; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path); - - memset(skill_abra_db,0,sizeof(skill_abra_db)); - sprintf(path, "%s/abra_db.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; - } - k=0; - while(fgets(line,1020,fp)){ - char *split[16]; - if(line[0]=='/' && line[1]=='/') - continue; - memset(split,0,sizeof(split)); - j = skill_split_str(line,split,13); - if(split[0]==0) //fixed by Lupus - continue; - i=atoi(split[0]); - if(i<=0) - continue; - - skill_abra_db[i].req_lv=atoi(split[2]); - skill_abra_db[i].per=atoi(split[3]); - - k++; - if(k >= MAX_SKILL_ABRA_DB) - break; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path); - - sprintf(path, "%s/skill_castnodex_db.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; - } - while(fgets(line,1020,fp)){ - char *split[50]; - if(line[0]=='/' && line[1]=='/') - continue; - memset(split,0,sizeof(split)); - j = skill_split_str(line,split,3); - if(split[0]==0) //fixed by Lupus - continue; - i=atoi(split[0]); - if (i >= GD_SKILLBASE) - i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; - if(i<=0 || i>MAX_SKILL_DB) - continue; - - skill_split_atoi(split[1],skill_db[i].castnodex); - if (!split[2]) - continue; - skill_split_atoi(split[2],skill_db[i].delaynodex); - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); - - sprintf(path, "%s/skill_nocast_db.txt", db_path); - fp=fopen(path,"r"); - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; - } - k=0; - while(fgets(line,1020,fp)){ - char *split[16]; - if(line[0]=='/' && line[1]=='/') - continue; - memset(split,0,sizeof(split)); - j = skill_split_str(line,split,2); - if(split[0]==0) //fixed by Lupus - continue; - i=atoi(split[0]); - if (i >= GD_SKILLBASE) - i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; - if(i<=0 || i>MAX_SKILL_DB) - continue; - skill_db[i].nocast|=atoi(split[1]); - k++; - } - fclose(fp); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); - - return 0; -} - -/*=============================================== - * For reading leveluseskillspamount.txt [Celest] - *----------------------------------------------- - */ -static int skill_read_skillspamount(void) -{ - char *buf,*p; - struct skill_db *skill = NULL; - int s, idx, new_flag=1, level=1, sp=0; - - buf=(char *) grfio_reads("data\\leveluseskillspamount.txt",&s); - - if(buf==NULL) - return -1; - - buf[s]=0; - for(p=buf;p-bufsp[level-1]=sp; - level++; - } - - p=strchr(p,10); - if(!p) break; - p++; - } - aFree(buf); - ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\leveluseskillspamount.txt"); - - return 0; -} - -void skill_reload(void) -{ - skill_readdb(); - if (battle_config.skill_sp_override_grffile) - skill_read_skillspamount(); -} - -/*========================================== - * スキル?係?炎化?? - *------------------------------------------ - */ -int do_init_skill(void) -{ - skill_readdb(); - - skill_unit_ers = ers_new((uint32)sizeof(struct skill_unit_group)); - skill_timer_ers = ers_new((uint32)sizeof(struct skill_timerskill)); - - if (battle_config.skill_sp_override_grffile) - skill_read_skillspamount(); - - add_timer_func_list(skill_unit_timer,"skill_unit_timer"); - add_timer_func_list(skill_castend_id,"skill_castend_id"); - add_timer_func_list(skill_castend_pos,"skill_castend_pos"); - add_timer_func_list(skill_timerskill,"skill_timerskill"); - add_timer_func_list(skill_blockpc_end, "skill_blockpc_end"); - - add_timer_interval(gettick()+SKILLUNITTIMER_INVERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INVERVAL); - - return 0; -} - -int do_final_skill(void) { - ers_destroy(skill_unit_ers); - ers_destroy(skill_timer_ers); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include +#include + +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/malloc.h" +#include "../common/showmsg.h" +#include "../common/grfio.h" +#include "../common/ers.h" + +#include "skill.h" +#include "map.h" +#include "clif.h" +#include "pc.h" +#include "status.h" +#include "pet.h" +#include "mob.h" +#include "battle.h" +#include "party.h" +#include "itemdb.h" +#include "script.h" +#include "intif.h" +#include "log.h" +#include "chrif.h" +#include "guild.h" +#include "date.h" +#include "unit.h" + +#define SKILLUNITTIMER_INVERVAL 100 +//Guild Skills are shifted to these to make them stick into the skill array. +#define GD_SKILLRANGEMIN 900 +#define GD_SKILLRANGEMAX GD_SKILLRANGEMIN+MAX_GUILDSKILL + +int skill_names_id[MAX_SKILL_DB]; +const struct skill_name_db skill_names[] = { + { AC_CHARGEARROW, "AC_CHARGEARROW", "Arrow_Repel" } , + { AC_CONCENTRATION, "AC_CONCENTRATION", "Improve_Concentration" } , + { AC_DOUBLE, "AC_DOUBLE", "Double_Strafe" } , + { AC_MAKINGARROW, "AC_MAKINGARROW", "Arrow_Crafting" } , + { AC_OWL, "AC_OWL", "Owl's_Eye" } , + { AC_SHOWER, "AC_SHOWER", "Arrow_Shower" } , + { AC_VULTURE, "AC_VULTURE", "Vulture's_Eye" } , + { ALL_RESURRECTION, "ALL_RESURRECTION", "Resurrection" } , + { AL_ANGELUS, "AL_ANGELUS", "Angelus" } , + { AL_BLESSING, "AL_BLESSING", "Blessing" } , + { AL_CRUCIS, "AL_CRUCIS", "Signum_Crusis" } , + { AL_CURE, "AL_CURE", "Cure" } , + { AL_DECAGI, "AL_DECAGI", "Decrease_AGI" } , + { AL_DEMONBANE, "AL_DEMONBANE", "Demon_Bane" } , + { AL_DP, "AL_DP", "Divine_Protection" } , + { AL_HEAL, "AL_HEAL", "Heal" } , + { AL_HOLYLIGHT, "AL_HOLYLIGHT", "Holy_Light" } , + { AL_HOLYWATER, "AL_HOLYWATER", "Aqua_Benedicta" } , + { AL_INCAGI, "AL_INCAGI", "Increase_AGI" } , + { AL_PNEUMA, "AL_PNEUMA", "Pneuma" } , + { AL_RUWACH, "AL_RUWACH", "Ruwach" } , + { AL_TELEPORT, "AL_TELEPORT", "Teleport" } , + { AL_WARP, "AL_WARP", "Warp_Portal" } , + { AM_ACIDTERROR, "AM_ACIDTERROR", "Acid_Terror" } , + { AM_AXEMASTERY, "AM_AXEMASTERY", "Axe_Mastery" } , + { AM_BERSERKPITCHER, "AM_BERSERKPITCHER", "Aid_Berserk_Potion" } , + { AM_BIOETHICS, "AM_BIOETHICS", "Bioethics" } , + { AM_CALLHOMUN, "AM_CALLHOMUN", "Call_Homunculus" } , + { AM_CANNIBALIZE, "AM_CANNIBALIZE", "Summon_Flora" } , + { AM_CP_ARMOR, "AM_CP_ARMOR", "Synthetic_Armor" } , + { AM_CP_HELM, "AM_CP_HELM", "Biochemical_Helm" } , + { AM_CP_SHIELD, "AM_CP_SHIELD", "Synthetized_Shield" } , + { AM_CP_WEAPON, "AM_CP_WEAPON", "Alchemical_Weapon" } , + { AM_CULTIVATION, "AM_CULTIVATION", "Cultivation" } , + { AM_DEMONSTRATION, "AM_DEMONSTRATION", "Bomb" } , + { AM_LEARNINGPOTION, "AM_LEARNINGPOTION", "Potion_Research" } , + { AM_PHARMACY, "AM_PHARMACY", "Prepare_Potion" } , + { AM_POTIONPITCHER, "AM_POTIONPITCHER", "Aid_Potion" } , + { AM_REST, "AM_REST", "Vaporize" } , + { AM_RESURRECTHOMUN, "AM_RESURRECTHOMUN", "Homunculus_Resurrection" } , + { AM_SPHEREMINE, "AM_SPHEREMINE", "Summon_Marine_Sphere" } , + { AM_TWILIGHT1, "AM_TWILIGHT1", "Twilight_Pharmacy_1" } , + { AM_TWILIGHT2, "AM_TWILIGHT2", "Twilight_Pharmacy_2" } , + { AM_TWILIGHT3, "AM_TWILIGHT3", "Twilight_Pharmacy_3" } , + { ASC_BREAKER, "ASC_BREAKER", "Soul_Destroyer" } , + { ASC_CDP, "ASC_CDP", "Create_Deadly_Poison" } , + { ASC_EDP, "ASC_EDP", "Enchant_Deadly_Poison" } , + { ASC_KATAR, "ASC_KATAR", "Advanced_Katar_Mastery" } , + { ASC_METEORASSAULT, "ASC_METEORASSAULT", "Meteor_Assault" } , + { AS_CLOAKING, "AS_CLOAKING", "Cloaking" } , + { AS_ENCHANTPOISON, "AS_ENCHANTPOISON", "Enchant_Poison" } , + { AS_GRIMTOOTH, "AS_GRIMTOOTH", "Grimtooth" } , + { AS_KATAR, "AS_KATAR", "Katar_Mastery" } , + { AS_LEFT, "AS_LEFT", "Lefthand_Mastery" } , + { AS_POISONREACT, "AS_POISONREACT", "Poison_React" } , + { AS_RIGHT, "AS_RIGHT", "Righthand_Mastery" } , + { AS_SONICACCEL, "AS_SONICACCEL", "Sonic_Acceleration" } , + { AS_SONICBLOW, "AS_SONICBLOW", "Sonic_Blow" } , + { AS_SPLASHER, "AS_SPLASHER", "Venom_Splasher" } , + { AS_VENOMDUST, "AS_VENOMDUST", "Venom_Dust" } , + { AS_VENOMKNIFE, "AS_VENOMKNIFE", "Throw_Venom_Knife" } , + { BA_APPLEIDUN, "BA_APPLEIDUN", "Song_of_Lutie" } , + { BA_ASSASSINCROSS, "BA_ASSASSINCROSS", "Impressive_Riff" } , + { BA_DISSONANCE, "BA_DISSONANCE", "Unchained_Serenade" } , + { BA_FROSTJOKE, "BA_FROSTJOKE", "Unbarring_Octave" } , + { BA_MUSICALLESSON, "BA_MUSICALLESSON", "Music_Lessons" } , + { BA_MUSICALSTRIKE, "BA_MUSICALSTRIKE", "Melody_Strike" } , + { BA_PANGVOICE, "BA_PANGVOICE", "Pang_Voice" } , + { BA_POEMBRAGI, "BA_POEMBRAGI", "Magic_Strings" } , + { BA_WHISTLE, "BA_WHISTLE", "Perfect_Tablature" } , + { BD_ADAPTATION, "BD_ADAPTATION", "Amp" } , + { BD_DRUMBATTLEFIELD, "BD_DRUMBATTLEFIELD", "Battle_Theme" } , + { BD_ENCORE, "BD_ENCORE", "Encore" } , + { BD_ETERNALCHAOS, "BD_ETERNALCHAOS", "Down_Tempo" } , + { BD_INTOABYSS, "BD_INTOABYSS", "Power_Cord" } , + { BD_LULLABY, "BD_LULLABY", "Lullaby" } , + { BD_RICHMANKIM, "BD_RICHMANKIM", "Mental_Sensing" } , + { BD_RINGNIBELUNGEN, "BD_RINGNIBELUNGEN", "Harmonic_Lick" } , + { BD_ROKISWEIL, "BD_ROKISWEIL", "Classical_Pluck" } , + { BD_SIEGFRIED, "BD_SIEGFRIED", "Acoustic_Rhythm" } , + { BS_ADRENALINE, "BS_ADRENALINE", "Adrenaline_Rush" } , + { BS_ADRENALINE2, "BS_ADRENALINE2", "Advanced_Adrenaline_Rush" } , + { BS_AXE, "BS_AXE", "Smith_Axe" } , + { BS_DAGGER, "BS_DAGGER", "Smith_Dagger" } , + { BS_ENCHANTEDSTONE, "BS_ENCHANTEDSTONE", "Enchantedstone_Craft" } , + { BS_FINDINGORE, "BS_FINDINGORE", "Ore_Discovery" } , + { BS_GREED, "BS_GREED", "Greed" } , + { BS_HAMMERFALL, "BS_HAMMERFALL", "Hammer_Fall" } , + { BS_HILTBINDING, "BS_HILTBINDING", "Hilt_Binding" } , + { BS_IRON, "BS_IRON", "Iron_Tempering" } , + { BS_KNUCKLE, "BS_KNUCKLE", "Smith_Knucklebrace" } , + { BS_MACE, "BS_MACE", "Smith_Mace" } , + { BS_MAXIMIZE, "BS_MAXIMIZE", "Power_Maximize" } , + { BS_ORIDEOCON, "BS_ORIDEOCON", "Oridecon_Research" } , + { BS_OVERTHRUST, "BS_OVERTHRUST", "Power-Thrust" } , + { BS_REPAIRWEAPON, "BS_REPAIRWEAPON", "Weapon_Repair" } , + { BS_SKINTEMPER, "BS_SKINTEMPER", "Skin_Tempering" } , + { BS_SPEAR, "BS_SPEAR", "Smith_Spear" } , + { BS_STEEL, "BS_STEEL", "Steel_Tempering" } , + { BS_SWORD, "BS_SWORD", "Smith_Sword" } , + { BS_TWOHANDSWORD, "BS_TWOHANDSWORD", "Smith_Two-handed_Sword" } , + { BS_UNFAIRLYTRICK, "BS_UNFAIRLYTRICK", "Unfair_Trick" } , + { BS_WEAPONPERFECT, "BS_WEAPONPERFECT", "Weapon_Perfection" } , + { BS_WEAPONRESEARCH, "BS_WEAPONRESEARCH", "Weaponry_Research" } , + { CG_ARROWVULCAN, "CG_ARROWVULCAN", "Vulcan_Arrow" } , + { CG_HERMODE, "CG_HERMODE", "Wand_of_Hermode" } , + { CG_LONGINGFREEDOM, "CG_LONGINGFREEDOM", "Longing_for_Freedom" } , + { CG_MARIONETTE, "CG_MARIONETTE", "Marionette_Control" } , + { CG_MOONLIT, "CG_MOONLIT", "Sheltering_Bliss" } , + { CG_TAROTCARD, "CG_TAROTCARD", "Tarot_Card_of_Fate" } , + { CH_CHAINCRUSH, "CH_CHAINCRUSH", "Chain_Crush_Combo" } , + { CH_PALMSTRIKE, "CH_PALMSTRIKE", "Raging_Palm_Strike" } , + { CH_SOULCOLLECT, "CH_SOULCOLLECT", "Zen" } , + { CH_TIGERFIST, "CH_TIGERFIST", "Glacier_Fist" } , + { CR_ACIDDEMONSTRATION, "CR_ACIDDEMONSTRATION", "Acid_Demonstration" } , + { CR_ALCHEMY, "CR_ALCHEMY", "Alchemy" } , + { CR_AUTOGUARD, "CR_AUTOGUARD", "Guard" } , + { CR_CULTIVATION, "CR_CULTIVATION", "Plant_Cultivation" } , + { CR_DEFENDER, "CR_DEFENDER", "Defending_Aura" } , + { CR_DEVOTION, "CR_DEVOTION", "Sacrifice" } , + { CR_FULLPROTECTION, "CR_FULLPROTECTION", "Full_Protection" } , + { CR_GRANDCROSS, "CR_GRANDCROSS", "Grand_Cross" } , + { CR_HOLYCROSS, "CR_HOLYCROSS", "Holy_Cross" } , + { CR_PROVIDENCE, "CR_PROVIDENCE", "Resistant_Souls" } , + { CR_REFLECTSHIELD, "CR_REFLECTSHIELD", "Shield_Reflect" } , + { CR_SHIELDBOOMERANG, "CR_SHIELDBOOMERANG", "Shield_Boomerang" } , + { CR_SHIELDCHARGE, "CR_SHIELDCHARGE", "Smite" } , + { CR_SHRINK, "CR_SHRINK", "Shrink" } , + { CR_SLIMPITCHER, "CR_SLIMPITCHER", "Slim_Pitcher" } , + { CR_SPEARQUICKEN, "CR_SPEARQUICKEN", "Spear_Quicken" } , + { CR_SYNTHESISPOTION, "CR_SYNTHESISPOTION", "Potion_Synthesis" } , + { CR_TRUST, "CR_TRUST", "Faith" } , + { DC_DANCINGLESSON, "DC_DANCINGLESSON", "Dance_Lessons" } , + { DC_DONTFORGETME, "DC_DONTFORGETME", "Slow_Grace" } , + { DC_FORTUNEKISS, "DC_FORTUNEKISS", "Lady_Luck" } , + { DC_HUMMING, "DC_HUMMING", "Focus_Ballet" } , + { DC_SCREAM, "DC_SCREAM", "Dazzler" } , + { DC_SERVICEFORYOU, "DC_SERVICEFORYOU", "Gypsy's_Kiss" } , + { DC_THROWARROW, "DC_THROWARROW", "Slinging_Arrow" } , + { DC_UGLYDANCE, "DC_UGLYDANCE", "Hip_Shaker" } , + { DC_WINKCHARM, "DC_WINKCHARM", "Sexy_Wink" } , + { GD_APPROVAL, "GD_APPROVAL", "Official_Guild_Approval" } , + { GD_BATTLEORDER, "GD_BATTLEORDER", "Battle_Command" } , + { GD_DEVELOPMENT, "GD_DEVELOPMENT", "Permanent_Development" } , + { GD_EMERGENCYCALL, "GD_EMERGENCYCALL", "Urgent_Call" } , + { GD_EXTENSION, "GD_EXTENSION", "Guild_Extension" } , + { GD_GLORYGUILD, "GD_GLORYGUILD", "Glory_of_Guild" } , + { GD_GLORYWOUNDS, "GD_GLORYWOUNDS", "Glorious_Wounds" } , + { GD_GUARDUP, "GD_GUARDUP", "Strengthen_Guardian" } , + { GD_HAWKEYES, "GD_HAWKEYES", "Sharp_Gaze" } , + { GD_KAFRACONTRACT, "GD_KAFRACONTRACT", "Contract_with_Kafra" } , + { GD_LEADERSHIP, "GD_LEADERSHIP", "Great_Leadership" } , + { GD_REGENERATION, "GD_REGENERATION", "Regeneration" } , + { GD_RESTORE, "GD_RESTORE", "Restoration" } , + { GD_SOULCOLD, "GD_SOULCOLD", "Cold_Heart" } , + { GS_ADJUSTMENT, "GS_ADJUSTMENT", "Adjustment" } , + { GS_BULLSEYE, "GS_BULLSEYE", "Bulls_Eye" } , + { GS_CHAINACTION, "GS_CHAINACTION", "Chain_Action" } , + { GS_CRACKER, "GS_CRACKER", "Cracker" } , + { GS_DESPERADO, "GS_DESPERADO", "Desperado" } , + { GS_DISARM, "GS_DISARM", "Disarm" } , + { GS_DUST, "GS_DUST", "Dust" } , + { GS_FLING, "GS_FLING", "Fling" } , + { GS_FULLBUSTER, "GS_FULLBUSTER", "Full_Buster" } , + { GS_GATLINGFEVER, "GS_GATLINGFEVER", "Gatling_Fever" } , + { GS_GLITTERING, "GS_GLITTERING", "Flip_the_Coin" } , + { GS_GROUNDDRIFT, "GS_GROUNDDRIFT", "Ground_Drift" } , + { GS_INCREASING, "GS_INCREASING", "Increasing_Accuracy" } , + { GS_MADNESSCANCEL, "GS_MADNESSCANCEL", "Madness_Canceler" } , + { GS_MAGICALBULLET, "GS_MAGICALBULLET", "Magical_Bullet" } , + { GS_PIERCINGSHOT, "GS_PIERCINGSHOT", "Piercing_Shot" } , + { GS_RAPIDSHOWER, "GS_RAPIDSHOWER", "Rapid_Shower" } , + { GS_SINGLEACTION, "GS_SINGLEACTION", "Single_Action" } , + { GS_SNAKEEYE, "GS_SNAKEEYE", "Snake_Eye" } , + { GS_SPREADATTACK, "GS_SPREADATTACK", "Spread_Attack" } , + { GS_TRACKING, "GS_TRACKING", "Tracking" } , + { GS_TRIPLEACTION, "GS_TRIPLEACTION", "Triple_Action" } , + { HP_ASSUMPTIO, "HP_ASSUMPTIO", "Assumptio" } , + { HP_BASILICA, "HP_BASILICA", "Basilica" } , + { HP_MANARECHARGE, "HP_MANARECHARGE", "Mana_Recharge" } , + { HP_MEDITATIO, "HP_MEDITATIO", "Meditatio" } , + { HT_ANKLESNARE, "HT_ANKLESNARE", "Ankle_Snare" } , + { HT_BEASTBANE, "HT_BEASTBANE", "Beast_Bane" } , + { HT_BLASTMINE, "HT_BLASTMINE", "Blast_Mine" } , + { HT_BLITZBEAT, "HT_BLITZBEAT", "Blitz_Beat" } , + { HT_CLAYMORETRAP, "HT_CLAYMORETRAP", "Claymore_Trap" } , + { HT_DETECTING, "HT_DETECTING", "Detect" } , + { HT_FALCON, "HT_FALCON", "Falconry_Mastery" } , + { HT_FLASHER, "HT_FLASHER", "Flasher" } , + { HT_FREEZINGTRAP, "HT_FREEZINGTRAP", "Freezing_Trap" } , + { HT_LANDMINE, "HT_LANDMINE", "Land_Mine" } , + { HT_PHANTASMIC, "HT_PHANTASMIC", "Phantasmic_Arrow" } , + { HT_POWER, "HT_POWER", "Beast_Strafing" } , + { HT_REMOVETRAP, "HT_REMOVETRAP", "Remove_Trap" } , + { HT_SANDMAN, "HT_SANDMAN", "Sandman" } , + { HT_SHOCKWAVE, "HT_SHOCKWAVE", "Shockwave_Trap" } , + { HT_SKIDTRAP, "HT_SKIDTRAP", "Skid_Trap" } , + { HT_SPRINGTRAP, "HT_SPRINGTRAP", "Spring_Trap" } , + { HT_STEELCROW, "HT_STEELCROW", "Steel_Crow" } , + { HT_TALKIEBOX, "HT_TALKIEBOX", "Talkie_Box" } , + { HW_GANBANTEIN, "HW_GANBANTEIN", "Ganbantein" } , + { HW_GRAVITATION, "HW_GRAVITATION", "Gravitation_Field" } , + { HW_MAGICCRASHER, "HW_MAGICCRASHER", "Stave_Crasher" } , + { HW_MAGICPOWER, "HW_MAGICPOWER", "Mystical_Amplification" } , + { HW_NAPALMVULCAN, "HW_NAPALMVULCAN", "Napalm_Vulcan" } , + { HW_SOULDRAIN, "HW_SOULDRAIN", "Soul_Drain" } , + { ITM_TOMAHAWK, "ITM_TOMAHAWK", "Tomahawk_Throwing" } , + { KN_AUTOCOUNTER, "KN_AUTOCOUNTER", "Counter_Attack" } , + { KN_BOWLINGBASH, "KN_BOWLINGBASH", "Bowling_Bash" } , + { KN_BRANDISHSPEAR, "KN_BRANDISHSPEAR", "Brandish_Spear" } , + { KN_CAVALIERMASTERY, "KN_CAVALIERMASTERY", "Cavalier_Mastery" } , + { KN_CHARGEATK, "KN_CHARGEATK", "Charge_Attack" } , + { KN_ONEHAND, "KN_ONEHAND", "Onehand_Quicken" } , + { KN_PIERCE, "KN_PIERCE", "Pierce" } , + { KN_RIDING, "KN_RIDING", "Peco_Peco_Ride" } , + { KN_SPEARBOOMERANG, "KN_SPEARBOOMERANG", "Spear_Boomerang" } , + { KN_SPEARMASTERY, "KN_SPEARMASTERY", "Spear_Mastery" } , + { KN_SPEARSTAB, "KN_SPEARSTAB", "Spear_Stab" } , + { KN_TWOHANDQUICKEN, "KN_TWOHANDQUICKEN", "Twohand_Quicken" } , + { LK_AURABLADE, "LK_AURABLADE", "Aura_Blade" } , + { LK_BERSERK, "LK_BERSERK", "Frenzy" } , + { LK_CONCENTRATION, "LK_CONCENTRATION", "Spear_Dynamo" } , + { LK_HEADCRUSH, "LK_HEADCRUSH", "Traumatic_Blow" } , + { LK_JOINTBEAT, "LK_JOINTBEAT", "Vital_Strike" } , + { LK_PARRYING, "LK_PARRYING", "Parry" } , + { LK_SPIRALPIERCE, "LK_SPIRALPIERCE", "Clashing_Spiral" } , + { LK_TENSIONRELAX, "LK_TENSIONRELAX", "Relax" } , + { MC_CARTREVOLUTION, "MC_CARTREVOLUTION", "Cart_Revolution" } , + { MC_CHANGECART, "MC_CHANGECART", "Change_Cart" } , + { MC_DISCOUNT, "MC_DISCOUNT", "Discount" } , + { MC_IDENTIFY, "MC_IDENTIFY", "Item_Appraisal" } , + { MC_INCCARRY, "MC_INCCARRY", "Enlarge_Weight_Limit" } , + { MC_LOUD, "MC_LOUD", "Crazy_Uproar" } , + { MC_MAMMONITE, "MC_MAMMONITE", "Mammonite" } , + { MC_OVERCHARGE, "MC_OVERCHARGE", "Overcharge" } , + { MC_PUSHCART, "MC_PUSHCART", "Pushcart" } , + { MC_VENDING, "MC_VENDING", "Vending" } , + { MG_COLDBOLT, "MG_COLDBOLT", "Cold_Bolt" } , + { MG_ENERGYCOAT, "MG_ENERGYCOAT", "Energy_Coat" } , + { MG_FIREBALL, "MG_FIREBALL", "Fire_Ball" } , + { MG_FIREBOLT, "MG_FIREBOLT", "Fire_Bolt" } , + { MG_FIREWALL, "MG_FIREWALL", "Fire_Wall" } , + { MG_FROSTDIVER, "MG_FROSTDIVER", "Frost_Diver" } , + { MG_LIGHTNINGBOLT, "MG_LIGHTNINGBOLT", "Lightening_Bolt" } , + { MG_NAPALMBEAT, "MG_NAPALMBEAT", "Napalm_Beat" } , + { MG_SAFETYWALL, "MG_SAFETYWALL", "Safety_Wall" } , + { MG_SIGHT, "MG_SIGHT", "Sight" } , + { MG_SOULSTRIKE, "MG_SOULSTRIKE", "Soul_Strike" } , + { MG_SRECOVERY, "MG_SRECOVERY", "Increase_SP_Recovery" } , + { MG_STONECURSE, "MG_STONECURSE", "Stone_Curse" } , + { MG_THUNDERSTORM, "MG_THUNDERSTORM", "Thunderstorm" } , + { MO_ABSORBSPIRITS, "MO_ABSORBSPIRITS", "Spiritual_Sphere_Absorption" } , + { MO_BALKYOUNG, "MO_BALKYOUNG", "Ki_Explosion" } , + { MO_BLADESTOP, "MO_BLADESTOP", "Root" } , + { MO_BODYRELOCATION, "MO_BODYRELOCATION", "Snap" } , + { MO_CALLSPIRITS, "MO_CALLSPIRITS", "Summon_Spirit_Sphere" } , + { MO_CHAINCOMBO, "MO_CHAINCOMBO", "Raging_Quadruple_Blow" } , + { MO_COMBOFINISH, "MO_COMBOFINISH", "Raging_Thrust" } , + { MO_DODGE, "MO_DODGE", "Flee" } , + { MO_EXPLOSIONSPIRITS, "MO_EXPLOSIONSPIRITS", "Fury" } , + { MO_EXTREMITYFIST, "MO_EXTREMITYFIST", "Guillotine_Fist" } , + { MO_FINGEROFFENSIVE, "MO_FINGEROFFENSIVE", "Throw_Spirit_Sphere" } , + { MO_INVESTIGATE, "MO_INVESTIGATE", "Occult_Impaction" } , + { MO_IRONHAND, "MO_IRONHAND", "Iron_Fists" } , + { MO_KITRANSLATION, "MO_KITRANSLATION", "Ki_Translation" } , + { MO_SPIRITSRECOVERY, "MO_SPIRITSRECOVERY", "Spiritual_Cadence" } , + { MO_STEELBODY, "MO_STEELBODY", "Mental_Strength" } , + { MO_TRIPLEATTACK, "MO_TRIPLEATTACK", "Raging_Trifecta_Blow" } , + { NJ_BAKUENRYU, "NJ_BAKUENRYU", "NJ_BAKUENRYU" } , + { NJ_BUNSINJYUTSU, "NJ_BUNSINJYUTSU", "NJ_BUNSINJYUTSU" } , + { NJ_HUUJIN, "NJ_HUUJIN", "NJ_HUUJIN" } , + { NJ_HUUMA, "NJ_HUUMA", "NJ_HUUMA" } , + { NJ_HYOUSENSOU, "NJ_HYOUSENSOU", "NJ_HYOUSENSOU" } , + { NJ_HYOUSYOURAKU, "NJ_HYOUSYOURAKU", "NJ_HYOUSYOURAKU" } , + { NJ_ISSEN, "NJ_ISSEN", "NJ_ISSEN" } , + { NJ_KAENSIN, "NJ_KAENSIN", "NJ_KAENSIN" } , + { NJ_KAMAITACHI, "NJ_KAMAITACHI", "NJ_KAMAITACHI" } , + { NJ_KASUMIKIRI, "NJ_KASUMIKIRI", "NJ_KASUMIKIRI" } , + { NJ_KIRIKAGE, "NJ_KIRIKAGE", "NJ_KIRIKAGE" } , + { NJ_KOUENKA, "NJ_KOUENKA", "NJ_KOUENKA" } , + { NJ_KUNAI, "NJ_KUNAI", "NJ_KUNAI" } , + { NJ_NEN, "NJ_NEN", "NJ_NEN" } , + { NJ_NINPOU, "NJ_NINPOU", "NJ_NINPOU" } , + { NJ_RAIGEKISAI, "NJ_RAIGEKISAI", "NJ_RAIGEKISAI" } , + { NJ_SHADOWJUMP, "NJ_SHADOWJUMP", "NJ_SHADOWJUMP" } , + { NJ_SUITON, "NJ_SUITON", "NJ_SUITON" } , + { NJ_SYURIKEN, "NJ_SYURIKEN", "NJ_SYURIKEN" } , + { NJ_TATAMIGAESHI, "NJ_TATAMIGAESHI", "NJ_TATAMIGAESHI" } , + { NJ_TOBIDOUGU, "NJ_TOBIDOUGU", "NJ_TOBIDOUGU" } , + { NJ_UTSUSEMI, "NJ_UTSUSEMI", "NJ_UTSUSEMI" } , + { NJ_ZENYNAGE, "NJ_ZENYNAGE", "NJ_ZENYNAGE" } , + { NPC_AGIUP, "NPC_AGIUP", "NPC_AGIUP" } , + { NPC_ATTRICHANGE, "NPC_ATTRICHANGE", "NPC_ATTRICHANGE" } , + { NPC_BARRIER, "NPC_BARRIER", "NPC_BARRIER" } , + { NPC_BLINDATTACK, "NPC_BLINDATTACK", "NPC_BLINDATTACK" } , + { NPC_BLOODDRAIN, "NPC_BLOODDRAIN", "NPC_BLOODDRAIN" } , + { NPC_BREAKARMOR, "NPC_BREAKARMOR", "NPC_BREAKARMOR" } , + { NPC_BREAKHELM, "NPC_BREAKHELM", "NPC_BREAKHELM" } , + { NPC_BREAKSHIELD, "NPC_BREAKSHIELD", "NPC_BREAKSHIELD" } , + { NPC_BREAKWEAPON, "NPC_BREAKWEAPON", "NPC_BREAKWEAPON" } , + { NPC_CALLSLAVE, "NPC_CALLSLAVE", "NPC_CALLSLAVE" } , + { NPC_CHANGEDARKNESS, "NPC_CHANGEDARKNESS", "NPC_CHANGEDARKNESS" } , + { NPC_CHANGEFIRE, "NPC_CHANGEFIRE", "NPC_CHANGEFIRE" } , + { NPC_CHANGEGROUND, "NPC_CHANGEGROUND", "NPC_CHANGEGROUND" } , + { NPC_CHANGEHOLY, "NPC_CHANGEHOLY", "NPC_CHANGEHOLY" } , + { NPC_CHANGEPOISON, "NPC_CHANGEPOISON", "NPC_CHANGEPOISON" } , + { NPC_CHANGETELEKINESIS, "NPC_CHANGETELEKINESIS", "NPC_CHANGETELEKINESIS" } , + { NPC_CHANGEUNDEAD, "NPC_CHANGEUNDEAD", "NPC_CHANGEUNDEAD" } , + { NPC_CHANGEWATER, "NPC_CHANGEWATER", "NPC_CHANGEWATER" } , + { NPC_CHANGEWIND, "NPC_CHANGEWIND", "NPC_CHANGEWIND" } , + { NPC_COMBOATTACK, "NPC_COMBOATTACK", "NPC_COMBOATTACK" } , + { NPC_CRITICALSLASH, "NPC_CRITICALSLASH", "NPC_CRITICALSLASH" } , + { NPC_CURSEATTACK, "NPC_CURSEATTACK", "NPC_CURSEATTACK" } , + { NPC_DARKBLESSING, "NPC_DARKBLESSING", "NPC_DARKBLESSING" } , + { NPC_DARKBREATH, "NPC_DARKBREATH", "NPC_DARKBREATH" } , + { NPC_DARKCROSS, "NPC_DARKCROSS", "NPC_DARKCROSS" } , + { NPC_DARKNESSATTACK, "NPC_DARKNESSATTACK", "NPC_DARKNESSATTACK" } , + { NPC_DARKSTRIKE, "NPC_DARKSTRIKE", "NPC_DARKSTRIKE" } , + { NPC_DARKTHUNDER, "NPC_DARKTHUNDER", "NPC_DARKTHUNDER" } , + { NPC_DEFENDER, "NPC_DEFENDER", "NPC_DEFENDER" } , + { NPC_EMOTION, "NPC_EMOTION", "NPC_EMOTION" } , + { NPC_EMOTION_ON, "NPC_EMOTION_ON", "NPC_EMOTION_ON" } , + { NPC_ENERGYDRAIN, "NPC_ENERGYDRAIN", "NPC_ENERGYDRAIN" } , + { NPC_FIREATTACK, "NPC_FIREATTACK", "NPC_FIREATTACK" } , + { NPC_GRANDDARKNESS, "NPC_GRANDDARKNESS", "NPC_GRANDDARKNESS" } , + { NPC_GROUNDATTACK, "NPC_GROUNDATTACK", "NPC_GROUNDATTACK" } , + { NPC_GUIDEDATTACK, "NPC_GUIDEDATTACK", "NPC_GUIDEDATTACK" } , + { NPC_HALLUCINATION, "NPC_HALLUCINATION", "NPC_HALLUCINATION" } , + { NPC_HOLYATTACK, "NPC_HOLYATTACK", "NPC_HOLYATTACK" } , + { NPC_INVISIBLE, "NPC_INVISIBLE", "NPC_INVISIBLE" } , + { NPC_KEEPING, "NPC_KEEPING", "NPC_KEEPING" } , + { NPC_LICK, "NPC_LICK", "NPC_LICK" } , + { NPC_MAGICALATTACK, "NPC_MAGICALATTACK", "NPC_MAGICALATTACK" } , + { NPC_MENTALBREAKER, "NPC_MENTALBREAKER", "NPC_MENTALBREAKER" } , + { NPC_METAMORPHOSIS, "NPC_METAMORPHOSIS", "NPC_METAMORPHOSIS" } , + { NPC_PETRIFYATTACK, "NPC_PETRIFYATTACK", "NPC_PETRIFYATTACK" } , + { NPC_PIERCINGATT, "NPC_PIERCINGATT", "NPC_PIERCINGATT" } , + { NPC_POISON, "NPC_POISON", "NPC_POISON" } , + { NPC_POISONATTACK, "NPC_POISONATTACK", "NPC_POISONATTACK" } , + { NPC_POWERUP, "NPC_POWERUP", "NPC_POWERUP" } , + { NPC_PROVOCATION, "NPC_PROVOCATION", "NPC_PROVOCATION" } , + { NPC_RANDOMATTACK, "NPC_RANDOMATTACK", "NPC_RANDOMATTACK" } , + { NPC_RANDOMMOVE, "NPC_RANDOMMOVE", "NPC_RANDOMMOVE" } , + { NPC_RANGEATTACK, "NPC_RANGEATTACK", "NPC_RANGEATTACK" } , + { NPC_REBIRTH, "NPC_REBIRTH", "NPC_REBIRTH" } , + { NPC_REVENGE, "NPC_REVENGE", "NPC_REVENGE" } , + { NPC_RUN, "NPC_RUN", "NPC_RUN" }, + { NPC_SELFDESTRUCTION, "NPC_SELFDESTRUCTION", "Kabooooom!" } , + { NPC_SIEGEMODE, "NPC_SIEGEMODE", "NPC_SIEGEMODE" } , + { NPC_SILENCEATTACK, "NPC_SILENCEATTACK", "NPC_SILENCEATTACK" } , + { NPC_SLEEPATTACK, "NPC_SLEEPATTACK", "NPC_SLEEPATTACK" } , + { NPC_SMOKING, "NPC_SMOKING", "NPC_SMOKING" } , + { NPC_SPEEDUP, "NPC_SPEEDUP", "NPC_SPEEDUP" } , + { NPC_SPLASHATTACK, "NPC_SPLASHATTACK", "NPC_SPLASHATTACK" } , + { NPC_STOP, "NPC_STOP", "NPC_STOP" } , + { NPC_STUNATTACK, "NPC_STUNATTACK", "NPC_STUNATTACK" } , + { NPC_SUICIDE, "NPC_SUICIDE", "NPC_SUICIDE" } , + { NPC_SUMMONMONSTER, "NPC_SUMMONMONSTER", "NPC_SUMMONMONSTER" } , + { NPC_SUMMONSLAVE, "NPC_SUMMONSLAVE", "NPC_SUMMONSLAVE" } , + { NPC_TELEKINESISATTACK, "NPC_TELEKINESISATTACK", "NPC_TELEKINESISATTACK" } , + { NPC_TRANSFORMATION, "NPC_TRANSFORMATION", "NPC_TRANSFORMATION" } , + { NPC_UNDEADATTACK, "NPC_UNDEADATTACK", "NPC_UNDEADATTACK" } , + { NPC_WATERATTACK, "NPC_WATERATTACK", "NPC_WATERATTACK" } , + { NPC_WINDATTACK, "NPC_WINDATTACK", "NPC_WINDATTACK" } , + { NV_BASIC, "NV_BASIC", "Basic_Skill" } , + { NV_FIRSTAID, "NV_FIRSTAID", "First Aid" } , + { NV_TRICKDEAD, "NV_TRICKDEAD", "Play_Dead" } , + { PA_GOSPEL, "PA_GOSPEL", "Battle_Chant" } , + { PA_PRESSURE, "PA_PRESSURE", "Gloria_Domini" } , + { PA_SACRIFICE, "PA_SACRIFICE", "Martyr's_Reckoning" } , + { PA_SHIELDCHAIN, "PA_SHIELDCHAIN", "Shield_Chain" } , + { PF_DOUBLECASTING, "PF_DOUBLECASTING", "Double_Casting" } , + { PF_FOGWALL, "PF_FOGWALL", "Blinding_Mist" } , + { PF_HPCONVERSION, "PF_HPCONVERSION", "Indulge" } , + { PF_MEMORIZE, "PF_MEMORIZE", "Foresight" } , + { PF_MINDBREAKER, "PF_MINDBREAKER", "Mind_Breaker" } , + { PF_SOULBURN, "PF_SOULBURN", "Soul_Siphon" } , + { PF_SOULCHANGE, "PF_SOULCHANGE", "Soul_Exhale" } , + { PF_SPIDERWEB, "PF_SPIDERWEB", "Fiber_Lock" } , + { PR_ASPERSIO, "PR_ASPERSIO", "Aspersio" } , + { PR_BENEDICTIO, "PR_BENEDICTIO", "B.S_Sacramenti" } , + { PR_GLORIA, "PR_GLORIA", "Gloria" } , + { PR_IMPOSITIO, "PR_IMPOSITIO", "Impositio_Manus" } , + { PR_KYRIE, "PR_KYRIE", "Kyrie_Eleison" } , + { PR_LEXAETERNA, "PR_LEXAETERNA", "Lex_Aeterna" } , + { PR_LEXDIVINA, "PR_LEXDIVINA", "Lex_Divina" } , + { PR_MACEMASTERY, "PR_MACEMASTERY", "Mace_Mastery" } , + { PR_MAGNIFICAT, "PR_MAGNIFICAT", "Magnificat" } , + { PR_MAGNUS, "PR_MAGNUS", "Magnus_Exorcismus" } , + { PR_REDEMPTIO, "PR_REDEMPTIO", "Redemptio" } , + { PR_SANCTUARY, "PR_SANCTUARY", "Sanctuary" } , + { PR_SLOWPOISON, "PR_SLOWPOISON", "Slow_Poison" } , + { PR_STRECOVERY, "PR_STRECOVERY", "Status_Recovery" } , + { PR_SUFFRAGIUM, "PR_SUFFRAGIUM", "Suffragium" } , + { PR_TURNUNDEAD, "PR_TURNUNDEAD", "Turn_Undead" } , + { RG_BACKSTAP, "RG_BACKSTAP", "Back_Stab" } , + { RG_CLEANER, "RG_CLEANER", "Remover" } , + { RG_CLOSECONFINE, "RG_CLOSECONFINE", "Close_Confine"} , + { RG_COMPULSION, "RG_COMPULSION", "Haggle" } , + { RG_FLAGGRAFFITI, "RG_FLAGGRAFFITI", "Piece" } , + { RG_GANGSTER, "RG_GANGSTER", "Slyness" } , + { RG_GRAFFITI, "RG_GRAFFITI", "Scribble" } , + { RG_INTIMIDATE, "RG_INTIMIDATE", "Snatch" } , + { RG_PLAGIARISM, "RG_PLAGIARISM", "Intimidate" } , + { RG_RAID, "RG_RAID", "Sightless_Mind" } , + { RG_SNATCHER, "RG_SNATCHER", "Gank" } , + { RG_STEALCOIN, "RG_STEALCOIN", "Mug" } , + { RG_STRIPARMOR, "RG_STRIPARMOR", "Divest_Armor" } , + { RG_STRIPHELM, "RG_STRIPHELM", "Divest_Helm" } , + { RG_STRIPSHIELD, "RG_STRIPSHIELD", "Divest_Shield" } , + { RG_STRIPWEAPON, "RG_STRIPWEAPON", "Divest_Weapon" } , + { RG_TUNNELDRIVE, "RG_TUNNELDRIVE", "Stalk" } , + { SA_ABRACADABRA, "SA_ABRACADABRA", "Hocus-pocus" } , + { SA_ADVANCEDBOOK, "SA_ADVANCEDBOOK", "Advanced_Book" } , + { SA_AUTOSPELL, "SA_AUTOSPELL", "Hindsight" } , + { SA_CASTCANCEL, "SA_CASTCANCEL", "Cast_Cancel" } , + { SA_CLASSCHANGE, "SA_CLASSCHANGE", "Class_Change" } , + { SA_COMA, "SA_COMA", "Coma" } , + { SA_CREATECON, "SA_CREATECON", "Create_Elemental_Converter" } , + { SA_DEATH, "SA_DEATH", "Grim_Reaper" } , + { SA_DELUGE, "SA_DELUGE", "Deluge" } , + { SA_DISPELL, "SA_DISPELL", "Dispell" } , + { SA_DRAGONOLOGY, "SA_DRAGONOLOGY", "Dragonology" } , + { SA_ELEMENTFIRE, "SA_ELEMENTFIRE", "Elemental_Change_Fire" } , + { SA_ELEMENTGROUND, "SA_ELEMENTGROUND", "Elemental_Change_Earth" } , + { SA_ELEMENTWATER, "SA_ELEMENTWATER", "Elemental_Change_Water" } , + { SA_ELEMENTWIND, "SA_ELEMENTWIND", "Elemental_Change_Wind" } , + { SA_FLAMELAUNCHER, "SA_FLAMELAUNCHER", "Endow_Blaze" } , + { SA_FORTUNE, "SA_FORTUNE", "Gold_Digger" } , + { SA_FREECAST, "SA_FREECAST", "Free_Cast" } , + { SA_FROSTWEAPON, "SA_FROSTWEAPON", "Endow_Tsunami" } , + { SA_FULLRECOVERY, "SA_FULLRECOVERY", "Rejuvenation" } , + { SA_GRAVITY, "SA_GRAVITY", "Gravity" } , + { SA_INSTANTDEATH, "SA_INSTANTDEATH", "Suicide" } , + { SA_LANDPROTECTOR, "SA_LANDPROTECTOR", "Magnetic_Earth" } , + { SA_LEVELUP, "SA_LEVELUP", "Leveling" } , + { SA_LIGHTNINGLOADER, "SA_LIGHTNINGLOADER", "Endow_Tornado" } , + { SA_MAGICROD, "SA_MAGICROD", "Magic_Rod" } , + { SA_MONOCELL, "SA_MONOCELL", "Mono_Cell" } , + { SA_QUESTION, "SA_QUESTION", "Questioning" } , + { SA_REVERSEORCISH, "SA_REVERSEORCISH", "Grampus_Morph" } , + { SA_SEISMICWEAPON, "SA_SEISMICWEAPON", "Endow_Quake" } , + { SA_SPELLBREAKER, "SA_SPELLBREAKER", "Spell_Breaker" } , + { SA_SUMMONMONSTER, "SA_SUMMONMONSTER", "Monster_Chant" } , + { SA_TAMINGMONSTER, "SA_TAMINGMONSTER", "Beastly_Hypnosis" } , + { SA_VIOLENTGALE, "SA_VIOLENTGALE", "Whirlwind" } , + { SA_VOLCANO, "SA_VOLCANO", "Volcano" } , + { SG_DEVIL, "SG_DEVIL", "Devil_of_the_Sun,_Moon_and_Stars" } , + { SG_FEEL, "SG_FEEL", "Feeling_of_the_Sun,_Moon_and_Star" } , + { SG_FRIEND, "SG_FRIEND", "Companion_of_the_Sun_and_Moon" } , + { SG_FUSION, "SG_FUSION", "Union_of_the_Sun,_Moon_and_Stars" } , + { SG_HATE, "SG_HATE", "Hatred_of_the_Sun,_Moon_and_Stars" } , + { SG_KNOWLEDGE, "SG_KNOWLEDGE", "Knowledge_of_the_Sun,_Moon_and_Stars" } , + { SG_MOON_ANGER, "SG_MOON_ANGER", "Fury_of_the_Moon" } , + { SG_MOON_BLESS, "SG_MOON_BLESS", "Bless_of_the_Moon" } , + { SG_MOON_COMFORT, "SG_MOON_COMFORT", "Comfort_of_the_Moon" } , + { SG_MOON_WARM, "SG_MOON_WARM", "Warmth_of_the_Moon" } , + { SG_STAR_ANGER, "SG_STAR_ANGER", "Fury_of_the_Stars" } , + { SG_STAR_BLESS, "SG_STAR_BLESS", "Bless_of_the_Stars" } , + { SG_STAR_COMFORT, "SG_STAR_COMFORT", "Comfort_of_the_Stars" } , + { SG_STAR_WARM, "SG_STAR_WARM", "Warmth_of_the_Stars" } , + { SG_SUN_ANGER, "SG_SUN_ANGER", "Fury_of_the_Sun" } , + { SG_SUN_BLESS, "SG_SUN_BLESS", "Bless_of_the_Sun" } , + { SG_SUN_COMFORT, "SG_SUN_COMFORT", "Comfort_of_the_Sun" } , + { SG_SUN_WARM, "SG_SUN_WARM", "Warmth_of_the_Sun" } , + { SL_ALCHEMIST, "SL_ALCHEMIST", "Spirit_of_Alchemist" } , + { SL_ASSASIN, "SL_ASSASIN", "Spirit_of_Assassin" } , + { SL_BARDDANCER, "SL_BARDDANCER", "Spirit_of_Bard_and_Dancer" } , + { SL_BLACKSMITH, "SL_BLACKSMITH", "Spirit_of_Blacksmith" } , + { SL_CRUSADER, "SL_CRUSADER", "Spirit_of_Crusader" } , + { SL_HIGH, "SL_HIGH", "Spirit_of_Advanced_1st_Class" } , + { SL_HUNTER, "SL_HUNTER", "Spirit_of_Hunter" } , + { SL_KAAHI, "SL_KAAHI", "Kaahi" } , + { SL_KAINA, "SL_KAINA", "Kaina" } , + { SL_KAITE, "SL_KAITE", "Kaite" } , + { SL_KAIZEL, "SL_KAIZEL", "Kaizel" } , + { SL_KAUPE, "SL_KAUPE", "Kaupe" } , + { SL_KNIGHT, "SL_KNIGHT", "Spirit_of_Knight" } , + { SL_MONK, "SL_MONK", "Spirit_of_Monk" } , + { SL_PRIEST, "SL_PRIEST", "Spirit_of_Priest" } , + { SL_ROGUE, "SL_ROGUE", "Spirit_of_Rogue" } , + { SL_SAGE, "SL_SAGE", "Spirit_of_Sage" } , + { SL_SKA, "SL_SKA", "Eska" } , + { SL_SKE, "SL_SKE", "Eske" } , + { SL_SMA, "SL_SMA", "Esma" } , + { SL_SOULLINKER, "SL_SOULLINKER", "Spirit_of_Soul_Linker" } , + { SL_STAR, "SL_STAR", "Spirit_of_Stars" } , + { SL_STIN, "SL_STIN", "Estin" } , + { SL_STUN, "SL_STUN", "Estun" } , + { SL_SUPERNOVICE, "SL_SUPERNOVICE", "Spirit_of_Super_Novice" } , + { SL_SWOO, "SL_SWOO", "Eswoo" } , + { SL_WIZARD, "SL_WIZARD", "Spirit_of_Wizard" } , + { SM_AUTOBERSERK, "SM_AUTOBERSERK", "Berserk" } , + { SM_BASH, "SM_BASH", "Bash" } , + { SM_ENDURE, "SM_ENDURE", "Endure" } , + { SM_FATALBLOW, "SM_FATALBLOW", "Fatal_Blow" } , + { SM_MAGNUM, "SM_MAGNUM", "Magnum_Break" } , + { SM_MOVINGRECOVERY, "SM_MOVINGRECOVERY", "HP_Recovery_While_Moving" } , + { SM_PROVOKE, "SM_PROVOKE", "Provoke" } , + { SM_RECOVERY, "SM_RECOVERY", "Increase_HP_Recovery" } , + { SM_SWORD, "SM_SWORD", "Sword_Mastery" } , + { SM_TWOHAND, "SM_TWOHAND", "Two-Handed_Sword_Mastery" } , + { SN_FALCONASSAULT, "SN_FALCONASSAULT", "Falcon_Assault" } , + { SN_SHARPSHOOTING, "SN_SHARPSHOOTING", "Focused_Arrow_Strike" } , + { SN_SIGHT, "SN_SIGHT", "Falcon_Eyes" } , + { SN_WINDWALK, "SN_WINDWALK", "Wind_Walker" } , + { ST_CHASEWALK, "ST_CHASEWALK", "Stealth" } , + { ST_FULLSTRIP, "ST_FULLSTRIP", "Full_Divestment" } , + { ST_PRESERVE, "ST_PRESERVE", "Preserve" } , + { ST_REJECTSWORD, "ST_REJECTSWORD", "Counter_Instinct" } , + { TF_BACKSLIDING, "TF_BACKSLIDING", "Back_Slide" } , + { TF_DETOXIFY, "TF_DETOXIFY", "Detoxify" } , + { TF_DOUBLE, "TF_DOUBLE", "Double_Attack" } , + { TF_HIDING, "TF_HIDING", "Hiding" } , + { TF_MISS, "TF_MISS", "Improve_Dodge" } , + { TF_PICKSTONE, "TF_PICKSTONE", "Find_Stone" } , + { TF_POISON, "TF_POISON", "Envenom" } , + { TF_SPRINKLESAND, "TF_SPRINKLESAND", "Sand_Attack" } , + { TF_STEAL, "TF_STEAL", "Steal" } , + { TF_THROWSTONE, "TF_THROWSTONE", "Stone_Fling" } , + { TK_COUNTER, "TK_COUNTER", "Spin_Kick" } , + { TK_DODGE, "TK_DODGE", "Sprint" } , + { TK_DOWNKICK, "TK_DOWNKICK", "Heel_Drop" } , + { TK_HIGHJUMP, "TK_HIGHJUMP", "Taekwon_Jump" } , + { TK_HPTIME, "TK_HPTIME", "Peaceful_Break" } , + { TK_JUMPKICK, "TK_JUMPKICK", "Flying_Kick" } , + { TK_MISSION, "TK_MISSION", "Mission" } , + { TK_POWER, "TK_POWER", "Kihop" } , + { TK_READYCOUNTER, "TK_READYCOUNTER", "Spin_Kick_Stance" } , + { TK_READYDOWN, "TK_READYDOWN", "Heel_Drop_Stance" } , + { TK_READYSTORM, "TK_READYSTORM", "Tornado_Stance" } , + { TK_READYTURN, "TK_READYTURN", "Roundhouse_Stance" } , + { TK_RUN, "TK_RUN", "Sprint" } , + { TK_SEVENWIND, "TK_SEVENWIND", "Mild_Wind" } , + { TK_SPTIME, "TK_SPTIME", "Happy_Break" } , + { TK_STORMKICK, "TK_STORMKICK", "Storm_Kick" } , + { TK_TURNKICK, "TK_TURNKICK", "Turn_Kick" } , + { WE_BABY, "WE_BABY", "Mom,_Dad,_I_love_you!" } , + { WE_CALLBABY, "WE_CALLBABY", "Come_to_me,_honey~" } , + { WE_CALLPARENT, "WE_CALLPARENT", "Mom,_Dad,_I_miss_you!" } , + { WE_CALLPARTNER, "WE_CALLPARTNER", "Romantic_Rendezvous" } , + { WE_FEMALE, "WE_FEMALE", "Loving_Touch" } , + { WE_MALE, "WE_MALE", "Undying_Love" } , + { WS_CARTBOOST, "WS_CARTBOOST", "Cart_Boost" } , + { WS_CARTTERMINATION, "WS_CARTTERMINATION", "Cart_Termination" } , + { WS_CREATECOIN, "WS_CREATECOIN", "Coin_Craft" } , + { WS_CREATENUGGET, "WS_CREATENUGGET", "Nugget_Craft" } , + { WS_MELTDOWN, "WS_MELTDOWN", "Shattering_Strike" } , + { WS_OVERTHRUSTMAX, "WS_OVERTHRUSTMAX", "Max_Power-Thust" } , + { WS_SYSTEMCREATE, "WS_SYSTEMCREATE", "Auto_Attacking_Machine_Craft" } , + { WS_WEAPONREFINE, "WS_WEAPONREFINE", "Weapon_Refine" } , + { WZ_EARTHSPIKE, "WZ_EARTHSPIKE", "Earth_Spike" } , + { WZ_ESTIMATION, "WZ_ESTIMATION", "Sense" } , + { WZ_FIREPILLAR, "WZ_FIREPILLAR", "Fire_Pillar" } , + { WZ_FROSTNOVA, "WZ_FROSTNOVA", "Frost_Nova" } , + { WZ_HEAVENDRIVE, "WZ_HEAVENDRIVE", "Heaven's_Drive" } , + { WZ_ICEWALL, "WZ_ICEWALL", "Ice_Wall" } , + { WZ_JUPITEL, "WZ_JUPITEL", "Jupitel_Thunder" } , + { WZ_METEOR, "WZ_METEOR", "Meteor_Storm" } , + { WZ_QUAGMIRE, "WZ_QUAGMIRE", "Quagmire" } , + { WZ_SIGHTBLASTER, "WZ_SIGHTBLASTER", "Sight_Blaster" } , + { WZ_SIGHTRASHER, "WZ_SIGHTRASHER", "Sightrasher" } , + { WZ_STORMGUST, "WZ_STORMGUST", "Storm_Gust" } , + { WZ_VERMILION, "WZ_VERMILION", "Lord_of_Vermilion" } , + { WZ_WATERBALL, "WZ_WATERBALL", "Water_Ball" } , + //[blackhole89] + { HLIF_HEAL, "HLIF_HEAL", "Healing_Touch" }, + { HLIF_AVOID, "HLIF_AVOID", "Avoid" }, + { HLIF_BRAIN, "HLIF_BRAIN", "Brain_Surgery" }, + { HLIF_CHANGE, "HLIF_CHANGE", "Change" }, + { HAMI_CASTLE, "HAMI_CASTLE", "Castling" }, + { HAMI_DEFENCE, "HAMI_DEFENCE", "Defense" }, + { HAMI_SKIN, "HAMI_SKIN", "Adamantium_Skin" }, + { HAMI_BLOODLUST, "HAMI_BLOODLUST", "Bloodlust" }, + { HFLI_MOON, "HFLI_MOON", "Moonlight" }, + { HFLI_FLEET, "HFLI_FLEET", "Fleeting_Move" }, + { HFLI_SPEED, "HFLI_SPEED", "Speed" }, + { HFLI_SBR44, "HFLI_SBR44", "S.B.R.44" }, + { HVAN_CAPRICE, "HVAN_CAPRICE", "Caprice" }, + { HVAN_CHAOTIC, "HVAN_CHAOTIC", "Benediction_of_Chaos" }, + { HVAN_INSTRUCT, "HVAN_INSTRUCT", "Instruct" }, + { HVAN_EXPLOSION, "HVAN_EXPLOSION", "Bio_Explosion" }, + { 0, "UNKNOWN_SKILL", "Unknown_Skill" } +}; + +static const int dirx[8]={0,-1,-1,-1,0,1,1,1}; +static const int diry[8]={1,1,0,-1,-1,-1,0,1}; + +static struct eri *skill_unit_ers = NULL; //For handling skill_unit's [Skotlex] +static struct eri *skill_timer_ers = NULL; //For handling skill_timerskills [Skotlex] + +/* スキルデ?タベ?ス */ +struct skill_db skill_db[MAX_SKILL_DB]; + +/* アイテム??ャデ?タベ?ス */ +struct skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB]; + +/* 矢??ャスキルデ?タベ?ス */ +struct skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB]; + +/* アブラカダブラ?動スキルデ?タベ?ス */ +struct skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB]; + +// macros to check for out of bounds errors [celest] +// i: Skill ID, l: Skill Level, var: Value to return after checking +// for values that don't require level just put a one (putting 0 will trigger return 0; instead +// for values that might need to use a different function just skill_chk would suffice. +#define skill_chk(i, l) \ + if (i >= GD_SKILLRANGEMIN && i <= GD_SKILLRANGEMAX) { return 0; } \ + if (i >= GD_SKILLBASE) {i = GD_SKILLRANGEMIN + i - GD_SKILLBASE;} \ + if (i < 1 || i >= MAX_SKILL_DB) {return 0;} \ + if (l <= 0 || l > MAX_SKILL_LEVEL) {return 0;} +#define skill_get(var, i, l) \ + { skill_chk(i, l); return var; } + +// Skill DB +int skill_get_hit( int id ){ skill_get (skill_db[id].hit, id, 1); } +int skill_get_inf( int id ){ skill_get (skill_db[id].inf, id, 1); } +int skill_get_pl( int id ){ skill_get (skill_db[id].pl, id, 1); } +int skill_get_nk( int id ){ skill_get (skill_db[id].nk, id, 1); } +int skill_get_max( int id ){ skill_get (skill_db[id].max, id, 1); } +int skill_get_range( int id , int lv ){ skill_get(skill_db[id].range[lv-1], id, lv); } +int skill_get_splash( int id , int lv ){ skill_chk (id, lv); return (skill_db[id].splash[lv-1]>=0?skill_db[id].splash[lv-1]:AREA_SIZE); } +int skill_get_hp( int id ,int lv ){ skill_get (skill_db[id].hp[lv-1], id, lv); } +int skill_get_sp( int id ,int lv ){ skill_get (skill_db[id].sp[lv-1], id, lv); } +int skill_get_hp_rate(int id, int lv ){ skill_get (skill_db[id].hp_rate[lv-1], id, lv); } +int skill_get_sp_rate(int id, int lv ){ skill_get (skill_db[id].sp_rate[lv-1], id, lv); } +int skill_get_state(int id) { skill_get (skill_db[id].state, id, 1); } +int skill_get_spiritball(int id, int lv) { skill_get (skill_db[id].spiritball[lv-1], id, lv); } +int skill_get_itemid(int id, int idx) { skill_get (skill_db[id].itemid[idx], id, 1); } +int skill_get_itemqty(int id, int idx) { skill_get (skill_db[id].amount[idx], id, 1); } +int skill_get_zeny( int id ,int lv ){ skill_get (skill_db[id].zeny[lv-1], id, lv); } +int skill_get_num( int id ,int lv ){ skill_get (skill_db[id].num[lv-1], id, lv); } +int skill_get_cast( int id ,int lv ){ skill_get (skill_db[id].cast[lv-1], id, lv); } +int skill_get_delay( int id ,int lv ){ skill_get (skill_db[id].delay[lv-1], id, lv); } +int skill_get_walkdelay( int id ,int lv ){ skill_get (skill_db[id].walkdelay[lv-1], id, lv); } +int skill_get_time( int id ,int lv ){ skill_get (skill_db[id].upkeep_time[lv-1], id, lv); } +int skill_get_time2( int id ,int lv ){ skill_get (skill_db[id].upkeep_time2[lv-1], id, lv); } +int skill_get_castdef( int id ){ skill_get (skill_db[id].cast_def_rate, id, 1); } +int skill_get_weapontype( int id ){ skill_get (skill_db[id].weapon, id, 1); } +int skill_get_ammotype( int id ){ skill_get (skill_db[id].ammo, id, 1); } +int skill_get_ammo_qty( int id, int lv ){ skill_get (skill_db[id].ammo_qty[lv-1], id, lv); } +int skill_get_inf2( int id ){ skill_get (skill_db[id].inf2, id, 1); } +int skill_get_castcancel( int id ){ skill_get (skill_db[id].castcancel, id, 1); } +int skill_get_maxcount( int id ){ skill_get (skill_db[id].maxcount, id, 1); } +int skill_get_blewcount( int id ,int lv ){ skill_get (skill_db[id].blewcount[lv-1], id, lv); } +int skill_get_mhp( int id ,int lv ){ skill_get (skill_db[id].mhp[lv-1], id, lv); } +int skill_get_castnodex( int id ,int lv ){ skill_get (skill_db[id].castnodex[lv-1], id, lv); } +int skill_get_delaynodex( int id ,int lv ){ skill_get (skill_db[id].delaynodex[lv-1], id, lv); } +int skill_get_nocast ( int id ){ skill_get (skill_db[id].nocast, id, 1); } +int skill_get_type( int id ){ skill_get (skill_db[id].skill_type, id, 1); } +int skill_get_unit_id ( int id, int flag ){ skill_get (skill_db[id].unit_id[flag], id, 1); } +int skill_get_unit_layout_type( int id ,int lv ){ skill_get (skill_db[id].unit_layout_type[lv-1], id, lv); } +int skill_get_unit_interval( int id ){ skill_get (skill_db[id].unit_interval, id, 1); } +int skill_get_unit_range( int id, int lv ){ skill_get (skill_db[id].unit_range[lv-1], id, lv); } +int skill_get_unit_target( int id ){ skill_get ((skill_db[id].unit_target&BCT_ALL), id, 1); } +int skill_get_unit_bl_target( int id ){ skill_get ((skill_db[id].unit_target&BL_ALL), id, 1); } +int skill_get_unit_flag( int id ){ skill_get (skill_db[id].unit_flag, id, 1); } +const char* skill_get_name( int id ){ + if (id >= GD_SKILLRANGEMIN && id <= GD_SKILLRANGEMAX) + return "UNKNOWN_SKILL"; + if (id >= GD_SKILLBASE) + id = GD_SKILLRANGEMIN + id - GD_SKILLBASE; + if (id < 1 || id > MAX_SKILL_DB || skill_db[id].name==NULL) + return "UNKNOWN_SKILL"; //Can't use skill_chk because we return a string. + return skill_db[id].name; +} + +int skill_tree_get_max(int id, int b_class){ + int i, skillid; + for(i=0;(skillid=skill_tree[b_class][i].id)>0;i++) + if (id == skillid) return skill_tree[b_class][i].max; + return skill_get_max (id); +} + +/* プ?トタイプ */ +int skill_castend_damage_id( struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag ); +int skill_frostjoke_scream(struct block_list *bl,va_list ap); +int status_change_timer_sub(struct block_list *bl, va_list ap); +int skill_attack_area(struct block_list *bl,va_list ap); +struct skill_unit_group *skill_locate_element_field(struct block_list *bl); // [Skotlex] +int skill_graffitiremover(struct block_list *bl, va_list ap); // [Valaris] +int skill_greed(struct block_list *bl, va_list ap); +int skill_landprotector(struct block_list *bl, va_list ap); +int skill_ganbatein(struct block_list *bl, va_list ap); +int skill_trap_splash(struct block_list *bl, va_list ap); +int skill_count_target(struct block_list *bl, va_list ap); +struct skill_unit_group_tickset *skill_unitgrouptickset_search(struct block_list *bl,struct skill_unit_group *sg,int tick); +static int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick); +static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick); +int skill_unit_effect(struct block_list *bl,va_list ap); +static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv); + +int enchant_eff[5] = { 10, 14, 17, 19, 20 }; +int deluge_eff[5] = { 5, 9, 12, 14, 15 }; + +int skill_get_casttype(int id) +{ + int inf = skill_get_inf(id); + if (inf&(INF_GROUND_SKILL)) + return CAST_GROUND; + if (inf&INF_SUPPORT_SKILL) + return CAST_NODAMAGE; + if (inf&INF_SELF_SKILL) { + if(skill_get_inf2(id)&INF2_NO_TARGET_SELF) + return CAST_DAMAGE; //Combo skill. + return CAST_NODAMAGE; + } + if (skill_get_nk(id)&NK_NO_DAMAGE) + return CAST_NODAMAGE; + return CAST_DAMAGE; +}; + +//Returns actual skill range taking into account attack range and AC_OWL [Skotlex] +int skill_get_range2(struct block_list *bl, int id, int lv) { + int range = skill_get_range(id, lv); + if(range < 0) { + if (battle_config.use_weapon_skill_range) + return status_get_range(bl); + range *=-1; + } + //TODO: Find a way better than hardcoding the list of skills affected by AC_VULTURE + switch (id) { + case AC_SHOWER: + case AC_DOUBLE: + case HT_BLITZBEAT: + case AC_CHARGEARROW: + case SN_FALCONASSAULT: + case SN_SHARPSHOOTING: + case HT_POWER: + if (bl->type == BL_PC) + range += pc_checkskill((struct map_session_data *)bl, AC_VULTURE); + else + range += 10; //Assume level 10? + break; + // added to allow GS skills to be effected by the range of Snake Eyes [Reddozen] + case GS_RAPIDSHOWER: + case GS_TRACKING: + case GS_PIERCINGSHOT: + case GS_FULLBUSTER: + case GS_SPREADATTACK: + case GS_GROUNDDRIFT: + if (bl->type == BL_PC) + range += pc_checkskill((struct map_session_data *)bl, GS_SNAKEEYE); + else + range += 10; //Assume level 10? + break; + } + + return range; +} + +int skill_calc_heal(struct block_list *bl, int skill_lv) { + int skill, heal; + heal = ( status_get_lv(bl)+status_get_int(bl) )/8 *(4+ skill_lv*8); + if(bl->type == BL_PC && (skill = pc_checkskill((TBL_PC*)bl, HP_MEDITATIO)) > 0) + heal += heal * skill * 2 / 100; + return heal; +} + +// Making plagiarize check its own function [Aru] +int can_copy(struct map_session_data *sd, int skillid) +{ + // Never copy NPC/Wedding Skills + if (skill_get_inf2(skillid)&(INF2_NPC_SKILL|INF2_WEDDING_SKILL)) + return 0; + + // High-class skills + if((skillid >= LK_AURABLADE && skillid <= ASC_CDP) || (skillid >= ST_PRESERVE && skillid <= CR_CULTIVATION)) + { + if(battle_config.copyskill_restrict == 2) + return 0; + else if(battle_config.copyskill_restrict) + return (sd->status.class_ == JOB_STALKER); + } + + return 1; +} + +// [MouseJstr] - skill ok to cast? and when? +int skillnotok(int skillid, struct map_session_data *sd) +{ + int i = skillid; + nullpo_retr (1, sd); + //if (sd == 0) + //return 0; + //return 1; + // I think it was meant to be "no skills allowed when not a valid sd" + + if (skillid >= GD_SKILLRANGEMIN && skillid <= GD_SKILLRANGEMAX) + return 1; + + if (i >= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + + if (i > MAX_SKILL || i < 0) + return 1; + + if (sd->blockskill[i] > 0) + return 1; + + if (battle_config.gm_skilluncond && pc_isGM(sd) >= battle_config.gm_skilluncond) + return 0; // gm's can do anything damn thing they want + + // Check skill restrictions [Celest] + if(!map_flag_vs(sd->bl.m) && skill_get_nocast (skillid) & 1) + return 1; + if(map[sd->bl.m].flag.pvp) { + if(!battle_config.pk_mode && skill_get_nocast (skillid) & 2) + return 1; + if(battle_config.pk_mode && skill_get_nocast (skillid) & 16) + return 1; + } + if(map_flag_gvg(sd->bl.m) && skill_get_nocast (skillid) & 4) + return 1; + if(agit_flag && skill_get_nocast (skillid) & 8) + return 1; + if(map[sd->bl.m].flag.restricted && map[sd->bl.m].zone && skill_get_nocast (skillid) & (8*map[sd->bl.m].zone)) + return 1; + + switch (skillid) { + case AL_WARP: + if(map[sd->bl.m].flag.nowarp) { + clif_skill_teleportmessage(sd,0); + return 1; + } + return 0; + break; + case AL_TELEPORT: + if(map[sd->bl.m].flag.noteleport) { + clif_skill_teleportmessage(sd,0); + return 1; + } + return 0; + case TK_HIGHJUMP: + if(map[sd->bl.m].flag.noteleport && !map_flag_gvg(sd->bl.m)) + { //Can't be used on noteleport maps, except for gvg maps [Skotlex] + clif_skill_fail(sd,skillid,0,0); + return 1; + } + break; + case WE_CALLPARTNER: + case WE_CALLPARENT: + case WE_CALLBABY: + if (map[sd->bl.m].flag.nomemo) { + clif_skill_teleportmessage(sd,1); + return 1; + } + break; + case MC_VENDING: + case MC_IDENTIFY: + return 0; // always allowed + case WZ_ICEWALL: + // noicewall flag [Valaris] + if (map[sd->bl.m].flag.noicewall) { + clif_skill_fail(sd,skillid,0,0); + return 1; + } + } + return (map[sd->bl.m].flag.noskill); +} + +/* スキルユニットの配置?報を返す */ +struct skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT]; +int firewall_unit_pos; +int icewall_unit_pos; + +struct skill_unit_layout *skill_get_unit_layout (int skillid, int skilllv, struct block_list *src, int x, int y) +{ + int pos = skill_get_unit_layout_type(skillid,skilllv); + int dir; + + if (pos != -1) + return &skill_unit_layout[pos]; + + if (src->x == x && src->y == y) + dir = 2; + else + dir = map_calc_dir(src,x,y); + + if (skillid == MG_FIREWALL) + return &skill_unit_layout [firewall_unit_pos + dir]; + else if (skillid == WZ_ICEWALL) + return &skill_unit_layout [icewall_unit_pos + dir]; + + ShowError("Unknown unit layout for skill %d, %d\n",skillid,skilllv); + return &skill_unit_layout[0]; +} + +/*========================================== + * スキル追加?果 + *------------------------------------------ + */ +int skill_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick) +{ + struct map_session_data *sd=NULL, *dstsd=NULL; + struct mob_data *md=NULL, *dstmd=NULL; + struct status_data *sstatus, *tstatus; + struct status_change *sc, *tsc; + + int skill; + int rate; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + + if(skillid < 0) + { // remove the debug print when this case is finished + ShowDebug("skill_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid, + src, bl,skillid,skilllv,attack_type,tick); + return 0; + } + if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest + + switch (src->type) { + case BL_PC: + sd = (struct map_session_data *)src; + break; + case BL_MOB: + md = (struct mob_data *)src; + break; + } + + switch (bl->type) { + case BL_PC: + dstsd=(struct map_session_data *)bl; + break; + case BL_MOB: + dstmd=(struct mob_data *)bl; + break; + } + sc = status_get_sc(src); + tsc = status_get_sc(bl); + sstatus = status_get_status_data(src); + tstatus = status_get_status_data(bl); + if (!tsc) //skill additional effect is about adding effects to the target... + //So if the target can't be inflicted with statuses, this is pointless. + return 0; + if (!sstatus || !tstatus) + return 0; //Required for stat data. + + switch(skillid){ + case 0: // Normal attacks (no skill used) + { + if(sd) { + // Automatic trigger of Blitz Beat + if (pc_isfalcon(sd) && sd->status.weapon == W_BOW && (skill=pc_checkskill(sd,HT_BLITZBEAT))>0 && + rand()%1000 <= tstatus->luk*10/3+1 ) { + int lv=(sd->status.job_level+9)/10; + skill_castend_damage_id(src,bl,HT_BLITZBEAT,(skillstate.steal_flagstatus.weapon != W_BOW && + (skill=pc_checkskill(sd,RG_SNATCHER)) > 0 && + (skill*15 + 55) + pc_checkskill(sd,TF_STEAL)*10 > rand()%1000) { + if(pc_steal_item(sd,bl)) + clif_skill_nodamage(src,bl,TF_STEAL,skill,1); + else if (battle_config.display_snatcher_skill_fail) + clif_skill_fail(sd,skillid,0,0); + } + // Chance to trigger Taekwon kicks [Dralnu] + if(sd->sc.count && sd->sc.data[SC_COMBO].timer == -1) { + if(sd->sc.data[SC_READYSTORM].timer != -1 && + sc_start4(src,SC_COMBO, 15, TK_STORMKICK,0,0,0, + (2000 - 4*sstatus->agi - 2*sstatus->dex))) + ; //Stance triggered + else if(sd->sc.data[SC_READYDOWN].timer != -1 && + sc_start4(src,SC_COMBO, 15, TK_DOWNKICK,0,0,0, + (2000 - 4*sstatus->agi - 2*sstatus->dex))) + ; //Stance triggered + else if(sd->sc.data[SC_READYTURN].timer != -1 && + sc_start4(src,SC_COMBO, 15, TK_TURNKICK,0,0,0, + (2000 - 4*sstatus->agi - 2*sstatus->dex))) + ; //Stance triggered + else if(sd->sc.data[SC_READYCOUNTER].timer != -1) + { //additional chance from SG_FRIEND [Komurka] + rate = 20; + if (sd->sc.data[SC_SKILLRATE_UP].timer != -1 && sd->sc.data[SC_SKILLRATE_UP].val1 == TK_COUNTER) { + rate += rate*sd->sc.data[SC_SKILLRATE_UP].val2/100; + status_change_end(src,SC_SKILLRATE_UP,-1); + } + sc_start4(src,SC_COMBO, rate, TK_COUNTER, bl->id,0,0, + (2000 - 4*sstatus->agi - 2*sstatus->dex)); + } + } + } + + if (sc && sc->count) { + // Enchant Poison gives a chance to poison attacked enemies + if(sc->data[SC_ENCPOISON].timer != -1) + sc_start(bl,SC_POISON,10*sc->data[SC_ENCPOISON].val2,sc->data[SC_ENCPOISON].val1, + skill_get_time2(AS_ENCHANTPOISON,sc->data[SC_ENCPOISON].val1)); + // Enchant Deadly Poison gives a chance to deadly poison attacked enemies + if(sc->data[SC_EDP].timer != -1) + sc_start4(bl,SC_DPOISON,sc->data[SC_EDP].val2, + sc->data[SC_EDP].val1,0,0,0,skill_get_time2(ASC_EDP,sc->data[SC_EDP].val1)); + } + if (tsc->count) { + if (tsc->data[SC_SPLASHER].timer != -1) + sc_start4(bl,SC_POISON,2*tsc->data[SC_SPLASHER].val1+10, + tsc->data[SC_SPLASHER].val1,0,0,0, + skill_get_time2(tsc->data[SC_SPLASHER].val2,tsc->data[SC_SPLASHER].val1)); + } + } + break; + + case SM_BASH: /* バッシュ?i急??U??j */ + if( sd && skilllv > 5 && pc_checkskill(sd,SM_FATALBLOW)>0 ){ + //TODO: How much % per base level it actually is? + sc_start(bl,SC_STUN,(5*(skilllv-5)+(int)sd->status.base_level/10), + skilllv,skill_get_time2(SM_FATALBLOW,skilllv)); + } + break; + + case AS_VENOMKNIFE: + if (sd) //Poison chance must be that of Envenom. [Skotlex] + skilllv = pc_checkskill(sd, TF_POISON); + case TF_POISON: /* インベナム */ + case AS_SPLASHER: /* ベナムスプラッシャ? */ + if(!sc_start(bl,SC_POISON,(2*skilllv+10),skilllv,skill_get_time2(skillid,skilllv)) + && sd && skillid==TF_POISON + ) + clif_skill_fail(sd,skillid,0,0); + break; + + case AS_SONICBLOW: /* ソニックブ?? */ + sc_start(bl,SC_STUN,(2*skilllv+10),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case AS_GRIMTOOTH: + { + int type = dstsd?SC_SLOWDOWN:SC_STOP; + if (tsc->data[type].timer == -1) + sc_start(bl,type,100,skilllv,skill_get_time2(skillid, skilllv)); + break; + } + case MG_FROSTDIVER: /* フ?ストダイバ? */ + case WZ_FROSTNOVA: /* フ?ストノヴァ */ + { + rate = (skilllv*3+35)-(tstatus->int_ + tstatus->luk)/15; + if (rate <= 5) + rate = 5; + sc_start(bl,SC_FREEZE,rate,skilllv,skill_get_time2(skillid,skilllv)); + } + break; + + case WZ_STORMGUST: /* スト?ムガスト */ + tsc->data[SC_FREEZE].val3++; + if(tsc->data[SC_FREEZE].val3 >= 3) + status_change_start(bl,SC_FREEZE,10000, + skilllv,0,0,0,skill_get_time2(skillid,skilllv),8); + break; + + case WZ_METEOR: + sc_start(bl,SC_STUN,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case WZ_VERMILION: + sc_start(bl,SC_BLIND,4*skilllv,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case HT_FREEZINGTRAP: /* フリ?ジングトラップ */ + sc_start(bl,SC_FREEZE,(3*skilllv+35),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case HT_FLASHER: /* Flasher */ + sc_start(bl,SC_BLIND,(10*skilllv+30),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case HT_LANDMINE: /* ランドマイン */ + sc_start(bl,SC_STUN,(5*skilllv+30),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case HT_SHOCKWAVE: + status_percent_damage(src, bl, 0, 15*skilllv+5); + break; + + case HT_SANDMAN: /* サンドマン */ + sc_start(bl,SC_SLEEP,(10*skilllv+40),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case TF_SPRINKLESAND: /* ?サまき */ + sc_start(bl,SC_BLIND,20,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case TF_THROWSTONE: /* ?ホ投げ */ + sc_start(bl,SC_STUN,3,skilllv,skill_get_time(skillid,skilllv)); + sc_start(bl,SC_BLIND,3,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case NPC_DARKCROSS: + case CR_HOLYCROSS: /* ホ?リ?クロス */ + sc_start(bl,SC_BLIND,3*skilllv,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case CR_GRANDCROSS: /* グランドク?ス */ + case NPC_GRANDDARKNESS: /*闇グランドク?ス*/ + { + if(battle_check_undead(tstatus->race,tstatus->def_ele) || tstatus->race == RC_DEMON) + sc_start(bl,SC_BLIND,100,skilllv,skill_get_time2(skillid,skilllv)); + } + break; + + case AM_ACIDTERROR: + sc_start(bl,SC_BLEEDING,(skilllv*3),skilllv,skill_get_time2(skillid,skilllv)); + if (skill_break_equip(bl, EQP_ARMOR, 100*skill_get_time(skillid,skilllv), BCT_ENEMY)) + clif_emotion(bl,23); + break; + + case AM_DEMONSTRATION: + skill_break_equip(bl, EQP_WEAPON, 100*skilllv, BCT_ENEMY); + break; + + case CR_SHIELDCHARGE: /* シ?ルドチャ?ジ */ + sc_start(bl,SC_STUN,(15+skilllv*5),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case PA_PRESSURE: + status_percent_damage(src, bl, 0, 15+5*skilllv); + break; + + case RG_RAID: /* サプライズアタック */ + sc_start(bl,SC_STUN,(10+3*skilllv),skilllv,skill_get_time(skillid,skilllv)); + sc_start(bl,SC_BLIND,(10+3*skilllv),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case BA_FROSTJOKE: + sc_start(bl,SC_FREEZE,(15+5*skilllv),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case DC_SCREAM: + sc_start(bl,SC_STUN,(25+5*skilllv),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case BD_LULLABY: /* 子守唄 */ + sc_start(bl,SC_SLEEP,15,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case DC_UGLYDANCE: + rate = 5+5*skilllv; + if(sd && (skill=pc_checkskill(sd,DC_DANCINGLESSON))) + rate += 5+skill; + status_zap(bl, 0, rate); + break; + case SL_STUN: + if (tstatus->size==1) //Only stuns mid-sized mobs. + sc_start(bl,SC_STUN,(30+10*skilllv),skilllv,skill_get_time(skillid,skilllv)); + break; + + /* MOBの追加?果付きスキル */ + case NPC_PETRIFYATTACK: + case NPC_CURSEATTACK: + case NPC_SLEEPATTACK: + case NPC_BLINDATTACK: + case NPC_POISON: + case NPC_SILENCEATTACK: + case NPC_STUNATTACK: + sc_start(bl,SkillStatusChangeTable[skillid],50+10*skilllv,skilllv,src->type==BL_PET?skilllv*1000:skill_get_time2(skillid,skilllv)); + break; + + case NPC_MENTALBREAKER: + status_percent_damage(src, bl, 0, -(10+skilllv)); + break; + // Equipment breaking monster skills [Celest] + case NPC_BREAKWEAPON: + skill_break_equip(bl, EQP_WEAPON, 150*skilllv, BCT_ENEMY); + break; + case NPC_BREAKARMOR: + skill_break_equip(bl, EQP_ARMOR, 150*skilllv, BCT_ENEMY); + break; + case NPC_BREAKHELM: + skill_break_equip(bl, EQP_HELM, 150*skilllv, BCT_ENEMY); + break; + case NPC_BREAKSHIELD: + skill_break_equip(bl, EQP_SHIELD, 150*skilllv, BCT_ENEMY); + break; + + case CH_TIGERFIST: + sc_start(bl,SC_STOP,(10+skilllv*10),0,skill_get_time2(skillid,skilllv)); + break; + + case LK_SPIRALPIERCE: + sc_start(bl,SC_STOP,(15+skilllv*5),0,skill_get_time2(skillid,skilllv)); + break; + + case ST_REJECTSWORD: /* フリ?ジングトラップ */ + sc_start(bl,SC_AUTOCOUNTER,(skilllv*15),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case PF_FOGWALL: /* ホ?リ?ク?ス */ + if (src != bl && tsc->data[SC_DELUGE].timer == -1) + status_change_start(bl,SC_BLIND,10000,skilllv,0,0,0,skill_get_time2(skillid,skilllv),8); + break; + + case LK_HEADCRUSH: + if (!(battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON)) + sc_start(bl, SC_BLEEDING,50, skilllv, skill_get_time2(skillid,skilllv)); + break; + + case LK_JOINTBEAT: /* ジョイントビ?ト */ + //??が良く分からないので適?に + sc_start(bl,SkillStatusChangeTable[skillid],(5*skilllv+5),skilllv,skill_get_time2(skillid,skilllv)); + break; + + case ASC_METEORASSAULT: /* ?テオアサルト */ + //Any enemies hit by this skill will receive Stun, Darkness, or external bleeding status ailment with a 5%+5*SkillLV% chance. + switch(rand()%3) { + case 0: + sc_start(bl,SC_BLIND,(5+skilllv*5),skilllv,skill_get_time2(skillid,1)); + break; + case 1: + sc_start(bl,SC_STUN,(5+skilllv*5),skilllv,skill_get_time2(skillid,2)); + break; + default: + sc_start(bl,SC_BLEEDING,(5+skilllv*5),skilllv,skill_get_time2(skillid,3)); + } + break; + + case HW_NAPALMVULCAN: /* ナパ?ムバルカン */ + // skilllv*5%の確率で呪い + sc_start(bl,SC_CURSE,5*skilllv,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case WS_CARTTERMINATION: // Cart termination + sc_start(bl,SC_STUN,5*skilllv,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case CR_ACIDDEMONSTRATION: + skill_break_equip(bl, EQP_WEAPON|EQP_ARMOR, 100*skilllv, BCT_ENEMY); + break; + + case TK_DOWNKICK: + sc_start(bl,SC_STUN,100,skilllv,skill_get_time2(skillid,skilllv)); + break; + + case TK_JUMPKICK: + //Cancel out Soul Linker status of the target. [Skotlex] + if (tsc->count) { + if (tsc->data[SC_PRESERVE].timer != -1) //preserve blocks the cleaning + break; + //Remove pitched potions effect. + if (tsc->data[SC_ASPDPOTION0].timer != -1 && tsc->data[SC_ASPDPOTION0].val4) + status_change_end(bl, SC_ASPDPOTION0, -1); + if (tsc->data[SC_ASPDPOTION1].timer != -1 && tsc->data[SC_ASPDPOTION1].val4) + status_change_end(bl, SC_ASPDPOTION1, -1); + if (tsc->data[SC_ASPDPOTION2].timer != -1 && tsc->data[SC_ASPDPOTION2].val4) + status_change_end(bl, SC_ASPDPOTION2, -1); + if (tsc->data[SC_ASPDPOTION3].timer != -1 && tsc->data[SC_ASPDPOTION3].val4) + status_change_end(bl, SC_ASPDPOTION3, -1); + if (tsc->data[SC_SPIRIT].timer != -1) + status_change_end(bl, SC_SPIRIT, -1); + if (tsc->data[SC_ONEHAND].timer != -1) + status_change_end(bl, SC_ONEHAND, -1); + if (tsc->data[SC_ADRENALINE2].timer != -1) + status_change_end(bl, SC_ADRENALINE2, -1); + } + break; + case TK_TURNKICK: + case MO_BALKYOUNG: //Note: attack_type is passed as BF_WEAPON for the actual target, BF_MISC for the splash-affected mobs. + if(attack_type == BF_MISC) //70% base stun chance... + sc_start(bl,SC_STUN,70,skilllv,skill_get_time2(skillid,skilllv)); + break; + //Until they're at right position - gs_statuschange- [Vicious] + case GS_BULLSEYE: //0.1% coma rate. + status_change_start(bl,SC_COMA,10,skilllv,0,0,0,0,0); + break; + case GS_CRACKER: + if (!dstsd) // according to latest patch, should not work on players [Reddozen] + sc_start(bl,SC_STUN,(100 - 10*distance_bl(src, bl)),skilllv,skill_get_time2(skillid,skilllv)); //Temp stun rate + break; + case GS_PIERCINGSHOT: + sc_start(bl,SC_BLEEDING,(skilllv*3),skilllv,skill_get_time2(skillid,skilllv)); + break; + case GS_FULLBUSTER: + sc_start(bl,SC_BLIND,(2*skilllv),skilllv,skill_get_time2(skillid,1)); + break; + case NJ_HYOUSYOURAKU: + sc_start(bl,SC_FREEZE,(10+10*skilllv),skilllv,skill_get_time2(skillid,skilllv)); + break; + + //case GS_FLING: // this needs to be looked at [Reddozen] + // if (skill == GS_FLING) { // gunslinger [marquis007] + // int spiritball = (sd->spiritball > 5 ? 5 : sd->spiritball); + // } else { + // int spiritball = 1; + // } + // + // if (spiritball <= sd->spiritball && sd->spiritball != 0){ + // pc_delspiritball(sd,spiritball,0); + // status_change_start(bl,SC_FLING,10000,spiritball*5,0,0,0,skill_get_time(skillid,skilllv))); + // } + // break; + } + + if (md && battle_config.summons_inherit_effects && md->master_id && md->special_state.ai) + { //Pass heritage to Master for status causing effects. [Skotlex] + sd = map_id2sd(md->master_id); + } + + if(sd && skillid != MC_CARTREVOLUTION && skillid != AM_DEMONSTRATION && skillid != CR_REFLECTSHIELD && attack_type&BF_WEAPON){ /* カ?ドによる追加?果 */ + int i, type; + for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){ + type=i-SC_COMMON_MIN; + rate = sd->addeff[type]+(sd->state.arrow_atk?sd->arrow_addeff[type]:0); + if (!rate) + continue; //Code Speedup. + status_change_start(bl,i,rate,7,0,0,0,skill_get_time2(StatusSkillChangeTable[type],7),0); + } + } + + //Reports say that autospell effects get triggered on skills and pretty much everything including splash attacks. [Skotlex] + //No need to check the NK value as this function is only called on attacks + //(or stuff that should invoke these things. + if(sd && !status_isdead(bl) && src != bl/* && + !(skillid && skill_get_nk(skillid)&NK_NO_DAMAGE)*/) { + struct block_list *tbl; + struct unit_data *ud; + int i, skilllv; + for (i = 0; i < MAX_PC_BONUS && sd->autospell[i].id; i++) { + + skill = (sd->autospell[i].id > 0) ? sd->autospell[i].id : -sd->autospell[i].id; + + if (skillnotok(skill, sd)) + continue; + + skilllv = (sd->autospell[i].lv > 0) ? sd->autospell[i].lv : 1; + rate = (!sd->state.arrow_atk) ? sd->autospell[i].rate : sd->autospell[i].rate / 2; + + if (rand()%1000 > rate) + continue; + if (sd->autospell[i].id < 0) + tbl = src; + else + tbl = bl; + + if (tbl != src && !battle_check_range(src, tbl, skill_get_range2(src, skill, skilllv))) + continue; //Autoskills DO check for target-src range. [Skotlex] + rate = skill_get_inf(skill); + switch (skill_get_casttype(skill)) { + case CAST_GROUND: + skill_castend_pos2(src, tbl->x, tbl->y, skill, skilllv, tick, 0); + break; + case CAST_NODAMAGE: + skill_castend_nodamage_id(src, tbl, skill, skilllv, tick, 0); + break; + case CAST_DAMAGE: + skill_castend_damage_id(src, tbl, skill, skilllv, tick, 0); + break; + } + //Set canact delay. [Skotlex] + ud = unit_bl2ud(src); + if (ud) { + rate = skill_delayfix(src, skill, skilllv)/2; + if (DIFF_TICK(ud->canact_tick, tick + rate) < 0) + ud->canact_tick = tick+rate; + } + break; //Only one auto skill comes off at a time. + } + } + return 0; +} + +/* Splitted off from skill_additional_effect, which is never called when the + * attack skill kills the enemy. Place in this function counter status effects + * when using skills (eg: Asura's sp regen penalty, or counter-status effects + * from cards) that will take effect on the source, not the target. [Skotlex] + * Note: Currently this function only applies to Extremity Fist and BF_WEAPON + * type of skills, so not every instance of skill_additional_effect needs a call + * to this one. + */ +int skill_counter_additional_effect (struct block_list* src, struct block_list *bl, int skillid, int skilllv, int attack_type, unsigned int tick) +{ + int rate; + struct map_session_data *sd=NULL; + struct map_session_data *dstsd=NULL; + struct status_change *tsc; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + + if(skillid < 0) + { // remove the debug print when this case is finished + ShowDebug("skill_counter_additional_effect: skillid=%i\ncall: %p %p %i %i %i %i",skillid, + src, bl,skillid,skilllv,attack_type,tick); + return 0; + } + if(skillid > 0 && skilllv <= 0) return 0; // don't forget auto attacks! - celest + + tsc = status_get_sc(bl); + if (tsc && !tsc->count) + tsc = NULL; + + BL_CAST(BL_PC, src, sd); + BL_CAST(BL_PC, bl, dstsd); + + switch(skillid){ + case 0: //Normal Attack + if(tsc && tsc->data[SC_KAAHI].timer != -1 && tsc->data[SC_KAAHI].val4 == -1) + tsc->data[SC_KAAHI].val4 = add_timer( + tick+skill_get_time2(SL_KAAHI,tsc->data[SC_KAAHI].val1), + kaahi_heal_timer, bl->id, SC_KAAHI); //Activate heal. + break; + case MO_EXTREMITYFIST: /* 阿?C羅覇凰? */ + //阿?C羅を使うと5分間自然回復しないようになる + sc_start(src,SkillStatusChangeTable[skillid],100,skilllv,skill_get_time2(skillid,skilllv)); + break; + } + + if((sd||dstsd) && skillid != MC_CARTREVOLUTION && attack_type&BF_WEAPON){ /* カ?ドによる追加?果 */ + int i, type; + + for(i=SC_COMMON_MIN;i<=SC_COMMON_MAX;i++){ + type=i-SC_COMMON_MIN; + + + rate = sd?(sd->addeff2[type]+(sd->state.arrow_atk?sd->arrow_addeff2[type]:0)):0; + if (rate) //Self infliced status from attacking. + status_change_start(src,i,rate,7,0,0,0,skill_get_time2(StatusSkillChangeTable[type],7),0); + + rate = dstsd?dstsd->addeff3[type]:0; + if (rate && (dstsd->addeff3_type[type] == 1 || ((sd && sd->state.arrow_atk) || (status_get_range(src)>2)))) + status_change_start(src,i,rate,7,0,0,0,skill_get_time2(StatusSkillChangeTable[type],7),0); + } + } + + if(sd && bl->type == BL_MOB && status_isdead(bl) && + skillid && skill_get_type(skillid)==BF_MAGIC && + skill_get_inf(skillid)!=INF_GROUND_SKILL && + (rate=pc_checkskill(sd,HW_SOULDRAIN))>0) + { //Soul Drain should only work on targetted spells [Skotlex] + int sp; + if (pc_issit(sd)) pc_setstand(sd); //Character stuck in attacking animation while 'sitting' fix. [Skotlex] + clif_skill_nodamage(src,bl,HW_SOULDRAIN,rate,1); + sp = (status_get_lv(bl))*(95+15*rate)/100; + if(sp > sd->status.max_sp - sd->status.sp) + sp = sd->status.max_sp - sd->status.sp; + if (sp) { + sd->status.sp += sp; + clif_heal(sd->fd,SP_SP,sp); + } + } + + //Trigger counter-spells to retaliate against damage causing skills. [Skotlex] + if(dstsd && !status_isdead(bl) && src != bl && !(skillid && skill_get_nk(skillid)&NK_NO_DAMAGE)) + { + struct block_list *tbl; + struct unit_data *ud; + int i, skillid, skilllv, rate; + + for (i = 0; i < MAX_PC_BONUS; i++) { + if (dstsd->autospell2[i].id == 0) + break; + + skillid = (dstsd->autospell2[i].id > 0) ? dstsd->autospell2[i].id : -dstsd->autospell2[i].id; + skilllv = (dstsd->autospell2[i].lv > 0) ? dstsd->autospell2[i].lv : 1; + rate = ((sd && !sd->state.arrow_atk) || (status_get_range(src)<=2)) ? + dstsd->autospell2[i].rate : dstsd->autospell2[i].rate / 2; + + if (skillnotok(skillid, dstsd)) + continue; + if (rand()%1000 > rate) + continue; + if (dstsd->autospell2[i].id < 0) + tbl = bl; + else + tbl = src; + + if (tbl != bl && !battle_check_range(bl, tbl, skill_get_range2(bl, skillid, skilllv))) + continue; //Autoskills DO check for target-src range. [Skotlex] + + switch (skill_get_casttype(skillid)) { + case CAST_GROUND: + skill_castend_pos2(bl, tbl->x, tbl->y, skillid, skilllv, tick, 0); + break; + case CAST_NODAMAGE: + skill_castend_nodamage_id(bl, tbl, skillid, skilllv, tick, 0); + break; + case CAST_DAMAGE: + skill_castend_damage_id(bl, tbl, skillid, skilllv, tick, 0); + break; + } + //Set canact delay. [Skotlex] + ud = unit_bl2ud(bl); + if (ud) { + rate = skill_delayfix(bl, skillid, skilllv)/2; + if (DIFF_TICK(ud->canact_tick, tick + rate) < 0) + ud->canact_tick = tick+rate; + } + break; //trigger only one auto-spell per hit. + } + } + return 0; +} +/*========================================================================= + Breaks equipment. On-non players causes the corresponding strip effect. + - rate goes from 0 to 10000 (100.00%) + - flag is a BCT_ flag to indicate which type of adjustment should be used + (BCT_ENEMY/BCT_PARTY/BCT_SELF) are the valid values. +--------------------------------------------------------------------------*/ +int skill_break_equip(struct block_list *bl, unsigned short where, int rate, int flag) { + static int where_list[4] = {EQP_WEAPON, EQP_ARMOR, EQP_SHIELD, EQP_HELM}; + static int scatk[4] = {SC_STRIPWEAPON, SC_STRIPARMOR, SC_STRIPSHIELD, SC_STRIPHELM }; + static int scdef[4] = {SC_CP_WEAPON, SC_CP_ARMOR, SC_CP_SHIELD, SC_CP_HELM}; + struct status_change *sc = status_get_sc(bl); + int i,j; + TBL_PC *sd; + BL_CAST(BL_PC, bl, sd); + if (sc && !sc->count) + sc = NULL; + + if (sd) { + if (sd->unbreakable_equip) + where &= ~sd->unbreakable_equip; + if (sd->unbreakable) + rate -= rate*sd->unbreakable/100; + if (where&EQP_WEAPON) { + switch (sd->status.weapon) { + case W_FIST: //Bare fists should not break :P + case W_1HAXE: + case W_2HAXE: + case W_MACE: // Axes and Maces can't be broken [DracoRPG] + case W_STAFF: + case W_BOOK: //Rods and Books can't be broken [Skotlex] + where &= ~EQP_WEAPON; + } + } + } + if (flag&BCT_ENEMY) { + if (battle_config.equip_skill_break_rate != 100) + rate = rate*battle_config.equip_skill_break_rate/100; + } else if (flag&(BCT_PARTY|BCT_SELF)) { + if (battle_config.equip_self_break_rate != 100) + rate = rate*battle_config.equip_self_break_rate/100; + } + + for (i = 0; i < 4; i++) { + if (where&where_list[i]) { + if (sc && sc->count && sc->data[scdef[i]].timer != -1) + where&=~where_list[i]; + else if (rand()%10000 >= rate) + where&=~where_list[i]; + else if (!sd) //Cause Strip effect. + sc_start(bl,scatk[i],100,0,skill_get_time(StatusSkillChangeTable[scatk[i]],1)); + } + } + if (!where) //Nothing to break. + return 0; + if (sd) { + for (i = 0; i < 11; i++) { + j = sd->equip_index[i]; + if (j <= 0 || sd->status.inventory[j].attribute == 1 || !sd->inventory_data[j]) + continue; + flag = 0; + switch(i) { + case 6: //Upper Head + flag = (where&EQP_HELM); + break; + case 7: //Body + flag = (where&EQP_ARMOR); + break; + case 8: //Left/Right hands + case 9: + flag = ( + (where&EQP_WEAPON && sd->inventory_data[j]->type == 4) || + (where&EQP_SHIELD && sd->inventory_data[j]->type == 5)); + break; + default: + continue; + } + if (flag) { + sd->status.inventory[j].attribute = 1; + pc_unequipitem(sd, j, 3); + } + } + clif_equiplist(sd); + } + + return where; //Return list of pieces broken. +} +/*========================================================================= + Used to knock back players, monsters, traps, etc + If count&0xf00000, the direction is send in the 6th byte. + If count&0x10000, the direction is to the back of the target, otherwise is away from the src. + If count&0x20000, position update packets must not be sent. + IF count&0X40000, direction is random. +--------------------------------------------------------------------------*/ +int skill_blown( struct block_list *src, struct block_list *target,int count) +{ + int dx=0,dy=0,nx,ny; + int x=target->x,y=target->y; + int dir,ret; + struct skill_unit *su=NULL; + + nullpo_retr(0, src); + + if (src != target && map_flag_gvg(target->m)) + return 0; //No knocking back in WoE + if (!count&0xffff) + return 0; //Actual knockback distance is 0. + + switch (target->type) { + case BL_MOB: + if (((TBL_MOB*)target)->class_ == MOBID_EMPERIUM) + return 0; + break; + case BL_SKILL: + su=(struct skill_unit *)target; + break; + } + + if (count&0xf00000) + dir = (count>>20)&0xf; + else if (count&0x10000 || (target->x==src->x && target->y==src->y)) + dir = unit_getdir(target); + else if (count&0x40000) //Flag for random pushing. + dir = rand()%8; + else + dir = map_calc_dir(target,src->x,src->y); + if (dir>=0 && dir<8){ + dx = -dirx[dir]; + dy = -diry[dir]; + } + + ret=path_blownpos(target->m,x,y,dx,dy,count&0xffff); + nx=ret>>16; + ny=ret&0xffff; + + if (!su) + unit_stop_walking(target,0); + + dx = nx - x; + dy = ny - y; + + if (!dx && !dy) //Could not knockback. + return 0; + + map_foreachinmovearea(clif_outsight,target->m, + x-AREA_SIZE,y-AREA_SIZE,x+AREA_SIZE,y+AREA_SIZE, + dx,dy,target->type==BL_PC?BL_ALL:BL_PC,target); + + if(su) + skill_unit_move_unit_group(su->group,target->m,dx,dy); + else + map_moveblock(target, nx, ny, gettick()); + + map_foreachinmovearea(clif_insight,target->m, + nx-AREA_SIZE,ny-AREA_SIZE,nx+AREA_SIZE,ny+AREA_SIZE, + -dx,-dy,target->type==BL_PC?BL_ALL:BL_PC,target); + + if(!(count&0x20000)) + clif_blown(target); + + return 0; +} + +/* + * ========================================================================= + * スキル?U??果??まとめ + * flagの?明?B16?i? + * 00XRTTff + * ff = magicで計算に渡される?j + * TT = パケットのtype部分(0でデフォルト?j + * X = パケットのスキルLv + * R = 予約?iskill_area_subで使用する?j + *------------------------------------------------------------------------- + */ + +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 ) +{ + struct Damage dmg; + struct status_data *sstatus, *tstatus; + struct status_change *sc; + struct map_session_data *sd=NULL, *tsd=NULL; + int type,lv,damage,rdamage=0; + + if(skillid > 0 && skilllv <= 0) return 0; + + nullpo_retr(0, src); //Source is the master behind the attack (player/mob/pet) + nullpo_retr(0, dsrc); //dsrc is the actual originator of the damage, can be the same as src, or a skill casted by src. + nullpo_retr(0, bl); //Target to be attacked. + + if (src != dsrc) { + //When caster is not the src of attack, this is a ground skill, and as such, do the relevant target checking. [Skotlex] + if (!status_check_skilluse(battle_config.skill_caster_check?src:NULL, bl, skillid, 2)) + return 0; + } else if (flag && skill_get_nk(skillid)&NK_SPLASH) { + //Note that splash attacks often only check versus the targetted mob, those around the splash area normally don't get checked for being hidden/cloaked/etc. [Skotlex] + if (!status_check_skilluse(dsrc, bl, skillid, 2)) + return 0; + } + + if (dsrc->type == BL_PC) + sd = (struct map_session_data *)dsrc; + if (bl->type == BL_PC) + tsd = (struct map_session_data *)bl; + + sstatus = status_get_status_data(dsrc); + tstatus = status_get_status_data(bl); +// Is this check really needed? FrostNova won't hurt you if you step right where the caster is? + if(skillid == WZ_FROSTNOVA && dsrc->x == bl->x && dsrc->y == bl->y) //使用スキルがフ?ストノヴァで?Adsrcとblが同じ??鰍ネら何もしない + return 0; + + type=-1; + lv=(flag>>20)&0xf; + dmg=battle_calc_attack(attack_type,src,bl,skillid,skilllv,flag&0xff ); + + //Skotlex: Adjusted to the new system + if(src->type==BL_PET && (struct pet_data *)src) + { // [Valaris] + struct pet_data *pd = (struct pet_data *)src; + if (pd->a_skill && pd->a_skill->div_ && pd->a_skill->id == skillid) + { + int element = skill_get_pl(skillid); + if (skillid == -1) + element = sstatus->rhw.ele; + dmg.damage=battle_attr_fix(src, bl, skilllv, element, tstatus->def_ele, tstatus->ele_lv); + dmg.damage2=0; + dmg.div_= pd->a_skill->div_; + } + } + + sc= status_get_sc(bl); + if (sc && !sc->count) sc = NULL; //Don't need it. + + if (attack_type&BF_MAGIC) { + if(sc && sc->data[SC_KAITE].timer != -1 && (dmg.damage || dmg.damage2) + && !(sstatus->mode&MD_BOSS) && (sd || status_get_lv(dsrc) <= 80) + ) { //Works on players or mobs with level under 80. + clif_skill_nodamage(bl,bl,SL_KAITE,sc->data[SC_KAITE].val1,1); + if (--sc->data[SC_KAITE].val2 <= 0) + status_change_end(bl, SC_KAITE, -1); + bl = src; //Just make the skill attack yourself @.@ + sc = status_get_sc(bl); + tsd = (bl->type == BL_PC)?(TBL_PC*)bl:NULL; + if (sc && !sc->count) + sc = NULL; //Don't need it. + if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_WIZARD) + { //Spirit of Wizard blocks bounced back spells. + dmg.damage = dmg.damage2 = 0; + dmg.dmg_lv = ATK_FLEE; + } + } + + if(sc && sc->data[SC_MAGICROD].timer != -1 && src == dsrc) { + dmg.damage = dmg.damage2 = 0; + dmg.dmg_lv = ATK_FLEE; //This will prevent skill additional effect from taking effect. [Skotlex] + if(tsd) { + int sp = skill_get_sp(skillid,skilllv); + sp = sp * sc->data[SC_MAGICROD].val2 / 100; + if(skillid == WZ_WATERBALL && skilllv > 1) + sp = sp/((skilllv|1)*(skilllv|1)); //Estimate SP cost of a single water-ball + if(sp > SHRT_MAX) sp = SHRT_MAX; + else if(sp < 1) sp = 1; + if(sp > tsd->status.max_sp - tsd->status.sp) + sp = tsd->status.max_sp - tsd->status.sp; + tsd->status.sp += sp; + clif_heal(tsd->fd,SP_SP,sp); + tsd->ud.canact_tick = tick + skill_delayfix(bl, SA_MAGICROD, sc->data[SC_MAGICROD].val1); + } + clif_skill_nodamage(bl,bl,SA_MAGICROD,sc->data[SC_MAGICROD].val1,1); + } + } + + damage = dmg.damage + dmg.damage2; + + if (damage > 0 && src != bl && src == dsrc) + rdamage = battle_calc_return_damage(bl, &damage, dmg.flag); + + if(lv==15) + lv=-1; + + if( flag&0xff00 ) + type=(flag&0xff00)>>8; + + if((damage <= 0 || damage < dmg.div_) + && skillid != CH_PALMSTRIKE) //Palm Strike is the only skill that will knockback even if it misses. [Skotlex] + dmg.blewcount = 0; + + if(skillid == CR_GRANDCROSS||skillid == NPC_GRANDDARKNESS) {//グランドクロス + if(battle_config.gx_disptype) dsrc = src; // 敵ダメ?ジ白文字表示 + if(src == bl) type = 4; // 反動はダメ?ジモ?ションなし + } + +//使用者がPCの??の??ここから + if(sd) { + //Sorry for removing the Japanese comments, but they were actually distracting + //from the actual code and I couldn't understand a thing anyway >.< [Skotlex] + if (sd->sc.data[SC_COMBO].timer!=-1) + { //End combo state after skill is invoked. [Skotlex] + switch (skillid) { + case TK_TURNKICK: + case TK_STORMKICK: + case TK_DOWNKICK: + case TK_COUNTER: + //set this skill as previous one. + sd->skillid_old = skillid; + sd->skilllv_old = skilllv; + if (pc_famerank(sd->char_id,MAPID_TAEKWON)) + break; //Do not end combo state. + default: + status_change_end(src,SC_COMBO,-1); + } + } + switch(skillid) + { + case MO_TRIPLEATTACK: + { + int delay = 1000 - 4*sstatus->agi - 2*sstatus->dex; + if (damage < tstatus->hp && + pc_checkskill(sd, MO_CHAINCOMBO) > 0) + delay += 300 * battle_config.combo_delay_rate / 100; + sc_start4(src,SC_COMBO,100,MO_TRIPLEATTACK,skilllv,0,0,delay); + clif_combo_delay(src, delay); + + if (sd->status.party_id>0) //bonus from SG_FRIEND [Komurka] + party_skill_check(sd, sd->status.party_id, MO_TRIPLEATTACK, skilllv); + break; + } + case MO_CHAINCOMBO: + { + int delay = 1000 - 4*sstatus->agi - 2*sstatus->dex; + if(damage < tstatus->hp && + (pc_checkskill(sd, MO_COMBOFINISH) > 0 && sd->spiritball > 0)) + delay += 300 * battle_config.combo_delay_rate /100; + sc_start4(src,SC_COMBO,100,MO_CHAINCOMBO,skilllv,0,0,delay); + clif_combo_delay(src,delay); + break; + } + case MO_COMBOFINISH: + { + int delay = 700 - 4*sstatus->agi - 2*sstatus->dex; + if(damage < tstatus->hp && + ( + (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 4 && sd->sc.data[SC_EXPLOSIONSPIRITS].timer != -1) || + (pc_checkskill(sd, CH_TIGERFIST) > 0 && sd->spiritball > 0) || + (pc_checkskill(sd, CH_CHAINCRUSH) > 0 && sd->spiritball > 1) + )) + delay += 300 * battle_config.combo_delay_rate /100; + sc_start4(src,SC_COMBO,100,MO_COMBOFINISH,skilllv,0,0,delay); + clif_combo_delay(src,delay); + break; + } + case CH_TIGERFIST: + { //Tigerfist is now a combo-only skill. [Skotlex] + int delay = 1000 - 4*sstatus->agi - 2*sstatus->dex; + if(damage < tstatus->hp && + ( + (pc_checkskill(sd, MO_EXTREMITYFIST) > 0 && sd->spiritball >= 3 && sd->sc.data[SC_EXPLOSIONSPIRITS].timer != -1) || + (pc_checkskill(sd, CH_CHAINCRUSH) > 0) + )) + delay += 300 * battle_config.combo_delay_rate /100; + sc_start4(src,SC_COMBO,100,CH_TIGERFIST,skilllv,0,0,delay); + clif_combo_delay(src,delay); + break; + } + case CH_CHAINCRUSH: + { + int delay = 1000 - 4*sstatus->agi - 2*sstatus->dex; + if(damage < tstatus->hp) + delay += 300 * battle_config.combo_delay_rate /100; + sc_start4(src,SC_COMBO,100,CH_CHAINCRUSH,skilllv,0,0,delay); + clif_combo_delay(src,delay); + break; + } + case AC_DOUBLE: + if((tstatus->race == RC_BRUTE || tstatus->race == RC_INSECT) && damage < tstatus->hp && pc_checkskill(sd, HT_POWER)) { + //TODO: This code was taken from Triple Blows,is this even how it should be? [Skotlex] + sc_start4(src,SC_COMBO,100,HT_POWER,bl->id,0,0,2000); + clif_combo_delay(src,2000); + } + break; + case TK_COUNTER: + { //bonus from SG_FRIEND [Komurka] + int level; + if(sd->status.party_id>0 && (level = pc_checkskill(sd,SG_FRIEND))) + party_skill_check(sd, sd->status.party_id, TK_COUNTER,level); + } + break; + case SL_STIN: + case SL_STUN: + if (skilllv >= 7 && sd->sc.data[SC_SMA].timer == -1) + sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA, skilllv)); + break; + case GS_FULLBUSTER: + //Can't attack nor use items until skill's delay expires. [Skotlex] + sd->ud.attackabletime = sd->canuseitem_tick = sd->ud.canact_tick; + break; + } //Switch End + } + + //Display damage. + switch(skillid){ + //Skills who's damage should't show any skill-animation. + case SM_MAGNUM: + case AS_SPLASHER: + case ASC_METEORASSAULT: + case GS_SPREADATTACK: + dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); + break; + case KN_BRANDISHSPEAR: + { //Only display skill animation for skill's target. + struct unit_data *ud = unit_bl2ud(src); + if (ud && ud->skilltarget == bl->id) + dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, type); + else + dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); + break; + } + case PA_GOSPEL: //Should look like Holy Cross [Skotlex] + dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, CR_HOLYCROSS, -1, 5); + break; + + case NPC_SELFDESTRUCTION: + if(src->type==BL_PC) + dmg.blewcount = 10; + break; + case KN_AUTOCOUNTER: //Skills that need be passed as a normal attack for the client to display correctly. + case TF_DOUBLE: + case GS_CHAINACTION: + case SN_SHARPSHOOTING: + dmg.dmotion = clif_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,dmg.type,dmg.damage2); + break; + case CR_GRANDCROSS: + case NPC_GRANDDARKNESS: + //Only show animation when hitting yourself. [Skotlex] + if (src!=bl) { + dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); + break; + } + default: + dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, (lv!=0)?lv:skilllv, (skillid==0)? 5:type ); + } + + map_freeblock_lock(); + + if(damage > 0 && dmg.flag&BF_SKILL && tsd + && pc_checkskill(tsd,RG_PLAGIARISM) + && (!sc || sc->data[SC_PRESERVE].timer == -1) + && damage < tsd->status.hp) + { //Updated to not be able to copy skills if the blow will kill you. [Skotlex] + if ((!tsd->status.skill[skillid].id || tsd->status.skill[skillid].flag >= 13) && + can_copy(tsd,skillid)) // Split all the check into their own function [Aru] + { + //?に?んでいるスキルが れば該?スキルを消す + if (tsd->cloneskill_id && tsd->status.skill[tsd->cloneskill_id].flag == 13){ + tsd->status.skill[tsd->cloneskill_id].id = 0; + tsd->status.skill[tsd->cloneskill_id].lv = 0; + tsd->status.skill[tsd->cloneskill_id].flag = 0; + } + tsd->cloneskill_id = skillid; + tsd->status.skill[skillid].id = skillid; + tsd->status.skill[skillid].lv = skilllv; + if ((lv = pc_checkskill(tsd,RG_PLAGIARISM)) < skilllv) + tsd->status.skill[skillid].lv = lv; + tsd->status.skill[skillid].flag = 13;//cloneskill flag + pc_setglobalreg(tsd, "CLONE_SKILL", tsd->cloneskill_id); + pc_setglobalreg(tsd, "CLONE_SKILL_LV", tsd->status.skill[skillid].lv); + clif_skillinfoblock(tsd); + } + } + if (skillid != WZ_HEAVENDRIVE && bl->type == BL_SKILL && damage > 0) { + struct skill_unit* su = (struct skill_unit*)bl; + if (su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP) + damage = 0; //Only Heaven's drive may damage traps. [Skotlex] + } + if (!dmg.amotion) { + status_fix_damage(src,bl,damage,dmg.dmotion); //Deal damage before knockback to allow stuff like firewall+storm gust combo. + if (dmg.dmg_lv == ATK_DEF || damage > 0) { + if (!status_isdead(bl)) + skill_additional_effect(src,bl,skillid,skilllv,attack_type,tick); + //Counter status effects [Skotlex] + skill_counter_additional_effect(dsrc,bl,skillid,skilllv,attack_type,tick); + } + } + + //Only knockback if it's still alive, otherwise a "ghost" is left behind. [Skotlex] + if (dmg.blewcount > 0 && !status_isdead(bl)) + skill_blown(dsrc,bl,dmg.blewcount); + + //Delayed damage must be dealt after the knockback (it needs to know actual position of target) + if (dmg.amotion) + battle_delay_damage(tick+dmg.amotion,src,bl,attack_type,skillid,skilllv,damage,dmg.dmg_lv,dmg.dmotion); + + if(skillid == RG_INTIMIDATE && damage > 0 && !(tstatus->mode&MD_BOSS)) { + int rate = 50 + skilllv * 5; + rate = rate + (status_get_lv(src) - status_get_lv(bl)); + if(rand()%100 < rate) + skill_addtimerskill(src,tick + 800,bl->id,0,0,skillid,skilllv,0,flag); + } + + if(sd && dmg.flag&BF_WEAPON && src != bl && src == dsrc && damage > 0) { + if (battle_config.left_cardfix_to_right) + battle_drain(sd, tsd, dmg.damage, dmg.damage, tstatus->race, tstatus->mode&MD_BOSS); + else + battle_drain(sd, tsd, dmg.damage, dmg.damage2, tstatus->race, tstatus->mode&MD_BOSS); + } + + if (rdamage>0) { + if (dmg.amotion) + battle_delay_damage(tick+dmg.amotion,bl,src,0,0,0,rdamage,ATK_DEF,0); + else + status_fix_damage(bl,src,rdamage,0); + clif_damage(src,src,tick, dmg.amotion,0,rdamage,1,4,0); + //Use Reflect Shield to signal this kind of skill trigger. [Skotlex] + skill_additional_effect(bl,src,CR_REFLECTSHIELD, 1,BF_WEAPON,tick); + } + + if (!(flag & 1) && + ( + skillid == MG_COLDBOLT || skillid == MG_FIREBOLT || skillid == MG_LIGHTNINGBOLT + ) && + (sc = status_get_sc(src)) && + sc->count && sc->data[SC_DOUBLECAST].timer != -1 && + rand() % 100 < 40+10*sc->data[SC_DOUBLECAST].val1) + { +// skill_addtimerskill(src, tick + dmg.div_*dmg.amotion, bl->id, 0, 0, skillid, skilllv, BF_MAGIC, flag|1); + skill_addtimerskill(src, tick + dmg.amotion, bl->id, 0, 0, skillid, skilllv, BF_MAGIC, flag|1); + } + + map_freeblock_unlock(); + + return damage; /* ?ダ?を返す */ +} + +/*========================================== + * スキル範??U?用(map_foreachinareaから呼ばれる) + * flagについて?F16?i?を確認 + * MSB <- 00fTffff ->LSB + * T =タ?ゲット選?用(BCT_*) + * ffff=自由に使用可能 + * 0 =予約?B0に固定 + *------------------------------------------ + */ +static int skill_area_temp[8]; /* 一時???B必要なら使う?B */ +static int skill_unit_temp[8]; /* For storing skill_unit ids as players move in/out of them. [Skotlex] */ +static int skill_unit_index=0; //Well, yeah... am too lazy to pass pointers around :X +typedef int (*SkillFunc)(struct block_list *,struct block_list *,int,int,unsigned int,int); +int skill_area_sub( struct block_list *bl,va_list ap ) +{ + struct block_list *src; + int skill_id,skill_lv,flag; + unsigned int tick; + SkillFunc func; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + src=va_arg(ap,struct block_list *); //ここではsrcの値を??ニしていないのでNULLチェックはしない + skill_id=va_arg(ap,int); + skill_lv=va_arg(ap,int); + tick=va_arg(ap,unsigned int); + flag=va_arg(ap,int); + func=va_arg(ap,SkillFunc); + + if(battle_check_target(src,bl,flag) > 0) + func(src,bl,skill_id,skill_lv,tick,flag); + return 0; +} + +static int skill_check_unit_range_sub( struct block_list *bl,va_list ap ) +{ + struct skill_unit *unit; + int skillid,g_skillid; + + unit = (struct skill_unit *)bl; + + if(bl->prev == NULL || bl->type != BL_SKILL) + return 0; + + if(!unit->alive) + return 0; + + skillid = va_arg(ap,int); + g_skillid = unit->group->skill_id; + + switch (skillid) + { + case MG_SAFETYWALL: + case AL_PNEUMA: + if(g_skillid != MG_SAFETYWALL && g_skillid != AL_PNEUMA) + return 0; + break; + case AL_WARP: + case HT_SKIDTRAP: + case HT_LANDMINE: + case HT_ANKLESNARE: + case HT_SHOCKWAVE: + case HT_SANDMAN: + case HT_FLASHER: + case HT_FREEZINGTRAP: + case HT_BLASTMINE: + case HT_CLAYMORETRAP: + case HT_TALKIEBOX: + case HP_BASILICA: + //Non stackable on themselves and traps (including venom dust which does not has the trap inf2 set) + if (skillid != g_skillid && !(skill_get_inf2(g_skillid)&INF2_TRAP) && g_skillid != AS_VENOMDUST) + return 0; + break; + default: //Avoid stacking with same kind of trap. [Skotlex] + if (g_skillid != skillid) + return 0; + break; + } + + return 1; +} + +static int skill_check_unit_range(struct block_list *bl,int x,int y,int skillid,int skilllv) +{ + //Non players do not check for the skill's splash-trigger area. + int range = bl->type==BL_PC?skill_get_unit_range(skillid, skilllv):0; + int layout_type = skill_get_unit_layout_type(skillid,skilllv); + if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) { + ShowError("skill_check_unit_range: unsupported layout type %d for skill %d\n",layout_type,skillid); + return 0; + } + + range += layout_type; + return map_foreachinarea(skill_check_unit_range_sub,bl->m, + x-range,y-range,x+range,y+range,BL_SKILL,skillid); +} + +static int skill_check_unit_range2_sub( struct block_list *bl,va_list ap ) +{ + int skillid; + + if(bl->prev == NULL) + return 0; + + if(status_isdead(bl)) + return 0; + + skillid = va_arg(ap,int); + if (skillid==HP_BASILICA && bl->type==BL_PC) + return 0; + + if (skillid==AM_DEMONSTRATION && bl->type==BL_MOB && ((TBL_MOB*)bl)->class_ == MOBID_EMPERIUM) + return 0; //Allow casting Bomb/Demonstration Right under emperium [Skotlex] + return 1; +} + +static int skill_check_unit_range2(struct block_list *bl, int x,int y,int skillid, int skilllv) +{ + int range, type; + + switch (skillid) { // to be expanded later + case WZ_ICEWALL: + range = 2; + break; + default: + { + int layout_type = skill_get_unit_layout_type(skillid,skilllv); + if (layout_type==-1 || layout_type>MAX_SQUARE_LAYOUT) { + ShowError("skill_check_unit_range2: unsupported layout type %d for skill %d\n",layout_type,skillid); + return 0; + } + range = skill_get_unit_range(skillid,skilllv) + layout_type; + } + break; + } + + // if the caster is a monster/NPC, only check for players + // otherwise just check characters + if (bl->type == BL_PC) + type = BL_CHAR; + else + type = BL_PC; + + return map_foreachinarea(skill_check_unit_range2_sub, bl->m, + x - range, y - range, x + range, y + range, + type, skillid); +} + +int skill_guildaura_sub (struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + int gid, id, *flag; + + nullpo_retr(0, sd = (struct map_session_data *)bl); + nullpo_retr(0, ap); + + id = va_arg(ap,int); + gid = va_arg(ap,int); + if (sd->status.guild_id != gid) + return 0; + nullpo_retr(0, flag = va_arg(ap,int *)); + + if (flag && *flag > 0) { + if (sd->sc.count && sd->sc.data[SC_GUILDAURA].timer != -1) { + if (sd->sc.data[SC_GUILDAURA].val4 != *flag) { + sd->sc.data[SC_GUILDAURA].val4 = *flag; + status_calc_bl(&sd->bl, StatusChangeFlagTable[SC_GUILDAURA]); + } + return 0; + } + sc_start4(&sd->bl, SC_GUILDAURA,100, 1, id, 0, *flag, 1000); + } + + return 0; +} + +/*========================================================================= + * 範?スキル使用???ャ分けここから + */ +/* ??ロの?をカウントする?B?iskill_area_temp[0]を?炎化しておくこと?j */ +int skill_area_sub_count(struct block_list *src,struct block_list *target,int skillid,int skilllv,unsigned int tick,int flag) +{ + if(skill_area_temp[0] < 0xffff) + skill_area_temp[0]++; + return 1; +} + +int skill_count_water(struct block_list *src,int range) +{ + int i,x,y,cnt = 0,size = range*2+1; + struct skill_unit *unit; + + for (i=0;ix+(i%size-range); + y = src->y+(i/size-range); + if (map_getcell(src->m,x,y,CELL_CHKWATER)) { + cnt++; + continue; + } + unit = map_find_skill_unit_oncell(src,x,y,SA_DELUGE,NULL); + if (unit) { + cnt++; + skill_delunit(unit); + } + } + return cnt; +} + +/*========================================== + * + *------------------------------------------ + */ +static int skill_timerskill(int tid, unsigned int tick, int id,int data ) +{ + struct block_list *src = map_id2bl(id),*target; + struct unit_data *ud = unit_bl2ud(src); + struct skill_timerskill *skl = NULL; + int range; + + nullpo_retr(0, src); + nullpo_retr(0, ud); + skl = ud->skilltimerskill[data]; + nullpo_retr(0, skl); + ud->skilltimerskill[data] = NULL; + + do { + if(src->prev == NULL) + break; + if(skl->target_id) { + target = map_id2bl(skl->target_id); + if(!target && skl->skill_id == RG_INTIMIDATE) + target = src; //Required since it has to warp. + if(target == NULL) + break; + if(target->prev == NULL) + break; + if(src->m != target->m) + break; + if(status_isdead(src)) + break; + if(status_isdead(target) && skl->skill_id != RG_INTIMIDATE && skl->skill_id != WZ_WATERBALL) + break; + + switch(skl->skill_id) { + case RG_INTIMIDATE: + if (unit_warp(src,-1,-1,-1,3) == 0) { + short x,y; + map_search_freecell(src, 0, &x, &y, 1, 1, 0); + if (target != src && !status_isdead(target)) + unit_warp(target, -1, x, y, 3); + } + break; + case BA_FROSTJOKE: /* 寒いジョ?ク */ + case DC_SCREAM: /* スクリ?ム */ + range= skill_get_splash(skl->skill_id, skl->skill_lv); + map_foreachinarea(skill_frostjoke_scream,skl->map,skl->x-range,skl->y-range, + skl->x+range,skl->y+range,BL_CHAR,src,skl->skill_id,skl->skill_lv,tick); + break; + + case WZ_WATERBALL: + if (!status_isdead(target)) + skill_attack(BF_MAGIC,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); + if (skl->type>1 && !status_isdead(target)) { + skill_addtimerskill(src,tick+150,target->id,0,0,skl->skill_id,skl->skill_lv,skl->type-1,skl->flag); + } else { + struct status_change *sc = status_get_sc(src); + if(sc && sc->data[SC_MAGICPOWER].timer != -1) + status_change_end(src,SC_MAGICPOWER,-1); + } + break; + default: + skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag); + break; + } + } + else { + if(src->m != skl->map) + break; + switch(skl->skill_id) { + case WZ_METEOR: + if(skl->type >= 0) { + skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->type>>16,skl->type&0xFFFF,skl->flag); + clif_skill_poseffect(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,tick); + } + else + skill_unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag); + break; + } + } + } while (0); + //Free skl now that it is no longer needed. + ers_free(skill_timer_ers, skl); + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +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 i; + struct unit_data *ud; + nullpo_retr(1, src); + ud = unit_bl2ud(src); + nullpo_retr(1, ud); + + for(i=0;iskilltimerskill[i]; i++); + if (i==MAX_SKILLTIMERSKILL) return 1; + + ud->skilltimerskill[i] = ers_alloc(skill_timer_ers, struct skill_timerskill); + ud->skilltimerskill[i]->timer = add_timer(tick, skill_timerskill, src->id, i); + ud->skilltimerskill[i]->src_id = src->id; + ud->skilltimerskill[i]->target_id = target; + ud->skilltimerskill[i]->skill_id = skill_id; + ud->skilltimerskill[i]->skill_lv = skill_lv; + ud->skilltimerskill[i]->map = src->m; + ud->skilltimerskill[i]->x = x; + ud->skilltimerskill[i]->y = y; + ud->skilltimerskill[i]->type = type; + ud->skilltimerskill[i]->flag = flag; + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int skill_cleartimerskill(struct block_list *src) +{ + int i; + struct unit_data *ud; + nullpo_retr(0, src); + ud = unit_bl2ud(src); + nullpo_retr(0, ud); + + for(i=0;iskilltimerskill[i]) { + delete_timer(ud->skilltimerskill[i]->timer, skill_timerskill); + ers_free(skill_timer_ers, ud->skilltimerskill[i]); + ud->skilltimerskill[i]=NULL; + } + } + return 1; +} + +static int skill_reveal_trap( struct block_list *bl,va_list ap ) +{ + TBL_SKILL *su = (TBL_SKILL*)bl; + if (su->alive && su->group && skill_get_inf2(su->group->skill_id)&INF2_TRAP) + { //Reveal trap. + //Change look is not good enough, the client ignores it as an actual trap still. [Skotlex] + //clif_changetraplook(bl, su->group->unit_id); + clif_skill_setunit(su); + return 1; + } + return 0; +} + +/*========================================== + * スキル使用?i詠?・完了?AID指定?U?系?j + * ?iスパゲッティに向けて1?前?i?I(ダ?ポ)?j + *------------------------------------------ + */ +int skill_castend_damage_id (struct block_list* src, struct block_list *bl,int skillid,int skilllv,unsigned int tick,int flag) +{ + struct map_session_data *sd = NULL, *tsd = NULL; + struct status_data *tstatus; + struct status_change *sc; + + if (skillid > 0 && skilllv <= 0) return 0; + + nullpo_retr(1, src); + nullpo_retr(1, bl); + + if (src->m != bl->m) + return 1; + + if (bl->prev == NULL) + return 1; + + if (src->type == BL_PC) + sd = (struct map_session_data *)src; + if (bl->type == BL_PC) + tsd = (struct map_session_data *)bl; + + if (status_isdead(src) || (src != bl && status_isdead(bl))) + return 1; + + if (skillid && skill_get_type(skillid) == BF_MAGIC && + !battle_config.gtb_pvp_only && status_isimmune(bl)) { + if (sd) clif_skill_fail(sd,skillid,0,0); + //GTB makes all targetted skills silently fail. + return 1; + } + + sc = status_get_sc(src); + if (sc && !sc->count) + sc = NULL; //Unneeded + + tstatus = status_get_status_data(bl); + + map_freeblock_lock(); + + switch(skillid) + { + /* ?器?U?系スキル */ + case SM_BASH: /* バッシュ */ + case MC_MAMMONITE: /* ?マ?ナイト */ + case TF_DOUBLE: + case AC_DOUBLE: /* ダブルストレイフィング */ + case AS_SONICBLOW: /* ソニックブ?? */ + case KN_PIERCE: /* ピア?ス */ + case KN_SPEARBOOMERANG: /* スピアブ??ラン */ + case KN_BRANDISHSPEAR: /* ブランディッシュスピア */ + case TF_POISON: /* インベナム */ + case TF_SPRINKLESAND: /* ?サまき */ + case AC_CHARGEARROW: /* チャ?ジア?? */ + case RG_RAID: /* サプライズアタック */ + case RG_INTIMIDATE: /* インティミデイト */ + case AM_ACIDTERROR: /* アシッドテラ? */ + case BA_MUSICALSTRIKE: /* ミュ?ジカルストライク */ + case DC_THROWARROW: /* 矢?ち */ + case BA_DISSONANCE: /* 不協和音 */ + case CR_HOLYCROSS: /* ホ?リ?ク?ス */ + case NPC_DARKCROSS: + case CR_SHIELDCHARGE: + case CR_SHIELDBOOMERANG: + /* 以下MOB?用 */ + /* ???U??ASP減?ュ?U??A遠距離?U??A防御無視?U??A多段?U? */ + case NPC_PIERCINGATT: + case NPC_MENTALBREAKER: + case NPC_RANGEATTACK: + case NPC_CRITICALSLASH: + case NPC_COMBOATTACK: + /* 必中?U??A毒?U??A暗??U??A沈??U??Aスタン?U? */ + case NPC_GUIDEDATTACK: + case NPC_POISON: + case NPC_BLINDATTACK: + case NPC_SILENCEATTACK: + case NPC_STUNATTACK: + /* ?ホ化?U??A呪い?U??A?眠?U??AランダムATK?U? */ + case NPC_PETRIFYATTACK: + case NPC_CURSEATTACK: + case NPC_SLEEPATTACK: + case NPC_RANDOMATTACK: + /* ???ォ?U??A地??ォ?U??A火??ォ?U??A風??ォ?U? */ + case NPC_WATERATTACK: + case NPC_GROUNDATTACK: + case NPC_FIREATTACK: + case NPC_WINDATTACK: + /* 毒??ォ?U??A?ケ??ォ?U??A闇??ォ?U??A念??ォ?U??ASP減?ュ?U? */ + case NPC_POISONATTACK: + case NPC_HOLYATTACK: + case NPC_DARKNESSATTACK: + case NPC_TELEKINESISATTACK: + case NPC_UNDEADATTACK: + case NPC_BREAKARMOR: + case NPC_BREAKWEAPON: + case NPC_BREAKHELM: + case NPC_BREAKSHIELD: + case LK_AURABLADE: /* オ?ラブレ?ド */ + case LK_SPIRALPIERCE: /* スパイラルピア?ス */ + case LK_HEADCRUSH: /* ヘッドクラッシュ */ + case LK_JOINTBEAT: /* ジョイントビ?ト */ + case CG_ARROWVULCAN: /* ア??バルカン */ + case HW_MAGICCRASHER: /* マジッククラッシャ? */ + case ASC_METEORASSAULT: /* ?テオアサルト */ + case ITM_TOMAHAWK: + case MO_TRIPLEATTACK: + case CH_CHAINCRUSH: /* 連柱崩? */ + case CH_TIGERFIST: /* 伏虎? */ + case PA_SHIELDCHAIN: // Shield Chain + case PA_SACRIFICE: // Sacrifice, Aru's style. + case WS_CARTTERMINATION: // Cart Termination + case AS_VENOMKNIFE: + case HT_PHANTASMIC: + case HT_POWER: + case TK_DOWNKICK: + case TK_COUNTER: + case ASC_BREAKER: + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + + case MO_COMBOFINISH: + if (!(flag&1) && sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_MONK) + { //Becomes a splash attack when Soul Linked. + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } else + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + + case TK_STORMKICK: // Taekwon kicks [Dralnu] + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_attack_area, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + BF_WEAPON, src, src, skillid, skilllv, tick, flag, BCT_ENEMY); + break; + + case NJ_SHADOWJUMP: //[blackhole89] + case TK_JUMPKICK: + if (skillid == TK_JUMPKICK) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if (unit_movepos(src, bl->x, bl->y, 0, 0)) + clif_slide(src,bl->x,bl->y); + break; + + case SN_SHARPSHOOTING: /* シャ?プシュ?ティング */ + // Does it stop if touch an obstacle? it shouldn't shoot trough walls + map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y, + skill_get_splash(skillid, skilllv),BL_CHAR, + BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY); // varargs + break; + + case MO_INVESTIGATE: /* ?勁 */ + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if (sc && sc->data[SC_BLADESTOP].timer != -1) + status_change_end(src,SC_BLADESTOP,-1); + break; + + case RG_BACKSTAP: /* バックスタブ */ + { + int dir = map_calc_dir(src, bl->x, bl->y), t_dir = unit_getdir(bl); + if ((!check_distance_bl(src, bl, 0) && !map_check_dir(dir, t_dir)) || bl->type == BL_SKILL) { + if (sc && sc->data[SC_HIDING].timer != -1) + status_change_end(src, SC_HIDING, -1); // ハイディング解? + skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, flag); + dir = dir < 4 ? dir+4 : dir-4; // change direction [Celest] + unit_setdir(bl,dir); + clif_changed_dir(bl); + } + else if (sd) + clif_skill_fail(sd,skillid,0,0); + } + break; + + case MO_FINGEROFFENSIVE: /* 指? */ + { + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if (battle_config.finger_offensive_type && sd) { + int i; + for (i = 1; i < sd->spiritball_old; i++) + skill_addtimerskill(src, tick + i * 200, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag); +// sd->canmove_tick = tick + (sd->spiritball_old - 1) * 200; Should be handled by the canmove delay on skill_cast_db [Skotlex] + } + if (sc && sc->data[SC_BLADESTOP].timer != -1) + status_change_end(src,SC_BLADESTOP,-1); + } + break; + + case MO_CHAINCOMBO: /* 連打?カ */ + { + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + if (sc && sc->data[SC_BLADESTOP].timer != -1) + status_change_end(src,SC_BLADESTOP,-1); + } + break; + + case KN_CHARGEATK: + case MO_EXTREMITYFIST: /* 阿?C羅覇鳳? */ + if (skillid == MO_EXTREMITYFIST && sc && sc->count) + { + if (sc->data[SC_EXPLOSIONSPIRITS].timer != -1) + status_change_end(src, SC_EXPLOSIONSPIRITS, -1); + if (sc->data[SC_BLADESTOP].timer != -1) + status_change_end(src,SC_BLADESTOP,-1); + } + if(!check_distance_bl(src, bl, 2)) { //Need to move to target. + int dx,dy; + + dx = bl->x - src->x; + dy = bl->y - src->y; + if(dx > 0) dx++; + else if(dx < 0) dx--; + if (dy > 0) dy++; + else if(dy < 0) dy--; + + if (skillid == KN_CHARGEATK) //Store distance in flag [Skotlex] + flag = distance_bl(src, bl); + + if (!unit_movepos(src, src->x+dx, src->y+dy, 1, 1)) { + if (sd) clif_skill_fail(sd,skillid,0,0); + break; + } + clif_slide(src,src->x,src->y); + if (skillid != MO_EXTREMITYFIST || battle_check_target(src, bl, BCT_ENEMY) > 0) //Check must be done here because EF should be broken this way.. [Skotlex] + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else if (sd) + clif_skill_fail(sd,skillid,0,0); + } + else //Assume minimum distance of 1 for Charge. + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,skillid == KN_CHARGEATK?1:flag); + break; + + /* ?器系範??U?スキル */ + case AS_GRIMTOOTH: /* グリムトゥ?ス */ + case MC_CARTREVOLUTION: /* カ?トレヴォリュ?ション */ + case NPC_SPLASHATTACK: /* スプラッシュアタック */ + case AC_SHOWER: //Targetted skill implementation. + if(flag&1){ + /* 個別にダ??ジを?える */ + if(bl->id!=skill_area_temp[1]){ + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick, + 0x0500); + } + } else { + skill_area_temp[1]=bl->id; + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + //Skill-attack at the end in case it has knockback. [Skotlex] + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0); + } + break; + + case AS_SPLASHER: + if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by. + if (bl->id != skill_area_temp[1]) + skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); + else + skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0); + } else { + skill_area_temp[0] = 0; + skill_area_temp[1] = bl->id; + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count); + skill_area_temp[0]--; //Substract one, the original target shouldn't count. [Skotlex] + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + + case SM_MAGNUM: + if(flag&1) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else { + //If we get here, someone changed magnum to be a enemy targetted skill, + //so treat it as such. + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + //Initiate 10% of your damage becomes fire element. + clif_skill_nodamage (src,bl,skillid,skilllv,1); + sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skillid, skilllv)); + if (sd) skill_blockpc_start (sd, skillid, skill_get_time(skillid, skilllv)); + } + break; + + case KN_BOWLINGBASH: /* ボウリングバッシュ */ + if(flag&1){ + /* 個別にダ??ジを?える */ + if(bl->id!=skill_area_temp[1]) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500); + } else { + int i,c; /* 他?lから聞いた動きなので間違ってる可能?ォ大??率が?いっす?? */ + /* まずタ?[ゲットに?U撃を加える */ + c = skill_get_blewcount(skillid,skilllv); + if(map_flag_gvg(bl->m) || status_get_mexp(bl)) + c = 0; + for(i=0;i1) break; + } + clif_blown(bl); //Update target pos. + skill_area_temp[1]=bl->id; + /* その後タ?ゲット以外の範??の敵全?に??を?sう */ + map_foreachinrange(skill_area_sub,bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0); + } + break; + + case KN_SPEARSTAB: /* スピアスタブ */ + if(flag&1){ + /* 個別にダ??[ジを与える */ + if (bl->id==skill_area_temp[1]) + break; + if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0x0500) && !status_get_mexp(bl)) + skill_blown(src,bl,skill_area_temp[2]); + } else { + int x=bl->x,y=bl->y,i,dir; + /* まずタ?[ゲットに?U撃を加える */ + dir = map_calc_dir(bl,src->x,src->y); + skill_area_temp[1] = bl->id; + skill_area_temp[2] = skill_get_blewcount(skillid,skilllv)|dir<<20; + if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0) && !status_get_mexp(bl)) + skill_blown(src,bl,skill_area_temp[2]); + for (i=0;i<4;i++) { + map_foreachincell(skill_area_sub,bl->m,x,y,BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1, + skill_castend_damage_id); + x += dirx[dir]; + y += diry[dir]; + } + } + break; + + case TK_TURNKICK: + case MO_BALKYOUNG: //Active part of the attack. Skill-attack [Skotlex] + { + skill_area_temp[1] = bl->id; //NOTE: This is used in skill_castend_nodamage_id to avoid affecting the target. + if (skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,0)) + map_foreachinrange(skill_area_sub,bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY|1, + skill_castend_nodamage_id); + } + break; + case CH_PALMSTRIKE: // Palm Strike takes effect 1sec after casting. [Skotlex] + // clif_skill_nodamage(src,bl,skillid,skilllv,0); //Can't make this one display the correct attack animation delay :/ + clif_damage(src,bl,tick,status_get_amotion(src),0,0,1,4,0); //Displays MISS, but better than nothing :X + skill_addtimerskill(src, tick + 1000, bl->id, 0, 0, skillid, skilllv, BF_WEAPON, flag); + break; + + case ALL_RESURRECTION: /* リザレクション */ + case PR_TURNUNDEAD: /* タ?ンアンデッド */ + //Undead check is on unit-use skill + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + + /* 魔法系スキル */ + case MG_SOULSTRIKE: /* ソウルストライク */ + case NPC_DARKSTRIKE: /*闇ソウルストライク*/ + case MG_COLDBOLT: /* コ?[ルドボルト */ + case MG_FIREBOLT: /* ファイア?[ボルト */ + case MG_LIGHTNINGBOLT: /* ライトニングボルト */ + case WZ_EARTHSPIKE: /* ア?[ススパイク */ + case AL_HEAL: /* ヒ?[ル */ + case AL_HOLYLIGHT: /* ホ?[リ?[ライト */ + case WZ_JUPITEL: /* ユピテルサンダ?[ */ + case NPC_DARKTHUNDER: /*闇ユピテル*/ + case NPC_MAGICALATTACK: /* MOB:魔法打??U? */ + case PR_ASPERSIO: /* アスペルシオ */ + case MG_FROSTDIVER: /* フ?ストダイバ?[ */ + case WZ_SIGHTBLASTER: + case WZ_SIGHTRASHER: /* サイトラッシャ?[ */ + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + + case HVAN_CAPRICE: //[blackhole89] + { + int ran=rand()%4; + int sid; + switch(ran) + { + case 0: sid=MG_COLDBOLT; break; + case 1: sid=MG_FIREBOLT; break; + case 2: sid=MG_LIGHTNINGBOLT; break; + case 3: sid=WZ_EARTHSPIKE; break; + } + skill_attack(BF_MAGIC,src,src,bl,sid,skilllv,tick,flag); + } + break; + case WZ_WATERBALL: /* ウォ?タ?ボ?ル */ + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + if (skilllv>1) { + int range = skilllv/2; + int cnt; + if (sd) + cnt = skill_count_water(src,range); + else { + range = 2*range+1; + cnt = range*range; + } + cnt--; + if (cnt > 0) + skill_addtimerskill(src,tick+150,bl->id,0,0, + skillid,skilllv,cnt,flag); + } else if (sd) //Eat up deluge tiles. + skill_count_water(src,0); + + break; + + case PR_BENEDICTIO: /* ?ケ??~福 */ + //Should attack undead and demons. [Skotlex] + if (battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race == RC_DEMON) + skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, flag); + break; + + /* 魔法系範??U?スキル */ + case MG_NAPALMBEAT: /* ナパ?ムビ?ト */ + case MG_FIREBALL: /* ファイヤ?ボ?ル */ + if (flag & 1) { + /* 個別にダ??ジを?える */ + if (bl->id == skill_area_temp[1]) + break; + if(skillid == MG_FIREBALL) //Store distance. + skill_area_temp[0] = distance_blxy(bl, skill_area_temp[2], skill_area_temp[3]); + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, skill_area_temp[0]| 0x0500); + } else { + skill_area_temp[0]=0; + skill_area_temp[1]=bl->id; + switch (skillid) { + case MG_NAPALMBEAT: + /* ナパ?[ムビ?[トは分散ダ??[ジなので敵の?狽?狽ヲる */ + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_ENEMY, + skill_area_sub_count); + break; + case MG_FIREBALL: + skill_area_temp[2]=bl->x; + skill_area_temp[3]=bl->y; + break; + } + /* タ?[ゲットに?U撃を加える(スキルエフェクト表示) */ + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick, skill_area_temp[0]); + /* タ?[ゲット以外の範囲内の敵全体に??を?sう */ + map_foreachinrange(skill_area_sub,bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + case HW_NAPALMVULCAN: // Fixed By SteelViruZ + if (flag & 1) { + if (bl->id != skill_area_temp[1]) + skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); + } else { + skill_area_temp[0] = 0; + skill_area_temp[1] = bl->id; + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY, + skill_area_sub_count); + skill_attack(BF_MAGIC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + case SL_STIN: + case SL_STUN: + case SL_SMA: + if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { + status_change_start(src,SC_STUN,10000,skilllv,0,0,0,500,10); + clif_skill_fail(sd,skillid,0,0); + break; + } + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + + /* その他 */ + case HT_BLITZBEAT: /* ブリッツビ?ト */ + if (flag & 1) { //Invoked from map_foreachinarea, skill_area_temp[0] holds number of targets to divide damage by. + skill_attack(BF_MISC, src, src, bl, skillid, skilllv, tick, skill_area_temp[0]); + } else { + skill_area_temp[0] = 0; + skill_area_temp[1] = bl->id; + if (flag & 0xf00000) //Warning, 0x100000 is currently BCT_NEUTRAL, so don't mix it when asking for the enemy. [Skotlex] + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, BCT_ENEMY, skill_area_sub_count); + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + + case NPC_DARKBREATH: + clif_emotion(src,7); + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); + break; + + case SN_FALCONASSAULT: /* ファルコンアサルト */ + case PA_PRESSURE: /* プレッシャ? */ + case CR_ACIDDEMONSTRATION: // Acid Demonstration + case TF_THROWSTONE: /* ?ホ投げ */ + case NPC_SMOKING: /* スモ?キング */ + case NPC_SELFDESTRUCTION: /* 自爆 */ + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); + break; + + // Celest + case PF_SOULBURN: + if (rand()%100 < (skilllv < 5 ? 30 + skilllv * 10 : 70)) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (skilllv == 5) + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,0 ); + if (tsd) { + tsd->status.sp = 0; + clif_updatestatus(tsd,SP_SP); + } + } else { + clif_skill_nodamage(src,src,skillid,skilllv,1); + if (skilllv == 5) + skill_attack(BF_MAGIC,src,src,src,skillid,skilllv,tick,0 ); + if (sd) { + sd->status.sp = 0; + clif_updatestatus(sd,SP_SP); + } + } + if (sd) skill_blockpc_start (sd, skillid, (skilllv < 5 ? 10000: 15000)); + break; + + /* HP吸?/HP吸?魔法 */ + case NPC_BLOODDRAIN: + case NPC_ENERGYDRAIN: + { + int heal = skill_attack( (skillid == NPC_BLOODDRAIN) ? BF_WEAPON : BF_MAGIC, + src, src, bl, skillid, skilllv, tick, flag); + if (heal > 0){ + clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1); + status_heal(src, heal, 0, 0); + } + } + break; + + //Until they're at right position - gs_damage- [Vicious] + case GS_TRIPLEACTION: + case GS_MAGICALBULLET: + case GS_CRACKER: + case GS_TRACKING: + case GS_PIERCINGSHOT: + case GS_RAPIDSHOWER: + case GS_DUST: + case GS_FULLBUSTER: + case GS_FLING: + case NJ_SYURIKEN: + case NJ_KUNAI: + case NJ_HUUMA: + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + case GS_BULLSEYE: + if(tstatus->race == RC_BRUTE || tstatus->race == RC_DEMIHUMAN) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else if (sd) + clif_skill_fail(sd,skillid,0,0); + break; + case GS_DESPERADO: + case GS_SPREADATTACK: + if(flag&1) + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + else { + //If we get here, someone changed it to be a enemy targetted skill, + //so treat it as such. + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + break; + case NJ_ZENYNAGE: + if(sd->status.zeny < skilllv*1000) + clif_skill_fail(sd,skillid,5,0); + else + skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag); + break; + case NJ_KASUMIKIRI: + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + sc_start(src,SC_HIDING,100,skilllv,skill_get_time(skillid,skilllv)); + break; + case NJ_KIRIKAGE: + status_change_end(src, SC_HIDING, -1); + skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag); + break; + case NJ_KOUENKA: + case NJ_HYOUSENSOU: + case NJ_HYOUSYOURAKU: + case NJ_HUUJIN: + case NJ_RAIGEKISAI: + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + case NJ_KAMAITACHI: + // Does it stop if touch an obstacle? it shouldn't shoot trough walls + map_foreachinpath (skill_attack_area,src->m,src->x,src->y,bl->x,bl->y, + skill_get_splash(skillid, skilllv),BL_CHAR, + BF_WEAPON,src,src,skillid,skilllv,tick,flag,BCT_ENEMY); // varargs + break; + //Not implemented yet [Vicious] + case GS_GROUNDDRIFT: + + //case NJ_SYURIKEN: + //case NJ_KUNAI: + //case NJ_HUUMA: + //case NJ_ZENYNAGE: + case NJ_TATAMIGAESHI: + //case NJ_KASUMIKIRI: + //case NJ_KIRIKAGE: + //case NJ_KOUENKA: + case NJ_KAENSIN: + //case NJ_HYOUSENSOU: + //case NJ_HYOUSYOURAKU: + //case NJ_HUUJIN: + //case NJ_RAIGEKISAI: + //case NJ_KAMAITACHI: + case NJ_ISSEN: + skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag); + break; + + case 0: + if(sd) { + if (flag & 3){ + if (bl->id != skill_area_temp[1]) + skill_attack(BF_WEAPON, src, src, bl, skillid, skilllv, tick, 0x0500); + } else { + skill_area_temp[1] = bl->id; + map_foreachinrange(skill_area_sub, bl, + sd->splash_range, BL_CHAR, + src, skillid, skilllv, tick, flag | BCT_ENEMY | 1, + skill_castend_damage_id); + } + } + break; + + default: + ShowWarning("skill_castend_damage_id: Unknown skill used:%d\n",skillid); + map_freeblock_unlock(); + return 1; + } + + map_freeblock_unlock(); + + if (sd && !(flag&1) && sd->state.arrow_atk) //Consume arrow on last invocation to this skill. + battle_consume_ammo(sd, skillid, skilllv); + return 0; +} + +/*========================================== + * スキル使用?i詠?・完了?AID指定支援系?j + *------------------------------------------ + */ +int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, int skillid, int skilllv, unsigned int tick, int flag) +{ + struct map_session_data *sd = NULL; + struct map_session_data *dstsd = NULL; + struct status_data *sstatus, *tstatus; + struct status_change *tsc; + struct mob_data *md = NULL; + struct mob_data *dstmd = NULL; + int i,type=-1; + + if(skillid > 0 && skilllv <= 0) return 0; // celest + + nullpo_retr(1, src); + nullpo_retr(1, bl); + + if (src->m != bl->m) + return 1; + + if (src->type == BL_PC) { + sd = (struct map_session_data *)src; + } else if (src->type == BL_MOB) { + md = (struct mob_data *)src; + } + + if (bl->type == BL_PC){ + dstsd = (struct map_session_data *)bl; + } else if (bl->type == BL_MOB){ + dstmd = (struct mob_data *)bl; + } + + if(bl->prev == NULL) + return 1; + if(status_isdead(src) && skillid != NPC_REBIRTH) + return 1; + if(status_isdead(bl) && skillid != NPC_REBIRTH && skillid != ALL_RESURRECTION && skillid != PR_REDEMPTIO) + return 1; + + tstatus = status_get_status_data(bl); + sstatus = status_get_status_data(src); + + //Check for undead skills that convert a no-damage skill into a damage one. [Skotlex] + switch (skillid) { + case AL_HEAL: + case ALL_RESURRECTION: + case PR_ASPERSIO: + if (battle_check_undead(tstatus->race,tstatus->def_ele)) { + if (battle_check_target(src, bl, BCT_ENEMY) < 1) { + //Offensive heal does not works on non-enemies. [Skotlex] + if (sd) clif_skill_fail(sd,skillid,0,0); + return 0; + } + if(!sd) { + //Prevent non-players from casting offensive heal. [Skotlex] + clif_emotion(src, 4); + return 0; + } + return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag); + } + break; + case NPC_SMOKING: //Since it is a self skill, this one ends here rather than in damage_id. [Skotlex] + return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag); + //These are actually ground placed. + case CR_GRANDCROSS: + case NPC_GRANDDARKNESS: + //Until they're at right position - gs_ground- [Vicious] + case GS_DESPERADO: + case NJ_KAENSIN: /*火炎陣*/ + case NJ_HYOUSYOURAKU: + case NJ_RAIGEKISAI: + return skill_castend_pos2(src,src->x,src->y,skillid,skilllv,tick,0); + } + + //Self skill with target changed? We assume these are offensive auto-select-target skills. [Skotlex] + //But only do this on the first call (flag&~1) + if (!(flag&1) && skill_get_inf(skillid)&INF_SELF_SKILL && src != bl && !(skill_get_nk(skillid)&NK_NO_DAMAGE)) + return skill_castend_damage_id (src, bl, skillid, skilllv, tick, flag); + + if (skillid > 0 && skillid < MAX_SKILL) + type = SkillStatusChangeTable[skillid]; + + tsc = status_get_sc(bl); + + map_freeblock_lock(); + switch(skillid) + { + case AL_HEAL: /* ヒ?ル */ + { + int heal = skill_calc_heal(src, skilllv); + int heal_get_jobexp; + + if (skilllv > 10) + heal = 9999; //9999ヒ?[ル + if (status_isimmune(bl) || (dstmd && dstmd->class_ == MOBID_EMPERIUM)) + heal=0; /* ?金蟲カ?ド?iヒ?ル量0?j */ + if (sd) { + if (sd && dstsd && sd->status.partner_id == dstsd->status.char_id && + (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.sex == 0) //自分も?象もPC、?象が自分のパ?トナ?、自分がスパノビ、自分が♀なら + heal = heal*2; //スパノビの嫁が旦那にヒ?ルすると2倍になる + } + + if (tsc && tsc->count && tsc->data[SC_KAITE].timer != -1 + && !(sstatus->mode&MD_BOSS) + ) { //Bounce back heal + if (--tsc->data[SC_KAITE].val2 <= 0) + status_change_end(bl, SC_KAITE, -1); + if (src == bl) heal=0; //When you try to heal yourself and you are under Kaite, the heal is voided. + clif_skill_nodamage (src, src, skillid, heal, 1); + heal_get_jobexp = status_heal(src,heal,0,0); + } else { + clif_skill_nodamage (src, bl, skillid, heal, 1); + heal_get_jobexp = status_heal(bl,heal,0,0); + } + + // JOB??値獲得 + if(sd && dstsd && heal > 0 && sd != dstsd && battle_config.heal_exp > 0){ + heal_get_jobexp = heal_get_jobexp * battle_config.heal_exp / 100; + if (heal_get_jobexp <= 0) + heal_get_jobexp = 1; + pc_gainexp (sd, 0, heal_get_jobexp); + } + } + break; + + case PR_REDEMPTIO: + if (sd && !(flag&1)) { + if (sd->status.party_id == 0) { + clif_skill_fail(sd,skillid,0,0); + break; + } + skill_area_temp[0] = 0; + party_foreachsamemap(skill_area_sub, + sd,skill_get_splash(skillid, skilllv), + src,skillid,skilllv,tick, flag|BCT_PARTY|1, + skill_castend_nodamage_id); + if (skill_area_temp[0] == 0) { + clif_skill_fail(sd,skillid,0,0); + break; + } + skill_area_temp[0] = 5 - skill_area_temp[0]; // The actual penalty... + if (skill_area_temp[0] > 0 && !map[src->m].flag.nopenalty) { //Apply penalty + sd->status.base_exp -= pc_nextbaseexp(sd) * skill_area_temp[0] * 2/1000; //0.2% penalty per each. + sd->status.job_exp -= pc_nextjobexp(sd) * skill_area_temp[0] * 2/1000; + clif_updatestatus(sd,SP_BASEEXP); + clif_updatestatus(sd,SP_JOBEXP); + } + status_zap(src, sstatus->hp-1, sstatus->sp-1); + break; + } else if (dstsd && pc_isdead(dstsd) && flag&1) { //Revive + skill_area_temp[0]++; //Count it in, then fall-through to the Resurrection code. + skilllv = 3; //Resurrection level 3 is used + } else //Invalid target, skip resurrection. + break; + + case ALL_RESURRECTION: /* リザレクション */ + if(sd && map_flag_gvg(bl->m)) + { //No reviving in WoE grounds! + clif_skill_fail(sd,skillid,0,0); + break; + } + if(dstsd && pc_isdead(dstsd)) { + int per = 0; + if (map[bl->m].flag.pvp && dstsd->pvp_point < 0) + break; + + clif_skill_nodamage(src,bl,ALL_RESURRECTION,skilllv,1); //Both Redemption and Res show this skill-animation. + switch(skilllv){ + case 1: per=10; break; + case 2: per=30; break; + case 3: per=50; break; + case 4: per=80; break; + } + tstatus->hp = 1; + if (dstsd->special_state.restart_full_recover) + status_percent_heal(bl, 100, 100); + else + status_percent_heal(bl, per, 0); + pc_setstand(dstsd); + if(battle_config.pc_invincible_time > 0) + pc_setinvincibletimer(dstsd, battle_config.pc_invincible_time); + clif_resurrection(bl, 1); + if(sd && battle_config.resurrection_exp > 0) { + int exp = 0,jexp = 0; + int lv = dstsd->status.base_level - sd->status.base_level, jlv = dstsd->status.job_level - sd->status.job_level; + if(lv > 0) { + exp = (int)((double)dstsd->status.base_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); + if (exp < 1) exp = 1; + } + if(jlv > 0) { + jexp = (int)((double)dstsd->status.job_exp * (double)lv * (double)battle_config.resurrection_exp / 1000000.); + if (jexp < 1) jexp = 1; + } + if(exp > 0 || jexp > 0) + pc_gainexp (sd, exp, jexp); + } + } + break; + + case AL_DECAGI: /* 速度減?ュ */ + clif_skill_nodamage (src, bl, skillid, skilllv, + sc_start(bl, type, + (40 + skilllv * 2 + (status_get_lv(src) + sstatus->int_)/5), + skilllv, skill_get_time(skillid,skilllv))); + break; + + case AL_CRUCIS: + if (flag & 1) { + if (battle_check_target (src, bl, BCT_ENEMY)) + sc_start(bl,type, + 23+skilllv*4 +status_get_lv(src) -status_get_lv(bl), + skilllv,60000); + } else { + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY|1, + skill_castend_nodamage_id); + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + } + break; + + case PR_LEXDIVINA: /* レックスディビ?ナ */ + if (tsc && tsc->count && tsc->data[type].timer != -1) { + status_change_end(bl,type, -1); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + } else + clif_skill_nodamage (src, bl, skillid, skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + + case SA_ABRACADABRA: + { + int abra_skillid = 0, abra_skilllv; + if (sd) + { //Crash-fix [Skotlex] + //require 1 yellow gemstone even with mistress card or Into the Abyss + if ((i = pc_search_inventory(sd, 715)) < 0 ) + { //bug fixed by Lupus (item pos can be 0, too!) + clif_skill_fail(sd,skillid,0,0); + break; + } + pc_delitem(sd, i, 1, 0); + } + do { + abra_skillid = rand() % MAX_SKILL_ABRA_DB; + if (skill_abra_db[abra_skillid].req_lv > skilllv || + rand()%10000 >= skill_abra_db[abra_skillid].per || //dbに基づくレベル?確率判定 + (abra_skillid >= NPC_PIERCINGATT && abra_skillid <= NPC_SUMMONMONSTER) || //NPCスキルはダ? + skill_get_unit_flag(abra_skillid) & UF_DANCE) //演奏スキルはダ? + abra_skillid = 0; // reset to get a new id + } while (abra_skillid == 0); + abra_skilllv = skill_get_max(abra_skillid) > skilllv ? skilllv : skill_get_max(abra_skillid); + clif_skill_nodamage (src, bl, skillid, skilllv, 1); + + if (sd) + { //Crash-protection against Abracadabra casting pets + sd->skillitem = abra_skillid; + sd->skillitemlv = abra_skilllv; + sd->state.abra_flag = 1; + clif_item_skill (sd, abra_skillid, abra_skilllv, "Abracadabra"); + } else + { // [Skotlex] + struct unit_data *ud = unit_bl2ud(src); + int inf = skill_get_inf(abra_skillid); + int target_id = 0; + if (!ud) break; + if (inf&INF_SELF_SKILL || inf&INF_SUPPORT_SKILL) { + if (src->type == BL_PET) + bl = (struct block_list*)((TBL_PET*)src)->msd; + if (!bl) bl = src; + unit_skilluse_id(src, bl->id, abra_skillid, abra_skilllv); + } else { //Assume offensive skills + if (ud->target) + target_id = ud->target; + else switch (src->type) { + case BL_MOB: + target_id = ((TBL_MOB*)src)->target_id; + break; + case BL_PET: + target_id = ((TBL_PET*)src)->target_id; + break; + } + if (!target_id) + break; + if (skill_get_casttype(abra_skillid) == CAST_GROUND) { + bl = map_id2bl(target_id); + if (!bl) bl = src; + unit_skilluse_pos(src, bl->x, bl->y, abra_skillid, abra_skilllv); + } else + unit_skilluse_id(src, target_id, abra_skillid, abra_skilllv); + } + } + } + break; + + case SA_COMA: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time2(skillid,skilllv))); + break; + case SA_FULLRECOVERY: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (status_isimmune(bl)) + break; + status_percent_heal(bl, 100, 100); + break; + case SA_SUMMONMONSTER: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (sd) mob_once_spawn(sd,map[src->m].name,src->x,src->y,"--ja--",-1,1,""); + break; + case SA_LEVELUP: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (sd && pc_nextbaseexp(sd)) pc_gainexp(sd, pc_nextbaseexp(sd) * 10 / 100, 0); + break; + case SA_INSTANTDEATH: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + status_zap(bl,tstatus->hp-1,0); + break; + case SA_QUESTION: + case SA_GRAVITY: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + case SA_CLASSCHANGE: + { + //クラスチェンジ用ボスモンスタ?ID + static int changeclass[]={1038,1039,1046,1059,1086,1087,1112,1115 + ,1157,1159,1190,1272,1312,1373,1492}; + int class_ = mob_random_class (changeclass,sizeof(changeclass)/sizeof(changeclass[0])); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(dstmd) mob_class_change(dstmd,class_); + } + break; + case SA_MONOCELL: + { + static int poringclass[]={1002}; + int class_ = mob_random_class (poringclass,sizeof(poringclass)/sizeof(poringclass[0])); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(dstmd) mob_class_change(dstmd,class_); + } + break; + case SA_DEATH: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + status_kill(bl); + break; + case SA_REVERSEORCISH: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid, skilllv))); + break; + case SA_FORTUNE: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(sd) pc_getzeny(sd,status_get_lv(bl)*100); + break; + case SA_TAMINGMONSTER: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (sd && dstmd) { + for (i = 0; i < MAX_PET_DB; i++) { + if (dstmd->class_ == pet_db[i].class_) { + pet_catch_process1 (sd, dstmd->class_); + break; + } + } + } + break; + + case AL_INCAGI: /* 速度?加 */ + case AL_BLESSING: /* ブレッシング */ + case PR_SLOWPOISON: + case PR_IMPOSITIO: /* イムポシティオマヌス */ + case PR_LEXAETERNA: /* レックスエ?テルナ */ + case PR_SUFFRAGIUM: /* サフラギウム */ + case PR_BENEDICTIO: /* ?ケ??~福 */ + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + + case CR_PROVIDENCE: /* プ?ヴィデンス */ + if(sd && dstsd){ //Check they are not another crusader [Skotlex] + if ((dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + + case CG_MARIONETTE: /* マリオネットコント??ル */ + { + struct status_change *sc= status_get_sc(src); + int type2 = SC_MARIONETTE2; + + if(sc && tsc){ + if (sc->data[type].timer == -1 && tsc->data[type2].timer == -1) { + sc_start(src,type,100,bl->id,skill_get_time(skillid,skilllv)); + sc_start(bl,type2,100,src->id,skill_get_time(skillid,skilllv)); + clif_marionette(src, bl); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + else if (sc->data[type].timer != -1 && tsc->data[type2].timer != -1 && + sc->data[type].val1 == bl->id && tsc->data[type2].val1 == src->id) { + status_change_end(src, type, -1); + status_change_end(bl, type2, -1); + } + else { + if (sd) clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + } + } + break; + + case RG_CLOSECONFINE: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,type,100,skilllv,src->id,0,0,skill_get_time(skillid,skilllv))); + break; + case SA_FLAMELAUNCHER: // added failure chance and chance to break weapon if turned on [Valaris] + case SA_FROSTWEAPON: + case SA_LIGHTNINGLOADER: + case SA_SEISMICWEAPON: + if (dstsd) { + if(dstsd->status.weapon == W_FIST || + (dstsd->sc.count && dstsd->sc.data[type].timer == -1 && + ( //Allow re-enchanting to lenghten time. [Skotlex] + dstsd->sc.data[SC_FIREWEAPON].timer != -1 || + dstsd->sc.data[SC_WATERWEAPON].timer != -1 || + dstsd->sc.data[SC_WINDWEAPON].timer != -1 || + dstsd->sc.data[SC_EARTHWEAPON].timer != -1 || + dstsd->sc.data[SC_SHADOWWEAPON].timer != -1 || + dstsd->sc.data[SC_GHOSTWEAPON].timer != -1 || + dstsd->sc.data[SC_ENCPOISON].timer != -1 + )) + ) { + if (sd) clif_skill_fail(sd,skillid,0,0); + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + } + if (sd) { + int i = pc_search_inventory (sd, skill_db[skillid].itemid[0]); + if(i < 0 || sd->status.inventory[i].amount < skill_db[skillid].amount[0]) { + clif_skill_fail(sd,skillid,0,0); + break; + } + pc_delitem(sd, i, skill_db[skillid].amount[0], 0); + } + // 100% success rate at lv4 & 5, but lasts longer at lv5 + i = skilllv <4?(60+skilllv*10):100; + i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + if(!i) { + if (sd) clif_skill_fail(sd,skillid,0,0); + if (skill_break_equip(bl, EQP_WEAPON, 10000, BCT_PARTY) && + sd && sd != dstsd) + clif_displaymessage(sd->fd,"You broke target's weapon"); + } + clif_skill_nodamage(src,bl,skillid,skilllv,i); + break; + + case PR_ASPERSIO: /* アスペルシオ */ + if (sd && dstmd) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + + case TK_SEVENWIND: + switch(skilllv){ + case 1: + type=SC_EARTHWEAPON; + break; + case 2: + type=SC_WINDWEAPON; + break; + case 3: + type=SC_WATERWEAPON; + break; + case 4: + type=SC_FIREWEAPON; + break; + case 5: + type=SC_GHOSTWEAPON; + break; + case 6: + type=SC_SHADOWWEAPON; + break; + case 7: + type=SC_ASPERSIO; + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + + case PR_KYRIE: /* キリエエレイソン */ + clif_skill_nodamage(bl,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + //Passive Magnum, should had been casted on yourself. + case SM_MAGNUM: + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + //Initiate 10% of your damage becomes fire element. + clif_skill_nodamage (src,src,skillid,skilllv,1); + sc_start4(src,SC_WATK_ELEMENT,100,3,20,0,0,skill_get_time2(skillid, skilllv)); + if (sd) skill_blockpc_start (sd, skillid, skill_get_time(skillid, skilllv)); + break; + case LK_BERSERK: /* バ?サ?ク */ + case KN_AUTOCOUNTER: /* オ?トカウンタ? */ + case KN_TWOHANDQUICKEN: /* ツ?ハンドクイッケン */ + case KN_ONEHAND: + case CR_SPEARQUICKEN: /* スピアクイッケン */ + case CR_REFLECTSHIELD: + case AS_POISONREACT: /* ポイズンリアクト */ + case MC_LOUD: /* ラウドボイス */ + case MG_ENERGYCOAT: /* エナジ?コ?ト */ + case MG_SIGHT: /* サイト */ + case AL_RUWACH: /* ルアフ */ + case MO_EXPLOSIONSPIRITS: // 爆裂波動 + case MO_STEELBODY: // 金? + case MO_BLADESTOP: // 白?n取り + case LK_AURABLADE: /* オ?ラブレ?ド */ + case LK_PARRYING: /* パリイング */ + case LK_CONCENTRATION: /* コンセントレ?ション */ + case WS_CARTBOOST: /* カ?トブ?スト */ + case SN_SIGHT: /* トゥル?サイト */ + case WS_MELTDOWN: /* ?ルトダウン */ + case WS_OVERTHRUSTMAX: // Overthrust Max [Celest] + case ST_REJECTSWORD: /* リジェクトソ?ド */ + case HW_MAGICPOWER: /* 魔法力?? */ + case PF_MEMORIZE: /* ?モライズ */ + case PA_SACRIFICE: + case ASC_EDP: // [Celest] + case NPC_STOP: + case WZ_SIGHTBLASTER: + case SG_SUN_COMFORT: + case SG_MOON_COMFORT: + case SG_STAR_COMFORT: + case NPC_HALLUCINATION: + case HP_ASSUMPTIO: + case GS_MADNESSCANCEL: + case GS_ADJUSTMENT: + case GS_INCREASING: + case GS_CRACKER: + case GS_GROUNDDRIFT: + case NJ_TATAMIGAESHI: + case NJ_KASUMIKIRI: + case NJ_UTSUSEMI: + case NJ_BUNSINJYUTSU: + case NJ_NEN: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + + + if(sd->spiritball >= 4 && sd->sc.data[SC_ADJUSTMENT].timer!=-1) + status_change_end(&sd->bl,SC_ADJUSTMENT,-1); + case SG_SUN_WARM: + case SG_MOON_WARM: + case SG_STAR_WARM: + { + struct skill_unit_group *sg; + if (!tsc) break; + sg = skill_unitsetting(bl,skillid,skilllv,src->x,src->y,0); + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,type,100,skilllv,0,0,(int)sg,skill_get_time(skillid,skilllv))); + break; + } + + case CG_MOONLIT: /* 月明りの泉に落ちる花びら */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if (sd && battle_config.player_skill_partner_check && + (!battle_config.gm_skilluncond || pc_isGM(sd) < battle_config.gm_skilluncond)) { + skill_check_pc_partner(sd, skillid, &skilllv, 1, 1); + } else + skill_moonlit(bl, NULL, skilllv); //The knockback must be invoked before starting the effect which places down the map cells. [Skotlex] + + break; +/* Was modified to only affect targetted char. [Skotlex] + case HP_ASSUMPTIO: + if (flag&1) + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + else + { + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_PC, + src, skillid, skilllv, tick, flag|BCT_ALL|1, + skill_castend_nodamage_id); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; +*/ + case SM_ENDURE: /* インデュア */ + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + if (sd) + skill_blockpc_start (sd, skillid, skill_get_time2(skillid,skilllv)); + break; + + case AS_ENCHANTPOISON: // Prevent spamming [Valaris] + if (sd && dstsd && dstsd->sc.count) { + if(dstsd->sc.data[SC_FIREWEAPON].timer != -1 || + dstsd->sc.data[SC_WATERWEAPON].timer != -1 || + dstsd->sc.data[SC_WINDWEAPON].timer != -1 || + dstsd->sc.data[SC_EARTHWEAPON].timer != -1 || + dstsd->sc.data[SC_SHADOWWEAPON].timer != -1 || + dstsd->sc.data[SC_GHOSTWEAPON].timer != -1 + // dstsd->sc.data[SC_ENCPOISON].timer != -1 //People say you should be able to recast to lengthen the timer. [Skotlex] + ) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + clif_skill_fail(sd,skillid,0,0); + break; + } + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + break; + + case LK_TENSIONRELAX: /* テンションリラックス */ + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,type,100,skilllv,0,0,skill_get_time2(skillid,skilllv), + skill_get_time(skillid,skilllv))); + break; + + case MC_CHANGECART: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + case TK_MISSION: + if (sd) { + int id; + if (sd->mission_mobid && (sd->mission_count || rand()%100)) { //Cannot change target when already have one + clif_mission_mob(sd, sd->mission_mobid, sd->mission_count); + clif_skill_fail(sd,skillid,0,0); + break; + } + id = mob_get_random_id(0,0, sd->status.base_level); + if (!id) { + clif_skill_fail(sd,skillid,0,0); + break; + } + sd->mission_mobid = id; + sd->mission_count = 0; + pc_setglobalreg(sd,"TK_MISSION_ID", id); + clif_mission_mob(sd, id, 0); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case AC_CONCENTRATION: /* ?W中力向? */ + { + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + map_foreachinrange( status_change_timer_sub, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + src,status_get_sc(src),type,tick); + } + break; + + case SM_PROVOKE: /* プ?ボック */ + /* MVPmobと不死には?かない */ + if((tstatus->mode&MD_BOSS) || battle_check_undead(tstatus->race,tstatus->def_ele)) { //不死には?かない + map_freeblock_unlock(); + return 1; + } + //TODO: How much does base level affects? Dummy value of 1% per level difference used. [Skotlex] + clif_skill_nodamage(src,bl,skillid,skilllv, + (i=sc_start(bl,type, + 50 +3*skilllv +status_get_lv(src) -status_get_lv(bl), + skilllv,skill_get_time(skillid,skilllv)))); + if (!i) + { + if (sd) + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 0; + } + unit_skillcastcancel(bl, 2); + + if(tsc && tsc->count){ + if(tsc->data[SC_FREEZE].timer!=-1) + status_change_end(bl,SC_FREEZE,-1); + if(tsc->data[SC_STONE].timer!=-1 && tsc->data[SC_STONE].val2==0) + status_change_end(bl,SC_STONE,-1); + if(tsc->data[SC_SLEEP].timer!=-1) + status_change_end(bl,SC_SLEEP,-1); + } + + if(dstmd) { + dstmd->state.provoke_flag = src->id; + mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv)); + } + break; + + case CR_DEVOTION: /* ディボ?ション */ + if(sd && dstsd) + { + //??カや養子の??の元の?E業を算?oする + + int lv = sd->status.base_level - dstsd->status.base_level; + if (lv < 0) lv = -lv; + if (lv > battle_config.devotion_level_difference || + (dstsd->sc.data[type].timer != -1 && dstsd->sc.data[type].val1 != src->id) || //Avoid overriding [Skotlex] + (dstsd->class_&MAPID_UPPERMASK) == MAPID_CRUSADER) { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + //Look for an empty slot (or reuse in case you cast it twice in the same char. [Skotlex] + for (i = 0; i < skilllv && i < 5 && sd->devotion[i]!=bl->id && sd->devotion[i]; i++); + if (i == skilllv) + { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + sd->devotion[i] = bl->id; + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,type,100,src->id,i,skill_get_range2(src,skillid,skilllv),skill_get_time2(skillid, skilllv),1000)); + clif_devotion(sd); + } + else + if (sd) + clif_skill_fail(sd,skillid,0,0); + break; + + case MO_CALLSPIRITS: // ?功 + if(sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + pc_addspiritball(sd,skill_get_time(skillid,skilllv),skilllv); + } + break; + + case CH_SOULCOLLECT: // 狂?功 + if(sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + for (i = 0; i < 5; i++) + pc_addspiritball(sd,skill_get_time(skillid,skilllv),5); + } + break; + + case MO_KITRANSLATION: + if(dstsd && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) { + pc_addspiritball(dstsd,skill_get_time(skillid,skilllv),5); + } + break; + + case TK_TURNKICK: + case MO_BALKYOUNG: //Passive part of the attack. Splash knock-back+stun. [Skotlex] + if (skill_area_temp[1] != bl->id) { + skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)); + skill_additional_effect(src,bl,skillid,skilllv,BF_MISC,tick); //Use Misc rather than weapon to signal passive pushback + } + break; + + case MO_ABSORBSPIRITS: // ?奪 + i = 0; + if (dstsd && dstsd->spiritball && (sd == dstsd || map_flag_vs(src->m)) && (dstsd->class_&MAPID_BASEMASK)!=MAPID_GUNSLINGER) + { // split the if for readability, and included gunslingers in the check so that their coins cannot be removed [Reddozen] + i = dstsd->spiritball * 10; + pc_delspiritball(dstsd,dstsd->spiritball,0); + } else if (dstmd && !(tstatus->mode&MD_BOSS) && rand() % 100 < 20) + { // check if target is a monster and not a Boss, for the 20% chance to absorb 2 SP per monster's level [Reddozen] + i = 2 * dstmd->db->lv; + mob_target(dstmd,src,0); + } + if (sd){ + if (i > 0x7FFF) + i = 0x7FFF; + if (sd->status.sp + i > sd->status.max_sp) + i = sd->status.max_sp - sd->status.sp; + if (i) { + sd->status.sp += i; + clif_heal(sd->fd,SP_SP,i); + } + } + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + + case AC_MAKINGARROW: /* 矢??ャ */ + if(sd) { + clif_arrow_create_list(sd); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case AM_PHARMACY: /* ポ?ション??ャ */ + if(sd) { + clif_skill_produce_mix_list(sd,22); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case SA_CREATECON: + if(sd) { + clif_skill_produce_mix_list(sd,23); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case BS_HAMMERFALL: /* ハンマ?フォ?ル */ + if(dstsd && dstsd->special_state.no_weapon_damage) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,SC_STUN,(20 + 10 * skilllv),skilllv,skill_get_time2(skillid,skilllv))); + break; + case RG_RAID: /* サプライズアタック */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + status_change_end(src, SC_HIDING, -1); // ハイディング解? + break; + + case ASC_METEORASSAULT: /* ?テオアサルト */ + case GS_SPREADATTACK: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + break; + + case KN_BRANDISHSPEAR: /*ブランディッシュスピア*/ + { + int c,n=4,ar; + int dir = map_calc_dir(src,bl->x,bl->y); + struct square tc; + int x=bl->x,y=bl->y; + ar=skilllv/3; + skill_brandishspear_first(&tc,dir,x,y); + skill_brandishspear_dir(&tc,dir,4); + /* 範?C */ + if(skilllv == 10){ + for(c=1;c<4;c++){ + map_foreachincell(skill_area_sub, + bl->m,tc.val1[c],tc.val2[c],BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|n, + skill_castend_damage_id); + } + } + /* 範?BA */ + if(skilllv > 6){ + skill_brandishspear_dir(&tc,dir,-1); + n--; + }else{ + skill_brandishspear_dir(&tc,dir,-2); + n-=2; + } + + if(skilllv > 3){ + for(c=0;c<5;c++){ + map_foreachincell(skill_area_sub, + bl->m,tc.val1[c],tc.val2[c],BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|n, + skill_castend_damage_id); + if(skilllv > 6 && n==3 && c==4){ + skill_brandishspear_dir(&tc,dir,-1); + n--;c=-1; + } + } + } + /* 範?@ */ + for(c=0;c<10;c++){ + if(c==0||c==5) skill_brandishspear_dir(&tc,dir,-1); + map_foreachincell(skill_area_sub, + bl->m,tc.val1[c%5],tc.val2[c%5],BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + } + } + break; + + case WZ_SIGHTRASHER: + //Passive side of the attack. + status_change_end(src,SC_SIGHT,-1); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub,src, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + break; + + case WZ_FROSTNOVA: + map_foreachinrange(skill_attack_area, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + BF_MAGIC, src, src, skillid, skilllv, tick, flag, BCT_ENEMY); + break; + + case NPC_SELFDESTRUCTION: + clif_skill_nodamage(src, src, skillid, -1, 1); + map_foreachinrange(skill_area_sub, bl, + skill_get_splash(skillid, skilllv), BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY, + skill_castend_damage_id); + status_damage(src, src, sstatus->max_hp,0,0,1); + break; + + /* パ?ティスキル */ + case AL_ANGELUS: /* エンジェラス */ + case PR_MAGNIFICAT: /* マグニフィカ?ト */ + case PR_GLORIA: /* グ?リア */ + case SN_WINDWALK: /* ウインドウォ?ク */ + if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { + clif_skill_nodamage(bl,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + } else if (sd) { + /* パ?ティ全?への?? */ + party_foreachsamemap (skill_area_sub, + sd,skill_get_splash(skillid, skilllv), + src,skillid,skilllv,tick, flag|BCT_PARTY|1, + skill_castend_nodamage_id); + } + break; + + case BS_ADRENALINE: /* アドレナリンラッシュ */ + case BS_ADRENALINE2: + case BS_WEAPONPERFECT: /* ウェポンパ?フェクション */ + case BS_OVERTHRUST: /* オ?バ?トラスト */ + if (sd == NULL || sd->status.party_id == 0 || (flag & 1)) { + /* 個別の?? */ + clif_skill_nodamage(bl,bl,skillid,skilllv, + sc_start4(bl,type,100,skilllv,(src == bl)? 1:0,0,0,skill_get_time(skillid,skilllv))); + } else if (sd) { + /* パ?ティ全?への?? */ + party_foreachsamemap(skill_area_sub, + sd,skill_get_splash(skillid, skilllv), + src,skillid,skilllv,tick, flag|BCT_PARTY|1, + skill_castend_nodamage_id); + } + break; + + case BS_MAXIMIZE: + case NV_TRICKDEAD: + case CR_DEFENDER: + case CR_AUTOGUARD: + case TK_READYSTORM: + case TK_READYDOWN: + case TK_READYTURN: + case TK_READYCOUNTER: + case TK_DODGE: + case CR_SHRINK: + case ST_PRESERVE: + case SG_FUSION: + case GS_GATLINGFEVER: + if (tsc && tsc->data[type].timer != -1) + i = status_change_end(bl, type, -1); + else + i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv,i); + break; + case SL_KAITE: + case SL_KAAHI: + case SL_KAIZEL: + case SL_KAUPE: + if (sd) { + if (!dstsd || !( + (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_SOULLINKER) || + (dstsd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER || + dstsd->char_id == sd->char_id || + dstsd->char_id == sd->status.partner_id || + dstsd->char_id == sd->status.child + )) { + status_change_start(src,SC_STUN,10000,skilllv,0,0,0,500,8); + clif_skill_fail(sd,skillid,0,0); + break; + } + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid, skilllv))); + break; + case SM_AUTOBERSERK: // Celest + if (tsc && tsc->data[type].timer != -1) + i = status_change_end(bl, type, -1); + else + i = sc_start(bl,type,100,skilllv,60000); + clif_skill_nodamage(src,bl,skillid,skilllv,i); + break; + case TF_HIDING: /* ハイディング */ + case ST_CHASEWALK: /* ハイディング */ + if (tsc && tsc->data[type].timer != -1) + i = status_change_end(bl, type, -1); + else + i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,-1,i); // Don't display the skill name as it is a hiding skill + break; + case TK_RUN: + if (tsc && tsc->data[type].timer != -1) + i = status_change_end(bl, type, -1); + else + i = sc_start4(bl,type,100,skilllv,unit_getdir(bl),0,0,0); +// If the client receives a skill-use packet inmediately before +// a walkok packet, it will discard the walk packet! [Skotlex] +// clif_skill_nodamage(src,bl,skillid,skilllv,i); + break; + case AS_CLOAKING: /* ク??キング */ + if(tsc && tsc->data[type].timer!=-1 ) + /* 解?怩キる */ + i = status_change_end(bl, type, -1); + else + i = sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,-1,i); + if (!i && sd) + clif_skill_fail(sd,skillid,0,0); + break; + + /* ?地スキル */ + case BD_LULLABY: /* 子守唄 */ + case BD_RICHMANKIM: /* ニヨルドの宴 */ + case BD_ETERNALCHAOS: /* 永遠の?ャ沌 */ + case BD_DRUMBATTLEFIELD: /* ?太鼓の響き */ + case BD_RINGNIBELUNGEN: /* ニ?ベルングの指輪 */ + case BD_ROKISWEIL: /* ?キの叫び */ + case BD_INTOABYSS: /* ?[淵の中に */ + case BD_SIEGFRIED: /* 不死?gのジ?クフリ?ド */ + case BA_DISSONANCE: /* 不協和音 */ + case BA_POEMBRAGI: /* ブラギの? */ + case BA_WHISTLE: /* 口笛 */ + case BA_ASSASSINCROSS: /* 夕陽のアサシンク?ス */ + case BA_APPLEIDUN: /* イドゥンの林檎 */ + case DC_UGLYDANCE: /* 自分?沁閧ネダンス */ + case DC_HUMMING: /* ハミング */ + case DC_DONTFORGETME: /* 私を忘れないで?c */ + case DC_FORTUNEKISS: /* ?K運のキス */ + case DC_SERVICEFORYOU: /* サ?ビスフォ?ユ? */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); + break; + + case HP_BASILICA: /* バジリカ */ + case CG_HERMODE: // Wand of Hermod + { + struct skill_unit_group *sg; + unit_stop_walking(src,1); + skill_clear_unitgroup(src); + sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); + if(skillid == CG_HERMODE) + i = sc_start4(src,SC_DANCING,100, + skillid,0,0,sg->group_id,skill_get_time(skillid,skilllv)); + else + i = sc_start4(src,type,100, + skilllv,0,BCT_SELF,sg->group_id, + skill_get_time(skillid,skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv,i); + } + break; + + case PA_GOSPEL: /* ゴスペル */ + if (!tsc) break; + if (tsc->data[type].timer != -1 && tsc->data[type].val4 == BCT_SELF) { + i = status_change_end(bl,SC_GOSPEL,-1); + } else { + struct skill_unit_group *sg = skill_unitsetting(src,skillid,skilllv,src->x,src->y,0); + if (tsc->data[type].timer != -1) + status_change_end(bl,type,-1); //Was under someone else's Gospel. [Skotlex] + i = sc_start4(bl,type,100,skilllv,0,(int)sg,BCT_SELF,skill_get_time(skillid,skilllv)); + } + clif_skill_nodamage(src,bl,skillid,skilllv,i); + break; + + case BD_ADAPTATION: /* アドリブ */ + if(tsc && tsc->data[SC_DANCING].timer!=-1){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_stop_dancing(bl); + } + break; + + case BA_FROSTJOKE: /* 寒いジョ?ク */ + case DC_SCREAM: /* スクリ?ム */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_addtimerskill(src,tick+2000,bl->id,src->x,src->y,skillid,skilllv,0,flag); + if (md) { // Mobは?れないから?Aスキル名を叫ばせてみる + char temp[128]; + if (strlen(md->name) + strlen(skill_db[skillid].desc) > 120) + break; //Message won't fit on buffer. [Skotlex] + sprintf(temp,"%s : %s !!",md->name,skill_db[skillid].desc); + clif_message(&md->bl,temp); + } + break; + + + case BA_PANGVOICE://パンボイス + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,SC_CONFUSION,50,7,skill_get_time(skillid,skilllv))); + break; + + case DC_WINKCHARM://魅惑のウィンク + if(dstsd){ + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,SC_CONFUSION,30,7,skill_get_time2(skillid,skilllv))); + }else if(dstmd) + { + if(status_get_lv(src)>status_get_lv(bl) && (tstatus->race == RC_DEMON || tstatus->race == RC_DEMIHUMAN || tstatus->race == RC_ANGEL)) { + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,70,skilllv,skill_get_time(skillid,skilllv))); + } else{ + clif_skill_nodamage(src,bl,skillid,skilllv,0); + if(sd) clif_skill_fail(sd,skillid,0,0); + } + } + break; + + case TF_STEAL: // スティ?ル + if(sd && dstmd) { + if(pc_steal_item(sd,bl)) + clif_skill_nodamage(src,bl,skillid,skilllv,1); + else + clif_skill_fail(sd,skillid,0x0a,0); + } + break; + + case RG_STEALCOIN: // スティ?ルコイン + if(sd) { + if(pc_steal_coin(sd,bl)) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv)); + } + else + clif_skill_fail(sd,skillid,0,0); + } + break; + + case MG_STONECURSE: /* スト?ンカ?ス */ + { + if (tstatus->mode&MD_BOSS) { + if (sd) clif_skill_fail(sd,skillid,0,0); + break; + } + if(status_isimmune(bl) || !tsc) + break; + if (dstmd) + mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv)); + + if (tsc->data[SC_STONE].timer != -1) { + status_change_end(bl,SC_STONE,-1); + if (sd) clif_skill_fail(sd,skillid,0,0); + break; + } + if (sc_start(bl,SC_STONE,(skilllv*4+20),skilllv,skill_get_time2(skillid,skilllv))) + clif_skill_nodamage(src,bl,skillid,skilllv,1); + else if(sd) { + clif_skill_fail(sd,skillid,0,0); + // Level 6-10 doesn't consume a red gem if it fails [celest] + if (skilllv > 5) break; + } + if (sd) { + if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_WIZARD) + break; //Do not delete the gemstone. + if ((i=pc_search_inventory(sd, skill_db[skillid].itemid[0])) >= 0 ) + pc_delitem(sd, i, skill_db[skillid].amount[0], 0); + } + } + break; + + case NV_FIRSTAID: /* ?急手? */ + clif_skill_nodamage(src,bl,skillid,5,1); + status_heal(bl,5,0,0); + break; + + case AL_CURE: /* キュア? */ + if(status_isimmune(bl)) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + status_change_end(bl, SC_SILENCE , -1 ); + status_change_end(bl, SC_BLIND , -1 ); + status_change_end(bl, SC_CONFUSION, -1 ); + if(battle_check_undead(tstatus->race,tstatus->def_ele)) + sc_start(bl, SC_CONFUSION,100,1,skill_get_time2(skillid, skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + case TF_DETOXIFY: /* 解毒 */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + status_change_end(bl, SC_POISON , -1 ); + status_change_end(bl, SC_DPOISON , -1 ); + break; + + case PR_STRECOVERY: /* リカバリ? */ + if(status_isimmune(bl)) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + status_change_end(bl, SC_FREEZE , -1 ); + status_change_end(bl, SC_STONE , -1 ); + status_change_end(bl, SC_SLEEP , -1 ); + status_change_end(bl, SC_STUN , -1 ); + //Is this equation really right? It looks so... special. + if(battle_check_undead(tstatus->race,tstatus->def_ele) ) + { + status_change_start(bl, SC_BLIND, + 100*(100-(tstatus->int_/2+tstatus->vit/3+tstatus->luk/10)), + 1,0,0,0, + skill_get_time2(skillid, skilllv) * (100-(tstatus->int_+tstatus->vit)/2)/100,10); + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(dstmd) + mob_unlocktarget(dstmd,tick); + break; + + case WZ_ESTIMATION: /* モンスタ??報 */ + if(sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + clif_skill_estimation((struct map_session_data *)src,bl); + } + break; + + case BS_REPAIRWEAPON: /* ?器?C? */ + if(sd && dstsd) + clif_item_repair_list(sd,dstsd); + break; + + case MC_IDENTIFY: /* アイテム鑑定 */ + if(sd) + clif_item_identify_list(sd); + break; + + // Weapon Refining [Celest] + case WS_WEAPONREFINE: + if(sd) + clif_item_refine_list(sd); + break; + + case MC_VENDING: /* 露店開?ン */ + if(sd) + { //Prevent vending of GMs with unnecessary Level to trade/drop. [Skotlex] + if ( pc_can_give_items(pc_isGM(sd)) ) + clif_skill_fail(sd,skillid,0,0); + else + clif_openvendingreq(sd,2+skilllv); + } + break; + + case AL_TELEPORT: /* テレポ?ト */ + if(sd) { + if (map[bl->m].flag.noteleport) { /* テレポ禁止 */ + clif_skill_teleportmessage(sd,0); + break; + } + if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza] + clif_displaymessage(sd->fd, "Duel: Can't use teleport in duel."); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(skilllv == 1) { + // possibility to skip menu [LuzZza] + if(!battle_config.skip_teleport_lv1_menu && + sd->skillitem != AL_TELEPORT) //If skillid is not teleport, this was auto-casted! [Skotlex] + clif_skill_warppoint(sd,skillid,skilllv,"Random","","",""); + else + pc_randomwarp(sd,3); + } else { + if (sd->skillitem != AL_TELEPORT) + clif_skill_warppoint(sd,skillid,skilllv,"Random", + mapindex_id2name(sd->status.save_point.map),"",""); + else //Autocasted Teleport level 2?? + pc_setpos(sd,sd->status.save_point.map, + sd->status.save_point.x,sd->status.save_point.y,3); + } + } else + unit_warp(bl,-1,-1,-1,3); + break; + + case AL_HOLYWATER: /* アクアベネディクタ */ + if(sd) { + if (skill_produce_mix(sd, skillid, 523, 0, 0, 0, 1)) + clif_skill_nodamage(src,bl,skillid,skilllv,1); + else + clif_skill_fail(sd,skillid,0,0); + } + break; + + case TF_PICKSTONE: + if(sd) { + int eflag; + struct item item_tmp; + struct block_list tbl; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + memset(&item_tmp,0,sizeof(item_tmp)); + memset(&tbl,0,sizeof(tbl)); // [MouseJstr] + item_tmp.nameid = 7049; + item_tmp.identify = 1; + tbl.id = 0; + clif_takeitem(&sd->bl,&tbl); + eflag = pc_additem(sd,&item_tmp,1); + if(eflag) { + clif_additem(sd,0,0,eflag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + break; + case ASC_CDP: + if(sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_produce_mix(sd, skillid, 678, 0, 0, 0, 1); //Produce a Deadly Poison Bottle. + } + break; + + case RG_STRIPWEAPON: /* ストリップウェポン */ + case RG_STRIPSHIELD: /* ストリップシ?[ルド */ + case RG_STRIPARMOR: /* ストリップア?[マ?[ */ + case RG_STRIPHELM: /* ストリップヘルム */ + case ST_FULLSTRIP: // Rewritten most of the code [DracoRPG] + case GS_DISARM: // Added disarm. [Reddozen] + { + int strip_fix, equip = 0; + int sclist[4] = {0,0,0,0}; + + if (skillid == RG_STRIPWEAPON || skillid == ST_FULLSTRIP || skillid == GS_DISARM) + equip |= EQP_WEAPON; + if (skillid == RG_STRIPSHIELD || skillid == ST_FULLSTRIP) + equip |= EQP_SHIELD; + if (skillid == RG_STRIPARMOR || skillid == ST_FULLSTRIP) + equip |= EQP_ARMOR; + if (skillid == RG_STRIPHELM || skillid == ST_FULLSTRIP) + equip |= EQP_HELM; + + strip_fix = sstatus->dex - tstatus->dex; + if(strip_fix < 0) + strip_fix=0; + if (rand()%100 >= 5+2*skilllv+strip_fix/5) + { + if (sd) + clif_skill_fail(sd,skillid,0,0); + break; + } + if (dstsd) { + for (i=0;i<11;i++) { + if (dstsd->equip_index[i]<0 || !dstsd->inventory_data[dstsd->equip_index[i]]) + continue; + switch (i) { + case 8: //Shield / left-hand weapon + if(dstsd->inventory_data[dstsd->equip_index[8]]->type == 5) + { //Shield + if (equip&EQP_SHIELD && + !(dstsd->unstripable_equip&EQP_SHIELD) && + !(tsc && tsc->data[SC_CP_SHIELD].timer != -1) + ){ + sclist[1] = SC_STRIPSHIELD; // Okay, we found a shield to strip - It is really a shield, not a two-handed weapon or a left-hand weapon + pc_unequipitem(dstsd,dstsd->equip_index[i],3); + } + continue; + } + //Continue to weapon + case 9: + if (equip &EQP_WEAPON && + !(dstsd->unstripable_equip&EQP_WEAPON) && + !(tsc && tsc->data[SC_CP_WEAPON].timer != -1) + ) { + sclist[0] = SC_STRIPWEAPON; // Okay, we found a weapon to strip - It can be a right-hand, left-hand or two-handed weapon + pc_unequipitem(dstsd,dstsd->equip_index[i],3); + } + break; + case 7: //Armor + if (equip &EQP_ARMOR && + !(dstsd->unstripable_equip &EQP_ARMOR) && + !(tsc && tsc->data[SC_CP_ARMOR].timer != -1) + ) { + sclist[2] = SC_STRIPARMOR; // Okay, we found an armor to strip + pc_unequipitem(dstsd,dstsd->equip_index[i],3); + } + break; + case 6: //Helm + if (equip &EQP_HELM && + !(dstsd->unstripable_equip &EQP_HELM) && + !(tsc && tsc->data[SC_CP_HELM].timer != -1) + ) { + sclist[3] = SC_STRIPHELM; // Okay, we found a helm to strip + pc_unequipitem(dstsd,dstsd->equip_index[i],3); + } + break; + } + } + } else if (!(tstatus->mode&MD_BOSS)) { + if (equip&EQP_WEAPON && !(tsc && tsc->data[SC_CP_WEAPON].timer != -1)) + sclist[0] = SC_STRIPWEAPON; + if (equip&EQP_SHIELD && !(tsc && tsc->data[SC_CP_SHIELD].timer != -1)) + sclist[1] = SC_STRIPSHIELD; + if (equip&EQP_ARMOR && !(tsc && tsc->data[SC_CP_ARMOR].timer != -1)) + sclist[2] = SC_STRIPARMOR; + if (equip&EQP_HELM && !(tsc && tsc->data[SC_CP_HELM].timer != -1)) + sclist[3] = SC_STRIPHELM; + } + equip = 0; //Reuse equip to hold how many stats are invoked. + for (i=0;i<4;i++) { + if (sclist[i]) // Start the SC only if an equipment was stripped from this location + equip+=sc_start(bl,sclist[i],100,skilllv,skill_get_time(skillid,skilllv)+strip_fix/2); + } + if (equip) + clif_skill_nodamage(src,bl,skillid,skilllv,1); + else if (sd) //Nothing stripped. + clif_skill_fail(sd,skillid,0,0); + break; + } + + /* PotionPitcher */ + case AM_BERSERKPITCHER: + case AM_POTIONPITCHER: /* ポ?ションピッチャ? */ + { + int i,x,hp = 0,sp = 0,bonus=100; + if(sd) { + x = skilllv%11 - 1; + i = pc_search_inventory(sd,skill_db[skillid].itemid[x]); + if(i < 0 || skill_db[skillid].itemid[x] <= 0) { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + if(sd->inventory_data[i] == NULL || sd->status.inventory[i].amount < skill_db[skillid].amount[x]) { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + if(skillid == AM_BERSERKPITCHER) { //Does not override use-level, and cannot be used on bows. + if (dstsd && (dstsd->status.base_level<(unsigned int)sd->inventory_data[i]->elv || dstsd->weapontype1 == W_BOW)) { + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 1; + } + } + potion_flag = 1; + potion_hp = potion_sp = potion_per_hp = potion_per_sp = 0; + potion_target = bl->id; + run_script(sd->inventory_data[i]->script,0,sd->bl.id,0); + pc_delitem(sd,i,skill_db[skillid].amount[x],0); + potion_flag = potion_target = 0; + if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_ALCHEMIST) + bonus += sd->status.base_level; + if(potion_per_hp > 0 || potion_per_sp > 0) { + hp = tstatus->max_hp * potion_per_hp / 100; + hp = hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; + if(dstsd) { + sp = dstsd->status.max_sp * potion_per_sp / 100; + sp = sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; + } + } + else { + if(potion_hp > 0) { + hp = potion_hp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; + hp = hp * (100 + (tstatus->vit<<1)) / 100; + if(dstsd) + hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; + } + if(potion_sp > 0) { + sp = potion_sp * (100 + pc_checkskill(sd,AM_POTIONPITCHER)*10 + pc_checkskill(sd,AM_LEARNINGPOTION)*5)*bonus/10000; + sp = sp * (100 + (tstatus->int_<<1)) / 100; + if(dstsd) + sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10) / 100; + } + } + } + else { + hp = (1 + rand()%400) * (100 + skilllv*10) / 100; + hp = hp * (100 + (tstatus->vit<<1)) / 100; + if(dstsd) + hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10) / 100; + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(hp > 0 || (skillid == AM_POTIONPITCHER && hp <= 0 && sp <= 0)) + clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); + if(sp > 0) + clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1); + status_heal(bl,hp,sp,0); + } + break; + case AM_CP_WEAPON: + case AM_CP_SHIELD: + case AM_CP_ARMOR: + case AM_CP_HELM: + { + int scid = SC_STRIPWEAPON + (skillid - AM_CP_WEAPON); + if(tsc && tsc->data[scid].timer != -1) + status_change_end(bl, scid, -1 ); + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + } + break; + case AM_TWILIGHT1: + if (sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + //Prepare 200 White Potions. + if (!skill_produce_mix(sd, skillid, 504, 0, 0, 0, 200)) + clif_skill_fail(sd,skillid,0,0); + } + break; + case AM_TWILIGHT2: + if (sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + //Prepare 200 Slim White Potions. + if (!skill_produce_mix(sd, skillid, 547, 0, 0, 0, 200)) + clif_skill_fail(sd,skillid,0,0); + } + break; + case AM_TWILIGHT3: + if (sd) { + //check if you can produce all three, if not, then fail: + if (!skill_can_produce_mix(sd,970,-1, 100) //100 Alcohol + || !skill_can_produce_mix(sd,7136,-1, 50) //50 Acid Bottle + || !skill_can_produce_mix(sd,7135,-1, 50) //50 Flame Bottle + ) { + clif_skill_fail(sd,skillid,0,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_produce_mix(sd, skillid, 970, 0, 0, 0, 100); + skill_produce_mix(sd, skillid, 7136, 0, 0, 0, 50); + skill_produce_mix(sd, skillid, 7135, 0, 0, 0, 50); + } + break; + case SA_DISPELL: /* ディスペル */ + { + int i; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + i = status_get_sc_def_mdef(bl); + if (i >= 10000 || + tsc == NULL || (tsc->data[SC_SPIRIT].timer != -1 && tsc->data[SC_SPIRIT].val2 == SL_ROGUE) || //Rogue's spirit defends againt dispel. + //Fixed & changed to use a proportionnal reduction (no info, but seems far more logical) [DracoRPG] + rand()%10000 >= (10000-i)*(50+10*skilllv)/100) + { + if (sd) + clif_skill_fail(sd,skillid,0,0); + break; + } + if(status_isimmune(bl) || !tsc->count) + break; + for(i=0;idata[i].timer == -1) + continue; + if(i==SC_HALLUCINATION || i==SC_WEIGHT50 || i==SC_WEIGHT90 + || i==SC_STRIPWEAPON || i==SC_STRIPSHIELD || i==SC_STRIPARMOR || i==SC_STRIPHELM + || i==SC_CP_WEAPON || i==SC_CP_SHIELD || i==SC_CP_ARMOR || i==SC_CP_HELM + || i==SC_COMBO || i==SC_DANCING || i==SC_GUILDAURA || i==SC_EDP + || i==SC_AUTOBERSERK || i==SC_CARTBOOST || i==SC_MELTDOWN || i==SC_MOONLIT + || i==SC_SAFETYWALL || i==SC_SMA + ) + continue; + if(i==SC_BERSERK) tsc->data[i].val4=1; //Mark a dispelled berserk to avoid setting hp to 100. + status_change_end(bl,i,-1); + } + } + break; + + case TF_BACKSLIDING: //This is the correct implementation as per packet logging information. [Skotlex] + clif_skill_nodamage(src,bl,skillid,skilllv,1); + skill_blown(src,bl,skill_get_blewcount(skillid,skilllv)|0x10000); + break; + + case TK_HIGHJUMP: + { + int x,y, dir = unit_getdir(src); + + x = src->x + dirx[dir]*skilllv*2; + y = src->y + diry[dir]*skilllv*2; + + clif_skill_nodamage(src,bl,TK_HIGHJUMP,skilllv,1); + if(map_getcell(src->m,x,y,CELL_CHKPASS)) { + unit_movepos(src, x, y, 1, 0); + clif_slide(src,x,y); + } + } + break; + + case SA_CASTCANCEL: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + unit_skillcastcancel(src,1); + if(sd) { + int sp = skill_get_sp(sd->skillid_old,sd->skilllv_old); + sp = sp * (90 - (skilllv-1)*20) / 100; + if(sp < 0) sp = 0; + status_zap(src, 0, sp); + } + break; + case SA_SPELLBREAKER: // スペルブレイカ? + { + int sp; + if(tsc && tsc->data[SC_MAGICROD].timer != -1) { + sp = skill_get_sp(skillid,skilllv); + sp = sp * tsc->data[SC_MAGICROD].val2 / 100; + if(sp < 1) sp = 1; + status_heal(bl,0,sp,2); + clif_skill_nodamage(bl,bl,SA_MAGICROD,tsc->data[SC_MAGICROD].val1,1); + status_percent_damage(bl, src, 0, -20); //20% max SP damage. + } else { + struct unit_data *ud = unit_bl2ud(bl); + int bl_skillid=0,bl_skilllv=0,hp = 0; + if (!ud || ud->skilltimer == -1) break; //Nothing to cancel. + bl_skillid = ud->skillid; + bl_skilllv = ud->skilllv; + if (tstatus->mode & MD_BOSS) + { //Only 10% success chance against bosses. [Skotlex] + if (rand()%100 < 90) + { + if (sd) clif_skill_fail(sd,skillid,0,0); + break; + } + } else if (!dstsd || map_flag_vs(bl->m)) //HP damage only on pvp-maps when against players. + hp = tstatus->max_hp/50; //Recover 2% HP [Skotlex] + + clif_skill_nodamage(src,bl,skillid,skilllv,1); + unit_skillcastcancel(bl,0); + sp = skill_get_sp(bl_skillid,bl_skilllv); + status_zap(bl, hp, sp); + + if (hp && skilllv >= 5) + hp>>=1; //Recover half damaged HP at level 5 [Skotlex] + else + hp = 0; + + if (skilllv > 1 && sp) //Recover some of the SP used + sp = sp*(25*(skilllv-1))/100; + else + sp = 0; + + if(hp || sp) + status_heal(src, hp, sp, 2); + } + } + break; + case SA_MAGICROD: + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + break; + case SA_AUTOSPELL: /* オ?トスペル */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(sd) + clif_autospell(sd,skilllv); + else { + int maxlv=1,spellid=0; + static const int spellarray[3] = { MG_COLDBOLT,MG_FIREBOLT,MG_LIGHTNINGBOLT }; + if(skilllv >= 10) { + spellid = MG_FROSTDIVER; +// if (tsc && tsc->data[SC_SPIRIT].timer != -1 && tsc->data[SC_SPIRIT].val2 == SA_SAGE) +// maxlv = 10; +// else + maxlv = skilllv - 9; + } + else if(skilllv >=8) { + spellid = MG_FIREBALL; + maxlv = skilllv - 7; + } + else if(skilllv >=5) { + spellid = MG_SOULSTRIKE; + maxlv = skilllv - 4; + } + else if(skilllv >=2) { + int i = rand()%3; + spellid = spellarray[i]; + maxlv = skilllv - 1; + } + else if(skilllv > 0) { + spellid = MG_NAPALMBEAT; + maxlv = 3; + } + if(spellid > 0) + sc_start4(src,SC_AUTOSPELL,100,skilllv,spellid,maxlv,0, + skill_get_time(SA_AUTOSPELL,skilllv)); + } + break; + + case BS_GREED: + if(sd){ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_greed,bl, + skill_get_splash(skillid, skilllv),BL_ITEM,bl); + } + break; + + case SA_ELEMENTWATER: + case SA_ELEMENTFIRE: + case SA_ELEMENTGROUND: + case SA_ELEMENTWIND: + if(sd && !dstmd) //Only works on monsters. + break; + if(tstatus->mode&MD_BOSS) + break; + case NPC_ATTRICHANGE: + case NPC_CHANGEWATER: + case NPC_CHANGEGROUND: + case NPC_CHANGEFIRE: + case NPC_CHANGEWIND: + case NPC_CHANGEPOISON: + case NPC_CHANGEHOLY: + case NPC_CHANGEDARKNESS: + case NPC_CHANGETELEKINESIS: + case NPC_CHANGEUNDEAD: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl, type, 100, skilllv, skillid, skill_get_pl(skillid), 0, + skill_get_time(skillid, skilllv))); + break; + + case NPC_PROVOCATION: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(md && md->skillidx >= 0) + clif_pet_performance(src,md->db->skill[md->skillidx].val[0]); + break; + + case NPC_KEEPING: + case NPC_BARRIER: + { + int skill_time = skill_get_time(skillid,skilllv); + struct unit_data *ud = unit_bl2ud(bl); + if (clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_time)) + && ud) { //Disable attacking/acting/moving for skill's duration. + ud->attackabletime = + ud->canact_tick = + ud->canmove_tick = tick + skill_time; + } + } + break; + + case NPC_REBIRTH: + //New rebirth System uses Kaizel lv1. [Skotlex] + sc_start(bl,type,100,1,skill_get_time(SL_KAIZEL,skilllv)); + break; + + case NPC_DARKBLESSING: + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,(50+skilllv*5),skilllv,skill_get_time2(skillid,skilllv))); + break; + + case NPC_LICK: + if (dstsd && dstsd->special_state.no_weapon_damage ) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + status_zap(bl, 0, 100); + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,(skilllv*5),skilllv,skill_get_time2(skillid,skilllv))); + break; + + case NPC_SUICIDE: /* 自決 */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + status_kill(src); //When suiciding, neither exp nor drops is given. + break; + + case NPC_SUMMONSLAVE: /* 手下?「喚 */ + case NPC_SUMMONMONSTER: /* MOB?「喚 */ + if(md && md->skillidx >= 0) + mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid); + break; + + case NPC_CALLSLAVE: //取り巻き呼び戻し + mob_warpslave(src,AREA_SIZE/2); + break; + + case NPC_RANDOMMOVE: + if (md) { + md->next_walktime = tick - 1; + mob_randomwalk(md,tick); + } + break; + + case NPC_SPEEDUP: + { + // or does it increase casting rate? just a guess xD + int i = SC_ASPDPOTION0 + skilllv - 1; + if (i > SC_ASPDPOTION3) + i = SC_ASPDPOTION3; + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,i,100,skilllv,skilllv * 60000)); + } + break; + + case NPC_REVENGE: + // not really needed... but adding here anyway ^^ + if (md && md->master_id > 0) { + struct block_list *mbl, *tbl; + if ((mbl = map_id2bl(md->master_id)) == NULL || + (tbl = battle_gettargeted(mbl)) == NULL) + break; + md->state.provoke_flag = tbl->id; + mob_target(md, tbl, sstatus->rhw.range); + } + break; + + case NPC_RUN: //後退 + { + const int mask[8][2] = {{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1}}; + int dir = (bl == src)?unit_getdir(src):map_calc_dir(src,bl->x,bl->y); //If cast on self, run forward, else run away. + unit_stop_attack(src); + //Run skillv tiles. + unit_walktoxy(src, bl->x + skilllv * mask[dir][0], bl->y + skilllv * mask[dir][1], 0); + } + break; + + case NPC_TRANSFORMATION: + case NPC_METAMORPHOSIS: + if(md && md->skillidx >= 0) { + if (skilllv > 1) + { //Multiply skilllv times, the original instance must be silently killed. [Skotlex] + mob_summonslave(md,md->db->skill[md->skillidx].val,skilllv,skillid); + unit_remove_map(src,1); + } + else + { //Transform into another class. + int class_ = mob_random_class (md->db->skill[md->skillidx].val,0); + if (class_) mob_class_change(md, class_); + } + } + break; + + case NPC_EMOTION_ON: + case NPC_EMOTION: + if(md && md->skillidx >= 0) + { + clif_emotion(&md->bl,md->db->skill[md->skillidx].val[0]); + if(!md->special_state.ai && + (md->db->skill[md->skillidx].val[1] || md->db->skill[md->skillidx].val[2])) + //NPC_EMOTION & NPC_EMOTION_ON can change a mob's mode 'permanently' [Skotlex] + //val[1] 'sets' the mode, val[2] can add/remove from the current mode based on skill used: + //NPC_EMOTION_ON adds a mode / NPC_EMOTION removes it. + sc_start4(src, type, 100, skilllv, + md->db->skill[md->skillidx].val[1], + skillid==NPC_EMOTION_ON?md->db->skill[md->skillidx].val[2]:0, + skillid==NPC_EMOTION ?md->db->skill[md->skillidx].val[2]:0, + skill_get_time(skillid, skilllv)); + } + break; + + case NPC_DEFENDER: + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + case NPC_POWERUP: + sc_start(bl,SC_INCATKRATE,100,40*skilllv,skill_get_time(skillid, skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,20*skilllv,skill_get_time(skillid, skilllv))); + break; + + case NPC_AGIUP: + sc_start(bl,SC_SPEEDUP1,100,skilllv,skill_get_time(skillid, skilllv)); + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,20*skilllv,skill_get_time(skillid, skilllv))); + break; + + case NPC_INVISIBLE: + //val4 passed as 1 is for "infinite cloak". + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,type,100,skilllv,0,0,1,skill_get_time(skillid,skilllv))); + break; + + case NPC_SIEGEMODE: + // not sure what it does + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + case WE_MALE: + { + int hp_rate=(skilllv <= 0)? 0:skill_db[skillid].hp_rate[skilllv-1]; + int gain_hp= sstatus->max_hp*abs(hp_rate)/100; // The earned is the same % of the target HP than it costed the caster. [Skotlex] + clif_skill_nodamage(src,bl,skillid,status_heal(bl, gain_hp, 0, 0),1); + } + break; + case WE_FEMALE: + { + int sp_rate=(skilllv <= 0)? 0:skill_db[skillid].sp_rate[skilllv-1]; + int gain_sp=sstatus->max_sp*abs(sp_rate)/100;// The earned is the same % of the target SP than it costed the caster. [Skotlex] + clif_skill_nodamage(src,bl,skillid,status_heal(bl, 0, gain_sp, 0),1); + } + break; + +// parent-baby skills + case WE_BABY: + if(sd){ + struct map_session_data *f_sd = pc_get_father(sd); + struct map_session_data *m_sd = pc_get_mother(sd); + // if neither was found + if(!f_sd && !m_sd){ + clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 0; + } + status_change_start(bl,SC_STUN,10000,skilllv,0,0,0,skill_get_time2(skillid,skilllv),8); + if (f_sd) sc_start(&f_sd->bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + if (m_sd) sc_start(&m_sd->bl,type,100,skilllv,skill_get_time(skillid,skilllv)); + } + break; + + case PF_HPCONVERSION: + { + int hp, sp; + hp = sstatus->max_hp/10; + sp = hp * 10 * skilllv / 100; + if (!status_charge(src,hp,0)) { + if (sd) clif_skill_fail(sd,skillid,0,0); + break; + } + clif_skill_nodamage(src, bl, skillid, skilllv, 1); + status_heal(bl,0,sp,2); + } + break; + case HT_REMOVETRAP: /* リム?ブトラップ */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + { + struct skill_unit *su=NULL; + struct item item_tmp; + int flag; + if((bl->type==BL_SKILL) && + (su=(struct skill_unit *)bl) && + (su->group->src_id == src->id || map_flag_vs(bl->m)) && + (skill_get_inf2(su->group->skill_id) & INF2_TRAP)) + { + if(sd && !su->group->state.into_abyss) + { //Avoid collecting traps when it does not costs to place them down. [Skotlex] + if(battle_config.skill_removetrap_type){ + for(i=0;i<10;i++) { + if(skill_db[su->group->skill_id].itemid[i] > 0){ + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = skill_db[su->group->skill_id].itemid[i]; + item_tmp.identify = 1; + if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,skill_db[su->group->skill_id].amount[i]))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,skill_db[su->group->skill_id].amount[i],sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + } + }else{ + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid = 1065; + item_tmp.identify = 1; + if(item_tmp.nameid && (flag=pc_additem(sd,&item_tmp,1))){ + clif_additem(sd,0,0,flag); + map_addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + } + if(su->group->unit_id == UNT_ANKLESNARE && su->group->val2){ + struct block_list *target=map_id2bl(su->group->val2); + if(target) + status_change_end(target,SC_ANKLE,-1); + } + skill_delunit(su); + } + } + break; + case HT_SPRINGTRAP: /* スプリングトラップ */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + { + struct skill_unit *su=NULL; + if((bl->type==BL_SKILL) && (su=(struct skill_unit *)bl) && (su->group) ){ + switch(su->group->unit_id){ + case UNT_ANKLESNARE: // ankle snare + if (su->group->val2 != 0) + // if it is already trapping something don't spring it, + // remove trap should be used instead + break; + // otherwise fallthrough to below + case UNT_BLASTMINE: + case UNT_SKIDTRAP: + case UNT_LANDMINE: + case UNT_SHOCKWAVE: + case UNT_SANDMAN: + case UNT_FLASHER: + case UNT_FREEZINGTRAP: + case UNT_CLAYMORETRAP: + case UNT_TALKIEBOX: + su->group->unit_id = UNT_USED_TRAPS; + clif_changetraplook(bl, UNT_USED_TRAPS); + su->group->limit=DIFF_TICK(tick+1500,su->group->tick); + su->limit=DIFF_TICK(tick+1500,su->group->tick); + } + } + } + break; + case BD_ENCORE: /* アンコ?ル */ + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(sd) + unit_skilluse_id(src,src->id,sd->skillid_dance,sd->skilllv_dance); + break; + + case AS_SPLASHER: /* ベナムスプラッシャ? */ + if(tstatus->max_hp*3/4 < tstatus->hp) { + map_freeblock_unlock(); + return 1; + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,type,100, + skilllv,skillid,src->id,skill_get_time(skillid,skilllv),1000)); + break; + + case PF_MINDBREAKER: + { + if(tstatus->mode&MD_BOSS || battle_check_undead(tstatus->race,tstatus->def_ele)) + { + map_freeblock_unlock(); + return 1; + } + + //Has a 55% + skilllv*5% success chance. + if (!clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,55+5*skilllv,skilllv,skill_get_time(skillid,skilllv)))) + { + if (sd) clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 0; + } + + unit_skillcastcancel(bl,0); + + if(tsc && tsc->count){ + if(tsc->data[SC_FREEZE].timer!=-1) + status_change_end(bl,SC_FREEZE,-1); + if(tsc->data[SC_STONE].timer!=-1 && tsc->data[SC_STONE].val2==0) + status_change_end(bl,SC_STONE,-1); + if(tsc->data[SC_SLEEP].timer!=-1) + status_change_end(bl,SC_SLEEP,-1); + } + + if(dstmd) + mob_target(dstmd,src,skill_get_range2(src,skillid,skilllv)); + } + break; + + case PF_SOULCHANGE: + { + unsigned int sp1 = 0, sp2 = 0; + if (dstmd) { + if (dstmd->state.soul_change_flag) { + if(sd) clif_skill_fail(sd,skillid,0,0); + break; + } + dstmd->state.soul_change_flag = 1; + sp2 = sstatus->max_sp * 3 /100; + status_heal(src, 0, sp2, 2); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + } + sp1 = sstatus->sp; + sp2 = tstatus->sp; + status_zap(src, 0, sp1); + status_zap(bl, 0, sp2); + status_heal(src, 0, sp2, 3); + status_heal(bl, 0, sp1, 3); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + // Slim Pitcher + case CR_SLIMPITCHER: + if (potion_hp || potion_sp) { + int hp = potion_hp, sp = potion_sp; + hp = hp * (100 + (tstatus->vit<<1))/100; + sp = sp * (100 + (tstatus->int_<<1))/100; + + if (dstsd) { + if (hp) + hp = hp * (100 + pc_checkskill(dstsd,SM_RECOVERY)*10)/100; + if (sp) + sp = sp * (100 + pc_checkskill(dstsd,MG_SRECOVERY)*10)/100; + } + if(hp > 0) + clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); + if(sp > 0) + clif_skill_nodamage(NULL,bl,MG_SRECOVERY,sp,1); + status_heal(bl,hp,sp,0); + } + break; + // Full Chemical Protection + case CR_FULLPROTECTION: + { + int i, skilltime; + skilltime = skill_get_time(skillid,skilllv); + if (!tsc) { + clif_skill_nodamage(src,bl,skillid,skilllv,0); + break; + } + for (i=0; i<4; i++) { + if(tsc->data[SC_STRIPWEAPON + i].timer != -1) + status_change_end(bl, SC_STRIPWEAPON + i, -1 ); + sc_start(bl,SC_CP_WEAPON + i,100,skilllv,skilltime); + } + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case RG_CLEANER: //AppleGirl + clif_skill_nodamage(src,bl,skillid,skilllv,1); + break; + + + case PF_DOUBLECASTING: + if (!clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,30+ 10*skilllv,skilllv,skill_get_time(skillid,skilllv)))) + if (sd) clif_skill_fail(sd,skillid,0,0); + break; + + case CG_LONGINGFREEDOM: + { + if (tsc && tsc->data[SC_LONGING].timer == -1 && tsc->data[SC_DANCING].timer != -1 && tsc->data[SC_DANCING].val4 + && tsc->data[SC_DANCING].val1 != CG_MOONLIT) //Can't use Longing for Freedom while under Moonlight Petals. [Skotlex] + { + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + } + } + break; + + case CG_TAROTCARD: + { + int eff, count = -1; + if (rand() % 100 > skilllv * 8) { + if (sd) clif_skill_fail(sd,skillid,0,0); + map_freeblock_unlock(); + return 0; + } + do { + eff = rand() % 14; + clif_specialeffect(bl, 523 + eff, 0); + switch (eff) + { + case 0: // heals SP to 0 + status_percent_damage(src, bl, 0, 100); + break; + case 1: // matk halved + sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skillid,skilllv)); + break; + case 2: // all buffs removed + status_change_clear_buffs(bl,1); + break; + case 3: // 1000 damage, random armor destroyed + { + int where[] = { EQP_ARMOR, EQP_SHIELD, EQP_HELM }; + status_fix_damage(src, bl, 1000, 0); + clif_damage(src,bl,tick,0,0,1000,0,0,0); + skill_break_equip(bl, where[rand()%3], 10000, BCT_ENEMY); + } + break; + case 4: // atk halved + sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skillid,skilllv)); + break; + case 5: // 2000HP heal, random teleported + status_heal(src, 2000, 0, 0); + unit_warp(src, -1,-1,-1, 3); + break; + case 6: // random 2 other effects + if (count == -1) + count = 3; + else + count++; //Should not retrigger this one. + break; + case 7: // stop freeze or stoned + { + int sc[] = { SC_STOP, SC_FREEZE, SC_STONE }; + sc_start(bl,sc[rand()%3],100,skilllv,skill_get_time2(skillid,skilllv)); + } + break; + case 8: // curse coma and poison + sc_start(bl,SC_COMA,100,skilllv,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_CURSE,100,skilllv,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_POISON,100,skilllv,skill_get_time2(skillid,skilllv)); + break; + case 9: // chaos + sc_start(bl,SC_CONFUSION,100,skilllv,skill_get_time2(skillid,skilllv)); + break; + case 10: // 6666 damage, atk matk halved, cursed + status_fix_damage(src, bl, 6666, 0); + clif_damage(src,bl,tick,0,0,6666,0,0,0); + sc_start(bl,SC_INCATKRATE,100,-50,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_INCMATKRATE,100,-50,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_CURSE,skilllv,100,skill_get_time2(skillid,skilllv)); + break; + case 11: // 4444 damage + status_fix_damage(src, bl, 4444, 0); + clif_damage(src,bl,tick,0,0,4444,0,0,0); + break; + case 12: // stun + sc_start(bl,SC_STUN,100,skilllv,5000); + break; + case 13: // atk,matk,hit,flee,def reduced + sc_start(bl,SC_INCATKRATE,100,-20,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_INCMATKRATE,100,-20,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_INCHITRATE,100,-20,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_INCFLEERATE,100,-20,skill_get_time2(skillid,skilllv)); + sc_start(bl,SC_INCDEFRATE,100,-20,skill_get_time2(skillid,skilllv)); + break; + default: + break; + } + } while ((--count) > 0); + clif_skill_nodamage(src,bl,skillid,skilllv,1); + } + break; + + case SL_ALCHEMIST: + case SL_ASSASIN: + case SL_BARDDANCER: + case SL_BLACKSMITH: + case SL_CRUSADER: + case SL_HUNTER: + case SL_KNIGHT: + case SL_MONK: + case SL_PRIEST: + case SL_ROGUE: + case SL_SAGE: + case SL_SOULLINKER: + case SL_STAR: + case SL_SUPERNOVICE: + case SL_WIZARD: + if (sd && !(dstsd && (dstsd->class_&MAPID_UPPERMASK) == type)) { + clif_skill_fail(sd,skillid,0,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,SC_SPIRIT,100,skilllv,skillid,0,0,skill_get_time(skillid,skilllv))); + sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA,skilllv)); + break; + case SL_HIGH: + if (sd && !(dstsd && (dstsd->class_&JOBL_UPPER) && !(dstsd->class_&JOBL_2) && dstsd->status.base_level < 70)) { + clif_skill_fail(sd,skillid,0,0); + break; + } + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start4(bl,type,100,skilllv,skillid,0,0,skill_get_time(skillid,skilllv))); + sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA,skilllv)); + break; + + case SL_SWOO: + if (tsc && tsc->data[type].timer != -1) { + sc_start(src,SC_STUN,100,skilllv,10000); + break; + } + case SL_SKA: // [marquis007] + case SL_SKE: + if (sd && !battle_config.allow_es_magic_pc && bl->type != BL_MOB) { + clif_skill_fail(sd,skillid,0,0); + status_change_start(src,SC_STUN,10000,skilllv,0,0,0,500,10); + } else + clif_skill_nodamage(src,bl,skillid,skilllv, + sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv))); + + if (skillid == SL_SKE) + sc_start(src,SC_SMA,100,skilllv,skill_get_time(SL_SMA,skilllv)); + + break; + + // New guild skills [Celest] + case GD_BATTLEORDER: + if(flag&1) { + if (status_get_guild_id(src) == status_get_guild_id(bl)) + sc_start(bl,SC_BATTLEORDERS,100,skilllv,skill_get_time(skillid, skilllv)); + } else if (status_get_guild_id(src)) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_GUILD|1, + skill_castend_nodamage_id); + if (sd) + guild_block_skill(sd,skill_get_time2(skillid,skilllv)); + } + break; + case GD_REGENERATION: + if(flag&1) { + if (status_get_guild_id(src) == status_get_guild_id(bl)) + sc_start(bl,SC_REGENERATION,100,skilllv,skill_get_time(skillid, skilllv)); + } else if (status_get_guild_id(src)) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_GUILD|1, + skill_castend_nodamage_id); + if (sd) + guild_block_skill(sd,skill_get_time2(skillid,skilllv)); + } + break; + case GD_RESTORE: + if(flag&1) { + if (status_get_guild_id(src) == status_get_guild_id(bl)) + clif_skill_nodamage(src,bl,AL_HEAL,status_percent_heal(bl,90,90),1); + } else if (status_get_guild_id(src)) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + map_foreachinrange(skill_area_sub, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_GUILD|1, + skill_castend_nodamage_id); + if (sd) + guild_block_skill(sd,skill_get_time2(skillid,skilllv)); + } + break; + case GD_EMERGENCYCALL: + { + int dx[9]={-1, 1, 0, 0,-1, 1,-1, 1, 0}; + int dy[9]={ 0, 0, 1,-1, 1,-1,-1, 1, 0}; + int j = 0; + struct guild *g = NULL; + // i don't know if it actually summons in a circle, but oh well. ;P + g = sd?sd->state.gmaster_flag:guild_search(status_get_guild_id(src)); + if (!g) + break; + for(i = 0; i < g->max_member; i++, j++) { + if (j>8) j=0; + if ((dstsd = g->member[i].sd) != NULL && sd != dstsd) { + if (map[dstsd->bl.m].flag.nowarp && !map_flag_gvg(dstsd->bl.m)) + continue; + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(map_getcell(src->m,src->x+dx[j],src->y+dy[j],CELL_CHKNOREACH)) + dx[j] = dy[j] = 0; + pc_setpos(dstsd, map[src->m].index, src->x+dx[j], src->y+dy[j], 2); + } + } + if (sd) + guild_block_skill(sd,skill_get_time2(skillid,skilllv)); + } + break; + + case SG_FEEL: + if (sd) { + if(!sd->feel_map[skilllv-1].index) { + //AuronX reported you CAN memorize the same map as all three. [Skotlex] + clif_skill_nodamage(src,bl,skillid,skilllv,1); + clif_parse_ReqFeel(sd->fd,sd, skilllv); + } + else + clif_feel_info(sd, skilllv-1); + } + break; + + case SG_HATE: + if (sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(dstsd) //PC + { + sd->hate_mob[skilllv-1] = dstsd->status.class_; + pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[skilllv-1]+1); + clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); + } + else if(dstmd) // mob + { + switch(skilllv) + { + case 1: + if (tstatus->size==0) + { + sd->hate_mob[0] = dstmd->class_; + pc_setglobalreg(sd,"PC_HATE_MOB_SUN",sd->hate_mob[0]+1); + clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); + } else clif_skill_fail(sd,skillid,0,0); + break; + case 2: + if (tstatus->size==1 && tstatus->max_hp>=6000) + { + sd->hate_mob[1] = dstmd->class_; + pc_setglobalreg(sd,"PC_HATE_MOB_MOON",sd->hate_mob[1]+1); + clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); + } else clif_skill_fail(sd,skillid,0,0); + break; + case 3: + if (tstatus->size==2 && tstatus->max_hp>=20000) + { + sd->hate_mob[2] = dstmd->class_; + pc_setglobalreg(sd,"PC_HATE_MOB_STAR",sd->hate_mob[2]+1); + clif_hate_mob(sd,skilllv,sd->hate_mob[skilllv-1]); + } else clif_skill_fail(sd,skillid,0,0); + break; + default: + clif_skill_fail(sd,skillid,0,0); + break; + } + } + } + break; + + //Until they're at right position - gs_nodamage- [Vicious] + //Not implemented yet [Vicious] + case GS_GLITTERING: + if(sd) { + clif_skill_nodamage(src,bl,skillid,skilllv,1); + if(rand()%100 < (50+10*skilllv)) + pc_addspiritball(sd,skill_get_time(skillid,skilllv),10); + else if(sd->spiritball > 0) + pc_delspiritball(sd,1,0); + } + break; + default: + ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid); + map_freeblock_unlock(); + return 1; + } + + if (dstmd) //Mob skill event for no damage skills (damage ones are handled in battle_calc_damage) [Skotlex] + mobskill_event(dstmd, src, tick, MSC_SKILLUSED|(skillid<<16)); + + if (sd && !(flag&1) && sd->state.arrow_atk) //Consume arrow on last invocation to this skill. + battle_consume_ammo(sd, skillid, skilllv); + + map_freeblock_unlock(); + return 0; +} +/*========================================== + * スキル使用(詠唱完了、ID指定) + *------------------------------------------ + */ +int skill_castend_id( int tid, unsigned int tick, int id,int data ) +{ + struct block_list *target, *src = map_id2bl(id); + struct map_session_data* sd = NULL; + struct mob_data* md = NULL; + struct unit_data* ud = unit_bl2ud(src); + struct status_change *sc; + int inf2; + + nullpo_retr(0, ud); + + BL_CAST( BL_PC, src, sd); + BL_CAST( BL_MOB, src, md); + + if( src->prev == NULL ) { + ud->skilltimer = -1; + return 0; + } + + switch (ud->skillid) { + //These three should become skill_castend_pos + case WE_CALLPARTNER: + case WE_CALLPARENT: + case WE_CALLBABY: + //Find a random spot to place the skill. [Skotlex] + inf2 = skill_get_splash(ud->skillid, ud->skilllv); + ud->skillx = src->x + inf2; + ud->skilly = src->y + inf2; + if (!map_random_dir(src, &ud->skillx, &ud->skilly)) { + ud->skillx = src->x; + ud->skilly = src->y; + } + return skill_castend_pos(tid,tick,id,data); + } + + if(ud->skillid != SA_CASTCANCEL ) { + if( ud->skilltimer != tid ) { + ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid); + ud->skilltimer = -1; + return 0; + } + if( sd && ud->skilltimer != -1 && pc_checkskill(sd,SA_FREECAST)) + status_freecast_switch(sd); + ud->skilltimer=-1; + } + + if (ud->skilltarget == id) + target = src; + else + target = map_id2bl(ud->skilltarget); + + // Use a do so that you can break out of it when the skill fails. + do { + if(!target || target->prev==NULL) break; + + if(src->m != target->m || status_isdead(src)) break; + + if(ud->skillid == RG_BACKSTAP) { + int dir = map_calc_dir(src,target->x,target->y),t_dir = unit_getdir(target); + if(check_distance_bl(src, target, 0) || map_check_dir(dir,t_dir)) { + break; + } + } + if (ud->skillid == PR_LEXDIVINA) + { + sc = status_get_sc(target); + if (battle_check_target(src,target, BCT_ENEMY)<=0 && + (!sc || sc->data[SC_SILENCE].timer == -1)) + { //If it's not an enemy, and not silenced, you can't use the skill on them. [Skotlex] + clif_skill_nodamage (src, target, ud->skillid, ud->skilllv, 0); + break; + } + } else { + inf2 = skill_get_inf(ud->skillid); + if((inf2&INF_ATTACK_SKILL || + (inf2&INF_SELF_SKILL && skill_get_inf2(ud->skillid)&INF2_NO_TARGET_SELF)) //Combo skills + && battle_check_target(src, target, BCT_ENEMY)<=0 + ) + break; + } + + //Avoid doing double checks for instant-cast skills. + if (tid != -1 && !status_check_skilluse(src, target, ud->skillid, 1)) + break; + + //沈黙や状態異常など + if(md) { + if(ud->skillid != NPC_EMOTION)//Set afterskill delay. + md->last_thinktime=tick + (tid==-1?md->status.adelay:md->status.amotion); + if(md->skillidx >= 0) { + md->skilldelay[md->skillidx]=tick; + if (md->db->skill[md->skillidx].emotion >= 0) + clif_emotion(src, md->db->skill[md->skillidx].emotion); + } + } + + inf2 = skill_get_inf2(ud->skillid); + if(inf2 & (INF2_PARTY_ONLY|INF2_GUILD_ONLY) && src != target) { + int fail_flag = 1; + if(inf2 & INF2_PARTY_ONLY && battle_check_target(src, target, BCT_PARTY) > 0) + fail_flag = 0; + else if(inf2 & INF2_GUILD_ONLY && battle_check_target(src, target, BCT_GUILD) > 0) + fail_flag = 0; + + if (ud->skillid == PF_SOULCHANGE && map_flag_vs(target->m)) + //Soul Change overrides this restriction during pvp/gvg [Skotlex] + fail_flag = 0; + + if(fail_flag) + break; + } + + if(src != target && battle_config.skill_add_range && + !check_distance_bl(src, target, skill_get_range2(src,ud->skillid,ud->skilllv)+battle_config.skill_add_range)) + { + if (sd) { + clif_skill_fail(sd,ud->skillid,0,0); + if(battle_config.skill_out_range_consume) //Consume items anyway. [Skotlex] + skill_check_condition(sd,ud->skillid, ud->skilllv,1); + } + break; + } + + if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv,1)) /* 使用条件チェック */ + break; + + if (ud->walktimer != -1 && ud->skillid != TK_RUN) + unit_stop_walking(src,1); + + if (ud->skillid == SA_MAGICROD) + ud->canact_tick = tick; + else + ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); + + if (skill_get_state(ud->skillid) != ST_MOVE_ENABLE) + unit_set_walkdelay(src, tick, battle_config.default_skill_delay+skill_get_walkdelay(ud->skillid, ud->skilllv), 1); + + if(battle_config.skill_log && battle_config.skill_log&src->type) + ShowInfo("Type %d, ID %d skill castend id [id =%d, lv=%d, target ID %d)\n", + src->type, src->id, ud->skillid, ud->skilllv, target->id); + if (skill_get_casttype(ud->skillid) == CAST_NODAMAGE) + skill_castend_nodamage_id(src,target,ud->skillid,ud->skilllv,tick,0); + else + skill_castend_damage_id(src,target,ud->skillid,ud->skilllv,tick,0); + + sc = status_get_sc(src); + if(sc && sc->count && sc->data[SC_MAGICPOWER].timer != -1 && ud->skillid != HW_MAGICPOWER && ud->skillid != WZ_WATERBALL) + status_change_end(src,SC_MAGICPOWER,-1); + + if (ud->skilltimer == -1) { + if(md) md->skillidx = -1; + else ud->skillid = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill' + ud->skilllv = ud->skilltarget = 0; + } + return 1; + } while(0); + //Skill failed. + ud->skillid = ud->skilllv = ud->skilltarget = 0; + ud->canact_tick = tick; + if(sd) sd->skillitem = sd->skillitemlv = -1; + if(md) md->skillidx = -1; + return 0; +} + +/*========================================== + * スキル使用(詠唱完了、場所指定) + *------------------------------------------ + */ +int skill_castend_pos( int tid, unsigned int tick, int id,int data ) +{ + struct block_list* src = map_id2bl(id); + int maxcount; + struct map_session_data *sd = NULL; + struct unit_data *ud = unit_bl2ud(src); + struct mob_data *md = NULL; + + nullpo_retr(0, ud); + + BL_CAST( BL_PC , src, sd); + BL_CAST( BL_MOB, src, md); + + if( src->prev == NULL ) { + ud->skilltimer = -1; + return 0; + } + + if( ud->skilltimer != tid ) /* タイマIDの確認 */ + { + ShowError("skill_castend_pos: Timer mismatch %d!=%d\n", ud->skilltimer, tid); + ud->skilltimer = -1; + return 0; + } + + if(sd && ud->skilltimer != -1 && pc_checkskill(sd,SA_FREECAST)) + status_freecast_switch(sd); + + ud->skilltimer=-1; + do { + if(status_isdead(src)) break; + + if (!(battle_config.skill_reiteration && src->type&battle_config.skill_reiteration) && + skill_get_unit_flag(ud->skillid)&UF_NOREITERATION && + skill_check_unit_range(src,ud->skillx,ud->skilly,ud->skillid,ud->skilllv) + ) + break; + + if (battle_config.skill_nofootset && src->type&battle_config.skill_nofootset && + skill_get_unit_flag(ud->skillid)&UF_NOFOOTSET && + skill_check_unit_range2(src,ud->skillx,ud->skilly,ud->skillid,ud->skilllv) + ) + break; + + if(battle_config.land_skill_limit && src->type&battle_config.land_skill_limit && + (maxcount = skill_get_maxcount(ud->skillid)) > 0 + ) { + int i; + for(i=0;iskillunit[i] && maxcount;i++) { + if(ud->skillunit[i]->skill_id == ud->skillid) + maxcount--; + } + if(!maxcount) + break; + } + + if(tid != -1) + { //Avoid double checks on instant cast skills. [Skotlex] + if (!status_check_skilluse(src, NULL, ud->skillid, 1)) + break; + if(battle_config.skill_add_range && + !check_distance_blxy(src, ud->skillx, ud->skilly, skill_get_range2(src,ud->skillid,ud->skilllv)+battle_config.skill_add_range)) { + if (sd && battle_config.skill_out_range_consume) //Consume items anyway. + skill_check_condition(sd,ud->skillid, ud->skilllv,1); + break; + } + } + + if(sd && !skill_check_condition(sd,ud->skillid, ud->skilllv, 1)) /* 使用条件チェック */ + break; + + if(md) { + md->last_thinktime=tick + (tid==-1?md->status.adelay:md->status.amotion); + if(md->skillidx >= 0) { + md->skilldelay[md->skillidx]=tick; + if (md->db->skill[md->skillidx].emotion >= 0) + clif_emotion(src, md->db->skill[md->skillidx].emotion); + } + } + + if(battle_config.skill_log && battle_config.skill_log&src->type) + ShowInfo("Type %d, ID %d skill castend pos [id =%d, lv=%d, (%d,%d)]\n", + src->type, src->id, ud->skillid, ud->skilllv, ud->skillx, ud->skilly); + unit_stop_walking(src,1); + ud->canact_tick = tick + skill_delayfix(src, ud->skillid, ud->skilllv); + unit_set_walkdelay(src, tick, battle_config.default_skill_delay+skill_get_walkdelay(ud->skillid, ud->skilllv), 1); + skill_castend_pos2(src,ud->skillx,ud->skilly,ud->skillid,ud->skilllv,tick,0); + + if (ud->skilltimer == -1) { + if (md) md->skillidx = -1; + else ud->skillid = 0; //Non mobs can't clear this one as it is used for skill condition 'afterskill' + ud->skilllv = ud->skillx = ud->skilly = 0; + } + return 1; + } while(0); + + ud->canact_tick = tick; + ud->skillid = ud->skilllv = 0; + if(sd) { + clif_skill_fail(sd,ud->skillid,0,0); + sd->skillitem = sd->skillitemlv = -1; + } + if(md) md->skillidx = -1; + return 0; + +} + +/*========================================== + * スキル使用?i詠?・完了?A??且w定の??ロの???j + *------------------------------------------ + */ +int skill_castend_pos2( struct block_list *src, int x,int y,int skillid,int skilllv,unsigned int tick,int flag) +{ + struct map_session_data *sd=NULL; + struct status_change *sc; + int i; + + //if(skilllv <= 0) return 0; + if(skillid > 0 && skilllv <= 0) return 0; // celest + + nullpo_retr(0, src); + + if(status_isdead(src)) + return 0; + + if(src->type==BL_PC) + sd=(struct map_session_data *)src; + + sc = status_get_sc(src); //Needed for Magic Power checks. + if (sc && !sc->count) + sc = NULL; //Unneeded. + + if(skillid != WZ_METEOR && + skillid != MO_BODYRELOCATION && + skillid != CR_CULTIVATION) + clif_skill_poseffect(src,skillid,skilllv,x,y,tick); + + switch(skillid) + { + case PR_BENEDICTIO: /* ?ケ??~福 */ + skill_area_temp[1] = src->id; + i = skill_get_splash(skillid, skilllv); + map_foreachinarea(skill_area_sub, + src->m, x-i, y-i, x+i, y+i, BL_PC, + src, skillid, skilllv, tick, flag|BCT_ALL|1, + skill_castend_nodamage_id); + map_foreachinarea(skill_area_sub, + src->m, x-i, y-i, x+i, y+i, BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY|1, + skill_castend_damage_id); + break; + + case BS_HAMMERFALL: + i = skill_get_splash(skillid, skilllv); + map_foreachinarea (skill_area_sub, + src->m, x-i, y-i, x+i, y+i, BL_CHAR, + src, skillid, skilllv, tick, flag|BCT_ENEMY|2, + skill_castend_nodamage_id); + break; + + case HT_DETECTING: /* ディテクティング */ + i = skill_get_splash(skillid, skilllv); + map_foreachinarea( status_change_timer_sub, + src->m, x-i, y-i, x+i,y+i,BL_CHAR, + src,status_get_sc(src),SC_SIGHT,tick); + if(battle_config.traps_setting&1) + map_foreachinarea( skill_reveal_trap, + src->m, x-i, y-i, x+i,y+i,BL_SKILL); + break; + + case MG_SAFETYWALL: /* セイフティウォ?ル */ + case MG_FIREWALL: /* ファイヤ?ウォ?ル */ + case MG_THUNDERSTORM: /* サンダ?スト?ム */ + case AL_PNEUMA: /* ニュ?マ */ + case WZ_ICEWALL: /* アイスウォ?ル */ + case WZ_FIREPILLAR: /* ファイアピラ? */ + case WZ_QUAGMIRE: /* クァグマイア */ + case WZ_VERMILION: /* ??ドオブヴァ?ミリオン */ + case WZ_STORMGUST: /* スト?ムガスト */ + case WZ_HEAVENDRIVE: /* ヘヴンズドライブ */ + case PR_SANCTUARY: /* サンクチュアリ */ + case PR_MAGNUS: /* マグヌスエクソシズム */ + case CR_GRANDCROSS: /* グランドク?ス */ + case NPC_GRANDDARKNESS: /*闇グランドク?ス*/ + case HT_SKIDTRAP: /* スキッドトラップ */ + case HT_LANDMINE: /* ランドマイン */ + case HT_ANKLESNARE: /* アンクルスネア */ + case HT_SHOCKWAVE: /* ショックウェ?ブトラップ */ + case HT_SANDMAN: /* サンドマン */ + case HT_FLASHER: /* フラッシャ? */ + case HT_FREEZINGTRAP: /* フリ?ジングトラップ */ + case HT_BLASTMINE: /* ブラストマイン */ + case HT_CLAYMORETRAP: /* クレイモア?トラップ */ + case AS_VENOMDUST: /* ベノムダスト */ + case AM_DEMONSTRATION: /* デモンストレ?ション */ + case PF_FOGWALL: /* フォグウォ?ル */ + case PF_SPIDERWEB: /* スパイダ?ウェッブ */ + case HT_TALKIEBOX: /* ト?キ?ボックス */ + case WE_CALLPARTNER: + case WE_CALLPARENT: + case WE_CALLBABY: + case AC_SHOWER: //Ground-placed skill implementation. + case GS_DESPERADO: + skill_unitsetting(src,skillid,skilllv,x,y,0); + flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete). + break; + + case RG_GRAFFITI: /* Graffiti [Valaris] */ + skill_clear_unitgroup(src); + skill_unitsetting(src,skillid,skilllv,x,y,0); + flag|=1; + break; + + case RG_CLEANER: // [Valaris] + i = skill_get_splash(skillid, skilllv); + map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL); + break; + case SA_VOLCANO: /* ボルケ?ノ */ + case SA_DELUGE: /* デリュ?ジ */ + case SA_VIOLENTGALE: /* バイオレントゲイル */ + case SA_LANDPROTECTOR: /* ランドプ?テクタ? */ + case NJ_SUITON: + skill_unitsetting(src,skillid,skilllv,x,y,0); + flag|=1; + break; + + case WZ_METEOR: //?テオスト?ム + { + int flag=0, area = skill_get_splash(skillid, skilllv); + short tmpx, tmpy, x1 = 0, y1 = 0; + if (sc && sc->data[SC_MAGICPOWER].timer != -1) + flag = flag|2; //Store the magic power flag for future use. [Skotlex] + for(i=0;i<2+(skilllv>>1);i++) { + tmpx = x; + tmpy = y; + if (!map_search_freecell(NULL, src->m, &tmpx, &tmpy, area, area, 1)) + continue; + if(!(flag&1)){ + clif_skill_poseffect(src,skillid,skilllv,tmpx,tmpy,tick); + flag=flag|1; + } + if(i > 0) + skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,(x1<<16)|y1,flag&2); //Only pass the Magic Power flag + x1 = tmpx; + y1 = tmpy; + } + skill_addtimerskill(src,tick+i*1000,0,tmpx,tmpy,skillid,skilllv,-1,flag&2); //Only pass the Magic Power flag + } + break; + + case AL_WARP: /* ??プポ?タル */ + if(sd) { + clif_skill_warppoint(sd,skillid,skilllv,mapindex_id2name(sd->status.save_point.map), + (skilllv>1 && sd->status.memo_point[0].map)?mapindex_id2name(sd->status.memo_point[0].map):"", + (skilllv>2 && sd->status.memo_point[1].map)?mapindex_id2name(sd->status.memo_point[1].map):"", + (skilllv>3 && sd->status.memo_point[2].map)?mapindex_id2name(sd->status.memo_point[2].map):""); + } + break; + + case MO_BODYRELOCATION: + if (unit_movepos(src, x, y, 1, 1)) { + clif_skill_poseffect(src,skillid,skilllv,src->x,src->y,tick); +// clif_slide(src, src->x, src->y); //Poseffect is the one that makes the char snap on the client... + if (sd) skill_blockpc_start (sd, MO_EXTREMITYFIST, 2000); + } + break; + case AM_SPHEREMINE: + case AM_CANNIBALIZE: + if(sd) { + int summons[5] = { 1020, 1068, 1118, 1500, 1368 }; + int class_ = skillid==AM_SPHEREMINE?1142:summons[skilllv-1]; + struct mob_data *md; + + // Correct info, don't change any of this! [celest] + md = mob_once_spawn_sub(src, src->m, -1, -1, sd->status.name,class_,""); + if (md) { + md->master_id = src->id; + md->special_state.ai = skillid==AM_SPHEREMINE?2:3; + md->deletetimer = add_timer (gettick() + skill_get_time(skillid,skilllv), mob_timer_delete, md->bl.id, 0); + mob_spawn (md); //Now it is ready for spawning. + } + } + break; + + // Slim Pitcher [Celest] + case CR_SLIMPITCHER: + if (sd) { + int i = skilllv%11 - 1; + int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]); + if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL || + sd->status.inventory[j].amount < skill_db[skillid].amount[i]) { + clif_skill_fail(sd,skillid,0,0); + return 1; + } + potion_flag = 1; + potion_hp = 0; + potion_sp = 0; + run_script(sd->inventory_data[j]->script,0,sd->bl.id,0); + pc_delitem(sd,j,skill_db[skillid].amount[i],0); + potion_flag = 0; + //Apply skill bonuses + i = pc_checkskill(sd,CR_SLIMPITCHER)*10 + + pc_checkskill(sd,AM_POTIONPITCHER)*10 + + pc_checkskill(sd,AM_LEARNINGPOTION)*5; + + potion_hp = potion_hp * (100+i)/100; + potion_sp = potion_sp * (100+i)/100; + + if(potion_hp > 0 || potion_sp > 0) { + i = skill_get_splash(skillid, skilllv); + map_foreachinarea(skill_area_sub, + src->m,x-i,y-i,x+i,y+i,BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_PARTY|BCT_GUILD|1, + skill_castend_nodamage_id); + } + } else { + int i = skilllv%11 - 1; + struct item_data *item = itemdb_search(i); + i = skill_db[skillid].itemid[i]; + item = itemdb_search(i); + potion_flag = 1; + potion_hp = 0; + potion_sp = 0; + run_script(item->script,0,src->id,0); + potion_flag = 0; + i = skill_get_max(CR_SLIMPITCHER)*10; + + potion_hp = potion_hp * (100+i)/100; + potion_sp = potion_sp * (100+i)/100; + + if(potion_hp > 0 || potion_sp > 0) { + i = skill_get_splash(skillid, skilllv); + map_foreachinarea(skill_area_sub, + src->m,x-i,y-i,x+i,y+i,BL_CHAR, + src,skillid,skilllv,tick,flag|BCT_PARTY|BCT_GUILD|1, + skill_castend_nodamage_id); + } + } + break; + + case HW_GANBANTEIN: + if (rand()%100 < 80) { + clif_skill_poseffect(src,skillid,skilllv,x,y,tick); + i = skill_get_splash(skillid, skilllv); + map_foreachinarea (skill_ganbatein, src->m, x-i, y-i, x+i, y+i, BL_SKILL); + } else { + if (sd) clif_skill_fail(sd,skillid,0,0); + return 1; + } + break; + + case HW_GRAVITATION: + { + struct skill_unit_group *sg; + clif_skill_poseffect(src,skillid,skilllv,x,y,tick); + sg = skill_unitsetting(src,skillid,skilllv,x,y,0); + sc_start4(src,SkillStatusChangeTable[skillid],100, + skilllv,0,BCT_SELF,(int)sg,skill_get_time(skillid,skilllv)); + flag|=1; + } + break; + + // Plant Cultivation [Celest] + case CR_CULTIVATION: + { + if (sd) { + int i = skilllv - 1; + int j = pc_search_inventory(sd,skill_db[skillid].itemid[i]); + if(j < 0 || skill_db[skillid].itemid[i] <= 0 || sd->inventory_data[j] == NULL || + sd->status.inventory[j].amount < skill_db[skillid].amount[i]) { + clif_skill_fail(sd,skillid,0,0); + return 1; + } + pc_delitem(sd,j,skill_db[skillid].amount[i],0); + clif_skill_poseffect(src,skillid,skilllv,x,y,tick); + if (rand()%100 < 50) + mob_once_spawn(sd, "this", x, y, "--ja--",(skilllv < 2 ? 1084+rand()%2 : 1078+rand()%6), 1, ""); + else + clif_skill_fail(sd,skillid,0,0); + } + } + break; + + //Until they're at right position - gs_unit- [Vicious] + case GS_GROUNDDRIFT: /* グラウンドドリフト*/ + case NJ_KAENSIN: /* 火炎陣*/ + case NJ_BAKUENRYU: /* 爆炎龍*/ + case NJ_HYOUSYOURAKU: + skill_unitsetting(src,skillid,skilllv,x,y,0); + flag|=1; + break; + + case NJ_RAIGEKISAI: + map_foreachinrange(skill_attack_area, src, + skill_get_splash(skillid, skilllv), BL_CHAR, + BF_MAGIC, src, src, skillid, skilllv, tick, flag, BCT_ENEMY); + break; + } + + if (sc && sc->data[SC_MAGICPOWER].timer != -1) + status_change_end(&sd->bl,SC_MAGICPOWER,-1); + + if (sd && !(flag&1) && sd->state.arrow_atk) //Consume arrow if a ground skill was not invoked. [Skotlex] + battle_consume_ammo(sd, skillid, skilllv); + + return 0; +} + +/*========================================== + * スキル使用?i詠?・完了?Amap指定?j + *------------------------------------------ + */ +int skill_castend_map( struct map_session_data *sd,int skill_num, const char *map) +{ + int x=0,y=0; + + nullpo_retr(0, sd); + +//Simplify skill_failed code. +#define skill_failed(sd) { sd->menuskill_id = sd->menuskill_lv = 0; } + + if( sd->bl.prev == NULL || pc_isdead(sd) ) + return 0; + + if(sd->sc.opt1 || sd->sc.option&OPTION_HIDE ) { + skill_failed(sd); + return 0; + } + //スキルが使えない?態異?中 + if(sd->sc.count && ( + sd->sc.data[SC_SILENCE].timer!=-1 || + sd->sc.data[SC_ROKISWEIL].timer!=-1 || + sd->sc.data[SC_AUTOCOUNTER].timer != -1 || + sd->sc.data[SC_STEELBODY].timer != -1 || + sd->sc.data[SC_DANCING].timer!=-1 || + sd->sc.data[SC_BERSERK].timer != -1 || + sd->sc.data[SC_MARIONETTE].timer != -1 + )) + return 0; + + if( skill_num != sd->menuskill_id) /* 不?ウパケットらしい */ + return 0; + + if (strlen(map) > MAP_NAME_LENGTH-1) + { //Map_length check, as it is sent by the client and we shouldn't trust it [Skotlex] + if (battle_config.error_log) + ShowError("skill_castend_map: Received map name '%s' too long!\n", map); + skill_failed(sd); + return 0; + } + + pc_stop_attack(sd); + pc_stop_walking(sd,0); + + if(battle_config.skill_log && battle_config.skill_log&BL_PC) + ShowInfo("PC %d skill castend skill =%d map=%s\n",sd->bl.id,skill_num,map); + + if(strcmp(map,"cancel")==0) { + skill_failed(sd); + return 0; + } + + switch(skill_num){ + case AL_TELEPORT: /* テレポ?ト */ + if(strcmp(map,"Random")==0) + pc_randomwarp(sd,3); + else if (sd->menuskill_lv > 1) //Need lv2 to be able to warp here. + pc_setpos(sd,sd->status.save_point.map, + sd->status.save_point.x,sd->status.save_point.y,3); + break; + + case AL_WARP: /* ??プポ?タル */ + { + const struct point *p[4]; + struct skill_unit_group *group; + int i, lv, wx, wy; + int maxcount=0; + unsigned short mapindex; + mapindex = mapindex_name2id((char*)map); + if(!mapindex) { //Given map not found? + clif_skill_fail(sd,skill_num,0,0); + skill_failed(sd); + return 0; + } + p[0] = &sd->status.save_point; + p[1] = &sd->status.memo_point[0]; + p[2] = &sd->status.memo_point[1]; + p[3] = &sd->status.memo_point[2]; + + if((maxcount = skill_get_maxcount(skill_num)) > 0) { + for(i=0;iud.skillunit[i] && maxcount;i++) { + if(sd->ud.skillunit[i]->skill_id == skill_num) + maxcount--; + } + if(!maxcount) { + clif_skill_fail(sd,skill_num,0,0); + skill_failed(sd); + return 0; + } + } + + //When it's an item-used warp-portal, the skill-lv used is lost.. assume max level. + lv = sd->skillitem==skill_num?skill_get_max(skill_num):pc_checkskill(sd,skill_num); + wx = sd->menuskill_lv>>16; + wy = sd->menuskill_lv&0xffff; + + if(lv <= 0) return 0; + for(i=0;imap){ + x=p[i]->x; + y=p[i]->y; + break; + } + } + if(x==0 || y==0) { /* 不?ウパケット?H */ + skill_failed(sd); + return 0; + } + + if(!skill_check_condition(sd, sd->menuskill_id, lv,3)) //This checks versus skillid/skilllv... + { + skill_failed(sd); + return 0; + } + + if(skill_check_unit_range2(&sd->bl,wx,wy,skill_num,lv) > 0) { + clif_skill_fail(sd,0,0,0); + skill_failed(sd); + return 0; + } + if((group=skill_unitsetting(&sd->bl,skill_num,lv,wx,wy,0))==NULL) { + skill_failed(sd); + return 0; + } + //Now that there's a mapindex, use that in val3 rather than a string. [Skotlex] + group->val2=(x<<16)|y; + group->val3 = mapindex; + } + break; + } + + sd->menuskill_id = sd->menuskill_lv = 0; + return 0; +#undef skill_failed +} + +/*========================================== + * Initializes and sets a ground skill. + * flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active) + * flag&2 is used to determine if this skill was casted with Magic Power active. + *------------------------------------------ + */ +struct skill_unit_group *skill_unitsetting( struct block_list *src, int skillid,int skilllv,int x,int y,int flag) +{ + struct skill_unit_group *group; + int i,limit,val1=0,val2=0,val3=0; + int count=0; + int target,interval,range,unit_flag; + struct skill_unit_layout *layout; + struct map_session_data *sd; + struct status_data *status; + struct status_change *sc; + int active_flag=1; + + nullpo_retr(0, src); + + limit = skill_get_time(skillid,skilllv); + range = skill_get_unit_range(skillid,skilllv); + interval = skill_get_unit_interval(skillid); + target = skill_get_unit_target(skillid); + unit_flag = skill_get_unit_flag(skillid); + layout = skill_get_unit_layout(skillid,skilllv,src,x,y); + + BL_CAST(BL_PC, src, sd); + status = status_get_status_data(src); + sc= status_get_sc(src); // for traps, firewall and fogwall - celest + if (sc && !sc->count) + sc = NULL; + + switch(skillid){ /* ?ン定 */ + + case MG_SAFETYWALL: /* セイフティウォ?ル */ + val2=skilllv+1; + break; + case MG_FIREWALL: /* ファイヤ?ウォ?ル */ + if(sc && sc->data[SC_VIOLENTGALE].timer!=-1) + limit = limit*3/2; + val2=4+skilllv; + break; + + case AL_WARP: /* ??プポ?タル */ + val1=skilllv+6; + if(!(flag&1)) + limit=2000; + active_flag=0; + break; + + case PR_SANCTUARY: /* サンクチュアリ */ + val1=(skilllv+3)*2; + val2=(skilllv>6)?777:skilllv*100; + break; + + case WZ_FIREPILLAR: /* ファイア?ピラ? */ + if((flag&1)!=0) + limit=1000; + val1=skilllv+2; + break; + case WZ_QUAGMIRE: //The target changes to "all" if used in a gvg map. [Skotlex] + case AM_DEMONSTRATION: + if (map_flag_vs(src->m) && battle_config.vs_traps_bctall + && (src->type&battle_config.vs_traps_bctall)) + target = BCT_ALL; + break; + case HT_SHOCKWAVE: /* ショックウェ?ブトラップ */ + val1=skilllv*15+10; + case HT_SANDMAN: /* サンドマン */ + case HT_CLAYMORETRAP: /* クレイモア?トラップ */ + case HT_SKIDTRAP: /* スキッドトラップ */ + case HT_LANDMINE: /* ランドマイン */ + case HT_ANKLESNARE: /* アンクルスネア */ + case HT_FLASHER: /* フラッシャ? */ + case HT_FREEZINGTRAP: /* フリ?ジングトラップ */ + case HT_BLASTMINE: /* ブラストマイン */ + if (map_flag_gvg(src->m)) + limit *= 4; // longer trap times in WOE [celest] + if (battle_config.vs_traps_bctall && map_flag_vs(src->m) + && (src->type&battle_config.vs_traps_bctall)) + target = BCT_ALL; + break; + + case SA_LANDPROTECTOR: /* グランドク?ス */ + { + int aoe_diameter; // -- aoe_diameter (moonsoul) added for sage Area Of Effect skills + val1=skilllv*15+10; + aoe_diameter=skilllv+skilllv%2+5; + count=aoe_diameter*aoe_diameter; // -- this will not function if changed to ^2 (moonsoul) + } + //No break because we also have to check if we use gemstones. [Skotlex] + case SA_VOLCANO: + case SA_DELUGE: + case SA_VIOLENTGALE: + { + struct skill_unit_group *old_sg; + if ((old_sg = skill_locate_element_field(src)) != NULL) + { + if (old_sg->skill_id == skillid && old_sg->limit > 0) + { //Use the previous limit (minus the elapsed time) [Skotlex] + limit = old_sg->limit - DIFF_TICK(gettick(), old_sg->tick); + if (limit < 0) //This can happen... + limit = skill_get_time(skillid,skilllv); + } + skill_clear_group(src,1); + } + break; + } + + case BA_DISSONANCE: + case DC_UGLYDANCE: + val1 = 10; //FIXME: This value is not used anywhere, what is it for? [Skotlex] + break; + case BA_WHISTLE: + val1 = skilllv +status->agi/10; // Flee increase + val2 = ((skilllv+1)/2)+status->luk/10; // Perfect dodge increase + if(sd){ + val1 += pc_checkskill(sd,BA_MUSICALLESSON); + val2 += pc_checkskill(sd,BA_MUSICALLESSON); + } + break; + case DC_HUMMING: + val1 = 2*skilllv+status->dex/10; // Hit increase + if(sd) + val1 += 2*pc_checkskill(sd,DC_DANCINGLESSON); + break; + case BA_POEMBRAGI: + val1 = 3*skilllv+status->dex/10; // Casting time reduction + val2 = 3*skilllv+status->int_/10; // After-cast delay reduction + if(sd){ + val1 += pc_checkskill(sd,BA_MUSICALLESSON); + val2 += pc_checkskill(sd,BA_MUSICALLESSON); + } + break; + case DC_DONTFORGETME: + val1 = 3*skilllv+status->dex/10; // ASPD decrease + val2 = 2*skilllv+status->agi/10; // Movement speed decrease + if(sd){ + val1 += pc_checkskill(sd,DC_DANCINGLESSON); + val2 += pc_checkskill(sd,DC_DANCINGLESSON); + } + break; + case BA_APPLEIDUN: + val1 = 5+2*skilllv+status->vit/10; // MaxHP percent increase + val2 = 30+5*skilllv+5*(status->vit/10); // HP recovery + if(sd){ + val1 += pc_checkskill(sd,BA_MUSICALLESSON); + val2 += 5*pc_checkskill(sd,BA_MUSICALLESSON); + } + break; + case DC_SERVICEFORYOU: + val1 = 10+skilllv+(status->int_/10); // MaxSP percent increase + val2 = 10+3*skilllv+(status->int_/10); // SP cost reduction + if(sd){ + val1 += pc_checkskill(sd,DC_DANCINGLESSON); + val2 += pc_checkskill(sd,DC_DANCINGLESSON); + } + break; + case BA_ASSASSINCROSS: + val1 = 10+skilllv+(status->agi/10); // ASPD increase + if(sd) + val1 += pc_checkskill(sd,BA_MUSICALLESSON); + break; + case DC_FORTUNEKISS: + val1 = 10+skilllv+(status->luk/10); // Critical increase + if(sd) + val1 += pc_checkskill(sd,DC_DANCINGLESSON); + val1*=10; //Because every 10 crit is an actual cri point. + break; + case BD_DRUMBATTLEFIELD: + val1 = (skilllv+1)*25; //Watk increase + val2 = (skilllv+1)*2; //Def increase + break; + case BD_RINGNIBELUNGEN: + val1 = (skilllv+2)*25; //Watk increase + break; + case BD_RICHMANKIM: + val1 = 25 + 11*skilllv; //Exp increase bonus. + break; + case BD_SIEGFRIED: + val1 = 55 + skilllv*5; //Elemental Resistance + val2 = skilllv*10; //Status ailment resistance + break; + case PF_FOGWALL: /* フォグウォ?ル */ + if(sc && sc->data[SC_DELUGE].timer!=-1) limit *= 2; + break; + case RG_GRAFFITI: /* Graffiti */ + count=1; // Leave this at 1 [Valaris] + break; + case WE_CALLPARTNER: + if (sd) val1 = sd->status.partner_id; + break; + case WE_CALLPARENT: + if (sd) { + val1 = sd->status.father; + val2 = sd->status.mother; + } + break; + case WE_CALLBABY: + if (sd) val1 = sd->status.child; + break; + } + + nullpo_retr(NULL, group=skill_initunitgroup(src,(count > 0 ? count : layout->count), + skillid,skilllv,skill_get_unit_id(skillid,flag&1), limit, interval)); + group->val1=val1; + group->val2=val2; + group->val3=val3; + group->target_flag=target; + group->bl_flag= skill_get_unit_bl_target(skillid); + group->state.into_abyss = (sc && sc->data[SC_INTOABYSS].timer != -1); //Store into abyss state, to know it shouldn't give traps back. [Skotlex] + group->state.magic_power = (flag&2 || (sc && sc->data[SC_MAGICPOWER].timer != -1)); //Store the magic power flag. [Skotlex] + group->state.ammo_consume = (sd && sd->state.arrow_atk); //Store if this skill needs to consume ammo. + + if(skillid==HT_TALKIEBOX || + skillid==RG_GRAFFITI){ + group->valstr=(char *) aMallocA(MESSAGE_SIZE*sizeof(char)); + if(group->valstr==NULL){ + ShowFatalError("skill_castend_map: out of memory !\n"); + exit(1); + } + memcpy(group->valstr,talkie_mes,MESSAGE_SIZE-1); + group->valstr[MESSAGE_SIZE-1] = '\0'; + } + + //Why redefine local variables when the ones of the function can be reused? [Skotlex] + val1=skilllv; + val2=0; + limit=group->limit; + for(i=0;icount;i++){ + struct skill_unit *unit; + int ux,uy,alive=1; + ux = x + layout->dx[i]; + uy = y + layout->dy[i]; + switch (skillid) { + case MG_FIREWALL: /* ファイヤ?ウォ?ル */ + val2=group->val2; + break; + case WZ_ICEWALL: /* アイスウォ?ル */ + if(skilllv <= 1) + val1 = 500; + else + val1 = 200 + 200*skilllv; + break; + case RG_GRAFFITI: /* Graffiti [Valaris] */ + ux+=(i%5-2); + uy+=(i/5-2); + break; + } + //直?繝Xキルの???ン置?タ標?繧ノランドプ?テクタ?がないかチェック + if(range<=0) + map_foreachincell(skill_landprotector,src->m,ux,uy,BL_SKILL,skillid,&alive, src); + + if(alive && map_getcell(src->m,ux,uy,CELL_CHKWALL)) + alive = 0; + + if (alive && battle_config.skill_wall_check) { + //Check if there's a path between cell and center of casting. + if (!path_search_long(NULL,src->m,ux,uy,x,y)) + alive = 0; + } + + if(alive && skillid == WZ_ICEWALL) { + if(src->x == x && src->y==y) // Ice Wall not allowed on self [DracoRPG] + alive=0; + else { + val2=map_getcell(src->m,ux,uy,CELL_GETTYPE); + if(val2==5 || val2==1) + alive=0; + else + clif_changemapcell(src->m,ux,uy,5,0); + } + } + + if(alive){ + nullpo_retr(NULL, unit=skill_initunit(group,i,ux,uy)); + unit->val1=val1; + unit->val2=val2; + unit->limit=limit; + unit->range=range; + + if (range==0 && active_flag) + map_foreachincell(skill_unit_effect,unit->bl.m, + unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),1); + } + } + + return group; +} + +/*========================================== + * スキルユニットの?動イベント + *------------------------------------------ + */ +int skill_unit_onplace(struct skill_unit *src,struct block_list *bl,unsigned int tick) +{ + struct skill_unit_group *sg; + struct block_list *ss; + struct status_change *sc; + int type,skillid; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + + if(bl->prev==NULL || !src->alive || status_isdead(bl)) + return 0; + + nullpo_retr(0, sg=src->group); + nullpo_retr(0, ss=map_id2bl(sg->src_id)); + + if (skill_get_type(sg->skill_id) == BF_MAGIC && + map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR)) + return 0; //AoE skills are ineffective. [Skotlex] + + if (battle_check_target(&src->bl,bl,sg->target_flag)<=0) + return 0; + + sc = status_get_sc(bl); + + if (sc && sc->option&OPTION_HIDE && sg->skill_id != WZ_HEAVENDRIVE) + return 0; //Hidden characters are inmune to AoE skills except Heaven's Drive. [Skotlex] + + type = SkillStatusChangeTable[sg->skill_id]; + skillid = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still. + switch (sg->unit_id) { + case UNT_SAFETYWALL: + //TODO: Find a more reliable way to handle the link to sg, this could cause dangling pointers. [Skotlex] + if (sc && sc->data[type].timer == -1) + sc_start4(bl,type,100,sg->skill_lv,sg->group_id,(int)sg,0,sg->limit); + break; + + case UNT_WARP_WAITING: + if(bl->type==BL_PC){ + struct map_session_data *sd = (struct map_session_data *)bl; + if((!sd->chatID || battle_config.chat_warpportal) + && sd->ud.to_x == src->bl.x && sd->ud.to_y == src->bl.y) { + if (pc_setpos(sd,sg->val3,sg->val2>>16,sg->val2&0xffff,3) == 0) { + if (--sg->val1<=0 || sg->src_id == bl->id) + skill_delunitgroup(NULL, sg); + } + } + } else if(battle_config.mob_warpportal && bl->type != BL_PET) + unit_warp(bl,map_mapindex2mapid(sg->val3),sg->val2>>16,sg->val2&0xffff,3); + break; + + case UNT_QUAGMIRE: + if(sc && sc->data[type].timer==-1) + sc_start4(bl,type,100,sg->skill_lv,sg->group_id,0,0,sg->limit); + break; + + case UNT_VOLCANO: + case UNT_DELUGE: + case UNT_VIOLENTGALE: + case UNT_SUITON: + if(sc && sc->data[type].timer==-1) + sc_start(bl,type,100,sg->skill_lv,skill_get_time2(sg->skill_id,sg->skill_lv)); + break; + + case UNT_RICHMANKIM: + case UNT_ETERNALCHAOS: + case UNT_DRUMBATTLEFIELD: + case UNT_RINGNIBELUNGEN: + case UNT_ROKISWEIL: + case UNT_INTOABYSS: + case UNT_SIEGFRIED: + case UNT_HERMODE: + //Needed to check when a dancer/bard leaves their ensemble area. + if (sg->src_id==bl->id && (!sc || sc->data[SC_SPIRIT].timer == -1 || sc->data[SC_SPIRIT].val2 != SL_BARDDANCER)) + return sg->skill_id; + if (sc && sc->data[type].timer==-1) + sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); + break; + case UNT_WHISTLE: + case UNT_ASSASSINCROSS: + case UNT_POEMBRAGI: + case UNT_APPLEIDUN: + case UNT_HUMMING: + case UNT_DONTFORGETME: + case UNT_FORTUNEKISS: + case UNT_SERVICEFORYOU: + if (sg->src_id==bl->id && (!sc || sc->data[SC_SPIRIT].timer == -1 || sc->data[SC_SPIRIT].val2 != SL_BARDDANCER)) + return 0; + if (!sc) + break; + if (sc->data[type].timer==-1) + sc_start4(bl,type,100,sg->skill_lv,sg->val1,sg->val2,0,sg->limit); + else if (sc->data[type].val4 == 1) { + //Readjust timers since the effect will not last long. + sc->data[type].val4 = 0; + delete_timer(sc->data[type].timer, status_change_timer); + sc->data[type].timer = add_timer(tick+sg->limit, status_change_timer, bl->id, type); + } + break; + case UNT_FOGWALL: + if (sc && sc->data[type].timer==-1) + { + sc_start4(bl, type, 100, sg->skill_lv, sg->val1, sg->val2, sg->group_id, sg->limit); + if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) + skill_additional_effect (ss, bl, sg->skill_id, sg->skill_lv, BF_MISC, tick); + } + break; + + case UNT_GRAVITATION: + if (sc && sc->data[type].timer==-1) + sc_start4(bl,type,100,sg->skill_lv,5*sg->skill_lv,BCT_ENEMY,sg->group_id,sg->limit); + break; + + case UNT_ICEWALL: //Destroy the cell. [Skotlex] + src->val1 = 0; + if(src->limit + sg->tick > tick + 700) + src->limit = DIFF_TICK(tick+700,sg->tick); + break; + } + + return skillid; +} + +/*========================================== + * スキルユニットの発動イベント(タイマ?[発動) + *------------------------------------------ + */ +int skill_unit_onplace_timer(struct skill_unit *src,struct block_list *bl,unsigned int tick) +{ + struct skill_unit_group *sg; + struct block_list *ss; + struct map_session_data *sd; + struct status_data *tstatus, *sstatus; + struct status_change *tsc, *sc; + struct skill_unit_group_tickset *ts; + int matk_min, matk_max; //For Magic power... + int type, skillid; + int diff=0; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + + if (bl->prev==NULL || !src->alive || status_isdead(bl)) + return 0; + + nullpo_retr(0, sg=src->group); + nullpo_retr(0, ss=map_id2bl(sg->src_id)); + BL_CAST(BL_PC, ss, sd); + tsc = status_get_sc(bl); + tstatus = status_get_status_data(bl); + if (sg->state.magic_power) //For magic power. + { + sc = status_get_sc(ss); + sstatus = status_get_status_data(ss); + } else { + sc = NULL; + sstatus = NULL; + } + type = SkillStatusChangeTable[sg->skill_id]; + skillid = sg->skill_id; + + if (sg->interval == -1) { + switch (sg->unit_id) { + case UNT_ANKLESNARE: //These happen when a trap is splash-triggered by multiple targets on the same cell. + case UNT_SPIDERWEB: + case UNT_FIREPILLAR_ACTIVE: + return 0; + default: + if (battle_config.error_log) + ShowError("skill_unit_onplace_timer: interval error (unit id %x)\n", sg->unit_id); + return 0; + } + } + + if ((ts = skill_unitgrouptickset_search(bl,sg,tick))) + { //Not all have it, eg: Traps don't have it even though they can be hit by Heaven's Drive [Skotlex] + diff = DIFF_TICK(tick,ts->tick); + if (diff < 0) + return 0; + ts->tick = tick+sg->interval; + + // GXは?dなっていたら3HITしない + if ((skillid==CR_GRANDCROSS || skillid==NPC_GRANDDARKNESS) && !battle_config.gx_allhit) + ts->tick += sg->interval*(map_count_oncell(bl->m,bl->x,bl->y,0)-1); + } + //Temporarily set magic power to have it take effect. [Skotlex] + if (sg->state.magic_power && sc && sc->data[SC_MAGICPOWER].timer == -1) + { //Store previous values. + matk_min = sstatus->matk_min; + matk_max = sstatus->matk_max; + //Note to NOT return from the function until this is unset! + sstatus->matk_min = sc->data[SC_MAGICPOWER].val3; + sstatus->matk_min = sc->data[SC_MAGICPOWER].val4; + } + + switch (sg->unit_id) { + case UNT_FIREWALL: + { + int count=0; + if (tstatus->def_ele == ELE_FIRE || battle_check_undead(tstatus->race, tstatus->def_ele)) { + //This is the best Aegis approximation we can do without + //changing the minimum skill unit interval. [Skotlex] + while (count++ < battle_config.firewall_hits_on_undead && src->val2-- && !status_isdead(bl)) + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*10,1); + } else { + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + src->val2--; + } + if (src->val2<=0) + skill_delunit(src); + break; + } + case UNT_SANCTUARY: + if (battle_check_undead(tstatus->race, tstatus->def_ele) || tstatus->race==RC_DEMON) + { //Only damage enemies with offensive Sanctuary. [Skotlex] + if(battle_check_target(&src->bl,bl,BCT_ENEMY)>0 && + skill_attack(BF_MAGIC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0)) + // reduce healing count if this was meant for damaging [hekate] + sg->val1 -= 2; + } else { + int heal = sg->val2; + if (tstatus->hp >= tstatus->max_hp) + break; + if (status_isimmune(bl)) + heal = 0; /* 黄金蟲カ?[ド?iヒ?[ル量0?j */ + clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); + status_heal(bl, heal, 0, 0); + if (diff >= 500) + sg->val1--; + } + if (sg->val1 <= 0) + skill_delunitgroup(NULL,sg); + break; + case UNT_MAGNUS: + if (!battle_check_undead(tstatus->race,tstatus->def_ele) && tstatus->race!=RC_DEMON) + break; + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + + case UNT_ATTACK_SKILLS: + switch (sg->skill_id) + { + case SG_SUN_WARM: //SG skills [Komurka] + case SG_MOON_WARM: + case SG_STAR_WARM: + if(bl->type==BL_PC) + //Only damage SP [Skotlex] + status_zap(bl, 0, 60); + else if(status_charge(bl, 0, 2)) + //Otherwise, Knockback attack. + skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + default: + skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + } + break; + case UNT_DESPERADO: + if (!(rand()%10)) //Has a low chance of connecting. [Skotlex] + skill_attack(BF_WEAPON,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + + case UNT_FIREPILLAR_WAITING: + skill_unitsetting(ss,sg->skill_id,sg->skill_lv,src->bl.x,src->bl.y,1); + skill_delunit(src); + break; + + case UNT_FIREPILLAR_ACTIVE: + map_foreachinrange(skill_attack_area,bl, + skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, + BF_MAGIC,ss,&src->bl,sg->skill_id,sg->skill_lv,tick,0,BCT_ENEMY); // area damage [Celest] + sg->interval = -1; //Mark it used up so others can't trigger it for massive splash damage. [Skotlex] + sg->limit=DIFF_TICK(tick,sg->tick) + 1500; + break; + + case UNT_SKIDTRAP: + { + skill_blown(&src->bl,bl,skill_get_blewcount(sg->skill_id,sg->skill_lv)|0x10000); + sg->unit_id = UNT_USED_TRAPS; + clif_changetraplook(&src->bl, UNT_USED_TRAPS); + sg->limit=DIFF_TICK(tick,sg->tick)+1500; + sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] + } + break; + + case UNT_SPIDERWEB: + case UNT_ANKLESNARE: + if(sg->val2==0 && tsc && tsc->data[type].timer==-1){ + int sec = skill_get_time2(sg->skill_id,sg->skill_lv); + if (sc_start(bl,type,100,sg->skill_lv,sec)) + { + struct TimerData* td = get_timer(tsc->data[type].timer); + if (td) sec = DIFF_TICK(td->tick, tick); + map_moveblock(bl, src->bl.x, src->bl.y, tick); + clif_fixpos(bl); + sg->val2=bl->id; + } else + sec = 3000; //Couldn't trap it? + //clif_01ac(&src->bl); //Removed? Check the openkore description of this packet: [Skotlex] + // 01AC: long ID + // Indicates that an object is trapped, but ID is not a + // valid monster or player ID. + sg->limit = DIFF_TICK(tick,sg->tick)+sec; + sg->interval = -1; + src->range = 0; + } + break; + + case UNT_VENOMDUST: + if(tsc && tsc->data[type].timer==-1 ) + status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8); + break; + + case UNT_LANDMINE: + skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + sg->unit_id = UNT_USED_TRAPS; + clif_changetraplook(&src->bl, UNT_FIREPILLAR_ACTIVE); + sg->limit=DIFF_TICK(tick,sg->tick)+1500; + sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] + break; + + case UNT_BLASTMINE: + case UNT_SHOCKWAVE: + case UNT_SANDMAN: + case UNT_FLASHER: + case UNT_FREEZINGTRAP: + case UNT_CLAYMORETRAP: + map_foreachinrange(skill_trap_splash,&src->bl, + skill_get_splash(sg->skill_id, sg->skill_lv), sg->bl_flag, + &src->bl,tick); + sg->unit_id = UNT_USED_TRAPS; + clif_changetraplook(&src->bl, UNT_USED_TRAPS); + sg->limit=DIFF_TICK(tick,sg->tick)+1500; + sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] + break; + + case UNT_TALKIEBOX: + if (sg->src_id == bl->id) //自分が踏んでも発動しない + break; + if (sg->val2 == 0){ + clif_talkiebox(&src->bl, sg->valstr); + sg->unit_id = UNT_USED_TRAPS; + clif_changetraplook(&src->bl, UNT_USED_TRAPS); + sg->limit = DIFF_TICK(tick, sg->tick) + 5000; + sg->val2 = -1; //踏んだ + sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] + } + break; + + case UNT_LULLABY: + if (ss->id == bl->id) + break; + skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick); + break; + + case UNT_UGLYDANCE: //Ugly Dance [Skotlex] + if (ss->id == bl->id) + break; + if (bl->type == BL_PC) + skill_additional_effect(ss, bl, sg->skill_id, sg->skill_lv, BF_LONG|BF_SKILL|BF_MISC, tick); + break; + + case UNT_DISSONANCE: + skill_attack(BF_MISC, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + break; + + case UNT_APPLEIDUN: //Apple of Idun [Skotlex] + { + int heal; + if (sg->src_id == bl->id) + break; + heal = sg->val2; + clif_skill_nodamage(&src->bl, bl, AL_HEAL, heal, 1); + status_heal(bl, heal, 0, 0); + break; + } + + case UNT_DEMONSTRATION: + skill_attack(BF_WEAPON, ss, &src->bl, bl, sg->skill_id, sg->skill_lv, tick, 0); + break; + + case UNT_GOSPEL: + if (rand()%100 > sg->skill_lv*10) + break; + if (ss != bl && battle_check_target(ss,bl,BCT_PARTY)>0) { // Support Effect only on party, not guild + int i = rand()%13; // Positive buff count + switch (i) + { + case 0: // Heal 1~9999 HP + { + int heal = rand() %9999+1; + clif_skill_nodamage(ss,bl,AL_HEAL,heal,1); + status_heal(bl,heal,0,0); + } + break; + case 1: // End all negative status + status_change_clear_buffs(bl,2); + break; + case 2: // Level 10 Blessing + sc_start(bl,SC_BLESSING,100,10,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 3: // Level 10 Increase AGI + sc_start(bl,SC_INCREASEAGI,100,10,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 4: // Enchant weapon with Holy element + sc_start(bl,SC_ASPERSIO,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 5: // Enchant armor with Holy element + sc_start(bl,SC_BENEDICTIO,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 6: // MaxHP +100% + sc_start(bl,SC_INCMHPRATE,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 7: // MaxSP +100% + sc_start(bl,SC_INCMSPRATE,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 8: // All stats +20 + sc_start(bl,SC_INCALLSTATUS,100,20,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 9: // DEF +25% + sc_start(bl,SC_INCDEFRATE,100,25,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 10: // ATK +100% + sc_start(bl,SC_INCATKRATE,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 11: // HIT/Flee +50 + sc_start(bl,SC_INCHIT,100,50,skill_get_time2(sg->skill_id, sg->skill_lv)); + sc_start(bl,SC_INCFLEE,100,50,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 12: // Immunity to all status + sc_start(bl,SC_SCRESIST,100,100,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + } + } + else if (battle_check_target(&src->bl,bl,BCT_ENEMY)>0) { // Offensive Effect + int i = rand()%9; // Negative buff count + switch (i) + { + case 0: // Deal 1~9999 damage + skill_attack(BF_MISC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + case 1: // Curse + sc_start(bl,SC_CURSE,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 2: // Blind + sc_start(bl,SC_BLIND,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 3: // Poison + sc_start(bl,SC_POISON,100,1,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 4: // Level 10 Provoke + sc_start(bl,SC_PROVOKE,100,10,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 5: // DEF -100% + sc_start(bl,SC_INCDEFRATE,100,-100,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 6: // ATK -100% + sc_start(bl,SC_INCATKRATE,100,-100,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 7: // Flee -100% + sc_start(bl,SC_INCFLEERATE,100,-100,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + case 8: // Speed/ASPD -25% + sc_start4(bl,SC_GOSPEL,100,1,0,0,BCT_ENEMY,skill_get_time2(sg->skill_id, sg->skill_lv)); + break; + } + } + break; + + case UNT_GRAVITATION: + skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + } + + if (sg->state.magic_power && sc && sc->data[SC_MAGICPOWER].timer == -1) + { //Unset magic power. + sstatus->matk_min = matk_min; + sstatus->matk_max = matk_max; + } + + if (bl->type == BL_MOB && ss != bl) + mobskill_event((TBL_MOB*)bl, ss, tick, MSC_SKILLUSED|(skillid<<16)); + + return skillid; +} +/*========================================== + * スキルユニットから離?する(もしくはしている)?? + *------------------------------------------ + */ +int skill_unit_onout(struct skill_unit *src,struct block_list *bl,unsigned int tick) +{ + struct skill_unit_group *sg; + struct status_change *sc; + int type; + + nullpo_retr(0, src); + nullpo_retr(0, bl); + nullpo_retr(0, sg=src->group); + sc = status_get_sc(bl); + if (sc && !sc->count) + sc = NULL; + + type = SkillStatusChangeTable[sg->skill_id]; + + if (bl->prev==NULL || !src->alive || //Need to delete the trap if the source died. + (status_isdead(bl) && sg->unit_id != UNT_ANKLESNARE && sg->unit_id != UNT_SPIDERWEB)) + return 0; + + switch(sg->unit_id){ + case UNT_SAFETYWALL: + if (sc && sc->data[type].timer!=-1) + status_change_end(bl,type,-1); + break; + case UNT_ANKLESNARE: + { + struct block_list *target = map_id2bl(sg->val2); + if(target && target == bl){ + status_change_end(bl,SC_ANKLE,-1); + sg->limit=DIFF_TICK(tick,sg->tick)+1000; + sg->state.into_abyss = 1; //Prevent Remove Trap from giving you the trap back. [Skotlex] + } + else + return 0; + break; + } + case UNT_BASILICA: //Clear basilica if the owner moved [Skotlex] + case UNT_HERMODE: //Clear Hermode if the owner moved. + if (sc && sc->data[type].timer!=-1 && sc->data[type].val3 == BCT_SELF && sc->data[type].val4 == sg->group_id) + status_change_end(bl,type,-1); + break; + + case UNT_SPIDERWEB: /* スパイダ?ウェッブ */ + { + struct block_list *target = map_id2bl(sg->val2); + if (target && target==bl) + { + status_change_end(bl,SC_SPIDERWEB,-1); + sg->limit = DIFF_TICK(tick,sg->tick)+1000; + } + break; + } + } + return sg->skill_id; +} + +/*========================================== + * Triggered when a char steps out of a skill group [Skotlex] + *------------------------------------------ + */ +static int skill_unit_onleft(int skill_id, struct block_list *bl,unsigned int tick) +{ + struct status_change *sc; + int type; + + sc = status_get_sc(bl); + if (sc && !sc->count) + sc = NULL; + + type = SkillStatusChangeTable[skill_id]; + + switch (skill_id) + { + case WZ_QUAGMIRE: + if (bl->type==BL_MOB) + break; + if (sc && sc->data[type].timer != -1) + status_change_end(bl, type, -1); + break; + + case BD_LULLABY: + case BD_RICHMANKIM: + case BD_ETERNALCHAOS: + case BD_DRUMBATTLEFIELD: + case BD_RINGNIBELUNGEN: + case BD_ROKISWEIL: + case BD_INTOABYSS: + case BD_SIEGFRIED: + if(sc && sc->data[SC_DANCING].timer != -1 && sc->data[SC_DANCING].val1 == skill_id) + { //Check if you just stepped out of your ensemble skill to cancel dancing. [Skotlex] + //We don't check for SC_LONGING because someone could always have knocked you back and out of the song/dance. + //FIXME: This code is not perfect, it doesn't checks for the real ensemble's owner, + //it only checks if you are doing the same ensemble. So if there's two chars doing an ensemble + //which overlaps, by stepping outside of the other parther's ensemble will cause you to cancel + //your own. Let's pray that scenario is pretty unlikely and noone will complain too much about it. + skill_stop_dancing(bl); + } + case MG_SAFETYWALL: + case AL_PNEUMA: + case SA_VOLCANO: + case SA_DELUGE: + case SA_VIOLENTGALE: + case CG_HERMODE: + case HW_GRAVITATION: + case NJ_SUITON: + if (sc && sc->data[type].timer != -1) + status_change_end(bl, type, -1); + break; + + case BA_POEMBRAGI: + case BA_WHISTLE: + case BA_ASSASSINCROSS: + case BA_APPLEIDUN: + case DC_HUMMING: + case DC_DONTFORGETME: + case DC_FORTUNEKISS: + case DC_SERVICEFORYOU: + if (sc && sc->data[type].timer != -1) + { + delete_timer(sc->data[type].timer, status_change_timer); + //NOTE: It'd be nice if we could get the skill_lv for a more accurate extra time, but alas... + //not possible on our current implementation. + sc->data[type].val4 = 1; //Store the fact that this is a "reduced" duration effect. + sc->data[type].timer = add_timer(tick+skill_get_time2(skill_id,1), status_change_timer, bl->id, type); + } + break; + case PF_FOGWALL: + if (sc && sc->data[type].timer != -1) + { + status_change_end(bl,type,-1); + if (sc->data[SC_BLIND].timer!=-1) + { + if (bl->type == BL_PC) //Players get blind ended inmediately, others have it still for 30 secs. [Skotlex] + status_change_end(bl, SC_BLIND, -1); + else { + delete_timer(sc->data[SC_BLIND].timer, status_change_timer); + sc->data[SC_BLIND].timer = add_timer(30000+tick, status_change_timer, bl->id, SC_BLIND); + } + } + } + break; + case UNT_GOSPEL: + if (sc && sc->data[type].timer != -1 && sc->data[type].val4 == BCT_ALL) //End item-no-use Gospel Effect. [Skotlex] + status_change_end(bl, type, -1); + break; + + } + return skill_id; +} + +/*========================================== + * Invoked when a unit cell has been placed/removed/deleted. + * flag values: + * flag&1: Invoke onplace function (otherwise invoke onout) + * flag&4: Invoke a onleft call (the unit might be scheduled for deletion) + *------------------------------------------ + */ +int skill_unit_effect(struct block_list *bl,va_list ap) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + int flag; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, unit=va_arg(ap,struct skill_unit*)); + tick = va_arg(ap,unsigned int); + flag = va_arg(ap,unsigned int); + + if (!unit->alive || bl->prev==NULL) + return 0; + + nullpo_retr(0, group=unit->group); + + if (flag&1) + skill_unit_onplace(unit,bl,tick); + else + skill_unit_onout(unit,bl,tick); + + if (flag&4) skill_unit_onleft(group->skill_id, bl, tick); + return 0; +} + +/*========================================== + * スキルユニットの限界イベント + *------------------------------------------ + */ +int skill_unit_onlimit(struct skill_unit *src,unsigned int tick) +{ + struct skill_unit_group *sg; + nullpo_retr(0, src); + nullpo_retr(0, sg=src->group); + + switch(sg->unit_id){ + case UNT_WARP_ACTIVE: /* ??プポ?タル(?動前) */ + { + struct skill_unit_group *group= + skill_unitsetting(map_id2bl(sg->src_id),sg->skill_id,sg->skill_lv, + src->bl.x,src->bl.y,1); + if(group == NULL) + return 0; + group->val2=sg->val2; //Copy the (x,y) position you warp to + group->val3=sg->val3; //as well as the mapindex to warp to. + } + break; + + case UNT_ICEWALL: /* アイスウォ?ル */ + clif_changemapcell(src->bl.m,src->bl.x,src->bl.y,src->val2,1); + break; + case UNT_CALLFAMILY: /* なたに?いたい */ + { + struct map_session_data *sd = NULL; + if(sg->val1) { + sd = map_charid2sd(sg->val1); + sg->val1 = 0; + if (sd && !map[sd->bl.m].flag.nowarp) + pc_setpos(sd,map[src->bl.m].index,src->bl.x,src->bl.y,3); + } + if(sg->val2) { + sd = map_charid2sd(sg->val2); + sg->val2 = 0; + if (sd && !map[sd->bl.m].flag.nowarp) + pc_setpos(sd,map[src->bl.m].index,src->bl.x,src->bl.y,3); + } + } + break; + } + + return 0; +} +/*========================================== + * スキルユニットのダ??ジイベント + *------------------------------------------ + */ +int skill_unit_ondamaged(struct skill_unit *src,struct block_list *bl, + int damage,unsigned int tick) +{ + struct skill_unit_group *sg; + + nullpo_retr(0, src); + nullpo_retr(0, sg=src->group); + + if (skill_get_inf2(sg->skill_id)&INF2_TRAP && damage > 0) + skill_delunitgroup(NULL,sg); + else + switch(sg->unit_id){ + case UNT_ICEWALL: + src->val1-=damage; + break; + default: + damage = 0; + break; + } + return damage; +} + +static int skill_moonlit_sub(struct block_list *bl, va_list ap) { + struct block_list *src = va_arg(ap, struct block_list*); + struct block_list *partner = va_arg(ap, struct block_list*); + int blowcount = va_arg(ap, int); + if (bl == src || bl == partner) + return 0; + skill_blown(src, bl, blowcount); + return 1; +} + +/*========================================== + * Starts the moonlit effect by first knocking back all other characters in the vecinity. + * partner may be null, but src cannot be. + *------------------------------------------ + */ +static void skill_moonlit(struct block_list* src, struct block_list* partner, int skilllv) +{ + int range = skill_get_range2(src, CG_MOONLIT, skilllv); + int blowcount = range+1, time = skill_get_time(CG_MOONLIT,skilllv); + + map_foreachinrange(skill_moonlit_sub,src, + skill_get_splash(CG_MOONLIT, skilllv), + BL_CHAR,src,partner,blowcount); + if(partner) + map_foreachinrange(skill_moonlit_sub,partner, + skill_get_splash(CG_MOONLIT, skilllv), + BL_CHAR,src,partner,blowcount); + + sc_start4(src,SC_DANCING,100,CG_MOONLIT,0,0,partner?partner->id:BCT_SELF,time+1000); + sc_start4(src,SkillStatusChangeTable[CG_MOONLIT],100,skilllv,0,0,0,time); + + if (partner) { + sc_start4(partner,SC_DANCING,100,CG_MOONLIT,0,0,src->id,time+1000); + sc_start4(partner,SkillStatusChangeTable[CG_MOONLIT],100,skilllv,0,0,0,time); + } + +} +/*========================================== + * 範??キャラ存?ン確認判定??(foreachinarea) + *------------------------------------------ + */ + +static int skill_check_condition_char_sub (struct block_list *bl, va_list ap) +{ + int *c, skillid; + struct block_list *src; + struct map_session_data *sd; + struct map_session_data *tsd; + int *p_sd; //Contains the list of characters found. + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, tsd=(struct map_session_data*)bl); + nullpo_retr(0, src=va_arg(ap,struct block_list *)); + nullpo_retr(0, sd=(struct map_session_data*)src); + + c=va_arg(ap,int *); + p_sd = va_arg(ap, int *); + skillid = va_arg(ap,int); + + if ((skillid != PR_BENEDICTIO && *c >=1) || *c >=2) + return 0; //Partner found for ensembles, or the two companions for Benedictio. [Skotlex] + + if (bl == src) + return 0; + + if(pc_isdead(tsd)) + return 0; + + if (tsd->sc.count && (tsd->sc.data[SC_SILENCE].timer != -1 || tsd->sc.data[SC_STUN].timer != -1)) + return 0; + + switch(skillid) + { + case PR_BENEDICTIO: /* ?ケ??~福 */ + { + int dir = map_calc_dir(&sd->bl,tsd->bl.x,tsd->bl.y); + dir = (unit_getdir(&sd->bl) + dir)%8; //This adjusts dir to account for the direction the sd is facing. + if ((tsd->class_&MAPID_BASEMASK) == MAPID_ACOLYTE && (dir == 2 || dir == 6) //Must be standing to the left/right of Priest. + && sd->status.sp >= 10) + p_sd[(*c)++]=tsd->bl.id; + return 1; + } + default: //Warning: Assuming Ensemble Dance/Songs for code speed. [Skotlex] + { + int skilllv; + if(pc_issit(tsd) || !unit_can_move(&tsd->bl)) + return 0; + if (sd->status.sex != tsd->status.sex && + (tsd->class_&MAPID_UPPERMASK) == MAPID_BARDDANCER && + (skilllv = pc_checkskill(tsd, skillid)) > 0 && + (tsd->weapontype1==W_MUSICAL || tsd->weapontype1==W_WHIP) && + sd->status.party_id && tsd->status.party_id && + sd->status.party_id == tsd->status.party_id && + tsd->sc.data[SC_DANCING].timer == -1) + { + p_sd[(*c)++]=tsd->bl.id; + return skilllv; + } else { + return 0; + } + } + break; + } + return 0; +} + +/*========================================== + * Checks and stores partners for ensemble skills [Skotlex] + *------------------------------------------ + */ +int skill_check_pc_partner(struct map_session_data *sd, int skill_id, int* skill_lv, int range, int cast_flag) +{ + static int c=0; + static int p_sd[2] = { 0, 0 }; + int i; + if (cast_flag) + { //Execute the skill on the partners. + struct map_session_data* tsd; + switch (skill_id) + { + case PR_BENEDICTIO: + for (i = 0; i < c; i++) + { + if ((tsd = map_id2sd(p_sd[i])) != NULL) + status_charge(&tsd->bl, 0, 10); + } + return c; + case CG_MOONLIT: + if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL) + { + clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1); + skill_moonlit(&sd->bl, &tsd->bl, *skill_lv); + tsd->skillid_dance = skill_id; + tsd->skilllv_dance = *skill_lv; + } + return c; + default: //Warning: Assuming Ensemble skills here (for speed) + if (c > 0 && (tsd = map_id2sd(p_sd[0])) != NULL) + { + sd->sc.data[SC_DANCING].val4= tsd->bl.id; + sc_start4(&tsd->bl,SC_DANCING,100,skill_id,sd->sc.data[SC_DANCING].val2,0,sd->bl.id,skill_get_time(skill_id,*skill_lv)+1000); + clif_skill_nodamage(&tsd->bl, &sd->bl, skill_id, *skill_lv, 1); + tsd->skillid_dance = skill_id; + tsd->skilllv_dance = *skill_lv; + } + return c; + } + } + //Else: new search for partners. + c = 0; + memset (p_sd, 0, sizeof(p_sd)); + i = map_foreachinrange(skill_check_condition_char_sub, &sd->bl, + range, BL_PC, &sd->bl, &c, &p_sd, skill_id); + + if (skill_id != PR_BENEDICTIO) //Apply the average lv to encore skills. + *skill_lv = (i+(*skill_lv))/(c+1); //I know c should be one, but this shows how it could be used for the average of n partners. + return c; +} + +/*========================================== + * 範??バイオプラント?Aスフィアマイン用Mob存?ン確認判定??(foreachinarea) + *------------------------------------------ + */ + +static int skill_check_condition_mob_master_sub(struct block_list *bl,va_list ap) +{ + int *c,src_id=0,mob_class=0; + struct mob_data *md; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, md=(struct mob_data*)bl); + nullpo_retr(0, src_id=va_arg(ap,int)); + nullpo_retr(0, mob_class=va_arg(ap,int)); + nullpo_retr(0, c=va_arg(ap,int *)); + + if(md->class_==mob_class && md->master_id==src_id) + (*c)++; + return 0; +} + +static int skill_check_condition_hermod_sub(struct block_list *bl,va_list ap) +{ + struct npc_data *nd; + nd=(struct npc_data*)bl; + + if (nd->bl.subtype == WARP) + return 1; + return 0; +} + +/*========================================== + * Determines if a given skill should be made to consume ammo + * when used by the player. [Skotlex] + *------------------------------------------ + */ +int skill_isammotype(TBL_PC *sd, int skill) +{ + return ( + (sd->status.weapon == W_BOW || (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) && + skill != HT_PHANTASMIC && skill != GS_MAGICALBULLET && + skill_get_type(skill) == BF_WEAPON && !(skill_get_nk(skill)&NK_NO_DAMAGE) + ); +} + +/*========================================== + * Checks that you have the requirements for casting a skill. + * Flag: + * &1: finished casting the skill (invoke hp/sp/item consumption) + * &2: picked menu entry (Warp Portal, Teleport and other menu based skills) + *------------------------------------------ + */ +int skill_check_condition(struct map_session_data *sd,int skill, int lv, int type) +{ + struct status_data *status; + struct status_change *sc; + int i,j,hp,sp,hp_rate,sp_rate,zeny,weapon,ammo,ammo_qty,state,spiritball,mhp; + int index[10],itemid[10],amount[10]; + int force_gem_flag = 0; + int delitem_flag = 1, checkitem_flag = 1; + + nullpo_retr(0, sd); + + if (lv <= 0) return 0; + + if( battle_config.gm_skilluncond && + pc_isGM(sd)>= battle_config.gm_skilluncond && + sd->skillitem != skill) + { //GMs don't override the skillItem check, otherwise they can use items without them being consumed! [Skotlex] + sd->skillitem = sd->skillitemlv = -1; + return 1; + } + + status = &sd->battle_status; + sc = &sd->sc; + if (!sc->count) + sc = NULL; + + if(pc_is90overweight(sd)) { + clif_skill_fail(sd,skill,9,0); + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + + if (sd->state.abra_flag) + { + sd->skillitem = sd->skillitemlv = -1; + if(type&1) sd->state.abra_flag = 0; + return 1; + } + + if (sd->menuskill_id == AM_PHARMACY && + (skill == AM_PHARMACY || skill == AC_MAKINGARROW || skill == BS_REPAIRWEAPON || + skill == AM_TWILIGHT1 || skill == AM_TWILIGHT2 || skill == AM_TWILIGHT3 + )) { + sd->skillitem = sd->skillitemlv = -1; + return 0; + } + + if(sd->skillitem == skill) { /* アイテムの??無???ャ功 */ + if(!type) //When a target was selected + { //Consume items that were skipped in pc_use_item [Skotlex] + if((i = sd->itemindex) == -1 || + sd->status.inventory[i].nameid != sd->itemid || + sd->inventory_data[i] == NULL || + !sd->inventory_data[i]->flag.delay_consume || + sd->status.inventory[i].amount < 1 + ) + { //Something went wrong, item exploit? + sd->itemid = sd->itemindex = -1; + return 0; + } + //Consume + sd->itemid = sd->itemindex = -1; + if(skill == WZ_EARTHSPIKE && sc && + sc->data[SC_TKREST].timer != -1 && rand()%100 > sc->data[SC_TKREST].val2) // [marquis007] + ; //Do not consume item. + else + pc_delitem(sd,i,1,0); + } + if (type&1) //Casting finished + sd->skillitem = sd->skillitemlv = -1; + return 1; + } + // for the guild skills [celest] + if (skill >= GD_SKILLBASE) + j = GD_SKILLRANGEMIN + skill - GD_SKILLBASE; + else + j = skill; + if (j < 0 || j >= MAX_SKILL_DB) + return 0; + //Code speedup, rather than using skill_get_* over and over again. + if (lv < 1 || lv > MAX_SKILL_LEVEL) + return 0; + hp = skill_db[j].hp[lv-1]; /* ?チ費HP */ + sp = skill_db[j].sp[lv-1]; /* ?チ費SP */ + if((sd->skillid_old == BD_ENCORE) && skill == sd->skillid_dance) + sp=sp/2; //アンコ?ル時はSP?チ費が半分 + hp_rate = skill_db[j].hp_rate[lv-1]; + sp_rate = skill_db[j].sp_rate[lv-1]; + zeny = skill_db[j].zeny[lv-1]; + weapon = skill_db[j].weapon; + ammo = skill_db[j].ammo; + ammo_qty = skill_db[j].ammo_qty[lv-1]; + state = skill_db[j].state; + spiritball = skill_db[j].spiritball[lv-1]; + mhp = skill_db[j].mhp[lv-1]; /* ?チ費HP */ + for(i = 0; i < 10; i++) { + itemid[i] = skill_db[j].itemid[i]; + amount[i] = skill_db[j].amount[i]; + } + if(mhp > 0) + hp += (status->max_hp * mhp)/100; + if(hp_rate > 0) + hp += (status->hp * hp_rate)/100; + else + hp += (status->max_hp * (-hp_rate))/100; + if(sp_rate > 0) + sp += (status->sp * sp_rate)/100; + else + sp += (status->max_sp * (-sp_rate))/100; + + if (!ammo && skill && skill_isammotype(sd, skill)) + { //Assume this skill is using the weapon, therefore it requires arrows. + ammo = 2; //1<<1 <- look 1 (arrows) moved right 1 times. + ammo_qty = skill_get_num(skill, lv); + if (ammo_qty < 0) ammo_qty *= -1; + } + + switch(skill) { // Check for cost reductions due to skills & SCs + case MC_MAMMONITE: + if(pc_checkskill(sd,BS_UNFAIRLYTRICK)>0) + zeny -= zeny*10/100; + break; + case AL_HOLYLIGHT: + if(sc && sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_PRIEST) + sp *= 5; + break; + case SL_SMA: + case SL_STUN: + case SL_STIN: + { + int kaina_lv = pc_checkskill(sd,SL_KAINA); + + if(kaina_lv==0 || sd->status.base_level<70) + break; + if(sd->status.base_level>=90) + sp -= sp*7*kaina_lv/100; + else if(sd->status.base_level>=80) + sp -= sp*5*kaina_lv/100; + else if(sd->status.base_level>=70) + sp -= sp*3*kaina_lv/100; + } + break; + case MO_TRIPLEATTACK: + case MO_CHAINCOMBO: + case MO_COMBOFINISH: + case CH_TIGERFIST: + case CH_CHAINCRUSH: + if(sc && sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_MONK) + sp -= sp*25/100; //FIXME: Need real data. this is a custom value. + break; + } + + if(sd->dsprate!=100) + sp=sp*sd->dsprate/100; /* ?チ費SP?C?ウ */ + + switch(skill) { + case SA_CASTCANCEL: + if(sd->ud.skilltimer == -1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case BS_MAXIMIZE: + case NV_TRICKDEAD: + case TF_HIDING: + case AS_CLOAKING: + case CR_AUTOGUARD: + case CR_DEFENDER: + case ST_CHASEWALK: + case PA_GOSPEL: + case CR_SHRINK: + case TK_RUN: + if(sc && sc->data[SkillStatusChangeTable[skill]].timer!=-1) + return 1; //Allow turning off. + break; + + case AL_WARP: + if(!(type&2)) //Delete the item when the portal has been selected (type&2). [Skotlex] + delitem_flag = 0; + if(!battle_config.duel_allow_teleport && sd->duel_group) { // duel restriction [LuzZza] + clif_displaymessage(sd->fd, "Duel: Can't use warp in duel."); + return 0; + } + break; + case MO_CALLSPIRITS: /* ?功 */ + if(sd->spiritball >= lv) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case CH_SOULCOLLECT: /* 狂?功 */ + if(sd->spiritball >= 5) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case MO_FINGEROFFENSIVE: //指? + case GS_FLING: + if (sd->spiritball > 0 && sd->spiritball < spiritball) { + spiritball = sd->spiritball; + sd->spiritball_old = sd->spiritball; + } + else sd->spiritball_old = spiritball; + break; + case MO_BODYRELOCATION: + if (sc && sc->data[SC_EXPLOSIONSPIRITS].timer!=-1) + spiritball = 0; + break; + case MO_CHAINCOMBO: //連打?カ + if(!sc) + return 0; + if(sc->data[SC_BLADESTOP].timer!=-1) + break; + if(sc->data[SC_COMBO].timer != -1 && sc->data[SC_COMBO].val1 == MO_TRIPLEATTACK) + break; + return 0; + case MO_COMBOFINISH: //猛龍? + if(!sc || sc->data[SC_COMBO].timer == -1 || sc->data[SC_COMBO].val1 != MO_CHAINCOMBO) + return 0; + break; + case CH_TIGERFIST: //伏虎? + if(!sc || sc->data[SC_COMBO].timer == -1 || sc->data[SC_COMBO].val1 != MO_COMBOFINISH) + return 0; + break; + case CH_CHAINCRUSH: //連柱崩? + if(!sc || sc->data[SC_COMBO].timer == -1) + return 0; + if(sc->data[SC_COMBO].val1 != MO_COMBOFINISH && sc->data[SC_COMBO].val1 != CH_TIGERFIST) + return 0; + break; + case MO_EXTREMITYFIST: +// if(sc && sc->data[SC_EXTREMITYFIST].timer != -1) //To disable Asura during the 5 min skill block uncomment this... +// return 0; + if(sc && sc->data[SC_BLADESTOP].timer!=-1) + spiritball--; + else if (sc && sc->data[SC_COMBO].timer != -1) { + switch(sc->data[SC_COMBO].val1) { + case MO_COMBOFINISH: + spiritball = 4; + break; + case CH_TIGERFIST: + spiritball = 3; + break; + case CH_CHAINCRUSH: //It should consume whatever is left as long as it's at least 1. + spiritball = sd->spiritball?sd->spiritball:1; + break; + default: + return 0; + } + } else if(!type && !unit_can_move(&sd->bl)) //Check only on begin casting. + { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex] + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + + case TK_MISSION: //Does not works on Non-Taekwon + if ((sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + + case TK_READYCOUNTER: + case TK_READYDOWN: + case TK_READYSTORM: + case TK_READYTURN: + case TK_JUMPKICK: + if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER) { + //They do not work on Soul Linkers. + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + + case TK_TURNKICK: + case TK_STORMKICK: + case TK_DOWNKICK: + case TK_COUNTER: + if(!sc || sc->data[SC_COMBO].timer == -1) + return 0; //Combo needs to be ready + if (pc_famerank(sd->char_id,MAPID_TAEKWON)) + { //Unlimited Combo + if (skill == sd->skillid_old) { + status_change_end(&sd->bl, SC_COMBO, -1); + sd->skillid_old = sd->skilllv_old = 0; + return 0; //Can't repeat previous combo skill. + } + break; + } else + if(sc->data[SC_COMBO].val1 == skill) + break; //Combo ready. + return 0; + case BD_ADAPTATION: /* アドリブ */ + { + struct skill_unit_group *group=NULL; + int time; + if(!sc || sc->data[SC_DANCING].timer==-1) + { + clif_skill_fail(sd,skill,0,0); + return 0; + } + group=(struct skill_unit_group*)sc->data[SC_DANCING].val2; + time = 1000*(sc->data[SC_DANCING].val3>>16); + if (!group || (skill_get_time(sc->data[SC_DANCING].val1,group->skill_lv) - time <= skill_get_time2(skill,lv))) + { + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + break; + case PR_BENEDICTIO: /* ?ケ??~福 */ + { + if (!battle_config.player_skill_partner_check || + (battle_config.gm_skilluncond && pc_isGM(sd) >= battle_config.gm_skilluncond) + ) + break; //No need to do any partner checking [Skotlex] + if (!(type&1)) + { //Started casting. + if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2) + { + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + else + { //Done casting + //Should I repeat the check? If so, it would be best to only do this on cast-ending. [Skotlex] + skill_check_pc_partner(sd, skill, &lv, 1, 1); + } + } + break; + case AM_CANNIBALIZE: /* バイオプラント */ + case AM_SPHEREMINE: /* スフィア?マイン */ + if(type&1){ + int c=0; + int summons[5] = { 1020, 1068, 1118, 1500, 1368 }; + int maxcount = (skill==AM_CANNIBALIZE)? 6-lv : skill_get_maxcount(skill); + int mob_class = (skill==AM_CANNIBALIZE)? summons[lv-1] :1142; + if(battle_config.land_skill_limit && maxcount>0 && (battle_config.land_skill_limit&BL_PC)) { + map_foreachinmap(skill_check_condition_mob_master_sub ,sd->bl.m, BL_MOB, sd->bl.id, mob_class,&c ); + if(c >= maxcount){ + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + } + break; + case WZ_FIREPILLAR: // celest + if (lv <= 5) // no gems required at level 1-5 + itemid[0] = 0; + break; + case SL_SMA: + if(type) break; //Only do the combo check when the target is selected (type == 0) + if(!sd || sc->data[SC_SMA].timer == -1) + return 0; + break; + + case HT_POWER: + if(!sc || sc->data[SC_COMBO].timer == -1 || sc->data[SC_COMBO].val1 != skill) + return 0; + break; + case HW_GANBANTEIN: + force_gem_flag = 1; + break; + case AM_BERSERKPITCHER: + case AM_POTIONPITCHER: + case CR_SLIMPITCHER: + case MG_STONECURSE: + case CR_CULTIVATION: + case SA_FLAMELAUNCHER: + case SA_FROSTWEAPON: + case SA_LIGHTNINGLOADER: + case SA_SEISMICWEAPON: + delitem_flag = 0; + break; + case SA_DELUGE: + case SA_VOLCANO: + case SA_VIOLENTGALE: + case SA_LANDPROTECTOR: + { //Does not consumes if the skill is already active. [Skotlex] + struct skill_unit_group *sg; + if ((sg= skill_locate_element_field(&sd->bl)) != NULL && sg->skill_id == skill) + { + if (sg->limit - DIFF_TICK(gettick(), sg->tick) > 0) + checkitem_flag = delitem_flag = 0; + else sg->limit = 0; //Disable it. + } + break; + } + case CG_HERMODE: + if (map_foreachinrange (skill_check_condition_hermod_sub, &sd->bl, + skill_get_splash(skill, lv), BL_NPC) < 1) + { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case CG_MOONLIT: //Check there's no wall in the range+1 area around the caster. [Skotlex] + { + int i,x,y,range = skill_get_splash(skill, lv)+1; + int size = range*2+1; + for (i=0;ibl.x+(i%size-range); + y = sd->bl.y+(i/size-range); + if (map_getcell(sd->bl.m,x,y,CELL_CHKWALL)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + } + } + break; + case PR_REDEMPTIO: + { + int exp; + if(((exp = pc_nextbaseexp(sd)) > 0 && sd->status.base_exp*100/exp < 1) || + ((exp = pc_nextjobexp(sd)) > 0 && sd->status.job_exp*100/exp < 1)) { + clif_skill_fail(sd,skill,0,0); //Not enough exp. + return 0; + } + break; + } + case AM_TWILIGHT2: + case AM_TWILIGHT3: + if (!party_skill_check(sd, sd->status.party_id, skill, lv)) + { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + //SHOULD BE OPTIMALIZED [Komurka] + //Optimized #1. optimize comfort later. [Vicious] + case SG_SUN_WARM: + case SG_MOON_WARM: + case SG_STAR_WARM: + if ((sd->bl.m == sd->feel_map[skill-SG_SUN_WARM].m) || (sc && sc->data[SC_MIRACLE].timer!=-1)) + break; + clif_skill_fail(sd,skill,0,0); + return 0; + break; + case SG_SUN_COMFORT: + if ((sd->bl.m == sd->feel_map[0].m && (battle_config.allow_skill_without_day || is_day_of_sun())) || (sc && sc->data[SC_MIRACLE].timer!=-1)) + break; + clif_skill_fail(sd,skill,0,0); + return 0; + case SG_MOON_COMFORT: + if ((sd->bl.m == sd->feel_map[1].m && (battle_config.allow_skill_without_day || is_day_of_moon())) || (sc && sc->data[SC_MIRACLE].timer!=-1)) + break; + clif_skill_fail(sd,skill,0,0); + return 0; + case SG_STAR_COMFORT: + if ((sd->bl.m == sd->feel_map[2].m && (battle_config.allow_skill_without_day || is_day_of_star())) || (sc && sc->data[SC_MIRACLE].timer!=-1)) + break; + clif_skill_fail(sd,skill,0,0); + return 0; + case SG_FUSION: + if (!sc || sc->data[SC_FUSION].timer!=-1) + return 1; + if (sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_STAR) + break; + return 0; + case GD_BATTLEORDER: + case GD_REGENERATION: + case GD_RESTORE: + case GD_EMERGENCYCALL: + if (!sd->status.guild_id || !sd->state.gmaster_flag) + return 0; + if (lv <= 0) + return 0; + + if (skill == GD_EMERGENCYCALL) { + if (!map_flag_gvg(sd->bl.m)) + { //if not allowed to warp to the map (castles are always allowed) + clif_skill_fail(sd,skill,0,0); + return 0; + } + } else if (!agit_flag) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + + //Until they're at right position - gs_skillcheck- [Vicious] + case GS_GLITTERING: + if(sd->spiritball >= 10) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + zeny = 1; + break; + + case NJ_ISSEN: + if (sc && sc->data[SC_NEN].timer!=-1) + return 0; + break; + } + + if(!(type&2)){ + if( hp>0 && status->hp <= hp) { /* HPチェック */ + clif_skill_fail(sd,skill,2,0); /* HP不足?F失敗通知 */ + return 0; + } + if( sp>0 && status->sp < sp) { /* SPチェック */ + clif_skill_fail(sd,skill,1,0); /* SP不足?F失敗通知 */ + return 0; + } + if( zeny>0 && sd->status.zeny < zeny) { + clif_skill_fail(sd,skill,5,0); + return 0; + } + if(!(weapon & (1<status.weapon) ) ) { + clif_skill_fail(sd,skill,6,0); + return 0; + } + if(ammo) { //Skill requires stuff equipped in the arrow slot. + if((i=sd->equip_index[10]) < 0 || + !sd->inventory_data[i] || + sd->status.inventory[i].amount < ammo_qty + ) { + clif_arrow_fail(sd,0); + return 0; + } + if (!(ammo&1<inventory_data[i]->look)) + { //Ammo type check. Send the "wrong weapon type" message + //which is the closest we have to wrong ammo type. [Skotlex] + clif_arrow_fail(sd,0); //Haplo suggested we just send the equip-arrows message instead. [Skotlex] + //clif_skill_fail(sd,skill,6,0); + return 0; + } + } + if( spiritball > 0 && sd->spiritball < spiritball) { + clif_skill_fail(sd,skill,0,0); // 氣球不足 + return 0; + } + } + + switch(state) { + case ST_HIDING: + if(!(sc && sc->option&OPTION_HIDE)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_CLOAKING: + if(!pc_iscloaking(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_HIDDEN: + if(!pc_ishiding(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_RIDING: + if(!pc_isriding(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_FALCON: + if(!pc_isfalcon(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_CART: + if(!pc_iscarton(sd)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_SHIELD: + if(sd->status.shield <= 0) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_SIGHT: + if((!sc || sc->data[SC_SIGHT].timer == -1) && type&1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_EXPLOSIONSPIRITS: + if(!sc || sc->data[SC_EXPLOSIONSPIRITS].timer == -1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_CARTBOOST: + if(!pc_iscarton(sd) || !sc || sc->data[SC_CARTBOOST].timer == -1) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_RECOV_WEIGHT_RATE: + if(battle_config.natural_heal_weight_rate <= 100 && sd->weight*100/sd->max_weight >= battle_config.natural_heal_weight_rate) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_MOVE_ENABLE: + //Check only on begin casting. [Skotlex] + if(!type && !unit_can_move(&sd->bl)) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + case ST_WATER: + if ( + (!sc || (sc->data[SC_DELUGE].timer == -1 && sc->data[SC_SUITON].timer == -1)) && + (!map_getcell(sd->bl.m,sd->bl.x,sd->bl.y,CELL_CHKWATER)) + ) { + clif_skill_fail(sd,skill,0,0); + return 0; + } + break; + } + + if (checkitem_flag) { + for(i=0;i<10;i++) { + int x = lv%11 - 1; + index[i] = -1; + if(itemid[i] <= 0) + continue; + if(itemid[i] >= 715 && itemid[i] <= 717 && sd->special_state.no_gemstone && !force_gem_flag) + continue; + if(((itemid[i] >= 715 && itemid[i] <= 717) || itemid[i] == 1065) + && sc && sc->data[SC_INTOABYSS].timer != -1 && !force_gem_flag) + continue; + if((skill == AM_POTIONPITCHER || + skill == CR_SLIMPITCHER || + skill == CR_CULTIVATION) && i != x) + continue; + + index[i] = pc_search_inventory(sd,itemid[i]); + if(index[i] < 0 || sd->status.inventory[index[i]].amount < amount[i]) { + if(itemid[i] == 716 || itemid[i] == 717) + clif_skill_fail(sd,skill,(7+(itemid[i]-716)),0); + else + clif_skill_fail(sd,skill,0,0); + return 0; + } + if((itemid[i] >= 715 && itemid[i] <= 717) && sc && sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_WIZARD) + index[i] = -1; //Gemstones are checked, but not substracted from inventory. + + } + } + + if(!(type&1)) + return 1; + + sd->state.arrow_atk = ammo?1:0; //Update arrow-atk state on cast-end. + + if(delitem_flag) { + for(i=0;i<10;i++) { + if(index[i] >= 0) + pc_delitem(sd,index[i],amount[i],0); // アイテム?チ費 + } +// Ammo is now reduced in battle_calc_weapon_attack. [Skotlex] +// if (ammo && battle_config.arrow_decrement) +// pc_delitem(sd,sd->equip_index[10],ammo_qty,0); + } + + if(type&2) + return 1; + + if(sp || hp) + status_zap(&sd->bl, hp, sp); + if(zeny > 0) // Zeny?チ費 + pc_payzeny(sd,zeny); + if(spiritball > 0) // 氣球?チ費 + pc_delspiritball(sd,spiritball,0); + + return 1; +} + +/*========================================== + * 詠?・時間計算 + *------------------------------------------ + */ +int skill_castfix( struct block_list *bl, int skill_id, int skill_lv) +{ + int castnodex = skill_get_castnodex(skill_id, skill_lv); + int time = skill_get_cast(skill_id, skill_lv); + struct map_session_data *sd; + + nullpo_retr(0, bl); + BL_CAST(BL_PC, bl, sd); + + // calculate base cast time (reduced by dex) + if (!(castnodex&1)) { // castnodex&~1? wtf. [blackhole89] + int scale = battle_config.castrate_dex_scale - status_get_dex(bl); + if (scale > 0) // not instant cast + time = time * scale / battle_config.castrate_dex_scale; + else return 0; // instant cast + } + + // calculate cast time reduced by card bonuses + if (sd && sd->castrate != 100) + time = time * sd->castrate / 100; + + // config cast time multiplier + if (battle_config.cast_rate != 100) + time = time * battle_config.cast_rate / 100; + + // calculate cast time reduced by skill bonuses + if (!(castnodex&2)) + time = skill_castfix_sc(bl, time); + + // return final cast time + return (time > 0) ? time : 0; +} + +/*========================================== + * Does cast-time reductions based on sc data. + *------------------------------------------ + */ +int skill_castfix_sc(struct block_list *bl, int time) +{ + struct status_change *sc = status_get_sc(bl); + + if (time <= 0) return 0; + + if (sc && sc->count) { + if (sc->data[SC_SUFFRAGIUM].timer != -1) { + time -= time * (sc->data[SC_SUFFRAGIUM].val1 * 15) / 100; + status_change_end(bl, SC_SUFFRAGIUM, -1); + } + if (sc->data[SC_POEMBRAGI].timer != -1) + time -= time * sc->data[SC_POEMBRAGI].val2 / 100; + } + return (time > 0) ? time : 0; +} + +/*========================================== + * ディレイ計算 + *------------------------------------------ + */ +int skill_delayfix(struct block_list *bl, int skill_id, int skill_lv) +{ + int delaynodex = skill_get_delaynodex(skill_id, skill_lv); + int time = skill_get_delay(skill_id, skill_lv); + + nullpo_retr(0, bl); + + if (bl->type == BL_MOB) + return 0; //Mobs have no delay other than the skill-specific delay in their skill db. [Skotlex] + + // instant cast attack skills depend on aspd as delay [celest] + if (time == 0) { + if (skill_get_type(skill_id) == BF_WEAPON && !(skill_get_nk(skill_id)&NK_NO_DAMAGE)) + time = status_get_adelay(bl); //Use attack delay as default delay. + else + time = battle_config.default_skill_delay; + } else if (time < 0) + time = -time + status_get_adelay(bl); // if set to <0, the attack delay is added. + + if (battle_config.delay_dependon_dex && !(delaynodex&1)) + { // if skill casttime is allowed to be reduced by dex + int scale = battle_config.castrate_dex_scale - status_get_dex(bl); + if (scale > 0) + time = time * scale / battle_config.castrate_dex_scale; + else //To be capped later to minimum. + time = 0; + } + + if (bl->type == BL_PC && ((TBL_PC*)bl)->delayrate != 100) + time = time * ((TBL_PC*)bl)->delayrate / 100; + + if (battle_config.delay_rate != 100) + time = time * battle_config.delay_rate / 100; + + if (!(delaynodex&2)) + { /* ブラギの? */ + struct status_change *sc; + sc= status_get_sc(bl); + if (sc && sc->count) { + if (sc->data[SC_POEMBRAGI].timer != -1) + time -= time * sc->data[SC_POEMBRAGI].val3 / 100; + if (sc->data[SC_SPIRIT].timer != -1) + switch (skill_id) { + case CR_SHIELDBOOMERANG: + if (sc->data[SC_SPIRIT].val2 == SL_CRUSADER) + time /=2; + break; + case AS_SONICBLOW: + if (!map_flag_gvg(bl->m) && sc->data[SC_SPIRIT].val2 == SL_ASSASIN) + time /= 2; + break; + } + } + } + + return (time < battle_config.min_skill_delay_limit)? + battle_config.min_skill_delay_limit:time; +} + +/*========================================= + * ブランディッシュスピア ?炎範?決定 + *---------------------------------------- + */ +void skill_brandishspear_first(struct square *tc,int dir,int x,int y){ + + nullpo_retv(tc); + + if(dir == 0){ + tc->val1[0]=x-2; + tc->val1[1]=x-1; + tc->val1[2]=x; + tc->val1[3]=x+1; + tc->val1[4]=x+2; + tc->val2[0]= + tc->val2[1]= + tc->val2[2]= + tc->val2[3]= + tc->val2[4]=y-1; + } + else if(dir==2){ + tc->val1[0]= + tc->val1[1]= + tc->val1[2]= + tc->val1[3]= + tc->val1[4]=x+1; + tc->val2[0]=y+2; + tc->val2[1]=y+1; + tc->val2[2]=y; + tc->val2[3]=y-1; + tc->val2[4]=y-2; + } + else if(dir==4){ + tc->val1[0]=x-2; + tc->val1[1]=x-1; + tc->val1[2]=x; + tc->val1[3]=x+1; + tc->val1[4]=x+2; + tc->val2[0]= + tc->val2[1]= + tc->val2[2]= + tc->val2[3]= + tc->val2[4]=y+1; + } + else if(dir==6){ + tc->val1[0]= + tc->val1[1]= + tc->val1[2]= + tc->val1[3]= + tc->val1[4]=x-1; + tc->val2[0]=y+2; + tc->val2[1]=y+1; + tc->val2[2]=y; + tc->val2[3]=y-1; + tc->val2[4]=y-2; + } + else if(dir==1){ + tc->val1[0]=x-1; + tc->val1[1]=x; + tc->val1[2]=x+1; + tc->val1[3]=x+2; + tc->val1[4]=x+3; + tc->val2[0]=y-4; + tc->val2[1]=y-3; + tc->val2[2]=y-1; + tc->val2[3]=y; + tc->val2[4]=y+1; + } + else if(dir==3){ + tc->val1[0]=x+3; + tc->val1[1]=x+2; + tc->val1[2]=x+1; + tc->val1[3]=x; + tc->val1[4]=x-1; + tc->val2[0]=y-1; + tc->val2[1]=y; + tc->val2[2]=y+1; + tc->val2[3]=y+2; + tc->val2[4]=y+3; + } + else if(dir==5){ + tc->val1[0]=x+1; + tc->val1[1]=x; + tc->val1[2]=x-1; + tc->val1[3]=x-2; + tc->val1[4]=x-3; + tc->val2[0]=y+3; + tc->val2[1]=y+2; + tc->val2[2]=y+1; + tc->val2[3]=y; + tc->val2[4]=y-1; + } + else if(dir==7){ + tc->val1[0]=x-3; + tc->val1[1]=x-2; + tc->val1[2]=x-1; + tc->val1[3]=x; + tc->val1[4]=x+1; + tc->val2[1]=y; + tc->val2[0]=y+1; + tc->val2[2]=y-1; + tc->val2[3]=y-2; + tc->val2[4]=y-3; + } + +} + +/*========================================= + * ブランディッシュスピア 方向判定 範??張 + *----------------------------------------- + */ +void skill_brandishspear_dir(struct square *tc,int dir,int are){ + + int c; + + nullpo_retv(tc); + + for(c=0;c<5;c++){ + if(dir==0){ + tc->val2[c]+=are; + }else if(dir==1){ + tc->val1[c]-=are; tc->val2[c]+=are; + }else if(dir==2){ + tc->val1[c]-=are; + }else if(dir==3){ + tc->val1[c]-=are; tc->val2[c]-=are; + }else if(dir==4){ + tc->val2[c]-=are; + }else if(dir==5){ + tc->val1[c]+=are; tc->val2[c]-=are; + }else if(dir==6){ + tc->val1[c]+=are; + }else if(dir==7){ + tc->val1[c]+=are; tc->val2[c]+=are; + } + } +} + +/*========================================== + * Weapon Repair [Celest/DracoRPG] + *------------------------------------------ + */ +void skill_repairweapon(struct map_session_data *sd, int idx) +{ + int material; + int materials[4] = { 1002, 998, 999, 756 }; + struct item *item; + struct map_session_data *target_sd; + + nullpo_retv(sd); + target_sd = map_id2sd(sd->menuskill_lv); + if (!target_sd) //Failed.... + return; + if(idx==0xFFFF) // No item selected ('Cancel' clicked) + return; + if(idx < 0 || idx >= MAX_INVENTORY) + return; //Invalid index?? + + item = &target_sd->status.inventory[idx]; + if(item->nameid <= 0 || item->attribute == 0) + return; //Again invalid item.... + + if(sd!=target_sd && !battle_check_range(&sd->bl,&target_sd->bl,skill_get_range2(&sd->bl, sd->menuskill_id,pc_checkskill(sd, sd->menuskill_id)))){ + clif_item_repaireffect(sd,item->nameid,1); + return; + } + + if (itemdb_type(item->nameid)==4) + material = materials [itemdb_wlv(item->nameid)-1]; // Lv1/2/3/4 weapons consume 1 Iron Ore/Iron/Steel/Rough Oridecon + else + material = materials [2]; // Armors consume 1 Steel + if (pc_search_inventory(sd,material) < 0 ) { + clif_skill_fail(sd,sd->menuskill_id,0,0); + return; + } + item->attribute=0; + clif_equiplist(target_sd); + pc_delitem(sd,pc_search_inventory(sd,material),1,0); + clif_item_repaireffect(sd,item->nameid,0); + if(sd!=target_sd) + clif_item_repaireffect(target_sd,item->nameid,0); +} + +/*========================================== + * Item Appraisal + *------------------------------------------ + */ +void skill_identify(struct map_session_data *sd,int idx) +{ + int flag=1; + + nullpo_retv(sd); + + if(idx >= 0 && idx < MAX_INVENTORY) { + if(sd->status.inventory[idx].nameid > 0 && sd->status.inventory[idx].identify == 0 ){ + flag=0; + sd->status.inventory[idx].identify=1; + } + } + clif_item_identified(sd,idx,flag); +} + +/*========================================== + * Weapon Refine [Celest] + *------------------------------------------ + */ +void skill_weaponrefine(struct map_session_data *sd,int idx) +{ + int i = 0, ep = 0, per; + int material[5] = { 0, 1010, 1011, 984, 984 }; + struct item *item; + + nullpo_retv(sd); + + if (idx >= 0 && idx < MAX_INVENTORY) { + struct item_data *ditem = sd->inventory_data[idx]; + item = &sd->status.inventory[idx]; + + if(item->nameid > 0 && ditem->type == 4) { + if (item->refine >= sd->menuskill_lv || + item->refine >= MAX_REFINE || // if it's no longer refineable + ditem->flag.no_refine || // if the item isn't refinable + (i = pc_search_inventory(sd, material [ditem->wlv])) < 0 ) { //fixed by Lupus (item pos can be = 0!) + clif_skill_fail(sd,sd->menuskill_id,0,0); + return; + } + + per = percentrefinery [ditem->wlv][(int)item->refine]; + per += (sd->status.job_level-50)/2; //Updated per the new kro descriptions. [Skotlex] + + if (per > rand() % 100) { + item->refine++; + pc_delitem(sd, i, 1, 0); + if(item->equip) { + ep = item->equip; + pc_unequipitem(sd,idx,3); + } + clif_refine(sd->fd,sd,0,idx,item->refine); + clif_delitem(sd,idx,1); + clif_additem(sd,idx,1,0); + if (ep) + pc_equipitem(sd,idx,ep); + clif_misceffect(&sd->bl,3); + if(item->refine == MAX_REFINE && item->card[0] == 0x00ff && MakeDWord(item->card[2],item->card[3]) == sd->char_id){ // Fame point system [DracoRPG] + switch(ditem->wlv){ + case 1: + pc_addfame(sd,1); // Success to refine to +10 a lv1 weapon you forged = +1 fame point + break; + case 2: + pc_addfame(sd,25); // Success to refine to +10 a lv2 weapon you forged = +25 fame point + break; + case 3: + pc_addfame(sd,1000); // Success to refine to +10 a lv3 weapon you forged = +1000 fame point + break; + } + } + } else { + pc_delitem(sd, i, 1, 0); + item->refine = 0; + if(item->equip) + pc_unequipitem(sd,idx,3); + clif_refine(sd->fd,sd,1,idx,item->refine); + pc_delitem(sd,idx,1,0); + clif_misceffect(&sd->bl,2); + clif_emotion(&sd->bl, 23); + } + } + } +} + +/*========================================== + * オ?トスペル + *------------------------------------------ + */ +int skill_autospell(struct map_session_data *sd,int skillid) +{ + int skilllv; + int maxlv=1,lv; + + nullpo_retr(0, sd); + + skilllv = sd->menuskill_lv; + lv=pc_checkskill(sd,skillid); + + if(skilllv <= 0 || !lv) return 0; // Player must learn the skill before doing auto-spell [Lance] + + if(skillid==MG_NAPALMBEAT) maxlv=3; + else if(skillid==MG_COLDBOLT || skillid==MG_FIREBOLT || skillid==MG_LIGHTNINGBOLT){ + if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_SAGE) + maxlv =10; //Soul Linker bonus. [Skotlex] + else if(skilllv==2) maxlv=1; + else if(skilllv==3) maxlv=2; + else if(skilllv>=4) maxlv=3; + } + else if(skillid==MG_SOULSTRIKE){ + if(skilllv==5) maxlv=1; + else if(skilllv==6) maxlv=2; + else if(skilllv>=7) maxlv=3; + } + else if(skillid==MG_FIREBALL){ + if(skilllv==8) maxlv=1; + else if(skilllv>=9) maxlv=2; + } + else if(skillid==MG_FROSTDIVER) maxlv=1; + else return 0; + + if(maxlv > lv) + maxlv = lv; + + sc_start4(&sd->bl,SC_AUTOSPELL,100,skilllv,skillid,maxlv,0, + skill_get_time(SA_AUTOSPELL,skilllv)); + return 0; +} + +/*========================================== + * ギャングスタ?パラダイス判定??(foreachinarea) + *------------------------------------------ + */ + +static int skill_gangster_count(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + sd=(struct map_session_data*)bl; + + if(sd && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0) + return 1; + return 0; +} + +static int skill_gangster_in(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + sd=(struct map_session_data*)bl; + if(sd && pc_issit(sd) && pc_checkskill(sd,RG_GANGSTER) > 0) + sd->state.gangsterparadise=1; + return 0; +} + +static int skill_gangster_out(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + sd=(struct map_session_data*)bl; + if(sd && sd->state.gangsterparadise) + sd->state.gangsterparadise=0; + return 0; +} + +int skill_gangsterparadise(struct map_session_data *sd ,int type) +{ + int range; + nullpo_retr(0, sd); + + if((range = pc_checkskill(sd,RG_GANGSTER)) <= 0) + return 0; + range = skill_get_splash(RG_GANGSTER, range); + + if(type==1) {/* ?タった時の?? */ + if (map_foreachinrange(skill_gangster_count,&sd->bl, range, BL_PC) > 1) + { /*ギャングスタ??ャ功したら自分にもギャングスタ???ォ付?*/ + map_foreachinrange(skill_gangster_in,&sd->bl, range, BL_PC); + sd->state.gangsterparadise = 1; + } + return 0; + } + else if(type==0) {/* 立ち?繧ェったときの?? */ + if (map_foreachinrange(skill_gangster_count,&sd->bl, range, BL_PC) < 2) + map_foreachinrange(skill_gangster_out,&sd->bl, range, BL_PC); + sd->state.gangsterparadise = 0; + return 0; + } + return 0; +} +/*========================================== + * Taekwon TK_HPTIME and TK_SPTIME skills [Dralnu] + *------------------------------------------ + */ +static int skill_rest_count(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + sd=(struct map_session_data*)bl; + + if(sd && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 )) + return 1; + return 0; +} + +static int skill_rest_in(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + sd=(struct map_session_data*)bl; + if(sd && pc_issit(sd) && (pc_checkskill(sd,TK_HPTIME) > 0 || pc_checkskill(sd,TK_SPTIME) > 0 )){ + sd->state.rest=1; + status_calc_pc(sd,0); + } + return 0; +} + +static int skill_rest_out(struct block_list *bl,va_list ap) +{ + struct map_session_data *sd; + sd=(struct map_session_data*)bl; + if(sd && sd->state.rest != 0) + sd->state.rest=0; + return 0; +} + +int skill_rest(struct map_session_data *sd ,int type) +{ + int range; + nullpo_retr(0, sd); + + if((range = pc_checkskill(sd,TK_HPTIME)) > 0) + range = skill_get_splash(TK_HPTIME, range); + else if ((range = pc_checkskill(sd,TK_SPTIME)) > 0) + range = skill_get_splash(TK_SPTIME, range); + else + return 0; + + + if(type==1) { //When you sit down + if (map_foreachinrange(skill_rest_count,&sd->bl, range, BL_PC) > 1) + { + map_foreachinrange(skill_rest_in,&sd->bl, range, BL_PC); + sd->state.rest = 1; + status_calc_pc(sd,0); + } + return 0; + } + else if(type==0) { //When you stand up + if (map_foreachinrange(skill_rest_count,&sd->bl, range, BL_PC) < 2) + map_foreachinrange(skill_rest_out,&sd->bl, range, BL_PC); + sd->state.rest = 0; + status_calc_pc(sd,0); + return 0; + } + return 0; +} +/*========================================== + * 寒いジョ?ク?スクリ?ム判定??(foreachinarea) + *------------------------------------------ + */ +int skill_frostjoke_scream(struct block_list *bl,va_list ap) +{ + struct block_list *src; + int skillnum,skilllv; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, src=va_arg(ap,struct block_list*)); + + skillnum=va_arg(ap,int); + skilllv=va_arg(ap,int); + if(skilllv <= 0) return 0; + tick=va_arg(ap,unsigned int); + + if (src == bl || //自分には?かない + bl->prev == NULL || + status_isdead(bl)) + return 0; + if (bl->type == BL_PC) { + struct map_session_data *sd = (struct map_session_data *)bl; + if (sd && sd->sc.option&OPTION_INVISIBLE) + return 0; + } + //It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex] + if(battle_check_target(src,bl,BCT_ENEMY) > 0) + skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick); + else if(battle_check_target(src,bl,BCT_PARTY) > 0 && rand()%100 < 10) + skill_additional_effect(src,bl,skillnum,skilllv,BF_MISC,tick); + + return 0; +} + +/*========================================== + * バジリカのセルを?ン定する + *------------------------------------------ + */ +void skill_unitsetmapcell(struct skill_unit *src, int skill_num, int skill_lv, int flag) +{ + int i,x,y,range = skill_get_unit_range(skill_num,skill_lv); + int size = range*2+1; + + for (i=0;ibl.x+(i%size-range); + y = src->bl.y+(i/size-range); + map_setcell(src->bl.m,x,y,flag); + } +} + +/*========================================== + * Sets a map cell around the caster, according to the skill's range. + *------------------------------------------ + */ +void skill_setmapcell(struct block_list *src, int skill_num, int skill_lv, int flag) +{ + int i,x,y,range = skill_get_range2(src, skill_num, skill_lv); + int size = range*2+1; + + for (i=0;ix+(i%size-range); + y = src->y+(i/size-range); + map_setcell(src->m,x,y,flag); + } +} + +/*========================================== + * + *------------------------------------------ + */ +int skill_attack_area(struct block_list *bl,va_list ap) +{ + struct block_list *src,*dsrc; + int atk_type,skillid,skilllv,flag,type; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + atk_type = va_arg(ap,int); + if((src=va_arg(ap,struct block_list*)) == NULL) + return 0; + if((dsrc=va_arg(ap,struct block_list*)) == NULL) + return 0; + skillid=va_arg(ap,int); + skilllv=va_arg(ap,int); + if(skillid > 0 && skilllv <= 0) return 0; // celest + tick=va_arg(ap,unsigned int); + flag=va_arg(ap,int); + type=va_arg(ap,int); + + if(battle_check_target(dsrc,bl,type) > 0) + skill_attack(atk_type,src,dsrc,bl,skillid,skilllv,tick,flag); + + return 0; +} +/*========================================== + * + *------------------------------------------ + */ +int skill_clear_group(struct block_list *bl, int flag) +{ + struct unit_data *ud = unit_bl2ud(bl); + struct skill_unit_group *group[MAX_SKILLUNITGROUP]; + int i, count=0; + + nullpo_retr(0, bl); + if (!ud) return 0; + + //All groups to be deleted are first stored on an array since the array elements shift around when you delete them. [Skotlex] + for (i=0;iskillunit[i];i++) + { + switch (ud->skillunit[i]->skill_id) { + case SA_DELUGE: + case SA_VOLCANO: + case SA_VIOLENTGALE: + case SA_LANDPROTECTOR: + case NJ_SUITON: + if (flag&1) + group[count++]= ud->skillunit[i]; + break; + default: + if (flag&2 && skill_get_inf2(ud->skillunit[i]->skill_id)&INF2_TRAP) + group[count++]= ud->skillunit[i]; + break; + } + + } + for (i=0;iskillunit[i];i++) { + switch (ud->skillunit[i]->skill_id) { + case SA_DELUGE: + case SA_VOLCANO: + case SA_VIOLENTGALE: + case SA_LANDPROTECTOR: + case NJ_SUITON: + return ud->skillunit[i]; + } + } + return NULL; +} + +// for graffiti cleaner [Valaris] +int skill_graffitiremover(struct block_list *bl, va_list ap) +{ + struct skill_unit *unit=NULL; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + if(bl->type!=BL_SKILL || (unit=(struct skill_unit *)bl) == NULL) + return 0; + + if((unit->group) && (unit->group->unit_id == UNT_GRAFFITI)) + skill_delunit(unit); + + return 0; +} + +int skill_greed(struct block_list *bl, va_list ap) +{ + struct block_list *src; + struct map_session_data *sd=NULL; + struct flooritem_data *fitem=NULL; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, src = va_arg(ap,struct block_list *)); + + if(src->type == BL_PC && (sd=(struct map_session_data *)src) && bl->type==BL_ITEM && (fitem=(struct flooritem_data *)bl)) + pc_takeitem(sd, fitem); + + return 0; +} + +/*========================================== + * ランドプ?テクタ?チェック(foreachinarea) + *------------------------------------------ + */ +int skill_landprotector(struct block_list *bl, va_list ap ) +{ + int skillid; + int *alive; + struct skill_unit *unit; + struct block_list *src; + + skillid = va_arg(ap,int); + alive = va_arg(ap,int *); + src = va_arg(ap,struct block_list *); + unit = (struct skill_unit *)bl; + if (unit == NULL || unit->group == NULL) + return 0; + + if (skillid == SA_LANDPROTECTOR && unit->group->skill_id == SA_LANDPROTECTOR + && battle_check_target(bl, src, BCT_ENEMY) > 0) + { //Check for offensive Land Protector to delete both. [Skotlex] + (*alive) = 0; + skill_delunit(unit); + return 1; + } + + if (skill_get_type(unit->group->skill_id) != BF_MAGIC) + return 0; //Only blocks out magical skills.```````` + + if (skillid == SA_LANDPROTECTOR || skillid == HW_GANBANTEIN ) { + skill_delunit(unit); + } else + if (unit->group->skill_id == SA_LANDPROTECTOR) { + (*alive) = 0; + } else + if (skillid == HP_BASILICA && unit->group->skill_id == HP_BASILICA) { + //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex] + (*alive) = 0; + } else + return 0; + return 1; +} + +/*========================================== + * variation of skill_landprotector + *------------------------------------------ + */ +int skill_ganbatein(struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + if ((unit = (struct skill_unit *)bl) == NULL || unit->group == NULL) + return 0; + +// Apparently, it REMOVES traps. +// if (skill_get_inf2(unit->group->skill_id)&INF2_TRAP) +// return 0; //Do not remove traps. + + if (unit->group->skill_id == SA_LANDPROTECTOR) + skill_delunit(unit); + else skill_delunitgroup(NULL, unit->group); + + return 1; +} + +/*========================================== + * 指定範??でsrcに?して有?なタ?ゲットのblの?を?える(foreachinarea) + *------------------------------------------ + */ +int skill_count_target (struct block_list *bl, va_list ap) +{ + struct block_list *src; + int *c; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + + if ((src = va_arg(ap,struct block_list *)) == NULL) + return 0; + if ((c = va_arg(ap,int *)) == NULL) + return 0; + if (battle_check_target(src,bl,BCT_ENEMY) > 0) + (*c)++; + return 0; +} +/*========================================== + * トラップ範???(foreachinarea) + *------------------------------------------ + */ +int skill_trap_splash (struct block_list *bl, va_list ap) +{ + struct block_list *src; + int tick; + struct skill_unit *unit; + struct skill_unit_group *sg; + struct block_list *ss; + + src = va_arg(ap,struct block_list *); + unit = (struct skill_unit *)src; + tick = va_arg(ap,int); + + nullpo_retr(0, sg = unit->group); + nullpo_retr(0, ss = map_id2bl(sg->src_id)); + + if(battle_check_target(src,bl,BCT_ENEMY) > 0){ + switch(sg->unit_id){ + case UNT_SHOCKWAVE: + case UNT_SANDMAN: + case UNT_FLASHER: + skill_additional_effect(ss,bl,sg->skill_id,sg->skill_lv,BF_MISC,tick); + break; + case UNT_BLASTMINE: + case UNT_CLAYMORETRAP: + skill_attack(BF_MISC,ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + case UNT_FREEZINGTRAP: + skill_attack(BF_WEAPON,ss,src,bl,sg->skill_id,sg->skill_lv,tick,0); + break; + } + } + + return 0; +} + +/*========================================== + * ステ?タス異??I了 + *------------------------------------------ + */ +int skill_enchant_elemental_end (struct block_list *bl, int type) +{ + struct status_change *sc; + + nullpo_retr(0, bl); + nullpo_retr(0, sc= status_get_sc(bl)); + + if (!sc->count) return 0; + + if (type != SC_ENCPOISON && sc->data[SC_ENCPOISON].timer != -1) /* エンチャントポイズン解? */ + status_change_end(bl, SC_ENCPOISON, -1); + if (type != SC_ASPERSIO && sc->data[SC_ASPERSIO].timer != -1) /* アスペルシオ解? */ + status_change_end(bl, SC_ASPERSIO, -1); + if (type != SC_FIREWEAPON && sc->data[SC_FIREWEAPON].timer != -1) /* フレイムランチャ解? */ + status_change_end(bl, SC_FIREWEAPON, -1); + if (type != SC_WATERWEAPON && sc->data[SC_WATERWEAPON].timer != -1) /* フ?ストウェポン解? */ + status_change_end(bl, SC_WATERWEAPON, -1); + if (type != SC_WINDWEAPON && sc->data[SC_WINDWEAPON].timer != -1) /* ライトニング??ダ?解? */ + status_change_end(bl, SC_WINDWEAPON, -1); + if (type != SC_EARTHWEAPON && sc->data[SC_EARTHWEAPON].timer != -1) /* サイスミックウェポン解? */ + status_change_end(bl, SC_EARTHWEAPON, -1); + if (type != SC_SHADOWWEAPON && sc->data[SC_SHADOWWEAPON].timer != -1) + status_change_end(bl, SC_SHADOWWEAPON, -1); + if (type != SC_GHOSTWEAPON && sc->data[SC_GHOSTWEAPON].timer != -1) + status_change_end(bl, SC_GHOSTWEAPON, -1); + return 0; +} + +int skill_check_cloaking(struct block_list *bl, struct status_change *sc) +{ + static int dx[] = { 0, 1, 0, -1, -1, 1, 1, -1}; //optimized by Lupus + static int dy[] = {-1, 0, 1, 0, -1, -1, 1, 1}; + int end = 1,i; + + if ((bl->type == BL_PC && battle_config.pc_cloak_check_type&1) || + (bl->type != BL_PC && battle_config.monster_cloak_check_type&1)) + { //Check for walls. + for (i = 0; i < 8; i++) + if (map_getcell(bl->m, bl->x+dx[i], bl->y+dy[i], CELL_CHKNOPASS)) + { + end = 0; + break; + } + } else + end = 0; //No wall check. + + if(end){ + if (sc->data[SC_CLOAKING].timer != -1) { + if (sc->data[SC_CLOAKING].val1 < 3) //End cloaking. + status_change_end(bl, SC_CLOAKING, -1); + else if(sc->data[SC_CLOAKING].val4&2) + { //Remove wall bonus + sc->data[SC_CLOAKING].val4&=~2; + status_calc_bl(bl,SCB_SPEED); + } + } + } + else if(sc->data[SC_CLOAKING].timer != -1 && !(sc->data[SC_CLOAKING].val4&2)) + { //Add wall speed bonus + sc->data[SC_CLOAKING].val4|=2; + status_calc_bl(bl,SCB_SPEED); + } + + return end; +} + +/* + *---------------------------------------------------------------------------- + * スキルユニット + *---------------------------------------------------------------------------- + */ + +/*========================================== + * 演奏/ダンスをやめる + * flag 1で?奏中なら相方にユニットを任せる + * + *------------------------------------------ + */ +void skill_stop_dancing(struct block_list *src) +{ + struct status_change* sc; + struct skill_unit_group* group; + struct map_session_data* dsd = NULL; + + nullpo_retv(src); + nullpo_retv(sc = status_get_sc(src)); + + if(!sc->count || sc->data[SC_DANCING].timer == -1) + return; + + group = (struct skill_unit_group *)sc->data[SC_DANCING].val2; + sc->data[SC_DANCING].val2 = 0; + + if (sc->data[SC_DANCING].val4) + { + if (sc->data[SC_DANCING].val4 != BCT_SELF) + dsd = map_id2sd(sc->data[SC_DANCING].val4); + sc->data[SC_DANCING].val4 = 0; + } + + if (group) + skill_delunitgroup(NULL, group); + + if (dsd) + { + dsd->sc.data[SC_DANCING].val4 = dsd->sc.data[SC_DANCING].val2 = 0; + status_change_end(&dsd->bl, SC_DANCING, -1); + } + status_change_end(src, SC_DANCING, -1); +} + +/*========================================== + * スキルユニット?炎化 + *------------------------------------------ + */ +struct skill_unit *skill_initunit(struct skill_unit_group *group,int idx,int x,int y) +{ + struct skill_unit *unit; + + nullpo_retr(NULL, group); + nullpo_retr(NULL, unit=&group->unit[idx]); + + if(!unit->alive) + group->alive_count++; + + unit->bl.id=map_addobject(&unit->bl); + unit->bl.type=BL_SKILL; + unit->bl.m=group->map; + unit->bl.x=x; + unit->bl.y=y; + unit->group=group; + unit->val1=unit->val2=0; + unit->alive=1; + + map_addblock(&unit->bl); + clif_skill_setunit(unit); + + switch (group->skill_id) { + case AL_PNEUMA: + skill_unitsetmapcell(unit,AL_PNEUMA,group->skill_lv,CELL_SETPNEUMA); + break; + case MG_SAFETYWALL: + skill_unitsetmapcell(unit,MG_SAFETYWALL,group->skill_lv,CELL_SETSAFETYWALL); + break; + case SA_LANDPROTECTOR: + skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_SETLANDPROTECTOR); + break; + case HP_BASILICA: + skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_SETBASILICA); + break; + case WZ_ICEWALL: + skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_SETICEWALL); + break; + } + return unit; +} + +/*========================================== + * スキルユニット?? + *------------------------------------------ + */ +int skill_delunit(struct skill_unit *unit) +{ + struct skill_unit_group *group; + + nullpo_retr(0, unit); + if(!unit->alive) + return 0; + nullpo_retr(0, group=unit->group); + + /* onlimitイベント呼び?oし */ + skill_unit_onlimit( unit,gettick() ); + + /* onoutイベント呼び?oし */ + if (!unit->range) { + map_foreachincell(skill_unit_effect,unit->bl.m, + unit->bl.x,unit->bl.y,group->bl_flag,&unit->bl,gettick(),4); + } + + switch (group->skill_id) { + case AL_PNEUMA: + skill_unitsetmapcell(unit,AL_PNEUMA,group->skill_lv,CELL_CLRPNEUMA); + break; + case MG_SAFETYWALL: + skill_unitsetmapcell(unit,MG_SAFETYWALL,group->skill_lv,CELL_CLRSAFETYWALL); + break; + case SA_LANDPROTECTOR: + skill_unitsetmapcell(unit,SA_LANDPROTECTOR,group->skill_lv,CELL_CLRLANDPROTECTOR); + break; + case HP_BASILICA: + skill_unitsetmapcell(unit,HP_BASILICA,group->skill_lv,CELL_CLRBASILICA); + break; + case WZ_ICEWALL: + skill_unitsetmapcell(unit,WZ_ICEWALL,group->skill_lv,CELL_CLRICEWALL); + break; + } + + clif_skill_delunit(unit); + + unit->group=NULL; + unit->alive=0; + map_delobjectnofree(unit->bl.id); + if(--group->alive_count==0) + skill_delunitgroup(NULL, group); + + return 0; +} +/*========================================== + * スキルユニットグル?プ?炎化 + *------------------------------------------ + */ +static int skill_unit_group_newid = MAX_SKILL_DB; +struct skill_unit_group *skill_initunitgroup(struct block_list *src, + int count,int skillid,int skilllv,int unit_id, int limit, int interval) +{ + struct unit_data *ud = unit_bl2ud(src); + struct skill_unit_group *group=NULL; + int i; + + if(skilllv <= 0) return 0; + + nullpo_retr(NULL, src); + nullpo_retr(NULL, ud); + + for(i=0;iskillunit[i]; i++); + + if(i == MAX_SKILLUNITGROUP) { + int j=0; + unsigned maxdiff=0,x,tick=gettick(); + for(i=0;iskillunit[i];i++) + if((x=DIFF_TICK(tick,ud->skillunit[i]->tick))>maxdiff){ + maxdiff=x; + j=i; + } + skill_delunitgroup(src, ud->skillunit[j]); + //Since elements must have shifted, we use the last slot. + i = MAX_SKILLUNITGROUP-1; + } + if (!ud->skillunit[i]) + ud->skillunit[i] = ers_alloc(skill_unit_ers, struct skill_unit_group); + group=ud->skillunit[i]; + + group->src_id=src->id; + group->party_id=status_get_party_id(src); + group->guild_id=status_get_guild_id(src); + group->group_id=skill_unit_group_newid++; + if(skill_unit_group_newid<=0) + skill_unit_group_newid = MAX_SKILL_DB; + group->unit=(struct skill_unit *)aCalloc(count,sizeof(struct skill_unit)); + group->unit_count=count; + group->alive_count=0; + group->val1=group->val2=group->val3=0; + group->skill_id=skillid; + group->skill_lv=skilllv; + group->unit_id=unit_id; + group->map=src->m; + group->limit=limit; + group->interval=interval; + group->tick=gettick(); + if (skillid == PR_SANCTUARY) //Sanctuary starts healing +1500ms after casted. [Skotlex] + group->tick += 1500; + else if (skillid == PA_GOSPEL) //Prevent Gospel from triggering bonuses right away. [Skotlex] + group->tick += interval; + group->valstr=NULL; + + i = skill_get_unit_flag(skillid); //Reuse for faster access from here on. [Skotlex] + if (i&UF_DANCE) { + struct map_session_data *sd = NULL; + if(src->type==BL_PC && (sd=(struct map_session_data *)src) ){ + sd->skillid_dance=skillid; + sd->skilllv_dance=skilllv; + } + sc_start4(src,SC_DANCING,100,skillid,(int)group,0,(i&UF_ENSEMBLE?BCT_SELF:0),skill_get_time(skillid,skilllv)+1000); + //?奏スキルは相方をダンス?態にする + if (sd && i&UF_ENSEMBLE && + battle_config.player_skill_partner_check && + (!battle_config.gm_skilluncond || pc_isGM(sd) < battle_config.gm_skilluncond) + ) { + skill_check_pc_partner(sd, skillid, &skilllv, 1, 1); + } + } + return group; +} + +/*========================================== + * スキルユニットグル?プ?? + *------------------------------------------ + */ +int skill_delunitgroup(struct block_list *src, struct skill_unit_group *group) +{ + struct unit_data *ud; + int i,j; + + nullpo_retr(0, group); + + if (!src) src=map_id2bl(group->src_id); + ud = unit_bl2ud(src); + if(!src || !ud) { + ShowError("skill_delunitgroup: Group's source not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); + return 0; + } + if (skill_get_unit_flag(group->skill_id)&UF_DANCE) + { + struct status_change* sc = status_get_sc(src); + if (sc && sc->data[SC_DANCING].timer != -1) + { + sc->data[SC_DANCING].val2 = 0 ; //This prevents status_change_end attempting to redelete the group. [Skotlex] + status_change_end(src,SC_DANCING,-1); + } + } + + if (group->unit_id == UNT_GOSPEL) { //Clear Gospel [Skotlex] + struct status_change *sc = status_get_sc(src); + if(sc && sc->data[SC_GOSPEL].timer != -1) { + sc->data[SC_GOSPEL].val3 = 0; //Remove reference to this group. [Skotlex] + status_change_end(src,SC_GOSPEL,-1); + } + } + if (group->skill_id == SG_SUN_WARM || + group->skill_id == SG_MOON_WARM || + group->skill_id == SG_STAR_WARM) { + struct status_change *sc = status_get_sc(src); + if(sc && sc->data[SC_WARM].timer != -1) { + sc->data[SC_WARM].val4 = 0; + status_change_end(src,SC_WARM,-1); + } + } + + if (src->type==BL_PC && group->state.ammo_consume) + battle_consume_ammo((TBL_PC*)src, group->skill_id, group->skill_lv); + + group->alive_count=0; + if(group->unit!=NULL){ + for(i=0;iunit_count;i++) + if(group->unit[i].alive) + skill_delunit(&group->unit[i]); + } + if(group->valstr!=NULL){ + aFree(group->valstr); + group->valstr=NULL; + } + + map_freeblock((struct block_list*)group->unit); /* aFree()の替わり */ + group->unit=NULL; + group->group_id=0; + group->unit_count=0; + + //Locate and clear this unit from the array. + for (i=0; iskillunit[i]!=group; i++); + for (j=i; jskillunit[j]; j++); + j--; + if (iskillunit[i] = ud->skillunit[j]; + ud->skillunit[j] = NULL; + ers_free(skill_unit_ers, group); + } else + ShowError("skill_delunitgroup: Group not found! (src_id: %d skill_id: %d)\n", group->src_id, group->skill_id); + return 1; +} + +/*========================================== + * スキルユニットグル?プ全?? + *------------------------------------------ + */ +int skill_clear_unitgroup(struct block_list *src) +{ + struct unit_data *ud = unit_bl2ud(src); + + nullpo_retr(0, ud); + + while (ud->skillunit[0]) + skill_delunitgroup(src, ud->skillunit[0]); + return 1; +} + +/*========================================== + * スキルユニットグル?プの被影響tick?? + *------------------------------------------ + */ +struct skill_unit_group_tickset *skill_unitgrouptickset_search( + struct block_list *bl,struct skill_unit_group *group,int tick) +{ + int i,j=-1,k,s,id; + struct unit_data *ud; + struct skill_unit_group_tickset *set; + + nullpo_retr(0, bl); + if (group->interval==-1) + return NULL; + + ud = unit_bl2ud(bl); + if (!ud) return NULL; + + set = ud->skillunittick; + + if (skill_get_unit_flag(group->skill_id)&UF_NOOVERLAP) + id = s = group->skill_id; + else + id = s = group->group_id; + + for (i=0; i0 || set[k].id==0)) + j=k; + } + + if (j == -1) { + if(battle_config.error_log) { + ShowWarning ("skill_unitgrouptickset_search: tickset is full\n"); + } + j = id % MAX_SKILLUNITGROUPTICKSET; + } + + set[j].id = id; + set[j].tick = tick; + return &set[j]; +} + +/*========================================== + * スキルユニットタイマ??動??用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_timer_sub_onplace( struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + unit = va_arg(ap,struct skill_unit *); + tick = va_arg(ap,unsigned int); + + if (!unit->alive || bl->prev==NULL) + return 0; + + nullpo_retr(0, group=unit->group); + + if (skill_get_type(group->skill_id)==BF_MAGIC + && map_getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR)) + return 0; //AoE skills are ineffective. [Skotlex] + + if (battle_check_target(&unit->bl,bl,group->target_flag)<=0) + return 0; + + skill_unit_onplace_timer(unit,bl,tick); + + return 0; +} + +/*========================================== + * スキルユニットタイマ???用(foreachobject) + *------------------------------------------ + */ +int skill_unit_timer_sub( struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit; + struct skill_unit_group *group; + unsigned int tick; + + nullpo_retr(0, bl); + nullpo_retr(0, ap); + nullpo_retr(0, unit=(struct skill_unit *)bl); + tick=va_arg(ap,unsigned int); + + if(!unit->alive) + return 0; + group=unit->group; + + nullpo_retr(0, group); + + /* onplace_timerイベント呼び?oし */ + if (unit->range>=0 && group->interval!=-1) { + if (battle_config.skill_wall_check) + map_foreachinshootrange(skill_unit_timer_sub_onplace, bl, unit->range, + group->bl_flag,bl,tick); + else + map_foreachinrange(skill_unit_timer_sub_onplace, bl, unit->range, + group->bl_flag,bl,tick); + if (!unit->alive) + return 0; + } + /* 時間?リれ?? */ + if((DIFF_TICK(tick,group->tick)>=group->limit || DIFF_TICK(tick,group->tick)>=unit->limit)){ + switch(group->unit_id){ + case UNT_BLASTMINE: + group->unit_id = UNT_USED_TRAPS; + clif_changetraplook(bl, UNT_USED_TRAPS); + group->limit=DIFF_TICK(tick+1500,group->tick); + unit->limit=DIFF_TICK(tick+1500,group->tick); + break; + case UNT_SKIDTRAP: + case UNT_ANKLESNARE: + case UNT_LANDMINE: + case UNT_SHOCKWAVE: + case UNT_SANDMAN: + case UNT_FLASHER: + case UNT_FREEZINGTRAP: + case UNT_CLAYMORETRAP: + case UNT_TALKIEBOX: + { + struct block_list *src=map_id2bl(group->src_id); + if(group->unit_id == UNT_ANKLESNARE && group->val2); + else{ + if(src && src->type==BL_PC && !group->state.into_abyss) + { //Avoid generating trap items when it did not cost to create them. [Skotlex] + struct item item_tmp; + memset(&item_tmp,0,sizeof(item_tmp)); + item_tmp.nameid=1065; + item_tmp.identify=1; + map_addflooritem(&item_tmp,1,bl->m,bl->x,bl->y,NULL,NULL,NULL,0); // ?返還 + } + } + skill_delunit(unit); + } + break; + + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + { + struct block_list *src=map_id2bl(group->src_id); + if (src) + group->tick = tick; + } + break; + + default: + skill_delunit(unit); + } + } + + if(group->unit_id == UNT_ICEWALL) { + unit->val1 -= 5; + if(unit->val1 <= 0 && unit->limit + group->tick > tick + 700) + unit->limit = DIFF_TICK(tick+700,group->tick); + } + + return 0; +} +/*========================================== + * スキルユニットタイマ??? + *------------------------------------------ + */ +int skill_unit_timer( int tid,unsigned int tick,int id,int data) +{ + map_freeblock_lock(); + + map_foreachobject( skill_unit_timer_sub, BL_SKILL, tick ); + + map_freeblock_unlock(); + + return 0; +} + +/*========================================== + * スキルユニット移動時??用(foreachinarea) + *------------------------------------------ + */ +int skill_unit_move_sub( struct block_list *bl, va_list ap ) +{ + struct skill_unit *unit = (struct skill_unit *)bl; + struct block_list *target; + unsigned int tick,flag,result; + int skill_id; + + target=va_arg(ap,struct block_list*); + tick = va_arg(ap,unsigned int); + flag = va_arg(ap,int); + + nullpo_retr(0, unit->group); + + if (!(unit->group->bl_flag&target->type)) + return 0; //we don't target this type of bl + + skill_id = unit->group->skill_id; //Necessary in case the group is deleted after calling on_place/on_out [Skotlex] + + if (unit->group->interval!=-1 && + !(skill_get_unit_flag(skill_id)&UF_DUALMODE)) //Skills in dual mode have to trigger both. [Skotlex] + return 0; + + if (!unit->alive || target->prev==NULL) + return 0; + + if (flag&1) + { + result = skill_unit_onplace(unit,target,tick); + if (flag&2 && result) + { //Clear skill ids we have stored in onout. + int i; + for(i=0; i<8 && skill_unit_temp[i]!=result; i++); + if (i<8) + skill_unit_temp[i] = 0; + } + } + else + { + result = skill_unit_onout(unit,target,tick); + if (flag&2 && skill_unit_index < 7 && result) //Store this unit id. + skill_unit_temp[skill_unit_index++] = result; + } + if (flag&4) + skill_unit_onleft(skill_id,target,tick); + + return 1; +} + +/*========================================== + * Invoked when a char has moved and unit cells must be invoked (onplace, onout, onleft) + * Flag values: + * flag&1: invoke skill_unit_onplace (otherwise invoke skill_unit_onout) + * flag&2: this function is being invoked twice as a bl moves, store in memory the affected + * units to figure out when they have left a group. + * flag&4: Force a onleft event (triggered when the bl is killed, for example) + *------------------------------------------ + */ +int skill_unit_move(struct block_list *bl,unsigned int tick,int flag) +{ + nullpo_retr(0, bl); + + if(bl->prev==NULL ) + return 0; + + if (flag&2 && !(flag&1)) + { //Onout, clear data + memset (&skill_unit_temp,0,sizeof(skill_unit_temp)); + skill_unit_index=0; + } + + map_foreachincell(skill_unit_move_sub, + bl->m,bl->x,bl->y,BL_SKILL,bl,tick,flag); + + if (flag&2 && flag&1) + { //Onplace, check any skill units you have left. + int i; + for (i=0; i< 8 && skill_unit_temp[i]>0; i++) + skill_unit_onleft(skill_unit_temp[i], bl, tick); + } + + return 0; +} + +/*========================================== + * スキルユニット自?の移動時?? + * 引?はグル?プと移動量 + *------------------------------------------ + */ +int skill_unit_move_unit_group( struct skill_unit_group *group, int m,int dx,int dy) +{ + int i,j; + unsigned int tick = gettick(); + int *m_flag; + struct skill_unit *unit1; + struct skill_unit *unit2; + + nullpo_retr(0, group); + if (group->unit_count<=0) + return 0; + if (group->unit==NULL) + return 0; + + if (skill_get_unit_flag(group->skill_id)&UF_ENSEMBLE) //Ensembles may not be moved around. + return 0; + + m_flag = (int *) aMalloc(sizeof(int)*group->unit_count); + memset(m_flag,0,sizeof(int)*group->unit_count);// 移動フラグ + // m_flag + // 0: Neither of the following (skill_unit_onplace & skill_unit_onout are needed) + // 1: Unit will move to a slot that had another unit of the same group (skill_unit_onplace not needed) + // 2: Another unit from same group will end up positioned on this unit (skill_unit_onout not needed) + // 3: Both 1+2. + for(i=0;iunit_count;i++){ + unit1=&group->unit[i]; + if (!unit1->alive || unit1->bl.m!=m) + continue; + for(j=0;junit_count;j++){ + unit2=&group->unit[j]; + if (!unit2->alive) + continue; + if (unit1->bl.x+dx==unit2->bl.x && unit1->bl.y+dy==unit2->bl.y){ + m_flag[i] |= 0x1; + } + if (unit1->bl.x-dx==unit2->bl.x && unit1->bl.y-dy==unit2->bl.y){ + m_flag[i] |= 0x2; + } + } + } + j = 0; + for (i=0;iunit_count;i++) { + unit1=&group->unit[i]; + if (!unit1->alive) + continue; + if (!(m_flag[i]&0x2)) { + map_foreachincell(skill_unit_effect,unit1->bl.m, + unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,4); + } + //Move Cell using "smart" criteria (avoid useless moving around) + switch(m_flag[i]) + { + case 0: + //Cell moves independently, safely move it. + map_moveblock(&unit1->bl, unit1->bl.x+dx, unit1->bl.y+dy, tick); + clif_skill_setunit(unit1); + break; + case 1: + //Cell moves unto another cell, look for a replacement cell that won't collide + //and has no cell moving into it (flag == 2) + for(;junit_count;j++) + { + if(m_flag[j]!=2 || !group->unit[j].alive) + continue; + //Move to where this cell would had moved. + unit2 = &group->unit[j]; + map_moveblock(&unit1->bl, unit2->bl.x+dx, unit2->bl.y+dy, tick); + clif_skill_setunit(unit1); + j++; //Skip this cell as we have used it. + break; + } + break; + case 2: + case 3: + break; //Don't move the cell as a cell will end on this tile anyway. + } + if (!(m_flag[i]&2)) { //We only moved the cell in 0-1 + map_foreachincell(skill_unit_effect,unit1->bl.m, + unit1->bl.x,unit1->bl.y,group->bl_flag,&unit1->bl,tick,1); + } + } + aFree(m_flag); + return 0; +} + +/*---------------------------------------------------------------------------- + * アイテム??ャ + *---------------------------------------------------------------------------- + */ + +/*========================================== + * アイテム??ャ可能判定 + *------------------------------------------ + */ +int skill_can_produce_mix( struct map_session_data *sd, int nameid, int trigger, int qty) +{ + int i,j; + + nullpo_retr(0, sd); + + if(nameid<=0) + return 0; + + for(i=0;i= MAX_SKILL_PRODUCE_DB ) /* デ?タベ?スにない */ + return 0; + + if(trigger>=0){ + if(trigger>20) { // Non-weapon, non-food item (itemlv must match) + if(skill_produce_db[i].itemlv!=trigger) + return 0; + } else if(trigger>10) { // Food (itemlv must be higher or equal) + if(skill_produce_db[i].itemlv<=10 || skill_produce_db[i].itemlv>trigger) + return 0; + } else { // Weapon (itemlv must be higher or equal) + if(skill_produce_db[i].itemlv>trigger) + return 0; + } + } + if( (j=skill_produce_db[i].req_skill)>0 && pc_checkskill(sd,j)<=0 ) + return 0; /* スキルが足りない */ + + for(j=0;jstatus.inventory[y].nameid == id ) + x+=sd->status.inventory[y].amount; + if(xbl); + + if( !(idx=skill_can_produce_mix(sd,nameid,-1, qty)) ) /* ??不足 */ + return 0; + idx--; + + if (qty < 1) + qty = 1; + + if (!skill_id) //A skill can be specified for some override cases. + skill_id = skill_produce_db[idx].req_skill; + + slot[0]=slot1; + slot[1]=slot2; + slot[2]=slot3; + + /* 埋め?み?? */ + for(i=0,sc=0,ele=0;i<3;i++){ //Note that qty should always be one if you are using these! + int j; + if( slot[i]<=0 ) + continue; + j = pc_search_inventory(sd,slot[i]); + if(j < 0) /* 不?ウパケット(アイテム存?ン)チェック */ + continue; + if(slot[i]==1000){ /* Star Crumb */ + pc_delitem(sd,j,1,1); + sc++; + } + if(slot[i]>=994 && slot[i]<=997 && ele==0){ /* Flame Heart . . . Great Nature */ + static const int ele_table[4]={3,1,4,2}; + pc_delitem(sd,j,1,1); + ele=ele_table[slot[i]-994]; + } + } + + /* ?゙料?チ費 */ + for(i=0;i= 0){ + y = sd->status.inventory[j].amount; + if(y>x)y=x; /* 足りている */ + pc_delitem(sd,j,y,0); + }else { + if(battle_config.error_log) + ShowError("skill_produce_mix: material item error\n"); + } + + x-=y; /* まだ足りない個?を計算 */ + }while( j>=0 && x>0 ); /* ?゙料を?チ費するか?Aエラ?になるまで繰り返す */ + } + + if((equip=itemdb_isequip(nameid))) + wlv = itemdb_wlv(nameid); + if(!equip) { + switch(skill_id){ + case BS_IRON: + case BS_STEEL: + case BS_ENCHANTEDSTONE: + { // Ores & Metals Refining - skill bonuses are straight from kRO website [DracoRPG] + int skill = pc_checkskill(sd,skill_id); + make_per = sd->status.job_level*20 + status->dex*10 + status->luk*10; //Base chance + switch(nameid){ + case 998: // Iron + make_per += 4000+skill*500; // Temper Iron bonus: +26/+32/+38/+44/+50 + break; + case 999: // Steel + make_per += 3000+skill*500; // Temper Steel bonus: +35/+40/+45/+50/+55 + break; + case 1000: //Star Crumb + make_per = 100000; // Star Crumbs are 100% success crafting rate? (made 1000% so it succeeds even after penalties) [Skotlex] + break; + default: // Enchanted Stones + make_per += 1000+skill*500; // Enchantedstone Craft bonus: +15/+20/+25/+30/+35 + break; + } + break; + case ASC_CDP: + make_per = (2000 + 40*status->dex + 20*status->luk); + break; + case AL_HOLYWATER: + make_per = 100000; //100% success + break; + case AM_PHARMACY: // Potion Preparation - reviewed with the help of various Ragnainfo sources [DracoRPG] + case AM_TWILIGHT1: + case AM_TWILIGHT2: + case AM_TWILIGHT3: + make_per = pc_checkskill(sd,AM_LEARNINGPOTION)*100 + + pc_checkskill(sd,AM_PHARMACY)*300 + sd->status.job_level*20 + + status->int_*5 + status->dex*10+status->luk*10; + switch(nameid){ + case 501: // Red Potion + case 503: // Yellow Potion + case 504: // White Potion + case 605: // Anodyne + case 606: // Aloevera + make_per += 2000; + break; + case 505: // Blue Potion + make_per -= 500; + break; + case 545: // Condensed Red Potion + case 546: // Condensed Yellow Potion + case 547: // Condensed White Potion + make_per -= 1000; + break; + case 970: // Alcohol + make_per += 1000; + break; + case 7139: // Glistening Coat + make_per -= 1000; + break; + case 7135: // Bottle Grenade + case 7136: // Acid Bottle + case 7137: // Plant Bottle + case 7138: // Marine Sphere Bottle + default: + break; + } + if(battle_config.pp_rate != 100) + make_per = make_per * battle_config.pp_rate / 100; + break; + case SA_CREATECON: // Elemental Converter Creation - skill bonuses are from kRO [DracoRPG] + make_per = pc_checkskill(sd, SA_ADVANCEDBOOK)*100 + //TODO: Advanced Book bonus is custom! [Skotlex] + sd->status.job_level*20 + status->int_*10 + status->dex*10; + switch(nameid){ + case 12114: + flag = pc_checkskill(sd,SA_FLAMELAUNCHER); + if (flag > 0) + make_per += 1000*flag-500; + break; + case 12115: + flag = pc_checkskill(sd,SA_FROSTWEAPON); + if (flag > 0) + make_per += 1000*flag-500; + break; + case 12116: + flag = pc_checkskill(sd,SA_SEISMICWEAPON); + if (flag > 0) + make_per += 1000*flag-500; + break; + case 12117: + flag = pc_checkskill(sd,SA_LIGHTNINGLOADER); + if (flag > 0) + make_per += 1000*flag-500; + break; + } + break; + default: + make_per = 5000; + break; + } + } + } else { // Weapon Forging - skill bonuses are straight from kRO website, other things from a jRO calculator [DracoRPG] + make_per = 5000 + sd->status.job_level*20 + status->dex*10 + status->luk*10; // Base + make_per += pc_checkskill(sd,skill_id)*500; // Smithing skills bonus: +5/+10/+15 + make_per += pc_checkskill(sd,BS_WEAPONRESEARCH)*100 +((wlv >= 3)? pc_checkskill(sd,BS_ORIDEOCON)*100:0); // Weaponry Research bonus: +1/+2/+3/+4/+5/+6/+7/+8/+9/+10, Oridecon Research bonus (custom): +1/+2/+3/+4/+5 + make_per -= (ele?2000:0) + sc*1500 + (wlv>1?wlv*1000:0); // Element Stone: -20%, Star Crumb: -15% each, Weapon level malus: -0/-20/-30 + if(pc_search_inventory(sd,989) > 0) make_per+= 1000; // Emperium Anvil: +10 + else if(pc_search_inventory(sd,988) > 0) make_per+= 500; // Golden Anvil: +5 + else if(pc_search_inventory(sd,987) > 0) make_per+= 300; // Oridecon Anvil: +3 + else if(pc_search_inventory(sd,986) > 0) make_per+= 0; // Anvil: +0? + if(battle_config.wp_rate != 100) + make_per = make_per * battle_config.wp_rate / 100; + } +// - Baby Class Penalty = 80% (from adult's chance) ----// + if (sd->class_&JOBL_BABY) //if it's a Baby Class + make_per = (make_per * 80) / 100; //Lupus + + if(make_per < 1) make_per = 1; + + + if(rand()%10000 < make_per || qty > 1){ //Success, or crafting multiple items. + struct item tmp_item; + memset(&tmp_item,0,sizeof(tmp_item)); + tmp_item.nameid=nameid; + tmp_item.amount=1; + tmp_item.identify=1; + if(equip){ + tmp_item.card[0]=0x00ff; + tmp_item.card[1]=((sc*5)<<8)+ele; + tmp_item.card[2]=GetWord(sd->char_id,0); // CharId + tmp_item.card[3]=GetWord(sd->char_id,1); + } else { + //Flag is only used on the end, so it can be used here. [Skotlex] + switch (skill_id) { + case AM_PHARMACY: + case AM_TWILIGHT1: + case AM_TWILIGHT2: + case AM_TWILIGHT3: + flag = battle_config.produce_potion_name_input; + break; + case AL_HOLYWATER: + flag = battle_config.holywater_name_input; + break; + case ASC_CDP: + flag = battle_config.cdp_name_input; + break; + default: + flag = battle_config.produce_item_name_input; + break; + } + if (flag) { + tmp_item.card[0]=0x00fe; + tmp_item.card[1]=0; + tmp_item.card[2]=GetWord(sd->char_id,0); // CharId + tmp_item.card[3]=GetWord(sd->char_id,1); + } + } + +// if(log_config.produce > 0) +// log_produce(sd,nameid,slot1,slot2,slot3,1); +//TODO update PICKLOG + + if(equip){ + clif_produceeffect(sd,0,nameid); + clif_misceffect(&sd->bl,3); + if(itemdb_wlv(nameid) >= 3 && ((ele? 1 : 0) + sc) >= 3) // Fame point system [DracoRPG] + pc_addfame(sd,10); // Success to forge a lv3 weapon with 3 additional ingredients = +10 fame point + } else { + int fame = 0; + tmp_item.amount = 0; + for (i=0; i< qty; i++) + { //Apply quantity modifiers. + if (rand()%10000 < make_per || qty == 1) + { //Success + tmp_item.amount++; + if(nameid < 545 || nameid > 547) + continue; + if(skill_id != AM_PHARMACY && + skill_id != AM_TWILIGHT1 && + skill_id != AM_TWILIGHT2 && + skill_id != AM_TWILIGHT3) + continue; + //Add fame as needed. + switch(++sd->potion_success_counter) { + case 3: + fame+=1; // Success to prepare 3 Condensed Potions in a row + break; + case 5: + fame+=3; // Success to prepare 5 Condensed Potions in a row + break; + case 7: + fame+=10; // Success to prepare 7 Condensed Potions in a row + break; + case 10: + fame+=50; // Success to prepare 10 Condensed Potions in a row + sd->potion_success_counter = 0; + break; + } + } else //Failure + sd->potion_success_counter = 0; + } + if (fame) + pc_addfame(sd,fame); + //Visual effects and the like. + switch (skill_id) { + case AM_PHARMACY: + case AM_TWILIGHT1: + case AM_TWILIGHT2: + case AM_TWILIGHT3: + case ASC_CDP: + clif_produceeffect(sd,2,nameid); + clif_misceffect(&sd->bl,5); + break; + case BS_IRON: + case BS_STEEL: + case BS_ENCHANTEDSTONE: + clif_produceeffect(sd,0,nameid); + clif_misceffect(&sd->bl,3); + break; + default: //Those that don't require a skill? + if (skill_produce_db[idx].itemlv==11) //Cooking items. + clif_specialeffect(&sd->bl, 608, 0); + break; + } + } + if (tmp_item.amount) { //Success + if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) { + clif_additem(sd,0,0,flag); + map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + return 1; + } + } + //Failure +// if(log_config.produce) +// log_produce(sd,nameid,slot1,slot2,slot3,0); +//TODO update PICKLOG + + if(equip){ + clif_produceeffect(sd,1,nameid); + clif_misceffect(&sd->bl,2); + } else { + switch (skill_id) { + case ASC_CDP: //50% Damage yourself, and display same effect as failed potion. + status_percent_damage(NULL, &sd->bl, -50, 0); + case AM_PHARMACY: + case AM_TWILIGHT1: + case AM_TWILIGHT2: + case AM_TWILIGHT3: + clif_produceeffect(sd,3,nameid); + clif_misceffect(&sd->bl,6); + sd->potion_success_counter = 0; // Fame point system [DracoRPG] + break; + case BS_IRON: + case BS_STEEL: + case BS_ENCHANTEDSTONE: + clif_produceeffect(sd,1,nameid); + clif_misceffect(&sd->bl,2); + break; + default: + if (skill_produce_db[idx].itemlv==11) + clif_specialeffect(&sd->bl, 609, 0); + } + } + return 0; +} + +int skill_arrow_create( struct map_session_data *sd,int nameid) +{ + int i,j,flag,index=-1; + struct item tmp_item; + + nullpo_retr(0, sd); + + if(nameid <= 0) + return 1; + + for(i=0;ichar_id,0); // CharId + tmp_item.card[3]=GetWord(sd->char_id,1); + } + if(tmp_item.nameid <= 0 || tmp_item.amount <= 0) + continue; + if((flag = pc_additem(sd,&tmp_item,tmp_item.amount))) { + clif_additem(sd,0,0,flag); + map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,NULL,NULL,NULL,0); + } + } + + return 0; +} + +/*========================================== + * + *------------------------------------------ + */ +int skill_blockpc_end(int tid,unsigned int tick,int id,int data) +{ + struct map_session_data *sd = map_id2sd(id); + if (data <= 0 || data >= MAX_SKILL) + return 0; + if (sd) sd->blockskill[data] = 0; + + return 1; +} +int skill_blockpc_start(struct map_session_data *sd, int skillid, int tick) +{ + nullpo_retr (-1, sd); + + if (skillid >= GD_SKILLBASE) + skillid = GD_SKILLRANGEMIN + skillid - GD_SKILLBASE; + if (skillid < 1 || skillid > MAX_SKILL) + return -1; + + sd->blockskill[skillid] = 1; + return add_timer(gettick()+tick,skill_blockpc_end,sd->bl.id,skillid); +} + + +/*---------------------------------------------------------------------------- + * ?炎化系 + */ + +/* + * 文字列?? + * ',' で区?リって val に戻す + */ +int skill_split_str(char *str,char **val,int num) +{ + int i; + + for (i=0; i= step; j--) + if ((val[j]-val[j-step]) != diff) + break; + + if (j>=step) //No match, try next step. + continue; + + for(; i < MAX_SKILL_LEVEL; i++) + { //Apply linear increase + val[i] = val[i-step]+diff; + if (val[i] < 1 && val[i-1] >=0) //Check if we have switched from + to -, cap the decrease to 0 in said cases. + { val[i] = 1; diff = 0; step = 1; } + } + return i; + } + //Okay.. we can't figure this one out, just fill out the stuff with the previous value. + for (;i= GD_SKILLRANGEMIN && i <= GD_SKILLRANGEMAX) { + ShowWarning("read skill_db: Can't use skill id %d as guild skills are placed there!\n"); + continue; + } + if (i >= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if(i<=0 || i>MAX_SKILL_DB) + continue; + + skill_split_atoi(split[1],skill_db[i].range); + skill_db[i].hit=atoi(split[2]); + skill_db[i].inf=atoi(split[3]); + skill_db[i].pl=atoi(split[4]); + skill_db[i].nk=atoi(split[5]); + skill_split_atoi(split[6],skill_db[i].splash); + skill_db[i].max=atoi(split[7]); + skill_split_atoi(split[8],skill_db[i].num); + + if(strcmpi(split[9],"yes") == 0) + skill_db[i].castcancel=1; + else + skill_db[i].castcancel=0; + skill_db[i].cast_def_rate=atoi(split[10]); + skill_db[i].inf2=atoi(split[11]); + skill_db[i].maxcount=atoi(split[12]); + if(strcmpi(split[13],"weapon") == 0) + skill_db[i].skill_type=BF_WEAPON; + else if(strcmpi(split[13],"magic") == 0) + skill_db[i].skill_type=BF_MAGIC; + else if(strcmpi(split[13],"misc") == 0) + skill_db[i].skill_type=BF_MISC; + else + skill_db[i].skill_type=0; + skill_split_atoi(split[14],skill_db[i].blewcount); + + for (j = 0; skill_names[j].id != 0; j++) + if (skill_names[j].id == i) { + skill_db[i].name = skill_names[j].name; + skill_db[i].desc = skill_names[j].desc; + break; + } + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + sprintf(path, "%s/skill_require_db.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + while(fgets(line,1020,fp)){ + char *split[50]; + if(line[0]=='/' && line[1]=='/') + continue; + j = skill_split_str(line,split,32); + if(j < 32 || split[31]==NULL) + continue; + + i=atoi(split[0]); + if (i >= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if(i<=0 || i>MAX_SKILL_DB) + continue; + + skill_split_atoi(split[1],skill_db[i].hp); + skill_split_atoi(split[2],skill_db[i].mhp); + skill_split_atoi(split[3],skill_db[i].sp); + skill_split_atoi(split[4],skill_db[i].hp_rate); + skill_split_atoi(split[5],skill_db[i].sp_rate); + skill_split_atoi(split[6],skill_db[i].zeny); + + p = split[7]; + for(j=0;j<32;j++){ + l = atoi(p); + if (l==99) { + skill_db[i].weapon = 0xffffffff; + break; + } + else + skill_db[i].weapon |= 1<= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if(i<=0 || i>MAX_SKILL_DB) + continue; + + skill_split_atoi(split[1],skill_db[i].cast); + skill_split_atoi(split[2],skill_db[i].delay); + skill_split_atoi(split[3],skill_db[i].walkdelay); + skill_split_atoi(split[4],skill_db[i].upkeep_time); + skill_split_atoi(split[5],skill_db[i].upkeep_time2); + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + /* スキルユニットデ?[タベ?[ス */ + + sprintf(path, "%s/skill_unit_db.txt", db_path); + fp=fopen(path,"r"); + if (fp==NULL) { + ShowError("can't read %s\n", path); + return 1; + } + k = 0; + while (fgets(line,1020,fp)) { + char *split[50]; + if (line[0]=='/' && line[1]=='/') + continue; + j = skill_split_str(line,split,8); + if (split[7]==NULL || j<8) + continue; + + i=atoi(split[0]); + if (i >= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if(i<=0 || i>MAX_SKILL_DB) + continue; + skill_db[i].unit_id[0] = strtol(split[1],NULL,16); + skill_db[i].unit_id[1] = strtol(split[2],NULL,16); + skill_split_atoi(split[3],skill_db[i].unit_layout_type); + skill_split_atoi(split[4],skill_db[i].unit_range); + skill_db[i].unit_interval = atoi(split[5]); + + if( strcmpi(split[6],"noenemy")==0 ) skill_db[i].unit_target=BCT_NOENEMY; + else if( strcmpi(split[6],"friend")==0 ) skill_db[i].unit_target=BCT_NOENEMY; + else if( strcmpi(split[6],"party")==0 ) skill_db[i].unit_target=BCT_PARTY; + else if( strcmpi(split[6],"ally")==0 ) skill_db[i].unit_target=BCT_PARTY|BCT_GUILD; + else if( strcmpi(split[6],"all")==0 ) skill_db[i].unit_target=BCT_ALL; + else if( strcmpi(split[6],"enemy")==0 ) skill_db[i].unit_target=BCT_ENEMY; + else if( strcmpi(split[6],"self")==0 ) skill_db[i].unit_target=BCT_SELF; + else if( strcmpi(split[6],"noone")==0 ) skill_db[i].unit_target=BCT_NOONE; + else skill_db[i].unit_target = strtol(split[6],NULL,16); + + skill_db[i].unit_flag = strtol(split[7],NULL,16); + + if (skill_db[i].unit_flag&UF_DEFNOTENEMY && battle_config.defnotenemy) + skill_db[i].unit_target=BCT_NOENEMY; + + //By default, target just characters. + skill_db[i].unit_target |= BL_CHAR; + if (skill_db[i].unit_flag&UF_NOPC) + skill_db[i].unit_target &= ~BL_PC; + if (skill_db[i].unit_flag&UF_NOMOB) + skill_db[i].unit_target &= ~BL_MOB; + if (skill_db[i].unit_flag&UF_SKILL) + skill_db[i].unit_target |= BL_SKILL; + k++; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + skill_init_unit_layout(); + + /* ?サ造系スキルデ?タベ?ス */ + memset(skill_produce_db,0,sizeof(skill_produce_db)); + for(m=0;m<2;m++){ + sprintf(path, "%s/%s", db_path, filename[m]); + fp=fopen(path,"r"); + if(fp==NULL){ + if(m>0) + continue; + ShowError("can't read %s\n",path); + return 1; + } + k=0; + while(fgets(line,1020,fp)){ + char *split[6 + MAX_PRODUCE_RESOURCE * 2]; + int x,y; + if(line[0]=='/' && line[1]=='/') + continue; + memset(split,0,sizeof(split)); + j = skill_split_str(line,split,(3 + MAX_PRODUCE_RESOURCE * 2)); + if(split[0]==0) //fixed by Lupus + continue; + i=atoi(split[0]); + if(i<=0) continue; + + skill_produce_db[k].nameid=i; + skill_produce_db[k].itemlv=atoi(split[1]); + skill_produce_db[k].req_skill=atoi(split[2]); + + for(x=3,y=0; split[x] && split[x+1] && y= MAX_SKILL_PRODUCE_DB) + break; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path); + } + + memset(skill_arrow_db,0,sizeof(skill_arrow_db)); + + sprintf(path, "%s/create_arrow_db.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + k=0; + while(fgets(line,1020,fp)){ + char *split[16]; + int x,y; + if(line[0]=='/' && line[1]=='/') + continue; + memset(split,0,sizeof(split)); + j = skill_split_str(line,split,13); + if(split[0]==0) //fixed by Lupus + continue; + i=atoi(split[0]); + if(i<=0) + continue; + + skill_arrow_db[k].nameid=i; + + for(x=1,y=0;split[x] && split[x+1] && y<5;x+=2,y++){ + skill_arrow_db[k].cre_id[y]=atoi(split[x]); + skill_arrow_db[k].cre_amount[y]=atoi(split[x+1]); + } + k++; + if(k >= MAX_SKILL_ARROW_DB) + break; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path); + + memset(skill_abra_db,0,sizeof(skill_abra_db)); + sprintf(path, "%s/abra_db.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + k=0; + while(fgets(line,1020,fp)){ + char *split[16]; + if(line[0]=='/' && line[1]=='/') + continue; + memset(split,0,sizeof(split)); + j = skill_split_str(line,split,13); + if(split[0]==0) //fixed by Lupus + continue; + i=atoi(split[0]); + if(i<=0) + continue; + + skill_abra_db[i].req_lv=atoi(split[2]); + skill_abra_db[i].per=atoi(split[3]); + + k++; + if(k >= MAX_SKILL_ABRA_DB) + break; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n",k,path); + + sprintf(path, "%s/skill_castnodex_db.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + while(fgets(line,1020,fp)){ + char *split[50]; + if(line[0]=='/' && line[1]=='/') + continue; + memset(split,0,sizeof(split)); + j = skill_split_str(line,split,3); + if(split[0]==0) //fixed by Lupus + continue; + i=atoi(split[0]); + if (i >= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if(i<=0 || i>MAX_SKILL_DB) + continue; + + skill_split_atoi(split[1],skill_db[i].castnodex); + if (!split[2]) + continue; + skill_split_atoi(split[2],skill_db[i].delaynodex); + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + sprintf(path, "%s/skill_nocast_db.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + k=0; + while(fgets(line,1020,fp)){ + char *split[16]; + if(line[0]=='/' && line[1]=='/') + continue; + memset(split,0,sizeof(split)); + j = skill_split_str(line,split,2); + if(split[0]==0) //fixed by Lupus + continue; + i=atoi(split[0]); + if (i >= GD_SKILLBASE) + i = GD_SKILLRANGEMIN + i - GD_SKILLBASE; + if(i<=0 || i>MAX_SKILL_DB) + continue; + skill_db[i].nocast|=atoi(split[1]); + k++; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + return 0; +} + +/*=============================================== + * For reading leveluseskillspamount.txt [Celest] + *----------------------------------------------- + */ +static int skill_read_skillspamount(void) +{ + char *buf,*p; + struct skill_db *skill = NULL; + int s, idx, new_flag=1, level=1, sp=0; + + buf=(char *) grfio_reads("data\\leveluseskillspamount.txt",&s); + + if(buf==NULL) + return -1; + + buf[s]=0; + for(p=buf;p-bufsp[level-1]=sp; + level++; + } + + p=strchr(p,10); + if(!p) break; + p++; + } + aFree(buf); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n","data\\leveluseskillspamount.txt"); + + return 0; +} + +void skill_reload(void) +{ + skill_readdb(); + if (battle_config.skill_sp_override_grffile) + skill_read_skillspamount(); +} + +/*========================================== + * スキル?係?炎化?? + *------------------------------------------ + */ +int do_init_skill(void) +{ + skill_readdb(); + + skill_unit_ers = ers_new((uint32)sizeof(struct skill_unit_group)); + skill_timer_ers = ers_new((uint32)sizeof(struct skill_timerskill)); + + if (battle_config.skill_sp_override_grffile) + skill_read_skillspamount(); + + add_timer_func_list(skill_unit_timer,"skill_unit_timer"); + add_timer_func_list(skill_castend_id,"skill_castend_id"); + add_timer_func_list(skill_castend_pos,"skill_castend_pos"); + add_timer_func_list(skill_timerskill,"skill_timerskill"); + add_timer_func_list(skill_blockpc_end, "skill_blockpc_end"); + + add_timer_interval(gettick()+SKILLUNITTIMER_INVERVAL,skill_unit_timer,0,0,SKILLUNITTIMER_INVERVAL); + + return 0; +} + +int do_final_skill(void) { + ers_destroy(skill_unit_ers); + ers_destroy(skill_timer_ers); + return 0; +} diff --git a/src/map/skill.h b/src/map/skill.h index b9f43ed4e..536e4c9fd 100644 --- a/src/map/skill.h +++ b/src/map/skill.h @@ -224,10 +224,9 @@ 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); -#define skill_calc_heal(bl,skill_lv) (( status_get_lv(bl)+status_get_int(bl) )/8 *(4+ skill_lv*8)) +int skill_calc_heal(struct block_list *bl, int skill_lv); -// その他 -int skill_check_cloaking(struct block_list *bl); +int skill_check_cloaking(struct block_list *bl, struct status_change *sc); // ステ?タス異常 int skill_enchant_elemental_end(struct block_list *bl, int type); diff --git a/src/map/status.c b/src/map/status.c index b8b38b5d9..a831bf51a 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -1,6174 +1,6123 @@ -// Copyright (c) Athena Dev Teams - Licensed under GNU GPL -// For more information, see LICENCE in the main folder - -#include -#include -#include -#include -#include -#include -#include - -#include "pc.h" -#include "map.h" -#include "pet.h" -#include "npc.h" -#include "mob.h" -#include "clif.h" -#include "guild.h" -#include "skill.h" -#include "itemdb.h" -#include "battle.h" -#include "chrif.h" -#include "status.h" -#include "script.h" -#include "unit.h" - -#include "../common/timer.h" -#include "../common/nullpo.h" -#include "../common/showmsg.h" - -int SkillStatusChangeTable[MAX_SKILL]; //Stores the status that should be associated to this skill. -int StatusIconChangeTable[SC_MAX]; //Stores the icon that should be associated to this status change. -int StatusSkillChangeTable[SC_MAX]; //Stores the skill that should be considered associated to this status change. - -static int max_weight_base[MAX_PC_CLASS]; -static int hp_coefficient[MAX_PC_CLASS]; -static int hp_coefficient2[MAX_PC_CLASS]; -static int hp_sigma_val[MAX_PC_CLASS][MAX_LEVEL]; -static int sp_coefficient[MAX_PC_CLASS]; -static int aspd_base[MAX_PC_CLASS][MAX_WEAPON_TYPE]; //[blackhole89] -#define MAX_REFINE_BONUS 5 -static int refinebonus[MAX_REFINE_BONUS][3]; // 精錬ボーナステーブル(refine_db.txt) -int percentrefinery[5][MAX_REFINE+1]; // 精錬成功率(refine_db.txt) -static int atkmods[3][MAX_WEAPON_TYPE]; // 武器ATKサイズ修正(size_fix.txt) -static char job_bonus[MAX_PC_CLASS][MAX_LEVEL]; - -int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus] -int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex] -//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only -//to avoid cards exploits - -//Initializes the StatusIconChangeTable variable. May seem somewhat slower than directly defining the array, -//but it is much less prone to errors. [Skotlex] -void initChangeTables(void) { - int i; - for (i = 0; i < SC_MAX; i++) - StatusIconChangeTable[i] = SI_BLANK; - for (i = 0; i < MAX_SKILL; i++) - SkillStatusChangeTable[i] = -1; - memset(StatusSkillChangeTable, 0, sizeof(StatusSkillChangeTable)); - - //First we define the skill for common ailments. These are used in - //skill_additional_effect through sc cards. [Skotlex] - StatusSkillChangeTable[SC_STONE] = MG_STONECURSE; - StatusSkillChangeTable[SC_FREEZE] = MG_FROSTDIVER; - StatusSkillChangeTable[SC_STUN] = NPC_STUNATTACK; - StatusSkillChangeTable[SC_SLEEP] = NPC_SLEEPATTACK; - StatusSkillChangeTable[SC_POISON] = NPC_POISON; - StatusSkillChangeTable[SC_CURSE] = NPC_CURSEATTACK; - StatusSkillChangeTable[SC_SILENCE] = NPC_SILENCEATTACK; - StatusSkillChangeTable[SC_CONFUSION] = DC_WINKCHARM; - StatusSkillChangeTable[SC_BLIND] = NPC_BLINDATTACK; - StatusSkillChangeTable[SC_BLEEDING] = LK_HEADCRUSH; - StatusSkillChangeTable[SC_DPOISON] = NPC_POISON; - -#define set_sc(skill, sc, icon) \ - if (SkillStatusChangeTable[skill]==-1) SkillStatusChangeTable[skill] = sc; \ - if (StatusSkillChangeTable[sc]==0) StatusSkillChangeTable[sc] = skill; \ - if (StatusIconChangeTable[sc]==SI_BLANK) StatusIconChangeTable[sc] = icon; - - set_sc(SM_BASH, SC_STUN, SI_BLANK); - set_sc(SM_PROVOKE, SC_PROVOKE, SI_PROVOKE); - set_sc(SM_MAGNUM, SC_WATK_ELEMENT, SI_BLANK); - set_sc(SM_ENDURE, SC_ENDURE, SI_ENDURE); - set_sc(MG_SIGHT, SC_SIGHT, SI_BLANK); - set_sc(MG_SAFETYWALL, SC_SAFETYWALL, SI_BLANK); - set_sc(MG_FROSTDIVER, SC_FREEZE, SI_BLANK); - set_sc(MG_STONECURSE, SC_STONE, SI_BLANK); - set_sc(AL_RUWACH, SC_RUWACH, SI_BLANK); - set_sc(AL_INCAGI, SC_INCREASEAGI, SI_INCREASEAGI); - set_sc(AL_DECAGI, SC_DECREASEAGI, SI_DECREASEAGI); - set_sc(AL_CRUCIS, SC_SIGNUMCRUCIS, SI_SIGNUMCRUCIS); - set_sc(AL_ANGELUS, SC_ANGELUS, SI_ANGELUS); - set_sc(AL_BLESSING, SC_BLESSING, SI_BLESSING); - set_sc(AC_CONCENTRATION, SC_CONCENTRATE, SI_CONCENTRATE); - set_sc(TF_HIDING, SC_HIDING, SI_HIDING); - set_sc(TF_POISON, SC_POISON, SI_BLANK); - set_sc(KN_TWOHANDQUICKEN, SC_TWOHANDQUICKEN, SI_TWOHANDQUICKEN); - set_sc(KN_AUTOCOUNTER, SC_AUTOCOUNTER, SI_BLANK); - set_sc(PR_IMPOSITIO, SC_IMPOSITIO, SI_IMPOSITIO); - set_sc(PR_SUFFRAGIUM, SC_SUFFRAGIUM, SI_SUFFRAGIUM); - set_sc(PR_ASPERSIO, SC_ASPERSIO, SI_ASPERSIO); - set_sc(PR_BENEDICTIO, SC_BENEDICTIO, SI_BENEDICTIO); - set_sc(PR_SLOWPOISON, SC_SLOWPOISON, SI_SLOWPOISON); - set_sc(PR_KYRIE, SC_KYRIE, SI_KYRIE); - set_sc(PR_MAGNIFICAT, SC_MAGNIFICAT, SI_MAGNIFICAT); - set_sc(PR_GLORIA, SC_GLORIA, SI_GLORIA); - set_sc(PR_LEXDIVINA, SC_SILENCE, SI_BLANK); - set_sc(PR_LEXAETERNA, SC_AETERNA, SI_AETERNA); - set_sc(WZ_METEOR, SC_STUN, SI_BLANK); - set_sc(WZ_VERMILION, SC_BLIND, SI_BLANK); - set_sc(WZ_FROSTNOVA, SC_FREEZE, SI_BLANK); - set_sc(WZ_STORMGUST, SC_FREEZE, SI_BLANK); - set_sc(WZ_QUAGMIRE, SC_QUAGMIRE, SI_QUAGMIRE); - set_sc(BS_ADRENALINE, SC_ADRENALINE, SI_ADRENALINE); - set_sc(BS_WEAPONPERFECT, SC_WEAPONPERFECTION, SI_WEAPONPERFECTION); - set_sc(BS_OVERTHRUST, SC_OVERTHRUST, SI_OVERTHRUST); - set_sc(BS_MAXIMIZE, SC_MAXIMIZEPOWER, SI_MAXIMIZEPOWER); - set_sc(HT_LANDMINE, SC_STUN, SI_BLANK); - set_sc(HT_ANKLESNARE, SC_ANKLE, SI_BLANK); - set_sc(HT_SANDMAN, SC_SLEEP, SI_BLANK); - set_sc(HT_FLASHER, SC_BLIND, SI_BLANK); - set_sc(HT_FREEZINGTRAP, SC_FREEZE, SI_BLANK); - set_sc(AS_CLOAKING, SC_CLOAKING, SI_CLOAKING); - set_sc(AS_SONICBLOW, SC_STUN, SI_BLANK); - set_sc(AS_GRIMTOOTH, SC_SLOWDOWN, SI_BLANK); - set_sc(AS_ENCHANTPOISON, SC_ENCPOISON, SI_ENCPOISON); - set_sc(AS_POISONREACT, SC_POISONREACT, SI_POISONREACT); - set_sc(AS_VENOMDUST, SC_POISON, SI_BLANK); - set_sc(AS_SPLASHER, SC_SPLASHER, SI_BLANK); - set_sc(NV_TRICKDEAD, SC_TRICKDEAD, SI_TRICKDEAD); - set_sc(SM_AUTOBERSERK, SC_AUTOBERSERK, SI_STEELBODY); - set_sc(TF_SPRINKLESAND, SC_BLIND, SI_BLANK); - set_sc(TF_THROWSTONE, SC_STUN, SI_BLANK); - set_sc(MC_LOUD, SC_LOUD, SI_LOUD); - set_sc(MG_ENERGYCOAT, SC_ENERGYCOAT, SI_ENERGYCOAT); - set_sc(NPC_POISON, SC_POISON, SI_BLANK); - set_sc(NPC_BLINDATTACK, SC_BLIND, SI_BLANK); - set_sc(NPC_SILENCEATTACK, SC_SILENCE, SI_BLANK); - set_sc(NPC_STUNATTACK, SC_STUN, SI_BLANK); - set_sc(NPC_PETRIFYATTACK, SC_STONE, SI_BLANK); - set_sc(NPC_CURSEATTACK, SC_CURSE, SI_BLANK); - set_sc(NPC_SLEEPATTACK, SC_SLEEP, SI_BLANK); - set_sc(NPC_KEEPING, SC_KEEPING, SI_BLANK); - set_sc(NPC_DARKBLESSING, SC_COMA, SI_BLANK); - set_sc(NPC_BARRIER, SC_BARRIER, SI_BLANK); - set_sc(NPC_LICK, SC_STUN, SI_BLANK); - set_sc(NPC_HALLUCINATION, SC_HALLUCINATION, SI_HALLUCINATION); - set_sc(NPC_REBIRTH, SC_KAIZEL, SI_KAIZEL); - set_sc(RG_RAID, SC_STUN, SI_BLANK); - set_sc(RG_STRIPWEAPON, SC_STRIPWEAPON, SI_STRIPWEAPON); - set_sc(RG_STRIPSHIELD, SC_STRIPSHIELD, SI_STRIPSHIELD); - set_sc(RG_STRIPARMOR, SC_STRIPARMOR, SI_STRIPARMOR); - set_sc(RG_STRIPHELM, SC_STRIPHELM, SI_STRIPHELM); - set_sc(AM_ACIDTERROR, SC_BLEEDING, SI_BLEEDING); - set_sc(AM_CP_WEAPON, SC_CP_WEAPON, SI_CP_WEAPON); - set_sc(AM_CP_SHIELD, SC_CP_SHIELD, SI_CP_SHIELD); - set_sc(AM_CP_ARMOR, SC_CP_ARMOR, SI_CP_ARMOR); - set_sc(AM_CP_HELM, SC_CP_HELM, SI_CP_HELM); - set_sc(CR_AUTOGUARD, SC_AUTOGUARD, SI_AUTOGUARD); - set_sc(CR_SHIELDCHARGE, SC_STUN, SI_BLANK); - set_sc(CR_REFLECTSHIELD, SC_REFLECTSHIELD, SI_REFLECTSHIELD); - set_sc(CR_HOLYCROSS, SC_BLIND, SI_BLANK); - set_sc(CR_GRANDCROSS, SC_BLIND, SI_BLANK); - set_sc(CR_DEVOTION, SC_DEVOTION, SI_DEVOTION); - set_sc(CR_PROVIDENCE, SC_PROVIDENCE, SI_PROVIDENCE); - set_sc(CR_DEFENDER, SC_DEFENDER, SI_DEFENDER); - set_sc(CR_SPEARQUICKEN, SC_SPEARQUICKEN, SI_SPEARQUICKEN); - set_sc(MO_STEELBODY, SC_STEELBODY, SI_STEELBODY); - set_sc(MO_BLADESTOP, SC_BLADESTOP_WAIT, SI_BLANK); - set_sc(MO_EXPLOSIONSPIRITS, SC_EXPLOSIONSPIRITS, SI_EXPLOSIONSPIRITS); - set_sc(MO_EXTREMITYFIST, SC_EXTREMITYFIST, SI_BLANK); - set_sc(SA_MAGICROD, SC_MAGICROD, SI_BLANK); - set_sc(SA_AUTOSPELL, SC_AUTOSPELL, SI_AUTOSPELL); - set_sc(SA_FLAMELAUNCHER, SC_FIREWEAPON, SI_FIREWEAPON); - set_sc(SA_FROSTWEAPON, SC_WATERWEAPON, SI_WATERWEAPON); - set_sc(SA_LIGHTNINGLOADER, SC_WINDWEAPON, SI_WINDWEAPON); - set_sc(SA_SEISMICWEAPON, SC_EARTHWEAPON, SI_EARTHWEAPON); - set_sc(SA_VOLCANO, SC_VOLCANO, SI_BLANK); - set_sc(SA_DELUGE, SC_DELUGE, SI_BLANK); - set_sc(SA_VIOLENTGALE, SC_VIOLENTGALE, SI_BLANK); - set_sc(SA_LANDPROTECTOR, SC_LANDPROTECTOR, SI_BLANK); - set_sc(SA_REVERSEORCISH, SC_ORCISH, SI_BLANK); - set_sc(SA_COMA, SC_COMA, SI_BLANK); - set_sc(BD_LULLABY, SC_LULLABY, SI_BLANK); - set_sc(BD_RICHMANKIM, SC_RICHMANKIM, SI_BLANK); - set_sc(BD_ETERNALCHAOS, SC_ETERNALCHAOS, SI_BLANK); - set_sc(BD_DRUMBATTLEFIELD, SC_DRUMBATTLE, SI_BLANK); - set_sc(BD_RINGNIBELUNGEN, SC_NIBELUNGEN, SI_BLANK); - set_sc(BD_ROKISWEIL, SC_ROKISWEIL, SI_BLANK); - set_sc(BD_INTOABYSS, SC_INTOABYSS, SI_BLANK); - set_sc(BD_SIEGFRIED, SC_SIEGFRIED, SI_BLANK); - set_sc(BA_FROSTJOKE, SC_FREEZE, SI_BLANK); - set_sc(BA_WHISTLE, SC_WHISTLE, SI_BLANK); - set_sc(BA_ASSASSINCROSS, SC_ASSNCROS, SI_BLANK); - set_sc(BA_POEMBRAGI, SC_POEMBRAGI, SI_BLANK); - set_sc(BA_APPLEIDUN, SC_APPLEIDUN, SI_BLANK); - set_sc(DC_UGLYDANCE, SC_UGLYDANCE, SI_BLANK); - set_sc(DC_SCREAM, SC_STUN, SI_BLANK); - set_sc(DC_HUMMING, SC_HUMMING, SI_BLANK); - set_sc(DC_DONTFORGETME, SC_DONTFORGETME, SI_BLANK); - set_sc(DC_FORTUNEKISS, SC_FORTUNE, SI_BLANK); - set_sc(DC_SERVICEFORYOU, SC_SERVICE4U, SI_BLANK); - set_sc(NPC_DARKCROSS, SC_BLIND, SI_BLANK); - set_sc(NPC_GRANDDARKNESS, SC_BLIND, SI_BLANK); - set_sc(NPC_STOP, SC_STOP, SI_BLANK); - set_sc(NPC_BREAKWEAPON, SC_BROKENWEAPON, SI_BROKENWEAPON); - set_sc(NPC_BREAKARMOR, SC_BROKENARMOR, SI_BROKENARMOR); - set_sc(NPC_POWERUP, SC_INCHITRATE, SI_BLANK); - set_sc(NPC_AGIUP, SC_INCFLEERATE, SI_BLANK); - set_sc(NPC_INVISIBLE, SC_CLOAKING, SI_CLOAKING); - set_sc(LK_AURABLADE, SC_AURABLADE, SI_AURABLADE); - set_sc(LK_PARRYING, SC_PARRYING, SI_PARRYING); - set_sc(LK_CONCENTRATION, SC_CONCENTRATION, SI_CONCENTRATION); - set_sc(LK_TENSIONRELAX, SC_TENSIONRELAX, SI_TENSIONRELAX); - set_sc(LK_BERSERK, SC_BERSERK, SI_BERSERK); - set_sc(LK_FURY, SC_FURY, SI_FURY); - set_sc(HP_ASSUMPTIO, SC_ASSUMPTIO, SI_ASSUMPTIO); - set_sc(HP_BASILICA, SC_BASILICA, SI_BLANK); - set_sc(HW_MAGICPOWER, SC_MAGICPOWER, SI_MAGICPOWER); - set_sc(PA_SACRIFICE, SC_SACRIFICE, SI_BLANK); - set_sc(PA_GOSPEL, SC_GOSPEL, SI_BLANK); - set_sc(CH_TIGERFIST, SC_STOP, SI_BLANK); - set_sc(ASC_EDP, SC_EDP, SI_EDP); - set_sc(SN_SIGHT, SC_TRUESIGHT, SI_TRUESIGHT); - set_sc(SN_WINDWALK, SC_WINDWALK, SI_WINDWALK); - set_sc(WS_MELTDOWN, SC_MELTDOWN, SI_MELTDOWN); - set_sc(WS_CARTBOOST, SC_CARTBOOST, SI_CARTBOOST); - set_sc(ST_CHASEWALK, SC_CHASEWALK, SI_CHASEWALK); - set_sc(ST_REJECTSWORD, SC_REJECTSWORD, SI_REJECTSWORD); - set_sc(ST_REJECTSWORD, SC_AUTOCOUNTER, SI_BLANK); - set_sc(CG_MOONLIT, SC_MOONLIT, SI_MOONLIT); - set_sc(CG_MARIONETTE, SC_MARIONETTE, SI_MARIONETTE); - set_sc(CG_MARIONETTE, SC_MARIONETTE2, SI_MARIONETTE2); - set_sc(LK_SPIRALPIERCE, SC_STOP, SI_BLANK); - set_sc(LK_HEADCRUSH, SC_BLEEDING, SI_BLEEDING); - set_sc(LK_JOINTBEAT, SC_JOINTBEAT, SI_JOINTBEAT); - set_sc(HW_NAPALMVULCAN, SC_CURSE, SI_BLANK); - set_sc(PF_MINDBREAKER, SC_MINDBREAKER, SI_BLANK); - set_sc(PF_MEMORIZE, SC_MEMORIZE, SI_BLANK); - set_sc(PF_FOGWALL, SC_FOGWALL, SI_BLANK); - set_sc(PF_SPIDERWEB, SC_SPIDERWEB, SI_BLANK); - set_sc(WE_BABY, SC_BABY, SI_BLANK); - set_sc(TK_RUN, SC_RUN, SI_RUN); - set_sc(TK_RUN, SC_SPURT, SI_SPURT); - set_sc(TK_READYSTORM, SC_READYSTORM, SI_READYSTORM); - set_sc(TK_READYDOWN, SC_READYDOWN, SI_READYDOWN); - set_sc(TK_DOWNKICK, SC_STUN, SI_BLANK); - set_sc(TK_READYTURN, SC_READYTURN, SI_READYTURN); - set_sc(TK_READYCOUNTER, SC_READYCOUNTER, SI_READYCOUNTER); - set_sc(TK_DODGE, SC_DODGE, SI_DODGE); - set_sc(TK_SPTIME, SC_TKREST, SI_TKREST); - set_sc(TK_SEVENWIND, SC_GHOSTWEAPON, SI_GHOSTWEAPON); - set_sc(TK_SEVENWIND, SC_SHADOWWEAPON, SI_SHADOWWEAPON); - set_sc(SG_SUN_WARM, SC_WARM, SI_WARM); - set_sc(SG_MOON_WARM, SC_WARM, SI_WARM); - set_sc(SG_STAR_WARM, SC_WARM, SI_WARM); - set_sc(SG_SUN_COMFORT, SC_SUN_COMFORT, SI_SUN_COMFORT); - set_sc(SG_MOON_COMFORT, SC_MOON_COMFORT, SI_MOON_COMFORT); - set_sc(SG_STAR_COMFORT, SC_STAR_COMFORT, SI_STAR_COMFORT); - set_sc(SG_KNOWLEDGE, SC_KNOWLEDGE, SI_BLANK); - set_sc(SG_FUSION, SC_FUSION, SI_BLANK); - set_sc(BS_ADRENALINE2, SC_ADRENALINE2, SI_ADRENALINE2); - set_sc(SL_KAIZEL, SC_KAIZEL, SI_KAIZEL); - set_sc(SL_KAAHI, SC_KAAHI, SI_KAAHI); - set_sc(SL_KAUPE, SC_KAUPE, SI_KAUPE); - set_sc(SL_KAITE, SC_KAITE, SI_KAITE); - set_sc(SL_STUN, SC_STUN, SI_BLANK); - set_sc(SL_SWOO, SC_SWOO, SI_BLANK); - set_sc(SL_SKE, SC_SKE, SI_BLANK); - set_sc(SL_SKA, SC_SKA, SI_BLANK); - set_sc(SL_SMA, SC_SMA, SI_SMA); - set_sc(ST_PRESERVE, SC_PRESERVE, SI_PRESERVE); - set_sc(PF_DOUBLECASTING, SC_DOUBLECAST, SI_DOUBLECAST); - set_sc(HW_GRAVITATION, SC_GRAVITATION, SI_BLANK); - set_sc(WS_CARTTERMINATION, SC_STUN, SI_BLANK); - set_sc(WS_OVERTHRUSTMAX, SC_MAXOVERTHRUST, SI_MAXOVERTHRUST); - set_sc(CG_LONGINGFREEDOM, SC_LONGING, SI_BLANK); - set_sc(CG_HERMODE, SC_HERMODE, SI_BLANK); - set_sc(SL_HIGH, SC_SPIRIT, SI_SPIRIT); - set_sc(KN_ONEHAND, SC_ONEHAND, SI_ONEHAND); - set_sc(CR_SHRINK, SC_SHRINK, SI_SHRINK); - set_sc(RG_CLOSECONFINE, SC_CLOSECONFINE2, SI_CLOSECONFINE2); - set_sc(RG_CLOSECONFINE, SC_CLOSECONFINE, SI_CLOSECONFINE); - set_sc(WZ_SIGHTBLASTER, SC_SIGHTBLASTER, SI_SIGHTBLASTER); - set_sc(DC_WINKCHARM, SC_WINKCHARM, SI_WINKCHARM); - set_sc(MO_BALKYOUNG, SC_STUN, SI_BLANK); - //Until they're at right position - gs_set_sc- [Vicious] / some of these don't seem to have a status icon adequate [blackhole89] - set_sc(GS_MADNESSCANCEL, SC_MADNESSCANCEL, SI_MADNESSCANCEL); - set_sc(GS_ADJUSTMENT, SC_ADJUSTMENT, SI_ADJUSTMENT); - set_sc(GS_INCREASING, SC_INCREASING, SI_ACCURACY); - set_sc(GS_GATLINGFEVER, SC_GATLINGFEVER, SI_GATLINGFEVER); - set_sc(NJ_TATAMIGAESHI, SC_TATAMIGAESHI, SI_BLANK); - set_sc(NJ_UTSUSEMI, SC_UTSUSEMI, SI_MAEMI); - set_sc(NJ_KAENSIN, SC_KAENSIN, SI_BLANK); - set_sc(NJ_SUITON, SC_SUITON, SI_BLANK); - set_sc(NJ_NEN, SC_NEN, SI_NEN); - - // Storing the target job rather than simply SC_SPIRIT simplifies code later on. - SkillStatusChangeTable[SL_ALCHEMIST] = MAPID_ALCHEMIST, - SkillStatusChangeTable[SL_MONK] = MAPID_MONK, - SkillStatusChangeTable[SL_STAR] = MAPID_STAR_GLADIATOR, - SkillStatusChangeTable[SL_SAGE] = MAPID_SAGE, - SkillStatusChangeTable[SL_CRUSADER] = MAPID_CRUSADER, - SkillStatusChangeTable[SL_SUPERNOVICE] = MAPID_SUPER_NOVICE, - SkillStatusChangeTable[SL_KNIGHT] = MAPID_KNIGHT, - SkillStatusChangeTable[SL_WIZARD] = MAPID_WIZARD, - SkillStatusChangeTable[SL_PRIEST] = MAPID_PRIEST, - SkillStatusChangeTable[SL_BARDDANCER] = MAPID_BARDDANCER, - SkillStatusChangeTable[SL_ROGUE] = MAPID_ROGUE, - SkillStatusChangeTable[SL_ASSASIN] = MAPID_ASSASSIN, - SkillStatusChangeTable[SL_BLACKSMITH] = MAPID_BLACKSMITH, - SkillStatusChangeTable[SL_HUNTER] = MAPID_HUNTER, - SkillStatusChangeTable[SL_SOULLINKER] = MAPID_SOUL_LINKER, - - //Status that don't have a skill associated. - StatusIconChangeTable[SC_WEIGHT50 ] = SI_WEIGHT50; - StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90; - StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION; - StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION; - StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION; - StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTION; - StatusIconChangeTable[SC_SPEEDUP0] = SI_SPEEDPOTION; - StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION; - StatusIconChangeTable[SC_MIRACLE] = SI_SPIRIT; - - //Guild skills don't fit due to their range being beyond MAX_SKILL - StatusIconChangeTable[SC_GUILDAURA] = SI_GUILDAURA; - StatusIconChangeTable[SC_BATTLEORDERS] = SI_BATTLEORDERS; -#undef set_sc - - if (!battle_config.display_hallucination) //Disable Hallucination. - StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK; -} - -/*========================================== - * 精錬ボーナス - *------------------------------------------ - */ -int status_getrefinebonus(int lv,int type) -{ - if (lv >= 0 && lv < 5 && type >= 0 && type < 3) - return refinebonus[lv][type]; - return 0; -} - -/*========================================== - * Checks whether the src can use the skill on the target, - * taking into account status/option of both source/target. [Skotlex] - * flag: - * 0 - Trying to use skill on target. - * 1 - Cast bar is done. - * 2 - Skill already pulled off, check is due to ground-based skills or splash-damage ones. - * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack. - * target MAY Be null, in which case the checks are only to see - * whether the source can cast or not the skill on the ground. - *------------------------------------------ - */ -int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag) -{ - int mode, race, hide_flag; - struct status_change *sc=NULL, *tsc; - - mode = src?status_get_mode(src):MD_CANATTACK; - - if (src && status_isdead(src)) - return 0; - - if (!skill_num) { //Normal attack checks. - if (!(mode&MD_CANATTACK)) - return 0; //This mode is only needed for melee attacking. - //Dead state is not checked for skills as some skills can be used - //on dead characters, said checks are left to skill.c [Skotlex] - if (target && status_isdead(target)) - return 0; - } - - if (skill_num == PA_PRESSURE && flag) { - //Gloria Avoids pretty much everything.... - tsc = target?status_get_sc(target):NULL; - if(tsc) { - if (tsc->option&OPTION_HIDE) - return 0; - if (tsc->count && tsc->data[SC_TRICKDEAD].timer != -1) - return 0; - } - return 1; - } - - if (((src && map_getcell(src->m,src->x,src->y,CELL_CHKBASILICA)) || - (target && target != src && map_getcell(target->m,target->x,target->y,CELL_CHKBASILICA))) - && !(mode&MD_BOSS)) - { //Basilica Check - if (!skill_num) return 0; - race = skill_get_inf(skill_num); - if (race&INF_ATTACK_SKILL) - return 0; - if (race&INF_GROUND_SKILL && skill_get_unit_target(skill_num)&BCT_ENEMY) - return 0; - } - - if (src) sc = status_get_sc(src); - - if(sc && sc->opt1 >0 && (battle_config.sc_castcancel || flag != 1)) - //When sc do not cancel casting, the spell should come out. - return 0; - - if(sc && sc->count) - { - if ( - (sc->data[SC_TRICKDEAD].timer != -1 && skill_num != NV_TRICKDEAD) - || (sc->data[SC_AUTOCOUNTER].timer != -1 && !flag) - || (sc->data[SC_GOSPEL].timer != -1 && sc->data[SC_GOSPEL].val4 == BCT_SELF && skill_num != PA_GOSPEL) - || (sc->data[SC_GRAVITATION].timer != -1 && sc->data[SC_GRAVITATION].val3 == BCT_SELF && skill_num != HW_GRAVITATION) - ) - return 0; - - if (sc->data[SC_WINKCHARM].timer != -1 && target && target->type == BL_PC && !flag) - { //Prevents skill usage against players? - clif_emotion(src, 3); - return 0; - } - - if (sc->data[SC_BLADESTOP].timer != -1) { - switch (sc->data[SC_BLADESTOP].val1) - { - case 1: return 0; - case 2: if (skill_num != MO_FINGEROFFENSIVE) return 0; break; - case 3: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE) return 0; break; - case 4: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO) return 0; break; - case 5: if (skill_num != MO_FINGEROFFENSIVE && skill_num != MO_INVESTIGATE && skill_num != MO_CHAINCOMBO && skill_num!=MO_EXTREMITYFIST) return 0; break; - default: return 0; - } - } - if (skill_num && //Do not block item-casted skills. - (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_num) - ) { //Skills blocked through status changes... - if (!flag && ( //Blocked only from using the skill (stuff like autospell may still go through - (sc->data[SC_MARIONETTE].timer != -1 && skill_num != CG_MARIONETTE) || - (sc->data[SC_MARIONETTE2].timer != -1 && skill_num == CG_MARIONETTE) || - sc->data[SC_SILENCE].timer != -1 || - sc->data[SC_STEELBODY].timer != -1 || - sc->data[SC_BERSERK].timer != -1 - )) - return 0; - //Skill blocking. - if ( - (sc->data[SC_VOLCANO].timer != -1 && skill_num == WZ_ICEWALL) || - (sc->data[SC_ROKISWEIL].timer != -1 && skill_num != BD_ADAPTATION && !(mode&MD_BOSS)) || - (sc->data[SC_HERMODE].timer != -1 && skill_get_inf(skill_num) & INF_SUPPORT_SKILL) || - sc->data[SC_NOCHAT].timer != -1 - ) - return 0; - - if (flag!=2 && sc->data[SC_DANCING].timer != -1) - { - if (skill_num != BD_ADAPTATION && skill_num != CG_LONGINGFREEDOM - && skill_num != BA_MUSICALSTRIKE && skill_num != DC_THROWARROW) - return 0; - if (sc->data[SC_DANCING].val1 == CG_HERMODE && skill_num == BD_ADAPTATION) - return 0; //Can't amp out of Wand of Hermode :/ [Skotlex] - } - } - } - - if (sc && sc->option) - { - if (sc->option&OPTION_HIDE && skill_num != TF_HIDING && skill_num != AS_GRIMTOOTH - && skill_num != RG_BACKSTAP && skill_num != RG_RAID && skill_num != NJ_SHADOWJUMP - && skill_num != NJ_KIRIKAGE) - return 0; -// if (sc->option&OPTION_CLOAK && skill_num == TF_HIDING) -// return 0; //Latest reports indicate Hiding is usable while Cloaking. [Skotlex] - if (sc->option&OPTION_CHASEWALK && skill_num != ST_CHASEWALK) - return 0; - } - if (target == NULL || target == src) //No further checking needed. - return 1; - - tsc = status_get_sc(target); - if(tsc && tsc->count) - { - if (!(mode & MD_BOSS) && tsc->data[SC_TRICKDEAD].timer != -1) - return 0; - if(skill_num == WZ_STORMGUST && tsc->data[SC_FREEZE].timer != -1) - return 0; - if(skill_num == PR_LEXAETERNA && (tsc->data[SC_FREEZE].timer != -1 || (tsc->data[SC_STONE].timer != -1 && tsc->data[SC_STONE].val2 == 0))) - return 0; - } - - race = src?status_get_race(src):0; - //If targetting, cloak+hide protect you, otherwise only hiding does. - hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK); - - //You cannot hide from ground skills. - if(skill_get_pl(skill_num) == 2) - hide_flag &= ~OPTION_HIDE; - - switch (target->type) - { - case BL_PC: - { - struct map_session_data *sd = (struct map_session_data*) target; - if (pc_isinvisible(sd)) - return 0; - if (tsc->option&hide_flag - && (sd->state.perfect_hiding || !(race == RC_INSECT || race == RC_DEMON || mode&MD_DETECTOR)) - && !(mode&MD_BOSS)) - return 0; - } - break; - case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them). - //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex] - if (mode&MD_LOOTER) - return 1; - else - return 0; - default: - //Check for chase-walk/hiding/cloaking opponents. - if (tsc && !(mode&MD_BOSS)) - { - if (tsc->option&hide_flag && !(race == RC_INSECT || race == RC_DEMON || mode&MD_DETECTOR)) - return 0; - } - } - return 1; -} - -//Skotlex: Calculates the stats of the given pet. -int status_calc_pet(struct map_session_data *sd, int first) -{ - struct pet_data *pd; - - nullpo_retr(0, sd); - if (sd->status.pet_id == 0 || sd->pd == NULL) - return 1; - - pd = sd->pd; - - if (battle_config.pet_lv_rate && pd->status) - { - sd->pet.level = sd->status.base_level*battle_config.pet_lv_rate/100; - if (sd->pet.level < 0) - sd->pet.level = 1; - if (pd->status->level != sd->pet.level || first) - { - if (!first) //Lv Up animation - clif_misceffect(&pd->bl, 0); - pd->status->level = sd->pet.level; - pd->status->atk1 = (pd->db->atk1*pd->status->level)/pd->db->lv; - pd->status->atk2 = (pd->db->atk2*pd->status->level)/pd->db->lv; - pd->status->str = (pd->db->str*pd->status->level)/pd->db->lv; - pd->status->agi = (pd->db->agi*pd->status->level)/pd->db->lv; - pd->status->vit = (pd->db->vit*pd->status->level)/pd->db->lv; - pd->status->int_ = (pd->db->int_*pd->status->level)/pd->db->lv; - pd->status->dex = (pd->db->dex*pd->status->level)/pd->db->lv; - pd->status->luk = (pd->db->luk*pd->status->level)/pd->db->lv; - - if (pd->status->atk1 > battle_config.pet_max_atk1) pd->status->atk1 = battle_config.pet_max_atk1; - if (pd->status->atk2 > battle_config.pet_max_atk2) pd->status->atk2 = battle_config.pet_max_atk2; - - if (pd->status->str > battle_config.pet_max_stats) pd->status->str = battle_config.pet_max_stats; - else if (pd->status->str < 1) pd->status->str = 1; - if (pd->status->agi > battle_config.pet_max_stats) pd->status->agi = battle_config.pet_max_stats; - else if (pd->status->agi < 1) pd->status->agi = 1; - if (pd->status->vit > battle_config.pet_max_stats) pd->status->vit = battle_config.pet_max_stats; - else if (pd->status->vit < 1) pd->status->vit = 1; - if (pd->status->int_ > battle_config.pet_max_stats) pd->status->int_ = battle_config.pet_max_stats; - else if (pd->status->int_ < 1) pd->status->int_ = 1; - if (pd->status->dex > battle_config.pet_max_stats) pd->status->dex = battle_config.pet_max_stats; - else if (pd->status->dex < 1) pd->status->dex = 1; - if (pd->status->luk > battle_config.pet_max_stats) pd->status->luk = battle_config.pet_max_stats; - else if (pd->status->luk < 1) pd->status->luk = 1; - - if (!first) //Not done the first time because the pet is not visible yet - clif_send_petstatus(sd); - } - } - //Support rate modifier (1000 = 100%) - pd->rate_fix = 1000*(sd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500; - if(battle_config.pet_support_rate != 100) - pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100; - return 0; -} - -/*========================================== - * パラメータ計算 - * first==0の時、計算対象のパラメータが呼び出し前から - * 変 化した場合自動でsendするが、 - * 能動的に変化させたパラメータは自前でsendするように - *------------------------------------------ - */ - -int status_calc_pc(struct map_session_data* sd,int first) -{ - static int calculating = 0; //Check for recursive call preemption. [Skotlex] - int b_speed,b_max_hp,b_max_sp,b_hp,b_sp,b_weight,b_max_weight,b_paramb[6],b_parame[6],b_hit,b_flee; - int b_aspd,b_watk,b_def,b_watk2,b_def2,b_flee2,b_critical,b_attackrange,b_matk1,b_matk2,b_mdef,b_mdef2; - int b_base_atk; - struct skill b_skill[MAX_SKILL]; - int i,bl,index; - int skill,refinedef=0; - int str,dstr,dex; - - nullpo_retr(0, sd); - if (++calculating > 10) //Too many recursive calls to status_calc_pc! - return -1; - - b_speed = sd->speed; - b_max_hp = sd->status.max_hp; - b_max_sp = sd->status.max_sp; - b_hp = sd->status.hp; - b_sp = sd->status.sp; - b_weight = sd->weight; - b_max_weight = sd->max_weight; - memcpy(b_paramb,&sd->paramb,sizeof(b_paramb)); - memcpy(b_parame,&sd->paramc,sizeof(b_parame)); - memcpy(b_skill,&sd->status.skill,sizeof(b_skill)); - b_hit = sd->hit; - b_flee = sd->flee; - b_aspd = sd->aspd; - b_watk = sd->right_weapon.watk + sd->left_weapon.watk; - b_def = sd->def; - b_watk2 = sd->right_weapon.watk2 + sd->left_weapon.watk2; - b_def2 = sd->def2; - b_flee2 = sd->flee2; - b_critical = sd->critical; - b_attackrange = sd->attackrange; - b_matk1 = sd->matk1; - b_matk2 = sd->matk2; - b_mdef = sd->mdef; - b_mdef2 = sd->mdef2; - b_base_atk = sd->base_atk; - - pc_calc_skilltree(sd); // スキルツリ?の計算 - - sd->max_weight = max_weight_base[sd->status.class_]+sd->status.str*300; - - if(first&1) { - sd->weight=0; - for(i=0;istatus.inventory[i].nameid==0 || sd->inventory_data[i] == NULL) - continue; - sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount; - } - sd->cart_max_weight=battle_config.max_cart_weight; - sd->cart_weight=0; - sd->cart_max_num=MAX_CART; - sd->cart_num=0; - for(i=0;istatus.cart[i].nameid==0) - continue; - sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount; - sd->cart_num++; - } - } - - // these are not zeroed. [zzo] - - sd->speed = DEFAULT_WALK_SPEED; - sd->hprate=100; - sd->sprate=100; - sd->castrate=100; - sd->delayrate=100; - sd->dsprate=100; - sd->aspd_rate = 100; - sd->speed_rate = 100; - sd->hprecov_rate = 100; - sd->sprecov_rate = 100; - sd->atk_rate = sd->matk_rate = 100; - sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100; - sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100; - sd->speed_add_rate = sd->aspd_add_rate = 100; - - // zeroed arays, order follows the order in map.h. - // add new arrays to the end of zeroed area in map.h (see comments) and size here. [zzo] - memset (sd->paramb, 0, sizeof(sd->paramb) - + sizeof(sd->parame) - + sizeof(sd->subele) - + sizeof(sd->subrace) - + sizeof(sd->subrace2) - + sizeof(sd->subsize) - + sizeof(sd->addeff) - + sizeof(sd->addeff2) - + sizeof(sd->reseff) - + sizeof(sd->weapon_coma_ele) - + sizeof(sd->weapon_coma_race) - + sizeof(sd->weapon_atk) - + sizeof(sd->weapon_atk_rate) - + sizeof(sd->arrow_addele) - + sizeof(sd->arrow_addrace) - + sizeof(sd->arrow_addsize) - + sizeof(sd->arrow_addeff) - + sizeof(sd->arrow_addeff2) - + sizeof(sd->magic_addele) - + sizeof(sd->magic_addrace) - + sizeof(sd->magic_addsize) - + sizeof(sd->critaddrace) - + sizeof(sd->expaddrace) - + sizeof(sd->itemhealrate) - + sizeof(sd->addeff3) - + sizeof(sd->addeff3_type) - + sizeof(sd->sp_gain_race) - ); - - - memset (&sd->right_weapon.watk, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods)); - memset (&sd->left_weapon.watk, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods)); - - memset(&sd->special_state,0,sizeof(sd->special_state)); - - sd->status.max_hp = 0; - sd->status.max_sp = 0; - - //zero up structures... - memset(&sd->autospell,0,sizeof(sd->autospell) - + sizeof(sd->autospell2) - + sizeof(sd->skillatk) - + sizeof(sd->skillblown) - + sizeof(sd->add_def) - + sizeof(sd->add_mdef) - + sizeof(sd->add_dmg) - + sizeof(sd->add_mdmg) - + sizeof(sd->add_drop) - ); - - // vars zeroing. ints, shorts, chars. in that order. - memset (&sd->hit, 0, sizeof(sd->hit) - + sizeof(sd->flee) - + sizeof(sd->flee2) - + sizeof(sd->critical) - + sizeof(sd->aspd) - + sizeof(sd->def) - + sizeof(sd->mdef) - + sizeof(sd->def2) - + sizeof(sd->mdef2) - + sizeof(sd->def_ele) - + sizeof(sd->matk1) - + sizeof(sd->matk2) - + sizeof(sd->base_atk) - + sizeof(sd->arrow_atk) - + sizeof(sd->arrow_ele) - + sizeof(sd->arrow_cri) - + sizeof(sd->arrow_hit) - + sizeof(sd->arrow_range) - + sizeof(sd->nhealhp) - + sizeof(sd->nhealsp) - + sizeof(sd->nshealhp) - + sizeof(sd->nshealsp) - + sizeof(sd->nsshealhp) - + sizeof(sd->nsshealsp) - + sizeof(sd->critical_def) - + sizeof(sd->double_rate) - + sizeof(sd->long_attack_atk_rate) - + sizeof(sd->near_attack_def_rate) - + sizeof(sd->long_attack_def_rate) - + sizeof(sd->magic_def_rate) - + sizeof(sd->misc_def_rate) - + sizeof(sd->ignore_mdef_ele) - + sizeof(sd->ignore_mdef_race) - + sizeof(sd->perfect_hit) - + sizeof(sd->perfect_hit_add) - + sizeof(sd->get_zeny_rate) - + sizeof(sd->get_zeny_num) - + sizeof(sd->double_add_rate) - + sizeof(sd->short_weapon_damage_return) - + sizeof(sd->long_weapon_damage_return) - + sizeof(sd->magic_damage_return) - + sizeof(sd->random_attack_increase_add) - + sizeof(sd->random_attack_increase_per) - + sizeof(sd->break_weapon_rate) - + sizeof(sd->break_armor_rate) - + sizeof(sd->crit_atk_rate) - + sizeof(sd->hp_loss_rate) - + sizeof(sd->sp_loss_rate) - + sizeof(sd->classchange) - + sizeof(sd->setitem_hash) - + sizeof(sd->setitem_hash2) - // shorts - + sizeof(sd->attackrange) - + sizeof(sd->attackrange_) - + sizeof(sd->splash_range) - + sizeof(sd->splash_add_range) - + sizeof(sd->add_steal_rate) - + sizeof(sd->hp_loss_value) - + sizeof(sd->sp_loss_value) - + sizeof(sd->hp_loss_type) - + sizeof(sd->hp_gain_value) - + sizeof(sd->sp_gain_value) - + sizeof(sd->add_drop_count) - + sizeof(sd->unbreakable) - + sizeof(sd->unbreakable_equip) - + sizeof(sd->unstripable_equip) - + sizeof(sd->no_regen) - + sizeof(sd->add_def_count) - + sizeof(sd->add_mdef_count) - + sizeof(sd->add_dmg_count) - + sizeof(sd->add_mdmg_count) - ); - - for(i=0;i<10;i++) { - current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus] - if(index < 0) - continue; - if(i == 9 && sd->equip_index[8] == index) - continue; - if(i == 5 && sd->equip_index[4] == index) - continue; - if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) - continue; - - if(sd->inventory_data[index]) { - int j,c; - struct item_data *data; - - //Card script execution. - if(sd->status.inventory[index].card[0]==0x00ff || - sd->status.inventory[index].card[0]==0x00fe || - sd->status.inventory[index].card[0]==(short)0xff00) - continue; - for(j=0;jinventory_data[index]->slot;j++){ // カ?ド - current_equip_card_id= c= sd->status.inventory[index].card[j]; - if(!c) - continue; - data = itemdb_exists(c); - if(!data) - continue; - if(first&1 && data->equip_script) - { //Execute equip-script on login - run_script(data->equip_script,0,sd->bl.id,0); - if (!calculating) - return 1; - } - if(!data->script) - continue; - if(data->flag.no_equip) { //Card restriction checks. - if(map[sd->bl.m].flag.restricted && data->flag.no_equip&map[sd->bl.m].zone) - continue; - if(map[sd->bl.m].flag.pvp && data->flag.no_equip&1) - continue; - if(map_flag_gvg(sd->bl.m) && data->flag.no_equip&2) - continue; - } - if(i == 8 && sd->status.inventory[index].equip == 0x20) - { //Left hand status. - sd->state.lr_flag = 1; - run_script(data->script,0,sd->bl.id,0); - sd->state.lr_flag = 0; - } else - run_script(data->script,0,sd->bl.id,0); - if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex] - return 1; - } - } - } - - if(sd->status.pet_id > 0 && battle_config.pet_status_support && sd->pet.intimate > 0) - { // Pet - struct pet_data *pd=sd->pd; - if(pd && (!battle_config.pet_equip_required || pd->equip > 0) && - pd->state.skillbonus == 1 && pd->bonus) //Skotlex: Readjusted for pets - pc_bonus(sd,pd->bonus->type, pd->bonus->val); - } - memcpy(sd->paramcard,sd->parame,sizeof(sd->paramcard)); - - // ?備品によるステ?タス?化はここで?行 - for(i=0;i<10;i++) { - current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus] - if(index < 0) - continue; - if(i == 9 && sd->equip_index[8] == index) - continue; - if(i == 5 && sd->equip_index[4] == index) - continue; - if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) - continue; - if(!sd->inventory_data[index]) - continue; - - sd->def += sd->inventory_data[index]->def; - - if(first&1 && sd->inventory_data[index]->equip_script) - { //Execute equip-script on login - run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0); - if (!calculating) - return 1; - } - - if(sd->inventory_data[index]->type == 4) { - int r,wlv = sd->inventory_data[index]->wlv; - struct weapon_data *wd; - if (wlv >= MAX_REFINE_BONUS) - wlv = MAX_REFINE_BONUS - 1; - if(i == 8 && sd->status.inventory[index].equip == 0x20) - wd = &sd->left_weapon; // Left-hand weapon - else - wd = &sd->right_weapon; - wd->watk += sd->inventory_data[index]->atk; - wd->watk2 = (r=sd->status.inventory[index].refine)*refinebonus[wlv][0]; - if((r-=refinebonus[wlv][2])>0) //Overrefine bonus. - wd->overrefine = r*refinebonus[wlv][1]; - - if (wd == &sd->left_weapon) { - sd->attackrange_ += sd->inventory_data[index]->range; - if(sd->inventory_data[index]->script) { - sd->state.lr_flag = 1; - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - sd->state.lr_flag = 0; - } - } else { - sd->attackrange += sd->inventory_data[index]->range; - if(sd->inventory_data[index]->script) - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - } - if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex] - return 1; - - if(sd->status.inventory[index].card[0]==0x00ff) - { // Forged weapon - wd->star += (sd->status.inventory[index].card[1]>>8); - if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg - if(pc_famerank( MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH)) - wd->star += 10; - - if (!wd->atk_ele) //Do not overwrite element from previous bonuses. - wd->atk_ele = (sd->status.inventory[index].card[1]&0x0f); - - } - } - else if(sd->inventory_data[index]->type == 5) { - refinedef += sd->status.inventory[index].refine*refinebonus[0][0]; - if(sd->inventory_data[index]->script) { - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex] - return 1; - } - } - } - - if(sd->equip_index[10] >= 0){ // 矢 - index = sd->equip_index[10]; - if(sd->inventory_data[index]){ // Arrows - sd->state.lr_flag = 2; - run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); - sd->state.lr_flag = 0; - sd->arrow_atk += sd->inventory_data[index]->atk; - } - } - sd->def += (refinedef+50)/100; - - if(sd->attackrange < 1) sd->attackrange = 1; - if(sd->attackrange_ < 1) sd->attackrange_ = 1; - if(sd->attackrange < sd->attackrange_) - sd->attackrange = sd->attackrange_; - if(sd->status.weapon == W_BOW) - sd->attackrange += sd->arrow_range; - sd->double_rate += sd->double_add_rate; - sd->perfect_hit += sd->perfect_hit_add; - sd->splash_range += sd->splash_add_range; - if(sd->aspd_add_rate != 100) - sd->aspd_rate += sd->aspd_add_rate-100; - if(sd->speed_add_rate != 100) - sd->speed_rate += sd->speed_add_rate-100; - - // Damage modifiers from weapon type - sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1]; - sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1]; - sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1]; - sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2]; - sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2]; - sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2]; - -// ----- STATS CALCULATION ----- - - // Job bonuses - for(i=0;i<(int)sd->status.job_level && istatus.class_][i]) - sd->paramb[job_bonus[sd->status.class_][i]-1]++; - } - - // If a Super Novice has never died and is at least joblv 70, he gets all stats +10 - if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){ - sd->paramb[0] += 10; - sd->paramb[1] += 10; - sd->paramb[2] += 10; - sd->paramb[3] += 10; - sd->paramb[4] += 10; - sd->paramb[5] += 10; - } - - // Absolute modifiers from passive skills - if(pc_checkskill(sd,BS_HILTBINDING)>0) - sd->paramb[0] ++; - if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0) - sd->paramb[3] += (skill+1)/2; // +1 INT / 2 lv - if((skill=pc_checkskill(sd,AC_OWL))>0) - sd->paramb[4] += skill; - - // Improve Concentration adds a percentage of base stat + job bonus + equipment bonus, but not from card bonus nor status change bonus - if(sd->sc.count && sd->sc.data[SC_CONCENTRATE].timer!=-1 && sd->sc.data[SC_QUAGMIRE].timer == -1){ - sd->paramb[1] += (sd->status.agi+sd->paramb[1]+sd->parame[1]-sd->paramcard[1])*(2+sd->sc.data[SC_CONCENTRATE].val1)/100; - sd->paramb[4] += (sd->status.dex+sd->paramb[4]+sd->parame[4]-sd->paramcard[4])*(2+sd->sc.data[SC_CONCENTRATE].val1)/100; - } - - // Absolute modifiers from status changes (only for PC) - if(sd->sc.count){ - if (sd->sc.data[SC_BATTLEORDERS].timer != -1) { - sd->paramb[0] += 5; - sd->paramb[3] += 5; - sd->paramb[4] += 5; - } - if (sd->sc.data[SC_GUILDAURA].timer != -1) { - int guildflag = sd->sc.data[SC_GUILDAURA].val4; - for (i = 12; i >= 0; i -= 4) { - skill = guildflag >> i; - switch (i) { - case 12: sd->paramb[0] += skill; break; - case 8: sd->paramb[2] += skill; break; - case 4: sd->paramb[1] += skill; break; - case 0: sd->paramb[4] += skill; break; - } - guildflag ^= skill << i; - } - } - } - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->paramb[0] = status_calc_str(&sd->bl,sd->paramb[0]); - sd->paramb[1] = status_calc_agi(&sd->bl,sd->paramb[1]); - sd->paramb[2] = status_calc_vit(&sd->bl,sd->paramb[2]); - sd->paramb[3] = status_calc_int(&sd->bl,sd->paramb[3]); - sd->paramb[4] = status_calc_dex(&sd->bl,sd->paramb[4]); - sd->paramb[5] = status_calc_luk(&sd->bl,sd->paramb[5]); - - // Relative modifiers from status changes (only for PC) - if(sd->sc.count){ - if(sd->sc.data[SC_MARIONETTE].timer!=-1){ - sd->paramb[0]-= sd->status.str/2; - sd->paramb[1]-= sd->status.agi/2; - sd->paramb[2]-= sd->status.vit/2; - sd->paramb[3]-= sd->status.int_/2; - sd->paramb[4]-= sd->status.dex/2; - sd->paramb[5]-= sd->status.luk/2; - } - else if(sd->sc.data[SC_MARIONETTE2].timer!=-1){ - struct map_session_data *psd = map_id2sd(sd->sc.data[SC_MARIONETTE2].val3); - if (psd) { // if partner is found - bl = pc_maxparameter(sd); //Cap to max parameter. [Skotlex] - if (sd->status.str < bl) - sd->paramb[0] += sd->status.str+psd->status.str/2 > bl ? - bl-sd->status.str : psd->status.str/2; - if (sd->status.agi < bl) - sd->paramb[1] += sd->status.agi+psd->status.agi/2 > bl ? - bl-sd->status.agi : psd->status.agi/2; - if (sd->status.vit < bl) - sd->paramb[2] += sd->status.vit+psd->status.vit/2 > bl ? - bl-sd->status.vit : psd->status.vit/2; - if (sd->status.int_ < bl) - sd->paramb[3] += sd->status.int_+psd->status.int_/2 > bl ? - bl-sd->status.int_ : psd->status.int_/2; - if (sd->status.dex < bl) - sd->paramb[4] += sd->status.dex+psd->status.dex/2 > bl ? - bl-sd->status.dex : psd->status.dex/2; - if (sd->status.luk < bl) - sd->paramb[5] += sd->status.luk+psd->status.luk/2 > bl ? - bl-sd->status.luk : psd->status.luk/2; - } - } - } - - // Calculate total stats - sd->paramc[0]=sd->status.str+sd->paramb[0]+sd->parame[0]; - sd->paramc[1]=sd->status.agi+sd->paramb[1]+sd->parame[1]; - sd->paramc[2]=sd->status.vit+sd->paramb[2]+sd->parame[2]; - sd->paramc[3]=sd->status.int_+sd->paramb[3]+sd->parame[3]; - sd->paramc[4]=sd->status.dex+sd->paramb[4]+sd->parame[4]; - sd->paramc[5]=sd->status.luk+sd->paramb[5]+sd->parame[5]; - - if(sd->sc.count){ - if(sd->sc.data[SC_SPIRIT].timer!=-1 && sd->sc.data[SC_SPIRIT].val2 == SL_HIGH) - { //Ups any status under 50 to 50. - if (sd->paramc[0] < 50) { - sd->paramb[0] += 50-sd->paramc[0]; - sd->paramc[0] = 50; - } - if (sd->paramc[1] < 50) { - sd->paramb[1] += 50-sd->paramc[1]; - sd->paramc[1] = 50; - } - if (sd->paramc[2] < 50) { - sd->paramb[2] += 50-sd->paramc[2]; - sd->paramc[2] = 50; - } - if (sd->paramc[3] < 50) { - sd->paramb[3] += 50-sd->paramc[3]; - sd->paramc[3] = 50; - } - if (sd->paramc[4] < 50) { - sd->paramb[4] += 50-sd->paramc[4]; - sd->paramc[4] = 50; - } - if (sd->paramc[5] < 50) { - sd->paramb[5] += 50-sd->paramc[5]; - sd->paramc[5] = 50; - } - } - } - for(i=0;i<6;i++) - if(sd->paramc[i] < 0) sd->paramc[i] = 0; - - if (sd->sc.count && sd->sc.data[SC_CURSE].timer!=-1) - sd->paramc[5] = 0; - -// ------ BASE ATTACK CALCULATION ------ - - // Basic Base ATK value - switch(sd->status.weapon){ - case W_BOW: - case W_MUSICAL: - case W_WHIP: - case W_REVOLVER: - case W_RIFLE: - case W_SHOTGUN: - case W_GATLING: - case W_GRENADE: - str = sd->paramc[4]; - dex = sd->paramc[0]; - break; - default: - str = sd->paramc[0]; - dex = sd->paramc[4]; - break; - } - dstr = str/10; - sd->base_atk += str + dstr*dstr + dex/5 + sd->paramc[5]/5; - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,BS_HILTBINDING))>0) - sd->base_atk += 4; - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->base_atk = status_calc_batk(&sd->bl,sd->base_atk); - - if(sd->base_atk < 1) - sd->base_atk = 1; - -// ----- WEAPON ATK CALCULATION ----- - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->right_weapon.watk = status_calc_watk(&sd->bl,sd->right_weapon.watk); - if((index= sd->equip_index[8]) >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == 4) - sd->left_weapon.watk = status_calc_watk(&sd->bl,sd->left_weapon.watk); - -// ----- WEAPON UPGRADE ATK CALCULATION ----- - - // Absolute modifiers from status changes (only for PC) - if(sd->sc.count){ - if(sd->sc.data[SC_NIBELUNGEN].timer!=-1){ - index = sd->equip_index[9]; - if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4) - sd->right_weapon.watk2 += sd->sc.data[SC_NIBELUNGEN].val2; - index = sd->equip_index[8]; - if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4) - sd->left_weapon.watk2 += sd->sc.data[SC_NIBELUNGEN].val2; - } - } - -// ----- MATK CALCULATION ----- - - // Basic MATK value - sd->matk1 += sd->paramc[3]+(sd->paramc[3]/5)*(sd->paramc[3]/5); - sd->matk2 += sd->paramc[3]+(sd->paramc[3]/7)*(sd->paramc[3]/7); - if(sd->matk1 < sd->matk2) { - int temp = sd->matk2; - sd->matk2 = sd->matk1; - sd->matk1 = temp; - } - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->matk1 = status_calc_matk(&sd->bl,sd->matk1); - sd->matk2 = status_calc_matk(&sd->bl,sd->matk2); - - // Apply relative modifiers from equipment - if(sd->matk_rate != 100){ - sd->matk1 = sd->matk1 * sd->matk_rate/100; - sd->matk2 = sd->matk2 * sd->matk_rate/100; - } - -// ----- CRIT CALCULATION ----- - - // Basic Crit value - sd->critical += (sd->paramc[5]*3)+10; - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->critical = status_calc_critical(&sd->bl,sd->critical); - - // Apply relative modifiers from equipment - if(sd->critical_rate != 100) - sd->critical = sd->critical * sd->critical_rate/100; - - if(sd->critical < 10) sd->critical = 10; - -// ----- HIT CALCULATION ----- - - // Basic Hit value - sd->hit += sd->paramc[4] + sd->status.base_level; - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0) - sd->hit += skill*2; - if((skill=pc_checkskill(sd,AC_VULTURE))>0){ - sd->hit += skill; - if(sd->status.weapon == W_BOW) - sd->attackrange += skill; - } - if(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE) - { - if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0) - sd->hit += 2*skill; - if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) { - sd->hit += skill; - sd->attackrange += skill; - } - } - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->hit = status_calc_hit(&sd->bl,sd->hit); - - // Apply relative modifiers from equipment - if(sd->hit_rate != 100) - sd->hit = sd->hit * sd->hit_rate/100; - - if(sd->hit < 1) sd->hit = 1; - -// ----- FLEE CALCULATION ----- - - // Basic Flee value - sd->flee += sd->paramc[1] + sd->status.base_level; - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,TF_MISS))>0) - sd->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3); - if((skill=pc_checkskill(sd,MO_DODGE))>0) - sd->flee += (skill*3)>>1; - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->flee = status_calc_flee(&sd->bl,sd->flee); - - // Apply relative modifiers from equipment - if(sd->flee_rate != 100) - sd->flee = sd->flee * sd->flee_rate/100; - - if(sd->flee < 1) sd->flee = 1; - -// ----- PERFECT DODGE CALCULATION ----- - - // Basic Perfect Dodge value - sd->flee2 += sd->paramc[5]+10; - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->flee2 = status_calc_flee2(&sd->bl,sd->flee2); - - // Apply relative modifiers from equipment - if(sd->flee2_rate != 100) - sd->flee2 = sd->flee2 * sd->flee2_rate/100; - - if(sd->flee2 < 10) sd->flee2 = 10; - -// ----- VIT-DEF CALCULATION ----- - - // Special fixed values from status changes - if(sd->sc.count && sd->sc.data[SC_BERSERK].timer!=-1) - sd->def2 = 0; - else if(sd->sc.count && sd->sc.data[SC_ETERNALCHAOS].timer!=-1) - sd->def2 = 0; - else { - // Basic VIT-DEF value - sd->def2 += sd->paramc[2]; - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->def2 = status_calc_def2(&sd->bl,sd->def2); - - // Apply relative modifiers from equipment - if(sd->def2_rate != 100) - sd->def2 = sd->def2 * sd->def2_rate/100; - - if(sd->def2 < 1) sd->def2 = 1; - } - -// ----- EQUIPMENT-DEF CALCULATION ----- - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->def = status_calc_def(&sd->bl,sd->def); - - // Apply relative modifiers from equipment - if(sd->def_rate != 100) - sd->def = sd->def * sd->def_rate/100; - - if(sd->def < 0) sd->def = 0; - - else if (!battle_config.player_defense_type && sd->def > battle_config.max_def) - { - sd->def2 += battle_config.over_def_bonus*(sd->def -battle_config.max_def); - sd->def = battle_config.max_def; - } - -// ----- INT-MDEF CALCULATION ----- - - // Special fixed values from status changes - if(sd->sc.count && sd->sc.data[SC_BERSERK].timer!=-1) - sd->mdef2 = 0; - else { - // Basic INT-MDEF value - sd->mdef2 += sd->paramc[3]; - - sd->mdef2 = status_calc_mdef2(&sd->bl,sd->mdef2); - - // Apply relative modifiers from equipment - if(sd->mdef2_rate != 100) - sd->mdef2 = sd->mdef2 * sd->mdef2_rate/100; - - if(sd->mdef2 < 1) sd->mdef2 = 1; - } - -// ----- EQUIPMENT-MDEF CALCULATION ----- - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->mdef = status_calc_mdef(&sd->bl,sd->mdef); - - // Apply relative modifiers from equipment - if(sd->mdef_rate != 100) - sd->mdef = sd->mdef * sd->mdef_rate/100; - - if(sd->mdef < 0) sd->mdef = 0; - - else if (!battle_config.player_defense_type && sd->mdef > battle_config.max_def) - { - sd->mdef2 += battle_config.over_def_bonus*(sd->mdef -battle_config.max_def); - sd->mdef = battle_config.max_def; - } - -// ----- WALKING SPEED CALCULATION ----- - - // Relative, then absolute modifiers from status changes (shared between PC and NPC) - sd->speed = status_calc_speed(&sd->bl,sd->speed); - - // Relative modifiers from passive skills - if((skill=pc_checkskill(sd,TF_MISS))>0 && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && sd->sc.data[SC_CLOAKING].timer==-1) - sd->speed -= sd->speed * skill/100; - if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0) - sd->speed -= sd->speed * 25/100; - if(pc_ishiding(sd) && (skill=pc_checkskill(sd,RG_TUNNELDRIVE))>0) - sd->speed += sd->speed * (100-16*skill)/100; - if(pc_iscarton(sd) && (skill=pc_checkskill(sd,MC_PUSHCART))>0) - sd->speed += sd->speed * (100-10*skill)/100; - if(sd->ud.skilltimer != -1 && (skill=pc_checkskill(sd,SA_FREECAST))>0) { - sd->prev_speed = sd->speed; //Store previous speed to correctly restore it. [Skotlex] - sd->speed += sd->speed * (75-5*skill)/100; - } - if(sd->sc.count && sd->sc.data[SC_DANCING].timer!=-1){ - int s_rate = 500-40*pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON)); - if (sd->sc.data[SC_SPIRIT].timer != -1 && sd->sc.data[SC_SPIRIT].val2 == SL_BARDDANCER) - s_rate -= 40; //TODO: Figure out real bonus rate. - if (sd->sc.data[SC_LONGING].timer!=-1) - s_rate -= 20 * sd->sc.data[SC_LONGING].val1; - sd->speed += sd->speed * s_rate/100; - } - if(sd->sc.data[SC_FUSION].timer != -1) //Additional movement speed from SG_FUSION [Komurka] - sd->speed -= sd->speed * 25/100; - - // Apply relative modifiers from equipment - if(sd->speed_rate != 100) - sd->speed = sd->speed*sd->speed_rate/100; - - if(sd->speed < battle_config.max_walk_speed) - sd->speed = battle_config.max_walk_speed; - -// ----- ASPD CALCULATION ----- -// Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied - - // Basic ASPD value - if (sd->status.weapon < MAX_WEAPON_TYPE) - sd->aspd += aspd_base[sd->status.class_][sd->status.weapon]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->status.weapon]/1000; - else - sd->aspd += ( - (aspd_base[sd->status.class_][sd->weapontype1]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->weapontype1]/1000) + - (aspd_base[sd->status.class_][sd->weapontype2]-(sd->paramc[1]*4+sd->paramc[4])*aspd_base[sd->status.class_][sd->weapontype2]/1000) - ) *2/3; //From what I read in rodatazone, 2/3 should be more accurate than 0.7 -> 140 / 200; [Skotlex] - - // Relative modifiers from passive skills - if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK) - sd->aspd_rate -= (skill/2); - if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd)) - sd->aspd_rate -= (skill*3); - if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 && - (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) - sd->aspd_rate -= (skill/2); - if(pc_isriding(sd)) - sd->aspd_rate += 50-10*pc_checkskill(sd,KN_CAVALIERMASTERY); - - // Relative modifiers from status changes (shared between PC and NPC) - sd->aspd_rate = status_calc_aspd_rate(&sd->bl,sd->aspd_rate); - - // Apply all relative modifiers - if(sd->aspd_rate != 100) - sd->aspd = sd->aspd*sd->aspd_rate/100; - - if(sd->aspd < battle_config.max_aspd) sd->aspd = battle_config.max_aspd; - sd->amotion = sd->aspd; - -// ----- HP MAX AND REGEN CALCULATION ----- - - // Basic MaxHP value - // here we recycle variable index, and do this calc apart to avoid mixing up the 30% bonus with card bonuses. [Skotlex] - bl = sd->status.base_level; - index = (3500 + bl*hp_coefficient2[sd->status.class_] + - hp_sigma_val[sd->status.class_][(bl > 0)? bl-1:0])/100 * - (100 + sd->paramc[2])/100 + (sd->parame[2] - sd->paramcard[2]); - if (sd->class_&JOBL_UPPER) - index += index * 30/100; - else if (sd->class_&JOBL_BABY) - index -= index * 30/100; - if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->char_id, MAPID_TAEKWON)) - index *= 3; //Triple max HP for top ranking Taekwons over level 90. - - sd->status.max_hp += index; - - if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99) - sd->status.max_hp = sd->status.max_hp + 2000; - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,CR_TRUST))>0) - sd->status.max_hp += skill*200; - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->status.max_hp = status_calc_maxhp(&sd->bl,sd->status.max_hp); - - // Apply relative modifiers from equipment - if(sd->hprate!=100) - sd->status.max_hp = sd->status.max_hp * sd->hprate/100; - if(battle_config.hp_rate != 100) - sd->status.max_hp = sd->status.max_hp * battle_config.hp_rate/100; - - if (sd->status.max_hp < 0) //HP overflow?? - sd->status.max_hp = battle_config.max_hp; - else if(sd->status.max_hp > battle_config.max_hp) - sd->status.max_hp = battle_config.max_hp; - else if(sd->status.max_hp == 0) - sd->status.max_hp = 1; - - if(sd->status.hp>sd->status.max_hp) - sd->status.hp=sd->status.max_hp; - - // Basic natural HP regeneration value - sd->nhealhp = 1 + (sd->paramc[2]/5) + (sd->status.max_hp/200); - - // Apply relative modifiers from equipment - if(sd->hprecov_rate != 100) - sd->nhealhp = sd->nhealhp*sd->hprecov_rate/100; - - if(sd->nhealhp < 1) sd->nhealhp = 1; - if(sd->nhealhp > SHRT_MAX) sd->nhealhp = SHRT_MAX; - - // Skill-related HP recovery - if((skill=pc_checkskill(sd,SM_RECOVERY)) > 0) - sd->nshealhp = skill*5 + (sd->status.max_hp*skill/500); - // Skill-related HP recovery (only when sit) - if((skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0) - sd->nsshealhp = skill*4 + (sd->status.max_hp*skill/500); - if((skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest) - sd->nsshealhp = skill*30 + (sd->status.max_hp*skill/500); - - if(sd->nshealhp > SHRT_MAX) sd->nshealhp = SHRT_MAX; - if(sd->nsshealhp > SHRT_MAX) sd->nsshealhp = SHRT_MAX; - -// ----- SP MAX AND REGEN CALCULATION ----- - - // Basic MaxSP value - // here we recycle variable index, and do this calc apart to avoid mixing up the 30% bonus with card bonuses. [Skotlex] - index = ((sp_coefficient[sd->status.class_] * bl) + 1000)/100 * (100 + sd->paramc[3])/100 + (sd->parame[3] - sd->paramcard[3]); - if (sd->class_&JOBL_UPPER) - index += index * 30/100; - else if (sd->class_&JOBL_BABY) - index -= index * 30/100; - if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->char_id, MAPID_TAEKWON)) - index *= 3; //Triple max SP for top ranking Taekwons over level 90. - - sd->status.max_sp += index; - - // Absolute modifiers from passive skills - if((skill=pc_checkskill(sd,SL_KAINA))>0) - sd->status.max_sp += 30*skill; - if((skill=pc_checkskill(sd,HP_MEDITATIO))>0) - sd->status.max_sp += sd->status.max_sp * skill/100; - if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0) - sd->status.max_sp += sd->status.max_sp * 2*skill/100; - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - sd->status.max_sp = status_calc_maxsp(&sd->bl,sd->status.max_sp); - - // Apply relative modifiers from equipment - if(sd->sprate!=100) - sd->status.max_sp = sd->status.max_sp * sd->sprate/100; - if(battle_config.sp_rate != 100) - sd->status.max_sp = sd->status.max_sp * battle_config.sp_rate/100; - - if(sd->status.max_sp > battle_config.max_sp) - sd->status.max_sp = battle_config.max_sp; - else if(sd->status.max_sp <= 0) - sd->status.max_sp = 1; - if(sd->status.sp>sd->status.max_sp) - sd->status.sp=sd->status.max_sp; - - if(sd->sc.data[SC_DANCING].timer==-1){ - // Basic natural SP regeneration value - sd->nhealsp = 1 + (sd->paramc[3]/6) + (sd->status.max_sp/100); - if(sd->paramc[3] >= 120) - sd->nhealsp += ((sd->paramc[3]-120)>>1) + 4; - - // Relative modifiers from passive skills - if((skill=pc_checkskill(sd,HP_MEDITATIO)) > 0) - sd->nhealsp += sd->nhealsp * 3*skill/100; - - // Apply relative modifiers from equipment - if(sd->sprecov_rate != 100) - sd->nhealsp = sd->nhealsp*sd->sprecov_rate/100; - - if(sd->nhealsp < 1) sd->nhealsp = 1; - if(sd->nhealsp > SHRT_MAX) sd->nhealsp = SHRT_MAX; - - // Skill-related SP recovery - if((skill=pc_checkskill(sd,MG_SRECOVERY)) > 0) - sd->nshealsp = skill*3 + (sd->status.max_sp*skill/500); - if((skill=pc_checkskill(sd,NJ_NINPOU)) > 0) - sd->nshealsp = skill*3 + (sd->status.max_sp*skill/500); - // Skill-related SP recovery (only when sit) - if((skill = pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0) - sd->nsshealsp = skill*2 + (sd->status.max_sp*skill/500); - if((skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest) - { - sd->nsshealsp = skill*3 + (sd->status.max_sp*skill/500); - if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest - sd->nsshealsp += (30+10*skill)*sd->nsshealsp/100; - } - if(sd->nshealsp > SHRT_MAX) sd->nshealsp = SHRT_MAX; - if(sd->nsshealsp > SHRT_MAX) sd->nsshealsp = SHRT_MAX; - } - -// ----- MISC CALCULATIONS ----- - - //Even though people insist this is too slow, packet data reports this is the actual real equation. - sd->dmotion = 800-sd->paramc[1]*4; - if(sd->dmotion<400) sd->dmotion = 400; - - // Weight - if((skill=pc_checkskill(sd,MC_INCCARRY))>0) - sd->max_weight += 2000*skill; - if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0) - sd->max_weight += 10000; - if(sd->sc.data[SC_KNOWLEDGE].timer != -1) - sd->max_weight += sd->max_weight*sd->sc.data[SC_KNOWLEDGE].val1/10; - - // Skill SP cost - if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 ) - sd->dsprate -= 4*skill; - - if(sd->sc.count){ - if(sd->sc.data[SC_SERVICE4U].timer!=-1) - sd->dsprate -= sd->sc.data[SC_SERVICE4U].val3; - } - - if(sd->dsprate < 0) sd->dsprate = 0; - - // Anti-element and anti-race - if((skill=pc_checkskill(sd,CR_TRUST))>0) - sd->subele[6] += skill*5; - if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) { - sd->subele[0] += skill; - sd->subele[3] += skill*4; - } - if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){ - skill = skill*4; - sd->right_weapon.addrace[RC_DRAGON]+=skill; - sd->left_weapon.addrace[RC_DRAGON]+=skill; - sd->magic_addrace[RC_DRAGON]+=skill; - sd->subrace[RC_DRAGON]+=skill; - } - - if(sd->sc.count){ - if(sd->sc.data[SC_SIEGFRIED].timer!=-1){ - sd->subele[1] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[2] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[3] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[4] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[5] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[6] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[7] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[8] += sd->sc.data[SC_SIEGFRIED].val2; - sd->subele[9] += sd->sc.data[SC_SIEGFRIED].val2; - } - if(sd->sc.data[SC_PROVIDENCE].timer!=-1){ - sd->subele[6] += sd->sc.data[SC_PROVIDENCE].val2; - sd->subrace[RC_DEMON] += sd->sc.data[SC_PROVIDENCE].val2; - } - } - -// ----- CLIENT-SIDE REFRESH ----- - if(first&4) { - calculating = 0; - return 0; - } - if(first&3) { - clif_updatestatus(sd,SP_SPEED); - clif_updatestatus(sd,SP_MAXHP); - clif_updatestatus(sd,SP_MAXSP); - if(first&1) { - clif_updatestatus(sd,SP_HP); - clif_updatestatus(sd,SP_SP); - } - calculating = 0; - return 0; - } - - if(memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill))) - clif_skillinfoblock(sd); - if(b_speed != sd->speed) - clif_updatestatus(sd,SP_SPEED); - if(b_weight != sd->weight) - clif_updatestatus(sd,SP_WEIGHT); - if(b_max_weight != sd->max_weight) { - clif_updatestatus(sd,SP_MAXWEIGHT); - pc_checkweighticon(sd); - } - for(i=0;i<6;i++) - if(b_paramb[i] + b_parame[i] != sd->paramb[i] + sd->parame[i]) - clif_updatestatus(sd,SP_STR+i); - if(b_hit != sd->hit) - clif_updatestatus(sd,SP_HIT); - if(b_flee != sd->flee) - clif_updatestatus(sd,SP_FLEE1); - if(b_aspd != sd->aspd) - clif_updatestatus(sd,SP_ASPD); - if(b_watk != sd->right_weapon.watk + sd->left_weapon.watk || b_base_atk != sd->base_atk) - clif_updatestatus(sd,SP_ATK1); - if(b_def != sd->def) - clif_updatestatus(sd,SP_DEF1); - if(b_watk2 != sd->right_weapon.watk2 + sd->left_weapon.watk2) - clif_updatestatus(sd,SP_ATK2); - if(b_def2 != sd->def2) - clif_updatestatus(sd,SP_DEF2); - if(b_flee2 != sd->flee2) - clif_updatestatus(sd,SP_FLEE2); - if(b_critical != sd->critical) - clif_updatestatus(sd,SP_CRITICAL); - if(b_matk1 != sd->matk1) - clif_updatestatus(sd,SP_MATK1); - if(b_matk2 != sd->matk2) - clif_updatestatus(sd,SP_MATK2); - if(b_mdef != sd->mdef) - clif_updatestatus(sd,SP_MDEF1); - if(b_mdef2 != sd->mdef2) - clif_updatestatus(sd,SP_MDEF2); - if(b_attackrange != sd->attackrange) - clif_updatestatus(sd,SP_ATTACKRANGE); - if(b_max_hp != sd->status.max_hp) - clif_updatestatus(sd,SP_MAXHP); - if(b_max_sp != sd->status.max_sp) - clif_updatestatus(sd,SP_MAXSP); - if(b_hp != sd->status.hp) - clif_updatestatus(sd,SP_HP); - if(b_sp != sd->status.sp) - clif_updatestatus(sd,SP_SP); - - calculating = 0; - return 0; -} - -/*========================================== - * Apply shared stat mods from status changes [DracoRPG] - *------------------------------------------ - */ -int status_calc_str(struct block_list *bl, int str) -{ - struct status_change *sc; - nullpo_retr(str,bl); - sc= status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCALLSTATUS].timer!=-1) - str += sc->data[SC_INCALLSTATUS].val1; - if(sc->data[SC_INCSTR].timer!=-1) - str += sc->data[SC_INCSTR].val1; - if(sc->data[SC_STRFOOD].timer!=-1) - str += sc->data[SC_STRFOOD].val1; - if(sc->data[SC_LOUD].timer!=-1) - str += 4; - if(sc->data[SC_TRUESIGHT].timer!=-1) - str += 5; - if(sc->data[SC_SPURT].timer!=-1) - str += 10; //Bonus is +!0 regardless of skill level - if(sc->data[SC_BLESSING].timer != -1){ - if(sc->data[SC_BLESSING].val2) - str += sc->data[SC_BLESSING].val2; - else - str >>= 1; - } - if(sc->data[SC_NEN].timer!=-1) - str += sc->data[SC_NEN].val1; - } - - return str; -} - -int status_calc_agi(struct block_list *bl, int agi) -{ - struct status_change *sc; - nullpo_retr(agi,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCALLSTATUS].timer!=-1) - agi += sc->data[SC_INCALLSTATUS].val1; - if(sc->data[SC_INCAGI].timer!=-1) - agi += sc->data[SC_INCAGI].val1; - if(sc->data[SC_AGIFOOD].timer!=-1) - agi += sc->data[SC_AGIFOOD].val1; - if(sc->data[SC_TRUESIGHT].timer!=-1) - agi += 5; - if(sc->data[SC_INCREASEAGI].timer!=-1) - agi += 2 + sc->data[SC_INCREASEAGI].val1; - if(sc->data[SC_DECREASEAGI].timer!=-1) - agi -= 2 + sc->data[SC_DECREASEAGI].val1; - if(sc->data[SC_QUAGMIRE].timer!=-1) - agi -= sc->data[SC_QUAGMIRE].val1*(bl->type==BL_PC?5:10); - if(sc->data[SC_SUITON].timer!=-1) - agi -= sc->data[SC_SUITON].val2; - if(sc->data[SC_INCREASING].timer!=-1) - agi += 4; // added based on skill updates [Reddozen] - } - - return agi; -} - -int status_calc_vit(struct block_list *bl, int vit) -{ - struct status_change *sc; - nullpo_retr(vit,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCALLSTATUS].timer!=-1) - vit += sc->data[SC_INCALLSTATUS].val1; - if(sc->data[SC_INCVIT].timer!=-1) - vit += sc->data[SC_INCVIT].val1; - if(sc->data[SC_VITFOOD].timer!=-1) - vit += sc->data[SC_VITFOOD].val1; - if(sc->data[SC_TRUESIGHT].timer!=-1) - vit += 5; - if(sc->data[SC_STRIPARMOR].timer!=-1 && bl->type != BL_PC) - vit -= vit * 8*sc->data[SC_STRIPARMOR].val1/100; - } - - return vit; -} - -int status_calc_int(struct block_list *bl, int int_) -{ - struct status_change *sc; - nullpo_retr(int_,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCALLSTATUS].timer!=-1) - int_ += sc->data[SC_INCALLSTATUS].val1; - if(sc->data[SC_INCINT].timer!=-1) - int_ += sc->data[SC_INCINT].val1; - if(sc->data[SC_INTFOOD].timer!=-1) - int_ += sc->data[SC_INTFOOD].val1; - if(sc->data[SC_TRUESIGHT].timer!=-1) - int_ += 5; - if(sc->data[SC_BLESSING].timer != -1){ - if (sc->data[SC_BLESSING].val2) - int_ += sc->data[SC_BLESSING].val2; - else - int_ >>= 1; - } - if(sc->data[SC_STRIPHELM].timer!=-1 && bl->type != BL_PC) - int_ -= int_ * 8*sc->data[SC_STRIPHELM].val1/100; - if(sc->data[SC_NEN].timer!=-1) - int_ += sc->data[SC_NEN].val1; - } - - return int_; -} - -int status_calc_dex(struct block_list *bl, int dex) -{ - struct status_change *sc; - nullpo_retr(dex,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCALLSTATUS].timer!=-1) - dex += sc->data[SC_INCALLSTATUS].val1; - if(sc->data[SC_INCDEX].timer!=-1) - dex += sc->data[SC_INCDEX].val1; - if(sc->data[SC_DEXFOOD].timer!=-1) - dex += sc->data[SC_DEXFOOD].val1; - if(sc->data[SC_TRUESIGHT].timer!=-1) - dex += 5; - if(sc->data[SC_QUAGMIRE].timer!=-1) - dex -= sc->data[SC_QUAGMIRE].val1*(bl->type==BL_PC?5:10); - if(sc->data[SC_BLESSING].timer != -1){ - if (sc->data[SC_BLESSING].val2) - dex += sc->data[SC_BLESSING].val2; - else - dex >>= 1; - } - if(sc->data[SC_INCREASING].timer!=-1) - dex += 4; // added based on skill updates [Reddozen] - } - - return dex; -} - -int status_calc_luk(struct block_list *bl, int luk) -{ - struct status_change *sc; - nullpo_retr(luk,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCALLSTATUS].timer!=-1) - luk += sc->data[SC_INCALLSTATUS].val1; - if(sc->data[SC_INCLUK].timer!=-1) - luk += sc->data[SC_INCLUK].val1; - if(sc->data[SC_LUKFOOD].timer!=-1) - luk += sc->data[SC_LUKFOOD].val1; - if(sc->data[SC_TRUESIGHT].timer!=-1) - luk += 5; - if(sc->data[SC_GLORIA].timer!=-1) - luk += 30; - } - - return luk; -} - -int status_calc_batk(struct block_list *bl, int batk) -{ - struct status_change *sc; - nullpo_retr(batk,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_ATKPOTION].timer!=-1) - batk += sc->data[SC_ATKPOTION].val1; - if(sc->data[SC_BATKFOOD].timer!=-1) - batk += sc->data[SC_BATKFOOD].val1; - if(sc->data[SC_INCATKRATE].timer!=-1) - batk += batk * sc->data[SC_INCATKRATE].val1/100; - if(sc->data[SC_PROVOKE].timer!=-1) - batk += batk * (2+3*sc->data[SC_PROVOKE].val1)/100; - if(sc->data[SC_CONCENTRATION].timer!=-1) - batk += batk * 5*sc->data[SC_CONCENTRATION].val1/100; - if(sc->data[SC_SKE].timer!=-1) - batk += batk * 3; - if(sc->data[SC_JOINTBEAT].timer!=-1 && sc->data[SC_JOINTBEAT].val2==4) - batk -= batk * 25/100; - if(sc->data[SC_CURSE].timer!=-1) - batk -= batk * 25/100; -//Curse shouldn't effect on this? -// if(sc->data[SC_BLEEDING].timer != -1) -// batk -= batk * 25/100; - if(sc->data[SC_MADNESSCANCEL].timer!=-1) - batk += 100; - } - - return batk; -} - -int status_calc_watk(struct block_list *bl, int watk) -{ - struct status_change *sc; - nullpo_retr(watk,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_IMPOSITIO].timer!=-1) - watk += 5*sc->data[SC_IMPOSITIO].val1; - if(sc->data[SC_WATKFOOD].timer!=-1) - watk += sc->data[SC_WATKFOOD].val1; - if(sc->data[SC_DRUMBATTLE].timer!=-1) - watk += sc->data[SC_DRUMBATTLE].val2; - if(sc->data[SC_VOLCANO].timer!=-1 && status_get_elem_type(bl)==3) - watk += sc->data[SC_VOLCANO].val3; - if(sc->data[SC_INCATKRATE].timer!=-1) - watk += watk * sc->data[SC_INCATKRATE].val1/100; - if(sc->data[SC_PROVOKE].timer!=-1) - watk += watk * (2+3*sc->data[SC_PROVOKE].val1)/100; - if(sc->data[SC_CONCENTRATION].timer!=-1) - watk += watk * 5*sc->data[SC_CONCENTRATION].val1/100; - if(sc->data[SC_SKE].timer!=-1) - watk += watk * 3; - if(sc->data[SC_NIBELUNGEN].timer!=-1 && bl->type != BL_PC && (status_get_element(bl)/10)>=8) - watk += sc->data[SC_NIBELUNGEN].val2; - if(sc->data[SC_CURSE].timer!=-1) - watk -= watk * 25/100; - if(sc->data[SC_STRIPWEAPON].timer!=-1 && bl->type != BL_PC) - watk -= watk * 5*sc->data[SC_STRIPWEAPON].val1/100; - } - return watk; -} - -int status_calc_matk(struct block_list *bl, int matk) -{ - struct status_change *sc; - nullpo_retr(matk,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_MATKPOTION].timer!=-1) - matk += sc->data[SC_MATKPOTION].val1; - if(sc->data[SC_MATKFOOD].timer!=-1) - matk += sc->data[SC_MATKFOOD].val1; - if(sc->data[SC_MAGICPOWER].timer!=-1) - matk += matk * 5*sc->data[SC_MAGICPOWER].val1/100; - if(sc->data[SC_MINDBREAKER].timer!=-1) - matk += matk * 20*sc->data[SC_MINDBREAKER].val1/100; - if(sc->data[SC_INCMATKRATE].timer!=-1) - matk += matk * sc->data[SC_INCMATKRATE].val1/100; - } - - return matk; -} - -int status_calc_critical(struct block_list *bl, int critical) -{ - struct status_change *sc; - nullpo_retr(critical,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if (sc->data[SC_EXPLOSIONSPIRITS].timer!=-1) - critical += sc->data[SC_EXPLOSIONSPIRITS].val2; - if (sc->data[SC_FORTUNE].timer!=-1) - critical += sc->data[SC_FORTUNE].val2*10; - if (sc->data[SC_TRUESIGHT].timer!=-1) - critical += sc->data[SC_TRUESIGHT].val1*10; - if(sc->data[SC_CLOAKING].timer!=-1) - critical += critical; - } - - return critical; -} - -int status_calc_hit(struct block_list *bl, int hit) -{ - struct status_change *sc; - nullpo_retr(hit,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCHIT].timer != -1) - hit += sc->data[SC_INCHIT].val1; - if(sc->data[SC_HITFOOD].timer!=-1) - hit += sc->data[SC_HITFOOD].val1; - if(sc->data[SC_TRUESIGHT].timer != -1) - hit += 3*sc->data[SC_TRUESIGHT].val1; - if(sc->data[SC_HUMMING].timer!=-1) - hit += sc->data[SC_HUMMING].val2; - if(sc->data[SC_CONCENTRATION].timer != -1) - hit += 10*sc->data[SC_CONCENTRATION].val1; - if(sc->data[SC_INCHITRATE].timer != -1) - hit += hit * sc->data[SC_INCHITRATE].val1/100; - if(sc->data[SC_BLIND].timer != -1) - hit -= hit * 25 / 100; - if(sc->data[SC_ADJUSTMENT].timer!=-1) - hit += 30; - if(sc->data[SC_INCREASING].timer!=-1) - hit += 20; // RockmanEXE; changed based on updated [Reddozen] - } - - return hit; -} - -int status_calc_flee(struct block_list *bl, int flee) -{ - struct status_change *sc; - nullpo_retr(flee,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_INCFLEE].timer!=-1) - flee += sc->data[SC_INCFLEE].val1; - if(sc->data[SC_FLEEFOOD].timer!=-1) - flee += sc->data[SC_FLEEFOOD].val1; - if(sc->data[SC_WHISTLE].timer!=-1) - flee += sc->data[SC_WHISTLE].val2; - if(sc->data[SC_WINDWALK].timer!=-1) - flee += sc->data[SC_WINDWALK].val2; - if(sc->data[SC_INCFLEERATE].timer!=-1) - flee += flee * sc->data[SC_INCFLEERATE].val1/100; - if(sc->data[SC_VIOLENTGALE].timer!=-1 && status_get_elem_type(bl)==4) - flee += flee * sc->data[SC_VIOLENTGALE].val3/100; - if(sc->data[SC_MOON_COMFORT].timer!=-1) //SG skill [Komurka] - flee += (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/10; - if(sc->data[SC_CLOSECONFINE].timer!=-1) - flee += 10; - if(sc->data[SC_SPIDERWEB].timer!=-1) - flee -= flee * 50/100; - if(sc->data[SC_BERSERK].timer!=-1) - flee -= flee * 50/100; - if(sc->data[SC_BLIND].timer!=-1) - flee -= flee * 25/100; - if(sc->data[SC_ADJUSTMENT].timer!=-1) - flee += 30; - if(sc->data[SC_GATLINGFEVER].timer!=-1) - flee -= sc->data[SC_GATLINGFEVER].val1*5; - } - - if (bl->type == BL_PC && map_flag_gvg(bl->m)) //GVG grounds flee penalty, placed here because it's "like" a status change. [Skotlex] - flee -= flee * battle_config.gvg_flee_penalty/100; - return flee; -} - -int status_calc_flee2(struct block_list *bl, int flee2) -{ - struct status_change *sc; - nullpo_retr(flee2,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_WHISTLE].timer!=-1) - flee2 += sc->data[SC_WHISTLE].val3*10; - } - - return flee2; -} - -int status_calc_def(struct block_list *bl, int def) -{ - struct status_change *sc; - nullpo_retr(def,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_BERSERK].timer!=-1) - return 0; - if(sc->data[SC_KEEPING].timer!=-1) - return 100; - if(sc->data[SC_SKA].timer != -1) - return rand()%100; //Reports indicate SKA actually randomizes defense. - if(sc->data[SC_STEELBODY].timer!=-1) - return 90; - if(sc->data[SC_DRUMBATTLE].timer!=-1) - def += sc->data[SC_DRUMBATTLE].val3; - if(sc->data[SC_INCDEFRATE].timer!=-1) - def += def * sc->data[SC_INCDEFRATE].val1/100; - if(sc->data[SC_SIGNUMCRUCIS].timer!=-1) - def -= def * sc->data[SC_SIGNUMCRUCIS].val2/100; - if(sc->data[SC_CONCENTRATION].timer!=-1) - def -= def * 5*sc->data[SC_CONCENTRATION].val1/100; - if(sc->data[SC_SKE].timer!=-1) - def -= def * 50/100; - if(sc->data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) // Provoke doesn't alter player defense. - def -= def * (5+5*sc->data[SC_PROVOKE].val1)/100; - if(sc->data[SC_STRIPSHIELD].timer!=-1 && bl->type != BL_PC) - def -= def * 3*sc->data[SC_STRIPSHIELD].val1/100; - //if (sd->data[SC_FLING].timer!=-1 && bl->type != BL_PC) - // def -= (def * sd->data[SC_FLING].val1) / 100; - } - - return def; -} - -int status_calc_def2(struct block_list *bl, int def2) -{ - struct status_change *sc; - nullpo_retr(def2,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_BERSERK].timer!=-1) - return 0; - if(sc->data[SC_ETERNALCHAOS].timer!=-1) - return 0; - if(sc->data[SC_SUN_COMFORT].timer!=-1) - def2 += (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/2; - if(sc->data[SC_ANGELUS].timer!=-1) - def2 += def2 * (5*sc->data[SC_ANGELUS].val1)/100; - if(sc->data[SC_CONCENTRATION].timer!=-1) - def2 -= def2 * 5*sc->data[SC_CONCENTRATION].val1/100; - if(sc->data[SC_POISON].timer!=-1) - def2 -= def2 * 25/100; - if(sc->data[SC_SKE].timer!=-1) - def2 -= def2 * 50/100; - if(sc->data[SC_PROVOKE].timer!=-1) - def2 -= def2 * (5+5*sc->data[SC_PROVOKE].val1)/100; - if(sc->data[SC_JOINTBEAT].timer!=-1){ - if(sc->data[SC_JOINTBEAT].val2==3) - def2 -= def2 * 50/100; - else if(sc->data[SC_JOINTBEAT].val2==4) - def2 -= def2 * 25/100; - } - //if (sd->data[SC_FLING].timer!=-1) - // def2 -= (def2 * sd->data[SC_FLING].val1) / 100; - } - - return def2; -} - -int status_calc_mdef(struct block_list *bl, int mdef) -{ - struct status_change *sc; - nullpo_retr(mdef,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_BERSERK].timer!=-1) - return 0; - if(sc->data[SC_BARRIER].timer!=-1) - return 100; - if(sc->data[SC_STEELBODY].timer!=-1) - return 90; - if(sc->data[SC_SKA].timer != -1) // [marquis007] - return 90; // should it up mdef too? - if(sc->data[SC_ENDURE].timer!=-1 && sc->data[SC_ENDURE].val4 == 0) - mdef += sc->data[SC_ENDURE].val1; - } - - return mdef; -} - -int status_calc_mdef2(struct block_list *bl, int mdef2) -{ - struct status_change *sc; - nullpo_retr(mdef2,bl); - sc = status_get_sc(bl); - - if(sc && sc->count){ - if(sc->data[SC_BERSERK].timer!=-1) - return 0; - if(sc->data[SC_MINDBREAKER].timer!=-1) - mdef2 -= mdef2 * 12*sc->data[SC_MINDBREAKER].val1/100; - } - - return mdef2; -} - -int status_calc_speed(struct block_list *bl, int speed) -{ - struct status_change *sc; - sc = status_get_sc(bl); - - if(sc && sc->count) { - if(sc->data[SC_CURSE].timer!=-1) - speed += 450; - if(sc->data[SC_SWOO].timer != -1) // [marquis007] - speed += 450; //Let's use Curse's slow down momentarily (exact value unknown) - if(sc->data[SC_WEDDING].timer!=-1) - speed += speed; - if(sc->data[SC_SPEEDUP1].timer!=-1) - speed -= speed*50/100; - else if(sc->data[SC_SPEEDUP0].timer!=-1) - speed -= speed*25/100; - else if(sc->data[SC_INCREASEAGI].timer!=-1) - speed -= speed * 25/100; - else if(sc->data[SC_CARTBOOST].timer!=-1) - speed -= speed * 20/100; - else if(sc->data[SC_BERSERK].timer!=-1) - speed -= speed * 20/100; - else if(sc->data[SC_WINDWALK].timer!=-1) - speed -= speed * 4*sc->data[SC_WINDWALK].val2/100; - if(sc->data[SC_SLOWDOWN].timer!=-1) - speed += speed * 50/100; - if(sc->data[SC_DECREASEAGI].timer!=-1) - speed += speed * 25/100; - if(sc->data[SC_STEELBODY].timer!=-1) - speed += speed * 25/100; - if(sc->data[SC_SKA].timer!=-1) - speed += speed * 25/100; - if(sc->data[SC_QUAGMIRE].timer!=-1) - speed += speed * 50/100; - if(sc->data[SC_DONTFORGETME].timer!=-1) - speed += speed * sc->data[SC_DONTFORGETME].val3/100; - if(sc->data[SC_DEFENDER].timer!=-1) - speed += speed * (35-5*sc->data[SC_DEFENDER].val1)/100; - if(sc->data[SC_GOSPEL].timer!=-1 && sc->data[SC_GOSPEL].val4 == BCT_ENEMY) - speed += speed * 25/100; - if(sc->data[SC_JOINTBEAT].timer!=-1) { - if (sc->data[SC_JOINTBEAT].val2 == 0) - speed += speed * 50/100; - else if (sc->data[SC_JOINTBEAT].val2 == 2) - speed += speed * 30/100; - } - if(sc->data[SC_CLOAKING].timer!=-1) - speed = speed * (sc->data[SC_CLOAKING].val3-3*sc->data[SC_CLOAKING].val1) /100; - if(sc->data[SC_CHASEWALK].timer!=-1) - speed = speed * sc->data[SC_CHASEWALK].val3/100; - if(sc->data[SC_RUN].timer!=-1)/*駆け足による速度変化*/ - speed -= speed * 25/100; - if(sc->data[SC_GATLINGFEVER].timer!=-1) - speed += speed * 25/100; - } - - return speed; -} - -int status_calc_aspd_rate(struct block_list *bl, int aspd_rate) -{ - struct status_change *sc; - sc = status_get_sc(bl); - - if(sc && sc->count) { - int i; - if(sc->data[SC_QUAGMIRE].timer==-1 && sc->data[SC_DONTFORGETME].timer==-1){ - if(sc->data[SC_TWOHANDQUICKEN].timer!=-1) - aspd_rate -= 30; - else if(sc->data[SC_ONEHAND].timer!=-1) - aspd_rate -= 30; - else if(sc->data[SC_ADRENALINE2].timer!=-1) - aspd_rate -= (sc->data[SC_ADRENALINE2].val2 || !battle_config.party_skill_penalty)?30:20; - else if(sc->data[SC_ADRENALINE].timer!=-1) - aspd_rate -= (sc->data[SC_ADRENALINE].val2 || !battle_config.party_skill_penalty)?30:20; - else if(sc->data[SC_SPEARQUICKEN].timer!=-1) - aspd_rate -= sc->data[SC_SPEARQUICKEN].val2; - else if(sc->data[SC_ASSNCROS].timer!=-1 && (bl->type!=BL_PC || ((struct map_session_data*)bl)->status.weapon != W_BOW)) - aspd_rate -= sc->data[SC_ASSNCROS].val2; - } - if(sc->data[SC_BERSERK].timer!=-1) - aspd_rate -= 30; - if(sc->data[i=SC_ASPDPOTION3].timer!=-1 || sc->data[i=SC_ASPDPOTION2].timer!=-1 || sc->data[i=SC_ASPDPOTION1].timer!=-1 || sc->data[i=SC_ASPDPOTION0].timer!=-1) - aspd_rate -= sc->data[i].val2; - if(sc->data[SC_DONTFORGETME].timer!=-1) - aspd_rate += sc->data[SC_DONTFORGETME].val2; - if(sc->data[SC_STEELBODY].timer!=-1) - aspd_rate += 25; - if(sc->data[SC_SKA].timer!=-1) - aspd_rate += 25; - if(sc->data[SC_DEFENDER].timer != -1) - aspd_rate += 25 -sc->data[SC_DEFENDER].val1*5; - if(sc->data[SC_GOSPEL].timer!=-1 && sc->data[SC_GOSPEL].val4 == BCT_ENEMY) - aspd_rate += 25; - if(sc->data[SC_GRAVITATION].timer!=-1) - aspd_rate += sc->data[SC_GRAVITATION].val2; -//Curse shouldn't effect on this? -// if(sc->data[SC_BLEEDING].timer != -1) -// aspd_rate += 25; - if(sc->data[SC_JOINTBEAT].timer!=-1) { - if (sc->data[SC_JOINTBEAT].val2 == 1) - aspd_rate += 25; - else if (sc->data[SC_JOINTBEAT].val2 == 2) - aspd_rate += 10; - } - if(sc->data[SC_STAR_COMFORT].timer!=-1) - aspd_rate -= (status_get_lv(bl) + status_get_dex(bl) + status_get_luk(bl))/10; - if(sc->data[SC_MADNESSCANCEL].timer!=-1) - aspd_rate -= 20; - if(sc->data[SC_GATLINGFEVER].timer!=-1) - aspd_rate -= sc->data[SC_GATLINGFEVER].val1*2; - } - - return aspd_rate; -} - -int status_calc_maxhp(struct block_list *bl, int maxhp) -{ - struct status_change *sc; - sc = status_get_sc(bl); - - if(sc && sc->count) { - if(sc->data[SC_INCMHPRATE].timer!=-1) - maxhp += maxhp * sc->data[SC_INCMHPRATE].val1/100; - if(sc->data[SC_APPLEIDUN].timer!=-1) - maxhp += maxhp * sc->data[SC_APPLEIDUN].val2/100; - if(sc->data[SC_DELUGE].timer!=-1 && status_get_elem_type(bl)==1) - maxhp += maxhp * deluge_eff[sc->data[SC_DELUGE].val1-1]/100; - if(sc->data[SC_BERSERK].timer!=-1) - maxhp += maxhp * 2; - } - - return maxhp; -} - -int status_calc_maxsp(struct block_list *bl, int maxsp) -{ - struct status_change *sc; - sc = status_get_sc(bl); - - if(sc && sc->count) { - if(sc->data[SC_INCMSPRATE].timer!=-1) - maxsp += maxsp * sc->data[SC_INCMSPRATE].val1/100; - if(sc->data[SC_SERVICE4U].timer!=-1) - maxsp += maxsp * sc->data[SC_SERVICE4U].val2/100; - } - - return maxsp; -} - -/*========================================== - * For quick calculating [Celest] Adapted by [Skotlex] - *------------------------------------------ - */ -int status_quick_recalc_speed(struct map_session_data *sd, int skill_num, int skill_lv, char start) -{ - /* [Skotlex] - This function individually changes a character's speed upon a skill change and restores it upon it's ending. - Should only be used on non-inclusive skills to avoid exploits. - Currently used for freedom of cast - and when cloaking changes it's val3 (in which case the new val3 value comes in the level. - */ - - int b_speed; - - b_speed = sd->speed; - - switch (skill_num) - { - case SA_FREECAST: - if (start) - { - sd->prev_speed = sd->speed; - sd->speed = sd->speed*(175 - skill_lv*5)/100; - } - else - sd->speed = sd->prev_speed; - break; - case AS_CLOAKING: - if (start && sd->sc.data[SC_CLOAKING].timer != -1) - { //There shouldn't be an "stop" case here. - //If the previous upgrade was - //SPEED_ADD_RATE(3*sd->sc.data[SC_CLOAKING].val1 -sd->sc.data[SC_CLOAKING].val3); - //Then just changing val3 should be a net difference of.... - if (3*sd->sc.data[SC_CLOAKING].val1 != sd->sc.data[SC_CLOAKING].val3) //This reverts the previous value. - sd->speed = sd->speed * 100 /(sd->sc.data[SC_CLOAKING].val3-3*sd->sc.data[SC_CLOAKING].val1); - sd->sc.data[SC_CLOAKING].val3 = skill_lv; - sd->speed = sd->speed * (sd->sc.data[SC_CLOAKING].val3-sd->sc.data[SC_CLOAKING].val1*3) /100; - } - break; - } - - if(sd->speed < battle_config.max_walk_speed) - sd->speed = battle_config.max_walk_speed; - - if(b_speed != sd->speed) - clif_updatestatus(sd,SP_SPEED); - - return 0; -} - -/*========================================== - * 対象のClassを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_class(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_MOB) //Class used on all code should be the view class of the mob. - return ((struct mob_data *)bl)->vd->class_; - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->status.class_; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->class_; - return 0; -} -/*========================================== - * 対象のレベルを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_lv(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_MOB) - return ((struct mob_data *)bl)->level; - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->status.base_level; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->msd->pet.level; - return 0; -} - -/*========================================== - * 対象の射程を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_range(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_MOB) - return ((struct mob_data *)bl)->db->range; - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->attackrange; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->range; - if(bl->type==BL_HOMUNCULUS) - return 1; //[blackhole89] - return 0; -} -/*========================================== - * 対象のHPを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_hp(struct block_list *bl) -{ - nullpo_retr(1, bl); - if(bl->type==BL_MOB) - return ((struct mob_data *)bl)->hp; - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->status.hp; - if(bl->type==BL_HOMUNCULUS) - return ((struct homun_data *)bl)->hp; - return 1; -} -/*========================================== - * 対象のMHPを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_max_hp(struct block_list *bl) -{ - nullpo_retr(1, bl); - - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->status.max_hp; - else if(bl->type==BL_HOMUNCULUS) - return ((struct homun_data *)bl)->max_hp; //[blackhole89] - else { - int max_hp = 1; - - if(bl->type == BL_MOB) { - struct mob_data *md; - nullpo_retr(1, md = (struct mob_data *)bl); - max_hp = md->max_hp; - - if(battle_config.mobs_level_up) // mobs leveling up increase [Valaris] - max_hp += (md->level - md->db->lv) * status_get_vit(bl); - - } - else if(bl->type == BL_PET) { - struct pet_data *pd; - nullpo_retr(1, pd = (struct pet_data*)bl); - max_hp = pd->db->max_hp; - } - - max_hp = status_calc_maxhp(bl,max_hp); - if(max_hp < 1) max_hp = 1; - return max_hp; - } -} -/*========================================== - * 対象のStrを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_str(struct block_list *bl) -{ - int str = 0; - nullpo_retr(0, bl); - - if (bl->type == BL_PC) - return ((struct map_session_data *)bl)->paramc[0]; - else { - if(bl->type == BL_MOB) { - str = ((struct mob_data *)bl)->db->str; - if(battle_config.mobs_level_up) // mobs leveling up increase [Valaris] - str += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv; - if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris] - str/=2; - else if(((struct mob_data*)bl)->special_state.size==2) - str*=2; - } else if(bl->type == BL_PET){ // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - str = ((struct pet_data *)bl)->status->str; - else - str = ((struct pet_data *)bl)->db->str; - } else if(bl->type == BL_HOMUNCULUS) { //[blackhole89] - str = ((struct homun_data *)bl)->str; - } - - str = status_calc_str(bl,str); - } - if(str < 0) str = 0; - return str; -} -/*========================================== - * 対象のAgiを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ - -int status_get_agi(struct block_list *bl) -{ - int agi=0; - nullpo_retr(0, bl); - - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->paramc[1]; - else { - if(bl->type == BL_MOB) { - agi = ((struct mob_data *)bl)->db->agi; - if(battle_config.mobs_level_up) // increase of mobs leveling up [Valaris] - agi += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv; - if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris] - agi/=2; - else if(((struct mob_data*)bl)->special_state.size==2) - agi*=2; - } else if(bl->type == BL_PET) { // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - agi = ((struct pet_data *)bl)->status->agi; - else - agi = ((struct pet_data *)bl)->db->agi; - } else if(bl->type == BL_HOMUNCULUS) { //[blackhole89] - agi = ((struct homun_data *)bl)->agi; - } - - agi = status_calc_agi(bl,agi); - } - if(agi < 0) agi = 0; - return agi; -} -/*========================================== - * 対象のVitを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_vit(struct block_list *bl) -{ - int vit = 0; - nullpo_retr(0, bl); - - if(bl->type == BL_PC) - return ((struct map_session_data *)bl)->paramc[2]; - else { - if(bl->type == BL_MOB) { - vit = ((struct mob_data *)bl)->db->vit; - if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris] - vit += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv; - if(((struct mob_data*)bl)->special_state.size==1) // change for sizes monsters [Valaris] - vit/=2; - else if(((struct mob_data*)bl)->special_state.size==2) - vit*=2; - } else if(bl->type == BL_PET) { // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - vit = ((struct pet_data *)bl)->status->vit; - else - vit = ((struct pet_data *)bl)->db->vit; - } else if(bl->type == BL_HOMUNCULUS) { //[blackhole89] - vit = ((struct homun_data *)bl)->vit; - } - - vit = status_calc_vit(bl,vit); - } - if(vit < 0) vit = 0; - return vit; -} -/*========================================== - * 対象のIntを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_int(struct block_list *bl) -{ - int int_=0; - nullpo_retr(0, bl); - - if(bl->type == BL_PC) - return ((struct map_session_data *)bl)->paramc[3]; - else { - if(bl->type == BL_MOB) { - int_ = ((struct mob_data *)bl)->db->int_; - if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris] - int_ += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv; - if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris] - int_/=2; - else if(((struct mob_data*)bl)->special_state.size==2) - int_*=2; - } else if(bl->type == BL_PET) { // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - int_ = ((struct pet_data *)bl)->status->int_; - else - int_ = ((struct pet_data *)bl)->db->int_; - } else if(bl->type == BL_HOMUNCULUS) { //[blackhole89] - int_ = ((struct homun_data *)bl)->int_; - } - - int_ = status_calc_int(bl,int_); - } - if(int_ < 0) int_ = 0; - return int_; -} -/*========================================== - * 対象のDexを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_dex(struct block_list *bl) -{ - int dex = 0; - nullpo_retr(0, bl); - - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->paramc[4]; - else { - if(bl->type == BL_MOB) { - dex = ((struct mob_data *)bl)->db->dex; - if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris] - dex += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv; - if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris] - dex/=2; - else if(((struct mob_data*)bl)->special_state.size==2) - dex*=2; - } else if(bl->type == BL_PET) { // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - dex = ((struct pet_data *)bl)->status->dex; - else - dex = ((struct pet_data *)bl)->db->dex; - } else if(bl->type == BL_HOMUNCULUS) { //[blackhole89] - dex = ((struct homun_data *)bl)->dex; - } - - dex = status_calc_dex(bl,dex); - } - if(dex < 0) dex = 0; - return dex; -} -/*========================================== - * 対象のLukを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_luk(struct block_list *bl) -{ - int luk = 0; - nullpo_retr(0, bl); - - if(bl->type == BL_PC) - return ((struct map_session_data *)bl)->paramc[5]; - else { - if(bl->type == BL_MOB) { - luk = ((struct mob_data *)bl)->db->luk; - if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris] - luk += ((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv; - if(((struct mob_data*)bl)->special_state.size==1) // change for sized monsters [Valaris] - luk/=2; - else if(((struct mob_data*)bl)->special_state.size==2) - luk*=2; - } else if(bl->type == BL_PET) { // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - luk = ((struct pet_data *)bl)->status->luk; - else - luk = ((struct pet_data *)bl)->db->luk; - } else if(bl->type == BL_HOMUNCULUS) { //[blackhole89] - luk = ((struct homun_data *)bl)->luk; - } - - luk = status_calc_luk(bl,luk); - } - if(luk < 0) luk = 0; - return luk; -} - -/*========================================== - * 対象のFleeを返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int status_get_flee(struct block_list *bl) -{ - int flee = 1; - nullpo_retr(1, bl); - - if(bl->type == BL_PC) - return ((struct map_session_data *)bl)->flee; - - flee = status_calc_flee(bl,status_get_agi(bl)+status_get_lv(bl)); - if(flee < 1) flee = 1; - return flee; -} -/*========================================== - * 対象のHitを返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int status_get_hit(struct block_list *bl) -{ - int hit = 1; - nullpo_retr(1, bl); - if (bl->type == BL_PC) - return ((struct map_session_data *)bl)->hit; - - hit = status_calc_hit(bl,status_get_dex(bl)+status_get_lv(bl)); - if(hit < 1) hit = 1; - return hit; -} -/*========================================== - * 対象の完全回避を返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int status_get_flee2(struct block_list *bl) -{ - int flee2 = 1; - nullpo_retr(1, bl); - - if (bl->type == BL_PC) - return ((struct map_session_data *)bl)->flee2; - - flee2 = status_calc_flee2(bl,status_get_luk(bl)+10); - if (flee2 < 1) flee2 = 1; - return flee2; -} -/*========================================== - * 対象のクリティカルを返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int status_get_critical(struct block_list *bl) -{ - int critical = 1; - nullpo_retr(1, bl); - - if (bl->type == BL_PC) - return ((struct map_session_data *)bl)->critical; - - critical = status_get_luk(bl)*3+10; - if(battle_config.enemy_critical_rate != 100) - critical = critical*battle_config.enemy_critical_rate/100; - critical = status_calc_critical(bl,critical); - if (critical < 1) critical = 1; - return critical; -} -/*========================================== - * base_atkの取得 - * 戻りは整数で1以上 - *------------------------------------------ - */ -int status_get_batk(struct block_list *bl) -{ - int batk = 1; - nullpo_retr(1, bl); - - if(bl->type==BL_PC) { - batk = ((struct map_session_data *)bl)->base_atk; - if (((struct map_session_data *)bl)->status.weapon < MAX_WEAPON_TYPE) - batk += ((struct map_session_data *)bl)->weapon_atk[((struct map_session_data *)bl)->status.weapon]; - } else { - int str,dstr; - str = status_get_str(bl); //STR - dstr = str/10; - batk = dstr*dstr + str; //base_atkを計算する - - if(bl->type == BL_MOB && ((struct mob_data *)bl)->guardian_data) - batk += batk * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv - - batk = status_calc_batk(bl,batk); - } - if(batk < 1) batk = 1; //base_atkは最低でも1 - return batk; -} -/*========================================== - * 対象のAtkを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_atk(struct block_list *bl) -{ - int atk=0; - nullpo_retr(0, bl); - switch (bl->type) { - case BL_PC: - return ((struct map_session_data*)bl)->right_weapon.watk; - case BL_MOB: - atk = ((struct mob_data*)bl)->db->atk1; - if(((struct mob_data *)bl)->guardian_data) - atk += atk * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv - break; - case BL_PET: // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - atk = ((struct pet_data *)bl)->status->atk1; - else - atk = ((struct pet_data*)bl)->db->atk1; - break; - case BL_HOMUNCULUS: //[blackhole89] - atk = ((struct homun_data*)bl)->atk; - break; - } - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - atk = status_calc_watk(bl,atk); - if(atk < 0) atk = 0; - return atk; -} -/*========================================== - * 対象の左手Atkを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_atk_(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_PC){ - return ((struct map_session_data*)bl)->left_weapon.watk; - } - return 0; -} -/*========================================== - * 対象のAtk2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_atk2(struct block_list *bl) -{ - int atk2=0; - nullpo_retr(0, bl); - - switch (bl->type) { - case BL_PC: - return ((struct map_session_data*)bl)->right_weapon.watk2; - case BL_MOB: - atk2 = ((struct mob_data*)bl)->db->atk2; - - if(((struct mob_data *)bl)->guardian_data) - atk2 += atk2 * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ATK / lv - break; - case BL_PET: // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - atk2 = ((struct pet_data *)bl)->status->atk2; - else - atk2 = ((struct pet_data*)bl)->db->atk2; - break; - case BL_HOMUNCULUS: //[blackhole89] - atk2 = ((struct homun_data*)bl)->atk; - break; - } - - // Absolute, then relative modifiers from status changes (shared between PC and NPC) - atk2 = status_calc_watk(bl,atk2); - - if(atk2 < 0) atk2 = 0; - return atk2; -} -/*========================================== - * 対象の左手Atk2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_atk_2(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_PC) - return ((struct map_session_data*)bl)->left_weapon.watk2; - return 0; -} -/*========================================== - * 対象のMAtk1を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_matk1(struct block_list *bl) -{ - nullpo_retr(0, bl); - - if(bl->type == BL_PC) - return ((struct map_session_data *)bl)->matk1; - else { - int matk = 0; - int int_ = status_get_int(bl); - matk = status_calc_matk(bl,int_+(int_/5)*(int_/5)); - return matk; - } -} -/*========================================== - * 対象のMAtk2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_matk2(struct block_list *bl) -{ - nullpo_retr(0, bl); - - if(bl->type == BL_PC) - return ((struct map_session_data *)bl)->matk2; - else { - int matk = 0; - int int_ = status_get_int(bl); - matk = status_calc_matk(bl,int_+(int_/7)*(int_/7)); - return matk; - } -} -/*========================================== - * 対象のDefを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_def(struct block_list *bl) -{ - struct unit_data *ud; - int def=0; - nullpo_retr(0, bl); - - if(bl->type==BL_PC){ - def = ((struct map_session_data *)bl)->def; - } else if(bl->type==BL_MOB) { - def = ((struct mob_data *)bl)->db->def; - } else if(bl->type==BL_PET) - def = ((struct pet_data *)bl)->db->def; - - - if (bl->type != BL_PC) //Players already had this one done. - def = status_calc_def(bl,def); - - ud = unit_bl2ud(bl); - if (ud && ud->skilltimer != -1) - def -= def * skill_get_castdef(ud->skillid)/100; - - if(def < 0) def = 0; - return def; -} -/*========================================== - * 対象のDef2を返す(汎用) - * 戻りは整数で1以上 - *------------------------------------------ - */ -int status_get_def2(struct block_list *bl) -{ - int def2 = 1; - nullpo_retr(1, bl); - - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->def2; - else if(bl->type==BL_MOB) - def2 = ((struct mob_data *)bl)->db->vit; - else if(bl->type==BL_PET) { // Use pet's stats - if (battle_config.pet_lv_rate && ((struct pet_data *)bl)->status) - def2 = ((struct pet_data *)bl)->status->vit; - else - def2 = ((struct pet_data *)bl)->db->vit; - } - - def2 = status_calc_def2(bl,def2); - if(def2 < 1) def2 = 1; - - return def2; -} -/*========================================== - * 対象のMDefを返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_mdef(struct block_list *bl) -{ - int mdef=0; - nullpo_retr(0, bl); - - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->mdef; - else if(bl->type==BL_MOB) - mdef = ((struct mob_data *)bl)->db->mdef; - else if(bl->type==BL_PET) - mdef = ((struct pet_data *)bl)->db->mdef; - - mdef = status_calc_mdef(bl,mdef); - if(mdef < 0) mdef = 0; - - return mdef; -} -/*========================================== - * 対象のMDef2を返す(汎用) - * 戻りは整数で0以上 - *------------------------------------------ - */ -int status_get_mdef2(struct block_list *bl) -{ - int mdef2=0; - nullpo_retr(0, bl); - - switch(bl->type) - { - case BL_PC: - return ((TBL_PC*)bl)->mdef2 + (((TBL_PC*)bl)->paramc[2]>>1); - case BL_MOB: - mdef2 = ((TBL_MOB*)bl)->db->int_ + (((TBL_MOB*)bl)->db->vit>>1); - break; - case BL_PET: - // Use pet's stats - if (battle_config.pet_lv_rate && ((TBL_PET*)bl)->status) - mdef2 = ((TBL_PET*)bl)->status->int_ +(((TBL_PET*)bl)->status->vit>>1); - else - mdef2 = ((TBL_PET*)bl)->db->int_ + (((TBL_PET*)bl)->db->vit>>1); - break; - } - - mdef2 = status_calc_mdef2(bl,mdef2); - if(mdef2 < 0) mdef2 = 0; - return mdef2; -} -/*========================================== - * 対象のSpeed(移動速度)を返す(汎用) - * 戻りは整数で1以上 - * Speedは小さいほうが移動速度が速い - *------------------------------------------ - */ -int status_get_speed(struct block_list *bl) -{ - int speed = 1000; - nullpo_retr(1000, bl); - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->speed; - else if(bl->type==BL_MOB) { - speed = ((struct mob_data *)bl)->speed; - if(battle_config.mobs_level_up) // increase from mobs leveling up [Valaris] - speed-=((struct mob_data *)bl)->level - ((struct mob_data *)bl)->db->lv; - } - else if(bl->type==BL_PET) - speed = ((struct pet_data *)bl)->speed; - else if(bl->type==BL_NPC) //Added BL_NPC (Skotlex) - speed = ((struct npc_data *)bl)->speed; - else if(bl->type==BL_HOMUNCULUS) //[blackhole89] - speed = ((struct homun_data *)bl)->speed; - - speed = status_calc_speed(bl,speed); - - if(speed < 1) speed = 1; - return speed; -} -/*========================================== - * 対象のaDelay(攻撃時ディレイ)を返す(汎用) - * aDelayは小さいほうが攻撃速度が速い - *------------------------------------------ - */ -int status_get_adelay(struct block_list *bl) -{ - int adelay,aspd_rate; - nullpo_retr(4000, bl); - switch (bl->type) { - case BL_PC: - return (((struct map_session_data *)bl)->aspd<<1); - case BL_MOB: - adelay = ((struct mob_data *)bl)->db->adelay; - if(((struct mob_data *)bl)->guardian_data) - aspd_rate = 100 - 10*((struct mob_data *)bl)->guardian_data->guardup_lv; // Strengthen Guardians - custom value +10% ASPD / lv - else - aspd_rate = 100; - break; - case BL_PET: - adelay = ((struct pet_data *)bl)->db->adelay; - aspd_rate = 100; - break; - case BL_HOMUNCULUS: - adelay = 500; //temp; this should go into the structure later - aspd_rate=100; //[blackhole89] - break; - default: - adelay=4000; - aspd_rate = 100; - break; - } - aspd_rate = status_calc_aspd_rate(bl,aspd_rate); - - if(aspd_rate != 100) - adelay = adelay*aspd_rate/100; - if(adelay < battle_config.monster_max_aspd<<1) adelay = battle_config.monster_max_aspd<<1; - return adelay; -} -int status_get_amotion(struct block_list *bl) -{ - nullpo_retr(2000, bl); - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->amotion; - else { - int amotion=2000,aspd_rate = 100; - if(bl->type==BL_MOB) { - amotion = ((struct mob_data *)bl)->db->amotion; - - if(((struct mob_data *)bl)->guardian_data) - aspd_rate -= aspd_rate * 10*((struct mob_data *)bl)->guardian_data->guardup_lv/100; // Strengthen Guardians - custom value +10% ASPD / lv - } else if(bl->type==BL_PET) - amotion = ((struct pet_data *)bl)->db->amotion; - else if(bl->type==BL_HOMUNCULUS) - amotion = ((struct homun_data *)bl)->amotion; //[blackhole89] - - aspd_rate = status_calc_aspd_rate(bl,aspd_rate); - - if(aspd_rate != 100) - amotion = amotion*aspd_rate/100; - if(amotion < battle_config.monster_max_aspd) amotion = battle_config.monster_max_aspd; - return amotion; - } - return 2000; -} -int status_get_dmotion(struct block_list *bl) -{ - int ret; - struct status_change *sc; - - nullpo_retr(0, bl); - sc = status_get_sc(bl); - if(bl->type==BL_MOB){ - ret=((struct mob_data *)bl)->db->dmotion; - if(battle_config.monster_damage_delay_rate != 100) - ret = ret*battle_config.monster_damage_delay_rate/100; - } - else if(bl->type==BL_PC){ - ret=((struct map_session_data *)bl)->dmotion; - if(battle_config.pc_damage_delay_rate != 100) - ret = ret*battle_config.pc_damage_delay_rate/100; - } - else if(bl->type==BL_PET) - ret=((struct pet_data *)bl)->db->dmotion; - else if(bl->type==BL_HOMUNCULUS) - ret=((struct homun_data *)bl)->dmotion; //[blackhole89] - else - return 2000; - - if(sc && sc->count - && (sc->data[SC_ENDURE].timer!=-1 || sc->data[SC_CONCENTRATION].timer!=-1) - && !map_flag_gvg(bl->m) //Only works on non-gvg grounds. [Skotlex] - ) - return 0; - - return ret; -} -int status_get_element(struct block_list *bl) -{ - // removed redundant variable ret [zzo] - struct status_change *sc= status_get_sc(bl); - - nullpo_retr(20, bl); - - if(sc && sc->count) { - if( sc->data[SC_FREEZE].timer!=-1 ) // 凍結 - return 21; - if( sc->data[SC_STONE].timer!=-1 && sc->data[SC_STONE].val2==0) - return 22; - if( sc->data[SC_BENEDICTIO].timer!=-1 ) // 聖体降福 - return 26; - } - if(bl->type==BL_MOB) // 10の位=Lv*2、1の位=属性 - return ((struct mob_data *)bl)->def_ele; - if(bl->type==BL_PC) - return 20+((struct map_session_data *)bl)->def_ele; // 防御属性Lv1 - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->element; - - return 20; -} -//Retrieves the object's element acquired by status changes only. -int status_get_attack_sc_element(struct block_list *bl) -{ - struct status_change *sc =status_get_sc(bl); - if(sc && sc->count) { - if( sc->data[SC_WATERWEAPON].timer!=-1) // フロストウェポン - return 1; - if( sc->data[SC_EARTHWEAPON].timer!=-1) // サイズミックウェポン - return 2; - if( sc->data[SC_FIREWEAPON].timer!=-1) // フレームランチャー - return 3; - if( sc->data[SC_WINDWEAPON].timer!=-1) // ライトニングローダー - return 4; - if( sc->data[SC_ENCPOISON].timer!=-1) // エンチャントポイズン - return 5; - if( sc->data[SC_ASPERSIO].timer!=-1) // アスペルシオ - return 6; - if( sc->data[SC_SHADOWWEAPON].timer!=-1) - return 7; - if( sc->data[SC_GHOSTWEAPON].timer!=-1) - return 8; - } - return 0; -} - - -int status_get_attack_element(struct block_list *bl) -{ - int ret = status_get_attack_sc_element(bl); - - nullpo_retr(0, bl); - - if (ret) return ret; - - if(bl->type==BL_MOB && (struct mob_data *)bl) - return 0; - if(bl->type==BL_PC && (struct map_session_data *)bl) - return ((struct map_session_data *)bl)->right_weapon.atk_ele; - if(bl->type==BL_PET && (struct pet_data *)bl) - return 0; - - return 0; -} -int status_get_attack_element2(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_PC) { - // removed redundant var, speeded up a bit [zzo] - int ret = status_get_attack_sc_element(bl); - - if(ret) return ret; - return ((struct map_session_data *)bl)->left_weapon.atk_ele; - } - return 0; -} -int status_get_party_id(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->status.party_id; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->msd->status.party_id; - if(bl->type==BL_MOB){ - struct mob_data *md=(struct mob_data *)bl; - if( md->master_id>0 ) - { - struct map_session_data *msd; - if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL) - return msd->status.party_id; - return -md->master_id; - } - return 0; //No party. - } - if(bl->type==BL_SKILL) - return ((struct skill_unit *)bl)->group->party_id; - return 0; -} - -int status_get_guild_id(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_PC) - return ((struct map_session_data *)bl)->status.guild_id; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->msd->status.guild_id; - if(bl->type==BL_MOB) - { - struct map_session_data *msd; - struct mob_data *md = (struct mob_data *)bl; - if (md->guardian_data) //Guardian's guild [Skotlex] - return md->guardian_data->guild_id; - if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL) - return msd->status.guild_id; //Alchemist's mobs [Skotlex] - return 0; //No guild. - } - if (bl->type == BL_NPC && bl->subtype == SCRIPT) - return ((TBL_NPC*)bl)->u.scr.guild_id; - if(bl->type==BL_SKILL) - return ((struct skill_unit *)bl)->group->guild_id; - return 0; -} -int status_get_race(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_MOB) - return ((struct mob_data *)bl)->db->race; - if(bl->type==BL_PC) - return RC_DEMIHUMAN; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->race; - return 0; -} -int status_get_size(struct block_list *bl) -{ - nullpo_retr(1, bl); - switch (bl->type) { - case BL_MOB: - return ((struct mob_data *)bl)->db->size; - case BL_PET: - return ((struct pet_data *)bl)->db->size; - case BL_PC: - { - struct map_session_data *sd = (struct map_session_data *)bl; - if (sd->sc.data[SC_SWOO].timer != -1) - return 0; - if (sd->class_&JOBL_BABY) //[Lupus] - return (pc_isriding(sd) && battle_config.character_size&2); //Baby Class Peco Rider + enabled option -> size = 1, else 0 - return 1+(pc_isriding(sd) && battle_config.character_size&1); //Peco Rider + enabled option -> size = 2, else 1 - } - } - return 1; -} -int status_get_mode(struct block_list *bl) -{ - nullpo_retr(MD_CANMOVE, bl); - if(bl->type==BL_MOB) - { - if (((struct mob_data *)bl)->mode) - return ((struct mob_data *)bl)->mode; - return ((struct mob_data *)bl)->db->mode; - } - if(bl->type==BL_PC) - return (MD_CANMOVE|MD_LOOTER|MD_CANATTACK); - if(bl->type==BL_HOMUNCULUS) //[blackhole89] - return (MD_CANMOVE|MD_CANATTACK); - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->mode; - if (bl->type==BL_SKILL) - return (MD_CANATTACK|MD_CANMOVE); //Default mode for skills: Can attack, can move (think dances). - //Default universal mode, can move - return MD_CANMOVE; // とりあえず動くということで1 -} - -int status_get_mexp(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type==BL_MOB) - return ((struct mob_data *)bl)->db->mexp; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->mexp; - return 0; -} -int status_get_race2(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type == BL_MOB) - return ((struct mob_data *)bl)->db->race2; - if(bl->type==BL_PET) - return ((struct pet_data *)bl)->db->race2; - return 0; -} -int status_isdead(struct block_list *bl) -{ - nullpo_retr(0, bl); - if(bl->type == BL_MOB) - return ((struct mob_data *)bl)->hp <= 0; - if(bl->type==BL_PC) - return pc_isdead((struct map_session_data *)bl); - return 0; -} -int status_isimmune(struct block_list *bl) -{ - struct status_change *sc =status_get_sc(bl); - if (bl->type == BL_PC && - ((struct map_session_data *)bl)->special_state.no_magic_damage) - return 1; - if (sc && sc->count && sc->data[SC_HERMODE].timer != -1) - return 1; - return 0; -} - -struct view_data *status_get_viewdata(struct block_list *bl) -{ - nullpo_retr(NULL, bl); - switch (bl->type) - { - case BL_PC: - return &((TBL_PC*)bl)->vd; - case BL_MOB: - return ((TBL_MOB*)bl)->vd; - case BL_PET: - return &((TBL_PET*)bl)->vd; - case BL_NPC: - return ((TBL_NPC*)bl)->vd; - case BL_HOMUNCULUS: //[blackhole89] - return ((struct homun_data*)bl)->vd; - } - return NULL; -} - -void status_set_viewdata(struct block_list *bl, int class_) -{ - struct view_data* vd; - nullpo_retv(bl); - if (mobdb_checkid(class_) || mob_is_clone(class_)) - vd = mob_get_viewdata(class_); - else if (npcdb_checkid(class_) || (bl->type == BL_NPC && class_ == WARP_CLASS)) - vd = npc_get_viewdata(class_); - else - vd = NULL; - - switch (bl->type) { - case BL_PC: - { - TBL_PC* sd = (TBL_PC*)bl; - if (pcdb_checkid(class_)) { - if (sd->sc.option&OPTION_RIDING) - switch (class_) - { //Adapt class to a Mounted one. - case JOB_KNIGHT: - class_ = JOB_KNIGHT2; - break; - case JOB_CRUSADER: - class_ = JOB_CRUSADER2; - break; - case JOB_LORD_KNIGHT: - class_ = JOB_LORD_KNIGHT2; - break; - case JOB_PALADIN: - class_ = JOB_PALADIN2; - break; - case JOB_BABY_KNIGHT: - class_ = JOB_BABY_KNIGHT2; - break; - case JOB_BABY_CRUSADER: - class_ = JOB_BABY_CRUSADER2; - break; - } - if (class_ == JOB_WEDDING) - sd->sc.option|=OPTION_WEDDING; - else if (sd->sc.option&OPTION_WEDDING) - sd->sc.option&=~OPTION_WEDDING; //If not going to display it, then remove the option. - sd->vd.class_ = class_; - clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield); - sd->vd.head_top = sd->status.head_top; - sd->vd.head_mid = sd->status.head_mid; - sd->vd.head_bottom = sd->status.head_bottom; - sd->vd.hair_style = sd->status.hair; - sd->vd.hair_color = sd->status.hair_color; - sd->vd.cloth_color = sd->status.clothes_color; - sd->vd.sex = sd->status.sex; - } else if (vd) - memcpy(&sd->vd, vd, sizeof(struct view_data)); - else if (battle_config.error_log) - ShowError("status_set_viewdata (PC): No view data for class %d\n", class_); - } - break; - case BL_MOB: - { - TBL_MOB* md = (TBL_MOB*)bl; - if (vd) - md->vd = vd; - else if (battle_config.error_log) - ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_); - } - break; - case BL_PET: - { - TBL_PET* pd = (TBL_PET*)bl; - if (vd) { - memcpy(&pd->vd, vd, sizeof(struct view_data)); - if (!pcdb_checkid(vd->class_)) { - pd->vd.hair_style = battle_config.pet_hair_style; - if(pd->equip) { - pd->vd.head_bottom = itemdb_viewid(pd->equip); - if (!pd->vd.head_bottom) - pd->vd.head_bottom = pd->equip; - } - } - } else if (battle_config.error_log) - ShowError("status_set_viewdata (PET): No view data for class %d\n", class_); - } - break; - case BL_NPC: - { - TBL_NPC* nd = (TBL_NPC*)bl; - if (vd) - nd->vd = vd; - else if (battle_config.error_log) - ShowError("status_set_viewdata (NPC): No view data for class %d\n", class_); - } - break; - case BL_HOMUNCULUS: //[blackhole89] - { - struct homun_data *hd = (struct homun_data*)bl; - if (vd) - hd->vd = vd; - else if (battle_config.error_log) - ShowError("status_set_viewdata (HOMUNCULUS): No view data for class %d\n", class_); - } - break; - } - vd = status_get_viewdata(bl); - if (vd && vd->cloth_color && ( - (vd->class_==JOB_WEDDING && !battle_config.wedding_ignorepalette) - || (vd->class_==JOB_XMAS && !battle_config.xmas_ignorepalette) - )) - vd->cloth_color = 0; -} - -struct status_change *status_get_sc(struct block_list *bl) -{ - nullpo_retr(NULL, bl); - switch (bl->type) { - case BL_MOB: - return &((TBL_MOB*)bl)->sc; - case BL_PC: - return &((TBL_PC*)bl)->sc; - case BL_NPC: - return &((TBL_NPC*)bl)->sc; - case BL_HOMUNCULUS: //[blackhole89] - return &((struct homun_data*)bl)->sc; - } - return NULL; -} - -void status_change_init(struct block_list *bl) -{ - struct status_change *sc = status_get_sc(bl); - int i; - nullpo_retv(sc); - memset(sc, 0, sizeof (struct status_change)); - for (i=0; i< SC_MAX; i++) - sc->data[i].timer = -1; -} - -//Returns defense against the specified status change. -//Return range is 0 (no resist) to 10000 (inmunity) -int status_get_sc_def(struct block_list *bl, int type) -{ - int sc_def; - struct status_change* sc; - nullpo_retr(0, bl); - - //Status that are blocked by Golden Thief Bug card or Wand of Hermod - if (status_isimmune(bl)) - switch (type) - { - case SC_DECREASEAGI: - case SC_SILENCE: - case SC_COMA: - case SC_INCREASEAGI: - case SC_BLESSING: - case SC_SLOWPOISON: - case SC_IMPOSITIO: - case SC_AETERNA: - case SC_SUFFRAGIUM: - case SC_BENEDICTIO: - case SC_PROVIDENCE: - case SC_KYRIE: - case SC_ASSUMPTIO: - case SC_ANGELUS: - case SC_MAGNIFICAT: - case SC_GLORIA: - case SC_WINDWALK: - case SC_MAGICROD: - case SC_HALLUCINATION: - case SC_STONE: - case SC_QUAGMIRE: - return 10000; - } - - switch (type) - { - //Note that stats that are *100/3 were simplified to *33 - case SC_STONE: - case SC_FREEZE: - case SC_DECREASEAGI: - case SC_COMA: - sc_def = 300 +100*status_get_mdef(bl) +33*status_get_luk(bl); - break; - case SC_SLEEP: - case SC_CONFUSION: - sc_def = 300 +100*status_get_int(bl) +33*status_get_luk(bl); - break; -// Removed since it collides with normal sc. -// case SP_DEF1: // def -// sc_def = 300 +100*status_get_def(bl) +33*status_get_luk(bl); -// break; - case SC_STUN: - case SC_POISON: - case SC_DPOISON: - case SC_SILENCE: - case SC_BLEEDING: - case SC_STOP: - sc_def = 300 +100*status_get_vit(bl) +33*status_get_luk(bl); - break; - case SC_BLIND: - sc_def = 300 +100*status_get_int(bl) +33*status_get_vit(bl); - break; - case SC_CURSE: - sc_def = 300 +100*status_get_luk(bl) +33*status_get_vit(bl); - break; - default: - return 0; //Effect that cannot be reduced? Likely a buff. - } - - if (bl->type == BL_PC) { - if (battle_config.pc_sc_def_rate != 100) - sc_def = sc_def*battle_config.pc_sc_def_rate/100; - } else - if (battle_config.mob_sc_def_rate != 100) - sc_def = sc_def*battle_config.mob_sc_def_rate/100; - - sc = status_get_sc(bl); - if (sc && sc->count) - { - if (sc->data[SC_SCRESIST].timer != -1) - sc_def += 100*sc->data[SC_SCRESIST].val1; //Status resist - else if (sc->data[SC_SIEGFRIED].timer != -1) - sc_def += 100*sc->data[SC_SIEGFRIED].val2; //Status resistance. - } - - if(bl->type == BL_PC) { - if (sc_def > battle_config.pc_max_sc_def) - sc_def = battle_config.pc_max_sc_def; - } else if (sc_def > battle_config.mob_max_sc_def) - sc_def = battle_config.mob_max_sc_def; - - return sc_def; -} - -//Reduces tick delay based on type and character defenses. -int status_get_sc_tick(struct block_list *bl, int type, int tick) -{ - struct map_session_data *sd; - int rate=0, min=0; - //If rate is positive, it is a % reduction (10000 -> 100%) - //if it is negative, it is an absolute reduction in ms. - sd = bl->type == BL_PC?(struct map_session_data *)bl:NULL; - switch (type) { - case SC_DECREASEAGI: /* 速度減少 */ - if (sd) // Celest - tick>>=1; - break; - case SC_ADRENALINE: /* アドレナリンラッシュ */ - case SC_ADRENALINE2: - case SC_WEAPONPERFECTION: /* ウェポンパ?フェクション */ - case SC_OVERTHRUST: /* オ?バ?スラスト */ - if(sd && pc_checkskill(sd,BS_HILTBINDING)>0) - tick += tick / 10; - break; - case SC_STONE: /* 石化 */ - rate = -200*status_get_mdef(bl); - break; - case SC_FREEZE: /* 凍結 */ - rate = 100*status_get_mdef(bl); - break; - case SC_STUN: //Reduction in duration is the same as reduction in rate. - rate = 300 +100*status_get_vit(bl) +33*status_get_luk(bl); - break; - case SC_DPOISON: /* 猛毒 */ - case SC_POISON: /* 毒 */ - rate = 100*status_get_vit(bl) + 20*status_get_luk(bl); - break; - case SC_SILENCE: /* 沈?(レックスデビ?ナ) */ - case SC_CONFUSION: - case SC_CURSE: - rate = 100*status_get_vit(bl); - break; - case SC_BLIND: /* 暗? */ - rate = 10*status_get_lv(bl) + 7*status_get_int(bl); - min = 5000; //Minimum 5 secs? - break; - case SC_BLEEDING: - rate = 20*status_get_lv(bl) +100*status_get_vit(bl); - min = 10000; //Need a min of 10 secs for it to hurt at least once. - break; - case SC_SWOO: - if (status_get_mode(bl)&MD_BOSS) - tick /= 5; //TODO: Reduce skill's duration. But for how long? - break; - case SC_ANKLE: - if(status_get_mode(bl)&MD_BOSS) // Lasts 5 times less on bosses - tick /= 5; - rate = -100*status_get_agi(bl); - // Minimum trap time of 3+0.03*skilllv seconds [celest] - // Changed to 3 secs and moved from skill.c [Skotlex] - min = 3000; - break; - case SC_SPIDERWEB: - if (map[bl->m].flag.pvp) - tick /=2; - break; - case SC_STOP: - // Unsure of this... but I get a feeling that agi reduces this - // (it was on Tiger Fist Code, but at -1 ms per 10 agi.... - rate = -100*status_get_agi(bl); - break; - } - if (rate) { - if (bl->type == BL_PC) { - if (battle_config.pc_sc_def_rate != 100) - rate = rate*battle_config.pc_sc_def_rate/100; - if (battle_config.pc_max_sc_def != 10000) - min = tick*(10000-battle_config.pc_max_sc_def)/10000; - } else { - if (battle_config.mob_sc_def_rate != 100) - rate = rate*battle_config.mob_sc_def_rate/100; - if (battle_config.mob_max_sc_def != 10000) - min = tick*(10000-battle_config.mob_max_sc_def)/10000; - } - - if (rate >0) - tick -= tick*rate/10000; - else - tick += rate; - } - return ticktype) - { - case BL_PC: - sd=(struct map_session_data *)bl; - break; - case BL_MOB: - if (((struct mob_data*)bl)->class_ == MOBID_EMPERIUM && type != SC_SAFETYWALL) - return 0; //Emperium can't be afflicted by status changes. - break; - } - - if(type < 0 || type >= SC_MAX) { - if(battle_config.error_log) - ShowError("status_change_start: invalid status change (%d)!\n", type); - return 0; - } - - //Check rate - if (!(flag&(4|1))) { - if (rate > 10000) //Shouldn't let this go above 100% - rate = 10000; - race = flag&8?0:status_get_sc_def(bl, type); //recycling race to store the sc_def value. - //sd resistance applies even if the flag is &8 - if(sd && SC_COMMON_MIN<=type && type<=SC_COMMON_MAX && sd->reseff[type-SC_COMMON_MIN] > 0) - race+= sd->reseff[type-SC_COMMON_MIN]; - - if (race) - rate -= rate*race/10000; - - if (!(rand()%10000 < rate)) - return 0; - } - - //SC duration reduction. - if(!(flag&(2|4)) && tick) { - tick = status_get_sc_tick(bl, type, tick); - if (tick <= 0) - return 0; - } - - race=status_get_race(bl); - mode=status_get_mode(bl); - elem=status_get_elem_type(bl); - undead_flag=battle_check_undead(race,elem); - - //Check for inmunities / sc fails - switch (type) { - case SC_FREEZE: - case SC_STONE: - //Undead are inmune to Freeze/Stone - if (undead_flag && !(flag&1)) - return 0; - case SC_SLEEP: - case SC_STUN: - if (sc->opt1) - return 0; //Cannot override other opt1 status changes. [Skotlex] - break; - case SC_CURSE: - //Dark Elementals are inmune to curse. - if (elem == 7 && !(flag&1)) - return 0; - break; - case SC_COMA: - //Dark elementals and Demons are inmune to coma. - if((elem == 7 || race == RC_DEMON) && !(flag&1)) - return 0; - break; - case SC_SIGNUMCRUCIS: - //Only affects demons and undead. - if(race != RC_DEMON && !undead_flag) - return 0; - break; - case SC_AETERNA: - if (sc->data[SC_STONE].timer != -1 || sc->data[SC_FREEZE].timer != -1) - return 0; - break; - case SC_OVERTHRUST: - if (sc->data[SC_MAXOVERTHRUST].timer != -1) - return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex] - break; - case SC_ADRENALINE: - if (sd && !(skill_get_weapontype(BS_ADRENALINE)&(1<status.weapon))) - return 0; - if (sc->data[SC_QUAGMIRE].timer!=-1 || - sc->data[SC_DONTFORGETME].timer!=-1 || - sc->data[SC_DECREASEAGI].timer!=-1 - ) - return 0; - break; - case SC_ADRENALINE2: - if (sd && !(skill_get_weapontype(BS_ADRENALINE2)&(1<status.weapon))) - return 0; - if (sc->data[SC_QUAGMIRE].timer!=-1 || - sc->data[SC_DONTFORGETME].timer!=-1 || - sc->data[SC_DECREASEAGI].timer!=-1 - ) - return 0; - break; - case SC_ONEHAND: - case SC_TWOHANDQUICKEN: - if(sc->data[SC_DECREASEAGI].timer!=-1) - return 0; - case SC_CONCENTRATE: - case SC_INCREASEAGI: - case SC_SPEARQUICKEN: - case SC_TRUESIGHT: - case SC_WINDWALK: - case SC_CARTBOOST: - case SC_ASSNCROS: - if (sc->data[SC_QUAGMIRE].timer!=-1 || sc->data[SC_DONTFORGETME].timer!=-1) - return 0; - break; - case SC_CLOAKING: - //Avoid cloaking with no wall and low skill level. [Skotlex] - //Due to the cloaking card, we have to check the wall versus to known skill level rather than the used one. [Skotlex] -// if (sd && skilllv < 3 && skill_check_cloaking(bl)) - if (sd && pc_checkskill(sd, AS_CLOAKING)< 3 && skill_check_cloaking(bl)) - return 0; - break; - } - - //Check for BOSS resistances - if(mode & MD_BOSS && !(flag&1)) { - if (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX) - return 0; - switch (type) { - case SC_BLESSING: - if (!undead_flag && race != RC_DEMON) - break; - case SC_QUAGMIRE: - case SC_DECREASEAGI: - case SC_SIGNUMCRUCIS: - case SC_PROVOKE: - case SC_ROKISWEIL: - case SC_COMA: - case SC_GRAVITATION: - return 0; - } - } - //Before overlapping fail, one must check for status cured. - switch (type) { - case SC_BLESSING: - if ((!undead_flag && race!=RC_DEMON) || bl->type == BL_PC) { - if (sc->data[SC_CURSE].timer!=-1) - status_change_end(bl,SC_CURSE,-1); - if (sc->data[SC_STONE].timer!=-1 && sc->data[SC_STONE].val2==0) - status_change_end(bl,SC_STONE,-1); - } - break; - case SC_INCREASEAGI: - if(sc->data[SC_DECREASEAGI].timer!=-1 ) - status_change_end(bl,SC_DECREASEAGI,-1); - break; - case SC_DONTFORGETME: - //is this correct? Maybe all three should stop the same subset of SCs... - if(sc->data[SC_ASSNCROS].timer!=-1 ) - status_change_end(bl,SC_ASSNCROS,-1); - case SC_QUAGMIRE: - if(sc->data[SC_CONCENTRATE].timer!=-1 ) - status_change_end(bl,SC_CONCENTRATE,-1); - if(sc->data[SC_TRUESIGHT].timer!=-1 ) - status_change_end(bl,SC_TRUESIGHT,-1); - if(sc->data[SC_WINDWALK].timer!=-1 ) - status_change_end(bl,SC_WINDWALK,-1); - //Also blocks the ones below... - case SC_DECREASEAGI: - if(sc->data[SC_INCREASEAGI].timer!=-1 ) - status_change_end(bl,SC_INCREASEAGI,-1); - if(sc->data[SC_ADRENALINE].timer!=-1 ) - status_change_end(bl,SC_ADRENALINE,-1); - if(sc->data[SC_ADRENALINE2].timer!=-1 ) - status_change_end(bl,SC_ADRENALINE2,-1); - if(sc->data[SC_SPEARQUICKEN].timer!=-1 ) - status_change_end(bl,SC_SPEARQUICKEN,-1); - if(sc->data[SC_TWOHANDQUICKEN].timer!=-1 ) - status_change_end(bl,SC_TWOHANDQUICKEN,-1); - if(sc->data[SC_CARTBOOST].timer!=-1 ) - status_change_end(bl,SC_CARTBOOST,-1); - if(sc->data[SC_ONEHAND].timer!=-1 ) - status_change_end(bl,SC_ONEHAND,-1); - break; - case SC_ONEHAND: - //Removes the Aspd potion effect, as reported by Vicious. [Skotlex] - if(sc->data[SC_ASPDPOTION0].timer!=-1) - status_change_end(bl,SC_ASPDPOTION0,-1); - if(sc->data[SC_ASPDPOTION1].timer!=-1) - status_change_end(bl,SC_ASPDPOTION1,-1); - if(sc->data[SC_ASPDPOTION2].timer!=-1) - status_change_end(bl,SC_ASPDPOTION2,-1); - if(sc->data[SC_ASPDPOTION3].timer!=-1) - status_change_end(bl,SC_ASPDPOTION3,-1); - break; - case SC_MAXOVERTHRUST: - //Cancels Normal Overthrust. [Skotlex] - if (sc->data[SC_OVERTHRUST].timer != -1) - status_change_end(bl, SC_OVERTHRUST, -1); - break; - case SC_KYRIE: - // -- moonsoul (added to undo assumptio status if target has it) - if(sc->data[SC_ASSUMPTIO].timer!=-1 ) - status_change_end(bl,SC_ASSUMPTIO,-1); - break; - case SC_DELUGE: - if (sc->data[SC_FOGWALL].timer != -1 && sc->data[SC_BLIND].timer != -1) - status_change_end(bl,SC_BLIND,-1); - break; - case SC_SILENCE: - if (sc->data[SC_GOSPEL].timer!=-1 && sc->data[SC_GOSPEL].val4 == BCT_SELF) - //Clear Gospel [Skotlex] - status_change_end(bl,SC_GOSPEL,-1); - break; - case SC_HIDING: - if(sc->data[SC_CLOSECONFINE].timer != -1) - status_change_end(bl, SC_CLOSECONFINE, -1); - if(sc->data[SC_CLOSECONFINE2].timer != -1) - status_change_end(bl, SC_CLOSECONFINE2, -1); - break; - case SC_BERSERK: /* バ?サ?ク */ - if(battle_config.berserk_cancels_buffs) - { - if (sc->data[SC_ONEHAND].timer != -1) - status_change_end(bl,SC_ONEHAND,-1); - if (sc->data[SC_TWOHANDQUICKEN].timer != -1) - status_change_end(bl,SC_TWOHANDQUICKEN,-1); - if (sc->data[SC_CONCENTRATION].timer != -1) - status_change_end(bl,SC_CONCENTRATION,-1); - if (sc->data[SC_PARRYING].timer != -1) - status_change_end(bl,SC_PARRYING,-1); - if (sc->data[SC_AURABLADE].timer != -1) - status_change_end(bl,SC_AURABLADE,-1); - } - break; - case SC_ASSUMPTIO: - if(sc->data[SC_KYRIE].timer!=-1) - status_change_end(bl,SC_KYRIE,-1); - break; - case SC_CARTBOOST: /* カ?トブ?スト */ - if(sc->data[SC_DECREASEAGI].timer!=-1 ) - { //Cancel Decrease Agi, but take no further effect [Skotlex] - status_change_end(bl,SC_DECREASEAGI,-1); - return 0; - } - break; - case SC_FUSION: - if(sc->data[SC_SPIRIT].timer!=-1 ) - status_change_end(bl,SC_SPIRIT,-1); - break; - } - //Check for overlapping fails - if(sc->data[type].timer != -1){ - switch (type) { - case SC_ADRENALINE: - case SC_ADRENALINE2: - case SC_WEAPONPERFECTION: - case SC_OVERTHRUST: - if (sc->data[type].val2 && !val2) - return 0; - break; - case SC_WARM: - { //Fetch the Group, half the attack interval. [Skotlex] - struct skill_unit_group *group = (struct skill_unit_group *)sc->data[type].val4; - if (group) - group->interval/=2; - return 1; - } - case SC_STUN: - case SC_SLEEP: - case SC_POISON: - case SC_CURSE: - case SC_SILENCE: - case SC_CONFUSION: - case SC_BLIND: - case SC_BLEEDING: - case SC_DPOISON: - case SC_COMBO: //You aren't supposed to change the combo (and it gets turned off when you trigger it) - case SC_CLOSECONFINE2: //Can't be re-closed in. - return 0; - case SC_DANCING: - case SC_DEVOTION: - case SC_ASPDPOTION0: - case SC_ASPDPOTION1: - case SC_ASPDPOTION2: - case SC_ASPDPOTION3: - case SC_ATKPOTION: - case SC_MATKPOTION: - break; - case SC_GOSPEL: - //Must not override a casting gospel char. - if(sc->data[type].val4 == BCT_SELF) - return 0; - if(sc->data[type].val1 > val1) - return 1; - break; - case SC_ENDURE: - if(sc->data[type].val4 && !val4) - return 1; //Don't let you override infinite endure. - if(sc->data[type].val1 > val1) - return 1; - break; - case SC_KAAHI: - if(sc->data[type].val1 > val1) - return 1; - //Delete timer if it exists. - if (sc->data[type].val4 != -1) { - delete_timer(sc->data[type].val4,kaahi_heal_timer); - sc->data[type].val4=-1; - } - break; - default: - if(sc->data[type].val1 > val1) - return 1; //Return true to not mess up skill animations. [Skotlex - } - (sc->count)--; - delete_timer(sc->data[type].timer, status_change_timer); - sc->data[type].timer = -1; - } - - switch(type){ /* 異常の種類ごとの?理 */ - case SC_PROVOKE: /* プロボック */ - calc_flag = 1; - if(tick <= 0) tick = 1000; /* (オ?トバ?サ?ク) */ - break; - case SC_ENDURE: /* インデュア */ - if(tick <= 0) tick = 1000 * 60; - val2 = 7; // [Celest] - if (!val4) - calc_flag = 1; // for updating mdef - break; - case SC_AUTOBERSERK: - { - if (!(flag&4)) - tick = 60*1000; - if (sd && sd->status.hpstatus.max_hp>>2 && - (sc->data[SC_PROVOKE].timer==-1 || sc->data[SC_PROVOKE].val2==0)) - sc_start4(bl,SC_PROVOKE,100,10,1,0,0,0); - } - break; - - case SC_SIGNUMCRUCIS: /* シグナムクルシス */ - calc_flag = 1; - val2 = 10 + val1*2; - if (!(flag&4)) - tick = 600*1000; - clif_emotion(bl,4); - break; - case SC_MAXIMIZEPOWER: /* マキシマイズパワ?(SPが1減る時間,val2にも) */ - if (flag&4) - break; - val2 = tick>0?tick:60000; - break; - case SC_EDP: // [Celest] - val2 = val1 + 2; /* 猛毒付?確率(%) */ - calc_flag = 1; - break; - case SC_POISONREACT: /* ポイズンリアクト */ - if (!(flag&4)) - val2=val1/2 + val1%2; // [Celest] - break; - case SC_MAGICROD: - val2 = val1*20; - break; - case SC_KYRIE: /* キリエエレイソン */ - if (!(flag&4)) - { - val2 = status_get_max_hp(bl) * (val1 * 2 + 10) / 100;/* 耐久度 */ - val3 = (val1 / 2 + 5); /* 回? */ - } - break; - case SC_MINDBREAKER: - calc_flag = 1; - if(tick <= 0) tick = 1000; /* (オ?トバ?サ?ク) */ - break; - case SC_MAGICPOWER: - calc_flag = 1; - val2 = 1; - break; - case SC_SACRIFICE: - if (!(flag&4)) - val2 = 5; - break; - case SC_ENCPOISON: /* エンチャントポイズン */ - calc_flag = 1; - val2=(((val1 - 1) / 2) + 3)*100; /* 毒付?確率 */ - case SC_ASPERSIO: /* アスペルシオ */ - case SC_FIREWEAPON: /* フレ?ムランチャ? */ - case SC_WATERWEAPON: /* フロストウェポン */ - case SC_WINDWEAPON: /* ライトニングロ?ダ? */ - case SC_EARTHWEAPON: /* サイズミックウェポン */ - case SC_SHADOWWEAPON: - case SC_GHOSTWEAPON: - skill_enchant_elemental_end(bl,type); - break; - case SC_PROVIDENCE: /* プロヴィデンス */ - calc_flag = 1; - val2=val1*5; - break; - case SC_REFLECTSHIELD: - val2=10+val1*3; - break; - case SC_STRIPWEAPON: - if (val2==0) val2=90; - break; - case SC_STRIPSHIELD: - if (val2==0) val2=85; - break; - - case SC_AUTOSPELL: /* オ?トスペル */ - val4 = 5 + val1*2; - break; - - case SC_VOLCANO: - calc_flag = 1; - val3 = val1*10; - break; - case SC_VIOLENTGALE: - calc_flag = 1; - val3 = val1*3; - break; - case SC_SUITON: - calc_flag = 1; - if (flag&4) - break; - if (status_get_class(bl) != JOB_NINJA) { - //Is there some kind of formula behind this? - switch ((val1+1)/3) { - case 3: - val2 = 8; - break; - case 2: - val2 = 5; - break; - case 1: - val2 = 3; - break; - case 0: - val2 = 0; - break; - default: - val2 = 3*((val1+1)/3); - break; - } - } else val2 = 0; - break; - - case SC_SPEARQUICKEN: /* スピアクイッケン */ - calc_flag = 1; - val2 = 20+val1; - break; - - case SC_MOONLIT: - val2 = bl->id; - skill_setmapcell(bl,CG_MOONLIT, val1, CELL_SETMOONLIT); - break; - case SC_DANCING: /* ダンス/演奏中 */ - calc_flag = 1; - if (!(flag&4)) - { - val3= tick / 1000; - tick = 1000; - } - break; - - case SC_EXPLOSIONSPIRITS: // 爆裂波動 - calc_flag = 1; - val2 = 75 + 25*val1; - break; - case SC_AUTOCOUNTER: - val3 = val4 = 0; - break; - - case SC_ASPDPOTION0: /* ?速ポ?ション */ - case SC_ASPDPOTION1: - case SC_ASPDPOTION2: - case SC_ASPDPOTION3: - calc_flag = 1; - if (!(flag&4)) - val2 = 5*(2+type-SC_ASPDPOTION0); - break; - - case SC_WEDDING: //結婚用(結婚衣裳になって?くのが?いとか) - case SC_XMAS: - { - struct view_data *vd = status_get_viewdata(bl); - if (vd) { - //Store previous values as they could be removed. - val1 = vd->class_; - val2 = vd->weapon; - val3 = vd->shield; - val4 = vd->cloth_color; - unit_stop_attack(bl); - clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:JOB_XMAS); - clif_changelook(bl,LOOK_WEAPON,0); - clif_changelook(bl,LOOK_SHIELD,0); - clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); - } - } - break; - case SC_NOCHAT: //チャット禁止?態 - { - if(!battle_config.muting_players) { - sd->status.manner = 0; //Zido - status_change_end(&sd->bl,SC_NOCHAT,-1); //Zido - return 0; - } - - if (!(flag&4)) - tick = 60000; - updateflag = SP_MANNER; - save_flag = 1; // celest - } - break; - - /* option1 */ - case SC_STONE: /* 石化 */ - if (flag&4) - break; - val2 = 1; - val3 = tick/1000; - if(val3 < 1) val3 = 1; - tick = 5000; - break; - - /* option2 */ - case SC_DPOISON: /* 猛毒 */ - { - int hp = status_get_hp(bl); - int mhp = status_get_max_hp(bl); - - //Lose 10/15% of your life as long as it doesn't brings life below 25% - if (hp > mhp>>2) { - int diff = mhp*(bl->type==BL_PC?10:15)/100; - if (hp - diff < mhp>>2) - diff = hp - (mhp>>2); - battle_damage(NULL, bl, diff, 0, 1); - } - } // fall through - case SC_POISON: /* 毒 */ - { - int mhp; - - calc_flag = 1; - if (flag&4) - break; - val3 = tick/1000; - if(val3 < 1) val3 = 1; - tick = 1000; - mhp = status_get_max_hp(bl); - if (bl->type == BL_PC) - val4 = (type == SC_DPOISON) ? 3 + mhp/50 : 3 + mhp*3/200; - else - val4 = (type == SC_DPOISON) ? 3 + mhp/100 : 3 + mhp/200; - - } - break; - case SC_CONFUSION: - clif_emotion(bl,1); - break; - case SC_BLEEDING: - val4 = tick; - tick = 10000; - break; - /* option */ - case SC_HIDING: /* ハイディング */ - calc_flag = 1; - if(bl->type == BL_PC && !(flag&4)) { - val2 = tick / 1000; /* 持?時間 */ - tick = 1000; - } - break; - case SC_CHASEWALK: - case SC_CLOAKING: /* クロ?キング */ - if (flag&4) - break; - calc_flag = 1; // [Celest] - val2 = tick>0?tick:60000; - val3 = type==SC_CLOAKING ? 130-val1*3 : 135-val1*5; - if (!val4) - { //val4 signals eternal cloaking (not cancelled on attack) [Skotlex] - //Standard cloaking. - if (bl->type == BL_PC) - val4 = battle_config.pc_cloak_check_type&2?1:0; - else - val4 = battle_config.monster_cloak_check_type&2?1:0; - } - break; - case SC_SIGHT: /* サイト/ルアフ */ - case SC_RUWACH: - case SC_SIGHTBLASTER: - if (flag&4) - break; - val2 = tick/250; - tick = 10; - break; - - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_BROKENWEAPON: - case SC_BROKENARMOR: - case SC_READYSTORM: // Taekwon stances SCs [Dralnu] - case SC_READYDOWN: - case SC_READYCOUNTER: - case SC_READYTURN: - case SC_DODGE: - if (flag&4) - break; - tick = 600*1000; - break; - - case SC_AUTOGUARD: - if (!flag) - { - struct map_session_data *tsd; - int i,t; - for(i=val2=0;i>1); - val2 += (t < 0)? 1:t; - } - if (sd) - for (i = 0; i < 5; i++) - { //Pass the status to the other affected chars. [Skotlex] - if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) - status_change_start(&tsd->bl,SC_AUTOGUARD,10000,val1,val2,0,0,tick,1); - } - } - break; - - case SC_DEFENDER: - calc_flag = 1; - if (!flag) - { - struct map_session_data *tsd; - int i; - val2 = 5 + val1*15; - if (sd) - for (i = 0; i < 5; i++) - { //See if there are devoted characters, and pass the status to them. [Skotlex] - if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) - status_change_start(&tsd->bl,SC_DEFENDER,10000,val1,5+val1*5,0,0,tick,1); - } - } - break; - - case SC_TENSIONRELAX: /* テンションリラックス */ - if (flag&4) - break; - if(bl->type == BL_PC) { - tick = 10000; - } else return 0; - break; - - case SC_PARRYING: /* パリイング */ - val2 = 20 + val1*3; - break; - - case SC_WINDWALK: /* ウインドウォ?ク */ - calc_flag = 1; - val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5, movement speed % increase is 4 times that - break; - - case SC_JOINTBEAT: // Random break [DracoRPG] - calc_flag = 1; - val2 = rand()%6; - if (val2 == 5) sc_start(bl,SC_BLEEDING,100,val1,skill_get_time2(StatusSkillChangeTable[type],val1)); - break; - - case SC_BERSERK: /* バ?サ?ク */ - if (sc->data[SC_ENDURE].timer == -1 || !sc->data[SC_ENDURE].val4) - sc_start4(bl, SC_ENDURE, 100,10,0,0,1, tick); - if(sd && !(flag&4)){ - sd->status.hp = sd->status.max_hp * 3; - sd->status.sp = 0; - clif_updatestatus(sd,SP_HP); - clif_updatestatus(sd,SP_SP); - sd->canregen_tick = gettick() + 300000; - } - if (!(flag&4)) - tick = 10000; - calc_flag = 1; - break; - - case SC_GOSPEL: - if (val4 == BCT_SELF) { // self effect - if (flag&4) - break; - val2 = tick; - tick = 1000; - status_change_clear_buffs(bl,3); //Remove buffs/debuffs - } else - calc_flag = 1; - break; - - case SC_MARIONETTE: /* マリオネットコントロ?ル */ - case SC_MARIONETTE2: - if (flag&4) - break; - val2 = tick; - if (!val3) - return 0; - tick = 1000; - calc_flag = 1; - break; - - case SC_REJECTSWORD: /* リジェクトソ?ド */ - val2 = 3; //3回攻?を跳ね返す - break; - - case SC_MEMORIZE: /* メモライズ */ - val2 = 5; //回詠唱を1/3にする - break; - - case SC_GRAVITATION: - if (val3 == BCT_SELF) { - struct unit_data *ud = unit_bl2ud(bl); - if (ud) { - ud->canmove_tick += tick; - ud->canact_tick += tick; - } - } else - calc_flag = 1; - break; - - case SC_HERMODE: - status_change_clear_buffs(bl,1); - break; - - case SC_REGENERATION: - val1 = 2; - case SC_BATTLEORDERS: - if (!(flag&4)) - tick = 60000; // 1 minute - calc_flag = 1; - break; - case SC_GUILDAURA: - calc_flag = 1; - if (!(flag&4)) - tick = 1000; - break; - - case SC_DEVOTION: /* ディボ?ション */ - { - struct map_session_data *src; - if ((src = map_id2sd(val1)) && src->sc.count) - { //Try to inherit the status from the Crusader [Skotlex] - //Ideally, we should calculate the remaining time and use that, but we'll trust that - //once the Crusader's status changes, it will reflect on the others. - int type2 = SC_AUTOGUARD; - if (src->sc.data[type2].timer != -1) - sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); - type2 = SC_DEFENDER; - if (src->sc.data[type2].timer != -1) - sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); - type2 = SC_REFLECTSHIELD; - if (src->sc.data[type2].timer != -1) - sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); - - } - break; - } - - case SC_COMA: //Coma. Sends a char to 1HP - battle_damage(NULL, bl, status_get_hp(bl)-1, 0, 1); - return 1; - - case SC_CLOSECONFINE2: - { - struct block_list *src = val2?map_id2bl(val2):NULL; - struct status_change *sc2 = src?status_get_sc(src):NULL; - if (src && sc2) { - if (sc2->data[SC_CLOSECONFINE].timer == -1) //Start lock on caster. - sc_start4(src,SC_CLOSECONFINE,100,sc->data[type].val1,1,0,0,tick+1000); - else { //Increase count of locked enemies and refresh time. - sc2->data[SC_CLOSECONFINE].val2++; - delete_timer(sc2->data[SC_CLOSECONFINE].timer, status_change_timer); - sc2->data[SC_CLOSECONFINE].timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE); - } - } else //Status failed. - return 0; - } - break; - case SC_KAITE: - val2 = 1+val1/5; //Number of bounces: 1 + skilllv/5 - break; - case SC_KAUPE: - if (flag&4) - break; //Do nothing when loading. - switch (val1) { - case 3: //33*3 + 1 -> 100% - val2++; - case 1: - case 2: //33, 66% - val2 += 33*val1; - val3 = 1; //Dodge 1 attack total. - break; - default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex] - val2 = 100; - val3 = val1-2; - break; - } - break; - - case SC_COMBO: - { - struct unit_data *ud = unit_bl2ud(bl); - switch (val1) { //Val1 contains the skill id - case TK_STORMKICK: - clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1); - break; - case TK_DOWNKICK: - clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1); - break; - case TK_TURNKICK: - clif_skill_nodamage(bl,bl,TK_READYTURN,1,1); - break; - case TK_COUNTER: - clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1); - break; - } - if (ud) { - ud->attackabletime = gettick()+tick; - unit_set_walkdelay(bl, gettick(), tick, 1); - } - } - break; - case SC_TKREST: - val2 = 11-val1; //Chance to consume: 11-skilllv% - break; - case SC_RUN: - if (!(flag&4)) - val4 = gettick(); //Store time at which you started running. - calc_flag = 1; - break; - case SC_KAAHI: - if(flag&4) { - val4 = -1; - break; - } - val2 = 200*val1; //HP heal - val3 = 5*val1; //SP cost - val4 = -1; //Kaahi Timer. - break; - case SC_BLESSING: - if ((!undead_flag && race!=RC_DEMON) || bl->type == BL_PC) - val2 = val1; - else - val2 = 0; //0 -> Half stat. - calc_flag = 1; - break; - case SC_TRICKDEAD: /* 死んだふり */ - { - struct view_data *vd = status_get_viewdata(bl); - if (vd) vd->dead_sit = 1; - break; - } - case SC_CONCENTRATION: /* コンセントレ?ション */case SC_ETERNALCHAOS: /* エタ?ナルカオス */ - case SC_DRUMBATTLE: /* ?太鼓の響き */ - case SC_NIBELUNGEN: /* ニ?ベルングの指輪 */ - case SC_SIEGFRIED: /* 不死身のジ?クフリ?ド */ - case SC_WHISTLE: /* 口笛 */ - case SC_ASSNCROS: /* 夕陽のアサシンクロス */ - case SC_APPLEIDUN: /* イドゥンの林檎 */ - case SC_HUMMING: /* ハミング */ - case SC_ATKPOTION: // Valaris - case SC_MATKPOTION: - case SC_FORTUNE: /* 幸運のキス */ - case SC_SERVICE4U: /* サ?ビスフォ?ユ? */ - case SC_ADRENALINE2: - case SC_ADRENALINE: /* アドレナリンラッシュ */ - case SC_BLIND: /* 暗? */ - case SC_CURSE: - case SC_CONCENTRATE: /* 集中力向上 */ - case SC_ANGELUS: /* アンゼルス */ - case SC_IMPOSITIO: /* インポシティオマヌス */ - case SC_GLORIA: /* グロリア */ - case SC_LOUD: /* ラウドボイス */ - case SC_KEEPING: - case SC_BARRIER: - case SC_MELTDOWN: /* メルトダウン */ - case SC_TRUESIGHT: /* トゥル?サイト */ - case SC_SPIDERWEB: /* スパイダ?ウェッブ */ - case SC_SLOWDOWN: - case SC_SPEEDUP0: - case SC_SPEEDUP1: - case SC_INCALLSTATUS: - case SC_INCHIT: /* HIT上昇 */ - case SC_INCHITRATE: /* HIT%上昇 */ - case SC_INCFLEE: /* FLEE上昇 */ - case SC_INCFLEERATE: /* FLEE%上昇 */ - case SC_INCMHPRATE: /* MHP%上昇 */ - case SC_INCMSPRATE: /* MSP%上昇 */ - case SC_INCATKRATE: /* ATK%上昇 */ - case SC_INCMATKRATE: - case SC_INCDEFRATE: - case SC_INCSTR: - case SC_INCAGI: - case SC_INCVIT: - case SC_INCINT: - case SC_INCDEX: - case SC_INCLUK: - case SC_STRFOOD: - case SC_AGIFOOD: - case SC_VITFOOD: - case SC_INTFOOD: - case SC_DEXFOOD: - case SC_LUKFOOD: - case SC_FLEEFOOD: - case SC_HITFOOD: - case SC_BATKFOOD: - case SC_WATKFOOD: - case SC_MATKFOOD: - case SC_SPURT: - case SC_SPIRIT: - case SC_SUN_COMFORT: - case SC_MOON_COMFORT: - case SC_STAR_COMFORT: - case SC_FUSION: - case SC_SKE: - case SC_SWOO: // [marquis007] - case SC_STEELBODY: // 金剛 - case SC_SKA: - case SC_TWOHANDQUICKEN: /* 2HQ */ - case SC_MIRACLE: - case SC_INCREASEAGI: /* 速度上昇 */ - case SC_DECREASEAGI: /* 速度減少 */ - case SC_ONEHAND: - case SC_DONTFORGETME: /* 私を忘れないで */ - case SC_DELUGE: - case SC_CARTBOOST: /* カ?トブ?スト */ - case SC_QUAGMIRE: /* クァグマイア */ - case SC_KNOWLEDGE: - calc_flag = 1; - break; - - case SC_LULLABY: /* 子守唄 */ - case SC_RICHMANKIM: - case SC_ROKISWEIL: /* ロキの叫び */ - case SC_INTOABYSS: /* 深淵の中に */ - case SC_POEMBRAGI: /* ブラギの詩 */ - case SC_UGLYDANCE: /* 自分勝手なダンス */ - case SC_WEAPONPERFECTION: /* ウェポンパ?フェクション */ - case SC_FREEZE: /* 凍結 */ - case SC_STUN: /* スタン(val2にミリ秒セット) */ - case SC_ENERGYCOAT: /* エナジ?コ?ト */ - case SC_SAFETYWALL: - case SC_OVERTHRUST: /* オ?バ?スラスト */ - case SC_SLOWPOISON: //Slow potion can be activated even if not poisoned. - case SC_SUFFRAGIUM: /* サフラギム */ - case SC_BENEDICTIO: /* 聖? */ - case SC_MAGNIFICAT: /* マグニフィカ?ト */ - case SC_AETERNA: /* エ?テルナ */ - case SC_STRIPARMOR: - case SC_STRIPHELM: - case SC_CP_WEAPON: - case SC_CP_SHIELD: - case SC_CP_ARMOR: - case SC_CP_HELM: - case SC_EXTREMITYFIST: /* 阿修羅覇凰拳 */ - case SC_ANKLE: /* アンクル */ - case SC_BLADESTOP_WAIT: /* 白刃取り(待ち) */ - case SC_HALLUCINATION: - case SC_SPLASHER: /* ベナムスプラッシャ? */ - case SC_FOGWALL: - case SC_PRESERVE: - case SC_DOUBLECAST: - case SC_AURABLADE: /* オ?ラブレ?ド */ - case SC_BABY: - case SC_WATK_ELEMENT: - case SC_ARMOR_ELEMENT: - case SC_LONGING: - case SC_ORCISH: - case SC_SHRINK: - case SC_WINKCHARM: - case SC_SCRESIST: - case SC_STOP: - case SC_CLOSECONFINE: - case SC_SKILLRATE_UP: - case SC_KAIZEL: - case SC_INTRAVISION: - case SC_BASILICA: - case SC_MAXOVERTHRUST: - case SC_SILENCE: /* 沈?(レックスデビ?ナ) */ - case SC_ASSUMPTIO: /* アスムプティオ */ - case SC_SLEEP: - case SC_SMA: - case SC_WARM: - case SC_BLADESTOP: - break; - // gs_something1 [Vicious] - case SC_MADNESSCANCEL: - case SC_ADJUSTMENT: - case SC_INCREASING: - case SC_GATLINGFEVER: - case SC_TATAMIGAESHI: - case SC_KAENSIN: - calc_flag = 1; - break; - case SC_UTSUSEMI: - case SC_NEN: - break; - - - default: - if(battle_config.error_log) - ShowError("UnknownStatusChange [%d]\n", type); - return 0; - } - - //Those that make you stop attacking/walking.... - switch (type) { - case SC_FREEZE: - case SC_STUN: - case SC_SLEEP: - case SC_STONE: - if (sd && pc_issit(sd)) //Avoid sprite sync problems. - pc_setstand(sd); - case SC_TRICKDEAD: - unit_stop_attack(bl); - skill_stop_dancing(bl); /* 演奏/ダンスの中? */ - // Cancel cast when get status [LuzZza] - if (battle_config.sc_castcancel) - unit_skillcastcancel(bl, 0); - case SC_STOP: - case SC_CONFUSION: - case SC_CLOSECONFINE: - case SC_CLOSECONFINE2: - case SC_ANKLE: - case SC_SPIDERWEB: - case SC_MADNESSCANCEL: - unit_stop_walking(bl,1); - break; - case SC_HIDING: - case SC_CLOAKING: - case SC_CHASEWALK: - unit_stop_attack(bl); /* 攻?停止 */ - break; - } - - - if (bl->type == BL_PC) - { - if (flag&4) - clif_status_load(bl,StatusIconChangeTable[type],1); //Sending to owner since they aren't in the map yet. [Skotlex] - clif_status_change(bl,StatusIconChangeTable[type],1); - } - - // Set option as needed. - opt_flag = 1; - switch(type){ - //OPT1 - case SC_STONE: - case SC_FREEZE: - case SC_STUN: - case SC_SLEEP: - if(type == SC_STONE) - sc->opt1 = OPT1_STONEWAIT; - else - sc->opt1 = OPT1_STONE + (type - SC_STONE); - break; - //OPT2 - case SC_POISON: - case SC_CURSE: - case SC_SILENCE: - case SC_BLIND: - sc->opt2 |= 1<<(type-SC_POISON); - break; - case SC_DPOISON: // 暫定で毒のエフェクトを使用 - sc->opt2 |= OPT2_DPOISON; - break; - case SC_SIGNUMCRUCIS: - sc->opt2 |= OPT2_SIGNUMCRUCIS; - break; - //OPT3 - case SC_TWOHANDQUICKEN: /* 2HQ */ - case SC_SPEARQUICKEN: /* スピアクイッケン */ - case SC_CONCENTRATION: /* コンセントレ?ション */ - sc->opt3 |= 1; - opt_flag = 0; - break; - case SC_MAXOVERTHRUST: - case SC_OVERTHRUST: /* オ?バ?スラスト */ - case SC_SWOO: //Why does it shares the same opt as Overthrust? Perhaps we'll never know... - sc->opt3 |= 2; - opt_flag = 0; - break; - case SC_ENERGYCOAT: /* エナジ?コ?ト */ - sc->opt3 |= 4; - opt_flag = 0; - break; - case SC_INCATKRATE: /* ATK%上昇 */ - //Simulate Explosion Spirits effect for NPC_POWERUP [Skotlex] - if (bl->type != BL_MOB) { - opt_flag = 0; - break; - } - case SC_EXPLOSIONSPIRITS: // 爆裂波動 - sc->opt3 |= 8; - opt_flag = 0; - break; - case SC_STEELBODY: // 金剛 - case SC_SKA: - sc->opt3 |= 16; - opt_flag = 0; - break; - case SC_BLADESTOP: /* 白刃取り */ - sc->opt3 |= 32; - opt_flag = 0; - break; - case SC_BERSERK: /* バ?サ?ク */ - sc->opt3 |= 128; - opt_flag = 0; - break; - case SC_MARIONETTE: /* マリオネットコントロ?ル */ - case SC_MARIONETTE2: - sc->opt3 |= 1024; - opt_flag = 0; - break; - case SC_ASSUMPTIO: /* アスムプティオ */ - sc->opt3 |= 2048; - opt_flag = 0; - break; - case SC_WARM: //SG skills [Komurka] - sc->opt3 |= 4096; - opt_flag = 0; - break; - - //OPTION - case SC_HIDING: - sc->option |= OPTION_HIDE; - break; - case SC_CLOAKING: - sc->option |= OPTION_CLOAK; - break; - case SC_CHASEWALK: - sc->option |= OPTION_CHASEWALK|OPTION_CLOAK; - break; - case SC_SIGHT: - sc->option |= OPTION_SIGHT; - break; - case SC_RUWACH: - sc->option |= OPTION_RUWACH; - break; - case SC_WEDDING: - sc->option |= OPTION_WEDDING; - break; - case SC_ORCISH: - sc->option |= OPTION_ORCISH; - break; - case SC_SIGHTTRASHER: - sc->option |= OPTION_SIGHTTRASHER; - break; - case SC_FUSION: - sc->option |= OPTION_FLYING; - break; - default: - opt_flag = 0; - } - - if(opt_flag) /* optionの?更 */ - clif_changeoption(bl); - - (sc->count)++; /* ステ?タス異常の? */ - - sc->data[type].val1 = val1; - sc->data[type].val2 = val2; - sc->data[type].val3 = val3; - sc->data[type].val4 = val4; - /* タイマ?設定 */ - sc->data[type].timer = add_timer( - gettick() + tick, status_change_timer, bl->id, type); - - if(sd) { - if (calc_flag) - status_calc_pc(sd,0); /* ステ?タス再計算 */ - if(save_flag) - chrif_save(sd,0); // save the player status - if(updateflag) - clif_updatestatus(sd,updateflag); /* ステ?タスをクライアントに送る */ - if (sd->pd) - pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing - } - - if (type==SC_RUN) { - struct unit_data *ud = unit_bl2ud(bl); - if (ud) - ud->state.running = unit_run(bl); - } - return 1; -} -/*========================================== - * ステータス異常全解除 - *------------------------------------------ - */ -int status_change_clear(struct block_list *bl,int type) -{ - struct status_change* sc; - int i; - - sc = status_get_sc(bl); - - if (!sc || sc->count == 0) - return 0; - for(i = 0; i < SC_MAX; i++) - { - //Type 0: PC killed -> Place here stats that do not dispel on death. - if(sc->data[i].timer == -1 || - (type == 0 && ( - i == SC_EDP || i == SC_MELTDOWN || i == SC_XMAS || i == SC_NOCHAT || - i == SC_FUSION || i == SC_TKREST || i == SC_READYSTORM || - i == SC_READYDOWN || i == SC_READYCOUNTER || i == SC_READYTURN - ))) - continue; - - status_change_end(bl, i, -1); - - if (type == 1 && sc->data[i].timer != -1) - { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex] - (sc->count)--; - delete_timer(sc->data[i].timer, status_change_timer); - sc->data[i].timer = -1; - } - } - sc->opt1 = 0; - sc->opt2 = 0; - sc->opt3 = 0; - sc->option &= OPTION_MASK; - - if(!type || type&2) - clif_changeoption(bl); - - return 1; -} - -/*========================================== - * ステータス異常終了 - *------------------------------------------ - */ -int status_change_end( struct block_list* bl , int type,int tid ) -{ - struct map_session_data *sd; - struct status_change *sc; - int opt_flag=0, calc_flag = 0; - - nullpo_retr(0, bl); - - sc = status_get_sc(bl); - if(!sc) { - if(battle_config.error_log) - ShowError("status_change_end: BL type %d doesn't has sc data!\n", bl->type); - return 0; - } - - if(type < 0 || type >= SC_MAX) - return 0; - - sd = bl->type==BL_PC?(struct map_session_data *)bl:NULL; - - if (sc->data[type].timer != -1 && (sc->data[type].timer == tid || tid == -1)) { - - if (tid == -1) // タイマから呼ばれていないならタイマ削除をする - delete_timer(sc->data[type].timer,status_change_timer); - - /* 該?の異常を正常に?す */ - sc->data[type].timer=-1; - (sc->count)--; - - switch(type){ /* 異常の種類ごとの?理 */ - case SC_PROVOKE: /* プロボック */ - case SC_CONCENTRATE: /* 集中力向上 */ - case SC_BLESSING: /* ブレッシング */ - case SC_ANGELUS: /* アンゼルス */ - case SC_INCREASEAGI: /* 速度上昇 */ - case SC_DECREASEAGI: /* 速度減少 */ - case SC_SIGNUMCRUCIS: /* シグナムクルシス */ - case SC_HIDING: - case SC_ONEHAND: - case SC_TWOHANDQUICKEN: /* 2HQ */ - case SC_ADRENALINE2: - case SC_ADRENALINE: /* アドレナリンラッシュ */ - case SC_ENCPOISON: /* エンチャントポイズン */ - case SC_IMPOSITIO: /* インポシティオマヌス */ - case SC_GLORIA: /* グロリア */ - case SC_LOUD: /* ラウドボイス */ - case SC_QUAGMIRE: /* クァグマイア */ - case SC_PROVIDENCE: /* プロヴィデンス */ - case SC_SPEARQUICKEN: /* スピアクイッケン */ - case SC_VOLCANO: - case SC_DELUGE: - case SC_VIOLENTGALE: - case SC_ETERNALCHAOS: /* エタ?ナルカオス */ - case SC_DRUMBATTLE: /* ?太鼓の響き */ - case SC_NIBELUNGEN: /* ニ?ベルングの指輪 */ - case SC_SIEGFRIED: /* 不死身のジ?クフリ?ド */ - case SC_WHISTLE: /* 口笛 */ - case SC_ASSNCROS: /* 夕陽のアサシンクロス */ - case SC_HUMMING: /* ハミング */ - case SC_DONTFORGETME: /* 私を忘れないで */ - case SC_FORTUNE: /* 幸運のキス */ - case SC_SERVICE4U: /* サ?ビスフォ?ユ? */ - case SC_EXPLOSIONSPIRITS: // 爆裂波動 - case SC_STEELBODY: // 金剛 - case SC_APPLEIDUN: /* イドゥンの林檎 */ - case SC_BLADESTOP_WAIT: - case SC_CONCENTRATION: /* コンセントレ?ション */ - case SC_ASSUMPTIO: /* アシャンプティオ */ - case SC_WINDWALK: /* ウインドウォ?ク */ - case SC_TRUESIGHT: /* トゥル?サイト */ - case SC_SPIDERWEB: /* スパイダ?ウェッブ */ - case SC_MAGICPOWER: /* 魔法力?幅 */ - case SC_CHASEWALK: - case SC_ATKPOTION: // [Valaris] - case SC_MATKPOTION: // [Valaris] - case SC_MELTDOWN: /* メルトダウン */ - case SC_CARTBOOST: - case SC_MINDBREAKER: /* マインドブレーカー */ - case SC_EDP: // Celest - case SC_SLOWDOWN: - case SC_ASPDPOTION0: /* ?速ポ?ション */ - case SC_ASPDPOTION1: - case SC_ASPDPOTION2: - case SC_ASPDPOTION3: - case SC_SPEEDUP0: - case SC_SPEEDUP1: - case SC_INCALLSTATUS: - case SC_INCHIT: /* HIT上昇 */ - case SC_INCHITRATE: /* HIT%上昇 */ - case SC_INCFLEE: /* FLEE上昇 */ - case SC_INCFLEERATE: /* FLEE%上昇 */ - case SC_INCMHPRATE: /* MHP%上昇 */ - case SC_INCMSPRATE: /* MSP%上昇 */ - case SC_INCATKRATE: /* ATK%上昇 */ - case SC_INCMATKRATE: - case SC_INCDEFRATE: - case SC_INCSTR: - case SC_INCAGI: - case SC_INCVIT: - case SC_INCINT: - case SC_INCDEX: - case SC_INCLUK: - case SC_STRFOOD: - case SC_AGIFOOD: - case SC_VITFOOD: - case SC_INTFOOD: - case SC_DEXFOOD: - case SC_LUKFOOD: - case SC_FLEEFOOD: - case SC_HITFOOD: - case SC_BATKFOOD: - case SC_WATKFOOD: - case SC_MATKFOOD: - case SC_BATTLEORDERS: - case SC_REGENERATION: - case SC_GUILDAURA: - case SC_SPURT: - case SC_SPIRIT: - case SC_SUN_COMFORT: - case SC_MOON_COMFORT: - case SC_STAR_COMFORT: - case SC_FUSION: - case SC_SKE: - case SC_SWOO: // [marquis007] - case SC_SKA: // [marquis007] - case SC_KNOWLEDGE: - case SC_KEEPING: - case SC_BARRIER: - calc_flag = 1; - break; - - case SC_ENDURE: // celest - calc_flag = (sc->data[type].val4==0); - break; - - case SC_WEDDING: //結婚用(結婚衣裳になって?くのが?いとか) - case SC_XMAS: - { - struct view_data *vd = status_get_viewdata(bl); - if (vd) { - if (sd) { - //Load data from sd->status.* as the stored values could have changed. - status_set_viewdata(bl, sd->status.class_); - } else { - vd->class_ = sc->data[type].val1; - vd->weapon = sc->data[type].val2; - vd->shield = sc->data[type].val3; - vd->cloth_color = sc->data[type].val4; - } - clif_changelook(bl,LOOK_BASE,vd->class_); - clif_changelook(bl,LOOK_WEAPON,vd->weapon); - clif_changelook(bl,LOOK_SHIELD,vd->shield); - clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); - } - } - break; - case SC_RUN://駆け足 - { - struct unit_data *ud = unit_bl2ud(bl); - if (ud) { - ud->state.running = 0; - if (ud->walktimer != -1) - unit_stop_walking(bl,1); - } - if (sc->data[type].val1 >= 7 && - DIFF_TICK(gettick(), sc->data[type].val4) <= 1000 && - (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0)) - ) - sc_start(bl,SC_SPURT,100,sc->data[type].val1,skill_get_time2(StatusSkillChangeTable[type], sc->data[type].val1)); - calc_flag = 1; - } - break; - case SC_AUTOBERSERK: - if (sc->data[SC_PROVOKE].timer != -1 && sc->data[SC_PROVOKE].val2 == 1) - status_change_end(bl,SC_PROVOKE,-1); - break; - - case SC_DEFENDER: - calc_flag = 1; - case SC_REFLECTSHIELD: - case SC_AUTOGUARD: - if (sd) { - struct map_session_data *tsd; - int i; - for (i = 0; i < 5; i++) - { //Clear the status from the others too [Skotlex] - if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type].timer != -1) - status_change_end(&tsd->bl,type,-1); - } - } - break; - case SC_DEVOTION: /* ディボ?ション */ - { - struct map_session_data *md = map_id2sd(sc->data[type].val1); - //The status could have changed because the Crusader left the game. [Skotlex] - if (md) - { - md->devotion[sc->data[type].val2] = 0; - clif_devotion(md); - } - //Remove AutoGuard and Defender [Skotlex] - if (sc->data[SC_AUTOGUARD].timer != -1) - status_change_end(bl,SC_AUTOGUARD,-1); - if (sc->data[SC_DEFENDER].timer != -1) - status_change_end(bl,SC_DEFENDER,-1); - if (sc->data[SC_REFLECTSHIELD].timer != -1) - status_change_end(bl,SC_REFLECTSHIELD,-1); - } - break; - case SC_BLADESTOP: - if(sc->data[type].val4) - { - struct block_list *tbl = (struct block_list *)sc->data[type].val4; - struct status_change *tsc = status_get_sc(tbl); - sc->data[type].val4 = 0; - if(tsc && tsc->data[SC_BLADESTOP].timer!=-1) - { - tsc->data[SC_BLADESTOP].val4 = 0; - status_change_end(tbl,SC_BLADESTOP,-1); - } - clif_bladestop(bl,tbl,0); - } - break; - case SC_DANCING: - { - struct map_session_data *dsd; - struct status_change *dsc; - if(sc->data[type].val2) - { - skill_delunitgroup(bl, (struct skill_unit_group *)sc->data[type].val2); - sc->data[type].val2 = 0; - } - if(sc->data[type].val4 && sc->data[type].val4 != BCT_SELF && (dsd=map_id2sd(sc->data[type].val4))){ - dsc = &dsd->sc; - //合奏で相手がいる場合相手のval4を0にする - if(dsc && dsc->data[type].timer!=-1) - { - dsc->data[type].val2 = dsc->data[type].val4 = 0; //This will prevent recursive loops. - status_change_end(&dsd->bl, type, -1); - } - } - if(sc->data[type].val1 == CG_MOONLIT) //Only dance that doesn't has ground tiles... [Skotlex] - status_change_end(bl, SC_MOONLIT, -1); - } - if (sc->data[SC_LONGING].timer!=-1) - status_change_end(bl,SC_LONGING,-1); - calc_flag = 1; - break; - case SC_NOCHAT: //チャット禁止?態 - if (sd) { - if(battle_config.manner_system){ - //Why set it to 0? Can't we use good manners for something? [Skotlex] -// if (sd->status.manner >= 0) // weeee ^^ [celest] -// sd->status.manner = 0; - clif_updatestatus(sd,SP_MANNER); - } - } - break; - case SC_SPLASHER: /* ベナムスプラッシャ? */ - { - struct block_list *src=map_id2bl(sc->data[type].val3); - if(src && tid!=-1){ - //自分にダメ?ジ&周?3*3にダメ?ジ - skill_castend_damage_id(src, bl,sc->data[type].val2,sc->data[type].val1,gettick(),0 ); - } - } - break; - case SC_CLOSECONFINE2: - { - struct block_list *src = sc->data[type].val2?map_id2bl(sc->data[type].val2):NULL; - struct status_change *sc2 = src?status_get_sc(src):NULL; - if (src && sc2 && sc2->count) { - if (sc2->data[SC_CLOSECONFINE].timer != -1) //If status was already ended, do nothing. - { //Decrease count - if (--sc2->data[SC_CLOSECONFINE].val1 <= 0) //No more holds, free him up. - status_change_end(src, SC_CLOSECONFINE, -1); - } - } - } - break; - case SC_CLOSECONFINE: - if (sc->data[type].val2 > 0) { //Caster has been unlocked... nearby chars need to be unlocked. - int range = 1 - +skill_get_range2(bl, StatusSkillChangeTable[type], sc->data[type].val1) - +skill_get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold.... - map_foreachinarea(status_change_timer_sub, - bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sc,type,gettick()); - } - break; - /* option1 */ - case SC_FREEZE: - sc->data[type].val3 = 0; - break; - - /* option2 */ - case SC_POISON: /* 毒 */ - case SC_BLIND: /* 暗? */ - case SC_CURSE: - calc_flag = 1; - break; - - case SC_MARIONETTE: /* マリオネットコントロ?ル */ - case SC_MARIONETTE2: /// Marionette target - { - // check for partner and end their marionette status as well - int type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE; - struct block_list *pbl = map_id2bl(sc->data[type].val3); - struct status_change* sc2 = pbl?status_get_sc(pbl):NULL; - if (pbl && sc2 && sc2->count && sc2->data[type2].timer != -1) - status_change_end(pbl, type2, -1); - if (type == SC_MARIONETTE) - clif_marionette(bl, 0); - calc_flag = 1; - } - break; - - case SC_BERSERK: //val4 indicates if the skill was dispelled. [Skotlex] - if (sd && sd->status.hp > 100 && !sc->data[type].val4) { - sd->status.hp = 100; - clif_updatestatus(sd,SP_HP); - } - if(sc->data[SC_ENDURE].timer != -1) - status_change_end(bl, SC_ENDURE, -1); - calc_flag = 1; - break; - case SC_GRAVITATION: - if (sc->data[type].val3 == BCT_SELF) { - struct unit_data *ud = unit_bl2ud(bl); - if (ud) - ud->canmove_tick = ud->canact_tick = gettick(); - } else - calc_flag = 1; - - case SC_GOSPEL: //Clear the buffs from other chars. - if(sc->data[type].val4 != BCT_SELF) - calc_flag = 1; - else if (sc->data[type].val3) { //Clear the group. - struct skill_unit_group *group = (struct skill_unit_group *)sc->data[type].val3; - sc->data[type].val3 = 0; - skill_delunitgroup(bl, group); - } - break; - case SC_HERMODE: - case SC_BASILICA: //Clear the skill area. [Skotlex] - if(sc->data[type].val3 == BCT_SELF) - skill_clear_unitgroup(bl); - break; - case SC_MOONLIT: //Clear the unit effect. [Skotlex] - skill_setmapcell(bl,CG_MOONLIT, sc->data[SC_MOONLIT].val1, CELL_CLRMOONLIT); - break; - case SC_TRICKDEAD: /* 死んだふり */ - { - struct view_data *vd = status_get_viewdata(bl); - if (vd) vd->dead_sit = 0; - break; - } - case SC_WARM: - if (sc->data[type].val4) { //Clear the group. - struct skill_unit_group *group = (struct skill_unit_group *)sc->data[type].val4; - sc->data[type].val4 = 0; - skill_delunitgroup(bl, group); - } - break; - case SC_KAAHI: - //Delete timer if it exists. - if (sc->data[type].val4 != -1) { - delete_timer(sc->data[type].val4,kaahi_heal_timer); - sc->data[type].val4=-1; - } - break; - case SC_COMBO: //Clear last used skill when it is part of a combo. - if (sd && sd->skillid_old == sc->data[type].val1) - sd->skillid_old = sd->skilllv_old = 0; - break; - //gs_something2 [Vicious] - case SC_MADNESSCANCEL: - case SC_ADJUSTMENT: - case SC_INCREASING: - case SC_GATLINGFEVER: - case SC_TATAMIGAESHI: - case SC_KAENSIN: - case SC_SUITON: - calc_flag = 1; - break; - case SC_UTSUSEMI: - case SC_NEN: - break; - } - - - if (sd) - clif_status_change(bl,StatusIconChangeTable[type],0); - - switch(type){ /* 正常に?るときなにか?理が必要 */ - case SC_STONE: - case SC_FREEZE: - case SC_STUN: - case SC_SLEEP: - sc->opt1 = 0; - opt_flag = 1; - break; - - case SC_POISON: - case SC_CURSE: - case SC_SILENCE: - case SC_BLIND: - sc->opt2 &= ~(1<<(type-SC_POISON)); - opt_flag = 1; - break; - case SC_DPOISON: - sc->opt2 &= ~OPT2_DPOISON; // 毒?態解除 - opt_flag = 1; - break; - case SC_SIGNUMCRUCIS: - sc->opt2 &= ~OPT2_SIGNUMCRUCIS; - opt_flag = 1; - break; - - case SC_HIDING: - sc->option &= ~OPTION_HIDE; - opt_flag = 1 ; - break; - case SC_CLOAKING: - sc->option &= ~OPTION_CLOAK; - calc_flag = 1; // orn - opt_flag = 1 ; - break; - case SC_CHASEWALK: - sc->option &= ~(OPTION_CHASEWALK|OPTION_CLOAK); - opt_flag = 1 ; - break; - case SC_SIGHT: - sc->option &= ~OPTION_SIGHT; - opt_flag = 1; - break; - case SC_WEDDING: //結婚用(結婚衣裳になって?くのが?いとか) - sc->option &= ~OPTION_WEDDING; - opt_flag = 1; - break; - case SC_ORCISH: - sc->option &= ~OPTION_ORCISH; - opt_flag = 1; - break; - case SC_RUWACH: - sc->option &= ~OPTION_RUWACH; - opt_flag = 1; - break; - case SC_SIGHTTRASHER: - sc->option &= ~OPTION_SIGHTTRASHER; - opt_flag = 1; - break; - case SC_FUSION: - sc->option &= ~OPTION_FLYING; - opt_flag = 1; - break; - //opt3 - case SC_TWOHANDQUICKEN: /* 2HQ */ - case SC_ONEHAND: /* 1HQ */ - case SC_SPEARQUICKEN: /* スピアクイッケン */ - case SC_CONCENTRATION: /* コンセントレ?ション */ - sc->opt3 &= ~1; - break; - case SC_OVERTHRUST: /* オ?バ?スラスト */ - case SC_MAXOVERTHRUST: - case SC_SWOO: - sc->opt3 &= ~2; - break; - case SC_ENERGYCOAT: /* エナジ?コ?ト */ - sc->opt3 &= ~4; - break; - case SC_INCATKRATE: //Simulated Explosion spirits effect. - if (bl->type != BL_MOB) - break; - case SC_EXPLOSIONSPIRITS: // 爆裂波動 - sc->opt3 &= ~8; - break; - case SC_STEELBODY: // 金剛 - case SC_SKA: - sc->opt3 &= ~16; - break; - case SC_BLADESTOP: /* 白刃取り */ - sc->opt3 &= ~32; - break; - case SC_BERSERK: /* バ?サ?ク */ - sc->opt3 &= ~128; - break; - case SC_MARIONETTE: /* マリオネットコントロ?ル */ - case SC_MARIONETTE2: - sc->opt3 &= ~1024; - break; - case SC_ASSUMPTIO: /* アスムプティオ */ - sc->opt3 &= ~2048; - break; - case SC_WARM: //SG skills [Komurka] - sc->opt3 &= ~4096; - break; - } - - if(opt_flag) /* optionの?更を?える */ - clif_changeoption(bl); - - if (sd && calc_flag) - status_calc_pc(sd,0); - } - - return 1; -} - -int kaahi_heal_timer(int tid, unsigned int tick, int id, int data) -{ - struct block_list *bl; - struct status_change *sc; - int hp; - - bl=map_id2bl(id); - sc=status_get_sc(bl); - if (!sc || data != SC_KAAHI || sc->data[data].timer==-1) - return 0; - if(sc->data[data].val4 != tid) { - if (battle_config.error_log) - ShowError("kaahi_heal_timer: Timer mismatch: %d != %d\n", tid, sc->data[data].val4); - sc->data[data].val4=-1; - return 0; - } - - if (bl->type == BL_PC && ((TBL_PC*)bl)->status.sp < sc->data[data].val3) { - sc->data[data].val4=-1; - return 0; - } - - hp = status_get_max_hp(bl) - status_get_hp(bl); - if (hp > sc->data[data].val2) - hp = sc->data[data].val2; - if (hp) { - battle_heal(bl, bl, hp, -sc->data[data].val3, 1); - clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); - } - sc->data[data].val4=-1; - return 1; -} - -/*========================================== - * ステータス異常終了タイマー - *------------------------------------------ - */ -int status_change_timer(int tid, unsigned int tick, int id, int data) -{ - int type = data; - struct block_list *bl; - struct map_session_data *sd=NULL; - struct status_change *sc; - -// security system to prevent forgetting timer removal - int temp_timerid; - - bl=map_id2bl(id); -#ifndef _WIN32 - nullpo_retr_f(0, bl, "id=%d data=%d",id,data); -#endif - sc=status_get_sc(bl); - if (!sc) - { //Temporal debug until case is resolved. [Skotlex] - ShowDebug("status_change_timer: Null pointer id: %d data: %d bl-type: %d\n", id, data, bl?bl->type:-1); - return 0; - } - - if(bl->type==BL_PC) - sd=(struct map_session_data *)bl; - - if(sc->data[type].timer != tid) { - if(battle_config.error_log) - ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sc->data[type].timer, bl->id); - return 0; - } - - // security system to prevent forgetting timer removal - // you shouldn't be that careless inside the switch here - temp_timerid = sc->data[type].timer; - sc->data[type].timer = -1; - - switch(type){ /* 特殊な?理になる場合 */ - case SC_MAXIMIZEPOWER: /* マキシマイズパワ? */ - case SC_CLOAKING: - if(!sd || sd->status.sp > 0) - { - if (sd) - { - sd->status.sp--; - clif_updatestatus(sd,SP_SP); - } - sc->data[type].timer=add_timer( /* タイマ?再設定 */ - sc->data[type].val2+tick, status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_CHASEWALK: - if(sd){ - int sp = 10+sc->data[type].val1*2; - if (map_flag_gvg(sd->bl.m)) sp *= 5; - if (pc_damage_sp(sd, sp, 0) > 0) { - if ((++sc->data[type].val4) == 1) { - sc_start(bl, SC_INCSTR,100,1<<(sc->data[type].val1-1), - (sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration - *skill_get_time2(StatusSkillChangeTable[type],sc->data[type].val1)); - } - sc->data[type].timer = add_timer( /* タイマ?再設定 */ - sc->data[type].val2+tick, status_change_timer, bl->id, data); - return 0; - } - } - break; - - case SC_HIDING: /* ハイディング */ - if(sd){ /* SPがあって、時間制限の間は持? */ - if( sd->status.sp > 0 && (--sc->data[type].val2)>0 ){ - if(sc->data[type].val2 % (sc->data[type].val1+3) ==0 ){ - sd->status.sp--; - clif_updatestatus(sd,SP_SP); - } - sc->data[type].timer=add_timer( /* タイマ?再設定 */ - 1000+tick, status_change_timer, - bl->id, data); - return 0; - } - } - break; - - case SC_SIGHT: /* サイト */ - case SC_RUWACH: /* ルアフ */ - case SC_SIGHTBLASTER: - { - map_foreachinrange( status_change_timer_sub, bl, - skill_get_splash(StatusSkillChangeTable[type], sc->data[type].val1), - BL_CHAR, bl,sc,type,tick); - - if( (--sc->data[type].val2)>0 ){ - sc->data[type].timer=add_timer( /* タイマ?再設定 */ - 250+tick, status_change_timer, - bl->id, data); - return 0; - } - } - break; - - case SC_PROVOKE: /* プロボック/オ?トバ?サ?ク */ - if(sc->data[type].val2!=0){ /* オ?トバ?サ?ク(1秒ごとにHPチェック) */ - if(sd && sd->status.hp>sd->status.max_hp>>2) /* 停止 */ - break; - sc->data[type].timer=add_timer( 1000+tick,status_change_timer, bl->id, data ); - return 0; - } - break; - - case SC_ENDURE: /* インデュア */ - if(sc->data[type].val4) - { - sc->data[type].timer=add_timer(1000*60+tick,status_change_timer, bl->id, data); - return 0; - } - break; - - case SC_STONE: - if(sc->data[type].val2 != 0) { - sc->data[type].val2 = 0; - sc->data[type].val4 = 0; - unit_stop_walking(bl,1); - sc->opt1 = OPT1_STONE; - clif_changeoption(bl); - sc->data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data ); - return 0; - } - else if( (--sc->data[type].val3) > 0) { - int hp = status_get_max_hp(bl); - if((++sc->data[type].val4)%5 == 0 && status_get_hp(bl) > hp>>2) { - hp = hp/100; - if(hp < 1) hp = 1; - battle_damage(NULL, bl, hp, 0, 1); - } - sc->data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data ); - return 0; - } - break; - - case SC_POISON: - if (status_get_hp(bl) <= status_get_max_hp(bl)>>2) //Stop damaging after 25% HP left. - break; - case SC_DPOISON: - if ((--sc->data[type].val3) > 0 && sc->data[SC_SLOWPOISON].timer == -1) - battle_damage(NULL, bl, sc->data[type].val4, 0, 1); - if (sc->data[type].val3 > 0 && !status_isdead(bl)) - { - sc->data[type].timer = add_timer (1000 + tick, status_change_timer, bl->id, data ); - return 0; - } - break; - - case SC_TENSIONRELAX: /* テンションリラックス */ - if(sd){ /* SPがあって、HPが?タンでなければ?? */ - if( sd->status.sp > 12 && sd->status.max_hp > sd->status.hp ){ - sc->data[type].timer=add_timer( /* タイマ?再設定 */ - 10000+tick, status_change_timer, - bl->id, data); - return 0; - } - if(sd->status.max_hp <= sd->status.hp) - { - status_change_end(&sd->bl,SC_TENSIONRELAX,-1); - return 0; - } - } - break; - case SC_BLEEDING: // [celest] - // i hope i haven't interpreted it wrong.. which i might ^^; - // Source: - // - 10ゥェエェネェヒHPェャハ盒 - // - ェホェ゙ェ゙ォオ?ォミケヤムェ茘ォォーェキェニェ?ヘェマ眈ェィェハェ、 - // To-do: bleeding effect increases damage taken? - if ((sc->data[type].val4 -= 10000) >= 0) { - int hp = rand()%600 + 200; - battle_damage(NULL,bl,hp,0,0); - if (!status_isdead(bl)) - sc->data[type].timer = add_timer(10000 + tick, status_change_timer, bl->id, data ); - return 0; - } - break; - - case SC_KNOWLEDGE: - if (sd) { - if(bl->m != sd->feel_map[0].m - && bl->m != sd->feel_map[1].m - && bl->m != sd->feel_map[2].m) - break; //End it - } //Otherwise continue. - // Status changes that don't have a time limit - case SC_AETERNA: - case SC_TRICKDEAD: - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_MAGICPOWER: - case SC_REJECTSWORD: - case SC_MEMORIZE: - case SC_BROKENWEAPON: - case SC_BROKENARMOR: - case SC_SACRIFICE: - case SC_READYSTORM: - case SC_READYDOWN: - case SC_READYTURN: - case SC_READYCOUNTER: - case SC_RUN: - case SC_DODGE: - case SC_AUTOBERSERK: //continues until triggered off manually. [Skotlex] - case SC_NEN: - case SC_SIGNUMCRUCIS: /* シグナムクルシス */ - sc->data[type].timer=add_timer( 1000*600+tick,status_change_timer, bl->id, data ); - return 0; - - case SC_DANCING: //ダンススキルの時間SP消費 - { - int s = 0; - int sp = 1; - if (--sc->data[type].val3 <= 0) - break; - if(sd) { - switch(sc->data[type].val1){ - case BD_RICHMANKIM: /* ニヨルドの宴 3秒にSP1 */ - case BD_DRUMBATTLEFIELD: /* ?太鼓の響き 3秒にSP1 */ - case BD_RINGNIBELUNGEN: /* ニ?ベルングの指輪 3秒にSP1 */ - case BD_SIEGFRIED: /* 不死身のジ?クフリ?ド 3秒にSP1 */ - case BA_DISSONANCE: /* 不協和音 3秒でSP1 */ - case BA_ASSASSINCROSS: /* 夕陽のアサシンクロス 3秒でSP1 */ - case DC_UGLYDANCE: /* 自分勝手なダンス 3秒でSP1 */ - s=3; - break; - case BD_LULLABY: /* 子守歌 4秒にSP1 */ - case BD_ETERNALCHAOS: /* 永遠の混沌 4秒にSP1 */ - case BD_ROKISWEIL: /* ロキの叫び 4秒にSP1 */ - case DC_FORTUNEKISS: /* 幸運のキス 4秒でSP1 */ - s=4; - break; - case CG_HERMODE: // Wand of Hermod - sp=5; //Upkeep = 5 - case BD_INTOABYSS: /* 深淵の中に 5秒にSP1 */ - case BA_WHISTLE: /* 口笛 5秒でSP1 */ - case DC_HUMMING: /* ハミング 5秒でSP1 */ - case BA_POEMBRAGI: /* ブラギの詩 5秒でSP1 */ - case DC_SERVICEFORYOU: /* サ?ビスフォ?ユ? 5秒でSP1 */ - s=5; - break; - case BA_APPLEIDUN: /* イドゥンの林檎 6秒でSP1 */ - s=6; - break; - case CG_MOONLIT: /* 月明りの泉に落ちる花びら 10秒でSP1? */ - sp= 4*sc->data[type].val2; //Moonlit's cost is 4sp*skill_lv [Skotlex] - //Upkeep is also every 10 secs. - case DC_DONTFORGETME: /* 私を忘れないで… 10秒でSP1 */ - s=10; - break; - } - if (s && ((sc->data[type].val3 % s) == 0)) { - if (sc->data[SC_LONGING].timer != -1) - sp = s; - if (pc_damage_sp(sd, sp, 0) <= 0) - break; - } - } - sc->data[type].timer=add_timer( /* タイマ?再設定 */ - 1000+tick, status_change_timer, - bl->id, data); - return 0; - } - break; - - case SC_DEVOTION: - { //Check range and timeleft to preserve status [Skotlex] - //This implementation won't work for mobs because of map_id2sd, but it's a small cost in exchange of the speed of map_id2sd over map_id2sd - struct map_session_data *md = map_id2sd(sc->data[type].val1); - if (md && battle_check_range(bl, &md->bl, sc->data[type].val3) && (sc->data[type].val4-=1000)>0) - { - sc->data[type].timer = add_timer(1000+tick, status_change_timer, bl->id, data); - return 0; - } - } - break; - - case SC_BERSERK: /* バ?サ?ク */ - if(sd){ /* HPが100以上なら?? */ - if( (sd->status.hp - sd->status.max_hp*5/100) > 100 ){ // 5% every 10 seconds [DracoRPG] - sd->status.hp -= sd->status.max_hp*5/100; // changed to max hp [celest] - clif_updatestatus(sd,SP_HP); - sc->data[type].timer = add_timer( /* タイマ?再設定 */ - 10000+tick, status_change_timer, - bl->id, data); - return 0; - } - else - sd->canregen_tick = gettick() + 300000; - } - break; - case SC_NOCHAT: //チャット禁止?態 - if(sd && battle_config.manner_system){ - sd->status.manner++; - clif_updatestatus(sd,SP_MANNER); - if (sd->status.manner < 0) - { //Every 60 seconds your manner goes up by 1 until it gets back to 0. - sc->data[type].timer=add_timer(60000+tick, status_change_timer, bl->id, data); - return 0; - } - } - break; - - case SC_SPLASHER: - if (sc->data[type].val4 % 1000 == 0) { - char timer[10]; - snprintf (timer, 10, "%d", sc->data[type].val4/1000); - clif_message(bl, timer); - } - if((sc->data[type].val4 -= 500) > 0) { - sc->data[type].timer = add_timer( - 500 + tick, status_change_timer, - bl->id, data); - return 0; - } - break; - - case SC_MARIONETTE: /* マリオネットコントロ?ル */ - case SC_MARIONETTE2: - { - struct block_list *pbl = map_id2bl(sc->data[type].val3); - if (pbl && battle_check_range(bl, pbl, 7) && - (sc->data[type].val2 -= 1000)>0) { - sc->data[type].timer = add_timer( - 1000 + tick, status_change_timer, - bl->id, data); - return 0; - } - } - break; - - case SC_GOSPEL: - if(sc->data[type].val4 == BCT_SELF){ - int hp, sp; - hp = (sc->data[type].val1 > 5) ? 45 : 30; - sp = (sc->data[type].val1 > 5) ? 35 : 20; - if(status_get_hp(bl) - hp > 0 && - (sd == NULL || sd->status.sp - sp> 0)) - { - if (sd) - pc_damage_sp(sd, sp, 0); - battle_damage(NULL, bl, hp, 0, 1); - if ((sc->data[type].val2 -= 10000) > 0) { - sc->data[type].timer = add_timer( - 10000+tick, status_change_timer, - bl->id, data); - return 0; - } - } - } - break; - - case SC_GUILDAURA: - { - struct block_list *tbl = map_id2bl(sc->data[type].val2); - - if (tbl && battle_check_range(bl, tbl, 2)){ - sc->data[type].timer = add_timer( - 1000 + tick, status_change_timer, - bl->id, data); - return 0; - } - } - break; - } - - // default for all non-handled control paths - // security system to prevent forgetting timer removal - - // if we reach this point we need the timer for the next call, - // so restore it to have status_change_end handle a valid timer - sc->data[type].timer = temp_timerid; - - return status_change_end( bl,type,tid ); -} - -/*========================================== - * ステータス異常タイマー範囲処理 - *------------------------------------------ - */ -int status_change_timer_sub(struct block_list *bl, va_list ap ) -{ - struct block_list *src; - struct status_change *sc, *tsc; - struct map_session_data* sd=NULL; - struct map_session_data* tsd=NULL; - - int type; - unsigned int tick; - - src=va_arg(ap,struct block_list*); - sc=va_arg(ap,struct status_change*); - type=va_arg(ap,int); - tick=va_arg(ap,unsigned int); - tsc=status_get_sc(bl); - - if (status_isdead(bl)) - return 0; - if (src->type==BL_PC) sd= (struct map_session_data*)src; - if (bl->type==BL_PC) tsd= (struct map_session_data*)bl; - - switch( type ){ - case SC_SIGHT: /* サイト */ - case SC_CONCENTRATE: - if (tsc && tsc->count) { - if (tsc->data[SC_HIDING].timer != -1) - status_change_end( bl, SC_HIDING, -1); - if (tsc->data[SC_CLOAKING].timer != -1) - status_change_end( bl, SC_CLOAKING, -1); - } - break; - case SC_RUWACH: /* ルアフ */ - if (tsc && tsc->count && (tsc->data[SC_HIDING].timer != -1 || // if the target is using a special hiding, i.e not using normal hiding/cloaking, don't bother - tsc->data[SC_CLOAKING].timer != -1)) { - status_change_end( bl, SC_HIDING, -1); - status_change_end( bl, SC_CLOAKING, -1); - if(battle_check_target( src, bl, BCT_ENEMY ) > 0) - skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0); - } - break; - case SC_SIGHTBLASTER: - { - if (sc && sc->count && sc->data[type].val2 > 0 && battle_check_target( src, bl, BCT_ENEMY ) > 0) - { //sc_ check prevents a single round of Sight Blaster hitting multiple opponents. [Skotlex] - skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0); - sc->data[type].val2 = 0; //This signals it to end. - } - } - break; - case SC_CLOSECONFINE: - //Lock char has released the hold on everyone... - if (tsc && tsc->count && tsc->data[SC_CLOSECONFINE2].timer != -1 && tsc->data[SC_CLOSECONFINE2].val2 == src->id) { - tsc->data[SC_CLOSECONFINE2].val2 = 0; - status_change_end(bl, SC_CLOSECONFINE2, -1); - } - break; - } - return 0; -} - -/*========================================== - * Clears buffs/debuffs of a character. - * type&1 -> buffs, type&2 -> debuffs - *------------------------------------------ - */ -int status_change_clear_buffs (struct block_list *bl, int type) -{ - int i; - struct status_change *sc= status_get_sc(bl); - - if (!sc || !sc->count) - return 0; - - if (type&2) //Debuffs - for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++) { - if(sc->data[i].timer != -1) - status_change_end(bl,i,-1); - } - - for (i = SC_COMMON_MAX+1; i < SC_MAX; i++) { - - if(sc->data[i].timer == -1) - continue; - - switch (i) { - //Stuff that cannot be removed - case SC_WEIGHT50: - case SC_WEIGHT90: - case SC_COMBO: - case SC_SMA: - case SC_DANCING: - case SC_GUILDAURA: - case SC_SAFETYWALL: - case SC_NOCHAT: - case SC_ANKLE: - case SC_BLADESTOP: - case SC_CP_WEAPON: - case SC_CP_SHIELD: - case SC_CP_ARMOR: - case SC_CP_HELM: - continue; - - //Debuffs that can be removed. - case SC_HALLUCINATION: - case SC_QUAGMIRE: - case SC_SIGNUMCRUCIS: - case SC_DECREASEAGI: - case SC_SLOWDOWN: - case SC_MINDBREAKER: - case SC_WINKCHARM: - case SC_STOP: - case SC_ORCISH: - case SC_STRIPWEAPON: - case SC_STRIPSHIELD: - case SC_STRIPARMOR: - case SC_STRIPHELM: - if (!(type&2)) - continue; - break; - //The rest are buffs that can be removed. - case SC_BERSERK: - if (!(type&1)) - continue; - sc->data[i].val4 = 1; - break; - default: - if (!(type&1)) - continue; - break; - } - status_change_end(bl,i,-1); - } - return 0; -} - -static int status_calc_sigma(void) -{ - int i,j; - unsigned int k; - - for(i=0;i= INT_MAX) - break; //Overflow protection. [Skotlex] - } - for(;j<=MAX_LEVEL;j++) - hp_sigma_val[i][j-1] = INT_MAX; - } - return 0; -} - -int status_readdb(void) { - int i,j; - FILE *fp; - char line[1024], path[1024],*p; - - sprintf(path, "%s/job_db1.txt", db_path); - fp=fopen(path,"r"); // Job-specific values (weight, HP, SP, ASPD) - if(fp==NULL){ - ShowError("can't read %s\n", path); - return 1; - } - i = 0; - while(fgets(line, sizeof(line)-1, fp)){ - char *split[MAX_WEAPON_TYPE + 5]; - i++; - if(line[0]=='/' && line[1]=='/') - continue; - for(j=0,p=line;j<(MAX_WEAPON_TYPE + 5) && p;j++){ //not 22 anymore [blackhole89] - split[j]=p; - p=strchr(p,','); - if(p) *p++=0; - } - if(j < MAX_WEAPON_TYPE + 5) - { //Weapon #.MAX_WEAPON_TYPE is constantly not load. Fix to that: replace < with <= [blackhole89] - ShowDebug("%s: Not enough columns at line %d\n", path, i); - continue; - } - if(atoi(split[0])>=MAX_PC_CLASS) - continue; - - max_weight_base[atoi(split[0])]=atoi(split[1]); - hp_coefficient[atoi(split[0])]=atoi(split[2]); - hp_coefficient2[atoi(split[0])]=atoi(split[3]); - sp_coefficient[atoi(split[0])]=atoi(split[4]); - for(j=0;j=MAX_PC_CLASS) - continue; - for(i=1;i MAX_STATUSCHANGE) - { - ShowDebug("status.h defines %d status changes, but the MAX_STATUSCHANGE is %d! Fix it.\n", SC_MAX, MAX_STATUSCHANGE); - exit(1); - } - add_timer_func_list(status_change_timer,"status_change_timer"); - add_timer_func_list(kaahi_heal_timer,"kaahi_heal_timer"); - initChangeTables(); - status_readdb(); - status_calc_sigma(); - return 0; -} +// Copyright (c) Athena Dev Teams - Licensed under GNU GPL +// For more information, see LICENCE in the main folder + +#include +#include +#include +#include +#include +#include +#include + +#include "pc.h" +#include "map.h" +#include "pet.h" +#include "npc.h" +#include "mob.h" +#include "clif.h" +#include "guild.h" +#include "skill.h" +#include "itemdb.h" +#include "battle.h" +#include "chrif.h" +#include "status.h" +#include "script.h" +#include "unit.h" + +#include "../common/timer.h" +#include "../common/nullpo.h" +#include "../common/showmsg.h" +#include "../common/malloc.h" + +int SkillStatusChangeTable[MAX_SKILL]; //Stores the status that should be associated to this skill. +int StatusIconChangeTable[SC_MAX]; //Stores the icon that should be associated to this status change. +int StatusSkillChangeTable[SC_MAX]; //Stores the skill that should be considered associated to this status change. +unsigned long StatusChangeFlagTable[SC_MAX]; //Stores the flag specifying what this SC changes. + +static int max_weight_base[MAX_PC_CLASS]; +static int hp_coefficient[MAX_PC_CLASS]; +static int hp_coefficient2[MAX_PC_CLASS]; +static int hp_sigma_val[MAX_PC_CLASS][MAX_LEVEL]; +static int sp_coefficient[MAX_PC_CLASS]; +static int aspd_base[MAX_PC_CLASS][MAX_WEAPON_TYPE]; //[blackhole89] +static int refinebonus[MAX_REFINE_BONUS][3]; // 精錬ボーナステーブル(refine_db.txt) +int percentrefinery[5][MAX_REFINE+1]; // 精錬成功率(refine_db.txt) +static int atkmods[3][MAX_WEAPON_TYPE]; // 武器ATKサイズ修正(size_fix.txt) +static char job_bonus[MAX_PC_CLASS][MAX_LEVEL]; + +static struct status_data dummy_status; +int current_equip_item_index; //Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus] +int current_equip_card_id; //To prevent card-stacking (from jA) [Skotlex] +//we need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only +//to avoid cards exploits + +//Initializes the StatusIconChangeTable variable. May seem somewhat slower than directly defining the array, +//but it is much less prone to errors. [Skotlex] +void initChangeTables(void) { + int i; + for (i = 0; i < SC_MAX; i++) + StatusIconChangeTable[i] = SI_BLANK; + for (i = 0; i < MAX_SKILL; i++) + SkillStatusChangeTable[i] = -1; + memset(StatusSkillChangeTable, 0, sizeof(StatusSkillChangeTable)); + memset(StatusChangeFlagTable, 0, sizeof(StatusChangeFlagTable)); + + //First we define the skill for common ailments. These are used in + //skill_additional_effect through sc cards. [Skotlex] + StatusSkillChangeTable[SC_STONE] = MG_STONECURSE; + StatusSkillChangeTable[SC_FREEZE] = MG_FROSTDIVER; + StatusSkillChangeTable[SC_STUN] = NPC_STUNATTACK; + StatusSkillChangeTable[SC_SLEEP] = NPC_SLEEPATTACK; + StatusSkillChangeTable[SC_POISON] = NPC_POISON; + StatusSkillChangeTable[SC_CURSE] = NPC_CURSEATTACK; + StatusSkillChangeTable[SC_SILENCE] = NPC_SILENCEATTACK; + StatusSkillChangeTable[SC_CONFUSION] = DC_WINKCHARM; + StatusSkillChangeTable[SC_BLIND] = NPC_BLINDATTACK; + StatusSkillChangeTable[SC_BLEEDING] = LK_HEADCRUSH; + StatusSkillChangeTable[SC_DPOISON] = NPC_POISON; + + //These are the status-change flags for the common ailments. + StatusChangeFlagTable[SC_STONE] = SCB_DEF_ELE; + StatusChangeFlagTable[SC_FREEZE] = SCB_DEF_ELE; +// StatusChangeFlagTable[SC_STUN] = SCB_NONE; +// StatusChangeFlagTable[SC_SLEEP] = SCB_NONE; + StatusChangeFlagTable[SC_POISON] = SCB_DEF2; + StatusChangeFlagTable[SC_CURSE] = SCB_LUK|SCB_BATK|SCB_WATK|SCB_SPEED; +// StatusChangeFlagTable[SC_SILENCE] = SCB_NONE; +// StatusChangeFlagTable[SC_CONFUSION] = SCB_NONE; + StatusChangeFlagTable[SC_BLIND] = SCB_HIT|SCB_FLEE; +// StatusChangeFlagTable[SC_BLEEDING] = SCB_NONE; +// StatusChangeFlagTable[SC_DPOISON] = SCB_NONE; + + //The icons for the common ailments +// StatusIconChangeTable[SC_STONE] = SI_BLANK; +// StatusIconChangeTable[SC_FREEZE] = SI_BLANK; +// StatusIconChangeTable[SC_STUN] = SI_BLANK; +// StatusIconChangeTable[SC_SLEEP] = SI_BLANK; +// StatusIconChangeTable[SC_POISON] = SI_BLANK; +// StatusIconChangeTable[SC_CURSE] = SI_BLANK; +// StatusIconChangeTable[SC_SILENCE] = SI_BLANK; +// StatusIconChangeTable[SC_CONFUSION] = SI_BLANK; +// StatusIconChangeTable[SC_BLIND] = SI_BLANK; + StatusIconChangeTable[SC_BLEEDING] = SI_BLEEDING; +// StatusIconChangeTable[SC_DPOISON] = SI_BLANK; + + +#define set_sc(skill, sc, icon, flag) \ + if (SkillStatusChangeTable[skill]==-1) SkillStatusChangeTable[skill] = sc; \ + if (StatusSkillChangeTable[sc]==0) StatusSkillChangeTable[sc] = skill; \ + if (StatusIconChangeTable[sc]==SI_BLANK) StatusIconChangeTable[sc] = icon; \ + StatusChangeFlagTable[sc] |= flag; + +//This one is for sc's that already were defined. +#define add_sc(skill, sc) \ + if (SkillStatusChangeTable[skill]==-1) SkillStatusChangeTable[skill] = sc; \ + if (StatusSkillChangeTable[sc]==0) StatusSkillChangeTable[sc] = skill; + + + add_sc(SM_BASH, SC_STUN); + set_sc(SM_PROVOKE, SC_PROVOKE, SI_PROVOKE, SCB_DEF|SCB_DEF2|SCB_BATK|SCB_WATK); + add_sc(SM_MAGNUM, SC_WATK_ELEMENT); + set_sc(SM_ENDURE, SC_ENDURE, SI_ENDURE, SCB_MDEF|SCB_DSPD); + add_sc(MG_SIGHT, SC_SIGHT); + add_sc(MG_SAFETYWALL, SC_SAFETYWALL); + add_sc(MG_FROSTDIVER, SC_FREEZE); + add_sc(MG_STONECURSE, SC_STONE); + add_sc(AL_RUWACH, SC_RUWACH); + set_sc(AL_INCAGI, SC_INCREASEAGI, SI_INCREASEAGI, SCB_AGI|SCB_SPEED); + set_sc(AL_DECAGI, SC_DECREASEAGI, SI_DECREASEAGI, SCB_AGI|SCB_SPEED); + set_sc(AL_CRUCIS, SC_SIGNUMCRUCIS, SI_SIGNUMCRUCIS, SCB_DEF); + set_sc(AL_ANGELUS, SC_ANGELUS, SI_ANGELUS, SCB_DEF2); + set_sc(AL_BLESSING, SC_BLESSING, SI_BLESSING, SCB_STR|SCB_INT|SCB_DEX); + set_sc(AC_CONCENTRATION, SC_CONCENTRATE, SI_CONCENTRATE, SCB_AGI|SCB_DEX); + set_sc(TF_HIDING, SC_HIDING, SI_HIDING, SCB_SPEED); + add_sc(TF_POISON, SC_POISON); + set_sc(KN_TWOHANDQUICKEN, SC_TWOHANDQUICKEN, SI_TWOHANDQUICKEN, SCB_ASPD); + add_sc(KN_AUTOCOUNTER, SC_AUTOCOUNTER); + set_sc(PR_IMPOSITIO, SC_IMPOSITIO, SI_IMPOSITIO, SCB_WATK); + set_sc(PR_SUFFRAGIUM, SC_SUFFRAGIUM, SI_SUFFRAGIUM, SCB_NONE); + set_sc(PR_ASPERSIO, SC_ASPERSIO, SI_ASPERSIO, SCB_ATK_ELE); + set_sc(PR_BENEDICTIO, SC_BENEDICTIO, SI_BENEDICTIO, SCB_DEF_ELE); + set_sc(PR_SLOWPOISON, SC_SLOWPOISON, SI_SLOWPOISON, SCB_NONE); + set_sc(PR_KYRIE, SC_KYRIE, SI_KYRIE, SCB_NONE); + set_sc(PR_MAGNIFICAT, SC_MAGNIFICAT, SI_MAGNIFICAT, SCB_NONE); + set_sc(PR_GLORIA, SC_GLORIA, SI_GLORIA, SCB_LUK); + add_sc(PR_LEXDIVINA, SC_SILENCE); + set_sc(PR_LEXAETERNA, SC_AETERNA, SI_AETERNA, SCB_NONE); + add_sc(WZ_METEOR, SC_STUN); + add_sc(WZ_VERMILION, SC_BLIND); + add_sc(WZ_FROSTNOVA, SC_FREEZE); + add_sc(WZ_STORMGUST, SC_FREEZE); + set_sc(WZ_QUAGMIRE, SC_QUAGMIRE, SI_QUAGMIRE, SCB_AGI|SCB_DEX|SCB_ASPD); + set_sc(BS_ADRENALINE, SC_ADRENALINE, SI_ADRENALINE, SCB_ASPD); + set_sc(BS_WEAPONPERFECT, SC_WEAPONPERFECTION, SI_WEAPONPERFECTION, SCB_NONE); + set_sc(BS_OVERTHRUST, SC_OVERTHRUST, SI_OVERTHRUST, SCB_NONE); + set_sc(BS_MAXIMIZE, SC_MAXIMIZEPOWER, SI_MAXIMIZEPOWER, SCB_NONE); + add_sc(HT_LANDMINE, SC_STUN); + add_sc(HT_ANKLESNARE, SC_ANKLE); + add_sc(HT_SANDMAN, SC_SLEEP); + add_sc(HT_FLASHER, SC_BLIND); + add_sc(HT_FREEZINGTRAP, SC_FREEZE); + set_sc(AS_CLOAKING, SC_CLOAKING, SI_CLOAKING, SCB_CRI|SCB_SPEED); + add_sc(AS_SONICBLOW, SC_STUN); + set_sc(AS_GRIMTOOTH, SC_SLOWDOWN, SI_BLANK, SCB_SPEED); + set_sc(AS_ENCHANTPOISON, SC_ENCPOISON, SI_ENCPOISON, SCB_ATK_ELE); + set_sc(AS_POISONREACT, SC_POISONREACT, SI_POISONREACT, SCB_NONE); + add_sc(AS_VENOMDUST, SC_POISON); + add_sc(AS_SPLASHER, SC_SPLASHER); + set_sc(NV_TRICKDEAD, SC_TRICKDEAD, SI_TRICKDEAD, SCB_NONE); + set_sc(SM_AUTOBERSERK, SC_AUTOBERSERK, SI_STEELBODY, SCB_NONE); + add_sc(TF_SPRINKLESAND, SC_BLIND); + add_sc(TF_THROWSTONE, SC_STUN); + set_sc(MC_LOUD, SC_LOUD, SI_LOUD, SCB_STR); + set_sc(MG_ENERGYCOAT, SC_ENERGYCOAT, SI_ENERGYCOAT, SCB_NONE); + set_sc(NPC_EMOTION, SC_MODECHANGE, SI_BLANK, SCB_MODE); + add_sc(NPC_EMOTION_ON, SC_MODECHANGE); + set_sc(NPC_ATTRICHANGE, SC_ELEMENTALCHANGE, SI_BLANK, SCB_DEF_ELE); + add_sc(NPC_CHANGEWATER, SC_ELEMENTALCHANGE); + add_sc(NPC_CHANGEGROUND, SC_ELEMENTALCHANGE); + add_sc(NPC_CHANGEFIRE, SC_ELEMENTALCHANGE); + add_sc(NPC_CHANGEWIND, SC_ELEMENTALCHANGE); + add_sc(NPC_CHANGEPOISON, SC_ELEMENTALCHANGE); + add_sc(NPC_CHANGEHOLY, SC_ELEMENTALCHANGE); + add_sc(NPC_CHANGEDARKNESS, SC_ELEMENTALCHANGE); + add_sc(NPC_CHANGETELEKINESIS, SC_ELEMENTALCHANGE); + add_sc(NPC_POISON, SC_POISON); + add_sc(NPC_BLINDATTACK, SC_BLIND); + add_sc(NPC_SILENCEATTACK, SC_SILENCE); + add_sc(NPC_STUNATTACK, SC_STUN); + add_sc(NPC_PETRIFYATTACK, SC_STONE); + add_sc(NPC_CURSEATTACK, SC_CURSE); + add_sc(NPC_SLEEPATTACK, SC_SLEEP); + set_sc(NPC_KEEPING, SC_KEEPING, SI_BLANK, SCB_DEF); + add_sc(NPC_DARKBLESSING, SC_COMA); + set_sc(NPC_BARRIER, SC_BARRIER, SI_BLANK, SCB_MDEF); + add_sc(NPC_LICK, SC_STUN); + set_sc(NPC_HALLUCINATION, SC_HALLUCINATION, SI_HALLUCINATION, SCB_NONE); + add_sc(NPC_REBIRTH, SC_KAIZEL); + add_sc(RG_RAID, SC_STUN); + set_sc(RG_STRIPWEAPON, SC_STRIPWEAPON, SI_STRIPWEAPON, SCB_WATK); + set_sc(RG_STRIPSHIELD, SC_STRIPSHIELD, SI_STRIPSHIELD, SCB_DEF); + set_sc(RG_STRIPARMOR, SC_STRIPARMOR, SI_STRIPARMOR, SCB_VIT); + set_sc(RG_STRIPHELM, SC_STRIPHELM, SI_STRIPHELM, SCB_INT); + add_sc(AM_ACIDTERROR, SC_BLEEDING); + set_sc(AM_CP_WEAPON, SC_CP_WEAPON, SI_CP_WEAPON, SCB_NONE); + set_sc(AM_CP_SHIELD, SC_CP_SHIELD, SI_CP_SHIELD, SCB_NONE); + set_sc(AM_CP_ARMOR, SC_CP_ARMOR, SI_CP_ARMOR, SCB_NONE); + set_sc(AM_CP_HELM, SC_CP_HELM, SI_CP_HELM, SCB_NONE); + set_sc(CR_AUTOGUARD, SC_AUTOGUARD, SI_AUTOGUARD, SCB_NONE); + add_sc(CR_SHIELDCHARGE, SC_STUN); + set_sc(CR_REFLECTSHIELD, SC_REFLECTSHIELD, SI_REFLECTSHIELD, SCB_NONE); + add_sc(CR_HOLYCROSS, SC_BLIND); + add_sc(CR_GRANDCROSS, SC_BLIND); + set_sc(CR_DEVOTION, SC_DEVOTION, SI_DEVOTION, SCB_NONE); + set_sc(CR_PROVIDENCE, SC_PROVIDENCE, SI_PROVIDENCE, SCB_PC); + set_sc(CR_DEFENDER, SC_DEFENDER, SI_DEFENDER, SCB_SPEED|SCB_ASPD); + set_sc(CR_SPEARQUICKEN, SC_SPEARQUICKEN, SI_SPEARQUICKEN, SCB_ASPD); + set_sc(MO_STEELBODY, SC_STEELBODY, SI_STEELBODY, SCB_DEF|SCB_MDEF|SCB_ASPD|SCB_SPEED); + add_sc(MO_BLADESTOP, SC_BLADESTOP_WAIT); + set_sc(MO_EXPLOSIONSPIRITS, SC_EXPLOSIONSPIRITS, SI_EXPLOSIONSPIRITS, SCB_CRI); + add_sc(MO_EXTREMITYFIST, SC_EXTREMITYFIST); + add_sc(SA_MAGICROD, SC_MAGICROD); + set_sc(SA_AUTOSPELL, SC_AUTOSPELL, SI_AUTOSPELL, SCB_NONE); + set_sc(SA_FLAMELAUNCHER, SC_FIREWEAPON, SI_FIREWEAPON, SCB_ATK_ELE); + set_sc(SA_FROSTWEAPON, SC_WATERWEAPON, SI_WATERWEAPON, SCB_ATK_ELE); + set_sc(SA_LIGHTNINGLOADER, SC_WINDWEAPON, SI_WINDWEAPON, SCB_ATK_ELE); + set_sc(SA_SEISMICWEAPON, SC_EARTHWEAPON, SI_EARTHWEAPON, SCB_ATK_ELE); + set_sc(SA_VOLCANO, SC_VOLCANO, SI_BLANK, SCB_WATK); + set_sc(SA_DELUGE, SC_DELUGE, SI_BLANK, SCB_MAXHP); + set_sc(SA_VIOLENTGALE, SC_VIOLENTGALE, SI_BLANK, SCB_FLEE); + add_sc(SA_LANDPROTECTOR, SC_LANDPROTECTOR); + add_sc(SA_REVERSEORCISH, SC_ORCISH); + add_sc(SA_COMA, SC_COMA); + add_sc(BD_RICHMANKIM, SC_RICHMANKIM); + set_sc(BD_ETERNALCHAOS, SC_ETERNALCHAOS, SI_BLANK, SCB_DEF2); + set_sc(BD_DRUMBATTLEFIELD, SC_DRUMBATTLE, SI_BLANK, SCB_WATK|SCB_DEF); + set_sc(BD_RINGNIBELUNGEN, SC_NIBELUNGEN, SI_BLANK, SCB_WATK); + add_sc(BD_ROKISWEIL, SC_ROKISWEIL); + add_sc(BD_INTOABYSS, SC_INTOABYSS); + set_sc(BD_SIEGFRIED, SC_SIEGFRIED, SI_BLANK, SCB_PC); + add_sc(BA_FROSTJOKE, SC_FREEZE); + set_sc(BA_WHISTLE, SC_WHISTLE, SI_BLANK, SCB_FLEE|SCB_FLEE2); + set_sc(BA_ASSASSINCROSS, SC_ASSNCROS, SI_BLANK, SCB_ASPD); + add_sc(BA_POEMBRAGI, SC_POEMBRAGI); + set_sc(BA_APPLEIDUN, SC_APPLEIDUN, SI_BLANK, SCB_MAXHP); + add_sc(DC_SCREAM, SC_STUN); + set_sc(DC_HUMMING, SC_HUMMING, SI_BLANK, SCB_HIT); + set_sc(DC_DONTFORGETME, SC_DONTFORGETME, SI_BLANK, SCB_SPEED|SCB_ASPD); + set_sc(DC_FORTUNEKISS, SC_FORTUNE, SI_BLANK, SCB_CRI); + set_sc(DC_SERVICEFORYOU, SC_SERVICE4U, SI_BLANK, SCB_MAXSP|SCB_PC); + add_sc(NPC_DARKCROSS, SC_BLIND); + add_sc(NPC_GRANDDARKNESS, SC_BLIND); + add_sc(NPC_STOP, SC_STOP); + set_sc(NPC_BREAKWEAPON, SC_BROKENWEAPON, SI_BROKENWEAPON, SCB_NONE); + set_sc(NPC_BREAKARMOR, SC_BROKENARMOR, SI_BROKENARMOR, SCB_NONE); + set_sc(NPC_POWERUP, SC_INCHITRATE, SI_BLANK, SCB_HIT); + set_sc(NPC_AGIUP, SC_INCFLEERATE, SI_BLANK, SCB_FLEE); + add_sc(NPC_INVISIBLE, SC_CLOAKING); + set_sc(LK_AURABLADE, SC_AURABLADE, SI_AURABLADE, SCB_NONE); + set_sc(LK_PARRYING, SC_PARRYING, SI_PARRYING, SCB_NONE); + set_sc(LK_CONCENTRATION, SC_CONCENTRATION, SI_CONCENTRATION, SCB_BATK|SCB_WATK|SCB_HIT|SCB_DEF|SCB_DEF2|SCB_DSPD); + set_sc(LK_TENSIONRELAX, SC_TENSIONRELAX, SI_TENSIONRELAX, SCB_NONE); + set_sc(LK_BERSERK, SC_BERSERK, SI_BERSERK, SCB_DEF|SCB_DEF2|SCB_MDEF|SCB_MDEF2|SCB_FLEE|SCB_SPEED|SCB_ASPD|SCB_MAXHP); +// set_sc(LK_FURY, SC_FURY, SI_FURY, SCB_NONE); //Unused skill + set_sc(HP_ASSUMPTIO, SC_ASSUMPTIO, SI_ASSUMPTIO, SCB_NONE); + add_sc(HP_BASILICA, SC_BASILICA); + set_sc(HW_MAGICPOWER, SC_MAGICPOWER, SI_MAGICPOWER, SCB_MATK); + add_sc(PA_SACRIFICE, SC_SACRIFICE); + set_sc(PA_GOSPEL, SC_GOSPEL, SI_BLANK, SCB_SPEED|SCB_ASPD); + add_sc(PA_GOSPEL, SC_SCRESIST); + add_sc(CH_TIGERFIST, SC_STOP); + set_sc(ASC_EDP, SC_EDP, SI_EDP, SCB_NONE); + set_sc(SN_SIGHT, SC_TRUESIGHT, SI_TRUESIGHT, SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK|SCB_CRI|SCB_HIT); + set_sc(SN_WINDWALK, SC_WINDWALK, SI_WINDWALK, SCB_FLEE|SCB_SPEED); + set_sc(WS_MELTDOWN, SC_MELTDOWN, SI_MELTDOWN, SCB_NONE); + set_sc(WS_CARTBOOST, SC_CARTBOOST, SI_CARTBOOST, SCB_SPEED); + set_sc(ST_CHASEWALK, SC_CHASEWALK, SI_CHASEWALK, SCB_SPEED); + set_sc(ST_REJECTSWORD, SC_REJECTSWORD, SI_REJECTSWORD, SCB_NONE); + add_sc(ST_REJECTSWORD, SC_AUTOCOUNTER); + set_sc(CG_MOONLIT, SC_MOONLIT, SI_MOONLIT, SCB_NONE); + set_sc(CG_MARIONETTE, SC_MARIONETTE, SI_MARIONETTE, SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK); + set_sc(CG_MARIONETTE, SC_MARIONETTE2, SI_MARIONETTE2, SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK); + add_sc(LK_SPIRALPIERCE, SC_STOP); + add_sc(LK_HEADCRUSH, SC_BLEEDING); + set_sc(LK_JOINTBEAT, SC_JOINTBEAT, SI_JOINTBEAT, SCB_BATK|SCB_DEF2|SCB_SPEED|SCB_ASPD); + add_sc(HW_NAPALMVULCAN, SC_CURSE); + set_sc(PF_MINDBREAKER, SC_MINDBREAKER, SI_BLANK, SCB_MATK|SCB_MDEF2); + add_sc(PF_MEMORIZE, SC_MEMORIZE); + add_sc(PF_FOGWALL, SC_FOGWALL); + set_sc(PF_SPIDERWEB, SC_SPIDERWEB, SI_BLANK, SCB_FLEE); + add_sc(WE_BABY, SC_BABY); + set_sc(TK_RUN, SC_RUN, SI_RUN, SCB_SPEED); + set_sc(TK_RUN, SC_SPURT, SI_SPURT, SCB_STR); + set_sc(TK_READYSTORM, SC_READYSTORM, SI_READYSTORM, SCB_NONE); + set_sc(TK_READYDOWN, SC_READYDOWN, SI_READYDOWN, SCB_NONE); + add_sc(TK_DOWNKICK, SC_STUN); + set_sc(TK_READYTURN, SC_READYTURN, SI_READYTURN, SCB_NONE); + set_sc(TK_READYCOUNTER,SC_READYCOUNTER, SI_READYCOUNTER, SCB_NONE); + set_sc(TK_DODGE, SC_DODGE, SI_DODGE, SCB_NONE); + set_sc(TK_SPTIME, SC_TKREST, SI_TKREST, SCB_NONE); + set_sc(TK_SEVENWIND, SC_GHOSTWEAPON, SI_GHOSTWEAPON, SCB_ATK_ELE); + set_sc(TK_SEVENWIND, SC_SHADOWWEAPON, SI_SHADOWWEAPON, SCB_ATK_ELE); + set_sc(SG_SUN_WARM, SC_WARM, SI_WARM, SCB_NONE); + add_sc(SG_MOON_WARM, SC_WARM); + add_sc(SG_STAR_WARM, SC_WARM); + set_sc(SG_SUN_COMFORT, SC_SUN_COMFORT, SI_SUN_COMFORT, SCB_DEF2); + set_sc(SG_MOON_COMFORT, SC_MOON_COMFORT, SI_MOON_COMFORT, SCB_FLEE); + set_sc(SG_STAR_COMFORT, SC_STAR_COMFORT, SI_STAR_COMFORT, SCB_ASPD); + add_sc(SG_FRIEND, SC_SKILLRATE_UP); + set_sc(SG_KNOWLEDGE, SC_KNOWLEDGE, SI_BLANK, SCB_PC); + set_sc(SG_FUSION, SC_FUSION, SI_BLANK, SCB_SPEED); + set_sc(BS_ADRENALINE2, SC_ADRENALINE2, SI_ADRENALINE2, SCB_ASPD); + set_sc(SL_KAIZEL, SC_KAIZEL, SI_KAIZEL, SCB_NONE); + set_sc(SL_KAAHI, SC_KAAHI, SI_KAAHI, SCB_NONE); + set_sc(SL_KAUPE, SC_KAUPE, SI_KAUPE, SCB_NONE); + set_sc(SL_KAITE, SC_KAITE, SI_KAITE, SCB_NONE); + add_sc(SL_STUN, SC_STUN); + set_sc(SL_SWOO, SC_SWOO, SI_BLANK, SCB_SPEED); + set_sc(SL_SKE, SC_SKE, SI_BLANK, SCB_BATK|SCB_WATK|SCB_DEF|SCB_DEF2); + set_sc(SL_SKA, SC_SKA, SI_BLANK, SCB_DEF|SCB_MDEF|SCB_SPEED|SCB_ASPD); + set_sc(SL_SMA, SC_SMA, SI_SMA, SCB_NONE); + set_sc(ST_PRESERVE, SC_PRESERVE, SI_PRESERVE, SCB_NONE); + set_sc(PF_DOUBLECASTING, SC_DOUBLECAST, SI_DOUBLECAST, SCB_NONE); + set_sc(HW_GRAVITATION, SC_GRAVITATION, SI_BLANK, SCB_ASPD); + add_sc(WS_CARTTERMINATION, SC_STUN); + set_sc(WS_OVERTHRUSTMAX, SC_MAXOVERTHRUST, SI_MAXOVERTHRUST, SCB_NONE); + set_sc(CG_LONGINGFREEDOM, SC_LONGING, SI_BLANK, SCB_SPEED|SCB_ASPD); + add_sc(CG_HERMODE, SC_HERMODE); + set_sc(SL_HIGH, SC_SPIRIT, SI_SPIRIT, SCB_PC); + set_sc(KN_ONEHAND, SC_ONEHAND, SI_ONEHAND, SCB_ASPD); + set_sc(CR_SHRINK, SC_SHRINK, SI_SHRINK, SCB_NONE); + set_sc(RG_CLOSECONFINE, SC_CLOSECONFINE2, SI_CLOSECONFINE2, SCB_NONE); + set_sc(RG_CLOSECONFINE, SC_CLOSECONFINE, SI_CLOSECONFINE, SCB_FLEE); + set_sc(WZ_SIGHTBLASTER, SC_SIGHTBLASTER, SI_SIGHTBLASTER, SCB_NONE); + set_sc(DC_WINKCHARM, SC_WINKCHARM, SI_WINKCHARM, SCB_NONE); + add_sc(MO_BALKYOUNG, SC_STUN); + add_sc(SA_ELEMENTWATER, SC_ELEMENTALCHANGE); + add_sc(SA_ELEMENTFIRE, SC_ELEMENTALCHANGE); + add_sc(SA_ELEMENTGROUND, SC_ELEMENTALCHANGE); + add_sc(SA_ELEMENTWIND, SC_ELEMENTALCHANGE); + + //Until they're at right position - gs_set_sc- [Vicious] / some of these don't seem to have a status icon adequate [blackhole89] + set_sc(GS_MADNESSCANCEL, SC_MADNESSCANCEL, SI_MADNESSCANCEL, SCB_BATK|SCB_ASPD); + set_sc(GS_ADJUSTMENT, SC_ADJUSTMENT, SI_ADJUSTMENT, SCB_HIT|SCB_FLEE); + set_sc(GS_INCREASING, SC_INCREASING, SI_ACCURACY, SCB_AGI|SCB_DEX|SCB_HIT); + set_sc(GS_GATLINGFEVER, SC_GATLINGFEVER, SI_GATLINGFEVER, SCB_FLEE|SCB_SPEED|SCB_ASPD); + //Uncomment and update when you plan on implementing. +// set_sc(NJ_TATAMIGAESHI, SC_TATAMIGAESHI, SI_BLANK); +// set_sc(NJ_UTSUSEMI, SC_UTSUSEMI, SI_MAEMI); +// set_sc(NJ_KAENSIN, SC_KAENSIN, SI_BLANK); + set_sc(NJ_SUITON, SC_SUITON, SI_BLANK, SCB_AGI); + set_sc(NJ_NEN, SC_NEN, SI_NEN, SCB_STR|SCB_INT); + + // Storing the target job rather than simply SC_SPIRIT simplifies code later on. + SkillStatusChangeTable[SL_ALCHEMIST] = MAPID_ALCHEMIST, + SkillStatusChangeTable[SL_MONK] = MAPID_MONK, + SkillStatusChangeTable[SL_STAR] = MAPID_STAR_GLADIATOR, + SkillStatusChangeTable[SL_SAGE] = MAPID_SAGE, + SkillStatusChangeTable[SL_CRUSADER] = MAPID_CRUSADER, + SkillStatusChangeTable[SL_SUPERNOVICE] = MAPID_SUPER_NOVICE, + SkillStatusChangeTable[SL_KNIGHT] = MAPID_KNIGHT, + SkillStatusChangeTable[SL_WIZARD] = MAPID_WIZARD, + SkillStatusChangeTable[SL_PRIEST] = MAPID_PRIEST, + SkillStatusChangeTable[SL_BARDDANCER] = MAPID_BARDDANCER, + SkillStatusChangeTable[SL_ROGUE] = MAPID_ROGUE, + SkillStatusChangeTable[SL_ASSASIN] = MAPID_ASSASSIN, + SkillStatusChangeTable[SL_BLACKSMITH] = MAPID_BLACKSMITH, + SkillStatusChangeTable[SL_HUNTER] = MAPID_HUNTER, + SkillStatusChangeTable[SL_SOULLINKER] = MAPID_SOUL_LINKER, + + //Status that don't have a skill associated. + StatusIconChangeTable[SC_WEIGHT50 ] = SI_WEIGHT50; + StatusIconChangeTable[SC_WEIGHT90] = SI_WEIGHT90; + StatusIconChangeTable[SC_ASPDPOTION0] = SI_ASPDPOTION; + StatusIconChangeTable[SC_ASPDPOTION1] = SI_ASPDPOTION; + StatusIconChangeTable[SC_ASPDPOTION2] = SI_ASPDPOTION; + StatusIconChangeTable[SC_ASPDPOTION3] = SI_ASPDPOTION; + StatusIconChangeTable[SC_SPEEDUP0] = SI_SPEEDPOTION; + StatusIconChangeTable[SC_SPEEDUP1] = SI_SPEEDPOTION; + StatusIconChangeTable[SC_MIRACLE] = SI_SPIRIT; + + //Other SC which are not necessarily associated to skills. + StatusChangeFlagTable[SC_INCALLSTATUS] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_INT|SCB_DEX|SCB_LUK; + StatusChangeFlagTable[SC_INCSTR] |= SCB_STR; + StatusChangeFlagTable[SC_INCAGI] |= SCB_AGI; + StatusChangeFlagTable[SC_INCVIT] |= SCB_VIT; + StatusChangeFlagTable[SC_INCINT] |= SCB_INT; + StatusChangeFlagTable[SC_INCDEX] |= SCB_DEX; + StatusChangeFlagTable[SC_INCLUK] |= SCB_LUK; + StatusChangeFlagTable[SC_INCHIT] |= SCB_HIT; + StatusChangeFlagTable[SC_INCHITRATE] |= SCB_HIT; + StatusChangeFlagTable[SC_INCFLEE] |= SCB_FLEE; + StatusChangeFlagTable[SC_INCFLEERATE] |= SCB_FLEE; + StatusChangeFlagTable[SC_INCMHPRATE] |= SCB_MAXHP; + StatusChangeFlagTable[SC_INCMSPRATE] |= SCB_MAXSP; + StatusChangeFlagTable[SC_INCATKRATE] |= SCB_BATK|SCB_WATK; + StatusChangeFlagTable[SC_INCMATKRATE] |= SCB_MATK; + StatusChangeFlagTable[SC_INCDEFRATE] |= SCB_DEF; + StatusChangeFlagTable[SC_STRFOOD] |= SCB_STR; + StatusChangeFlagTable[SC_AGIFOOD] |= SCB_AGI; + StatusChangeFlagTable[SC_VITFOOD] |= SCB_VIT; + StatusChangeFlagTable[SC_INTFOOD] |= SCB_INT; + StatusChangeFlagTable[SC_DEXFOOD] |= SCB_DEX; + StatusChangeFlagTable[SC_LUKFOOD] |= SCB_LUK; + StatusChangeFlagTable[SC_HITFOOD] |= SCB_HIT; + StatusChangeFlagTable[SC_FLEEFOOD] |= SCB_FLEE; + StatusChangeFlagTable[SC_BATKFOOD] |= SCB_BATK; + StatusChangeFlagTable[SC_WATKFOOD] |= SCB_WATK; + StatusChangeFlagTable[SC_MATKFOOD] |= SCB_MATK; + + //Guild skills don't fit due to their range being beyond MAX_SKILL + StatusIconChangeTable[SC_GUILDAURA] = SI_GUILDAURA; + StatusChangeFlagTable[SC_GUILDAURA] |= SCB_STR|SCB_AGI|SCB_VIT|SCB_DEX; + StatusIconChangeTable[SC_BATTLEORDERS] = SI_BATTLEORDERS; + StatusChangeFlagTable[SC_BATTLEORDERS] |= SCB_STR|SCB_INT|SCB_DEX; +#undef set_sc +#undef add_sc + if (!battle_config.display_hallucination) //Disable Hallucination. + StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK; +} + +static void initDummyData(void) { + memset(&dummy_status, 0, sizeof(dummy_status)); + dummy_status.hp = + dummy_status.max_hp = + dummy_status.max_sp = + dummy_status.str = + dummy_status.agi = + dummy_status.vit = + dummy_status.int_ = + dummy_status.dex = + dummy_status.luk = + dummy_status.hit = 1; + dummy_status.speed = 2000; + dummy_status.adelay = 4000; + dummy_status.amotion = 2000; + dummy_status.dmotion = 2000; + dummy_status.ele_lv = 1; //Min elemental level. + dummy_status.mode = MD_CANMOVE; +} + +/*========================================== + * 精錬ボーナス + *------------------------------------------ + */ +int status_getrefinebonus(int lv,int type) +{ + if (lv >= 0 && lv < 5 && type >= 0 && type < 3) + return refinebonus[lv][type]; + return 0; +} + +//Inflicts damage on the target with the according walkdelay. +//If flag&1, damage is passive and does not triggers cancelling status changes. +//If flag&2, fail if target does not has enough to substract. +//If flag&4, if killed, mob must not give exp/loot. +int status_damage(struct block_list *src,struct block_list *target,unsigned int hp, unsigned sp, int walkdelay, int flag) +{ + struct status_data *status; + struct status_change *sc; + + if(sp && target->type != BL_PC) + sp = 0; //Only players get SP damage. + + if (!hp && !sp) + return 0; + + if (target->type == BL_SKILL) + return skill_unit_ondamaged((struct skill_unit *)target, src, hp, gettick()); + + status = status_get_status_data(target); + + if (status == &dummy_status || !status->hp || !target->prev) + return 0; //Invalid targets: no damage, dead, not on a map. + + sc = status_get_sc(target); + + if (sc && !sc->count) + sc = NULL; + + if (hp && !(flag&1)) { + if (sc) { + if (sc->data[SC_FREEZE].timer != -1) + status_change_end(target,SC_FREEZE,-1); + if (sc->data[SC_STONE].timer!=-1 && sc->opt1 == OPT1_STONE) + status_change_end(target,SC_STONE,-1); + if (sc->data[SC_SLEEP].timer != -1) + status_change_end(target,SC_SLEEP,-1); + if (sc->data[SC_WINKCHARM].timer != -1) + status_change_end(target,SC_WINKCHARM,-1); + if (sc->data[SC_CONFUSION].timer != -1) + status_change_end(target, SC_CONFUSION, -1); + if (sc->data[SC_TRICKDEAD].timer != -1) + status_change_end(target, SC_TRICKDEAD, -1); + if (sc->data[SC_HIDING].timer != -1) + status_change_end(target, SC_HIDING, -1); + if (sc->data[SC_CLOAKING].timer != -1) + status_change_end(target, SC_CLOAKING, -1); + if (sc->data[SC_CHASEWALK].timer != -1) + status_change_end(target, SC_CHASEWALK, -1); + if (sc->data[SC_ENDURE].timer != -1 && !sc->data[SC_ENDURE].val4) { + //Endure count is only reduced by non-players on non-gvg maps. + //val4 signals infinite endure. [Skotlex] + if (src && src->type != BL_PC && !map_flag_gvg(target->m) + && --(sc->data[SC_ENDURE].val2) < 0) + status_change_end(target, SC_ENDURE, -1); + } + if (sc->data[SC_GRAVITATION].timer != -1 && + sc->data[SC_GRAVITATION].val3 == BCT_SELF) { + struct skill_unit_group *sg = (struct skill_unit_group *)sc->data[SC_GRAVITATION].val4; + if (sg) { + skill_delunitgroup(target,sg); + sc->data[SC_GRAVITATION].val4 = 0; + status_change_end(target, SC_GRAVITATION, -1); + } + } + if (sc->data[SC_DEVOTION].val1 && src && battle_getcurrentskill(src) != PA_PRESSURE) + { + struct map_session_data *sd2 = map_id2sd(sc->data[SC_DEVOTION].val1); + if (sd2 && sd2->devotion[sc->data[SC_DEVOTION].val2] == target->id) + { + clif_damage(src, &sd2->bl, gettick(), 0, 0, hp, 0, 0, 0); + status_fix_damage(NULL, &sd2->bl, hp, 0); + return 0; + } + status_change_end(target, SC_DEVOTION, -1); + } + if(sc->data[SC_DANCING].timer != -1 && hp > status->max_hp>>2) + skill_stop_dancing(target); + } + unit_skillcastcancel(target, 2); + } + + if (hp >= status->hp) { + if (flag&2) return 0; + hp = status->hp; + } + + if (sp > status->sp) { + if (flag&2) return 0; + sp = status->sp; + } + + status->hp-= hp; + status->sp-= sp; + + if (sc && hp && status->hp) { + if (sc->data[SC_AUTOBERSERK].timer != -1 && + (sc->data[SC_PROVOKE].timer==-1 || !sc->data[SC_PROVOKE].val2) && + status->hp < status->max_hp>>2) + sc_start4(target,SC_PROVOKE,100,10,1,0,0,0); + } + + switch (target->type) + { + case BL_MOB: + mob_damage((TBL_MOB*)target, src, hp); + if (!status->hp) + mob_dead((TBL_MOB*)target, src, flag&4?3:0); + break; + case BL_PC: + pc_damage((TBL_PC*)target,src,hp,sp); + if (!status->hp) + pc_dead((TBL_PC*)target,src); + break; + } + + if (walkdelay && status->hp) + unit_set_walkdelay(target, gettick(), walkdelay, 0); + + return hp+sp; +} + +//Heals a character. If flag&1, this is forced healing (otherwise stuff like Berserk can block it) +//If flag&2, when the player is healed, show the HP/SP heal effect. +int status_heal(struct block_list *bl,unsigned int hp,unsigned int sp, int flag) +{ + struct status_data *status; + struct status_change *sc; + + status = status_get_status_data(bl); + + if (status == &dummy_status || !status->hp) + return 0; + + sc = status_get_sc(bl); + if (sc && !sc->count) + sc = NULL; + + if(hp) { + if (!(flag&1) && sc && sc->data[SC_BERSERK].timer!=-1) + hp = 0; + + if(hp > status->max_hp - status->hp) + hp = status->max_hp - status->hp; + } + + if(sp) { + if (bl->type != BL_PC) + sp = 0; //Only players get SP changes + + if(sp > status->max_sp - status->sp) + sp = status->max_sp - status->sp; + } + + if(!sp && !hp) return 0; + + status->hp+= hp; + status->sp+= sp; + + if(hp && sc && + sc->data[SC_AUTOBERSERK].timer != -1 && + sc->data[SC_PROVOKE].timer!=-1 && + sc->data[SC_PROVOKE].val2==1 && + status->hp>=status->max_hp>>2 + ) //End auto berserk. + status_change_end(bl,SC_PROVOKE,-1); + + switch(bl->type) { + case BL_MOB: + mob_heal((TBL_MOB*)bl,hp); + break; + case BL_PC: + pc_heal((TBL_PC*)bl,hp,sp,flag&2?1:0); + break; + } + return hp+sp; +} + +//Does percentual non-flinching damage/heal. If mob is killed this way, +//no exp/drops will be awarded if there is no src (or src is target) +//If rates are > 0, percent is of current HP/SP +//If rates are < 0, percent is of max HP/SP +//If flag, this is heal, otherwise it is damage. +int status_percent_change(struct block_list *src,struct block_list *target,char hp_rate, char sp_rate, int flag) +{ + struct status_data *status; + unsigned int hp =0, sp = 0; + + status = status_get_status_data(target); + + if (hp_rate > 0) + hp = (hp_rate*status->hp)/100; + else if (hp_rate < 0) + hp = (-hp_rate)*status->max_hp/100; + if (hp_rate && !hp) + hp = 1; + + if (sp_rate > 0) + sp = (sp_rate*status->sp)/100; + else if (sp_rate < 0) + sp = (-sp_rate)*status->max_sp/100; + if (sp_rate && !sp) + sp = 1; + + if (flag) return status_heal(target, hp, sp, 0); + return status_damage(src, target, hp, sp, 0, (!src||src==target?5:1)); +} + +/*========================================== + * Checks whether the src can use the skill on the target, + * taking into account status/option of both source/target. [Skotlex] + * flag: + * 0 - Trying to use skill on target. + * 1 - Cast bar is done. + * 2 - Skill already pulled off, check is due to ground-based skills or splash-damage ones. + * src MAY be null to indicate we shouldn't check it, this is a ground-based skill attack. + * target MAY Be null, in which case the checks are only to see + * whether the source can cast or not the skill on the ground. + *------------------------------------------ + */ +int status_check_skilluse(struct block_list *src, struct block_list *target, int skill_num, int flag) +{ + int mode, race, hide_flag; + struct status_change *sc=NULL, *tsc; + + mode = src?status_get_mode(src):MD_CANATTACK; + + if (src && status_isdead(src)) + return 0; + + if (!skill_num) { //Normal attack checks. + if (!(mode&MD_CANATTACK)) + return 0; //This mode is only needed for melee attacking. + //Dead state is not checked for skills as some skills can be used + //on dead characters, said checks are left to skill.c [Skotlex] + if (target && status_isdead(target)) + return 0; + } + + if (skill_num == PA_PRESSURE && flag) { + //Gloria Avoids pretty much everything.... + tsc = target?status_get_sc(target):NULL; + if(tsc) { + if (tsc->option&OPTION_HIDE) + return 0; + if (tsc->count && tsc->data[SC_TRICKDEAD].timer != -1) + return 0; + } + return 1; + } + + if (((src && map_getcell(src->m,src->x,src->y,CELL_CHKBASILICA)) || + (target && target != src && map_getcell(target->m,target->x,target->y,CELL_CHKBASILICA))) + && !(mode&MD_BOSS)) + { //Basilica Check + if (!skill_num) return 0; + race = skill_get_inf(skill_num); + if (race&INF_ATTACK_SKILL) + return 0; + if (race&INF_GROUND_SKILL && skill_get_unit_target(skill_num)&BCT_ENEMY) + return 0; + } + + if (src) sc = status_get_sc(src); + + if(sc && sc->opt1 >0 && (battle_config.sc_castcancel || flag != 1)) + //When sc do not cancel casting, the spell should come out. + return 0; + + if(sc && sc->count) + { + if ( + (sc->data[SC_TRICKDEAD].timer != -1 && skill_num != NV_TRICKDEAD) + || (sc->data[SC_AUTOCOUNTER].timer != -1 && !flag) + || (sc->data[SC_GOSPEL].timer != -1 && sc->data[SC_GOSPEL].val4 == BCT_SELF && skill_num != PA_GOSPEL) + || (sc->data[SC_GRAVITATION].timer != -1 && sc->data[SC_GRAVITATION].val3 == BCT_SELF && skill_num != HW_GRAVITATION) + ) + return 0; + + if (sc->data[SC_WINKCHARM].timer != -1 && target && target->type == BL_PC && !flag) + { //Prevents skill usage against players? + clif_emotion(src, 3); + return 0; + } + + if (sc->data[SC_BLADESTOP].timer != -1) { + switch (sc->data[SC_BLADESTOP].val1) + { + case 5: if (skill_num == MO_EXTREMITYFIST) break; + case 4: if (skill_num == MO_CHAINCOMBO) break; + case 3: if (skill_num == MO_INVESTIGATE) break; + case 2: if (skill_num == MO_FINGEROFFENSIVE) break; + default: return 0; + } + } + if (skill_num && //Do not block item-casted skills. + (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_num) + ) { //Skills blocked through status changes... + if (!flag && ( //Blocked only from using the skill (stuff like autospell may still go through + (sc->data[SC_MARIONETTE].timer != -1 && skill_num != CG_MARIONETTE) || + (sc->data[SC_MARIONETTE2].timer != -1 && skill_num == CG_MARIONETTE) || + sc->data[SC_SILENCE].timer != -1 || + sc->data[SC_STEELBODY].timer != -1 || + sc->data[SC_BERSERK].timer != -1 + )) + return 0; + //Skill blocking. + if ( + (sc->data[SC_VOLCANO].timer != -1 && skill_num == WZ_ICEWALL) || + (sc->data[SC_ROKISWEIL].timer != -1 && skill_num != BD_ADAPTATION && !(mode&MD_BOSS)) || + (sc->data[SC_HERMODE].timer != -1 && skill_get_inf(skill_num) & INF_SUPPORT_SKILL) || + sc->data[SC_NOCHAT].timer != -1 + ) + return 0; + + if (flag!=2 && sc->data[SC_DANCING].timer != -1) + { + if (skill_num != BD_ADAPTATION && skill_num != CG_LONGINGFREEDOM + && skill_num != BA_MUSICALSTRIKE && skill_num != DC_THROWARROW) + return 0; + if (sc->data[SC_DANCING].val1 == CG_HERMODE && skill_num == BD_ADAPTATION) + return 0; //Can't amp out of Wand of Hermode :/ [Skotlex] + } + } + } + + if (sc && sc->option) + { + if (sc->option&OPTION_HIDE && skill_num != TF_HIDING && skill_num != AS_GRIMTOOTH + && skill_num != RG_BACKSTAP && skill_num != RG_RAID && skill_num != NJ_SHADOWJUMP + && skill_num != NJ_KIRIKAGE) + return 0; +// if (sc->option&OPTION_CLOAK && skill_num == TF_HIDING) +// return 0; //Latest reports indicate Hiding is usable while Cloaking. [Skotlex] + if (sc->option&OPTION_CHASEWALK && skill_num != ST_CHASEWALK) + return 0; + } + if (target == NULL || target == src) //No further checking needed. + return 1; + + tsc = status_get_sc(target); + if(tsc && tsc->count) + { + if (!(mode & MD_BOSS) && tsc->data[SC_TRICKDEAD].timer != -1) + return 0; + if(skill_num == WZ_STORMGUST && tsc->data[SC_FREEZE].timer != -1) + return 0; + if(skill_num == PR_LEXAETERNA && (tsc->data[SC_FREEZE].timer != -1 || (tsc->data[SC_STONE].timer != -1 && tsc->opt1 == OPT1_STONE))) + return 0; + } + + race = src?status_get_race(src):0; + //If targetting, cloak+hide protect you, otherwise only hiding does. + hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK); + + //You cannot hide from ground skills. + if(skill_get_pl(skill_num) == 2) + hide_flag &= ~OPTION_HIDE; + + switch (target->type) + { + case BL_PC: + { + struct map_session_data *sd = (struct map_session_data*) target; + if (pc_isinvisible(sd)) + return 0; + if (tsc->option&hide_flag + && (sd->state.perfect_hiding || !(race == RC_INSECT || race == RC_DEMON || mode&MD_DETECTOR)) + && !(mode&MD_BOSS)) + return 0; + } + break; + case BL_ITEM: //Allow targetting of items to pick'em up (or in the case of mobs, to loot them). + //TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex] + if (mode&MD_LOOTER) + return 1; + else + return 0; + default: + //Check for chase-walk/hiding/cloaking opponents. + if (tsc && !(mode&MD_BOSS)) + { + if (tsc->option&hide_flag && !(race == RC_INSECT || race == RC_DEMON || mode&MD_DETECTOR)) + return 0; + } + } + return 1; +} + +void status_calc_bl(struct block_list *bl, unsigned long flag); + +static int status_base_atk(struct block_list *bl, struct status_data *status) +{ + int flag = 0, str, dex, dstr; + if (bl->type == BL_PC) + switch(((TBL_PC*)bl)->status.weapon){ + case W_BOW: + case W_MUSICAL: + case W_WHIP: + case W_REVOLVER: + case W_RIFLE: + case W_SHOTGUN: + case W_GATLING: + case W_GRENADE: + flag = 1; + } + if (flag) { + str = status->dex; + dex = status->str; + } else { + str = status->str; + dex = status->dex; + } + dstr = str/10; + return str + dstr*dstr + dex/5 + status->luk/5; +} + + +//Fills in the misc data that can be calculated from the other status info (except for level) +void status_calc_misc(struct status_data *status, int level) +{ + status->matk_min = status->int_+(status->int_/7)*(status->int_/7); + status->matk_max = status->int_+(status->int_/5)*(status->int_/5); + + status->hit = level + status->dex; + status->flee = level + status->agi; + status->def2 = status->vit; + status->mdef2 = status->int_ + (status->vit>>1); + + status->cri = status->luk*3 + 10; + status->flee2 = status->luk + 10; +} + +//Skotlex: Calculates the initial status for the given mob +//first will only be false when the mob leveled up or got a GuardUp level. +int status_calc_mob(struct mob_data* md, int first) +{ + struct status_data *status; + struct block_list *mbl; + int flag=0; + + //Check if we need custom base-status + if (battle_config.mobs_level_up && md->level != md->db->lv) + flag|=1; + + if (md->special_state.size) + flag|=2; + + if (md->guardian_data && md->guardian_data->guardup_lv) + flag|=4; + + if (battle_config.slaves_inherit_speed && md->master_id) + flag|=8; + + if (md->master_id && md->special_state.ai>1) + flag|=16; + + if (!flag) + { //No special status required. + if (md->base_status) { + aFree(md->base_status); + md->base_status = NULL; + } + if(first) + memcpy(&md->status, &md->db->status, sizeof(struct status_data)); + return 0; + } + if (!md->base_status) + md->base_status = aCalloc(1, sizeof(struct status_data)); + + status = md->base_status; + memcpy(status, &md->db->status, sizeof(struct status_data)); + + + if (flag&(8|16)) + mbl = map_id2bl(md->master_id); + + if (flag&8 && mbl) { + struct status_data *mstatus = status_get_base_status(mbl); + if (mstatus && mstatus->mode&MD_CANMOVE && status->mode&MD_CANMOVE) + status->speed = mstatus->speed; + } + + if (flag&16 && mbl) + { //Max HP setting from Summon Flora/marine Sphere + struct unit_data *ud = unit_bl2ud(mbl); + if (ud) + { // different levels of HP according to skill level + if (ud->skillid == AM_SPHEREMINE) { + status->max_hp = 2000 + 400*ud->skilllv; + status->mode|= MD_CANMOVE; //Needed for the skill + } else { //AM_CANNIBALIZE + status->max_hp = 1500 + 200*ud->skilllv + 10*status_get_lv(mbl); + status->mode|= MD_CANATTACK|MD_AGGRESSIVE; + } + status->hp = status->max_hp; + } + } + + if (flag&1) + { // increase from mobs leveling up [Valaris] + int diff = md->level - md->db->lv; + status->str+= diff; + status->agi+= diff; + status->vit+= diff; + status->int_+= diff; + status->dex+= diff; + status->luk+= diff; + status->max_hp += diff*status->vit; + status->max_sp += diff*status->int_; + status->hp = status->max_hp; + status->sp = status->max_sp; + status->speed -= diff; + } + + + if (flag&2) + { // change for sized monsters [Valaris] + if (md->special_state.size==1) { + status->max_hp>>=1; + status->max_sp>>=1; + if (!status->max_hp) status->max_hp = 1; + if (!status->max_sp) status->max_sp = 1; + status->hp=status->max_hp; + status->sp=status->max_sp; + status->str>>=1; + status->agi>>=1; + status->vit>>=1; + status->int_>>=1; + status->dex>>=1; + status->luk>>=1; + if (!status->str) status->str = 1; + if (!status->agi) status->agi = 1; + if (!status->vit) status->vit = 1; + if (!status->int_) status->int_ = 1; + if (!status->dex) status->dex = 1; + if (!status->luk) status->luk = 1; + } else if (md->special_state.size==2) { + status->max_hp<<=1; + status->max_sp<<=1; + status->hp=status->max_hp; + status->sp=status->max_sp; + status->str<<=1; + status->agi<<=1; + status->vit<<=1; + status->int_<<=1; + status->dex<<=1; + status->luk<<=1; + } + } + + status->batk = status_base_atk(&md->bl, status); + status_calc_misc(status, md->level); + + if(flag&4) + { // Strengthen Guardians - custom value +10% / lv + struct guild_castle *gc; + gc=guild_mapname2gc(map[md->bl.m].name); + if (!gc) + ShowError("status_calc_mob: No castle set at map %s\n", map[md->bl.m].name); + else { + status->max_hp += 2000 * gc->defense; + status->max_sp += 200 * gc->defense; + if (md->guardian_data->number < MAX_GUARDIANS) //Spawn with saved HP + status->hp = gc->guardian[md->guardian_data->number].hp; + else //Emperium + status->hp = status->max_hp; + status->sp = status->max_sp; + } + status->batk += status->batk * 10*md->guardian_data->guardup_lv/100; + status->rhw.atk += status->rhw.atk * 10*md->guardian_data->guardup_lv/100; + status->rhw.atk2 += status->rhw.atk2 * 10*md->guardian_data->guardup_lv/100; + status->aspd_rate -= 10*md->guardian_data->guardup_lv; + } + + if(!battle_config.enemy_str) + status->batk = 0; + + if(battle_config.enemy_critical_rate != 100) + status->cri = status->cri*battle_config.enemy_critical_rate/100; + if (!status->cri && battle_config.enemy_critical_rate) + status->cri = 1; + + if (!battle_config.enemy_perfect_flee) + status->flee2 = 0; + + //Initial battle status + if (!first) { + status_cpy(&md->status, status); + if (md->sc.count) + status_calc_bl(&md->bl, SCB_ALL); + } else + memcpy(&md->status, status, sizeof(struct status_data)); + return 1; +} + +//Skotlex: Calculates the stats of the given pet. +int status_calc_pet(struct pet_data *pd, int first) +{ + struct map_session_data *sd; + int lv; + + nullpo_retr(0, pd); + sd = pd->msd; + if(!sd || sd->status.pet_id == 0 || sd->pd == NULL) + return 0; + + if (first) { + memcpy(&pd->status, &pd->db->status, sizeof(struct status_data)); + pd->status.speed = pd->petDB->speed; + } + + if (battle_config.pet_lv_rate) + { + lv =sd->status.base_level*battle_config.pet_lv_rate/100; + if (lv < 0) + lv = 1; + if (lv != sd->pet.level || first) + { + struct status_data *bstat = &pd->db->status, *status = &pd->status; + sd->pet.level = lv; + if (!first) //Lv Up animation + clif_misceffect(&pd->bl, 0); + status->rhw.atk = (bstat->rhw.atk*lv)/pd->db->lv; + status->rhw.atk2 = (bstat->rhw.atk2*lv)/pd->db->lv; + status->str = (bstat->str*lv)/pd->db->lv; + status->agi = (bstat->agi*lv)/pd->db->lv; + status->vit = (bstat->vit*lv)/pd->db->lv; + status->int_ = (bstat->int_*lv)/pd->db->lv; + status->dex = (bstat->dex*lv)/pd->db->lv; + status->luk = (bstat->luk*lv)/pd->db->lv; + + if(status->rhw.atk > battle_config.pet_max_atk1) + status->rhw.atk = battle_config.pet_max_atk1; + if(status->rhw.atk2 > battle_config.pet_max_atk2) + status->rhw.atk2 = battle_config.pet_max_atk2; + + if(status->str > battle_config.pet_max_stats) + status->str = battle_config.pet_max_stats; + else if (status->str < 1) status->str = 1; + if(status->agi > battle_config.pet_max_stats) + status->agi = battle_config.pet_max_stats; + else if (status->agi < 1) status->agi = 1; + if(status->vit > battle_config.pet_max_stats) + status->vit = battle_config.pet_max_stats; + else if (status->vit < 1) status->vit = 1; + if(status->int_ > battle_config.pet_max_stats) + status->int_ = battle_config.pet_max_stats; + else if (status->int_ < 1) status->int_ = 1; + if(status->dex > battle_config.pet_max_stats) + status->dex = battle_config.pet_max_stats; + else if (status->dex < 1) status->dex = 1; + if(status->luk > battle_config.pet_max_stats) + status->luk = battle_config.pet_max_stats; + else if (status->luk < 1) status->luk = 1; + + status->batk = status_base_atk(&pd->bl, &pd->status); + status_calc_misc(&pd->status, lv); + if (!battle_config.pet_str) + pd->status.batk = 0; + if (!first) //Not done the first time because the pet is not visible yet + clif_send_petstatus(sd); + } + } else if (first) { + pd->status.batk = status_base_atk(&pd->bl, &pd->status); + status_calc_misc(&pd->status, pd->db->lv); + if (!battle_config.pet_str) + pd->status.batk = 0; + } + + //Support rate modifier (1000 = 100%) + pd->rate_fix = 1000*(sd->pet.intimate - battle_config.pet_support_min_friendly)/(1000- battle_config.pet_support_min_friendly) +500; + if(battle_config.pet_support_rate != 100) + pd->rate_fix = pd->rate_fix*battle_config.pet_support_rate/100; + return 1; +} + +static unsigned int status_base_pc_maxhp(struct map_session_data* sd, struct status_data *status) +{ + unsigned int val; + val = (3500 + sd->status.base_level*hp_coefficient2[sd->status.class_] + + hp_sigma_val[sd->status.class_][sd->status.base_level-1])/100 + * (100 + status->vit)/100 + sd->param_equip[2]; + if (sd->class_&JOBL_UPPER) + val += val * 30/100; + else if (sd->class_&JOBL_BABY) + val -= val * 30/100; + if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->char_id, MAPID_TAEKWON)) + val *= 3; //Triple max HP for top ranking Taekwons over level 90. + return val; +} + +static unsigned int status_base_pc_maxsp(struct map_session_data* sd, struct status_data *status) +{ + unsigned int val; + val = (1000 + sd->status.base_level*sp_coefficient[sd->status.class_])/100 + * (100 + status->int_)/100 + sd->param_equip[3]; + if (sd->class_&JOBL_UPPER) + val += val * 30/100; + else if (sd->class_&JOBL_BABY) + val -= val * 30/100; + if ((sd->class_&MAPID_UPPERMASK) == MAPID_TAEKWON && sd->status.base_level >= 90 && pc_famerank(sd->char_id, MAPID_TAEKWON)) + val *= 3; //Triple max SP for top ranking Taekwons over level 90. + + return val; +} + + +//Calculates player data from scratch without counting SC adjustments. +//Should be invoked whenever players raise stats, learn passive skills or change equipment. +int status_calc_pc(struct map_session_data* sd,int first) +{ + static int calculating = 0; //Check for recursive call preemption. [Skotlex] + struct status_data b_status, *status; + struct weapon_atk b_lhw; + struct skill b_skill[MAX_SKILL]; + + int b_weight,b_max_weight; + int b_paramcard[6]; + int i,index; + int skill,refinedef=0; + + if (++calculating > 10) //Too many recursive calls! + return -1; + + memcpy(&b_status, &sd->battle_status, sizeof(struct status_data)); + memcpy(&b_lhw, &sd->battle_lhw, sizeof(struct weapon_atk)); + b_status.lhw = &b_lhw; + + memcpy(b_skill,&sd->status.skill,sizeof(b_skill)); + b_weight = sd->weight; + b_max_weight = sd->max_weight; + + pc_calc_skilltree(sd); // スキルツリ?の計算 + + sd->max_weight = max_weight_base[sd->status.class_]+sd->status.str*300; + + if(first&1) { + //Load Hp/SP from char-received data. + sd->battle_status.hp = sd->status.hp; + sd->battle_status.sp = sd->status.sp; + sd->battle_status.lhw = &sd->battle_lhw; + sd->base_status.lhw = &sd->base_lhw; + + sd->weight=0; + for(i=0;istatus.inventory[i].nameid==0 || sd->inventory_data[i] == NULL) + continue; + sd->weight += sd->inventory_data[i]->weight*sd->status.inventory[i].amount; + } + sd->cart_max_weight=battle_config.max_cart_weight; + sd->cart_weight=0; + sd->cart_max_num=MAX_CART; + sd->cart_num=0; + for(i=0;istatus.cart[i].nameid==0) + continue; + sd->cart_weight+=itemdb_weight(sd->status.cart[i].nameid)*sd->status.cart[i].amount; + sd->cart_num++; + } + } + + status = &sd->base_status; + // these are not zeroed. [zzo] + sd->hprate=100; + sd->sprate=100; + sd->castrate=100; + sd->delayrate=100; + sd->dsprate=100; + sd->speed_rate = 100; + sd->hprecov_rate = 100; + sd->sprecov_rate = 100; + sd->atk_rate = sd->matk_rate = 100; + sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100; + sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100; + + // zeroed arays, order follows the order in map.h. + // add new arrays to the end of zeroed area in map.h (see comments) and size here. [zzo] + memset (sd->param_bonus, 0, sizeof(sd->param_bonus) + + sizeof(sd->param_equip) + + sizeof(sd->subele) + + sizeof(sd->subrace) + + sizeof(sd->subrace2) + + sizeof(sd->subsize) + + sizeof(sd->addeff) + + sizeof(sd->addeff2) + + sizeof(sd->reseff) + + sizeof(sd->weapon_coma_ele) + + sizeof(sd->weapon_coma_race) + + sizeof(sd->arrow_addele) + + sizeof(sd->arrow_addrace) + + sizeof(sd->arrow_addsize) + + sizeof(sd->arrow_addeff) + + sizeof(sd->arrow_addeff2) + + sizeof(sd->magic_addele) + + sizeof(sd->magic_addrace) + + sizeof(sd->magic_addsize) + + sizeof(sd->critaddrace) + + sizeof(sd->expaddrace) + + sizeof(sd->itemhealrate) + + sizeof(sd->addeff3) + + sizeof(sd->addeff3_type) + + sizeof(sd->sp_gain_race) + ); + + memset (&sd->right_weapon.overrefine, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods)); + memset (&sd->left_weapon.overrefine, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods)); + + memset(&sd->special_state,0,sizeof(sd->special_state)); + memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)+sizeof(status->lhw))); + memset(status->lhw, 0, sizeof(struct weapon_atk)); + + //FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex] + status->speed = DEFAULT_WALK_SPEED; + status->aspd_rate = 100; + status->mode = MD_CANMOVE|MD_CANATTACK|MD_LOOTER|MD_ASSIST|MD_AGGRESSIVE|MD_CASTSENSOR; + status->size = (sd->class_&JOBL_BABY)?0:1; + if (battle_config.character_size && pc_isriding(sd)) { //[Lupus] + if (sd->class_&JOBL_BABY) { + if (battle_config.character_size&2) + status->size++; + } if(battle_config.character_size&1) + status->size++; + } + status->aspd_rate = 100; + status->ele_lv = 1; + status->race = RC_DEMIHUMAN; + + //zero up structures... + memset(&sd->autospell,0,sizeof(sd->autospell) + + sizeof(sd->autospell2) + + sizeof(sd->skillatk) + + sizeof(sd->skillblown) + + sizeof(sd->add_def) + + sizeof(sd->add_mdef) + + sizeof(sd->add_dmg) + + sizeof(sd->add_mdmg) + + sizeof(sd->add_drop) + ); + + // vars zeroing. ints, shorts, chars. in that order. + memset (&sd->arrow_atk, 0,sizeof(sd->arrow_atk) + + sizeof(sd->arrow_ele) + + sizeof(sd->arrow_cri) + + sizeof(sd->arrow_hit) + + sizeof(sd->nhealhp) + + sizeof(sd->nhealsp) + + sizeof(sd->nshealhp) + + sizeof(sd->nshealsp) + + sizeof(sd->nsshealhp) + + sizeof(sd->nsshealsp) + + sizeof(sd->critical_def) + + sizeof(sd->double_rate) + + sizeof(sd->long_attack_atk_rate) + + sizeof(sd->near_attack_def_rate) + + sizeof(sd->long_attack_def_rate) + + sizeof(sd->magic_def_rate) + + sizeof(sd->misc_def_rate) + + sizeof(sd->ignore_mdef_ele) + + sizeof(sd->ignore_mdef_race) + + sizeof(sd->perfect_hit) + + sizeof(sd->perfect_hit_add) + + sizeof(sd->get_zeny_rate) + + sizeof(sd->get_zeny_num) + + sizeof(sd->double_add_rate) + + sizeof(sd->short_weapon_damage_return) + + sizeof(sd->long_weapon_damage_return) + + sizeof(sd->magic_damage_return) + + sizeof(sd->random_attack_increase_add) + + sizeof(sd->random_attack_increase_per) + + sizeof(sd->break_weapon_rate) + + sizeof(sd->break_armor_rate) + + sizeof(sd->crit_atk_rate) + + sizeof(sd->hp_loss_rate) + + sizeof(sd->sp_loss_rate) + + sizeof(sd->classchange) + + sizeof(sd->speed_add_rate) + + sizeof(sd->aspd_add_rate) + + sizeof(sd->setitem_hash) + + sizeof(sd->setitem_hash2) + // shorts + + sizeof(sd->splash_range) + + sizeof(sd->splash_add_range) + + sizeof(sd->add_steal_rate) + + sizeof(sd->hp_loss_value) + + sizeof(sd->sp_loss_value) + + sizeof(sd->hp_loss_type) + + sizeof(sd->hp_gain_value) + + sizeof(sd->sp_gain_value) + + sizeof(sd->add_drop_count) + + sizeof(sd->unbreakable) + + sizeof(sd->unbreakable_equip) + + sizeof(sd->unstripable_equip) + + sizeof(sd->no_regen) + + sizeof(sd->add_def_count) + + sizeof(sd->add_mdef_count) + + sizeof(sd->add_dmg_count) + + sizeof(sd->add_mdmg_count) + ); + + for(i=0;i<10;i++) { + current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus] + if(index < 0) + continue; + if(i == 9 && sd->equip_index[8] == index) + continue; + if(i == 5 && sd->equip_index[4] == index) + continue; + if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) + continue; + + if(sd->inventory_data[index]) { + int j,c; + struct item_data *data; + + //Card script execution. + if(sd->status.inventory[index].card[0]==0x00ff || + sd->status.inventory[index].card[0]==0x00fe || + sd->status.inventory[index].card[0]==(short)0xff00) + continue; + for(j=0;jinventory_data[index]->slot;j++){ + current_equip_card_id= c= sd->status.inventory[index].card[j]; + if(!c) + continue; + data = itemdb_exists(c); + if(!data) + continue; + if(first&1 && data->equip_script) + { //Execute equip-script on login + run_script(data->equip_script,0,sd->bl.id,0); + if (!calculating) + return 1; + } + if(!data->script) + continue; + if(data->flag.no_equip) { //Card restriction checks. + if(map[sd->bl.m].flag.restricted && data->flag.no_equip&map[sd->bl.m].zone) + continue; + if(map[sd->bl.m].flag.pvp && data->flag.no_equip&1) + continue; + if(map_flag_gvg(sd->bl.m) && data->flag.no_equip&2) + continue; + } + if(i == 8 && sd->status.inventory[index].equip == 0x20) + { //Left hand status. + sd->state.lr_flag = 1; + run_script(data->script,0,sd->bl.id,0); + sd->state.lr_flag = 0; + } else + run_script(data->script,0,sd->bl.id,0); + if (!calculating) //Abort, run_script his function. [Skotlex] + return 1; + } + } + } + + if(sd->status.pet_id > 0 && battle_config.pet_status_support && sd->pet.intimate > 0) + { // Pet + struct pet_data *pd=sd->pd; + if(pd && (!battle_config.pet_equip_required || pd->equip > 0) && + pd->state.skillbonus == 1 && pd->bonus) //Skotlex: Readjusted for pets + pc_bonus(sd,pd->bonus->type, pd->bonus->val); + } + memcpy(b_paramcard,sd->param_bonus,sizeof(b_paramcard)); + memset(sd->param_bonus, 0, sizeof(sd->param_bonus)); + + // ?備品によるステ?タス?化はここで?行 + for(i=0;i<10;i++) { + current_equip_item_index = index = sd->equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus] + if(index < 0) + continue; + if(i == 9 && sd->equip_index[8] == index) + continue; + if(i == 5 && sd->equip_index[4] == index) + continue; + if(i == 6 && (sd->equip_index[5] == index || sd->equip_index[4] == index)) + continue; + if(!sd->inventory_data[index]) + continue; + + status->def += sd->inventory_data[index]->def; + + if(first&1 && sd->inventory_data[index]->equip_script) + { //Execute equip-script on login + run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0); + if (!calculating) + return 1; + } + + if(sd->inventory_data[index]->type == 4) { + int r,wlv = sd->inventory_data[index]->wlv; + struct weapon_data *wd; + struct weapon_atk *wa; + + if (wlv >= MAX_REFINE_BONUS) + wlv = MAX_REFINE_BONUS - 1; + if(i == 8 && sd->status.inventory[index].equip == 0x20) { + wd = &sd->left_weapon; // Left-hand weapon + wa = status->lhw; + } else { + wd = &sd->right_weapon; + wa = &status->rhw; + } + wa->atk += sd->inventory_data[index]->atk; + wa->atk2 = (r=sd->status.inventory[index].refine)*refinebonus[wlv][0]; + if((r-=refinebonus[wlv][2])>0) //Overrefine bonus. + wd->overrefine = r*refinebonus[wlv][1]; + + wa->range += sd->inventory_data[index]->range; + if(sd->inventory_data[index]->script) { + if (wd == &sd->left_weapon) { + sd->state.lr_flag = 1; + run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); + sd->state.lr_flag = 0; + } else + run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); + if (!calculating) //Abort, run_script retriggered this. [Skotlex] + return 1; + } + + if(sd->status.inventory[index].card[0]==0x00ff) + { // Forged weapon + wd->star += (sd->status.inventory[index].card[1]>>8); + if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg + if(pc_famerank( MakeDWord(sd->status.inventory[index].card[2],sd->status.inventory[index].card[3]) ,MAPID_BLACKSMITH)) + wd->star += 10; + + if (!wa->ele) //Do not overwrite element from previous bonuses. + wa->ele = (sd->status.inventory[index].card[1]&0x0f); + } + } + else if(sd->inventory_data[index]->type == 5) { + refinedef += sd->status.inventory[index].refine*refinebonus[0][0]; + if(sd->inventory_data[index]->script) { + run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); + if (!calculating) //Abort, run_script retriggered this. [Skotlex] + return 1; + } + } + } + + if(sd->equip_index[10] >= 0){ // 矢 + index = sd->equip_index[10]; + if(sd->inventory_data[index]){ // Arrows + sd->state.lr_flag = 2; + run_script(sd->inventory_data[index]->script,0,sd->bl.id,0); + sd->state.lr_flag = 0; + sd->arrow_atk += sd->inventory_data[index]->atk; + } + } + + //Store equipment script bonuses + memcpy(sd->param_equip,sd->param_bonus,sizeof(sd->param_equip)); + //We store card bonuses here because Improve Concentration is the only SC + //that will not take it into consideration when buffing you up. + memcpy(sd->param_bonus, b_paramcard, sizeof(sd->param_bonus)); + + status->def += (refinedef+50)/100; + + if(status->rhw.range < 1) status->rhw.range = 1; + if(status->lhw->range < 1) status->lhw->range = 1; + if(status->rhw.range < status->lhw->range) + status->rhw.range = status->lhw->range; + + sd->double_rate += sd->double_add_rate; + sd->perfect_hit += sd->perfect_hit_add; + sd->splash_range += sd->splash_add_range; + if(sd->aspd_add_rate) + sd->aspd_rate += sd->aspd_add_rate; + if(sd->speed_add_rate) + sd->speed_rate += sd->speed_add_rate; + + // Damage modifiers from weapon type + sd->right_weapon.atkmods[0] = atkmods[0][sd->weapontype1]; + sd->right_weapon.atkmods[1] = atkmods[1][sd->weapontype1]; + sd->right_weapon.atkmods[2] = atkmods[2][sd->weapontype1]; + sd->left_weapon.atkmods[0] = atkmods[0][sd->weapontype2]; + sd->left_weapon.atkmods[1] = atkmods[1][sd->weapontype2]; + sd->left_weapon.atkmods[2] = atkmods[2][sd->weapontype2]; + +// ----- STATS CALCULATION ----- + + // Job bonuses + for(i=0;i<(int)sd->status.job_level && istatus.class_][i]) + continue; + switch(job_bonus[sd->status.class_][i]) { + case 1: + status->str++; + break; + case 2: + status->agi++; + break; + case 3: + status->vit++; + break; + case 4: + status->int_++; + break; + case 5: + status->dex++; + break; + case 6: + status->luk++; + break; + } + } + + // If a Super Novice has never died and is at least joblv 70, he gets all stats +10 + if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->die_counter == 0 && sd->status.job_level >= 70){ + status->str += 10; + status->agi += 10; + status->vit += 10; + status->int_+= 10; + status->dex += 10; + status->luk += 10; + } + + // Absolute modifiers from passive skills + if(pc_checkskill(sd,BS_HILTBINDING)>0) + status->str++; + if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0) + status->int_ += (skill+1)/2; // +1 INT / 2 lv + if((skill=pc_checkskill(sd,AC_OWL))>0) + status->dex += skill; + + // Bonuses from cards and equipment as well as base stat, remember to avoid overflows. + i = status->str + sd->status.str + b_paramcard[0] + sd->param_equip[0]; + status->str = i<0?0:(i>USHRT_MAX?USHRT_MAX:i); + i = status->agi + sd->status.agi + b_paramcard[1] + sd->param_equip[1]; + status->agi = i<0?0:(i>USHRT_MAX?USHRT_MAX:i); + i = status->vit + sd->status.vit + b_paramcard[2] + sd->param_equip[2]; + status->vit = i<0?0:(i>USHRT_MAX?USHRT_MAX:i); + i = status->int_+ sd->status.int_+ b_paramcard[3] + sd->param_equip[3]; + status->int_ = i<0?0:(i>USHRT_MAX?USHRT_MAX:i); + i = status->dex + sd->status.dex + b_paramcard[4] + sd->param_equip[4]; + status->dex = i<0?0:(i>USHRT_MAX?USHRT_MAX:i); + i = status->luk + sd->status.luk + b_paramcard[5] + sd->param_equip[5]; + status->luk = i<0?0:(i>USHRT_MAX?USHRT_MAX:i); + +// ------ BASE ATTACK CALCULATION ------ + + // Basic Base ATK value + status->batk += status_base_atk(&sd->bl,status); + // weapon-type bonus (FIXME: Why is the weapon_atk bonus applied to base attack?) + if (sd->status.weapon < MAX_WEAPON_TYPE && sd->weapon_atk[sd->status.weapon]) + status->batk += sd->weapon_atk[sd->status.weapon]; + // Absolute modifiers from passive skills + if((skill=pc_checkskill(sd,BS_HILTBINDING))>0) + status->batk += 4; + +// ----- MATK CALCULATION ----- + + // Basic MATK value + status->matk_max += status->int_+(status->int_/5)*(status->int_/5); + status->matk_min += status->int_+(status->int_/7)*(status->int_/7); + +// ----- CRIT CALCULATION ----- + + // Basic Crit value + status->cri += (status->luk*3)+10; + +// ----- HIT CALCULATION ----- + + // Basic Hit value + status->hit += status->dex + sd->status.base_level; + + // Absolute modifiers from passive skills + if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0) + status->hit += skill*2; + if((skill=pc_checkskill(sd,AC_VULTURE))>0){ + status->hit += skill; + if(sd->status.weapon == W_BOW) + status->rhw.range += skill; + } + if(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE) + { + if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0) + status->hit += 2*skill; + if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) { + status->hit += skill; + status->rhw.range += skill; + } + } + +// ----- FLEE CALCULATION ----- + + // Basic Flee value + status->flee += status->agi + sd->status.base_level; + + // Absolute modifiers from passive skills + if((skill=pc_checkskill(sd,TF_MISS))>0) + status->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3); + if((skill=pc_checkskill(sd,MO_DODGE))>0) + status->flee += (skill*3)>>1; + +// ----- PERFECT DODGE CALCULATION ----- + + // Basic Perfect Dodge value + status->flee2 += status->luk+10; + +// ----- VIT-DEF CALCULATION ----- + + // Basic VIT-DEF value + status->def2 += status->vit; + +// ----- EQUIPMENT-DEF CALCULATION ----- + + // Apply relative modifiers from equipment + if(sd->def_rate != 100) + status->def = status->def * sd->def_rate/100; + + if (!battle_config.weapon_defense_type && status->def > battle_config.max_def) + { + status->def2 += battle_config.over_def_bonus*(status->def -battle_config.max_def); + status->def = battle_config.max_def; + } + +// ----- INT-MDEF CALCULATION ----- + + // Basic INT-MDEF value + status->mdef2 += status->int_ + (status->vit>>1); + +// ----- EQUIPMENT-MDEF CALCULATION ----- + + // Apply relative modifiers from equipment + if(sd->mdef_rate != 100) + status->mdef = status->mdef * sd->mdef_rate/100; + + if (!battle_config.weapon_defense_type && status->mdef > battle_config.max_def) + { + status->mdef2 += battle_config.over_def_bonus*(status->mdef -battle_config.max_def); + status->mdef = battle_config.max_def; + } + +// ----- WALKING SPEED CALCULATION ----- + + // Relative modifiers from passive skills + if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0) + status->speed -= status->speed * 25/100; + if(pc_iscarton(sd) && (skill=pc_checkskill(sd,MC_PUSHCART))>0) + status->speed += status->speed * (100-10*skill)/100; + +// ----- ASPD CALCULATION ----- +// Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied + + // Basic ASPD value + if (sd->status.weapon < MAX_WEAPON_TYPE) + status->amotion = aspd_base[sd->status.class_][sd->status.weapon]-(status->agi*4+status->dex)*aspd_base[sd->status.class_][sd->status.weapon]/1000; + else + status->amotion = ( + (aspd_base[sd->status.class_][sd->weapontype1] + -(status->agi*4+status->dex)*aspd_base[sd->status.class_][sd->weapontype1]/1000) + + (aspd_base[sd->status.class_][sd->weapontype2] + -(status->agi*4+status->dex)*aspd_base[sd->status.class_][sd->weapontype2]/1000) + ) *2/3; //From what I read in rodatazone, 2/3 should be more accurate than 0.7 -> 140 / 200; [Skotlex] + + // Relative modifiers from passive skills + if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK) + status->aspd_rate -= (skill/2); + if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd)) + status->aspd_rate -= (skill*3); + if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 && + (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE)) + status->aspd_rate -= (skill/2); + if(pc_isriding(sd)) + status->aspd_rate += 50-10*pc_checkskill(sd,KN_CAVALIERMASTERY); + + status->adelay = 2*status->amotion; + + +// ----- DMOTION ----- +// + status->dmotion = 800-status->agi*4; + if(status->dmotion<400) status->dmotion = 400; + +// ----- HP MAX CALCULATION ----- + + // Basic MaxHP value + //We hold the standard Max HP here to make it faster to recalculate on vit changes. + sd->status.max_hp = status_base_pc_maxhp(sd,status); + status->max_hp += sd->status.max_hp; + + if((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99) + status->max_hp += 2000; + + // Absolute modifiers from passive skills + if((skill=pc_checkskill(sd,CR_TRUST))>0) + status->max_hp += skill*200; + +// ----- SP MAX CALCULATION ----- + + // Basic MaxSP value + sd->status.max_sp = status_base_pc_maxsp(sd,status); + status->max_sp += sd->status.max_sp; + + // Absolute modifiers from passive skills + if((skill=pc_checkskill(sd,SL_KAINA))>0) + status->max_sp += 30*skill; + + if(status->sp>status->max_sp) + status->sp=status->max_sp; + +// ----- RESPAWN HP/SP ----- +// + //Calc respawn hp and store it on base_status + if (sd->special_state.restart_full_recover) + { + status->hp = status->max_hp; + status->sp = status->max_sp; + } else { + if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2) + && battle_config.restart_hp_rate < 50) + status->hp=status->max_hp>>1; + else + status->hp=status->max_hp * battle_config.restart_hp_rate/100; + if(status->hp < 0) + status->hp = 1; + + status->sp = status->max_sp * battle_config.restart_sp_rate /100; + } + +// ----- MISC CALCULATIONS ----- + + // Weight + if((skill=pc_checkskill(sd,MC_INCCARRY))>0) + sd->max_weight += 2000*skill; + if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0) + sd->max_weight += 10000; + if(sd->sc.data[SC_KNOWLEDGE].timer != -1) + sd->max_weight += sd->max_weight*sd->sc.data[SC_KNOWLEDGE].val1/10; + + // Skill SP cost + if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 ) + sd->dsprate -= 4*skill; + + if(sd->sc.count){ + if(sd->sc.data[SC_SERVICE4U].timer!=-1) + sd->dsprate -= sd->sc.data[SC_SERVICE4U].val3; + } + + if(sd->dsprate < 0) sd->dsprate = 0; + + // Anti-element and anti-race + if((skill=pc_checkskill(sd,CR_TRUST))>0) + sd->subele[6] += skill*5; + if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) { + sd->subele[0] += skill; + sd->subele[3] += skill*4; + } + if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){ + skill = skill*4; + sd->right_weapon.addrace[RC_DRAGON]+=skill; + sd->left_weapon.addrace[RC_DRAGON]+=skill; + sd->magic_addrace[RC_DRAGON]+=skill; + sd->subrace[RC_DRAGON]+=skill; + } + + if(sd->sc.count){ + if(sd->sc.data[SC_CONCENTRATE].timer!=-1) + { //Update the card-bonus data + sd->sc.data[SC_CONCENTRATE].val3 = sd->param_bonus[1]; //Agi + sd->sc.data[SC_CONCENTRATE].val4 = sd->param_bonus[4]; //Dex + } + if(sd->sc.data[SC_SIEGFRIED].timer!=-1){ + sd->subele[1] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[2] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[3] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[4] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[5] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[6] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[7] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[8] += sd->sc.data[SC_SIEGFRIED].val2; + sd->subele[9] += sd->sc.data[SC_SIEGFRIED].val2; + } + if(sd->sc.data[SC_PROVIDENCE].timer!=-1){ + sd->subele[6] += sd->sc.data[SC_PROVIDENCE].val2; + sd->subrace[RC_DEMON] += sd->sc.data[SC_PROVIDENCE].val2; + } + } + + status_cpy(&sd->battle_status, status); + status_calc_bl(&sd->bl, SCB_ALL); //Status related changes. + status = &sd->battle_status; //Need to compare versus this. + +// ----- CLIENT-SIDE REFRESH ----- + if(first&4) { + calculating = 0; + return 0; + } + if(first&3) { + clif_updatestatus(sd,SP_SPEED); + clif_updatestatus(sd,SP_MAXHP); + clif_updatestatus(sd,SP_MAXSP); + if(first&1) { + clif_updatestatus(sd,SP_HP); + clif_updatestatus(sd,SP_SP); + } + calculating = 0; + return 0; + } + + if(memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill))) + clif_skillinfoblock(sd); + if(b_status.speed != status->speed) + clif_updatestatus(sd,SP_SPEED); + if(b_weight != sd->weight) + clif_updatestatus(sd,SP_WEIGHT); + if(b_max_weight != sd->max_weight) { + clif_updatestatus(sd,SP_MAXWEIGHT); + pc_checkweighticon(sd); + } + if(b_status.str != status->str) + clif_updatestatus(sd,SP_STR); + if(b_status.agi != status->agi) + clif_updatestatus(sd,SP_AGI); + if(b_status.vit != status->vit) + clif_updatestatus(sd,SP_VIT); + if(b_status.int_ != status->int_) + clif_updatestatus(sd,SP_INT); + if(b_status.dex != status->dex) + clif_updatestatus(sd,SP_DEX); + if(b_status.luk != status->luk) + clif_updatestatus(sd,SP_LUK); + if(b_status.hit != status->hit) + clif_updatestatus(sd,SP_HIT); + if(b_status.flee != status->flee) + clif_updatestatus(sd,SP_FLEE1); + if(b_status.amotion != status->amotion) + clif_updatestatus(sd,SP_ASPD); + if(b_status.rhw.atk != status->rhw.atk || + b_status.lhw->atk != status->lhw->atk || + b_status.batk != status->batk) + clif_updatestatus(sd,SP_ATK1); + if(b_status.def != status->def) + clif_updatestatus(sd,SP_DEF1); + if(b_status.rhw.atk2 != status->rhw.atk2 || + b_status.lhw->atk2 != status->lhw->atk2) + clif_updatestatus(sd,SP_ATK2); + if(b_status.def2 != status->def2) + clif_updatestatus(sd,SP_DEF2); + if(b_status.flee2 != status->flee2) + clif_updatestatus(sd,SP_FLEE2); + if(b_status.cri != status->cri) + clif_updatestatus(sd,SP_CRITICAL); + if(b_status.matk_max != status->matk_max) + clif_updatestatus(sd,SP_MATK1); + if(b_status.matk_min != status->matk_min) + clif_updatestatus(sd,SP_MATK2); + if(b_status.mdef != status->mdef) + clif_updatestatus(sd,SP_MDEF1); + if(b_status.mdef2 != status->mdef2) + clif_updatestatus(sd,SP_MDEF2); + if(b_status.rhw.range != status->rhw.range) + clif_updatestatus(sd,SP_ATTACKRANGE); + if(b_status.max_hp != status->max_hp) + clif_updatestatus(sd,SP_MAXHP); + if(b_status.max_sp != status->max_sp) + clif_updatestatus(sd,SP_MAXSP); + if(b_status.hp != status->hp) + clif_updatestatus(sd,SP_HP); + if(b_status.sp != status->sp) + clif_updatestatus(sd,SP_SP); + + calculating = 0; + return 0; +} + +static unsigned short status_calc_str(struct block_list *,struct status_change *,int); +static unsigned short status_calc_agi(struct block_list *,struct status_change *,int); +static unsigned short status_calc_vit(struct block_list *,struct status_change *,int); +static unsigned short status_calc_int(struct block_list *,struct status_change *,int); +static unsigned short status_calc_dex(struct block_list *,struct status_change *,int); +static unsigned short status_calc_luk(struct block_list *,struct status_change *,int); +static unsigned short status_calc_batk(struct block_list *,struct status_change *,int); +static unsigned short status_calc_watk(struct block_list *,struct status_change *,int); +static unsigned short status_calc_matk(struct block_list *,struct status_change *,int); +static unsigned short status_calc_hit(struct block_list *,struct status_change *,int); +static unsigned short status_calc_critical(struct block_list *,struct status_change *,int); +static unsigned short status_calc_flee(struct block_list *,struct status_change *,int); +static unsigned short status_calc_flee2(struct block_list *,struct status_change *,int); +static unsigned char status_calc_def(struct block_list *,struct status_change *,int); +static unsigned short status_calc_def2(struct block_list *,struct status_change *,int); +static unsigned char status_calc_mdef(struct block_list *,struct status_change *,int); +static unsigned short status_calc_mdef2(struct block_list *,struct status_change *,int); +static unsigned short status_calc_speed(struct block_list *,struct status_change *,int); +static short status_calc_aspd_rate(struct block_list *,struct status_change *,int); +static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion); +static unsigned int status_calc_maxhp(struct block_list *,struct status_change *,unsigned int); +static unsigned int status_calc_maxsp(struct block_list *,struct status_change *,unsigned int); +static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element); +static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv); +static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode); + +//Calculates some attributes that depends on modified stats from status changes. +void status_calc_bl_sub_pc(struct map_session_data *sd, unsigned long flag) +{ + struct status_data *status = &sd->battle_status, *b_status = &sd->base_status; + int skill; + + if(flag&(SCB_MAXHP|SCB_VIT)) + { + flag|=SCB_MAXHP; //Ensures client-side refresh + + status->max_hp = status_base_pc_maxhp(sd,status); + status->max_hp += b_status->max_hp - sd->status.max_hp; + + status->max_hp = status_calc_maxhp(&sd->bl, &sd->sc, status->max_hp); + // Apply relative modifiers from equipment + if(sd->hprate!=100) + status->max_hp = status->max_hp * sd->hprate/100; + if(battle_config.hp_rate != 100) + status->max_hp = status->max_hp * battle_config.hp_rate/100; + + if(status->max_hp > battle_config.max_hp) + status->max_hp = battle_config.max_hp; + else if(!status->max_hp) + status->max_hp = 1; + + if(status->hp > status->max_hp) { + status->hp = status->max_hp; + clif_updatestatus(sd,SP_HP); + } + + sd->nhealhp = 1 + (status->vit/5) + (status->max_hp/200); + + // Apply relative modifiers from equipment + if(sd->hprecov_rate != 100) + sd->nhealhp = sd->nhealhp*sd->hprecov_rate/100; + + if(sd->nhealhp < 1) sd->nhealhp = 1; + else if(sd->nhealhp > SHRT_MAX) sd->nhealhp = SHRT_MAX; + + // Skill-related HP recovery + if((skill=pc_checkskill(sd,SM_RECOVERY)) > 0) + sd->nshealhp = skill*5 + (status->max_hp*skill/500); + // Skill-related HP recovery (only when sit) + if((skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0) + sd->nsshealhp = skill*4 + (status->max_hp*skill/500); + if((skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest) + sd->nsshealhp = skill*30 + (status->max_hp*skill/500); + + if(sd->nshealhp > SHRT_MAX) sd->nshealhp = SHRT_MAX; + if(sd->nsshealhp > SHRT_MAX) sd->nsshealhp = SHRT_MAX; + } + + if(flag&(SCB_MAXSP|SCB_INT)) + { + flag|=SCB_MAXSP; + + status->max_sp = status_base_pc_maxsp(sd,status); + status->max_sp += b_status->max_sp - sd->status.max_sp; + + if((skill=pc_checkskill(sd,HP_MEDITATIO))>0) + status->max_sp += status->max_sp * skill/100; + if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0) + status->max_sp += status->max_sp * 2*skill/100; + + status->max_sp = status_calc_maxsp(&sd->bl, &sd->sc, status->max_sp); + + // Apply relative modifiers from equipment + if(sd->sprate!=100) + status->max_sp = status->max_sp * sd->sprate/100; + if(battle_config.sp_rate != 100) + status->max_sp = status->max_sp * battle_config.sp_rate/100; + + if(status->max_sp > battle_config.max_sp) + status->max_sp = battle_config.max_sp; + else if(!status->max_sp) + status->max_sp = 1; + + if(status->sp > status->max_sp) { + status->sp = status->max_sp; + clif_updatestatus(sd,SP_SP); + } + + sd->nhealsp = 1 + (status->int_/6) + (status->max_sp/100); + if(status->int_ >= 120) + sd->nhealsp += ((status->int_-120)>>1) + 4; + + // Relative modifiers from passive skills + if((skill=pc_checkskill(sd,HP_MEDITATIO)) > 0) + sd->nhealsp += sd->nhealsp * 3*skill/100; + + // Apply relative modifiers from equipment + if(sd->sprecov_rate != 100) + sd->nhealsp = sd->nhealsp*sd->sprecov_rate/100; + + if(sd->nhealsp > SHRT_MAX) sd->nhealsp = SHRT_MAX; + else if(sd->nhealsp < 1) sd->nhealsp = 1; + + // Skill-related SP recovery + if((skill=pc_checkskill(sd,MG_SRECOVERY)) > 0) + sd->nshealsp = skill*3 + (status->max_sp*skill/500); + if((skill=pc_checkskill(sd,NJ_NINPOU)) > 0) + sd->nshealsp = skill*3 + (status->max_sp*skill/500); + // Skill-related SP recovery (only when sit) + if((skill = pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0) + sd->nsshealsp = skill*2 + (status->max_sp*skill/500); + if((skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest) + { + sd->nsshealsp = skill*3 + (status->max_sp*skill/500); + if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) //Power up Enjoyable Rest + sd->nsshealsp += (30+10*skill)*sd->nsshealsp/100; + } + if(sd->nshealsp > SHRT_MAX) sd->nshealsp = SHRT_MAX; + if(sd->nsshealsp > SHRT_MAX) sd->nsshealsp = SHRT_MAX; + } + + if(flag&SCB_MATK) { + //New matk + status->matk_min = status->int_+(status->int_/7)*(status->int_/7); + status->matk_max = status->int_+(status->int_/5)*(status->int_/5); + + //Bonuses from previous matk + status->matk_max += b_status->matk_max - (b_status->int_+(b_status->int_/5)*(b_status->int_/5)); + status->matk_min += b_status->matk_min - (b_status->int_+(b_status->int_/7)*(b_status->int_/7)); + + status->matk_min = status_calc_matk(&sd->bl, &sd->sc, status->matk_min); + status->matk_max = status_calc_matk(&sd->bl, &sd->sc, status->matk_max); + if(sd->matk_rate != 100){ + status->matk_max = status->matk_max * sd->matk_rate/100; + status->matk_min = status->matk_min * sd->matk_rate/100; + } + + if(sd->sc.data[SC_MAGICPOWER].timer!=-1) { //Store current matk values + sd->sc.data[SC_MAGICPOWER].val3 = status->matk_min; + sd->sc.data[SC_MAGICPOWER].val4 = status->matk_max; + } + } + + if(flag&SCB_HIT) { + if(sd->hit_rate != 100) + status->hit = status->hit * sd->hit_rate/100; + + if(status->hit < 1) status->hit = 1; + } + + if(flag&SCB_FLEE) { + if(sd->flee_rate != 100) + status->flee = status->flee * sd->flee_rate/100; + + if(status->flee < 1) status->flee = 1; + } + + if(flag&SCB_DEF2) { + if(sd->def2_rate != 100) + status->def2 = status->def2 * sd->def2_rate/100; + + if(status->def2 < 1) status->def2 = 1; + } + + if(flag&SCB_MDEF2) { + if(sd->mdef2_rate != 100) + status->mdef2 = status->mdef2 * sd->mdef2_rate/100; + + if(status->mdef2 < 1) status->mdef2 = 1; + } + + if(flag&SCB_SPEED) { + if(sd->speed_rate != 100) + status->speed = status->speed*sd->speed_rate/100; + + if(status->speed < battle_config.max_walk_speed) + status->speed = battle_config.max_walk_speed; + + if ((skill=pc_checkskill(sd,SA_FREECAST))>0) { + //Store casting walk speed for quick restoration. [Skotlex] + sd->prev_speed = status->speed * (175-5*skill)/100; + if(sd->ud.skilltimer != -1) { //Swap speed. + skill = status->speed; + status->speed = sd->prev_speed; + sd->prev_speed = skill; + } + } + } + + if(flag&(SCB_ASPD|SCB_AGI|SCB_DEX)) { + if (sd->status.weapon < MAX_WEAPON_TYPE) + status->amotion = aspd_base[sd->status.class_][sd->status.weapon]-(status->agi*4+status->dex)*aspd_base[sd->status.class_][sd->status.weapon]/1000; + else + status->amotion = ( + (aspd_base[sd->status.class_][sd->weapontype1] + -(status->agi*4+status->dex)*aspd_base[sd->status.class_][sd->weapontype1]/1000) + + (aspd_base[sd->status.class_][sd->weapontype2] + -(status->agi*4+status->dex)*aspd_base[sd->status.class_][sd->weapontype2]/1000) + ) *2/3; //From what I read in rodatazone, 2/3 should be more accurate than 0.7 -> 140 / 200; [Skotlex] + + status->aspd_rate = status_calc_aspd_rate(&sd->bl, &sd->sc , b_status->aspd_rate); + + // Apply all relative modifiers + if(status->aspd_rate != 100) + status->amotion = status->amotion*status->aspd_rate/100; + + if(status->amotion < battle_config.max_aspd) + status->amotion = battle_config.max_aspd; + + status->adelay = 2*status->amotion; + if ((skill=pc_checkskill(sd,SA_FREECAST))>0) { + //Store casting adelay for quick restoration. [Skotlex] + sd->prev_adelay = status->adelay*(150-5*skill)/100; + if(sd->ud.skilltimer != -1) { //Swap adelay. + skill = status->adelay; + status->adelay = sd->prev_adelay; + sd->prev_adelay = skill; + } + } + + } + + if(flag&(SCB_AGI|SCB_DSPD)) { + //Even though people insist this is too slow, packet data reports this is the actual real equation. + status->dmotion = 800-status->agi*4; + if(status->dmotion<400) status->dmotion = 400; + + if(battle_config.pc_damage_delay_rate != 100) + status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100; + + status->dmotion = status_calc_dmotion(&sd->bl, &sd->sc, b_status->dmotion); + } + + + if(flag&SCB_CRI) + { + if(sd->critical_rate != 100) + status->cri = status->cri * sd->critical_rate/100; + + if(status->cri < 10) status->cri = 10; + } + + if(flag&SCB_FLEE2) { + if(sd->flee2_rate != 100) + status->flee2 = status->flee2 * sd->flee2_rate/100; + + if(status->flee2 < 10) status->flee2 = 10; + } + if (flag == SCB_ALL) + return; //Refresh is done on invoking function (status_calc_pc) + + if(flag&SCB_SPEED) + clif_updatestatus(sd,SP_SPEED); + if(flag&SCB_STR) + clif_updatestatus(sd,SP_STR); + if(flag&SCB_AGI) + clif_updatestatus(sd,SP_AGI); + if(flag&SCB_VIT) + clif_updatestatus(sd,SP_VIT); + if(flag&SCB_DEX) + clif_updatestatus(sd,SP_DEX); + if(flag&SCB_LUK) + clif_updatestatus(sd,SP_LUK); + if(flag&SCB_HIT) + clif_updatestatus(sd,SP_HIT); + if(flag&SCB_FLEE) + clif_updatestatus(sd,SP_FLEE1); + if(flag&SCB_ASPD) + clif_updatestatus(sd,SP_ASPD); + if(flag&(SCB_BATK|SCB_WATK)) + clif_updatestatus(sd,SP_ATK1); + if(flag&SCB_DEF) + clif_updatestatus(sd,SP_DEF1); + if(flag&SCB_WATK) + clif_updatestatus(sd,SP_ATK2); + if(flag&SCB_DEF2) + clif_updatestatus(sd,SP_DEF2); + if(flag&SCB_FLEE2) + clif_updatestatus(sd,SP_FLEE2); + if(flag&SCB_CRI) + clif_updatestatus(sd,SP_CRITICAL); + if(flag&SCB_MATK) { + clif_updatestatus(sd,SP_MATK1); + clif_updatestatus(sd,SP_MATK2); + } + if(flag&SCB_MDEF) + clif_updatestatus(sd,SP_MDEF1); + if(flag&SCB_MDEF2) + clif_updatestatus(sd,SP_MDEF2); + if(flag&SCB_RANGE) + clif_updatestatus(sd,SP_ATTACKRANGE); + if(flag&SCB_MAXHP) + clif_updatestatus(sd,SP_MAXHP); + if(flag&SCB_MAXSP) + clif_updatestatus(sd,SP_MAXSP); +} + +void status_calc_bl(struct block_list *bl, unsigned long flag) +{ + struct status_data *b_status, *status; + struct status_change *sc; + int temp; + TBL_PC *sd; + b_status = status_get_base_status(bl); + status = status_get_status_data(bl); + sc = status_get_sc(bl); + + if (!b_status || !status) + return; + + BL_CAST(BL_PC,bl,sd); + + if(sd && flag&SCB_PC) + { //Recalc everything. + status_calc_pc(sd,0); + return; + } + + if(!sd && (!sc || !sc->count)) { //No difference. + status_cpy(status, b_status); + return; + } + + if(flag&SCB_STR) { + status->str = status_calc_str(bl, sc, b_status->str); + flag|=SCB_BATK; + } + + if(flag&SCB_AGI) { + status->agi = status_calc_agi(bl, sc, b_status->agi); + flag|=SCB_FLEE; + } + + if(flag&SCB_VIT) { + status->vit = status_calc_vit(bl, sc, b_status->vit); + flag|=SCB_DEF2|SCB_MDEF2; + } + + if(flag&SCB_INT) { + status->int_ = status_calc_int(bl, sc, b_status->int_); + flag|=SCB_MATK|SCB_MDEF2; + } + + if(flag&SCB_DEX) { + status->dex = status_calc_dex(bl, sc, b_status->dex); + flag|=SCB_BATK|SCB_HIT; + } + + if(flag&SCB_LUK) { + status->luk = status_calc_luk(bl, sc, b_status->luk); + flag|=SCB_CRI|SCB_FLEE2; + } + + if(flag&SCB_BATK && b_status->batk) { + status->batk = status_base_atk(bl,status); + temp = b_status->batk - status_base_atk(bl,b_status); + if (temp) + status->batk += temp; + status->batk = status_calc_batk(bl, sc, status->batk); + } + + if(flag&SCB_WATK) { + status->rhw.atk = status_calc_watk(bl, sc, b_status->rhw.atk); + status->rhw.atk2 = status_calc_watk(bl, sc, b_status->rhw.atk2); + if(status->lhw && b_status->lhw && b_status->lhw->atk) { + if (sd) sd->state.lr_flag = 1; + status->lhw->atk = status_calc_watk(bl, sc, b_status->lhw->atk); + status->lhw->atk2 = status_calc_watk(bl, sc, b_status->lhw->atk2); + if (sd) sd->state.lr_flag = 0; + } + } + + if(flag&SCB_HIT) { + status->hit = status_get_lv(bl) + status->dex; + temp = b_status->dex - status->dex; + if (temp) + status->hit += temp; + status->hit = status_calc_hit(bl, sc, status->hit); + } + + if(flag&SCB_FLEE) { + status->flee = status_get_lv(bl) + status->agi; + temp = b_status->agi - status->agi; + if (temp) + status->flee += temp; + status->flee = status_calc_flee(bl, sc, status->flee); + } + + if(flag&SCB_DEF) + status->def = status_calc_def(bl, sc, b_status->def); + + if(flag&SCB_DEF2) { + status->def2 = status->vit; + temp = b_status->def2 - b_status->vit; + if (temp) + status->def2+=temp; + status->def2 = status_calc_def2(bl, sc, status->def2); + } + + if(flag&SCB_MDEF) + status->mdef = status_calc_mdef(bl, sc, b_status->mdef); + + if(flag&SCB_MDEF2) { + status->mdef2 = status->int_ + (status->vit>>1); + temp = b_status->mdef2 -(b_status->int_ + (b_status->vit>>1)); + if (temp) + status->mdef2+=temp; + status->mdef2 = status_calc_mdef2(bl, sc, status->mdef2); + } + + if(flag&SCB_SPEED) + status->speed = status_calc_speed(bl, sc, b_status->speed); + + if(flag&SCB_CRI && b_status->cri) { + status->cri = status->luk*3 + 10; + temp = b_status->cri - (b_status->luk*3 + 10); + if (temp) + status->cri += temp; + status->cri = status_calc_critical(bl, sc, status->cri); + } + + if(flag&SCB_FLEE2 && b_status->flee2) { + status->flee2 = status->luk + 10; + temp = b_status->flee2 - b_status->flee2 + 10; + if (temp) + status->flee2 += temp; + status->flee2 = status_calc_flee2(bl, sc, status->flee2); + } + + if(flag&SCB_ATK_ELE) { + status->rhw.ele = status_calc_attack_element(bl, sc, b_status->rhw.ele); + if(status->lhw && b_status->lhw) { + if (sd) sd->state.lr_flag = 1; + status->lhw->ele = status_calc_attack_element(bl, sc, b_status->lhw->ele); + if (sd) sd->state.lr_flag = 0; + } + } + + if(flag&SCB_DEF_ELE) { + status->def_ele = status_calc_element(bl, sc, b_status->def_ele); + status->ele_lv = status_calc_element_lv(bl, sc, b_status->ele_lv); + } + + if(flag&SCB_MODE) + { + status->mode = status_calc_mode(bl, sc, b_status->mode); + //Since mode changed, reset their state. + unit_stop_attack(bl); + unit_stop_walking(bl,0); + } + +// No status changes alter these yet. +// if(flag&SCB_SIZE) +// if(flag&SCB_RACE) +// if(flag&SCB_RANGE) + + if(sd) { + //The remaining are handled quite different by players, so use their own function. + status_calc_bl_sub_pc(sd, flag); + return; + } + + if(flag&SCB_MAXHP) { + status->max_hp = status_calc_maxhp(bl, sc, b_status->max_hp); + if (status->hp > status->max_hp) //FIXME: Should perhaps a status_zap should be issued? + status->hp = status->max_hp; + } + + if(flag&SCB_MAXSP) { + status->max_sp = status_calc_maxsp(bl, sc, b_status->max_sp); + if (status->sp > status->max_sp) + status->sp = status->max_sp; + } + + if(flag&SCB_MATK) { + status->matk_min = status->int_+(status->int_/7)*(status->int_/7); + status->matk_max = status->int_+(status->int_/5)*(status->int_/5); + status->matk_min = status_calc_matk(bl, sc, status->matk_min); + status->matk_max = status_calc_matk(bl, sc, status->matk_max); + if(sc->data[SC_MAGICPOWER].timer!=-1) { //Store current matk values + sc->data[SC_MAGICPOWER].val3 = status->matk_min; + sc->data[SC_MAGICPOWER].val4 = status->matk_max; + } + } + + if(flag&SCB_ASPD) { + status->aspd_rate = status_calc_aspd_rate(bl, sc , b_status->aspd_rate); + status->amotion = status->aspd_rate*b_status->amotion/100; + status->adelay = status->aspd_rate*b_status->adelay/100; + + if(status->adelay < battle_config.monster_max_aspd<<1) + status->adelay = battle_config.monster_max_aspd<<1; + if(status->amotion < battle_config.monster_max_aspd) + status->amotion = battle_config.monster_max_aspd; + } + + if(flag&SCB_DSPD) + status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion); +} +//Caps values to min/max +#define cap_value(a, min, max) (a>max?max:acount) + return str; + + if(sc->data[SC_INCALLSTATUS].timer!=-1) + str += sc->data[SC_INCALLSTATUS].val1; + if(sc->data[SC_INCSTR].timer!=-1) + str += sc->data[SC_INCSTR].val1; + if(sc->data[SC_STRFOOD].timer!=-1) + str += sc->data[SC_STRFOOD].val1; + if(sc->data[SC_BATTLEORDERS].timer!=-1) + str += 5; + if(sc->data[SC_GUILDAURA].timer != -1 && ((sc->data[SC_GUILDAURA].val4>>12)&0xF)) + str += (sc->data[SC_GUILDAURA].val4>>12)&0xF; + if(sc->data[SC_LOUD].timer!=-1) + str += 4; + if(sc->data[SC_TRUESIGHT].timer!=-1) + str += 5; + if(sc->data[SC_SPURT].timer!=-1) + str += 10; + if(sc->data[SC_NEN].timer!=-1) + str += sc->data[SC_NEN].val1; + if(sc->data[SC_BLESSING].timer != -1){ + if(sc->data[SC_BLESSING].val2) + str += sc->data[SC_BLESSING].val2; + else + str >>= 1; + } + if(sc->data[SC_MARIONETTE].timer!=-1) + str -= (sc->data[SC_MARIONETTE].val3>>16)&0xFF; + if(sc->data[SC_MARIONETTE2].timer!=-1) + str += (sc->data[SC_MARIONETTE2].val3>>16)&0xFF; + if(sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_HIGH && str < 50) + str = 50; + + return cap_value(str,1,USHRT_MAX); +} + +static unsigned short status_calc_agi(struct block_list *bl, struct status_change *sc, int agi) +{ + if(!sc || !sc->count) + return agi; + + if(sc->data[SC_CONCENTRATE].timer!=-1 && sc->data[SC_QUAGMIRE].timer == -1) + agi += (agi-sc->data[SC_CONCENTRATE].val3)*sc->data[SC_CONCENTRATE].val2/100; + if(sc->data[SC_INCALLSTATUS].timer!=-1) + agi += sc->data[SC_INCALLSTATUS].val1; + if(sc->data[SC_INCAGI].timer!=-1) + agi += sc->data[SC_INCAGI].val1; + if(sc->data[SC_AGIFOOD].timer!=-1) + agi += sc->data[SC_AGIFOOD].val1; + if(sc->data[SC_GUILDAURA].timer != -1 && ((sc->data[SC_GUILDAURA].val4>>4)&0xF)) + agi += (sc->data[SC_GUILDAURA].val4>>4)&0xF; + if(sc->data[SC_TRUESIGHT].timer!=-1) + agi += 5; + if(sc->data[SC_INCREASEAGI].timer!=-1) + agi += 2 + sc->data[SC_INCREASEAGI].val1; + if(sc->data[SC_INCREASING].timer!=-1) + agi += 4; // added based on skill updates [Reddozen] + if(sc->data[SC_DECREASEAGI].timer!=-1) + agi -= 2 + sc->data[SC_DECREASEAGI].val1; + if(sc->data[SC_QUAGMIRE].timer!=-1) + agi -= sc->data[SC_QUAGMIRE].val2; + if(sc->data[SC_SUITON].timer!=-1) + agi -= sc->data[SC_SUITON].val2; + if(sc->data[SC_MARIONETTE].timer!=-1) + agi -= (sc->data[SC_MARIONETTE].val3>>8)&0xFF; + if(sc->data[SC_MARIONETTE2].timer!=-1) + agi += (sc->data[SC_MARIONETTE2].val3>>8)&0xFF; + if(sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_HIGH && agi < 50) + agi = 50; + + return cap_value(agi,1,USHRT_MAX); +} + +static unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc, int vit) +{ + if(!sc || !sc->count) + return vit; + + if(sc->data[SC_INCALLSTATUS].timer!=-1) + vit += sc->data[SC_INCALLSTATUS].val1; + if(sc->data[SC_INCVIT].timer!=-1) + vit += sc->data[SC_INCVIT].val1; + if(sc->data[SC_VITFOOD].timer!=-1) + vit += sc->data[SC_VITFOOD].val1; + if(sc->data[SC_GUILDAURA].timer != -1 && ((sc->data[SC_GUILDAURA].val4>>8)&0xF)) + vit += (sc->data[SC_GUILDAURA].val4>>8)&0xF; + if(sc->data[SC_TRUESIGHT].timer!=-1) + vit += 5; + if(sc->data[SC_STRIPARMOR].timer!=-1) + vit -= vit * sc->data[SC_STRIPARMOR].val2/100; + if(sc->data[SC_MARIONETTE].timer!=-1) + vit -= sc->data[SC_MARIONETTE].val3&0xFF; + if(sc->data[SC_MARIONETTE2].timer!=-1) + vit += sc->data[SC_MARIONETTE2].val3&0xFF; + if(sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_HIGH && vit < 50) + vit = 50; + + return cap_value(vit,1,USHRT_MAX); +} + +static unsigned short status_calc_int(struct block_list *bl, struct status_change *sc, int int_) +{ + if(!sc || !sc->count) + return int_; + + if(sc->data[SC_INCALLSTATUS].timer!=-1) + int_ += sc->data[SC_INCALLSTATUS].val1; + if(sc->data[SC_INCINT].timer!=-1) + int_ += sc->data[SC_INCINT].val1; + if(sc->data[SC_INTFOOD].timer!=-1) + int_ += sc->data[SC_INTFOOD].val1; + if(sc->data[SC_BATTLEORDERS].timer!=-1) + int_ += 5; + if(sc->data[SC_TRUESIGHT].timer!=-1) + int_ += 5; + if(sc->data[SC_BLESSING].timer != -1){ + if (sc->data[SC_BLESSING].val2) + int_ += sc->data[SC_BLESSING].val2; + else + int_ >>= 1; + } + if(sc->data[SC_STRIPHELM].timer!=-1) + int_ -= int_ * sc->data[SC_STRIPHELM].val2/100; + if(sc->data[SC_NEN].timer!=-1) + int_ += sc->data[SC_NEN].val1; + if(sc->data[SC_MARIONETTE].timer!=-1) + int_ -= (sc->data[SC_MARIONETTE].val4>>16)&0xFF; + if(sc->data[SC_MARIONETTE2].timer!=-1) + int_ += (sc->data[SC_MARIONETTE2].val4>>16)&0xFF; + if(sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_HIGH && int_ < 50) + int_ = 50; + + return cap_value(int_,1,USHRT_MAX); +} + +static unsigned short status_calc_dex(struct block_list *bl, struct status_change *sc, int dex) +{ + if(!sc || !sc->count) + return dex; + + if(sc->data[SC_CONCENTRATE].timer!=-1 && sc->data[SC_QUAGMIRE].timer == -1) + dex += (dex-sc->data[SC_CONCENTRATE].val4)*sc->data[SC_CONCENTRATE].val2/100; + if(sc->data[SC_INCALLSTATUS].timer!=-1) + dex += sc->data[SC_INCALLSTATUS].val1; + if(sc->data[SC_INCDEX].timer!=-1) + dex += sc->data[SC_INCDEX].val1; + if(sc->data[SC_DEXFOOD].timer!=-1) + dex += sc->data[SC_DEXFOOD].val1; + if(sc->data[SC_BATTLEORDERS].timer!=-1) + dex += 5; + if(sc->data[SC_GUILDAURA].timer != -1 && (sc->data[SC_GUILDAURA].val4&0xF)) + dex += sc->data[SC_GUILDAURA].val4&0xF; + if(sc->data[SC_TRUESIGHT].timer!=-1) + dex += 5; + if(sc->data[SC_QUAGMIRE].timer!=-1) + dex -= sc->data[SC_QUAGMIRE].val2; + if(sc->data[SC_BLESSING].timer != -1){ + if (sc->data[SC_BLESSING].val2) + dex += sc->data[SC_BLESSING].val2; + else + dex >>= 1; + } + if(sc->data[SC_INCREASING].timer!=-1) + dex += 4; // added based on skill updates [Reddozen] + if(sc->data[SC_MARIONETTE].timer!=-1) + dex -= (sc->data[SC_MARIONETTE].val4>>8)&0xFF; + if(sc->data[SC_MARIONETTE2].timer!=-1) + dex += (sc->data[SC_MARIONETTE2].val4>>8)&0xFF; + if(sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_HIGH && dex < 50) + dex = 50; + + return cap_value(dex,1,USHRT_MAX); +} + +static unsigned short status_calc_luk(struct block_list *bl, struct status_change *sc, int luk) +{ + if(!sc || !sc->count) + return luk; + + if(sc->data[SC_CURSE].timer!=-1) + return 0; + if(sc->data[SC_INCALLSTATUS].timer!=-1) + luk += sc->data[SC_INCALLSTATUS].val1; + if(sc->data[SC_INCLUK].timer!=-1) + luk += sc->data[SC_INCLUK].val1; + if(sc->data[SC_LUKFOOD].timer!=-1) + luk += sc->data[SC_LUKFOOD].val1; + if(sc->data[SC_TRUESIGHT].timer!=-1) + luk += 5; + if(sc->data[SC_GLORIA].timer!=-1) + luk += 30; + if(sc->data[SC_MARIONETTE].timer!=-1) + luk -= sc->data[SC_MARIONETTE].val4&0xFF; + if(sc->data[SC_MARIONETTE2].timer!=-1) + luk += sc->data[SC_MARIONETTE2].val4&0xFF; + if(sc->data[SC_SPIRIT].timer!=-1 && sc->data[SC_SPIRIT].val2 == SL_HIGH && luk < 50) + luk = 50; + + return cap_value(luk,1,USHRT_MAX); +} + +static unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, int batk) +{ + if(!sc || !sc->count) + return batk; + + if(sc->data[SC_ATKPOTION].timer!=-1) + batk += sc->data[SC_ATKPOTION].val1; + if(sc->data[SC_BATKFOOD].timer!=-1) + batk += sc->data[SC_BATKFOOD].val1; + if(sc->data[SC_INCATKRATE].timer!=-1) + batk += batk * sc->data[SC_INCATKRATE].val1/100; + if(sc->data[SC_PROVOKE].timer!=-1) + batk += batk * (2+3*sc->data[SC_PROVOKE].val1)/100; + if(sc->data[SC_CONCENTRATION].timer!=-1) + batk += batk * sc->data[SC_CONCENTRATION].val2/100; + if(sc->data[SC_SKE].timer!=-1) + batk += batk * 3; + if(sc->data[SC_JOINTBEAT].timer!=-1 && sc->data[SC_JOINTBEAT].val2==4) + batk -= batk * 25/100; + if(sc->data[SC_CURSE].timer!=-1) + batk -= batk * 25/100; +//Curse shouldn't effect on this? <- Curse OR Bleeding?? +// if(sc->data[SC_BLEEDING].timer != -1) +// batk -= batk * 25/100; + if(sc->data[SC_MADNESSCANCEL].timer!=-1) + batk += 100; + return cap_value(batk,0,USHRT_MAX); +} + +static unsigned short status_calc_watk(struct block_list *bl, struct status_change *sc, int watk) +{ + if(!sc || !sc->count) + return watk; + + if(sc->data[SC_IMPOSITIO].timer!=-1) + watk += sc->data[SC_IMPOSITIO].val2; + if(sc->data[SC_WATKFOOD].timer!=-1) + watk += sc->data[SC_WATKFOOD].val1; + if(sc->data[SC_DRUMBATTLE].timer!=-1) + watk += sc->data[SC_DRUMBATTLE].val2; + if(sc->data[SC_VOLCANO].timer!=-1) + watk += sc->data[SC_VOLCANO].val2; + if(sc->data[SC_INCATKRATE].timer!=-1) + watk += watk * sc->data[SC_INCATKRATE].val1/100; + if(sc->data[SC_PROVOKE].timer!=-1) + watk += watk * (2+3*sc->data[SC_PROVOKE].val1)/100; + if(sc->data[SC_CONCENTRATION].timer!=-1) + watk += watk * sc->data[SC_CONCENTRATION].val2/100; + if(sc->data[SC_SKE].timer!=-1) + watk += watk * 3; + if(sc->data[SC_NIBELUNGEN].timer!=-1) { + if (bl->type != BL_PC) + watk += sc->data[SC_NIBELUNGEN].val2; + else { + TBL_PC *sd = (TBL_PC*)bl; + int index = sd->equip_index[sd->state.lr_flag?8:9]; + if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->wlv == 4) + watk += sc->data[SC_NIBELUNGEN].val2; + } + } + if(sc->data[SC_CURSE].timer!=-1) + watk -= watk * 25/100; + if(sc->data[SC_STRIPWEAPON].timer!=-1) + watk -= watk * sc->data[SC_STRIPWEAPON].val2/100; + return cap_value(watk,0,USHRT_MAX); +} + +static unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, int matk) +{ + if(!sc || !sc->count) + return matk; + + if(sc->data[SC_MATKPOTION].timer!=-1) + matk += sc->data[SC_MATKPOTION].val1; + if(sc->data[SC_MATKFOOD].timer!=-1) + matk += sc->data[SC_MATKFOOD].val1; + if(sc->data[SC_MAGICPOWER].timer!=-1) + matk += matk * 5*sc->data[SC_MAGICPOWER].val1/100; + if(sc->data[SC_MINDBREAKER].timer!=-1) + matk += matk * 20*sc->data[SC_MINDBREAKER].val1/100; + if(sc->data[SC_INCMATKRATE].timer!=-1) + matk += matk * sc->data[SC_INCMATKRATE].val1/100; + + return cap_value(matk,0,USHRT_MAX); +} + +static unsigned short status_calc_critical(struct block_list *bl, struct status_change *sc, int critical) +{ + if(!sc || !sc->count) + return critical; + + if (sc->data[SC_EXPLOSIONSPIRITS].timer!=-1) + critical += sc->data[SC_EXPLOSIONSPIRITS].val2; + if (sc->data[SC_FORTUNE].timer!=-1) + critical += sc->data[SC_FORTUNE].val2; + if (sc->data[SC_TRUESIGHT].timer!=-1) + critical += sc->data[SC_TRUESIGHT].val2; + if(sc->data[SC_CLOAKING].timer!=-1) + critical += critical; + + return cap_value(critical,0,USHRT_MAX); +} + +static unsigned short status_calc_hit(struct block_list *bl, struct status_change *sc, int hit) +{ + + if(!sc || !sc->count) + return hit; + + if(sc->data[SC_INCHIT].timer != -1) + hit += sc->data[SC_INCHIT].val1; + if(sc->data[SC_HITFOOD].timer!=-1) + hit += sc->data[SC_HITFOOD].val1; + if(sc->data[SC_TRUESIGHT].timer != -1) + hit += sc->data[SC_TRUESIGHT].val3; + if(sc->data[SC_HUMMING].timer!=-1) + hit += sc->data[SC_HUMMING].val2; + if(sc->data[SC_CONCENTRATION].timer != -1) + hit += sc->data[SC_CONCENTRATION].val3; + if(sc->data[SC_INCHITRATE].timer != -1) + hit += hit * sc->data[SC_INCHITRATE].val1/100; + if(sc->data[SC_BLIND].timer != -1) + hit -= hit * 25/100; + if(sc->data[SC_ADJUSTMENT].timer!=-1) + hit += 30; + if(sc->data[SC_INCREASING].timer!=-1) + hit += 20; // RockmanEXE; changed based on updated [Reddozen] + + return cap_value(hit,0,USHRT_MAX); +} + +static unsigned short status_calc_flee(struct block_list *bl, struct status_change *sc, int flee) +{ + if (bl->type == BL_PC && map_flag_gvg(bl->m)) //GVG grounds flee penalty, placed here because it's "like" a status change. [Skotlex] + flee -= flee * battle_config.gvg_flee_penalty/100; + + if(!sc || !sc->count) + return flee; + if(sc->data[SC_INCFLEE].timer!=-1) + flee += sc->data[SC_INCFLEE].val1; + if(sc->data[SC_FLEEFOOD].timer!=-1) + flee += sc->data[SC_FLEEFOOD].val1; + if(sc->data[SC_WHISTLE].timer!=-1) + flee += sc->data[SC_WHISTLE].val2; + if(sc->data[SC_WINDWALK].timer!=-1) + flee += sc->data[SC_WINDWALK].val2; + if(sc->data[SC_INCFLEERATE].timer!=-1) + flee += flee * sc->data[SC_INCFLEERATE].val1/100; + if(sc->data[SC_VIOLENTGALE].timer!=-1) + flee += flee * sc->data[SC_VIOLENTGALE].val2/100; + if(sc->data[SC_MOON_COMFORT].timer!=-1) //SG skill [Komurka] + flee += sc->data[SC_MOON_COMFORT].val2; + if(sc->data[SC_CLOSECONFINE].timer!=-1) + flee += 10; + if(sc->data[SC_SPIDERWEB].timer!=-1) + flee -= flee * 50/100; + if(sc->data[SC_BERSERK].timer!=-1) + flee -= flee * 50/100; + if(sc->data[SC_BLIND].timer!=-1) + flee -= flee * 25/100; + if(sc->data[SC_ADJUSTMENT].timer!=-1) + flee += 30; + if(sc->data[SC_GATLINGFEVER].timer!=-1) + flee -= sc->data[SC_GATLINGFEVER].val1*5; + + return cap_value(flee,0,USHRT_MAX); +} + +static unsigned short status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2) +{ + if(!sc || !sc->count) + return flee2; + if(sc->data[SC_WHISTLE].timer!=-1) + flee2 += sc->data[SC_WHISTLE].val3*10; + return cap_value(flee2,0,USHRT_MAX); +} + +static unsigned char status_calc_def(struct block_list *bl, struct status_change *sc, int def) +{ + if(!sc || !sc->count) + return def; + if(sc->data[SC_BERSERK].timer!=-1) + return 0; + if(sc->data[SC_KEEPING].timer!=-1) + return 100; + if(sc->data[SC_SKA].timer != -1) + return rand()%100; //Reports indicate SKA actually randomizes defense. + if(sc->data[SC_STEELBODY].timer!=-1) + return 90; + if(sc->data[SC_DRUMBATTLE].timer!=-1) + def += sc->data[SC_DRUMBATTLE].val3; + if(sc->data[SC_INCDEFRATE].timer!=-1) + def += def * sc->data[SC_INCDEFRATE].val1/100; + if(sc->data[SC_SIGNUMCRUCIS].timer!=-1) + def -= def * sc->data[SC_SIGNUMCRUCIS].val2/100; + if(sc->data[SC_CONCENTRATION].timer!=-1) + def -= def * sc->data[SC_CONCENTRATION].val4/100; + if(sc->data[SC_SKE].timer!=-1) + def -= def * 50/100; + if(sc->data[SC_PROVOKE].timer!=-1 && bl->type != BL_PC) // Provoke doesn't alter player defense. + def -= def * (5+5*sc->data[SC_PROVOKE].val1)/100; + if(sc->data[SC_STRIPSHIELD].timer!=-1) + def -= def * sc->data[SC_STRIPSHIELD].val2/100; + //if (sd->data[SC_FLING].timer!=-1 && bl->type != BL_PC) + // def -= (def * sd->data[SC_FLING].val1) / 100; + return cap_value(def,0,UCHAR_MAX); +} + +static unsigned short status_calc_def2(struct block_list *bl, struct status_change *sc, int def2) +{ + if(!sc || !sc->count) + return def2; + + if(sc->data[SC_BERSERK].timer!=-1) + return 0; + if(sc->data[SC_ETERNALCHAOS].timer!=-1) + return 0; + if(sc->data[SC_SUN_COMFORT].timer!=-1) + def2 += sc->data[SC_SUN_COMFORT].val2; + if(sc->data[SC_ANGELUS].timer!=-1) + def2 += def2 * sc->data[SC_ANGELUS].val2/100; + if(sc->data[SC_CONCENTRATION].timer!=-1) + def2 -= def2 * sc->data[SC_CONCENTRATION].val4/100; + if(sc->data[SC_POISON].timer!=-1) + def2 -= def2 * 25/100; + if(sc->data[SC_SKE].timer!=-1) + def2 -= def2 * 50/100; + if(sc->data[SC_PROVOKE].timer!=-1) + def2 -= def2 * (5+5*sc->data[SC_PROVOKE].val1)/100; + if(sc->data[SC_JOINTBEAT].timer!=-1){ + if(sc->data[SC_JOINTBEAT].val2==3) + def2 -= def2 * 50/100; + else if(sc->data[SC_JOINTBEAT].val2==4) + def2 -= def2 * 25/100; + } + //if (sd->data[SC_FLING].timer!=-1) + // def2 -= (def2 * sd->data[SC_FLING].val1) / 100; + + return cap_value(def2,0,USHRT_MAX); +} + +static unsigned char status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef) +{ + if(!sc || !sc->count) + return mdef; + + if(sc->data[SC_BERSERK].timer!=-1) + return 0; + if(sc->data[SC_BARRIER].timer!=-1) + return 100; + if(sc->data[SC_STEELBODY].timer!=-1) + return 90; + if(sc->data[SC_SKA].timer != -1) // [marquis007] + return 90; + if(sc->data[SC_ENDURE].timer!=-1 && sc->data[SC_ENDURE].val4 == 0) + mdef += sc->data[SC_ENDURE].val1; + + return cap_value(mdef,0,UCHAR_MAX); +} + +static unsigned short status_calc_mdef2(struct block_list *bl, struct status_change *sc, int mdef2) +{ + if(!sc || !sc->count) + return mdef2; + if(sc->data[SC_BERSERK].timer!=-1) + return 0; + if(sc->data[SC_MINDBREAKER].timer!=-1) + mdef2 -= mdef2 * 12*sc->data[SC_MINDBREAKER].val1/100; + + return cap_value(mdef2,0,USHRT_MAX); +} + +static unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc, int speed) +{ + if(!sc || !sc->count) + return speed; + if(sc->data[SC_CURSE].timer!=-1) + speed += 450; + if(sc->data[SC_SWOO].timer != -1) // [marquis007] + speed += 450; //Let's use Curse's slow down momentarily (exact value unknown) + if(sc->data[SC_WEDDING].timer!=-1) + speed += 300; + if(sc->data[SC_SPEEDUP1].timer!=-1) + speed -= speed*50/100; + else if(sc->data[SC_SPEEDUP0].timer!=-1) + speed -= speed*25/100; + else if(sc->data[SC_INCREASEAGI].timer!=-1) + speed -= speed * 25/100; + else if(sc->data[SC_CARTBOOST].timer!=-1) + speed -= speed * 20/100; + else if(sc->data[SC_BERSERK].timer!=-1) + speed -= speed * 20/100; + else if(sc->data[SC_WINDWALK].timer!=-1) + speed -= speed * sc->data[SC_WINDWALK].val3/100; + if(sc->data[SC_SLOWDOWN].timer!=-1) + speed += speed * 50/100; + if(sc->data[SC_DECREASEAGI].timer!=-1) + speed += speed * 25/100; + if(sc->data[SC_STEELBODY].timer!=-1) + speed += speed * 25/100; + if(sc->data[SC_SKA].timer!=-1) + speed += speed * 25/100; + if(sc->data[SC_QUAGMIRE].timer!=-1) + speed += speed * 50/100; + if(sc->data[SC_DONTFORGETME].timer!=-1) + speed += speed * sc->data[SC_DONTFORGETME].val3/100; + if(sc->data[SC_DEFENDER].timer!=-1) + speed += speed * (35-5*sc->data[SC_DEFENDER].val1)/100; + if(sc->data[SC_GOSPEL].timer!=-1 && sc->data[SC_GOSPEL].val4 == BCT_ENEMY) + speed += speed * 25/100; + if(sc->data[SC_JOINTBEAT].timer!=-1) { + if (sc->data[SC_JOINTBEAT].val2 == 0) + speed += speed * 50/100; + else if (sc->data[SC_JOINTBEAT].val2 == 2) + speed += speed * 30/100; + } + if(sc->data[SC_CLOAKING].timer!=-1) + speed = speed*( + (sc->data[SC_CLOAKING].val4&2?25:0) //Wall speed bonus + +sc->data[SC_CLOAKING].val3) /100; //Normal adjustment bonus. + + if(sc->data[SC_DANCING].timer!=-1 && sc->data[SC_DANCING].val3&0xFFFF) + speed += speed*(sc->data[SC_DANCING].val3&0xFFFF)/100; + if(sc->data[SC_LONGING].timer!=-1) + speed += speed*sc->data[SC_LONGING].val2/100; + if(sc->data[SC_HIDING].timer!=-1 && sc->data[SC_HIDING].val3) + speed += speed*sc->data[SC_HIDING].val3/100; + if(sc->data[SC_CHASEWALK].timer!=-1) + speed = speed * sc->data[SC_CHASEWALK].val3/100; + if(sc->data[SC_RUN].timer!=-1) + speed -= speed * 25/100; + if(sc->data[SC_FUSION].timer != -1) + speed -= speed * 25/100; + if(sc->data[SC_GATLINGFEVER].timer!=-1) + speed += speed * 25/100; + + return cap_value(speed,0,USHRT_MAX); +} + +static short status_calc_aspd_rate(struct block_list *bl, struct status_change *sc, int aspd_rate) +{ + int i; + if(!sc || !sc->count) + return aspd_rate; + + if(sc->data[SC_QUAGMIRE].timer==-1 && sc->data[SC_DONTFORGETME].timer==-1) + { + int max = 0; + if(sc->data[SC_STAR_COMFORT].timer!=-1) + max = sc->data[SC_STAR_COMFORT].val2; + if((sc->data[SC_TWOHANDQUICKEN].timer!=-1 || + sc->data[SC_ONEHAND].timer!=-1 || + sc->data[SC_BERSERK].timer!=-1 + ) && max < 30) + max = 30; + + if(sc->data[SC_MADNESSCANCEL].timer!=-1 && max < 20) + max = 20; + + if(sc->data[SC_ADRENALINE2].timer!=-1 && + max < sc->data[SC_ADRENALINE2].val2) + max = sc->data[SC_ADRENALINE2].val2; + + if(sc->data[SC_ADRENALINE].timer!=-1 && + max < sc->data[SC_ADRENALINE].val2) + max = sc->data[SC_ADRENALINE].val2; + + if(sc->data[SC_SPEARQUICKEN].timer!=-1 && + max < sc->data[SC_SPEARQUICKEN].val2) + max = sc->data[SC_SPEARQUICKEN].val2; + + if(sc->data[SC_GATLINGFEVER].timer!=-1 && + max < sc->data[SC_GATLINGFEVER].val2) + max = sc->data[SC_GATLINGFEVER].val2; + + if(sc->data[SC_ASSNCROS].timer!=-1 && + max < sc->data[SC_ASSNCROS].val2) + { + if (bl->type!=BL_PC) + max = sc->data[SC_ASSNCROS].val2; + else + switch(((TBL_PC*)bl)->status.weapon) + { + case W_BOW: + case W_REVOLVER: + case W_RIFLE: + case W_SHOTGUN: + case W_GATLING: + case W_GRENADE: + break; + default: + max = sc->data[SC_ASSNCROS].val2; + } + } + aspd_rate -= max; + } + if(sc->data[i=SC_ASPDPOTION3].timer!=-1 || + sc->data[i=SC_ASPDPOTION2].timer!=-1 || + sc->data[i=SC_ASPDPOTION1].timer!=-1 || + sc->data[i=SC_ASPDPOTION0].timer!=-1) + aspd_rate -= sc->data[i].val2; + if(sc->data[SC_DONTFORGETME].timer!=-1) + aspd_rate += sc->data[SC_DONTFORGETME].val2; + if(sc->data[SC_LONGING].timer!=-1) + aspd_rate += sc->data[SC_LONGING].val2; + if(sc->data[SC_STEELBODY].timer!=-1) + aspd_rate += 25; + if(sc->data[SC_SKA].timer!=-1) + aspd_rate += 25; + if(sc->data[SC_DEFENDER].timer != -1) + aspd_rate += 25 -sc->data[SC_DEFENDER].val1*5; + if(sc->data[SC_GOSPEL].timer!=-1 && sc->data[SC_GOSPEL].val4 == BCT_ENEMY) + aspd_rate += 25; + if(sc->data[SC_GRAVITATION].timer!=-1) + aspd_rate += sc->data[SC_GRAVITATION].val2; +//Curse shouldn't effect on this? +// if(sc->data[SC_BLEEDING].timer != -1) +// aspd_rate += 25; + if(sc->data[SC_JOINTBEAT].timer!=-1) { + if (sc->data[SC_JOINTBEAT].val2 == 1) + aspd_rate += 25; + else if (sc->data[SC_JOINTBEAT].val2 == 2) + aspd_rate += 10; + } + + return cap_value(aspd_rate,0,SHRT_MAX); +} + +static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion) +{ + if(!sc || !sc->count || map_flag_gvg(bl->m)) + return dmotion; + + if (sc->data[SC_ENDURE].timer!=-1 || + sc->data[SC_CONCENTRATION].timer!=-1) + return 0; + + return cap_value(dmotion,0,USHRT_MAX); +} + +static unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc, unsigned int maxhp) +{ + if(!sc || !sc->count) + return maxhp; + + if(sc->data[SC_INCMHPRATE].timer!=-1) + maxhp += maxhp * sc->data[SC_INCMHPRATE].val1/100; + if(sc->data[SC_APPLEIDUN].timer!=-1) + maxhp += maxhp * sc->data[SC_APPLEIDUN].val2/100; + if(sc->data[SC_DELUGE].timer!=-1) + maxhp += maxhp * sc->data[SC_DELUGE].val2/100; + if(sc->data[SC_BERSERK].timer!=-1) + maxhp += maxhp * 2; + + return cap_value(maxhp,1,UINT_MAX); +} + +static unsigned int status_calc_maxsp(struct block_list *bl, struct status_change *sc, unsigned int maxsp) +{ + if(!sc || !sc->count) + return maxsp; + if(sc->data[SC_INCMSPRATE].timer!=-1) + maxsp += maxsp * sc->data[SC_INCMSPRATE].val1/100; + if(sc->data[SC_SERVICE4U].timer!=-1) + maxsp += maxsp * sc->data[SC_SERVICE4U].val2/100; + + return cap_value(maxsp,1,UINT_MAX); +} + +static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element) +{ + if(!sc || !sc->count) + return element; + if( sc->data[SC_FREEZE].timer!=-1 ) + return ELE_WATER; + if( sc->data[SC_STONE].timer!=-1 && sc->opt1 == OPT1_STONE) + return ELE_EARTH; + if( sc->data[SC_BENEDICTIO].timer!=-1 ) + return ELE_HOLY; + if( sc->data[SC_ELEMENTALCHANGE].timer!=-1) + return sc->data[SC_ELEMENTALCHANGE].val3; + return cap_value(element,0,UCHAR_MAX); +} + +static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv) +{ + if(!sc || !sc->count) + return lv; + if(sc->data[SC_ELEMENTALCHANGE].timer!=-1) + return sc->data[SC_ELEMENTALCHANGE].val4; + return cap_value(lv,0,UCHAR_MAX); +} + + +unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element) +{ + if(!sc || !sc->count) + return element; + if( sc->data[SC_WATERWEAPON].timer!=-1) + return ELE_WATER; + if( sc->data[SC_EARTHWEAPON].timer!=-1) + return ELE_EARTH; + if( sc->data[SC_FIREWEAPON].timer!=-1) + return ELE_FIRE; + if( sc->data[SC_WINDWEAPON].timer!=-1) + return ELE_WIND; + if( sc->data[SC_ENCPOISON].timer!=-1) + return ELE_POISON; + if( sc->data[SC_ASPERSIO].timer!=-1) + return ELE_HOLY; + if( sc->data[SC_SHADOWWEAPON].timer!=-1) + return ELE_DARK; + if( sc->data[SC_GHOSTWEAPON].timer!=-1) + return ELE_GHOST; + return cap_value(element,0,UCHAR_MAX); +} + +static unsigned short status_calc_mode(struct block_list *bl, struct status_change *sc, int mode) +{ + if(!sc || !sc->count) + return mode; + if(sc->data[SC_MODECHANGE].timer!=-1) { + if (sc->data[SC_MODECHANGE].val2) + mode = sc->data[SC_MODECHANGE].val2; //Set mode + if (sc->data[SC_MODECHANGE].val3) + mode|= sc->data[SC_MODECHANGE].val3; //Add mode + if (sc->data[SC_MODECHANGE].val4) + mode&=~sc->data[SC_MODECHANGE].val4; //Del mode + } + return cap_value(mode,0,USHRT_MAX); +} + +/*========================================== + * Quick swap of adelay/speed when starting ending SA_FREECAST + *------------------------------------------ + */ +void status_freecast_switch(struct map_session_data *sd) +{ + struct status_data *status; + unsigned short b_speed,tmp; + + status = &sd->battle_status; + + b_speed = status->speed; + + tmp = status->speed; + status->speed = sd->prev_speed; + sd->prev_speed = tmp; + + tmp = status->adelay; + status->adelay = sd->prev_adelay; + sd->prev_adelay = tmp; + + if(b_speed != status->speed) + clif_updatestatus(sd,SP_SPEED); +} + +/*========================================== + * 対象のClassを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int status_get_class(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB) //Class used on all code should be the view class of the mob. + return ((struct mob_data *)bl)->vd->class_; + if(bl->type==BL_PC) + return ((struct map_session_data *)bl)->status.class_; + if(bl->type==BL_PET) + return ((struct pet_data *)bl)->class_; + return 0; +} +/*========================================== + * 対象のレベルを返す(汎用) + * 戻りは整数で0以上 + *------------------------------------------ + */ +int status_get_lv(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB) + return ((struct mob_data *)bl)->level; + if(bl->type==BL_PC) + return ((struct map_session_data *)bl)->status.base_level; + if(bl->type==BL_PET) + return ((struct pet_data *)bl)->msd->pet.level; + return 0; +} + +struct status_data *status_get_status_data(struct block_list *bl) +{ + nullpo_retr(NULL, bl); + + switch (bl->type) { + case BL_PC: + return &((TBL_PC*)bl)->battle_status; + case BL_MOB: + return &((TBL_MOB*)bl)->status; + case BL_PET: + return &((TBL_PET*)bl)->status; + default: + return &dummy_status; + } +} + +struct status_data *status_get_base_status(struct block_list *bl) +{ + nullpo_retr(NULL, bl); + switch (bl->type) { + case BL_PC: + return &((TBL_PC*)bl)->base_status; + case BL_MOB: + return ((TBL_MOB*)bl)->base_status? + ((TBL_MOB*)bl)->base_status: + &((TBL_MOB*)bl)->db->status; + case BL_PET: + return &((TBL_PET*)bl)->db->status; + default: + return NULL; + } +} + +int status_get_lwatk(struct block_list *bl) +{ + struct status_data *status = status_get_status_data(bl); + return status->lhw?status->lhw->atk:0; +} + +int status_get_lwatk2(struct block_list *bl) +{ + struct status_data *status = status_get_status_data(bl); + return status->lhw?status->lhw->atk2:0; +} + +int status_get_def(struct block_list *bl) +{ + struct unit_data *ud; + struct status_data *status = status_get_status_data(bl); + int def = status?status->def:0; + ud = unit_bl2ud(bl); + if (ud && ud->skilltimer != -1) + def -= def * skill_get_castdef(ud->skillid)/100; + if(def < 0) def = 0; + return def; +} + +int status_get_speed(struct block_list *bl) +{ + struct status_data *status; + + if(bl->type==BL_NPC)//Only BL with speed data but no status_data [Skotlex] + return ((struct npc_data *)bl)->speed; + + status = status_get_status_data(bl); + return status?status->speed:2000; +} + +int status_get_attack_lelement(struct block_list *bl) +{ + struct status_data *status = status_get_status_data(bl); + return status->lhw?status->lhw->ele:0; +} + +int status_get_party_id(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC) + return ((struct map_session_data *)bl)->status.party_id; + if(bl->type==BL_PET) + return ((struct pet_data *)bl)->msd->status.party_id; + if(bl->type==BL_MOB){ + struct mob_data *md=(struct mob_data *)bl; + if( md->master_id>0 ) + { + struct map_session_data *msd; + if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL) + return msd->status.party_id; + return -md->master_id; + } + return 0; //No party. + } + if(bl->type==BL_SKILL) + return ((struct skill_unit *)bl)->group->party_id; + return 0; +} + +int status_get_guild_id(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_PC) + return ((struct map_session_data *)bl)->status.guild_id; + if(bl->type==BL_PET) + return ((struct pet_data *)bl)->msd->status.guild_id; + if(bl->type==BL_MOB) + { + struct map_session_data *msd; + struct mob_data *md = (struct mob_data *)bl; + if (md->guardian_data) //Guardian's guild [Skotlex] + return md->guardian_data->guild_id; + if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL) + return msd->status.guild_id; //Alchemist's mobs [Skotlex] + return 0; //No guild. + } + if (bl->type == BL_NPC && bl->subtype == SCRIPT) + return ((TBL_NPC*)bl)->u.scr.guild_id; + if(bl->type==BL_SKILL) + return ((struct skill_unit *)bl)->group->guild_id; + return 0; +} + +int status_get_mexp(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type==BL_MOB) + return ((struct mob_data *)bl)->db->mexp; + if(bl->type==BL_PET) + return ((struct pet_data *)bl)->db->mexp; + return 0; +} +int status_get_race2(struct block_list *bl) +{ + nullpo_retr(0, bl); + if(bl->type == BL_MOB) + return ((struct mob_data *)bl)->db->race2; + if(bl->type==BL_PET) + return ((struct pet_data *)bl)->db->race2; + return 0; +} +int status_isdead(struct block_list *bl) +{ + nullpo_retr(0, bl); + return status_get_status_data(bl)->hp == 0; +} + +int status_isimmune(struct block_list *bl) +{ + struct status_change *sc =status_get_sc(bl); + if (bl->type == BL_PC && + ((struct map_session_data *)bl)->special_state.no_magic_damage) + return 1; + if (sc && sc->count && sc->data[SC_HERMODE].timer != -1) + return 1; + return 0; +} + +struct view_data *status_get_viewdata(struct block_list *bl) +{ + nullpo_retr(NULL, bl); + switch (bl->type) + { + case BL_PC: + return &((TBL_PC*)bl)->vd; + case BL_MOB: + return ((TBL_MOB*)bl)->vd; + case BL_PET: + return &((TBL_PET*)bl)->vd; + case BL_NPC: + return ((TBL_NPC*)bl)->vd; + case BL_HOMUNCULUS: //[blackhole89] + return ((struct homun_data*)bl)->vd; + } + return NULL; +} + +void status_set_viewdata(struct block_list *bl, int class_) +{ + struct view_data* vd; + nullpo_retv(bl); + if (mobdb_checkid(class_) || mob_is_clone(class_)) + vd = mob_get_viewdata(class_); + else if (npcdb_checkid(class_) || (bl->type == BL_NPC && class_ == WARP_CLASS)) + vd = npc_get_viewdata(class_); + else + vd = NULL; + + switch (bl->type) { + case BL_PC: + { + TBL_PC* sd = (TBL_PC*)bl; + if (pcdb_checkid(class_)) { + if (sd->sc.option&OPTION_RIDING) + switch (class_) + { //Adapt class to a Mounted one. + case JOB_KNIGHT: + class_ = JOB_KNIGHT2; + break; + case JOB_CRUSADER: + class_ = JOB_CRUSADER2; + break; + case JOB_LORD_KNIGHT: + class_ = JOB_LORD_KNIGHT2; + break; + case JOB_PALADIN: + class_ = JOB_PALADIN2; + break; + case JOB_BABY_KNIGHT: + class_ = JOB_BABY_KNIGHT2; + break; + case JOB_BABY_CRUSADER: + class_ = JOB_BABY_CRUSADER2; + break; + } + if (class_ == JOB_WEDDING) + sd->sc.option|=OPTION_WEDDING; + else if (sd->sc.option&OPTION_WEDDING) + sd->sc.option&=~OPTION_WEDDING; //If not going to display it, then remove the option. + sd->vd.class_ = class_; + clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield); + sd->vd.head_top = sd->status.head_top; + sd->vd.head_mid = sd->status.head_mid; + sd->vd.head_bottom = sd->status.head_bottom; + sd->vd.hair_style = sd->status.hair; + sd->vd.hair_color = sd->status.hair_color; + sd->vd.cloth_color = sd->status.clothes_color; + sd->vd.sex = sd->status.sex; + } else if (vd) + memcpy(&sd->vd, vd, sizeof(struct view_data)); + else if (battle_config.error_log) + ShowError("status_set_viewdata (PC): No view data for class %d\n", class_); + } + break; + case BL_MOB: + { + TBL_MOB* md = (TBL_MOB*)bl; + if (vd) + md->vd = vd; + else if (battle_config.error_log) + ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_); + } + break; + case BL_PET: + { + TBL_PET* pd = (TBL_PET*)bl; + if (vd) { + memcpy(&pd->vd, vd, sizeof(struct view_data)); + if (!pcdb_checkid(vd->class_)) { + pd->vd.hair_style = battle_config.pet_hair_style; + if(pd->equip) { + pd->vd.head_bottom = itemdb_viewid(pd->equip); + if (!pd->vd.head_bottom) + pd->vd.head_bottom = pd->equip; + } + } + } else if (battle_config.error_log) + ShowError("status_set_viewdata (PET): No view data for class %d\n", class_); + } + break; + case BL_NPC: + { + TBL_NPC* nd = (TBL_NPC*)bl; + if (vd) + nd->vd = vd; + else if (battle_config.error_log) + ShowError("status_set_viewdata (NPC): No view data for class %d\n", class_); + } + break; + case BL_HOMUNCULUS: //[blackhole89] + { + struct homun_data *hd = (struct homun_data*)bl; + if (vd) + hd->vd = vd; + else if (battle_config.error_log) + ShowError("status_set_viewdata (HOMUNCULUS): No view data for class %d\n", class_); + } + break; + } + vd = status_get_viewdata(bl); + if (vd && vd->cloth_color && ( + (vd->class_==JOB_WEDDING && !battle_config.wedding_ignorepalette) + || (vd->class_==JOB_XMAS && !battle_config.xmas_ignorepalette) + )) + vd->cloth_color = 0; +} + +struct status_change *status_get_sc(struct block_list *bl) +{ + nullpo_retr(NULL, bl); + switch (bl->type) { + case BL_MOB: + return &((TBL_MOB*)bl)->sc; + case BL_PC: + return &((TBL_PC*)bl)->sc; + case BL_NPC: + return &((TBL_NPC*)bl)->sc; + case BL_HOMUNCULUS: //[blackhole89] + return &((struct homun_data*)bl)->sc; + } + return NULL; +} + +void status_change_init(struct block_list *bl) +{ + struct status_change *sc = status_get_sc(bl); + int i; + nullpo_retv(sc); + memset(sc, 0, sizeof (struct status_change)); + for (i=0; i< SC_MAX; i++) + sc->data[i].timer = -1; +} + +//Returns defense against the specified status change. +//Return range is 0 (no resist) to 10000 (inmunity) +int status_get_sc_def(struct block_list *bl, int type) +{ + int sc_def; + struct status_change* sc; + nullpo_retr(0, bl); + + //Status that are blocked by Golden Thief Bug card or Wand of Hermod + if (status_isimmune(bl)) + switch (type) + { + case SC_DECREASEAGI: + case SC_SILENCE: + case SC_COMA: + case SC_INCREASEAGI: + case SC_BLESSING: + case SC_SLOWPOISON: + case SC_IMPOSITIO: + case SC_AETERNA: + case SC_SUFFRAGIUM: + case SC_BENEDICTIO: + case SC_PROVIDENCE: + case SC_KYRIE: + case SC_ASSUMPTIO: + case SC_ANGELUS: + case SC_MAGNIFICAT: + case SC_GLORIA: + case SC_WINDWALK: + case SC_MAGICROD: + case SC_HALLUCINATION: + case SC_STONE: + case SC_QUAGMIRE: + return 10000; + } + + switch (type) + { + //Note that stats that are *100/3 were simplified to *33 + case SC_STONE: + case SC_FREEZE: + case SC_DECREASEAGI: + case SC_COMA: + sc_def = 300 +100*status_get_mdef(bl) +33*status_get_luk(bl); + break; + case SC_SLEEP: + case SC_CONFUSION: + sc_def = 300 +100*status_get_int(bl) +33*status_get_luk(bl); + break; +// Removed since it collides with normal sc. +// case SP_DEF1: // def +// sc_def = 300 +100*status_get_def(bl) +33*status_get_luk(bl); +// break; + case SC_STUN: + case SC_POISON: + case SC_DPOISON: + case SC_SILENCE: + case SC_BLEEDING: + case SC_STOP: + sc_def = 300 +100*status_get_vit(bl) +33*status_get_luk(bl); + break; + case SC_BLIND: + sc_def = 300 +100*status_get_int(bl) +33*status_get_vit(bl); + break; + case SC_CURSE: + sc_def = 300 +100*status_get_luk(bl) +33*status_get_vit(bl); + break; + default: + return 0; //Effect that cannot be reduced? Likely a buff. + } + + if (bl->type == BL_PC) { + if (battle_config.pc_sc_def_rate != 100) + sc_def = sc_def*battle_config.pc_sc_def_rate/100; + } else + if (battle_config.mob_sc_def_rate != 100) + sc_def = sc_def*battle_config.mob_sc_def_rate/100; + + sc = status_get_sc(bl); + if (sc && sc->count) + { + if (sc->data[SC_SCRESIST].timer != -1) + sc_def += 100*sc->data[SC_SCRESIST].val1; //Status resist + else if (sc->data[SC_SIEGFRIED].timer != -1) + sc_def += 100*sc->data[SC_SIEGFRIED].val3; //Status resistance. + } + + if(bl->type == BL_PC) { + if (sc_def > battle_config.pc_max_sc_def) + sc_def = battle_config.pc_max_sc_def; + } else if (sc_def > battle_config.mob_max_sc_def) + sc_def = battle_config.mob_max_sc_def; + + return sc_def; +} + +//Reduces tick delay based on type and character defenses. +int status_get_sc_tick(struct block_list *bl, int type, int tick) +{ + struct map_session_data *sd; + int rate=0, min=0; + //If rate is positive, it is a % reduction (10000 -> 100%) + //if it is negative, it is an absolute reduction in ms. + sd = bl->type == BL_PC?(struct map_session_data *)bl:NULL; + switch (type) { + case SC_DECREASEAGI: /* 速度減少 */ + if (sd) // Celest + tick>>=1; + break; + case SC_ADRENALINE: /* アドレナリンラッシュ */ + case SC_ADRENALINE2: + case SC_WEAPONPERFECTION: /* ウェポンパ?フェクション */ + case SC_OVERTHRUST: /* オ?バ?スラスト */ + if(sd && pc_checkskill(sd,BS_HILTBINDING)>0) + tick += tick / 10; + break; + case SC_STONE: /* 石化 */ + rate = -200*status_get_mdef(bl); + break; + case SC_FREEZE: /* 凍結 */ + rate = 100*status_get_mdef(bl); + break; + case SC_STUN: //Reduction in duration is the same as reduction in rate. + rate = 300 +100*status_get_vit(bl) +33*status_get_luk(bl); + break; + case SC_DPOISON: /* 猛毒 */ + case SC_POISON: /* 毒 */ + rate = 100*status_get_vit(bl) + 20*status_get_luk(bl); + break; + case SC_SILENCE: /* 沈?(レックスデビ?ナ) */ + case SC_CONFUSION: + case SC_CURSE: + rate = 100*status_get_vit(bl); + break; + case SC_BLIND: /* 暗? */ + rate = 10*status_get_lv(bl) + 7*status_get_int(bl); + min = 5000; //Minimum 5 secs? + break; + case SC_BLEEDING: + rate = 20*status_get_lv(bl) +100*status_get_vit(bl); + min = 10000; //Need a min of 10 secs for it to hurt at least once. + break; + case SC_SWOO: + if (status_get_mode(bl)&MD_BOSS) + tick /= 5; //TODO: Reduce skill's duration. But for how long? + break; + case SC_ANKLE: + if(status_get_mode(bl)&MD_BOSS) // Lasts 5 times less on bosses + tick /= 5; + rate = -100*status_get_agi(bl); + // Minimum trap time of 3+0.03*skilllv seconds [celest] + // Changed to 3 secs and moved from skill.c [Skotlex] + min = 3000; + break; + case SC_SPIDERWEB: + if (map[bl->m].flag.pvp) + tick /=2; + break; + case SC_STOP: + // Unsure of this... but I get a feeling that agi reduces this + // (it was on Tiger Fist Code, but at -1 ms per 10 agi.... + rate = -100*status_get_agi(bl); + break; + } + if (rate) { + if (bl->type == BL_PC) { + if (battle_config.pc_sc_def_rate != 100) + rate = rate*battle_config.pc_sc_def_rate/100; + if (battle_config.pc_max_sc_def != 10000) + min = tick*(10000-battle_config.pc_max_sc_def)/10000; + } else { + if (battle_config.mob_sc_def_rate != 100) + rate = rate*battle_config.mob_sc_def_rate/100; + if (battle_config.mob_max_sc_def != 10000) + min = tick*(10000-battle_config.mob_max_sc_def)/10000; + } + + if (rate >0) + tick -= tick*rate/10000; + else + tick += rate; + } + return ticktype) + { + case BL_PC: + sd=(struct map_session_data *)bl; + break; + case BL_MOB: + if (((struct mob_data*)bl)->class_ == MOBID_EMPERIUM && type != SC_SAFETYWALL) + return 0; //Emperium can't be afflicted by status changes. + break; + } + + if(type < 0 || type >= SC_MAX) { + if(battle_config.error_log) + ShowError("status_change_start: invalid status change (%d)!\n", type); + return 0; + } + + //Check rate + if (!(flag&(4|1))) { + int def; + def = flag&8?0:status_get_sc_def(bl, type); //recycling race to store the sc_def value. + //sd resistance applies even if the flag is &8 + if(sd && SC_COMMON_MIN<=type && type<=SC_COMMON_MAX && sd->reseff[type-SC_COMMON_MIN] > 0) + def+= sd->reseff[type-SC_COMMON_MIN]; + + if (def) + rate -= rate*def/10000; + + if (!(rand()%10000 < rate)) + return 0; + } + + //SC duration reduction. + if(!(flag&(2|4)) && tick) { + tick = status_get_sc_tick(bl, type, tick); + if (tick <= 0) + return 0; + } + + undead_flag=battle_check_undead(status->race,status->def_ele); + + //Check for inmunities / sc fails + switch (type) { + case SC_FREEZE: + case SC_STONE: + //Undead are inmune to Freeze/Stone + if (undead_flag && !(flag&1)) + return 0; + case SC_SLEEP: + case SC_STUN: + if (sc->opt1) + return 0; //Cannot override other opt1 status changes. [Skotlex] + break; + case SC_CURSE: + //Dark Elementals are inmune to curse. + if (status->def_ele == ELE_DARK && !(flag&1)) + return 0; + break; + case SC_COMA: + //Dark elementals and Demons are inmune to coma. + if((status->def_ele == ELE_DARK || status->race == RC_DEMON) && !(flag&1)) + return 0; + break; + case SC_SIGNUMCRUCIS: + //Only affects demons and undead. + if(status->race != RC_DEMON && !undead_flag) + return 0; + break; + case SC_AETERNA: + if (sc->data[SC_STONE].timer != -1 || sc->data[SC_FREEZE].timer != -1) + return 0; + break; + case SC_OVERTHRUST: + if (sc->data[SC_MAXOVERTHRUST].timer != -1) + return 0; //Overthrust can't take effect if under Max Overthrust. [Skotlex] + break; + case SC_ADRENALINE: + if (sd && !(skill_get_weapontype(BS_ADRENALINE)&(1<status.weapon))) + return 0; + if (sc->data[SC_QUAGMIRE].timer!=-1 || + sc->data[SC_DONTFORGETME].timer!=-1 || + sc->data[SC_DECREASEAGI].timer!=-1 + ) + return 0; + break; + case SC_ADRENALINE2: + if (sd && !(skill_get_weapontype(BS_ADRENALINE2)&(1<status.weapon))) + return 0; + if (sc->data[SC_QUAGMIRE].timer!=-1 || + sc->data[SC_DONTFORGETME].timer!=-1 || + sc->data[SC_DECREASEAGI].timer!=-1 + ) + return 0; + break; + case SC_ONEHAND: + case SC_TWOHANDQUICKEN: + if(sc->data[SC_DECREASEAGI].timer!=-1) + return 0; + case SC_CONCENTRATE: + case SC_INCREASEAGI: + case SC_SPEARQUICKEN: + case SC_TRUESIGHT: + case SC_WINDWALK: + case SC_CARTBOOST: + case SC_ASSNCROS: + if (sc->data[SC_QUAGMIRE].timer!=-1 || sc->data[SC_DONTFORGETME].timer!=-1) + return 0; + break; + case SC_CLOAKING: + //Avoid cloaking with no wall and low skill level. [Skotlex] + //Due to the cloaking card, we have to check the wall versus to known skill level rather than the used one. [Skotlex] +// if (sd && skilllv < 3 && skill_check_cloaking(bl,&sd->sc)) + if (sd && pc_checkskill(sd, AS_CLOAKING)< 3 && skill_check_cloaking(bl, &sd->sc)) + return 0; + break; + case SC_MODECHANGE: + { + int mode; + struct status_data *bstatus = status_get_base_status(bl); + if (!bstatus) return 0; + mode = val2?val2:bstatus->mode; //Base mode + if (val3) mode|= val3; //Add mode + if (val4) mode&=~val4; //Del mode + if (mode == bstatus->mode) { //No change. + if (sc->data[type].timer != -1) //Abort previous status + return status_change_end(bl, type, -1); + return 0; + } + } + } + + //Check for BOSS resistances + if(status->mode&MD_BOSS && !(flag&1)) { + if (type>=SC_COMMON_MIN && type <= SC_COMMON_MAX) + return 0; + switch (type) { + case SC_BLESSING: + if (!undead_flag && status->race != RC_DEMON) + break; + case SC_QUAGMIRE: + case SC_DECREASEAGI: + case SC_SIGNUMCRUCIS: + case SC_PROVOKE: + case SC_ROKISWEIL: + case SC_COMA: + case SC_GRAVITATION: + return 0; + } + } + //Before overlapping fail, one must check for status cured. + switch (type) { + case SC_BLESSING: + if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) { + if (sc->data[SC_CURSE].timer!=-1) + status_change_end(bl,SC_CURSE,-1); + if (sc->data[SC_STONE].timer!=-1 && sc->opt1 == OPT1_STONE) + status_change_end(bl,SC_STONE,-1); + } + break; + case SC_INCREASEAGI: + if(sc->data[SC_DECREASEAGI].timer!=-1 ) + status_change_end(bl,SC_DECREASEAGI,-1); + break; + case SC_DONTFORGETME: + //is this correct? Maybe all three should stop the same subset of SCs... + if(sc->data[SC_ASSNCROS].timer!=-1 ) + status_change_end(bl,SC_ASSNCROS,-1); + case SC_QUAGMIRE: + if(sc->data[SC_CONCENTRATE].timer!=-1 ) + status_change_end(bl,SC_CONCENTRATE,-1); + if(sc->data[SC_TRUESIGHT].timer!=-1 ) + status_change_end(bl,SC_TRUESIGHT,-1); + if(sc->data[SC_WINDWALK].timer!=-1 ) + status_change_end(bl,SC_WINDWALK,-1); + //Also blocks the ones below... + case SC_DECREASEAGI: + if(sc->data[SC_INCREASEAGI].timer!=-1 ) + status_change_end(bl,SC_INCREASEAGI,-1); + if(sc->data[SC_ADRENALINE].timer!=-1 ) + status_change_end(bl,SC_ADRENALINE,-1); + if(sc->data[SC_ADRENALINE2].timer!=-1 ) + status_change_end(bl,SC_ADRENALINE2,-1); + if(sc->data[SC_SPEARQUICKEN].timer!=-1 ) + status_change_end(bl,SC_SPEARQUICKEN,-1); + if(sc->data[SC_TWOHANDQUICKEN].timer!=-1 ) + status_change_end(bl,SC_TWOHANDQUICKEN,-1); + if(sc->data[SC_CARTBOOST].timer!=-1 ) + status_change_end(bl,SC_CARTBOOST,-1); + if(sc->data[SC_ONEHAND].timer!=-1 ) + status_change_end(bl,SC_ONEHAND,-1); + break; + case SC_ONEHAND: + //Removes the Aspd potion effect, as reported by Vicious. [Skotlex] + if(sc->data[SC_ASPDPOTION0].timer!=-1) + status_change_end(bl,SC_ASPDPOTION0,-1); + if(sc->data[SC_ASPDPOTION1].timer!=-1) + status_change_end(bl,SC_ASPDPOTION1,-1); + if(sc->data[SC_ASPDPOTION2].timer!=-1) + status_change_end(bl,SC_ASPDPOTION2,-1); + if(sc->data[SC_ASPDPOTION3].timer!=-1) + status_change_end(bl,SC_ASPDPOTION3,-1); + break; + case SC_MAXOVERTHRUST: + //Cancels Normal Overthrust. [Skotlex] + if (sc->data[SC_OVERTHRUST].timer != -1) + status_change_end(bl, SC_OVERTHRUST, -1); + break; + case SC_KYRIE: + // -- moonsoul (added to undo assumptio status if target has it) + if(sc->data[SC_ASSUMPTIO].timer!=-1 ) + status_change_end(bl,SC_ASSUMPTIO,-1); + break; + case SC_DELUGE: + if (sc->data[SC_FOGWALL].timer != -1 && sc->data[SC_BLIND].timer != -1) + status_change_end(bl,SC_BLIND,-1); + break; + case SC_SILENCE: + if (sc->data[SC_GOSPEL].timer!=-1 && sc->data[SC_GOSPEL].val4 == BCT_SELF) + //Clear Gospel [Skotlex] + status_change_end(bl,SC_GOSPEL,-1); + break; + case SC_HIDING: + if(sc->data[SC_CLOSECONFINE].timer != -1) + status_change_end(bl, SC_CLOSECONFINE, -1); + if(sc->data[SC_CLOSECONFINE2].timer != -1) + status_change_end(bl, SC_CLOSECONFINE2, -1); + break; + case SC_BERSERK: + if(battle_config.berserk_cancels_buffs) + { + if (sc->data[SC_ONEHAND].timer != -1) + status_change_end(bl,SC_ONEHAND,-1); + if (sc->data[SC_TWOHANDQUICKEN].timer != -1) + status_change_end(bl,SC_TWOHANDQUICKEN,-1); + if (sc->data[SC_CONCENTRATION].timer != -1) + status_change_end(bl,SC_CONCENTRATION,-1); + if (sc->data[SC_PARRYING].timer != -1) + status_change_end(bl,SC_PARRYING,-1); + if (sc->data[SC_AURABLADE].timer != -1) + status_change_end(bl,SC_AURABLADE,-1); + } + break; + case SC_ASSUMPTIO: + if(sc->data[SC_KYRIE].timer!=-1) + status_change_end(bl,SC_KYRIE,-1); + break; + case SC_CARTBOOST: + if(sc->data[SC_DECREASEAGI].timer!=-1 ) + { //Cancel Decrease Agi, but take no further effect [Skotlex] + status_change_end(bl,SC_DECREASEAGI,-1); + return 0; + } + break; + case SC_FUSION: + if(sc->data[SC_SPIRIT].timer!=-1 ) + status_change_end(bl,SC_SPIRIT,-1); + break; + case SC_ADJUSTMENT: + if(sc->data[SC_MADNESSCANCEL].timer != -1) + status_change_end(bl,SC_MADNESSCANCEL,-1); + break; + case SC_MADNESSCANCEL: + if(sc->data[SC_ADJUSTMENT].timer!=-1) + status_change_end(bl,SC_ADJUSTMENT,-1); + break; + } + //Check for overlapping fails + if(sc->data[type].timer != -1){ + switch (type) { + case SC_ADRENALINE: + case SC_ADRENALINE2: + case SC_WEAPONPERFECTION: + case SC_OVERTHRUST: + if (sc->data[type].val2 > val2) + return 0; + break; + case SC_WARM: + { //Fetch the Group, half the attack interval. [Skotlex] + struct skill_unit_group *group = (struct skill_unit_group *)sc->data[type].val4; + if (group) + group->interval/=2; + return 1; + } + case SC_STUN: + case SC_SLEEP: + case SC_POISON: + case SC_CURSE: + case SC_SILENCE: + case SC_CONFUSION: + case SC_BLIND: + case SC_BLEEDING: + case SC_DPOISON: + case SC_COMBO: //You aren't supposed to change the combo (and it gets turned off when you trigger it) + case SC_CLOSECONFINE2: //Can't be re-closed in. + case SC_MARIONETTE: + case SC_MARIONETTE2: + return 0; + case SC_DANCING: + case SC_DEVOTION: + case SC_ASPDPOTION0: + case SC_ASPDPOTION1: + case SC_ASPDPOTION2: + case SC_ASPDPOTION3: + case SC_ATKPOTION: + case SC_MATKPOTION: + break; + case SC_GOSPEL: + //Must not override a casting gospel char. + if(sc->data[type].val4 == BCT_SELF) + return 0; + if(sc->data[type].val1 > val1) + return 1; + break; + case SC_ENDURE: + if(sc->data[type].val4 && !val4) + return 1; //Don't let you override infinite endure. + if(sc->data[type].val1 > val1) + return 1; + break; + case SC_KAAHI: + if(sc->data[type].val1 > val1) + return 1; + //Delete timer if it exists. + if (sc->data[type].val4 != -1) { + delete_timer(sc->data[type].val4,kaahi_heal_timer); + sc->data[type].val4=-1; + } + break; + default: + if(sc->data[type].val1 > val1) + return 1; //Return true to not mess up skill animations. [Skotlex + } + (sc->count)--; + delete_timer(sc->data[type].timer, status_change_timer); + sc->data[type].timer = -1; + } + + calc_flag = StatusChangeFlagTable[type]; + if(!(flag&4)) //Do not parse val settings when loading SCs + switch(type){ + case SC_ENDURE: /* インデュア */ + val2 = 7; // Hit-count [Celest] + break; + case SC_AUTOBERSERK: + if (status->hp < status->max_hp>>2 && + (sc->data[SC_PROVOKE].timer==-1 || sc->data[SC_PROVOKE].val2==0)) + sc_start4(bl,SC_PROVOKE,100,10,1,0,0,60000); + break; + + case SC_SIGNUMCRUCIS: + val2 = 10 + val1*2; //Def reduction + clif_emotion(bl,4); + break; + case SC_MAXIMIZEPOWER: + val2 = tick>0?tick:60000; + break; + case SC_EDP: // [Celest] + val2 = val1 + 2; //Chance to Poison enemies. + break; + case SC_POISONREACT: + val2=val1/2 + val1%2; // Number of counters [Celest] + break; + case SC_MAGICROD: + val2 = val1*20; //SP gained + break; + case SC_KYRIE: + val2 = status->max_hp * (val1 * 2 + 10) / 100; //%Max HP to absorb + val3 = (val1 / 2 + 5); //Hits + break; + case SC_MAGICPOWER: + //val1: Skill lv + val2 = 1; //Lasts 1 invocation + //val3 will store matk_min (needed in case you use ground-spells) + //val4 will store matk_max + break; + case SC_SACRIFICE: + val2 = 5; //Lasts 5 hits + break; + case SC_ENCPOISON: + val2= 25+5*val1; //Poisoning Chance (2.5+5%) + case SC_ASPERSIO: + case SC_FIREWEAPON: + case SC_WATERWEAPON: + case SC_WINDWEAPON: + case SC_EARTHWEAPON: + case SC_SHADOWWEAPON: + case SC_GHOSTWEAPON: + skill_enchant_elemental_end(bl,type); + break; + case SC_ELEMENTALCHANGE: + //Val1 is skill level, val2 is skill that invoked this. + if (!val3) //Val 3 holds the element, when not given, a random one is picked. + val3 = rand()%ELE_MAX; + val4 =1+rand()%4; //Elemental Lv is always a random value between 1 and 4. + break; + case SC_PROVIDENCE: + val2=val1*5; //Race/Ele resist + break; + case SC_REFLECTSHIELD: + val2=10+val1*3; //% Dmg reflected + break; + case SC_STRIPWEAPON: + if (bl->type != BL_PC) //Watk reduction + val2 = 5*val1; + break; + case SC_STRIPSHIELD: + if (bl->type != BL_PC) //Def reduction + val2 = 3*val1; + break; + case SC_STRIPARMOR: + if (bl->type != BL_PC) //Vit reduction + val2 = 8*val1; + break; + case SC_STRIPHELM: + if (bl->type != BL_PC) //Int reduction + val2 = 8*val1; + break; + case SC_AUTOSPELL: + //Val1 Skill LV of Autospell + //Val2 Skill ID to cast + //Val3 Max Lv to cast + val4 = 5 + val1*2; //Chance of casting + break; + case SC_VOLCANO: + if (status->def_ele == ELE_FIRE) + val2 = val1*10; //Watk increase + else + val2 = 0; + break; + case SC_VIOLENTGALE: + if (status->def_ele == ELE_WIND) + val2 = val1*3; //Flee increase + else + val2 = 0; + break; + case SC_DELUGE: + if(status->def_ele == ELE_WATER) + val2 = deluge_eff[val1-1]; //HP increase + else + val2 = 0; + break; + case SC_SUITON: + if (status_get_class(bl) != JOB_NINJA) { + //Is there some kind of formula behind this? + switch ((val1+1)/3) { + case 3: + val2 = 8; + break; + case 2: + val2 = 5; + break; + case 1: + val2 = 3; + break; + case 0: + val2 = 0; + break; + default: + val2 = 3*((val1+1)/3); + break; + } + } else val2 = 0; + break; + + case SC_SPEARQUICKEN: /* スピアクイッケン */ + calc_flag = 1; + val2 = 20+val1; + break; + + case SC_MOONLIT: + val2 = bl->id; + skill_setmapcell(bl,CG_MOONLIT, val1, CELL_SETMOONLIT); + break; + case SC_DANCING: + //val1 : Skill which is being danced. + //val2 : Skill Group of the Dance. + //val4 : Partner + val3 = 0; //Tick duration/Speed penalty. + if (sd) { //Store walk speed change in lower part of val3 + val3 = 500-40*pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON)); + if (sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_BARDDANCER) + val3 -= 40; //TODO: Figure out real bonus rate. + } + val3|= ((tick/1000)<<16)&0xFFFF0000; //Store tick in upper part of val3 + tick = 1000; + break; + case SC_LONGING: + val2 = 50-10*val1; //Aspd/Speed penalty. + break; + case SC_EXPLOSIONSPIRITS: + val2 = 75 + 25*val1; //Cri bonus + break; + case SC_ASPDPOTION0: + case SC_ASPDPOTION1: + case SC_ASPDPOTION2: + case SC_ASPDPOTION3: + val2 = 5*(2+type-SC_ASPDPOTION0); + break; + + case SC_WEDDING: + case SC_XMAS: + { + struct view_data *vd = status_get_viewdata(bl); + if (!vd) return 0; + //Store previous values as they could be removed. + val1 = vd->class_; + val2 = vd->weapon; + val3 = vd->shield; + val4 = vd->cloth_color; + unit_stop_attack(bl); + clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:JOB_XMAS); + clif_changelook(bl,LOOK_WEAPON,0); + clif_changelook(bl,LOOK_SHIELD,0); + clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); + } + break; + case SC_NOCHAT: + if(!battle_config.muting_players) { + sd->status.manner = 0; //Zido + return 0; + } + tick = 60000; + if (sd) clif_updatestatus(sd,SP_MANNER); + break; + + case SC_STONE: + val2 = status->max_hp/100; //Petrified damage per second: 1% + if (!val2) val2 = 1; + val3 = tick/1000; //Petrified HP-damage iterations. + if(val3 < 1) val3 = 1; + tick = 5000; //Petrifying time. + break; + + case SC_DPOISON: + //Lose 10/15% of your life as long as it doesn't brings life below 25% + if (status->hp > status->max_hp>>2) + { + int diff = status->max_hp*(bl->type==BL_PC?10:15)/100; + if (status->hp - diff < status->max_hp>>2) + diff = status->hp - (status->max_hp>>2); + status_zap(bl, diff, 0); + } + // fall through + case SC_POISON: /* 毒 */ + val3 = tick/1000; //Damage iterations + if(val3 < 1) val3 = 1; + tick = 1000; + //val4: HP damage + if (bl->type == BL_PC) + val4 = (type == SC_DPOISON) ? 3 + status->max_hp/50 : 3 + status->max_hp*3/200; + else + val4 = (type == SC_DPOISON) ? 3 + status->max_hp/100 : 3 + status->max_hp/200; + + break; + case SC_CONFUSION: + clif_emotion(bl,1); + break; + case SC_BLEEDING: + val4 = tick; + tick = 10000; + break; + + case SC_HIDING: + val2 = tick/1000; + tick = 1000; + //Store speed penalty on val3. + if(sd && (val3 = pc_checkskill(sd,RG_TUNNELDRIVE))>0) + val3 = 100 - 16*val3; + val4 = val1+3; //Seconds before SP substraction happen. + break; + case SC_CHASEWALK: + val2 = tick>0?tick:10000; //Interval at which SP is drained. + val3 = 65+val1*5; //Speed adjustment. + val4 = 10+val1*2; //SP cost. + if (map_flag_gvg(bl->m)) val4 *= 5; + break; + case SC_CLOAKING: + val2 = tick>0?tick:60000; //SP consumption rate. + val3 = 0; + if (sd && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && + (val3=pc_checkskill(sd,TF_MISS))>0) + val3 *= -1; //Substract the Dodge speed bonus. + val3+= 70+val1*3; //Speed adjustment without a wall. + //With a wall, it is val3 +25. + //val4&2 signals the presence of a wall. + if (!val4) + { //val4&1 signals eternal cloaking (not cancelled on attack) [Skotlex] + if (bl->type == BL_PC) //Standard cloaking. + val4 = battle_config.pc_cloak_check_type&2?1:0; + else + val4 = battle_config.monster_cloak_check_type&2?1:0; + } + break; + case SC_SIGHT: /* サイト/ルアフ */ + case SC_RUWACH: + case SC_SIGHTBLASTER: + val2 = tick/250; + tick = 10; + break; + + //Permanent effects. + case SC_MODECHANGE: + case SC_WEIGHT50: + case SC_WEIGHT90: + case SC_BROKENWEAPON: + case SC_BROKENARMOR: + case SC_READYSTORM: // Taekwon stances SCs [Dralnu] + case SC_READYDOWN: + case SC_READYCOUNTER: + case SC_READYTURN: + case SC_DODGE: + tick = 600*1000; + break; + + case SC_AUTOGUARD: + if (!flag) + { + struct map_session_data *tsd; + int i,t; + for(i=val2=0;i>1); + val2 += (t < 0)? 1:t; + } + if (sd) + for (i = 0; i < 5; i++) + { //Pass the status to the other affected chars. [Skotlex] + if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) + status_change_start(&tsd->bl,SC_AUTOGUARD,10000,val1,val2,0,0,tick,1); + } + } + break; + + case SC_DEFENDER: + if (!flag) + { + struct map_session_data *tsd; + int i; + val2 = 5 + val1*15; + if (sd) + for (i = 0; i < 5; i++) + { //See if there are devoted characters, and pass the status to them. [Skotlex] + if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i]))) + status_change_start(&tsd->bl,SC_DEFENDER,10000,val1,5+val1*5,0,0,tick,1); + } + } + break; + + case SC_TENSIONRELAX: + if (sd) { + pc_setsit(sd); + clif_sitting(sd); + } + val2 = 12; //SP cost + val4 = 10000; //Decrease at 10secs intervals. + val3 = tick/val4; + tick = val4; + break; + case SC_PARRYING: + val2 = 20 + val1*3; //Block Chance + break; + + case SC_WINDWALK: + val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5 + val3 = 4*val2; //movement speed % increase is 4 times that + break; + + case SC_JOINTBEAT: // Random break [DracoRPG] + val2 = rand()%6; //Type of break + if (val2 == 5) sc_start(bl,SC_BLEEDING,100,val1,skill_get_time2(StatusSkillChangeTable[type],val1)); + break; + + case SC_BERSERK: + if (sc->data[SC_ENDURE].timer == -1 || !sc->data[SC_ENDURE].val4) + sc_start4(bl, SC_ENDURE, 100,10,0,0,1, tick); + //HP healing is performing after the calc_status call. + if (sd) sd->canregen_tick = gettick() + 300000; + //Val2 holds HP penalty + if (!val4) val4 = 10000; //Val4 holds damage interval + val3 = tick/val4; //val3 holds skill duration + tick = val4; + break; + + case SC_GOSPEL: + if(val4 == BCT_SELF) { // self effect + val2 = tick/10000; + tick = 10000; + status_change_clear_buffs(bl,3); //Remove buffs/debuffs + } + break; + + case SC_MARIONETTE: + if (sd) { + val3 = 0; + val2 = sd->status.str>>1; + if (val2 > 0xFF) val2 = 0xFF; + val3|=val2<<16; + + val2 = sd->status.agi>>1; + if (val2 > 0xFF) val2 = 0xFF; + val3|=val2<<8; + + val2 = sd->status.vit>>1; + if (val2 > 0xFF) val2 = 0xFF; + val3|=val2; + + val4 = 0; + val2 = sd->status.int_>>1; + if (val2 > 0xFF) val2 = 0xFF; + val4|=val2<<16; + + val2 = sd->status.dex>>1; + if (val2 > 0xFF) val2 = 0xFF; + val4|=val2<<8; + + val2 = sd->status.luk>>1; + if (val2 > 0xFF) val2 = 0xFF; + val4|=val2; + } else { + struct status_data *b_status = status_get_base_status(bl); + if (!b_status) + return 0; + + val3 = 0; + val2 = b_status->str>>1; + if (val2 > 0xFF) val2 = 0xFF; + val3|=val2<<16; + + val2 = b_status->agi>>1; + if (val2 > 0xFF) val2 = 0xFF; + val3|=val2<<8; + + val2 = b_status->vit>>1; + if (val2 > 0xFF) val2 = 0xFF; + val3|=val2; + + val4 = 0; + val2 = b_status->int_>>1; + if (val2 > 0xFF) val2 = 0xFF; + val4|=val2<<16; + + val2 = b_status->dex>>1; + if (val2 > 0xFF) val2 = 0xFF; + val4|=val2<<8; + + val2 = b_status->luk>>1; + if (val2 > 0xFF) val2 = 0xFF; + val4|=val2; + } + val2 = tick/1000; + tick = 1000; + break; + case SC_MARIONETTE2: + { + struct block_list *pbl = map_id2bl(val1); + struct status_change *psc = pbl?status_get_sc(pbl):NULL; + int stat,max; + if (!psc || psc->data[SC_MARIONETTE].timer == -1) + return 0; + val2 = tick /1000; + val3 = val4 = 0; + if (sd) { + max = pc_maxparameter(sd); //Cap to max parameter. [Skotlex] + //Str + stat = (psc->data[SC_MARIONETTE].val3>>16)&0xFF; + if (sd->status.str+stat > max) + stat =max-sd->status.str; + val3 |= stat<<16; + //Agi + stat = (psc->data[SC_MARIONETTE].val3>>8)&0xFF; + if (sd->status.agi+stat > max) + stat =max-sd->status.agi; + val3 |= stat<<8; + //Vit + stat = psc->data[SC_MARIONETTE].val3&0xFF; + if (sd->status.vit+stat > max) + stat =max-sd->status.vit; + val3 |= stat; + //Int + stat = (psc->data[SC_MARIONETTE].val4>>16)&0xFF; + if (sd->status.int_+stat > max) + stat =max-sd->status.int_; + val4 |= stat<<16; + //Dex + stat = (psc->data[SC_MARIONETTE].val4>>8)&0xFF; + if (sd->status.dex+stat > max) + stat =max-sd->status.dex; + val4 |= stat<<8; + //Luk + stat = psc->data[SC_MARIONETTE].val4&0xFF; + if (sd->status.luk+stat > max) + stat =max-sd->status.luk; + val4 |= stat; + } else { + struct status_data *status = status_get_base_status(bl); + if (!status) return 0; + max = 0xFF; //Assume a 256 max parameter + //Str + stat = (psc->data[SC_MARIONETTE].val3>>16)&0xFF; + if (status->str+stat > max) + stat = max - status->str; + val3 |= stat<<16; + //Agi + stat = (psc->data[SC_MARIONETTE].val3>>8)&0xFF; + if (status->agi+stat > max) + stat = max - status->agi; + val3 |= stat<<8; + //Vit + stat = psc->data[SC_MARIONETTE].val3&0xFF; + if (status->vit+stat > max) + stat = max - status->vit; + val3 |= stat; + //Int + stat = (psc->data[SC_MARIONETTE].val4>>16)&0xFF; + if (status->int_+stat > max) + stat = max - status->int_; + val4 |= stat<<16; + //Dex + stat = (psc->data[SC_MARIONETTE].val4>>8)&0xFF; + if (status->dex+stat > max) + stat = max - status->dex; + val4 |= stat<<8; + //Luk + stat = psc->data[SC_MARIONETTE].val4&0xFF; + if (status->luk+stat > max) + stat = max - status->luk; + val4 |= stat; + } + tick = 1000; + break; + } + case SC_REJECTSWORD: + val2 = 15*val1; //Reflect chance + val3 = 3; //Reflections + break; + + case SC_MEMORIZE: + val2 = 5; //Memorized casts. + break; + + case SC_GRAVITATION: + if (val3 == BCT_SELF) { + struct unit_data *ud = unit_bl2ud(bl); + if (ud) { + ud->canmove_tick += tick; + ud->canact_tick += tick; + } + } + break; + + case SC_HERMODE: + status_change_clear_buffs(bl,1); + break; + + case SC_REGENERATION: + if (val1 == 1) + val2 = 2; + else + val2 = val1; //HP Regerenation rate: 200% 200% 300% + val3 = val1; //SP Regeneration Rate: 100% 200% 300% + break; + + case SC_DEVOTION: + { + struct map_session_data *src; + if ((src = map_id2sd(val1)) && src->sc.count) + { //Try to inherit the status from the Crusader [Skotlex] + //Ideally, we should calculate the remaining time and use that, but we'll trust that + //once the Crusader's status changes, it will reflect on the others. + int type2 = SC_AUTOGUARD; + if (src->sc.data[type2].timer != -1) + sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); + type2 = SC_DEFENDER; + if (src->sc.data[type2].timer != -1) + sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); + type2 = SC_REFLECTSHIELD; + if (src->sc.data[type2].timer != -1) + sc_start(bl,type2,100,src->sc.data[type2].val1,skill_get_time(StatusSkillChangeTable[type2],src->sc.data[type2].val1)); + + } + break; + } + + case SC_COMA: //Coma. Sends a char to 1HP + status_zap(bl, status_get_hp(bl)-1, 0); + return 1; + + case SC_CLOSECONFINE2: + { + struct block_list *src = val2?map_id2bl(val2):NULL; + struct status_change *sc2 = src?status_get_sc(src):NULL; + if (src && sc2) { + if (sc2->data[SC_CLOSECONFINE].timer == -1) //Start lock on caster. + sc_start4(src,SC_CLOSECONFINE,100,sc->data[type].val1,1,0,0,tick+1000); + else { //Increase count of locked enemies and refresh time. + sc2->data[SC_CLOSECONFINE].val2++; + delete_timer(sc2->data[SC_CLOSECONFINE].timer, status_change_timer); + sc2->data[SC_CLOSECONFINE].timer = add_timer(gettick()+tick+1000, status_change_timer, src->id, SC_CLOSECONFINE); + } + } else //Status failed. + return 0; + } + break; + case SC_KAITE: + val2 = 1+val1/5; //Number of bounces: 1 + skilllv/5 + break; + case SC_KAUPE: + switch (val1) { + case 3: //33*3 + 1 -> 100% + val2++; + case 1: + case 2: //33, 66% + val2 += 33*val1; + val3 = 1; //Dodge 1 attack total. + break; + default: //Custom. For high level mob usage, higher level means more blocks. [Skotlex] + val2 = 100; + val3 = val1-2; + break; + } + break; + + case SC_COMBO: + { + struct unit_data *ud = unit_bl2ud(bl); + switch (val1) { //Val1 contains the skill id + case TK_STORMKICK: + clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1); + break; + case TK_DOWNKICK: + clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1); + break; + case TK_TURNKICK: + clif_skill_nodamage(bl,bl,TK_READYTURN,1,1); + break; + case TK_COUNTER: + clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1); + break; + } + if (ud) { + ud->attackabletime = gettick()+tick; + unit_set_walkdelay(bl, gettick(), tick, 1); + } + } + break; + case SC_TKREST: + val2 = 11-val1; //Chance to consume: 11-skilllv% + break; + case SC_RUN: + val4 = gettick(); //Store time at which you started running. + break; + case SC_KAAHI: + if(flag&4) { + val4 = -1; + break; + } + val2 = 200*val1; //HP heal + val3 = 5*val1; //SP cost + val4 = -1; //Kaahi Timer. + break; + case SC_BLESSING: + if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) + val2 = val1; + else + val2 = 0; //0 -> Half stat. + break; + case SC_TRICKDEAD: /* 死んだふり */ + { + struct view_data *vd = status_get_viewdata(bl); + if (vd) vd->dead_sit = 1; + break; + } + + case SC_CONCENTRATE: + val2 = 2 + val1; + if (sd) { //Store the card-bonus data that should not count in the % + val3 = sd->param_bonus[1]; //Agi + val4 = sd->param_bonus[4]; //Dex + } else { + val3 = val4 = 0; + } + break; + case SC_ADRENALINE2: + case SC_ADRENALINE: + if (val2 || !battle_config.party_skill_penalty) + val2 = 30; + else + val2 = 20; + break; + case SC_CONCENTRATION: + val2 = 5*val1; //Batk/Watk Increase + val3 = 10*val1; //Hit Increase + val4 = 5*val1; //Def reduction + break; + case SC_ANGELUS: + val2 = 5*val1; //def increase + break; + case SC_IMPOSITIO: + val2 = 5*val1; //watk increase + break; + case SC_MELTDOWN: + val2 = 100*val1; //Chance to break weapon + val3 = 70*val1; //Change to break armor + break; + case SC_TRUESIGHT: + val2 = 10*val1; //Critical increase + val3 = 3*val1; //Hit increase + break; + case SC_SUN_COMFORT: + val2 = (status_get_lv(bl) + status->dex + status->luk)/2; //def increase + break; + case SC_MOON_COMFORT: + val2 = (status_get_lv(bl) + status->dex + status->luk)/10; //luk increase + break; + case SC_STAR_COMFORT: + val2 = (status_get_lv(bl) + status->dex + status->luk)/10; //Aspd increase + break; + case SC_QUAGMIRE: + val2 = (sd?5:10)*val1; //Agi/Dex decrease. + break; + + // gs_something1 [Vicious] + case SC_GATLINGFEVER: + val2 = 2*val1; //Aspd increase + val3 = 5*val1; //Flee decrease + break; + + default: + if (calc_flag == SCB_NONE && StatusSkillChangeTable[type]==0) + { //Status change with no calc, and no skill associated...? unknown? + if(battle_config.error_log) + ShowError("UnknownStatusChange [%d]\n", type); + return 0; + } + } + else //Special considerations when loading SC data. + switch (type) { + case SC_WEDDING: + case SC_XMAS: + clif_changelook(bl,LOOK_BASE,type==SC_WEDDING?JOB_WEDDING:JOB_XMAS); + clif_changelook(bl,LOOK_WEAPON,0); + clif_changelook(bl,LOOK_SHIELD,0); + clif_changelook(bl,LOOK_CLOTHES_COLOR,val4); + break; + case SC_KAAHI: + val4 = -1; + break; + } + //Those that make you stop attacking/walking.... + switch (type) { + case SC_FREEZE: + case SC_STUN: + case SC_SLEEP: + case SC_STONE: + if (sd && pc_issit(sd)) //Avoid sprite sync problems. + pc_setstand(sd); + case SC_TRICKDEAD: + unit_stop_attack(bl); + skill_stop_dancing(bl); + // Cancel cast when get status [LuzZza] + if (battle_config.sc_castcancel) + unit_skillcastcancel(bl, 0); + case SC_STOP: + case SC_CONFUSION: + case SC_CLOSECONFINE: + case SC_CLOSECONFINE2: + case SC_ANKLE: + case SC_SPIDERWEB: + case SC_MADNESSCANCEL: + unit_stop_walking(bl,1); + break; + case SC_HIDING: + case SC_CLOAKING: + case SC_CHASEWALK: + unit_stop_attack(bl); + break; + } + + if (sd) + { //Why must it be ONLY for players? [Skotlex] + if (bl->prev) + clif_status_change(bl,StatusIconChangeTable[type],1); + else + clif_status_load(bl,StatusIconChangeTable[type],1); + } + + // Set option as needed. + opt_flag = 1; + switch(type){ + //OPT1 + case SC_STONE: + case SC_FREEZE: + case SC_STUN: + case SC_SLEEP: + if(type == SC_STONE) + sc->opt1 = OPT1_STONEWAIT; + else + sc->opt1 = OPT1_STONE + (type - SC_STONE); + break; + //OPT2 + case SC_POISON: + case SC_CURSE: + case SC_SILENCE: + case SC_BLIND: + sc->opt2 |= 1<<(type-SC_POISON); + break; + case SC_DPOISON: + sc->opt2 |= OPT2_DPOISON; + break; + case SC_SIGNUMCRUCIS: + sc->opt2 |= OPT2_SIGNUMCRUCIS; + break; + //OPT3 + case SC_TWOHANDQUICKEN: + case SC_SPEARQUICKEN: + case SC_CONCENTRATION: + sc->opt3 |= 1; + opt_flag = 0; + break; + case SC_MAXOVERTHRUST: + case SC_OVERTHRUST: + case SC_SWOO: //Why does it shares the same opt as Overthrust? Perhaps we'll never know... + sc->opt3 |= 2; + opt_flag = 0; + break; + case SC_ENERGYCOAT: + sc->opt3 |= 4; + opt_flag = 0; + break; + case SC_INCATKRATE: + //Simulate Explosion Spirits effect for NPC_POWERUP [Skotlex] + if (bl->type != BL_MOB) { + opt_flag = 0; + break; + } + case SC_EXPLOSIONSPIRITS: + sc->opt3 |= 8; + opt_flag = 0; + break; + case SC_STEELBODY: + case SC_SKA: + sc->opt3 |= 16; + opt_flag = 0; + break; + case SC_BLADESTOP: + sc->opt3 |= 32; + opt_flag = 0; + break; + case SC_BERSERK: + sc->opt3 |= 128; + opt_flag = 0; + break; + case SC_MARIONETTE: + case SC_MARIONETTE2: + sc->opt3 |= 1024; + opt_flag = 0; + break; + case SC_ASSUMPTIO: + sc->opt3 |= 2048; + opt_flag = 0; + break; + case SC_WARM: //SG skills [Komurka] + sc->opt3 |= 4096; + opt_flag = 0; + break; + + //OPTION + case SC_HIDING: + sc->option |= OPTION_HIDE; + break; + case SC_CLOAKING: + sc->option |= OPTION_CLOAK; + break; + case SC_CHASEWALK: + sc->option |= OPTION_CHASEWALK|OPTION_CLOAK; + break; + case SC_SIGHT: + sc->option |= OPTION_SIGHT; + break; + case SC_RUWACH: + sc->option |= OPTION_RUWACH; + break; + case SC_WEDDING: + sc->option |= OPTION_WEDDING; + break; + case SC_ORCISH: + sc->option |= OPTION_ORCISH; + break; + case SC_SIGHTTRASHER: + sc->option |= OPTION_SIGHTTRASHER; + break; + case SC_FUSION: + sc->option |= OPTION_FLYING; + break; + default: + opt_flag = 0; + } + + if(opt_flag) + clif_changeoption(bl); + + (sc->count)++; + + sc->data[type].val1 = val1; + sc->data[type].val2 = val2; + sc->data[type].val3 = val3; + sc->data[type].val4 = val4; + + sc->data[type].timer = add_timer( + gettick() + tick, status_change_timer, bl->id, type); + + if (calc_flag) + status_calc_bl(bl,calc_flag); + + if(sd && sd->pd) + pet_sc_check(sd, type); //Skotlex: Pet Status Effect Healing + + if (type==SC_BERSERK) { + sc->data[type].val2 = 5*status->max_hp/100; + status_heal(bl, status->max_hp, 0, 1); //Do not use percent_heal as this healing must override BERSERK's block. + status_percent_damage(NULL, bl, 0, 100); //Damage all SP + } + + if (type==SC_RUN) { + struct unit_data *ud = unit_bl2ud(bl); + if (ud) + ud->state.running = unit_run(bl); + } + return 1; +} +/*========================================== + * ステータス異常全解除 + *------------------------------------------ + */ +int status_change_clear(struct block_list *bl,int type) +{ + struct status_change* sc; + int i; + + sc = status_get_sc(bl); + + if (!sc || sc->count == 0) + return 0; + + if(sc->data[SC_DANCING].timer != -1) + skill_stop_dancing(bl); + for(i = 0; i < SC_MAX; i++) + { + //Type 0: PC killed -> Place here stats that do not dispel on death. + if(sc->data[i].timer == -1 || + (type == 0 && ( + i == SC_EDP || i == SC_MELTDOWN || i == SC_XMAS || i == SC_NOCHAT || + i == SC_FUSION || i == SC_TKREST || i == SC_READYSTORM || + i == SC_READYDOWN || i == SC_READYCOUNTER || i == SC_READYTURN + ))) + continue; + + status_change_end(bl, i, -1); + + if (type == 1 && sc->data[i].timer != -1) + { //If for some reason status_change_end decides to still keep the status when quitting. [Skotlex] + (sc->count)--; + delete_timer(sc->data[i].timer, status_change_timer); + sc->data[i].timer = -1; + } + } + sc->opt1 = 0; + sc->opt2 = 0; + sc->opt3 = 0; + sc->option &= OPTION_MASK; + + if(!type || type&2) + clif_changeoption(bl); + + return 1; +} + +/*========================================== + * ステータス異常終了 + *------------------------------------------ + */ +int status_change_end( struct block_list* bl , int type,int tid ) +{ + struct map_session_data *sd; + struct status_change *sc; + struct status_data *status; + int opt_flag=0, calc_flag = 0; + + nullpo_retr(0, bl); + + sc = status_get_sc(bl); + status = status_get_status_data(bl); + nullpo_retr(0,sc); + nullpo_retr(0,status); + + if(type < 0 || type >= SC_MAX) + return 0; + + BL_CAST(BL_PC,bl,sd); + + if (sc->data[type].timer == -1 || + (sc->data[type].timer != tid && tid != -1)) + return 0; + + if (tid == -1) + delete_timer(sc->data[type].timer,status_change_timer); + + sc->data[type].timer=-1; + (sc->count)--; + + calc_flag = StatusChangeFlagTable[type]; + switch(type){ + case SC_WEDDING: + case SC_XMAS: + { + struct view_data *vd = status_get_viewdata(bl); + if (!vd) return 0; + if (sd) //Load data from sd->status.* as the stored values could have changed. + status_set_viewdata(bl, sd->status.class_); + else { + vd->class_ = sc->data[type].val1; + vd->weapon = sc->data[type].val2; + vd->shield = sc->data[type].val3; + vd->cloth_color = sc->data[type].val4; + } + clif_changelook(bl,LOOK_BASE,vd->class_); + clif_changelook(bl,LOOK_WEAPON,vd->weapon); + clif_changelook(bl,LOOK_SHIELD,vd->shield); + clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color); + } + break; + case SC_RUN: + { + struct unit_data *ud = unit_bl2ud(bl); + if (ud) { + ud->state.running = 0; + if (ud->walktimer != -1) + unit_stop_walking(bl,1); + } + if (sc->data[type].val1 >= 7 && + DIFF_TICK(gettick(), sc->data[type].val4) <= 1000 && + (!sd || (sd->weapontype1 == 0 && sd->weapontype2 == 0 && + (sd->class_&MAPID_BASEMASK) != MAPID_SOUL_LINKER)) + ) + sc_start(bl,SC_SPURT,100,sc->data[type].val1,skill_get_time2(StatusSkillChangeTable[type], sc->data[type].val1)); + } + break; + case SC_AUTOBERSERK: + if (sc->data[SC_PROVOKE].timer != -1 && sc->data[SC_PROVOKE].val2 == 1) + status_change_end(bl,SC_PROVOKE,-1); + break; + + case SC_DEFENDER: + case SC_REFLECTSHIELD: + case SC_AUTOGUARD: + if (sd) { + struct map_session_data *tsd; + int i; + for (i = 0; i < 5; i++) + { //Clear the status from the others too [Skotlex] + if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type].timer != -1) + status_change_end(&tsd->bl,type,-1); + } + } + break; + case SC_DEVOTION: + { + struct map_session_data *md = map_id2sd(sc->data[type].val1); + //The status could have changed because the Crusader left the game. [Skotlex] + if (md) + { + md->devotion[sc->data[type].val2] = 0; + clif_devotion(md); + } + //Remove AutoGuard and Defender [Skotlex] + if (sc->data[SC_AUTOGUARD].timer != -1) + status_change_end(bl,SC_AUTOGUARD,-1); + if (sc->data[SC_DEFENDER].timer != -1) + status_change_end(bl,SC_DEFENDER,-1); + if (sc->data[SC_REFLECTSHIELD].timer != -1) + status_change_end(bl,SC_REFLECTSHIELD,-1); + break; + } + case SC_BLADESTOP: + if(sc->data[type].val4) + { + struct block_list *tbl = (struct block_list *)sc->data[type].val4; + struct status_change *tsc = status_get_sc(tbl); + sc->data[type].val4 = 0; + if(tsc && tsc->data[SC_BLADESTOP].timer!=-1) + { + tsc->data[SC_BLADESTOP].val4 = 0; + status_change_end(tbl,SC_BLADESTOP,-1); + } + clif_bladestop(bl,tbl,0); + } + break; + case SC_DANCING: + { + struct map_session_data *dsd; + struct status_change *dsc; + struct skill_unit_group *group; + if(sc->data[type].val2) + { + group = (struct skill_unit_group *)sc->data[type].val2; + sc->data[type].val2 = 0; + skill_delunitgroup(bl, group); + } + if(sc->data[type].val4 && sc->data[type].val4 != BCT_SELF && (dsd=map_id2sd(sc->data[type].val4))){ + dsc = &dsd->sc; + if(dsc && dsc->data[type].timer!=-1) + { //This will prevent recursive loops. + dsc->data[type].val2 = dsc->data[type].val4 = 0; + status_change_end(&dsd->bl, type, -1); + } + } + } + //Only dance that doesn't has ground tiles... [Skotlex] + if(sc->data[type].val1 == CG_MOONLIT) + status_change_end(bl, SC_MOONLIT, -1); + + if (sc->data[SC_LONGING].timer!=-1) + status_change_end(bl,SC_LONGING,-1); + break; + case SC_NOCHAT: + if (sd && battle_config.manner_system) + { + //Why set it to 0? Can't we use good manners for something? [Skotlex] +// if (sd->status.manner >= 0) // weeee ^^ [celest] +// sd->status.manner = 0; + clif_updatestatus(sd,SP_MANNER); + } + break; + case SC_SPLASHER: + { + struct block_list *src=map_id2bl(sc->data[type].val3); + if(src && tid!=-1) + skill_castend_damage_id(src, bl,sc->data[type].val2,sc->data[type].val1,gettick(),0 ); + } + break; + case SC_CLOSECONFINE2: + { + struct block_list *src = sc->data[type].val2?map_id2bl(sc->data[type].val2):NULL; + struct status_change *sc2 = src?status_get_sc(src):NULL; + if (src && sc2 && sc2->count) { + //If status was already ended, do nothing. + if (sc2->data[SC_CLOSECONFINE].timer != -1) + { //Decrease count + if (--sc2->data[SC_CLOSECONFINE].val1 <= 0) //No more holds, free him up. + status_change_end(src, SC_CLOSECONFINE, -1); + } + } + } + case SC_CLOSECONFINE: + if (sc->data[type].val2 > 0) { + //Caster has been unlocked... nearby chars need to be unlocked. + int range = 1 + +skill_get_range2(bl, StatusSkillChangeTable[type], sc->data[type].val1) + +skill_get_range2(bl, TF_BACKSLIDING, 1); //Since most people use this to escape the hold.... + map_foreachinarea(status_change_timer_sub, + bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sc,type,gettick()); + } + break; + case SC_COMBO: //Clear last used skill when it is part of a combo. + if (sd && sd->skillid_old == sc->data[type].val1) + sd->skillid_old = sd->skilllv_old = 0; + break; + + case SC_FREEZE: + sc->data[type].val3 = 0; //Clear Storm Gust hit count + break; + + case SC_MARIONETTE: + case SC_MARIONETTE2: /// Marionette target + if (sc->data[type].val1) + { // check for partner and end their marionette status as well + int type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE; + struct block_list *pbl = map_id2bl(sc->data[type].val1); + struct status_change* sc2 = pbl?status_get_sc(pbl):NULL; + + if (sc2 && sc2->count && sc2->data[type2].timer != -1) + { + sc2->data[type2].val1 = 0; + status_change_end(pbl, type2, -1); + } + } + if (type == SC_MARIONETTE) + clif_marionette(bl, 0); //Clear effect. + break; + + case SC_BERSERK: + //val4 indicates if the skill was dispelled. [Skotlex] + if(status->hp > 100 && !sc->data[type].val4) + status_zap(bl, status->hp-100, 0); + if(sc->data[SC_ENDURE].timer != -1) + status_change_end(bl, SC_ENDURE, -1); + break; + case SC_GRAVITATION: + if (sc->data[type].val3 == BCT_SELF) { + struct unit_data *ud = unit_bl2ud(bl); + if (ud) + ud->canmove_tick = ud->canact_tick = gettick(); + } + break; + case SC_GOSPEL: //Clear the buffs from other chars. + if (sc->data[type].val3) { //Clear the group. + struct skill_unit_group *group = (struct skill_unit_group *)sc->data[type].val3; + sc->data[type].val3 = 0; + skill_delunitgroup(bl, group); + } + break; + case SC_HERMODE: + case SC_BASILICA: //Clear the skill area. [Skotlex] + if(sc->data[type].val3 == BCT_SELF) + skill_clear_unitgroup(bl); + break; + case SC_MOONLIT: //Clear the unit effect. [Skotlex] + skill_setmapcell(bl,CG_MOONLIT, sc->data[SC_MOONLIT].val1, CELL_CLRMOONLIT); + break; + case SC_TRICKDEAD: /* 死んだふり */ + { + struct view_data *vd = status_get_viewdata(bl); + if (vd) vd->dead_sit = 0; + break; + } + case SC_WARM: + if (sc->data[type].val4) { //Clear the group. + struct skill_unit_group *group = (struct skill_unit_group *)sc->data[type].val4; + sc->data[type].val4 = 0; + skill_delunitgroup(bl, group); + } + break; + case SC_KAAHI: + //Delete timer if it exists. + if (sc->data[type].val4 != -1) { + delete_timer(sc->data[type].val4,kaahi_heal_timer); + sc->data[type].val4=-1; + } + break; + } + + if (sd) + { //Why must it be ONLY for players? [Skotlex] + if (bl->prev) + clif_status_change(bl,StatusIconChangeTable[type],0); + else + clif_status_load(bl,StatusIconChangeTable[type],0); + } + + opt_flag = 1; + switch(type){ + case SC_STONE: + case SC_FREEZE: + case SC_STUN: + case SC_SLEEP: + sc->opt1 = 0; + break; + + case SC_POISON: + case SC_CURSE: + case SC_SILENCE: + case SC_BLIND: + sc->opt2 &= ~(1<<(type-SC_POISON)); + break; + case SC_DPOISON: + sc->opt2 &= ~OPT2_DPOISON; + break; + case SC_SIGNUMCRUCIS: + sc->opt2 &= ~OPT2_SIGNUMCRUCIS; + break; + + case SC_HIDING: + sc->option &= ~OPTION_HIDE; + break; + case SC_CLOAKING: + sc->option &= ~OPTION_CLOAK; + break; + case SC_CHASEWALK: + sc->option &= ~(OPTION_CHASEWALK|OPTION_CLOAK); + break; + case SC_SIGHT: + sc->option &= ~OPTION_SIGHT; + break; + case SC_WEDDING: + sc->option &= ~OPTION_WEDDING; + break; + case SC_ORCISH: + sc->option &= ~OPTION_ORCISH; + break; + case SC_RUWACH: + sc->option &= ~OPTION_RUWACH; + break; + case SC_SIGHTTRASHER: + sc->option &= ~OPTION_SIGHTTRASHER; + break; + case SC_FUSION: + sc->option &= ~OPTION_FLYING; + break; + //opt3 + case SC_TWOHANDQUICKEN: + case SC_ONEHAND: + case SC_SPEARQUICKEN: + case SC_CONCENTRATION: + sc->opt3 &= ~1; + opt_flag = 0; + break; + case SC_OVERTHRUST: + case SC_MAXOVERTHRUST: + case SC_SWOO: + sc->opt3 &= ~2; + opt_flag = 0; + break; + case SC_ENERGYCOAT: + sc->opt3 &= ~4; + opt_flag = 0; + break; + case SC_INCATKRATE: //Simulated Explosion spirits effect. + if (bl->type != BL_MOB) + break; + case SC_EXPLOSIONSPIRITS: + sc->opt3 &= ~8; + opt_flag = 0; + break; + case SC_STEELBODY: + case SC_SKA: + sc->opt3 &= ~16; + opt_flag = 0; + break; + case SC_BLADESTOP: + sc->opt3 &= ~32; + opt_flag = 0; + break; + case SC_BERSERK: + sc->opt3 &= ~128; + opt_flag = 0; + break; + case SC_MARIONETTE: + case SC_MARIONETTE2: + sc->opt3 &= ~1024; + opt_flag = 0; + break; + case SC_ASSUMPTIO: + sc->opt3 &= ~2048; + opt_flag = 0; + break; + case SC_WARM: //SG skills [Komurka] + sc->opt3 &= ~4096; + opt_flag = 0; + break; + default: + opt_flag = 0; + } + + if(opt_flag) + clif_changeoption(bl); + + if (calc_flag) + status_calc_bl(bl,calc_flag); + + return 1; +} + +int kaahi_heal_timer(int tid, unsigned int tick, int id, int data) +{ + struct block_list *bl; + struct status_change *sc; + struct status_data *status; + int hp; + + bl=map_id2bl(id); + sc=status_get_sc(bl); + status=status_get_status_data(bl); + + if (!sc || !status || data != SC_KAAHI || sc->data[data].timer==-1) + return 0; + if(sc->data[data].val4 != tid) { + if (battle_config.error_log) + ShowError("kaahi_heal_timer: Timer mismatch: %d != %d\n", tid, sc->data[data].val4); + sc->data[data].val4=-1; + return 0; + } + + if(!status_charge(bl, 0, sc->data[data].val3)) { + sc->data[data].val4=-1; + return 0; + } + + hp = status->max_hp - status->hp; + if (hp > sc->data[data].val2) + hp = sc->data[data].val2; + if (hp) { + status_heal(bl, hp, 0, 0); + clif_skill_nodamage(NULL,bl,AL_HEAL,hp,1); + } + sc->data[data].val4=-1; + return 1; +} + +/*========================================== + * ステータス異常終了タイマー + *------------------------------------------ + */ +int status_change_timer(int tid, unsigned int tick, int id, int data) +{ + int type = data; + struct block_list *bl; + struct map_session_data *sd=NULL; + struct status_data *status; + struct status_change *sc; + +// security system to prevent forgetting timer removal + int temp_timerid; + + bl=map_id2bl(id); +#ifndef _WIN32 + nullpo_retr_f(0, bl, "id=%d data=%d",id,data); +#endif + sc=status_get_sc(bl); + status = status_get_status_data(bl); + + if (!sc || !status) + { //Temporal debug until case is resolved. [Skotlex] + ShowDebug("status_change_timer: Null pointer id: %d data: %d bl-type: %d\n", id, data, bl?bl->type:-1); + return 0; + } + + if(bl->type==BL_PC) + sd=(struct map_session_data *)bl; + + if(sc->data[type].timer != tid) { + if(battle_config.error_log) + ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sc->data[type].timer, bl->id); + return 0; + } + + // security system to prevent forgetting timer removal + // you shouldn't be that careless inside the switch here + temp_timerid = sc->data[type].timer; + sc->data[type].timer = -1; + + switch(type){ /* 特殊な?理になる場合 */ + case SC_MAXIMIZEPOWER: /* マキシマイズパワ? */ + case SC_CLOAKING: + if(!status_charge(bl, 0, 1)) + break; //Not enough SP to continue. + sc->data[type].timer=add_timer( + sc->data[type].val2+tick, status_change_timer, bl->id, data); + return 0; + + case SC_CHASEWALK: + if(!status_charge(bl, 0, sc->data[type].val4)) + break; //Not enough SP to continue. + + if (sc->data[SC_INCSTR].timer == -1) { + sc_start(bl, SC_INCSTR,100,1<<(sc->data[type].val1-1), + (sc->data[SC_SPIRIT].timer != -1 && sc->data[SC_SPIRIT].val2 == SL_ROGUE?10:1) //SL bonus -> x10 duration + *skill_get_time2(StatusSkillChangeTable[type],sc->data[type].val1)); + } + sc->data[type].timer = add_timer( + sc->data[type].val2+tick, status_change_timer, bl->id, data); + return 0; + break; + + case SC_HIDING: + if((--sc->data[type].val2)>0){ + + if(sc->data[type].val2 % sc->data[type].val4 == 0 &&!status_charge(bl, 0, 1)) + break; //Fail if it's time to substract SP and there isn't. + + sc->data[type].timer=add_timer( + 1000+tick, status_change_timer, + bl->id, data); + return 0; + } + break; + + case SC_SIGHT: + case SC_RUWACH: + case SC_SIGHTBLASTER: + { + map_foreachinrange( status_change_timer_sub, bl, + skill_get_splash(StatusSkillChangeTable[type], sc->data[type].val1), + BL_CHAR, bl,sc,type,tick); + + if( (--sc->data[type].val2)>0 ){ + sc->data[type].timer=add_timer( /* タイマ?再設定 */ + 250+tick, status_change_timer, + bl->id, data); + return 0; + } + } + break; + + case SC_PROVOKE: + if(sc->data[type].val2) { //Auto-provoke (it is ended in status_heal) + sc->data[type].timer=add_timer(1000*60+tick,status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_ENDURE: + if(sc->data[type].val4) { //Infinite Endure. + sc->data[type].timer=add_timer(1000*60+tick,status_change_timer, bl->id, data); + return 0; + } + break; + + case SC_STONE: + if(sc->opt1 == OPT1_STONEWAIT) { + sc->data[type].val4 = 0; + unit_stop_walking(bl,1); + sc->opt1 = OPT1_STONE; + clif_changeoption(bl); + sc->data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data ); + status_calc_bl(bl, SCB_DEF_ELE); + return 0; + } + if((--sc->data[type].val3) > 0) { + if((++sc->data[type].val4)%5 == 0 && status->hp > status->max_hp>>2) + status_zap(bl, sc->data[type].val2, 0); + sc->data[type].timer=add_timer(1000+tick,status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_POISON: + if(status->hp <= status->max_hp>>2) //Stop damaging after 25% HP left. + break; + case SC_DPOISON: + if ((--sc->data[type].val3) > 0) { + if (sc->data[SC_SLOWPOISON].timer == -1) { + status_zap(bl, sc->data[type].val4, 0); + if (status_isdead(bl)) + break; + } + sc->data[type].timer = add_timer (1000 + tick, status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_TENSIONRELAX: + if(status->max_hp > status->hp && (--sc->data[type].val3) > 0){ + sc->data[type].timer=add_timer( + sc->data[type].val4+tick, status_change_timer, + bl->id, data); + return 0; + } + break; + case SC_BLEEDING: // [celest] + // i hope i haven't interpreted it wrong.. which i might ^^; + // Source: + // - 10ゥェエェネェヒHPェャハ盒 + // - ェホェ゙ェ゙ォオ?ォミケヤムェ茘ォォーェキェニェ?ヘェマ眈ェィェハェ、 + // To-do: bleeding effect increases damage taken? + if ((sc->data[type].val4 -= 10000) >= 0) { + status_fix_damage(NULL, bl, rand()%600 + 200, 0); + if (status_isdead(bl)) + break; + sc->data[type].timer = add_timer(10000 + tick, status_change_timer, bl->id, data ); + return 0; + } + break; + + case SC_KNOWLEDGE: + if (sd) { + if(bl->m != sd->feel_map[0].m + && bl->m != sd->feel_map[1].m + && bl->m != sd->feel_map[2].m) + break; //End it + } //Otherwise continue. + // Status changes that don't have a time limit + case SC_AETERNA: + case SC_TRICKDEAD: + case SC_MODECHANGE: + case SC_WEIGHT50: + case SC_WEIGHT90: + case SC_MAGICPOWER: + case SC_REJECTSWORD: + case SC_MEMORIZE: + case SC_BROKENWEAPON: + case SC_BROKENARMOR: + case SC_SACRIFICE: + case SC_READYSTORM: + case SC_READYDOWN: + case SC_READYTURN: + case SC_READYCOUNTER: + case SC_RUN: + case SC_DODGE: + case SC_AUTOBERSERK: //continues until triggered off manually. [Skotlex] + case SC_NEN: + case SC_SIGNUMCRUCIS: /* シグナムクルシス */ + sc->data[type].timer=add_timer( 1000*600+tick,status_change_timer, bl->id, data ); + return 0; + + case SC_DANCING: //ダンススキルの時間SP消費 + { + int s = 0; + int sp = 1; + int counter = sc->data[type].val3>>16; + if (--counter <= 0) + break; + sc->data[type].val3&= 0xFFFF; //Remove counter + sc->data[type].val3|=(counter<<16);//Reset it. + switch(sc->data[type].val1){ + case BD_RICHMANKIM: + case BD_DRUMBATTLEFIELD: + case BD_RINGNIBELUNGEN: + case BD_SIEGFRIED: + case BA_DISSONANCE: + case BA_ASSASSINCROSS: + case DC_UGLYDANCE: + s=3; + break; + case BD_LULLABY: + case BD_ETERNALCHAOS: + case BD_ROKISWEIL: + case DC_FORTUNEKISS: + s=4; + break; + case CG_HERMODE: + case BD_INTOABYSS: + case BA_WHISTLE: + case DC_HUMMING: + case BA_POEMBRAGI: + case DC_SERVICEFORYOU: + s=5; + break; + case BA_APPLEIDUN: + s=6; + break; + case CG_MOONLIT: + sp= 4*sc->data[SC_MOONLIT].val1; //Moonlit's cost is 4sp*skill_lv [Skotlex] + //Upkeep is also every 10 secs. + case DC_DONTFORGETME: + s=10; + break; + } + if (s && ((sc->data[type].val3 % s) == 0)) { + if (sc->data[SC_LONGING].timer != -1) + sp = s; + if (!status_charge(bl, 0, sp)) + break; + } + sc->data[type].timer=add_timer( + 1000+tick, status_change_timer, + bl->id, data); + return 0; + } + break; + + case SC_DEVOTION: + { //Check range and timeleft to preserve status [Skotlex] + //This implementation won't work for mobs because of map_id2sd, but it's a small cost in exchange of the speed of map_id2sd over map_id2sd + struct map_session_data *md = map_id2sd(sc->data[type].val1); + if (md && battle_check_range(bl, &md->bl, sc->data[type].val3) && (sc->data[type].val4-=1000)>0) + { + sc->data[type].timer = add_timer(1000+tick, status_change_timer, bl->id, data); + return 0; + } + } + break; + + case SC_BERSERK: + // 5% every 10 seconds [DracoRPG] + if((--sc->data[type].val3)>0 && status_charge(bl, sc->data[type].val2, 0)) + { + sc->data[type].timer = add_timer( + sc->data[type].val4+tick, status_change_timer, + bl->id, data); + return 0; + } + else if (sd) + sd->canregen_tick = gettick() + 300000; + break; + case SC_NOCHAT: + if(sd && battle_config.manner_system){ + sd->status.manner++; + clif_updatestatus(sd,SP_MANNER); + if (sd->status.manner < 0) + { //Every 60 seconds your manner goes up by 1 until it gets back to 0. + sc->data[type].timer=add_timer(60000+tick, status_change_timer, bl->id, data); + return 0; + } + } + break; + + case SC_SPLASHER: + if (sc->data[type].val4 % 1000 == 0) { + char timer[10]; + snprintf (timer, 10, "%d", sc->data[type].val4/1000); + clif_message(bl, timer); + } + if((sc->data[type].val4 -= 500) > 0) { + sc->data[type].timer = add_timer( + 500 + tick, status_change_timer, + bl->id, data); + return 0; + } + break; + + case SC_MARIONETTE: + case SC_MARIONETTE2: + { + struct block_list *pbl = map_id2bl(sc->data[type].val1); + if (pbl && battle_check_range(bl, pbl, 7) && (sc->data[type].val2--)>0) + { + sc->data[type].timer = add_timer( + 1000 + tick, status_change_timer, + bl->id, data); + return 0; + } + } + break; + + case SC_GOSPEL: + if(sc->data[type].val4 == BCT_SELF && (--sc->data[type].val2) > 0) + { + int hp, sp; + hp = (sc->data[type].val1 > 5) ? 45 : 30; + sp = (sc->data[type].val1 > 5) ? 35 : 20; + if(!status_charge(bl, hp, sp)) + break; + sc->data[type].timer = add_timer( + 10000+tick, status_change_timer, + bl->id, data); + return 0; + } + break; + + case SC_GUILDAURA: + { + struct block_list *tbl = map_id2bl(sc->data[type].val2); + + if (tbl && battle_check_range(bl, tbl, 2)){ + sc->data[type].timer = add_timer( + 1000 + tick, status_change_timer, + bl->id, data); + return 0; + } + } + break; + } + + // default for all non-handled control paths + // security system to prevent forgetting timer removal + + // if we reach this point we need the timer for the next call, + // so restore it to have status_change_end handle a valid timer + sc->data[type].timer = temp_timerid; + + return status_change_end( bl,type,tid ); +} + +/*========================================== + * ステータス異常タイマー範囲処理 + *------------------------------------------ + */ +int status_change_timer_sub(struct block_list *bl, va_list ap ) +{ + struct block_list *src; + struct status_change *sc, *tsc; + struct map_session_data* sd=NULL; + struct map_session_data* tsd=NULL; + + int type; + unsigned int tick; + + src=va_arg(ap,struct block_list*); + sc=va_arg(ap,struct status_change*); + type=va_arg(ap,int); + tick=va_arg(ap,unsigned int); + tsc=status_get_sc(bl); + + if (status_isdead(bl)) + return 0; + if (src->type==BL_PC) sd= (struct map_session_data*)src; + if (bl->type==BL_PC) tsd= (struct map_session_data*)bl; + + switch( type ){ + case SC_SIGHT: /* サイト */ + case SC_CONCENTRATE: + if (tsc && tsc->count) { + if (tsc->data[SC_HIDING].timer != -1) + status_change_end( bl, SC_HIDING, -1); + if (tsc->data[SC_CLOAKING].timer != -1) + status_change_end( bl, SC_CLOAKING, -1); + } + break; + case SC_RUWACH: /* ルアフ */ + if (tsc && tsc->count && (tsc->data[SC_HIDING].timer != -1 || // if the target is using a special hiding, i.e not using normal hiding/cloaking, don't bother + tsc->data[SC_CLOAKING].timer != -1)) { + status_change_end( bl, SC_HIDING, -1); + status_change_end( bl, SC_CLOAKING, -1); + if(battle_check_target( src, bl, BCT_ENEMY ) > 0) + skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0); + } + break; + case SC_SIGHTBLASTER: + { + if (sc && sc->count && sc->data[type].val2 > 0 && battle_check_target( src, bl, BCT_ENEMY ) > 0) + { //sc_ check prevents a single round of Sight Blaster hitting multiple opponents. [Skotlex] + skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0); + sc->data[type].val2 = 0; //This signals it to end. + } + } + break; + case SC_CLOSECONFINE: + //Lock char has released the hold on everyone... + if (tsc && tsc->count && tsc->data[SC_CLOSECONFINE2].timer != -1 && tsc->data[SC_CLOSECONFINE2].val2 == src->id) { + tsc->data[SC_CLOSECONFINE2].val2 = 0; + status_change_end(bl, SC_CLOSECONFINE2, -1); + } + break; + } + return 0; +} + +/*========================================== + * Clears buffs/debuffs of a character. + * type&1 -> buffs, type&2 -> debuffs + *------------------------------------------ + */ +int status_change_clear_buffs (struct block_list *bl, int type) +{ + int i; + struct status_change *sc= status_get_sc(bl); + + if (!sc || !sc->count) + return 0; + + if (type&2) //Debuffs + for (i = SC_COMMON_MIN; i <= SC_COMMON_MAX; i++) { + if(sc->data[i].timer != -1) + status_change_end(bl,i,-1); + } + + for (i = SC_COMMON_MAX+1; i < SC_MAX; i++) { + + if(sc->data[i].timer == -1) + continue; + + switch (i) { + //Stuff that cannot be removed + case SC_WEIGHT50: + case SC_WEIGHT90: + case SC_COMBO: + case SC_SMA: + case SC_DANCING: + case SC_GUILDAURA: + case SC_SAFETYWALL: + case SC_NOCHAT: + case SC_ANKLE: + case SC_BLADESTOP: + case SC_CP_WEAPON: + case SC_CP_SHIELD: + case SC_CP_ARMOR: + case SC_CP_HELM: + continue; + + //Debuffs that can be removed. + case SC_HALLUCINATION: + case SC_QUAGMIRE: + case SC_SIGNUMCRUCIS: + case SC_DECREASEAGI: + case SC_SLOWDOWN: + case SC_MINDBREAKER: + case SC_WINKCHARM: + case SC_STOP: + case SC_ORCISH: + case SC_STRIPWEAPON: + case SC_STRIPSHIELD: + case SC_STRIPARMOR: + case SC_STRIPHELM: + if (!(type&2)) + continue; + break; + //The rest are buffs that can be removed. + case SC_BERSERK: + if (!(type&1)) + continue; + sc->data[i].val4 = 1; + break; + default: + if (!(type&1)) + continue; + break; + } + status_change_end(bl,i,-1); + } + return 0; +} + +static int status_calc_sigma(void) +{ + int i,j; + unsigned int k; + + for(i=0;i= INT_MAX) + break; //Overflow protection. [Skotlex] + } + for(;j<=MAX_LEVEL;j++) + hp_sigma_val[i][j-1] = INT_MAX; + } + return 0; +} + +int status_readdb(void) { + int i,j; + FILE *fp; + char line[1024], path[1024],*p; + + sprintf(path, "%s/job_db1.txt", db_path); + fp=fopen(path,"r"); // Job-specific values (weight, HP, SP, ASPD) + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + i = 0; + while(fgets(line, sizeof(line)-1, fp)){ + char *split[MAX_WEAPON_TYPE + 5]; + i++; + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<(MAX_WEAPON_TYPE + 5) && p;j++){ //not 22 anymore [blackhole89] + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(j < MAX_WEAPON_TYPE + 5) + { //Weapon #.MAX_WEAPON_TYPE is constantly not load. Fix to that: replace < with <= [blackhole89] + ShowDebug("%s: Not enough columns at line %d\n", path, i); + continue; + } + if(atoi(split[0])>=MAX_PC_CLASS) + continue; + + max_weight_base[atoi(split[0])]=atoi(split[1]); + hp_coefficient[atoi(split[0])]=atoi(split[2]); + hp_coefficient2[atoi(split[0])]=atoi(split[3]); + sp_coefficient[atoi(split[0])]=atoi(split[4]); + for(j=0;j=MAX_PC_CLASS) + continue; + for(i=1;i MAX_STATUSCHANGE) + { + ShowDebug("status.h defines %d status changes, but the MAX_STATUSCHANGE is %d! Fix it.\n", SC_MAX, MAX_STATUSCHANGE); + exit(1); + } + add_timer_func_list(status_change_timer,"status_change_timer"); + add_timer_func_list(kaahi_heal_timer,"kaahi_heal_timer"); + initChangeTables(); + initDummyData(); + status_readdb(); + status_calc_sigma(); + return 0; +} diff --git a/src/map/status.h b/src/map/status.h index a046b7170..03126ba0c 100644 --- a/src/map/status.h +++ b/src/map/status.h @@ -6,6 +6,12 @@ #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. @@ -167,7 +173,7 @@ enum { SC_CLOSECONFINE, SC_CLOSECONFINE2, SC_DANCING, - SC_LULLABY, + SC_ELEMENTALCHANGE, SC_RICHMANKIM, SC_ETERNALCHAOS, SC_DRUMBATTLE, @@ -179,7 +185,7 @@ enum { SC_ASSNCROS, SC_POEMBRAGI, SC_APPLEIDUN, - SC_UGLYDANCE, + SC_MODECHANGE, SC_HUMMING, SC_DONTFORGETME, SC_FORTUNE, //180 @@ -241,7 +247,7 @@ enum { SC_SUITON, SC_NEN, SC_KNOWLEDGE, - SC_SMA, //Not combined with SC_COMBO because SMA has it's own visual effect. [Skotlex]. + SC_SMA, SC_MAX, //Automatically updated max, used in for's and at startup to check we are within bounds. [Skotlex] }; extern int SkillStatusChangeTable[MAX_SKILL]; @@ -382,11 +388,12 @@ enum { SI_ADJUSTMENT = 209, SI_ACCURACY = 210 }; -extern int StatusIconChangeTable[]; 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 @@ -460,43 +467,105 @@ enum { //TODO: Get these Missing options... #define OPTION_SIGHTTRASHER 0x0001 -// パラメータ所得系 battle.c より移動 +//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_PC 0x80000000 +#define SCB_ALL 0x7FFFFFFF + +int status_damage(struct block_list *src,struct block_list *target,unsigned int hp, unsigned 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_PC || status_damage(NULL, bl, hp, sp, 0, 3)) +int status_percent_change(struct block_list *src,struct block_list *target,char hp_rate, 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) +int status_heal(struct block_list *bl,unsigned int hp,unsigned int sp, int flag); + +//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 status_data *status_get_status_data(struct block_list *bl); +struct status_data *status_get_base_status(struct block_list *bl); int status_get_class(struct block_list *bl); int status_get_lv(struct block_list *bl); -int status_get_range(struct block_list *bl); -int status_get_hp(struct block_list *bl); -int status_get_max_hp(struct block_list *bl); -int status_get_str(struct block_list *bl); -int status_get_agi(struct block_list *bl); -int status_get_vit(struct block_list *bl); -int status_get_int(struct block_list *bl); -int status_get_dex(struct block_list *bl); -int status_get_luk(struct block_list *bl); -int status_get_hit(struct block_list *bl); -int status_get_flee(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 int status_get_def(struct block_list *bl); -int status_get_mdef(struct block_list *bl); -int status_get_flee2(struct block_list *bl); -int status_get_def2(struct block_list *bl); -int status_get_mdef2(struct block_list *bl); -int status_get_batk(struct block_list *bl); -int status_get_atk(struct block_list *bl); -int status_get_atk2(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 +int status_get_lwatk(struct block_list *bl); +int status_get_lwatk2(struct block_list *bl); int status_get_speed(struct block_list *bl); -int status_get_adelay(struct block_list *bl); -int status_get_amotion(struct block_list *bl); -int status_get_dmotion(struct block_list *bl); -int status_get_element(struct block_list *bl); -int status_get_attack_sc_element(struct block_list *bl); -int status_get_attack_element(struct block_list *bl); -int status_get_attack_element2(struct block_list *bl); //左手武器属性取得 -#define status_get_elem_type(bl) (status_get_element(bl)%10) -#define status_get_elem_level(bl) (status_get_element(bl)/10/2) +#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 +int 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_race(struct block_list *bl); -int status_get_size(struct block_list *bl); -int status_get_mode(struct block_list *bl); int status_get_mexp(struct block_list *bl); int status_get_race2(struct block_list *bl); @@ -505,13 +574,6 @@ 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_get_matk1(struct block_list *bl); -int status_get_matk2(struct block_list *bl); -int status_get_critical(struct block_list *bl); -int status_get_atk_(struct block_list *bl); -int status_get_atk_2(struct block_list *bl); -int status_get_atk2(struct block_list *bl); - int status_isdead(struct block_list *bl); int status_isimmune(struct block_list *bl); @@ -533,37 +595,16 @@ 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); -int status_calc_pet(struct map_session_data* sd, int first); // [Skotlex] +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_str(struct block_list *,int); -int status_calc_agi(struct block_list *,int); -int status_calc_vit(struct block_list *,int); -int status_calc_int(struct block_list *,int); -int status_calc_dex(struct block_list *,int); -int status_calc_luk(struct block_list *,int); -int status_calc_batk(struct block_list *,int); -int status_calc_watk(struct block_list *,int); -int status_calc_matk(struct block_list *,int); -int status_calc_hit(struct block_list *,int); -int status_calc_critical(struct block_list *,int); -int status_calc_flee(struct block_list *,int); -int status_calc_flee2(struct block_list *,int); -int status_calc_def(struct block_list *,int); -int status_calc_def2(struct block_list *,int); -int status_calc_mdef(struct block_list *,int); -int status_calc_mdef2(struct block_list *,int); -int status_calc_speed(struct block_list *,int); -int status_calc_aspd_rate(struct block_list *,int); -int status_calc_maxhp(struct block_list *,int); -int status_calc_maxsp(struct block_list *,int); -int status_quick_recalc_speed(struct map_session_data*, int, int, char); // [Celest] - modified by [Skotlex] +int status_calc_mob(struct mob_data* md, int first); //[Skotlex] +void status_calc_misc(struct status_data *status, int level); + +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] -//Use this to refer the max refinery level [Skotlex] -#define MAX_REFINE 10 -extern int percentrefinery[5][MAX_REFINE+1]; //The last slot always has a 0% success chance [Skotlex] - int status_readdb(void); int do_init_status(void); diff --git a/src/map/unit.c b/src/map/unit.c index 9fc3003a1..39b8c45dc 100644 --- a/src/map/unit.c +++ b/src/map/unit.c @@ -638,11 +638,13 @@ int unit_can_move(struct block_list *bl) sc->data[SC_AUTOCOUNTER].timer !=-1 || sc->data[SC_TRICKDEAD].timer !=-1 || sc->data[SC_BLADESTOP].timer !=-1 || + sc->data[SC_BLADESTOP_WAIT].timer !=-1 || sc->data[SC_SPIDERWEB].timer !=-1 || (sc->data[SC_DANCING].timer !=-1 && ( (sc->data[SC_DANCING].val4 && sc->data[SC_LONGING].timer == -1) || sc->data[SC_DANCING].val1 == CG_HERMODE //cannot move while Hermod is active. )) || + sc->data[SC_MOONLIT].timer != -1 || (sc->data[SC_GOSPEL].timer !=-1 && sc->data[SC_GOSPEL].val4 == BCT_SELF) || // cannot move while gospel is in effect sc->data[SC_STOP].timer != -1 || sc->data[SC_CLOSECONFINE].timer != -1 || @@ -688,6 +690,7 @@ int unit_set_walkdelay(struct block_list *bl, unsigned int tick, int delay, int int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int skill_lv, int casttime, int castcancel) { struct unit_data *ud; + struct status_data *tstatus; struct status_change *sc; struct map_session_data *sd = NULL; struct block_list * target = NULL; @@ -763,16 +766,17 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int if(!status_check_skilluse(src, target, skill_num, 0)) return 0; + tstatus = status_get_status_data(target); //直前のスキル状況の記録 if(sd) { switch(skill_num){ case SA_CASTCANCEL: - if(ud->skillid != skill_num){ //キャストキャンセル自体は覚えない + if(ud->skillid != skill_num){ sd->skillid_old = ud->skillid; sd->skilllv_old = ud->skilllv; break; } - case BD_ENCORE: /* アンコール */ + case BD_ENCORE: //Prevent using the dance skill if you no longer have the skill in your tree. if(!sd->skillid_dance || pc_checkskill(sd,sd->skillid_dance)<=0){ clif_skill_fail(sd,skill_num,0,0); @@ -780,15 +784,15 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int } sd->skillid_old = skill_num; break; - case BD_LULLABY: /* 子守歌 */ - case BD_RICHMANKIM: /* ニヨルドの宴 */ - case BD_ETERNALCHAOS: /* 永遠の?ャ沌 */ - case BD_DRUMBATTLEFIELD: /* ?太鼓の響き */ - case BD_RINGNIBELUNGEN: /* ニ?ベルングの指輪 */ - case BD_ROKISWEIL: /* ?キの叫び */ - case BD_INTOABYSS: /* ?[淵の中に */ - case BD_SIEGFRIED: /* 不死?gのジ?クフリ?ド */ - case CG_MOONLIT: /* 月明りの?に落ちる花びら */ + case BD_LULLABY: + case BD_RICHMANKIM: + case BD_ETERNALCHAOS: + case BD_DRUMBATTLEFIELD: + case BD_RINGNIBELUNGEN: + case BD_ROKISWEIL: + case BD_INTOABYSS: + case BD_SIEGFRIED: + case CG_MOONLIT: if (battle_config.player_skill_partner_check && (!battle_config.gm_skilluncond || pc_isGM(sd) < battle_config.gm_skilluncond) && (skill_check_pc_partner(sd, skill_num, &skill_lv, 1, 0) < 1) @@ -825,20 +829,19 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int //temp: Used to signal force cast now. temp = 0; - /* 何か特殊な処理が必要 */ - // 失敗判定はskill_check_condition() に書くこと + switch(skill_num){ - case ALL_RESURRECTION: /* リザレクション */ - if(battle_check_undead(status_get_race(target),status_get_elem_type(target))){ /* 敵がアンデッドなら */ - temp=1; /* ターンアンデットと同じ詠唱時間 */ + case ALL_RESURRECTION: + if(battle_check_undead(tstatus->race,tstatus->def_ele)){ + temp=1; casttime = skill_castfix(src, PR_TURNUNDEAD, skill_lv); } break; - case MO_FINGEROFFENSIVE: /* 指弾 */ + case MO_FINGEROFFENSIVE: if(sd) casttime += casttime * ((skill_lv > sd->spiritball)? sd->spiritball:skill_lv); break; - case MO_EXTREMITYFIST: /*阿?C羅覇鳳?*/ + case MO_EXTREMITYFIST: if (sc && sc->data[SC_COMBO].timer != -1 && (sc->data[SC_COMBO].val1 == MO_COMBOFINISH || sc->data[SC_COMBO].val1 == CH_TIGERFIST || @@ -854,37 +857,35 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int case SA_SPELLBREAKER: temp =1; break; - case KN_CHARGEATK: //チャージアタック + case KN_CHARGEATK: //Taken from jA: Casttime is increased by dist/3*100% casttime = casttime * ((distance_bl(src,target)-1)/3+1); break; } - //メモライズ状態ならキャストタイムが1/2 if (sc && sc->data[SC_MEMORIZE].timer != -1 && casttime > 0) { casttime = casttime/2; if ((--sc->data[SC_MEMORIZE].val2) <= 0) status_change_end(src, SC_MEMORIZE, -1); } - if( casttime>0 || temp){ /* 詠唱が必要 */ + if( casttime>0 || temp){ clif_skillcasting(src, src->id, target_id, 0,0, skill_num,casttime); - /* 詠唱反応モンスター */ if (sd && target->type == BL_MOB) { TBL_MOB *md = (TBL_MOB*)target; mobskill_event(md, src, tick, -1); //Cast targetted skill event. //temp: used to store mob's mode now. - if ((temp=status_get_mode(target))&MD_CASTSENSOR && + if (tstatus->mode&MD_CASTSENSOR && battle_check_target(target, src, BCT_ENEMY) > 0) { switch (md->state.skillstate) { case MSS_ANGRY: case MSS_RUSH: case MSS_FOLLOW: - if (!(temp&(MD_AGGRESSIVE|MD_ANGRY))) + if (!(tstatus->mode&(MD_AGGRESSIVE|MD_ANGRY))) break; //Only Aggressive mobs change target while chasing. case MSS_IDLE: case MSS_WALK: @@ -906,22 +907,19 @@ int unit_skilluse_id2(struct block_list *src, int target_id, int skill_num, int ud->skillid = skill_num; ud->skilllv = skill_lv; - if(sc && sc->data[SC_CLOAKING].timer != -1 && !sc->data[SC_CLOAKING].val4 && skill_num != AS_CLOAKING) + if(sc && sc->data[SC_CLOAKING].timer != -1 && + !(sc->data[SC_CLOAKING].val4&1) && skill_num != AS_CLOAKING) status_change_end(src,SC_CLOAKING,-1); if(casttime > 0) { ud->skilltimer = add_timer( tick+casttime, skill_castend_id, src->id, 0 ); - //temp: used to hold FreeCast's level - if(sd && (temp = pc_checkskill(sd,SA_FREECAST)) > 0) - status_quick_recalc_speed (sd, SA_FREECAST, temp, 1); + if(sd && pc_checkskill(sd,SA_FREECAST)) + status_freecast_switch(sd); else unit_stop_walking(src,1); } - else { -// if(skill_num != SA_CASTCANCEL) -// ud->skilltimer = -1; //This check is done above... + else skill_castend_id(ud->skilltimer,tick,src->id,0); - } return 1; } @@ -987,7 +985,6 @@ int unit_skilluse_pos2( struct block_list *src, int skill_x, int skill_y, int sk unit_stop_attack(src); ud->state.skillcastcancel = castcancel; - //?モライズ?態ならキャストタイムが1/3 if (sc && sc->data[SC_MEMORIZE].timer != -1 && casttime > 0){ casttime = casttime/3; if ((--sc->data[SC_MEMORIZE].val2)<=0) @@ -995,12 +992,11 @@ int unit_skilluse_pos2( struct block_list *src, int skill_x, int skill_y, int sk } if( casttime>0 ) { - /* 詠唱が必要 */ - unit_stop_walking( src, 1); // 歩行停止 + unit_stop_walking( src, 1); clif_skillcasting(src, src->id, 0, skill_x,skill_y, skill_num,casttime); } - if( casttime<=0 ) /* 詠唱の無いものはキャンセルされない */ + if( casttime<=0 ) ud->state.skillcastcancel=0; ud->canact_tick = tick + casttime + 100; @@ -1010,14 +1006,14 @@ int unit_skilluse_pos2( struct block_list *src, int skill_x, int skill_y, int sk ud->skilly = skill_y; ud->skilltarget = 0; - if (sc && sc->data[SC_CLOAKING].timer != -1 && !sc->data[SC_CLOAKING].val4) + if (sc && sc->data[SC_CLOAKING].timer != -1 && + !(sc->data[SC_CLOAKING].val4&1)) status_change_end(src,SC_CLOAKING,-1); if(casttime > 0) { ud->skilltimer = add_timer( tick+casttime, skill_castend_pos, src->id, 0 ); - //castcancel recylced to store FREECAST lv. - if(sd && (castcancel = pc_checkskill(sd,SA_FREECAST)) > 0) - status_quick_recalc_speed (sd, SA_FREECAST, castcancel, 1); + if(sd && pc_checkskill(sd,SA_FREECAST)) + status_freecast_switch(sd); else unit_stop_walking(src,1); } @@ -1030,7 +1026,6 @@ int unit_skilluse_pos2( struct block_list *src, int skill_x, int skill_y, int sk static int unit_attack_timer(int tid,unsigned int tick,int id,int data); -// 攻撃停止 int unit_stop_attack(struct block_list *bl) { struct unit_data *ud = unit_bl2ud(bl); @@ -1182,6 +1177,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t { struct block_list *target; struct unit_data *ud; + struct status_data *sstatus; struct map_session_data *sd = NULL; struct mob_data *md = NULL; int range; @@ -1207,6 +1203,8 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if(src->m != target->m || status_isdead(src) || status_isdead(target) || !status_check_skilluse(src, target, 0, 0)) return 0; + sstatus = status_get_status_data(src); + if(!battle_config.sdelay_attack_enable && DIFF_TICK(ud->canact_tick,tick) > 0 && (!sd || pc_checkskill(sd,SA_FREECAST) <= 0) @@ -1224,9 +1222,9 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t return 1; } - range = status_get_range(src); + range = sstatus->rhw.range; - if(!sd || sd->status.weapon != 11) range++; //Dunno why everyone but bows gets this extra range... + if(!sd || sd->status.weapon != W_BOW) range++; //Dunno why everyone but bows gets this extra range... if(unit_is_walking(target)) range++; //Extra range when chasing if(!check_distance_bl(src,target,range) ) { @@ -1259,7 +1257,7 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t if(md) { if (mobskill_use(md,tick,-1)) return 1; - if (status_get_mode(src)&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME) + if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME) { // Link monsters nearby [Skotlex] md->last_linktime = tick; map_foreachinrange(mob_linksearch, src, md->db->range2, @@ -1272,17 +1270,15 @@ static int unit_attack_timer_sub(struct block_list* src, int tid, unsigned int t map_freeblock_lock(); ud->attacktarget_lv = battle_weapon_attack(src,target,tick,0); - if(sd && sd->status.pet_id > 0 && sd->pd && sd->petDB && battle_config.pet_attack_support) + if(sd && sd->status.pet_id > 0 && sd->pd && battle_config.pet_attack_support) pet_target_check(sd,target,0); map_freeblock_unlock(); - if(ud->skilltimer != -1 && sd && (range = pc_checkskill(sd,SA_FREECAST)) > 0 ) // フリーキャスト - ud->attackabletime = tick + (status_get_adelay(src)*(150 - range*5)/100); - else - ud->attackabletime = tick + status_get_adelay(src); + +ud->attackabletime = tick + sstatus->adelay; // You can't move if you can't attack neither. - unit_set_walkdelay(src, tick, status_get_amotion(src), 1); + unit_set_walkdelay(src, tick, sstatus->amotion, 1); } if(ud->state.attack_continue) @@ -1329,8 +1325,8 @@ int unit_skillcastcancel(struct block_list *bl,int type) } ud->canact_tick=tick; - if(sd && (skill = pc_checkskill(sd,SA_FREECAST)) > 0) - status_quick_recalc_speed(sd, SA_FREECAST, skill, 0); //Updated to use calc_speed [Skotlex] + if(sd && pc_checkskill(sd,SA_FREECAST)) + status_freecast_switch(sd); if(type&1 && sd) skill = sd->skillid_old; @@ -1398,7 +1394,7 @@ int unit_fixdamage(struct block_list *src,struct block_list *target,unsigned int if(damage+damage2 <= 0) return 0; - return battle_damage(src,target,damage+damage2,clif_damage(target,target,tick,sdelay,ddelay,damage,div,type,damage2),0); + return status_fix_damage(src,target,damage+damage2,clif_damage(target,target,tick,sdelay,ddelay,damage,div,type,damage2)); } /*========================================== * 自分をロックしている対象の数を返す @@ -1412,18 +1408,6 @@ int unit_counttargeted(struct block_list *bl,int target_lv) bl->id, target_lv)); } -/*========================================== - * idを攻撃しているPCの攻撃を停止 - * clif_foreachclientのcallback関数 - *------------------------------------------ - */ -int unit_mobstopattacked(struct map_session_data *sd,va_list ap) -{ - int id=va_arg(ap,int); - if(sd->ud.target==id) - unit_stop_attack(&sd->bl); - return 0; -} /*========================================== * 見た目のサイズを変更する *------------------------------------------ @@ -1558,12 +1542,9 @@ int unit_remove_map(struct block_list *bl, int clrtype) { if (md->master_id) md->master_dist = 0; if (clrtype == 1) { //Death. md->last_deadtime=gettick(); -// Isn't this too much? Why not let the attack-timer fail when the mob is dead? [Skotlex] -// clif_foreachclient(unit_mobstopattacked,md->bl.id); if(md->deletetimer!=-1) delete_timer(md->deletetimer,mob_timer_delete); md->deletetimer=-1; - md->hp=0; if(pcdb_checkid(md->vd->class_)) //Player mobs are not removed automatically by the client. clif_clearchar_delay(gettick()+3000,bl,0); mob_deleteslave(md); @@ -1591,10 +1572,7 @@ int unit_remove_map(struct block_list *bl, int clrtype) { intif_delete_petdata(sd->status.pet_id); sd->status.pet_id = 0; sd->pd = NULL; - sd->petDB = NULL; pd->msd = NULL; - if(battle_config.pet_status_support) - status_calc_pc(sd,2); map_delblock(bl); unit_free(bl); map_freeblock_unlock(); @@ -1661,8 +1639,7 @@ int unit_free(struct block_list *bl) { } else if( bl->type == BL_PET ) { struct pet_data *pd = (struct pet_data*)bl; struct map_session_data *sd = pd->msd; - if(sd && sd->pet_hungry_timer != -1) - pet_hungry_timer_delete(sd); + pet_hungry_timer_delete(pd); if (pd->a_skill) { aFree(pd->a_skill); @@ -1700,11 +1677,6 @@ int unit_free(struct block_list *bl) { aFree (pd->loot); pd->loot = NULL; } - if (pd->status) - { - aFree(pd->status); - pd->status = NULL; - } if (sd) sd->pd = NULL; } else if(bl->type == BL_MOB) { struct mob_data *md = (struct mob_data*)bl; @@ -1728,6 +1700,10 @@ int unit_free(struct block_list *bl) { aFree(md->spawn); md->spawn = NULL; } + if(md->base_status) { + aFree(md->base_status); + md->base_status = NULL; + } if(mob_is_clone(md->class_)) mob_clone_delete(md->class_); } -- cgit v1.2.3-70-g09d2