summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changelog-Trunk.txt43
-rw-r--r--conf-tmpl/battle/battle.conf26
-rw-r--r--db/skill_cast_db.txt21
-rw-r--r--db/skill_db.txt18
-rw-r--r--db/skill_require_db.txt2
-rw-r--r--src/map/atcommand.c116
-rw-r--r--src/map/battle.c8886
-rw-r--r--src/map/battle.h15
-rw-r--r--src/map/charcommand.c11
-rw-r--r--src/map/clif.c169
-rw-r--r--src/map/clif.h2
-rw-r--r--src/map/guild.c2
-rw-r--r--src/map/map.c7
-rw-r--r--src/map/map.h115
-rw-r--r--src/map/mob.c8196
-rw-r--r--src/map/mob.h22
-rw-r--r--src/map/npc.c4
-rw-r--r--src/map/pc.c1113
-rw-r--r--src/map/pc.h12
-rw-r--r--src/map/pet.c425
-rw-r--r--src/map/pet.h5
-rw-r--r--src/map/script.c24112
-rw-r--r--src/map/skill.c22080
-rw-r--r--src/map/skill.h5
-rw-r--r--src/map/status.c12297
-rw-r--r--src/map/status.h179
-rw-r--r--src/map/unit.c132
27 files changed, 38599 insertions, 39416 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt
index 6cadcf436..bc029a757 100644
--- a/Changelog-Trunk.txt
+++ b/Changelog-Trunk.txt
@@ -3,6 +3,49 @@ Date Added
AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK.
IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
+2006/05/27
+ - Added structure status_data which holds status-related information (str,
+ agi, etc, speed, amotion, adelay, dmotion, weapon-damage, race, size, etc)
+ and weapon_atk structure with the weapon specific info (atk, atk2, element)
+ to be used by all combat structures (TODO: Homun needs to be updated to use
+ it). This in change involves a LOT of changes throughout the code and many
+ optimizations were done as well. Partial list (see svn changelog for complete
+ changes): [Skotlex]
+ - NOTE: Changes are substantial! I tested a bunch of stuff and all the
+ skills I changed the most, but it is possible there are unnoticed bugs
+ remaining to fix!
+ - Cleaned up TBL_PC, TBL_MOB, TBL_PC and mob_db structures to use status_data.
+ - Split damage received functions into pc_damage/pc_dead and mob_damage/mob_dead
+ - Added status functions to deal with damage and healing (status_damage,
+ status_heal, status_percent_change) and a bunch of defines for easier
+ handling of them (status_percent_heal, status_percent_damage,
+ status_fix_damage, status_kill, etc). Objects must be hurt/healed through
+ THIS, pc_damage/mob_damage most no longer be directly invoked!
+ - Rewrote and cleaned up battle_calc_misc_attack
+ - Merged config options pc_attack_attr_none, mob_attack_attr_none,
+ pet_attack_attr_none into attack_attr_none (type 4)
+ - Removed config options player_defense_type, monster_defense_type,
+ pet_defense_type in favor of weapon_defense_type
+ - Modified skill_calc_heal to take into account the MEDITATION bonus.
+ - Modified Slim Pitcher so it will work when casted by non-players. Will
+ now also work with SP-healing items.
+ - Rewrote Freedom of Cast code to use function status_freecast_switch to
+ switch adelay/speed when cast begins/ends.
+ - Modified Magic Power to store amplified MATK/MATK2 in val3/val4 for
+ easier updating when used in conjunction with ground skills.
+ - Fixed Asura Strike being usable from within a combo regardless of combo skill.
+ - Added status_calc_bl which does status-change related calculations using
+ as base the base_status of the bl object and the SCB_* flag passed. This is
+ invoked on status changes, and status_calc_pc will no longer be invoked
+ (which results on much faster status-change calculations).
+ - pc_clean_skilltree will now also remove item-granted skills.
+ - Learning skills will now only invoke status_calc_pc when the skill is passive.
+ - Cleaned up pc_check_base/job_lvup to only invoke the lv-up related
+ packets and functions ONCE regardless of skill-levls earned.
+ - Cleaned up pc_ regen related functions.
+ - Made player-sprite mobs have item pickup animation and walkdelay when taking items.
+ - Clones will copy a player's base status rather than battle status (so
+ status-change alterations are not cloned)
2006/05/26
* Part B of the homunculus code. [blackhole89]
While most of the homunculus handling functions are in now, it still lacks
diff --git a/conf-tmpl/battle/battle.conf b/conf-tmpl/battle/battle.conf
index 4cff45097..dec785359 100644
--- a/conf-tmpl/battle/battle.conf
+++ b/conf-tmpl/battle/battle.conf
@@ -83,17 +83,11 @@ vit_penalty_count: 3
// Amount of VIT defense penalized per each attacking monster more than vit_penalty_count
vit_penalty_num: 5
-// When the player attacks an object, the calculation method of DEF.
-// With 0 this will be ignored specification, at 1 or more def = subtraction of (DEF* value).
-player_defense_type: 0
-
-// When the monster attacks an object, the calculation method of DEF.
-// With 0 this will be ignored, at 1 or more def = subtraction of (DEF* value).
-monster_defense_type: 0
-
-// When the pet attacks an object, the calculation method of DEF.
-// With 0 this will be ignored specification, at 1 or more def = subtraction of (DEF* value).
-pet_defense_type: 0
+// Use alternate method of DEF calculation for physical attacks.
+// With 0, disabled (use normal def% reduction with further def2 reduction)
+// At 1 or more defense is substraction of (DEF* value).
+// eg: 10 + 50 def becomes 0 + (10*type + 50)
+weapon_defense_type: 0
//MDEFTame as above....(MDEF*value)
magic_defense_type: 0
@@ -115,14 +109,8 @@ vit_penalty_count_lv: 3
// Change attacker's direction to face opponent on every attack? (Note 4)
attack_direction_change: 15
-// Is a usual attack of a pet delivered withOUT an attribute? (Note 1)
-pet_attack_attr_none: no
-
-// Is a usual attack of a player delivered withOUT an attribute? (Note 1)
-pc_attack_attr_none: no
-
-// Is a usual attack of a monster delivered withOUT an attribute? (Note 1)
-mob_attack_attr_none: no
+// Remove elemental modifiers from normal attacks (skills will always have elemental modifiers)? (Note 4)
+attack_attr_none: 0
// Rate at which equipment can break (base rate before it's modified by any skills)
// 1 = 0.01% chance. Default for official servers: 0
diff --git a/db/skill_cast_db.txt b/db/skill_cast_db.txt
index 06b00bc57..6a5b233e6 100644
--- a/db/skill_cast_db.txt
+++ b/db/skill_cast_db.txt
@@ -286,6 +286,25 @@
//===== NPC Skills Part 1 ==================
+//-- NPC_ATTRICHANGE
+161,0,0,0,1800000,0
+//-- NPC_CHANGEWATER
+162,0,0,0,1800000,0
+//-- NPC_CHANGEGROUND
+163,0,0,0,1800000,0
+//-- NPC_CHANGEFIRE
+164,0,0,0,1800000,0
+//-- NPC_CHANGEWIND
+165,0,0,0,1800000,0
+//-- NPC_CHANGEPOISON
+166,0,0,0,1800000,0
+//-- NPC_CHANGEHOLY
+167,0,0,0,1800000,0
+//-- NPC_CHANGEDARKNESS
+168,0,0,0,1800000,0
+//-- NPC_CHANGETELEKINESIS
+169,0,0,0,1800000,0
+
//-- NPC_SELFDESTRUCTION
173,0,0,0,3500,0
@@ -531,7 +550,7 @@
//-- LK_TENSIONRELAX
358,0,0,0,180000,0
//-- LK_BERSERK
-359,0,0,0,300000,0
+359,0,0,0,300000,15000
//-- LK_FURY
360,0,0,0,300000,0
//==========================================
diff --git a/db/skill_db.txt b/db/skill_db.txt
index f19c6c407..ceebf260a 100644
--- a/db/skill_db.txt
+++ b/db/skill_db.txt
@@ -180,15 +180,15 @@
158,3,6,1,-1,0,0,10,1,no,0,2,0,weapon,0 //NPC_PIERCINGATT#Thrusting attack#
159,-1,6,1,-1,0,0,10,1,no,0,2,0,weapon,0 //NPC_MENTALBREAKER#Spirit Destruction#
160,9,6,1,0,0,0,10,1,no,0,2,0,weapon,0 //NPC_RANGEATTACK#Stand off attack#
-161,0,0,4,0,1,0,10,1,no,0,2,0,magic,0 //NPC_ATTRICHANGE#Run Attribute Change#
-162,0,0,4,1,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGEWATER#Water Attribute Change#
-163,0,0,4,2,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGEGROUND#Earth Attribute Change#
-164,0,0,4,3,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGEFIRE#Fire Attribute Change#
-165,0,0,4,4,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGEWIND#Wind Attribute Change#
-166,0,0,4,5,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGEPOISON#Poison Attribute Change#
-167,0,0,4,6,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGEHOLY#Holy Attribute Change#
-168,0,0,4,7,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGEDARKNESS#Shadow Attribute Change#
-169,0,0,4,8,1,0,10,1,no,0,2,0,magic,0 //NPC_CHANGETELEKINESIS#Sense Attribute Change#
+161,0,0,4,0,1,0,1,1,no,0,2,0,magic,0 //NPC_ATTRICHANGE#Run Attribute Change#
+162,0,0,4,1,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGEWATER#Water Attribute Change#
+163,0,0,4,2,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGEGROUND#Earth Attribute Change#
+164,0,0,4,3,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGEFIRE#Fire Attribute Change#
+165,0,0,4,4,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGEWIND#Wind Attribute Change#
+166,0,0,4,5,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGEPOISON#Poison Attribute Change#
+167,0,0,4,6,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGEHOLY#Holy Attribute Change#
+168,0,0,4,7,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGEDARKNESS#Shadow Attribute Change#
+169,0,0,4,8,1,0,1,1,no,0,2,0,magic,0 //NPC_CHANGETELEKINESIS#Sense Attribute Change#
170,-1,6,1,-1,0,0,10,1,no,0,2,0,weapon,0 //NPC_CRITICALSLASH#Defense disregard attack#
171,-1,8,1,-1,0,0,10,2:3:4:5:6:7:8:9:10:11,no,0,2,0,weapon,0 //NPC_COMBOATTACK#Multi-stage Attack#
172,-1,6,1,-1,0,0,10,1,no,0,2,0,weapon,0 //NPC_GUIDEATTACK#On-target Impact Attack#
diff --git a/db/skill_require_db.txt b/db/skill_require_db.txt
index b71a28f5c..518a35383 100644
--- a/db/skill_require_db.txt
+++ b/db/skill_require_db.txt
@@ -391,7 +391,7 @@
499,0,0,8,0,0,0,11,1,1,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //HT_POWER#ビ?[ストストレイピング#
//temp plugs
500,0,0,10,0,0,0,99,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //GS_GLITTERING
-501,0,0,10,0,0,0,17:18:19:20:21,0,0,none,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //GS_FLING
+501,0,0,10,0,0,0,17:18:19:20:21,0,0,none,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //GS_FLING
502,0,0,10,0,0,0,17:18:19:20:21,3:4:5,1,none,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //GS_TRIPLEACTION
503,0,0,10,0,0,0,17:18:19:20:21,3:4:5,1,none,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //GS_BULLSEYE
504,0,0,10,0,0,0,17:18:19:20:21,0,0,none,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 //GS_MADNESSCANCEL
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_);
}