diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/map/atcommand.c | 116 | ||||
-rw-r--r-- | src/map/battle.c | 8886 | ||||
-rw-r--r-- | src/map/battle.h | 15 | ||||
-rw-r--r-- | src/map/charcommand.c | 11 | ||||
-rw-r--r-- | src/map/clif.c | 169 | ||||
-rw-r--r-- | src/map/clif.h | 2 | ||||
-rw-r--r-- | src/map/guild.c | 2 | ||||
-rw-r--r-- | src/map/map.c | 7 | ||||
-rw-r--r-- | src/map/map.h | 115 | ||||
-rw-r--r-- | src/map/mob.c | 8196 | ||||
-rw-r--r-- | src/map/mob.h | 22 | ||||
-rw-r--r-- | src/map/npc.c | 4 | ||||
-rw-r--r-- | src/map/pc.c | 1113 | ||||
-rw-r--r-- | src/map/pc.h | 12 | ||||
-rw-r--r-- | src/map/pet.c | 425 | ||||
-rw-r--r-- | src/map/pet.h | 5 | ||||
-rw-r--r-- | src/map/script.c | 24112 | ||||
-rw-r--r-- | src/map/skill.c | 22080 | ||||
-rw-r--r-- | src/map/skill.h | 5 | ||||
-rw-r--r-- | src/map/status.c | 12297 | ||||
-rw-r--r-- | src/map/status.h | 179 | ||||
-rw-r--r-- | src/map/unit.c | 132 |
22 files changed, 38519 insertions, 39386 deletions
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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#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<<t_ele) ||
- sd->right_weapon.def_ratio_atk_race & (1<<t_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<<t_ele) ||
- sd->left_weapon.def_ratio_atk_race & (1<<t_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 && (
- (tmd && sd->right_weapon.ignore_def_mob & (is_boss(target)?2:1)) ||
- sd->right_weapon.ignore_def_ele & (1<<t_ele) ||
- sd->right_weapon.ignore_def_race & (1<<t_race) ||
- sd->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<<t_ele) ||
- sd->left_weapon.ignore_def_race & (1<<t_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;
- 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;i<sd->right_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;i<sd->left_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;i<tsd->add_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<<t_ele) ||
- sd->ignore_mdef_race & (1<<t_race) ||
- sd->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;i<sd->add_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;i<tsd->add_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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#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<<tstatus->def_ele) || + sd->right_weapon.def_ratio_atk_race & (1<<tstatus->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<<tstatus->def_ele) || + sd->left_weapon.def_ratio_atk_race & (1<<tstatus->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<<tstatus->def_ele) || + sd->right_weapon.ignore_def_race & (1<<tstatus->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<<tstatus->def_ele) || + sd->left_weapon.ignore_def_race & (1<<tstatus->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;i<sd->right_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;i<sd->left_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;i<tsd->add_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<<tstatus->def_ele) || + sd->ignore_mdef_race & (1<<tstatus->race) || + sd->ignore_mdef_race & (is_boss(target)?1<<RC_BOSS:1<<RC_NONBOSS) + )) + flag.imdef = 1; + } + + if(!flag.imdef){ + if(battle_config.magic_defense_type) + ad.damage = ad.damage - tstatus->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;i<sd->add_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;i<tsd->add_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 <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <math.h>
-#include <limits.h>
-
-#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<amount;i++){
- int j=0;
- do{
- x=rand()%(x1-x0+1)+x0;
- y=rand()%(y1-y0+1)+y0;
- } while (map_getcell(m,x,y,CELL_CHKNOPASS) && (++j)<max);
- if(j>=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;count<data.num;count++){
- md= mob_spawn_dataset(&data);
- mob_spawn(md);
-
- md->max_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_dist<md->db->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;i<retrycount;i++){ // Search of a movable place
- int r=rand();
- x=r%(d*2+1)-d;
- y=r/(d*2+1)%(d*2+1)-d;
- x+=md->bl.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;i<md->ud.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_range<md->db->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)<MIN_MOBTHINKTIME*10)
- return 0;
-
- md->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()%1000<MOB_LAZYMOVEPERC)
- mob_randomwalk(md,tick);
- else if(rand()%1000<MOB_LAZYSKILLPERC) //Chance to do a mob's idle skill.
- mobskill_use(md, tick, -1);
- // MOB which is not not the summons MOB but BOSS, either sometimes reboils.
- // People don't want this, it seems custom, noone can prove it....
-// else if( rand()%1000<MOB_LAZYWARPPERC
-// && (md->spawn && !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()%1000<MOB_LAZYWARPPERC
- && (md->spawn && !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;i<DAMAGELOG_SIZE;i++){
- if(md->dmglog[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].dmg<mindmg){
- minpos=i;
- mindmg=md->dmglog[i].dmg;
- }
- }
- if(i<DAMAGELOG_SIZE)
- md->dmglog[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;i<DAMAGELOG_SIZE;i++){
- if(md->dmglog[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_damage<md->dmglog[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;i<DAMAGELOG_SIZE;i++){
- int pid,flag=1,zeny=0;
- unsigned int base_exp,job_exp;
- double per;
- struct party *p;
- if(tmpsd[i]==NULL || tmpsd[i]->bl.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;j<pnum;j++) // 公平パーティリストにいるかどうか
- if(pt[j].id==pid)
- break;
- if(j==pnum){ // いないときは公平かどうか確認
- if((p=party_search(pid))!=NULL && p->exp!=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;i<pnum;i++)
- party_exp_share(pt[i].p,md->bl.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<<race) ||
- sd->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;i<md->lootitem_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]
-
- // <Agit> 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;i<MAX_MOBSKILL;i++)
- md->skilldelay[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(;k<amount;k++) {
- short x,y;
- data.class_ = value[k%count]; //Summon slaves in round-robin fashion. [Skotlex]
- if (mobdb_checkid(data.class_) == 0)
- continue;
-
- if (map_search_freecell(&md2->bl, 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;i<max;i++){
- if(ms[i].skill_id == skillid)
- return i;
- }
- return -1;
-
-}
-
-/*==========================================
- * Friendly Mob whose HP is decreasing by a nearby MOB is looked for.
- *------------------------------------------
- */
-int mob_getfriendhprate_sub(struct block_list *bl,va_list ap)
-{
- int min_rate, max_rate,rate;
- struct block_list **fr;
- struct mob_data *md;
-
- md = va_arg(ap,struct mob_data *);
- min_rate=va_arg(ap,int);
- max_rate=va_arg(ap,int);
- fr=va_arg(ap,struct block_list **);
-
- if( md->bl.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; class_++){
- if(mob_db_data[class_]==NULL)
- break;
- }
-
- if(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:((rate<rate_min)?rate_min:rate);
-}
-/*==========================================
- * mob_db.txt reading
- *------------------------------------------
- */
-static int mob_readdb(void)
-{
- FILE *fp;
- char line[1024];
- char *filename[]={ "mob_db.txt","mob_db2.txt" };
- int class_, i, fi, k;
-
- for(fi=0;fi<2;fi++){
- sprintf(line, "%s/%s", db_path, filename[fi]);
- fp=fopen(line,"r");
- if(fp==NULL){
- if(fi>0)
- 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_]->range3<mob_db_data[class_]->range2)
- 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;i<MAX_MOB_DROP;i++){
- 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=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;i<MAX_RANDOMMONSTER;i++){
- mob_db_data[0]->summonper[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;i<MAX_MOBSKILL;i++)
- if( (ms=&mob_db_data[mob_id]->skill[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;j<tmp && strcmp(sp[2],state[j].str);j++);
- if (j < tmp)
- ms->state=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;j<sizeof(target)/sizeof(target[0]);j++){
- if( strcmp(sp[9],target[j].str)==0)
- ms->target=target[j].id;
- }
- ms->cond1=-1;
- tmp = sizeof(cond1)/sizeof(cond1[0]);
- for(j=0;j<tmp && strcmp(sp[10],cond1[j].str);j++);
- if (j < tmp)
- ms->cond1=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;j<tmp && strcmp(sp[11],cond2[j].str);j++);
- if (j < tmp)
- ms->cond2=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;j<MAX_MOBSKILL;j++)
- if( mob_db_data[i]->skill[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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <limits.h> + +#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<amount;i++){ + int j=0; + do{ + x=rand()%(x1-x0+1)+x0; + y=rand()%(y1-y0+1)+y0; + } while (map_getcell(m,x,y,CELL_CHKNOPASS) && (++j)<max); + if(j>=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;count<data.num;count++){ + md= mob_spawn_dataset(&data); + 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; + 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_dist<md->db->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;i<retrycount;i++){ // Search of a movable place + int r=rand(); + x=r%(d*2+1)-d; + y=r/(d*2+1)%(d*2+1)-d; + x+=md->bl.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;i<md->ud.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_range<md->status.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)<MIN_MOBTHINKTIME*10) + return 0; + + md->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()%1000<MOB_LAZYMOVEPERC) + mob_randomwalk(md,tick); + else if(rand()%1000<MOB_LAZYSKILLPERC) //Chance to do a mob's idle skill. + mobskill_use(md, tick, -1); + // MOB which is not not the summons MOB but BOSS, either sometimes reboils. + // People don't want this, it seems custom, noone can prove it.... +// else if( rand()%1000<MOB_LAZYWARPPERC +// && (md->spawn && !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()%1000<MOB_LAZYWARPPERC + && (md->spawn && !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;i<DAMAGELOG_SIZE;i++){ + if(md->dmglog[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].dmg<mindmg){ + minpos=i; + mindmg=md->dmglog[i].dmg; + } + } + if(i<DAMAGELOG_SIZE) + md->dmglog[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;i<DAMAGELOG_SIZE && md->dmglog[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_damage<md->dmglog[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;i<DAMAGELOG_SIZE && tmpsd[i];i++){ + int flag=1,zeny=0; + unsigned int base_exp,job_exp; + double per; //Your share of the mob's exp + int bonus; //Bonus on top of your share. + + if (battle_config.exp_calc_type) // eAthena's exp formula based on max hp. + per = (double)md->dmglog[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;j<pnum && pt[j].id!=temp;j++); //Locate party. + + if(j==pnum){ //Possibly add party. + pt[pnum].p = party_search(temp); + if(pt[pnum].p && pt[pnum].p->exp) + { + 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;i<pnum;i++) //Party share. + party_exp_share(pt[i].p,md->bl.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<<status->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;i<md->lootitem_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;i<md->lootitem_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); + } + + // <Agit> 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;i<MAX_MOBSKILL;i++) + md->skilldelay[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(;k<amount;k++) { + short x,y; + data.class_ = value[k%count]; //Summon slaves in round-robin fashion. [Skotlex] + if (mobdb_checkid(data.class_) == 0) + continue; + + if (map_search_freecell(&md2->bl, 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;i<max;i++){ + if(ms[i].skill_id == skillid) + return i; + } + return -1; + +} + +/*========================================== + * Friendly Mob whose HP is decreasing by a nearby MOB is looked for. + *------------------------------------------ + */ +int mob_getfriendhprate_sub(struct block_list *bl,va_list ap) +{ + int min_rate, max_rate,rate; + struct block_list **fr; + struct mob_data *md; + + md = va_arg(ap,struct mob_data *); + min_rate=va_arg(ap,int); + max_rate=va_arg(ap,int); + fr=va_arg(ap,struct block_list **); + + if( md->bl.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; class_++){ + if(mob_db_data[class_]==NULL) + break; + } + + if(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:((rate<rate_min)?rate_min:rate); +} +/*========================================== + * mob_db.txt reading + *------------------------------------------ + */ +static int mob_readdb(void) +{ + FILE *fp; + char line[1024]; + char *filename[]={ "mob_db.txt","mob_db2.txt" }; + struct status_data *status; + int class_, i, fi, k; + + for(fi=0;fi<2;fi++){ + sprintf(line, "%s/%s", db_path, filename[fi]); + fp=fopen(line,"r"); + if(fp==NULL){ + if(fi>0) + 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_]->range3<mob_db_data[class_]->range2) + 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;i<MAX_MOB_DROP;i++){ + 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=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;i<MAX_RANDOMMONSTER;i++){ + mob_db_data[0]->summonper[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;i<MAX_MOBSKILL;i++) + if( (ms=&mob_db_data[mob_id]->skill[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;j<tmp && strcmp(sp[2],state[j].str);j++); + if (j < tmp) + ms->state=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;j<sizeof(target)/sizeof(target[0]);j++){ + if( strcmp(sp[9],target[j].str)==0) + ms->target=target[j].id; + } + ms->cond1=-1; + tmp = sizeof(cond1)/sizeof(cond1[0]); + for(j=0;j<tmp && strcmp(sp[10],cond1[j].str);j++); + if (j < tmp) + ms->cond1=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;j<tmp && strcmp(sp[11],cond2[j].str);j++); + if (j < tmp) + ms->cond2=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;j<MAX_MOBSKILL;j++) + if( mob_db_data[i]->skill[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; i<MAX_MOB_DROP; i++)//Pick one mobs drop slot.
{
@@ -2943,7 +2918,7 @@ int pc_steal_item(struct map_session_data *sd,struct block_list *bl) if (i == MAX_MOB_DROP)
return 0;
- md->state.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) //<Skotlex> 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) //<Skotlex> 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.hp<sd->status.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.hp<sd->battle_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;i<MAX_INVENTORY;i++){
@@ -4600,10 +4562,12 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) pc_dropitem(sd,n,1);
}
}
- }*/
+ }
+ */
if(battle_config.bone_drop==2
- || (battle_config.bone_drop==1 && map[sd->bl.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;j<MAX_DROP_PER_MAP;j++){
int id = map[sd->bl.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;i<MAX_INVENTORY;i++){
int k;
if( (type == 1 && !sd->status.inventory[i].equip)
|| (type == 2 && sd->status.inventory[i].equip)
|| type == 3){
- //InventoryIndexを格納
for(k=0;k<MAX_INVENTORY;k++){
if(eq_n[k] <= 0){
eq_n[k]=i;
@@ -4730,7 +4661,7 @@ int pc_damage(struct block_list *src,struct map_session_data *sd,int damage) }
}
if(eq_num > 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;i<MAX_INVENTORY;i++){
- if(sd->status.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;i<pd->ud.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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-
-#ifndef _WIN32
-#include <sys/time.h>
-#endif
-
-#include <time.h>
-
-#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); // <Agit>
-int buildin_agitend(struct script_state *st);
-int buildin_agitcheck(struct script_state *st); // <Agitcheck>
-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",""}, // <Agit>
- {buildin_agitend,"agitend",""},
- {buildin_agitcheck,"agitcheck","i"}, // <Agitcheck>
- {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<lineend){
- fprintf(stderr, "\r"); //To not printout the error next to the spinner...
- ShowError(" "); //Better error display [Skotlex]
- if (current_file) {
- printf("%s in "CL_WHITE"\'%s\'"CL_RESET" line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, current_file, line);
- } else {
- printf("%s line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, line);
- }
- for(i=0;(linestart[i]!='\r') && (linestart[i]!='\n') && linestart[i];i++){
- if(linestart+i!=pos)
- printf("%c",linestart[i]);
- else
- printf("\'%c\'",linestart[i]);
- }
- printf("\a\n");
- if(lineend)
- *lineend=c;
- return;
- }
- *lineend=c;
- p=lineend+1;
- }
-}
-
-/*==========================================
- * 項の解析
- *------------------------------------------
- */
-unsigned char* parse_simpleexpr(unsigned char *p)
-{
- int i;
- p=skip_space(p);
-
-#ifdef DEBUG_FUNCIN
- if(battle_config.etc_log)
- ShowDebug("parse_simpleexpr %s\n",p);
-#endif
- if(*p==';' || *p==','){
- disp_error_message("unexpected expr end",p);
- exit(1);
- }
- if(*p=='('){
-
- p=parse_subexpr(p+1,-1);
- p=skip_space(p);
- if((*p++)!=')'){
- disp_error_message("unmatch ')'",p);
- exit(1);
- }
- } else if(isdigit(*p) || ((*p=='-' || *p=='+') && isdigit(p[1]))){
- char *np;
- i=strtoul((char *) p,&np,0);
- add_scripti(i);
- p=(unsigned char *) np;
- } else if(*p=='"'){
- add_scriptc(C_STR);
- p++;
- while(*p && *p!='"'){
- if(p[-1]<=0x7e && *p=='\\')
- p++;
- else if(*p=='\n'){
- disp_error_message("unexpected newline @ string",p);
- exit(1);
- }
- add_scriptb(*p++);
- }
- if(!*p){
- disp_error_message("unexpected eof @ string",p);
- exit(1);
- }
- add_scriptb(0);
- p++; //'"'
- } else {
- int c,l;
- char *p2;
- // label , register , function etc
- if(skip_word(p)==p && !(*p==')' && p[-1]=='(')){
- disp_error_message("unexpected character",p);
- exit(1);
- }
-
- p2=(char *) skip_word(p);
- c=*p2; *p2=0; // 名前をadd_strする
- l=add_str(p);
-
- parse_cmd=l; // warn_*_mismatch_paramnumのために必要
-
- *p2=c;
- p=(unsigned char *) p2;
-
- if(str_data[l].type!=C_FUNC && c=='['){
- // array(name[i] => 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<j)?i:j]));
- }
- }
- } else {
- p=parse_subexpr(p,opl);
- }
- add_scriptc(op);
- p=skip_space(p);
- }
-#ifdef DEBUG_FUNCIN
- if(battle_config.etc_log)
- ShowDebug("parse_subexpr end %s\n",p);
-#endif
- return p; /* return first untreated operator */
-}
-
-/*==========================================
- * 式の評価
- *------------------------------------------
- */
-unsigned char* parse_expr(unsigned char *p)
-{
-#ifdef DEBUG_FUNCIN
- if(battle_config.etc_log)
- ShowDebug("parse_expr %s\n",p);
-#endif
- switch(*p){
- case ')': case ';': case ':': case '[': case ']':
- case '}':
- disp_error_message("unexpected char",p);
- exit(1);
- }
- p=parse_subexpr(p,-1);
-#ifdef DEBUG_FUNCIN
- if(battle_config.etc_log)
- ShowDebug("parse_expr end %s\n",p);
-#endif
- return p;
-}
-
-/*==========================================
- * 行の解析
- *------------------------------------------
- */
-unsigned char* parse_line(unsigned char *p)
-{
- int i=0,cmd;
- const char *plist[128];
- unsigned char *p2;
- char end;
-
- p=skip_space(p);
- if(*p==';')
- return p + 1;
-
- p = skip_space(p);
- if(p[0] == '{') {
- syntax.curly[syntax.curly_count].type = TYPE_NULL;
- syntax.curly[syntax.curly_count].count = -1;
- syntax.curly[syntax.curly_count].index = -1;
- syntax.curly_count++;
- return p + 1;
- } else if(p[0] == '}') {
- return parse_curly_close(p);
- }
-
- // 構文関連の処理
- p2 = parse_syntax(p);
- if(p2 != NULL) { return p2; }
-
- // 最初は関数名
- p2=(char *) p;
- p=parse_simpleexpr(p);
- p=skip_space(p);
-
- 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 *)p2
- );
-// exit(0);
- }
- cmd=parse_cmd;
-
- if(parse_syntax_for_flag) {
- end = ')';
- } else {
- end = ';';
- }
- while(p && *p && *p!=end && i<128){
- plist[i]=(char *) p;
-
- p=parse_expr(p);
- p=skip_space(p);
- // 引数区切りの,処理
- if(*p==',') p++;
- else if(*p!=end && script_config.warn_cmd_no_comma && 0 <= i ){
- if(parse_syntax_for_flag) {
- disp_error_message("expect ',' or ')' at cmd params",p);
- } else {
- disp_error_message("expect ',' or ';' at cmd params",p);
- }
- }
- p=skip_space(p);
- i++;
- }
- plist[i]=(char *) p;
- if(!p || *(p++)!=end){
- if(parse_syntax_for_flag) {
- disp_error_message("need ')'",p);
- } else {
- disp_error_message("need ';'",p);
- }
- exit(1);
- }
- add_scriptc(C_FUNC);
-
- // if, for , while の閉じ判定
- p = parse_syntax_close(p);
-
- if( str_data[cmd].type==C_FUNC && script_config.warn_cmd_mismatch_paramnum){
- const char *arg=buildin_func[str_data[cmd].val].arg;
- int j=0;
- for(j=0;arg[j];j++) if(arg[j]=='*')break;
- if( (arg[j]==0 && i!=j) || (arg[j]=='*' && i<j) ){
- disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i<j)?i:j]));
- }
- }
-
-
- return p;
-}
-
-
-// { ... } の閉じ処理
-unsigned char* parse_curly_close(unsigned char *p) {
- if(syntax.curly_count <= 0) {
- disp_error_message("unexpected string",p);
- return p + 1;
- } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) {
- syntax.curly_count--;
- // if, for , while の閉じ判定
- p = parse_syntax_close(p + 1);
- return p;
- } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) {
- // switch() 閉じ判定
- int pos = syntax.curly_count-1;
- unsigned char label[256];
- int l;
- // 一時変数を消す
- 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--;
-
- // 無条件で終了ポインタに移動
- sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index);
- 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);
-
- if(syntax.curly[pos].flag) {
- // default が存在する
- sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index);
- syntax.curly[syntax.curly_count++].type = TYPE_NULL;
- parse_line(label);
- syntax.curly_count--;
- }
-
- // 終了ラベルを付ける
- sprintf(label,"__SW%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 {
- disp_error_message("unexpected string",p);
- return p + 1;
- }
-}
-
-// 構文関連の処理
-// break, case, continue, default, do, for, function,
-// if, switch, while をこの内部で処理します。
-unsigned char* parse_syntax(unsigned char *p) {
- switch(p[0]) {
- case 'b':
- if(!strncmp(p,"break",5) && !isalpha(*(p + 5))) {
- // break の処理
- char label[256];
- int pos = syntax.curly_count - 1;
- while(pos >= 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;i<end;i++){
- if(stack->stack_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;i<st->end;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;i<st->end;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;i<st->end;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;i<st->end;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; i<users; i++) {
- sd = pl_allsd[i];
- if(sd->status.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; i<st->end && 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;i<sz;i++)
- set_reg(st,sd,num+(i<<24),name,v,st->stack->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;i<sz;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
- );
- }
- 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;i<sz;i++){
- set_reg(
- st,sd,num+(i<<24),name,
- get_val2(st,num+((i+count)<<24),st->stack->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;i<MAX_INVENTORY;i++){
- if(sd->status.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;i<MAX_INVENTORY;i++){
- if(sd->status.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;i<MAX_INVENTORY;i++){
- //we don't delete wrong item or equipped item
- if(sd->status.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;i<MAX_INVENTORY;i++){
- //we don't delete wrong item
- if(sd->status.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;i<MAX_INVENTORY;i++){
- //we don't delete wrong item or equipped item
- if(sd->status.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;i<MAX_PARTY;i++){
- if(p->member[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; i<MAX_INVENTORY; i++) {
- if(sd->status.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; i<MAX_INVENTORY; i++) {
- if(sd->status.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;i<level;i++)
- guild_skillup(sd,id,flag);
-
- return 0;
-}
-/*==========================================
- * スキルレベル所得
- *------------------------------------------
- */
-int buildin_getskilllv(struct script_state *st)
-{
- int id=conv_num(st,& (st->stack->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;i<users;i++)
- {
- pl_sd = pl_allsd[i];
- if( !(battle_config.hide_GM_session && pc_isGM(pl_sd)) )
- {
- if((disp_num++)%10==0)
- clif_scriptnext(script_rid2sd(st),st->oid);
- 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, <id>;
- *------------------------------------------
- */
-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;i<n;i++){
- struct map_session_data *sd=cd->usersd[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;i<users;i++)
- {
- if ((pl_sd = pl_allsd[i]) && m == pl_sd->bl.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;i<users;i++)
- {
- if((pl_sd=pl_allsd[i]) && m == pl_sd->bl.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#, <target: 0 - NPC, 1 - PC>
- *------------------------------------------
- */
-//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;i<MAX_GUILDCASTLE;i++){
- if( (gc=guild_castle_search(i)) != NULL ){
- if(strcmp(mapname,gc->map_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;i<MAX_GUILDCASTLE;i++)
- if( (gc=guild_castle_search(i)) != NULL &&
- strcmp(mapname,gc->map_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;i<MAX_GUILDCASTLE;i++){
- if( (gc=guild_castle_search(i)) != NULL ){
- if(strcmp(mapname,gc->map_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;i<MAX_GUILDCASTLE;i++){
- if( (gc=guild_castle_search(i)) != NULL ){
- if(strcmp(mapname,gc->map_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;i<MAX_INVENTORY;i++){
- if(sd->status.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;i<MAX_SKILL;i++){
- if(sd->status.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; i<MAX_INVENTORY; i++) {
- if (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;
-}
-
-/*==========================================
- 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;i<MAX_INVENTORY;i++){
- if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount){
- for(n=0;n<MAX_SLOTS;n++){
- if(sd->status.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;i<st->end;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;i<st->end;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;i<c && bl;i++,bl=bl->next){
- 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; i<MAX_INVENTORY; i++) {
- if(sd->status.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; k<sd->inventory_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; k<sd->inventory_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; k<sd->inventory_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;i<MAX_INVENTORY;i++){
- if(sd->status.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)pos<strlen(str) ) ? isalpha( str[pos] ) : 0;
-
- push_val(st->stack,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 <mili sec>
-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 <mili sec>
-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(<param>, <npc name>);
-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)++]<<j;
- j+=6;
- }
- return i+(script[(*pos)++]<<j);
-}
-
-/*==========================================
- * コマンドのプッシュバック
- *------------------------------------------
- */
-void unget_com(int c)
-{
- if(unget_com_data!=-1){
- if(battle_config.error_log)
- ShowError("unget_com can back only 1 data\n");
- }
- unget_com_data=c;
-}
-
-/*==========================================
- * 数値の所得
- *------------------------------------------
- */
-int get_num(unsigned char *script,int *pos)
-{
- int i,j;
- i=0; j=0;
- while(script[*pos]>=0xc0){
- i+=(script[(*pos)++]&0x7f)<<j;
- j+=6;
- }
- return i+((script[(*pos)++]&0x7f)<<j);
-}
-
-/*==========================================
- * スタックから値を取り出す
- *------------------------------------------
- */
-int pop_val(struct script_state* st)
-{
- if(st->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_LE:
- i1=i1<=i2;
- break;
- case C_R_SHIFT:
- i1=i1>>i2;
- break;
- case C_L_SHIFT:
- i1=i1<<i2;
- break;
- }
- push_val(st->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;i<end_sp;i++){
- switch(st->stack->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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> + +#ifndef _WIN32 +#include <sys/time.h> +#endif + +#include <time.h> + +#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); // <Agit> +int buildin_agitend(struct script_state *st); +int buildin_agitcheck(struct script_state *st); // <Agitcheck> +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",""}, // <Agit> + {buildin_agitend,"agitend",""}, + {buildin_agitcheck,"agitcheck","i"}, // <Agitcheck> + {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<lineend){ + fprintf(stderr, "\r"); //To not printout the error next to the spinner... + ShowError(" "); //Better error display [Skotlex] + if (current_file) { + printf("%s in "CL_WHITE"\'%s\'"CL_RESET" line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, current_file, line); + } else { + printf("%s line "CL_WHITE"\'%d\'"CL_RESET" : ", mes, line); + } + for(i=0;(linestart[i]!='\r') && (linestart[i]!='\n') && linestart[i];i++){ + if(linestart+i!=pos) + printf("%c",linestart[i]); + else + printf("\'%c\'",linestart[i]); + } + printf("\a\n"); + if(lineend) + *lineend=c; + return; + } + *lineend=c; + p=lineend+1; + } +} + +/*========================================== + * 項の解析 + *------------------------------------------ + */ +unsigned char* parse_simpleexpr(unsigned char *p) +{ + int i; + p=skip_space(p); + +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + ShowDebug("parse_simpleexpr %s\n",p); +#endif + if(*p==';' || *p==','){ + disp_error_message("unexpected expr end",p); + exit(1); + } + if(*p=='('){ + + p=parse_subexpr(p+1,-1); + p=skip_space(p); + if((*p++)!=')'){ + disp_error_message("unmatch ')'",p); + exit(1); + } + } else if(isdigit(*p) || ((*p=='-' || *p=='+') && isdigit(p[1]))){ + char *np; + i=strtoul((char *) p,&np,0); + add_scripti(i); + p=(unsigned char *) np; + } else if(*p=='"'){ + add_scriptc(C_STR); + p++; + while(*p && *p!='"'){ + if(p[-1]<=0x7e && *p=='\\') + p++; + else if(*p=='\n'){ + disp_error_message("unexpected newline @ string",p); + exit(1); + } + add_scriptb(*p++); + } + if(!*p){ + disp_error_message("unexpected eof @ string",p); + exit(1); + } + add_scriptb(0); + p++; //'"' + } else { + int c,l; + char *p2; + // label , register , function etc + if(skip_word(p)==p && !(*p==')' && p[-1]=='(')){ + disp_error_message("unexpected character",p); + exit(1); + } + + p2=(char *) skip_word(p); + c=*p2; *p2=0; // 名前をadd_strする + l=add_str(p); + + parse_cmd=l; // warn_*_mismatch_paramnumのために必要 + + *p2=c; + p=(unsigned char *) p2; + + if(str_data[l].type!=C_FUNC && c=='['){ + // array(name[i] => 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<j)?i:j])); + } + } + } else { + p=parse_subexpr(p,opl); + } + add_scriptc(op); + p=skip_space(p); + } +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + ShowDebug("parse_subexpr end %s\n",p); +#endif + return p; /* return first untreated operator */ +} + +/*========================================== + * 式の評価 + *------------------------------------------ + */ +unsigned char* parse_expr(unsigned char *p) +{ +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + ShowDebug("parse_expr %s\n",p); +#endif + switch(*p){ + case ')': case ';': case ':': case '[': case ']': + case '}': + disp_error_message("unexpected char",p); + exit(1); + } + p=parse_subexpr(p,-1); +#ifdef DEBUG_FUNCIN + if(battle_config.etc_log) + ShowDebug("parse_expr end %s\n",p); +#endif + return p; +} + +/*========================================== + * 行の解析 + *------------------------------------------ + */ +unsigned char* parse_line(unsigned char *p) +{ + int i=0,cmd; + const char *plist[128]; + unsigned char *p2; + char end; + + p=skip_space(p); + if(*p==';') + return p + 1; + + p = skip_space(p); + if(p[0] == '{') { + syntax.curly[syntax.curly_count].type = TYPE_NULL; + syntax.curly[syntax.curly_count].count = -1; + syntax.curly[syntax.curly_count].index = -1; + syntax.curly_count++; + return p + 1; + } else if(p[0] == '}') { + return parse_curly_close(p); + } + + // 構文関連の処理 + p2 = parse_syntax(p); + if(p2 != NULL) { return p2; } + + // 最初は関数名 + p2=(char *) p; + p=parse_simpleexpr(p); + p=skip_space(p); + + 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 *)p2 + ); +// exit(0); + } + cmd=parse_cmd; + + if(parse_syntax_for_flag) { + end = ')'; + } else { + end = ';'; + } + while(p && *p && *p!=end && i<128){ + plist[i]=(char *) p; + + p=parse_expr(p); + p=skip_space(p); + // 引数区切りの,処理 + if(*p==',') p++; + else if(*p!=end && script_config.warn_cmd_no_comma && 0 <= i ){ + if(parse_syntax_for_flag) { + disp_error_message("expect ',' or ')' at cmd params",p); + } else { + disp_error_message("expect ',' or ';' at cmd params",p); + } + } + p=skip_space(p); + i++; + } + plist[i]=(char *) p; + if(!p || *(p++)!=end){ + if(parse_syntax_for_flag) { + disp_error_message("need ')'",p); + } else { + disp_error_message("need ';'",p); + } + exit(1); + } + add_scriptc(C_FUNC); + + // if, for , while の閉じ判定 + p = parse_syntax_close(p); + + if( str_data[cmd].type==C_FUNC && script_config.warn_cmd_mismatch_paramnum){ + const char *arg=buildin_func[str_data[cmd].val].arg; + int j=0; + for(j=0;arg[j];j++) if(arg[j]=='*')break; + if( (arg[j]==0 && i!=j) || (arg[j]=='*' && i<j) ){ + disp_error_message("illegal number of parameters",(unsigned char *) (plist[(i<j)?i:j])); + } + } + + + return p; +} + + +// { ... } の閉じ処理 +unsigned char* parse_curly_close(unsigned char *p) { + if(syntax.curly_count <= 0) { + disp_error_message("unexpected string",p); + return p + 1; + } else if(syntax.curly[syntax.curly_count-1].type == TYPE_NULL) { + syntax.curly_count--; + // if, for , while の閉じ判定 + p = parse_syntax_close(p + 1); + return p; + } else if(syntax.curly[syntax.curly_count-1].type == TYPE_SWITCH) { + // switch() 閉じ判定 + int pos = syntax.curly_count-1; + unsigned char label[256]; + int l; + // 一時変数を消す + 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--; + + // 無条件で終了ポインタに移動 + sprintf(label,"goto __SW%x_FIN;",syntax.curly[pos].index); + 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); + + if(syntax.curly[pos].flag) { + // default が存在する + sprintf(label,"goto __SW%x_DEF;",syntax.curly[pos].index); + syntax.curly[syntax.curly_count++].type = TYPE_NULL; + parse_line(label); + syntax.curly_count--; + } + + // 終了ラベルを付ける + sprintf(label,"__SW%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 { + disp_error_message("unexpected string",p); + return p + 1; + } +} + +// 構文関連の処理 +// break, case, continue, default, do, for, function, +// if, switch, while をこの内部で処理します。 +unsigned char* parse_syntax(unsigned char *p) { + switch(p[0]) { + case 'b': + if(!strncmp(p,"break",5) && !isalpha(*(p + 5))) { + // break の処理 + char label[256]; + int pos = syntax.curly_count - 1; + while(pos >= 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;i<end;i++){ + if(stack->stack_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;i<st->end;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;i<st->end;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;i<st->end;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;i<st->end;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; i<users; i++) { + sd = pl_allsd[i]; + if(sd->status.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; i<st->end && 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;i<sz;i++) + set_reg(st,sd,num+(i<<24),name,v,st->stack->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;i<sz;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 + ); + } + 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;i<sz;i++){ + set_reg( + st,sd,num+(i<<24),name, + get_val2(st,num+((i+count)<<24),st->stack->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;i<MAX_INVENTORY;i++){ + if(sd->status.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;i<MAX_INVENTORY;i++){ + if(sd->status.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;i<MAX_INVENTORY;i++){ + //we don't delete wrong item or equipped item + if(sd->status.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;i<MAX_INVENTORY;i++){ + //we don't delete wrong item + if(sd->status.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;i<MAX_INVENTORY;i++){ + //we don't delete wrong item or equipped item + if(sd->status.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;i<MAX_PARTY;i++){ + if(p->member[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; i<MAX_INVENTORY; i++) { + if(sd->status.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; i<MAX_INVENTORY; i++) { + if(sd->status.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;i<level;i++) + guild_skillup(sd,id,flag); + + return 0; +} +/*========================================== + * スキルレベル所得 + *------------------------------------------ + */ +int buildin_getskilllv(struct script_state *st) +{ + int id=conv_num(st,& (st->stack->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;i<users;i++) + { + pl_sd = pl_allsd[i]; + if( !(battle_config.hide_GM_session && pc_isGM(pl_sd)) ) + { + if((disp_num++)%10==0) + clif_scriptnext(script_rid2sd(st),st->oid); + 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, <id>; + *------------------------------------------ + */ +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;i<n;i++){ + struct map_session_data *sd=cd->usersd[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;i<users;i++) + { + if ((pl_sd = pl_allsd[i]) && m == pl_sd->bl.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;i<users;i++) + { + if((pl_sd=pl_allsd[i]) && m == pl_sd->bl.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#, <target: 0 - NPC, 1 - PC> + *------------------------------------------ + */ +//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;i<MAX_GUILDCASTLE;i++){ + if( (gc=guild_castle_search(i)) != NULL ){ + if(strcmp(mapname,gc->map_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;i<MAX_GUILDCASTLE;i++) + if( (gc=guild_castle_search(i)) != NULL && + strcmp(mapname,gc->map_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;i<MAX_GUILDCASTLE;i++){ + if( (gc=guild_castle_search(i)) != NULL ){ + if(strcmp(mapname,gc->map_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;i<MAX_GUILDCASTLE;i++){ + if( (gc=guild_castle_search(i)) != NULL ){ + if(strcmp(mapname,gc->map_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;i<MAX_INVENTORY;i++){ + if(sd->status.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;i<MAX_SKILL;i++){ + if(sd->status.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; i<MAX_INVENTORY; i++) { + if (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; +} + +/*========================================== + 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;i<MAX_INVENTORY;i++){ + if(sd->status.inventory[i].nameid > 0 && sd->status.inventory[i].amount){ + for(n=0;n<MAX_SLOTS;n++){ + if(sd->status.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;i<st->end;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;i<st->end;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;i<c && bl;i++,bl=bl->next){ + 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; i<MAX_INVENTORY; i++) { + if(sd->status.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; k<sd->inventory_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; k<sd->inventory_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; k<sd->inventory_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;i<MAX_INVENTORY;i++){ + if(sd->status.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)pos<strlen(str) ) ? isalpha( str[pos] ) : 0; + + push_val(st->stack,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 <mili sec> +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 <mili sec> +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(<param>, <npc name>); +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)++]<<j; + j+=6; + } + return i+(script[(*pos)++]<<j); +} + +/*========================================== + * コマンドのプッシュバック + *------------------------------------------ + */ +void unget_com(int c) +{ + if(unget_com_data!=-1){ + if(battle_config.error_log) + ShowError("unget_com can back only 1 data\n"); + } + unget_com_data=c; +} + +/*========================================== + * 数値の所得 + *------------------------------------------ + */ +int get_num(unsigned char *script,int *pos) +{ + int i,j; + i=0; j=0; + while(script[*pos]>=0xc0){ + i+=(script[(*pos)++]&0x7f)<<j; + j+=6; + } + return i+((script[(*pos)++]&0x7f)<<j); +} + +/*========================================== + * スタックから値を取り出す + *------------------------------------------ + */ +int pop_val(struct script_state* st) +{ + if(st->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_LE: + i1=i1<=i2; + break; + case C_R_SHIFT: + i1=i1>>i2; + break; + case C_L_SHIFT: + i1=i1<<i2; + break; + } + push_val(st->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;i<end_sp;i++){ + switch(st->stack->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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <limits.h>
-
-#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,(skill<lv)?skill:lv,tick,0xf00000);
- }
- // Gank
- if(dstmd && dstmd->state.steal_flag<battle_config.skill_steal_max_tries && sd->status.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;i<size*size;i++) {
- x = src->x+(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;i<MAX_SKILLTIMERSKILL && ud->skilltimerskill[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;i<MAX_SKILLTIMERSKILL;i++) {
- if(ud->skilltimerskill[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;i<c;i++){
- skill_blown(src,bl,0x20000|1);
- skill_area_temp[0]=0;
- map_foreachinrange(skill_area_sub,bl,
- skill_get_splash(skillid, skilllv),BL_CHAR,
- src,skillid,skilllv,tick, flag|BCT_ENEMY,
- skill_area_sub_count);
- if(skill_area_temp[0]>1) 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;i<SC_MAX;i++){
- if (tsc->data[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;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<MAX_SKILLUNITGROUP && sd->ud.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;i<lv;i++){
- if(mapindex == p[i]->map){
- 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;i<layout->count;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;i<size*size;i++) {
- x = sd->bl.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<<sd->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<<sd->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;i<size*size;i++) {
- x = src->bl.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;i<size*size;i++) {
- x = src->x+(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;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<count;i++)
- skill_delunitgroup(bl, group[i]);
- return count;
-}
-
-/*==========================================
- * Returns the first element field found [Skotlex]
- *------------------------------------------
- */
-struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
-{
- struct unit_data *ud = unit_bl2ud(bl);
- int i;
- nullpo_retr(0, bl);
- if (!ud) return NULL;
-
- for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<MAX_SKILLUNITGROUP && ud->skillunit[i]; i++);
-
- if(i == MAX_SKILLUNITGROUP) {
- int j=0;
- unsigned maxdiff=0,x,tick=gettick();
- for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<group->unit_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; i<MAX_SKILLUNITGROUP && ud->skillunit[i]!=group; i++);
- for (j=i; j<MAX_SKILLUNITGROUP && ud->skillunit[j]; j++);
- j--;
- if (i<MAX_SKILLUNITGROUP) {
- ud->skillunit[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; i<MAX_SKILLUNITGROUPTICKSET; i++) {
- k = (i+s) % MAX_SKILLUNITGROUPTICKSET;
- if (set[k].id == id)
- return &set[k];
- else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || 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;i<group->unit_count;i++){
- unit1=&group->unit[i];
- if (!unit1->alive || unit1->bl.m!=m)
- continue;
- for(j=0;j<group->unit_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;i<group->unit_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(;j<group->unit_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;i++){
- if(skill_produce_db[i].nameid == nameid )
- break;
- }
- if( 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;j<MAX_PRODUCE_RESOURCE;j++){
- int id,x,y;
- if( (id=skill_produce_db[i].mat_id[j]) <= 0 ) /* これ以?繧ヘ?゙料要らない */
- continue;
- if(skill_produce_db[i].mat_amount[j] <= 0) {
- if(pc_search_inventory(sd,id) < 0)
- return 0;
- }
- else {
- for(y=0,x=0;y<MAX_INVENTORY;y++)
- if( sd->status.inventory[y].nameid == id )
- x+=sd->status.inventory[y].amount;
- if(x<qty*skill_produce_db[i].mat_amount[j]) /* アイテムが足りない */
- return 0;
- }
- }
- return i+1;
-}
-
-/*==========================================
- * アイテム??ャ可能判定
- *------------------------------------------
- */
-int skill_produce_mix( struct map_session_data *sd, int skill_id,
- int nameid, int slot1, int slot2, int slot3, int qty)
-{
- int slot[3];
- int i,sc,ele,idx,equip,wlv,make_per,flag;
-
- nullpo_retr(0, sd);
-
- 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<MAX_PRODUCE_RESOURCE;i++){
- int j,id,x;
- if( (id=skill_produce_db[idx].mat_id[i]) <= 0 )
- continue;
- x=qty*skill_produce_db[idx].mat_amount[i]; /* 必要な個? */
- do{ /* 2つ以?繧フインデックスにまたがっているかもしれない */
- int y=0;
- j = pc_search_inventory(sd,id);
-
- if(j >= 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;i<MAX_SKILL_ARROW_DB;i++)
- if(nameid == skill_arrow_db[i].nameid) {
- index = i;
- break;
- }
-
- if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0)
- return 1;
-
- pc_delitem(sd,j,1,0);
- for(i=0;i<5;i++) {
- memset(&tmp_item,0,sizeof(tmp_item));
- tmp_item.identify = 1;
- tmp_item.nameid = skill_arrow_db[index].cre_id[i];
- tmp_item.amount = skill_arrow_db[index].cre_amount[i];
- if(battle_config.making_arrow_name_input) {
- 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(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<num && str; i++){
- val[i] = str;
- str = strchr(str,',');
- if (str)
- *str++=0;
- }
- return i;
-}
-/*
- * 文字列??
- * ':' で区?リってatoiしてvalに戻す
- */
-int skill_split_atoi(char *str,int *val)
-{
- int i, j, diff, step = 1;
-
- for (i=0; i<MAX_SKILL_LEVEL; i++) {
- if (!str) break;
- val[i] = atoi(str);
- str = strchr(str,':');
- if (str)
- *str++=0;
- }
- if(i==0) //No data found.
- return 0;
- if(i==1)
- { //Single value, have the whole range have the same value.
- for (; i < MAX_SKILL_LEVEL; i++)
- val[i] = val[i-1];
- return i;
- }
- //Check for linear change with increasing steps until we reach half of the data acquired.
- for (step = 1; step <= i/2; step++)
- {
- diff = val[i-1] - val[i-step-1];
- for(j = i-1; j >= 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<MAX_SKILL_LEVEL; i++)
- val[i] = val[i-1];
- return i;
-}
-
-/*
- * スキルユニットの配置?報??ャ
- */
-void skill_init_unit_layout(void)
-{
- int i,j,size,pos = 0;
-
- memset(skill_unit_layout,0,sizeof(skill_unit_layout));
- // 矩形のユニット配置を??ャする
- for (i=0; i<=MAX_SQUARE_LAYOUT; i++) {
- size = i*2+1;
- skill_unit_layout[i].count = size*size;
- for (j=0; j<size*size; j++) {
- skill_unit_layout[i].dx[j] = (j%size-i);
- skill_unit_layout[i].dy[j] = (j/size-i);
- }
- }
- pos = i;
- // 矩形以外のユニット配置を??ャする
- for (i=0;i<MAX_SKILL_DB;i++) {
- if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1)
- continue;
- switch (i) {
- case MG_FIREWALL:
- case WZ_ICEWALL:
- // ファイア?[ウォ?[ル?Aアイスウォ?[ルは方向で変わるので別??
- break;
- case PR_SANCTUARY:
- {
- static const int dx[] = {
- -1, 0, 1,-2,-1, 0, 1, 2,-2,-1,
- 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1};
- static const int dy[]={
- -2,-2,-2,-1,-1,-1,-1,-1, 0, 0,
- 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2};
- skill_unit_layout[pos].count = 21;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case PR_MAGNUS:
- {
- static const int dx[] = {
- -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
- 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
- -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1};
- static const int dy[] = {
- -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
- -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3};
- skill_unit_layout[pos].count = 33;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case AS_VENOMDUST:
- {
- static const int dx[] = {-1, 0, 0, 0, 1};
- static const int dy[] = { 0,-1, 0, 1, 0};
- skill_unit_layout[pos].count = 5;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case CR_GRANDCROSS:
- case NPC_GRANDDARKNESS:
- {
- static const int dx[] = {
- 0, 0,-1, 0, 1,-2,-1, 0, 1, 2,
- -4,-3,-2,-1, 0, 1, 2, 3, 4,-2,
- -1, 0, 1, 2,-1, 0, 1, 0, 0};
- static const int dy[] = {
- -4,-3,-2,-2,-2,-1,-1,-1,-1,-1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
- 1, 1, 1, 1, 2, 2, 2, 3, 4};
- skill_unit_layout[pos].count = 29;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case PF_FOGWALL:
- {
- static const int dx[] = {
- -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
- static const int dy[] = {
- -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
- skill_unit_layout[pos].count = 15;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- case PA_GOSPEL:
- {
- static const int dx[] = {
- -1, 0, 1,-1, 0, 1,-3,-2,-1, 0,
- 1, 2, 3,-3,-2,-1, 0, 1, 2, 3,
- -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,
- -1, 0, 1};
- static const int dy[] = {
- -3,-3,-3,-2,-2,-2,-1,-1,-1,-1,
- -1,-1,-1, 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
- 3, 3, 3};
- skill_unit_layout[pos].count = 33;
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
- }
- default:
- ShowError("unknown unit layout at skill %d\n",i);
- break;
- }
- if (!skill_unit_layout[pos].count)
- continue;
- for (j=0;j<MAX_SKILL_LEVEL;j++)
- skill_db[i].unit_layout_type[j] = pos;
- pos++;
- }
- // ファイヤ?[ウォ?[ル
- firewall_unit_pos = pos;
- for (i=0;i<8;i++) {
- if (i&1) { /* 斜め配置 */
- skill_unit_layout[pos].count = 5;
- if (i&0x2) {
- int dx[] = {-1,-1, 0, 0, 1};
- int dy[] = { 1, 0, 0,-1,-1};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else {
- int dx[] = { 1, 1 ,0, 0,-1};
- int dy[] = { 1, 0, 0,-1,-1};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- } else { /* ?c横配置 */
- skill_unit_layout[pos].count = 3;
- if (i%4==0) { /* ?繪コ */
- int dx[] = {-1, 0, 1};
- int dy[] = { 0, 0, 0};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else { /* ?カ右 */
- int dx[] = { 0, 0, 0};
- int dy[] = {-1, 0, 1};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- }
- pos++;
- }
- // アイスウォ?[ル
- icewall_unit_pos = pos;
- for (i=0;i<8;i++) {
- skill_unit_layout[pos].count = 5;
- if (i&1) { /* 斜め配置 */
- if (i&0x2) {
- int dx[] = {-2,-1, 0, 1, 2};
- int dy[] = { 2, 1, 0,-1,-2};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else {
- int dx[] = { 2, 1 ,0,-1,-2};
- int dy[] = { 2, 1, 0,-1,-2};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- } else { /* ?c横配置 */
- if (i%4==0) { /* ?繪コ */
- int dx[] = {-2,-1, 0, 1, 2};
- int dy[] = { 0, 0, 0, 0, 0};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- } else { /* ?カ右 */
- int dx[] = { 0, 0, 0, 0, 0};
- int dy[] = {-2,-1, 0, 1, 2};
- memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
- memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- }
- }
- pos++;
- }
-}
-
-/*==========================================
- * スキル?係ファイル?み?み
- * skill_db.txt スキルデ?タ
- * skill_cast_db.txt スキルの詠?・時間とディレイデ?タ
- * produce_db.txt アイテム??ャスキル用デ?タ
- * create_arrow_db.txt 矢??ャスキル用デ?タ
- * abra_db.txt アブラカダブラ?動スキルデ?タ
- *------------------------------------------
- */
-int skill_readdb(void)
-{
- int i,j,k,l,m;
- FILE *fp;
- char line[1024],path[1024],*p;
- char *filename[]={"produce_db.txt","produce_db2.txt"};
-
- /* スキルデ?タベ?ス */
- memset(skill_db,0,sizeof(skill_db));
- sprintf(path, "%s/skill_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,15);
- if(j < 15 || split[14]==NULL)
- continue;
-
- i=atoi(split[0]);
- if (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<<l;
- p=strchr(p,':');
- if(!p)
- break;
- p++;
- }
-
- p = split[8];
- for(j=0;j<32;j++){
- l = atoi(p);
- if (l)
- skill_db[i].ammo |= 1<<l;
- p=strchr(p,':');
- if(!p)
- break;
- p++;
- }
- skill_split_atoi(split[9],skill_db[i].ammo_qty);
-
- if( strcmpi(split[10],"hiding")==0 ) skill_db[i].state=ST_HIDING;
- else if( strcmpi(split[10],"cloaking")==0 ) skill_db[i].state=ST_CLOAKING;
- else if( strcmpi(split[10],"hidden")==0 ) skill_db[i].state=ST_HIDDEN;
- else if( strcmpi(split[10],"riding")==0 ) skill_db[i].state=ST_RIDING;
- else if( strcmpi(split[10],"falcon")==0 ) skill_db[i].state=ST_FALCON;
- else if( strcmpi(split[10],"cart")==0 ) skill_db[i].state=ST_CART;
- else if( strcmpi(split[10],"shield")==0 ) skill_db[i].state=ST_SHIELD;
- else if( strcmpi(split[10],"sight")==0 ) skill_db[i].state=ST_SIGHT;
- else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[i].state=ST_EXPLOSIONSPIRITS;
- else if( strcmpi(split[10],"cartboost")==0 ) skill_db[i].state=ST_CARTBOOST;
- else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[i].state=ST_RECOV_WEIGHT_RATE;
- else if( strcmpi(split[10],"move_enable")==0 ) skill_db[i].state=ST_MOVE_ENABLE;
- else if( strcmpi(split[10],"water")==0 ) skill_db[i].state=ST_WATER;
- else skill_db[i].state=ST_NONE;
-
- skill_split_atoi(split[11],skill_db[i].spiritball);
- for (j = 0; j < 10; j++) {
- skill_db[i].itemid[j]=atoi(split[12+ 2*j]);
- skill_db[i].amount[j]=atoi(split[13+ 2*j]);
- }
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
-
- /* キャスティングデ?タベ?ス */
-
- sprintf(path, "%s/skill_cast_db.txt", db_path);
- fp=fopen(path,"r");
- if(fp==NULL){
- ShowError("can't read %s\n", path);
- return 1;
- }
-
- l=0;
- while(fgets(line,1020,fp)){
- char *split[50];
- l++;
- memset(split,0,sizeof(split)); // [Valaris] thanks to fov
- if(line[0]=='/' && line[1]=='/')
- continue;
- j = skill_split_str(line,split,6);
- if(split[0]==NULL || j<2)
- continue; //Blank line.
- if(split[5]==NULL || j<6) {
- ShowWarning("skill_cast_db.txt: Insufficient number of fields at line %d\n", l);
- 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].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_PRODUCE_RESOURCE; x+=2,y++){
- skill_produce_db[k].mat_id[y]=atoi(split[x]);
- skill_produce_db[k].mat_amount[y]=atoi(split[x+1]);
- }
- k++;
- if(k >= 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-buf<s;){
- char buf2[64];
-
- if (sscanf(p,"%[@]",buf2) == 1) {
- level = 1;
- new_flag = 1;
- } else if (new_flag && sscanf(p,"%[^#]#",buf2) == 1) {
- for (idx=0; skill_names[idx].id != 0; idx++) {
- if (strstr(buf2, skill_names[idx].name) != NULL) {
- skill = &skill_db[ skill_names[idx].id ];
- new_flag = 0;
- break;
- }
- }
- } else if (!new_flag && sscanf(p,"%d#",&sp) == 1) {
- skill->sp[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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <limits.h> + +#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,(skill<lv)?skill:lv,tick,0xf00000); + } + // Gank + if(dstmd && dstmd->state.steal_flag<battle_config.skill_steal_max_tries && sd->status.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;i<size*size;i++) { + x = src->x+(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;i<MAX_SKILLTIMERSKILL && ud->skilltimerskill[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;i<MAX_SKILLTIMERSKILL;i++) { + if(ud->skilltimerskill[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;i<c;i++){ + skill_blown(src,bl,0x20000|1); + skill_area_temp[0]=0; + map_foreachinrange(skill_area_sub,bl, + skill_get_splash(skillid, skilllv),BL_CHAR, + src,skillid,skilllv,tick, flag|BCT_ENEMY, + skill_area_sub_count); + if(skill_area_temp[0]>1) 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;i<SC_MAX;i++){ + if (tsc->data[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;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<MAX_SKILLUNITGROUP && sd->ud.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;i<lv;i++){ + if(mapindex == p[i]->map){ + 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;i<layout->count;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;i<size*size;i++) { + x = sd->bl.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<<sd->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<<sd->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;i<size*size;i++) { + x = src->bl.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;i<size*size;i++) { + x = src->x+(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;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<count;i++) + skill_delunitgroup(bl, group[i]); + return count; +} + +/*========================================== + * Returns the first element field found [Skotlex] + *------------------------------------------ + */ +struct skill_unit_group *skill_locate_element_field(struct block_list *bl) +{ + struct unit_data *ud = unit_bl2ud(bl); + int i; + nullpo_retr(0, bl); + if (!ud) return NULL; + + for (i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<MAX_SKILLUNITGROUP && ud->skillunit[i]; i++); + + if(i == MAX_SKILLUNITGROUP) { + int j=0; + unsigned maxdiff=0,x,tick=gettick(); + for(i=0;i<MAX_SKILLUNITGROUP && ud->skillunit[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;i<group->unit_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; i<MAX_SKILLUNITGROUP && ud->skillunit[i]!=group; i++); + for (j=i; j<MAX_SKILLUNITGROUP && ud->skillunit[j]; j++); + j--; + if (i<MAX_SKILLUNITGROUP) { + ud->skillunit[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; i<MAX_SKILLUNITGROUPTICKSET; i++) { + k = (i+s) % MAX_SKILLUNITGROUPTICKSET; + if (set[k].id == id) + return &set[k]; + else if (j==-1 && (DIFF_TICK(tick,set[k].tick)>0 || 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;i<group->unit_count;i++){ + unit1=&group->unit[i]; + if (!unit1->alive || unit1->bl.m!=m) + continue; + for(j=0;j<group->unit_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;i<group->unit_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(;j<group->unit_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;i++){ + if(skill_produce_db[i].nameid == nameid ) + break; + } + if( 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;j<MAX_PRODUCE_RESOURCE;j++){ + int id,x,y; + if( (id=skill_produce_db[i].mat_id[j]) <= 0 ) /* これ以?繧ヘ?゙料要らない */ + continue; + if(skill_produce_db[i].mat_amount[j] <= 0) { + if(pc_search_inventory(sd,id) < 0) + return 0; + } + else { + for(y=0,x=0;y<MAX_INVENTORY;y++) + if( sd->status.inventory[y].nameid == id ) + x+=sd->status.inventory[y].amount; + if(x<qty*skill_produce_db[i].mat_amount[j]) /* アイテムが足りない */ + return 0; + } + } + return i+1; +} + +/*========================================== + * アイテム??ャ可能判定 + *------------------------------------------ + */ +int skill_produce_mix( struct map_session_data *sd, int skill_id, + int nameid, int slot1, int slot2, int slot3, int qty) +{ + int slot[3]; + int i,sc,ele,idx,equip,wlv,make_per,flag; + struct status_data *status; + + nullpo_retr(0, sd); + status = status_get_status_data(&sd->bl); + + 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<MAX_PRODUCE_RESOURCE;i++){ + int j,id,x; + if( (id=skill_produce_db[idx].mat_id[i]) <= 0 ) + continue; + x=qty*skill_produce_db[idx].mat_amount[i]; /* 必要な個? */ + do{ /* 2つ以?繧フインデックスにまたがっているかもしれない */ + int y=0; + j = pc_search_inventory(sd,id); + + if(j >= 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;i<MAX_SKILL_ARROW_DB;i++) + if(nameid == skill_arrow_db[i].nameid) { + index = i; + break; + } + + if(index < 0 || (j = pc_search_inventory(sd,nameid)) < 0) + return 1; + + pc_delitem(sd,j,1,0); + for(i=0;i<5;i++) { + memset(&tmp_item,0,sizeof(tmp_item)); + tmp_item.identify = 1; + tmp_item.nameid = skill_arrow_db[index].cre_id[i]; + tmp_item.amount = skill_arrow_db[index].cre_amount[i]; + if(battle_config.making_arrow_name_input) { + 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(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<num && str; i++){ + val[i] = str; + str = strchr(str,','); + if (str) + *str++=0; + } + return i; +} +/* + * 文字列?? + * ':' で区?リってatoiしてvalに戻す + */ +int skill_split_atoi(char *str,int *val) +{ + int i, j, diff, step = 1; + + for (i=0; i<MAX_SKILL_LEVEL; i++) { + if (!str) break; + val[i] = atoi(str); + str = strchr(str,':'); + if (str) + *str++=0; + } + if(i==0) //No data found. + return 0; + if(i==1) + { //Single value, have the whole range have the same value. + for (; i < MAX_SKILL_LEVEL; i++) + val[i] = val[i-1]; + return i; + } + //Check for linear change with increasing steps until we reach half of the data acquired. + for (step = 1; step <= i/2; step++) + { + diff = val[i-1] - val[i-step-1]; + for(j = i-1; j >= 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<MAX_SKILL_LEVEL; i++) + val[i] = val[i-1]; + return i; +} + +/* + * スキルユニットの配置?報??ャ + */ +void skill_init_unit_layout(void) +{ + int i,j,size,pos = 0; + + memset(skill_unit_layout,0,sizeof(skill_unit_layout)); + // 矩形のユニット配置を??ャする + for (i=0; i<=MAX_SQUARE_LAYOUT; i++) { + size = i*2+1; + skill_unit_layout[i].count = size*size; + for (j=0; j<size*size; j++) { + skill_unit_layout[i].dx[j] = (j%size-i); + skill_unit_layout[i].dy[j] = (j/size-i); + } + } + pos = i; + // 矩形以外のユニット配置を??ャする + for (i=0;i<MAX_SKILL_DB;i++) { + if (!skill_db[i].unit_id[0] || skill_db[i].unit_layout_type[0] != -1) + continue; + switch (i) { + case MG_FIREWALL: + case WZ_ICEWALL: + // ファイア?[ウォ?[ル?Aアイスウォ?[ルは方向で変わるので別?? + break; + case PR_SANCTUARY: + { + static const int dx[] = { + -1, 0, 1,-2,-1, 0, 1, 2,-2,-1, + 0, 1, 2,-2,-1, 0, 1, 2,-1, 0, 1}; + static const int dy[]={ + -2,-2,-2,-1,-1,-1,-1,-1, 0, 0, + 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2}; + skill_unit_layout[pos].count = 21; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + break; + } + case PR_MAGNUS: + { + static const int dx[] = { + -1, 0, 1,-1, 0, 1,-3,-2,-1, 0, + 1, 2, 3,-3,-2,-1, 0, 1, 2, 3, + -3,-2,-1, 0, 1, 2, 3,-1, 0, 1,-1, 0, 1}; + static const int dy[] = { + -3,-3,-3,-2,-2,-2,-1,-1,-1,-1, + -1,-1,-1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3}; + skill_unit_layout[pos].count = 33; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + break; + } + case AS_VENOMDUST: + { + static const int dx[] = {-1, 0, 0, 0, 1}; + static const int dy[] = { 0,-1, 0, 1, 0}; + skill_unit_layout[pos].count = 5; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + break; + } + case CR_GRANDCROSS: + case NPC_GRANDDARKNESS: + { + static const int dx[] = { + 0, 0,-1, 0, 1,-2,-1, 0, 1, 2, + -4,-3,-2,-1, 0, 1, 2, 3, 4,-2, + -1, 0, 1, 2,-1, 0, 1, 0, 0}; + static const int dy[] = { + -4,-3,-2,-2,-2,-1,-1,-1,-1,-1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 2, 2, 2, 3, 4}; + skill_unit_layout[pos].count = 29; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + break; + } + case PF_FOGWALL: + { + static const int dx[] = { + -2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2}; + static const int dy[] = { + -1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1}; + skill_unit_layout[pos].count = 15; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + break; + } + case PA_GOSPEL: + { + static const int dx[] = { + -1, 0, 1,-1, 0, 1,-3,-2,-1, 0, + 1, 2, 3,-3,-2,-1, 0, 1, 2, 3, + -3,-2,-1, 0, 1, 2, 3,-1, 0, 1, + -1, 0, 1}; + static const int dy[] = { + -3,-3,-3,-2,-2,-2,-1,-1,-1,-1, + -1,-1,-1, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, + 3, 3, 3}; + skill_unit_layout[pos].count = 33; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + break; + } + default: + ShowError("unknown unit layout at skill %d\n",i); + break; + } + if (!skill_unit_layout[pos].count) + continue; + for (j=0;j<MAX_SKILL_LEVEL;j++) + skill_db[i].unit_layout_type[j] = pos; + pos++; + } + // ファイヤ?[ウォ?[ル + firewall_unit_pos = pos; + for (i=0;i<8;i++) { + if (i&1) { /* 斜め配置 */ + skill_unit_layout[pos].count = 5; + if (i&0x2) { + int dx[] = {-1,-1, 0, 0, 1}; + int dy[] = { 1, 0, 0,-1,-1}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } else { + int dx[] = { 1, 1 ,0, 0,-1}; + int dy[] = { 1, 0, 0,-1,-1}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + } else { /* ?c横配置 */ + skill_unit_layout[pos].count = 3; + if (i%4==0) { /* ?繪コ */ + int dx[] = {-1, 0, 1}; + int dy[] = { 0, 0, 0}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } else { /* ?カ右 */ + int dx[] = { 0, 0, 0}; + int dy[] = {-1, 0, 1}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + } + pos++; + } + // アイスウォ?[ル + icewall_unit_pos = pos; + for (i=0;i<8;i++) { + skill_unit_layout[pos].count = 5; + if (i&1) { /* 斜め配置 */ + if (i&0x2) { + int dx[] = {-2,-1, 0, 1, 2}; + int dy[] = { 2, 1, 0,-1,-2}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } else { + int dx[] = { 2, 1 ,0,-1,-2}; + int dy[] = { 2, 1, 0,-1,-2}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + } else { /* ?c横配置 */ + if (i%4==0) { /* ?繪コ */ + int dx[] = {-2,-1, 0, 1, 2}; + int dy[] = { 0, 0, 0, 0, 0}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } else { /* ?カ右 */ + int dx[] = { 0, 0, 0, 0, 0}; + int dy[] = {-2,-1, 0, 1, 2}; + memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx)); + memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy)); + } + } + pos++; + } +} + +/*========================================== + * スキル?係ファイル?み?み + * skill_db.txt スキルデ?タ + * skill_cast_db.txt スキルの詠?・時間とディレイデ?タ + * produce_db.txt アイテム??ャスキル用デ?タ + * create_arrow_db.txt 矢??ャスキル用デ?タ + * abra_db.txt アブラカダブラ?動スキルデ?タ + *------------------------------------------ + */ +int skill_readdb(void) +{ + int i,j,k,l,m; + FILE *fp; + char line[1024],path[1024],*p; + char *filename[]={"produce_db.txt","produce_db2.txt"}; + + /* スキルデ?タベ?ス */ + memset(skill_db,0,sizeof(skill_db)); + sprintf(path, "%s/skill_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,15); + if(j < 15 || split[14]==NULL) + continue; + + i=atoi(split[0]); + if (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<<l; + p=strchr(p,':'); + if(!p) + break; + p++; + } + + p = split[8]; + for(j=0;j<32;j++){ + l = atoi(p); + if (l) + skill_db[i].ammo |= 1<<l; + p=strchr(p,':'); + if(!p) + break; + p++; + } + skill_split_atoi(split[9],skill_db[i].ammo_qty); + + if( strcmpi(split[10],"hiding")==0 ) skill_db[i].state=ST_HIDING; + else if( strcmpi(split[10],"cloaking")==0 ) skill_db[i].state=ST_CLOAKING; + else if( strcmpi(split[10],"hidden")==0 ) skill_db[i].state=ST_HIDDEN; + else if( strcmpi(split[10],"riding")==0 ) skill_db[i].state=ST_RIDING; + else if( strcmpi(split[10],"falcon")==0 ) skill_db[i].state=ST_FALCON; + else if( strcmpi(split[10],"cart")==0 ) skill_db[i].state=ST_CART; + else if( strcmpi(split[10],"shield")==0 ) skill_db[i].state=ST_SHIELD; + else if( strcmpi(split[10],"sight")==0 ) skill_db[i].state=ST_SIGHT; + else if( strcmpi(split[10],"explosionspirits")==0 ) skill_db[i].state=ST_EXPLOSIONSPIRITS; + else if( strcmpi(split[10],"cartboost")==0 ) skill_db[i].state=ST_CARTBOOST; + else if( strcmpi(split[10],"recover_weight_rate")==0 ) skill_db[i].state=ST_RECOV_WEIGHT_RATE; + else if( strcmpi(split[10],"move_enable")==0 ) skill_db[i].state=ST_MOVE_ENABLE; + else if( strcmpi(split[10],"water")==0 ) skill_db[i].state=ST_WATER; + else skill_db[i].state=ST_NONE; + + skill_split_atoi(split[11],skill_db[i].spiritball); + for (j = 0; j < 10; j++) { + skill_db[i].itemid[j]=atoi(split[12+ 2*j]); + skill_db[i].amount[j]=atoi(split[13+ 2*j]); + } + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + /* キャスティングデ?タベ?ス */ + + sprintf(path, "%s/skill_cast_db.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + + l=0; + while(fgets(line,1020,fp)){ + char *split[50]; + l++; + memset(split,0,sizeof(split)); // [Valaris] thanks to fov + if(line[0]=='/' && line[1]=='/') + continue; + j = skill_split_str(line,split,6); + if(split[0]==NULL || j<2) + continue; //Blank line. + if(split[5]==NULL || j<6) { + ShowWarning("skill_cast_db.txt: Insufficient number of fields at line %d\n", l); + 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].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_PRODUCE_RESOURCE; x+=2,y++){ + skill_produce_db[k].mat_id[y]=atoi(split[x]); + skill_produce_db[k].mat_amount[y]=atoi(split[x+1]); + } + k++; + if(k >= 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-buf<s;){ + char buf2[64]; + + if (sscanf(p,"%[@]",buf2) == 1) { + level = 1; + new_flag = 1; + } else if (new_flag && sscanf(p,"%[^#]#",buf2) == 1) { + for (idx=0; skill_names[idx].id != 0; idx++) { + if (strstr(buf2, skill_names[idx].name) != NULL) { + skill = &skill_db[ skill_names[idx].id ]; + new_flag = 0; + break; + } + } + } else if (!new_flag && sscanf(p,"%d#",&sp) == 1) { + skill->sp[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 <time.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <memory.h>
-#include <string.h>
-#include <limits.h>
-
-#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;i<MAX_INVENTORY;i++){
- if(sd->status.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;i<MAX_CART;i++){
- if(sd->status.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;j<sd->inventory_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 && i<MAX_LEVEL;i++){
- if(job_bonus[sd->status.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){ //<Skotlex> 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) { //<Skotlex> 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) { //<Skotlex> 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) { //<Skotlex> 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) { //<Skotlex> 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) { //<Skotlex> 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: //<Skotlex> 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: //<Skotlex> 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) { //<Skotlex> 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:
- //<Skotlex> 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 tick<min?min:tick;
-}
-/*==========================================
- * Starts a status change.
- * type = type, val1~4 depend on the type.
- * rate = base success rate. 10000 = 100%
- * Tick is base duration
- * flag:
- * &1: Cannot be avoided (it has to start)
- * &2: Tick should not be reduced (by vit, luk, lv, etc)
- * &4: sc_data loaded, no value has to be altered.
- * &8: rate should not be reduced
- *------------------------------------------
- */
-int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag)
-{
- struct map_session_data *sd = NULL;
- struct status_change* sc;
- int opt_flag , calc_flag = 0,updateflag = 0, save_flag = 0, race, mode, elem, undead_flag;
-
- nullpo_retr(0, bl);
- sc=status_get_sc(bl);
-
- if (!sc || status_isdead(bl))
- return 0;
-
- switch (bl->type)
- {
- 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<<sd->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<<sd->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.hp<sd->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,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<val1;i++) {
- t = 5-(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<MAX_PC_CLASS;i++) {
- memset(hp_sigma_val[i],0,sizeof(hp_sigma_val[i]));
- for(k=0,j=2;j<=MAX_LEVEL;j++) {
- k += hp_coefficient[i]*j + 50;
- k -= k%100;
- hp_sigma_val[i][j-1] = k;
- if (k >= 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_WEAPON_TYPE;j++)
- aspd_base[atoi(split[0])][j]=atoi(split[j+5]);
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
-
- memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus
- sprintf(path, "%s/job_db2.txt", db_path);
- fp=fopen(path,"r");
- if(fp==NULL){
- ShowError("can't read %s\n", path);
- return 1;
- }
- while(fgets(line, sizeof(line)-1, fp)){
- char *split[MAX_LEVEL+1]; //Job Level is limited to MAX_LEVEL, so the bonuses should likewise be limited to it. [Skotlex]
- if(line[0]=='/' && line[1]=='/')
- continue;
- for(j=0,p=line;j<MAX_LEVEL+1 && p;j++){
- split[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- if(atoi(split[0])>=MAX_PC_CLASS)
- continue;
- for(i=1;i<j && split[i];i++)
- job_bonus[atoi(split[0])][i-1]=atoi(split[i]);
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
-
- // サイズ補正テ?ブル
- for(i=0;i<3;i++)
- for(j=0;j<MAX_WEAPON_TYPE;j++)
- atkmods[i][j]=100;
- sprintf(path, "%s/size_fix.txt", db_path);
- fp=fopen(path,"r");
- 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];
- if(line[0]=='/' && line[1]=='/')
- continue;
- if(atoi(line)<=0)
- continue;
- memset(split,0,sizeof(split));
- for(j=0,p=line;j<MAX_WEAPON_TYPE && p;j++){
- split[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- atkmods[i][j]=atoi(split[j]);
- }
- i++;
- }
- fclose(fp);
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
-
- // 精?デ?タテ?ブル
- for(i=0;i<5;i++){
- for(j=0;j<MAX_REFINE; j++)
- percentrefinery[i][j]=100;
- percentrefinery[i][j]=0; //Slot MAX+1 always has 0% success chance [Skotlex]
- refinebonus[i][0]=0;
- refinebonus[i][1]=0;
- refinebonus[i][2]=10;
- }
-
- sprintf(path, "%s/refine_db.txt", db_path);
- fp=fopen(path,"r");
- if(fp==NULL){
- ShowError("can't read %s\n", path);
- return 1;
- }
- i=0;
- while(fgets(line, sizeof(line)-1, fp)){
- char *split[16];
- if(line[0]=='/' && line[1]=='/')
- continue;
- if(atoi(line)<=0)
- continue;
- memset(split,0,sizeof(split));
- for(j=0,p=line;j<16 && p;j++){
- split[j]=p;
- p=strchr(p,',');
- if(p) *p++=0;
- }
- refinebonus[i][0]=atoi(split[0]); // 精?ボ?ナス
- refinebonus[i][1]=atoi(split[1]); // 過?精?ボ?ナス
- refinebonus[i][2]=atoi(split[2]); // 安全精?限界
- for(j=0;j<MAX_REFINE && split[j];j++)
- percentrefinery[i][j]=atoi(split[j+3]);
- i++;
- }
- fclose(fp); //Lupus. close this file!!!
- ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path);
-
- return 0;
-}
-
-/*==========================================
- * スキル関係初期化処理
- *------------------------------------------
- */
-int do_init_status(void)
-{
- if (SC_MAX > 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 <time.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <memory.h> +#include <string.h> +#include <limits.h> + +#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;i<MAX_INVENTORY;i++){ + if(sd->status.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;i<MAX_CART;i++){ + if(sd->status.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;j<sd->inventory_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 && i<MAX_LEVEL;i++){ + if(!job_bonus[sd->status.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:a<min?min:a) +/*========================================== + * Apply shared stat mods from status changes [DracoRPG] + *------------------------------------------ + */ +static unsigned short status_calc_str(struct block_list *bl, struct status_change *sc, int str) +{ + if(!sc || !sc->count) + 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 tick<min?min:tick; +} +/*========================================== + * Starts a status change. + * type = type, val1~4 depend on the type. + * rate = base success rate. 10000 = 100% + * Tick is base duration + * flag: + * &1: Cannot be avoided (it has to start) + * &2: Tick should not be reduced (by vit, luk, lv, etc) + * &4: sc_data loaded, no value has to be altered. + * &8: rate should not be reduced + *------------------------------------------ + */ +int status_change_start(struct block_list *bl,int type,int rate,int val1,int val2,int val3,int val4,int tick,int flag) +{ + struct map_session_data *sd = NULL; + struct status_change* sc; + struct status_data *status; + int opt_flag , calc_flag = 0, undead_flag; + + nullpo_retr(0, bl); + sc=status_get_sc(bl); + status = status_get_status_data(bl); + + if (!sc || !status || status_isdead(bl)) + return 0; + + switch (bl->type) + { + 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<<sd->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<<sd->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<val1;i++) { + t = 5-(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<MAX_PC_CLASS;i++) { + memset(hp_sigma_val[i],0,sizeof(hp_sigma_val[i])); + for(k=0,j=2;j<=MAX_LEVEL;j++) { + k += hp_coefficient[i]*j + 50; + k -= k%100; + hp_sigma_val[i][j-1] = k; + if (k >= 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_WEAPON_TYPE;j++) + aspd_base[atoi(split[0])][j]=atoi(split[j+5]); + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + memset(job_bonus,0,sizeof(job_bonus)); // Job-specific stats bonus + sprintf(path, "%s/job_db2.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + while(fgets(line, sizeof(line)-1, fp)){ + char *split[MAX_LEVEL+1]; //Job Level is limited to MAX_LEVEL, so the bonuses should likewise be limited to it. [Skotlex] + if(line[0]=='/' && line[1]=='/') + continue; + for(j=0,p=line;j<MAX_LEVEL+1 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + if(atoi(split[0])>=MAX_PC_CLASS) + continue; + for(i=1;i<j && split[i];i++) + job_bonus[atoi(split[0])][i-1]=atoi(split[i]); + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + // サイズ補正テ?ブル + for(i=0;i<3;i++) + for(j=0;j<MAX_WEAPON_TYPE;j++) + atkmods[i][j]=100; + sprintf(path, "%s/size_fix.txt", db_path); + fp=fopen(path,"r"); + 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]; + if(line[0]=='/' && line[1]=='/') + continue; + if(atoi(line)<=0) + continue; + memset(split,0,sizeof(split)); + for(j=0,p=line;j<MAX_WEAPON_TYPE && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + atkmods[i][j]=atoi(split[j]); + } + i++; + } + fclose(fp); + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + // 精?デ?タテ?ブル + for(i=0;i<5;i++){ + for(j=0;j<MAX_REFINE; j++) + percentrefinery[i][j]=100; + percentrefinery[i][j]=0; //Slot MAX+1 always has 0% success chance [Skotlex] + refinebonus[i][0]=0; + refinebonus[i][1]=0; + refinebonus[i][2]=10; + } + + sprintf(path, "%s/refine_db.txt", db_path); + fp=fopen(path,"r"); + if(fp==NULL){ + ShowError("can't read %s\n", path); + return 1; + } + i=0; + while(fgets(line, sizeof(line)-1, fp)){ + char *split[16]; + if(line[0]=='/' && line[1]=='/') + continue; + if(atoi(line)<=0) + continue; + memset(split,0,sizeof(split)); + for(j=0,p=line;j<16 && p;j++){ + split[j]=p; + p=strchr(p,','); + if(p) *p++=0; + } + refinebonus[i][0]=atoi(split[0]); // 精?ボ?ナス + refinebonus[i][1]=atoi(split[1]); // 過?精?ボ?ナス + refinebonus[i][2]=atoi(split[2]); // 安全精?限界 + for(j=0;j<MAX_REFINE && split[j];j++) + percentrefinery[i][j]=atoi(split[j+3]); + i++; + } + fclose(fp); //Lupus. close this file!!! + ShowStatus("Done reading '"CL_WHITE"%s"CL_RESET"'.\n",path); + + return 0; +} + +/*========================================== + * スキル関係初期化処理 + *------------------------------------------ + */ +int do_init_status(void) +{ + if (SC_MAX > 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));
}
/*==========================================
* 自分をロックしている対象の数を返す
@@ -1413,18 +1409,6 @@ int unit_counttargeted(struct block_list *bl,int 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_);
}
|