summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/Makefile.in4
-rw-r--r--src/map/atcommand.c3
-rw-r--r--src/map/battle.c252
-rw-r--r--src/map/chrif.c3
-rw-r--r--src/map/clif.c305
-rw-r--r--src/map/clif.h39
-rw-r--r--src/map/elemental.c825
-rw-r--r--src/map/elemental.h95
-rw-r--r--src/map/intif.c97
-rw-r--r--src/map/intif.h6
-rw-r--r--src/map/itemdb.h1
-rw-r--r--src/map/map.c11
-rw-r--r--src/map/map.h6
-rw-r--r--src/map/mob.c10
-rw-r--r--src/map/npc_chat.c2
-rw-r--r--src/map/pc.c11
-rw-r--r--src/map/pc.h3
-rw-r--r--src/map/script.c2
-rw-r--r--src/map/skill.c2245
-rw-r--r--src/map/skill.h91
-rw-r--r--src/map/status.c477
-rw-r--r--src/map/status.h22
-rw-r--r--src/map/unit.c287
23 files changed, 3625 insertions, 1172 deletions
diff --git a/src/map/Makefile.in b/src/map/Makefile.in
index 0b5ba023d..beeab32dd 100644
--- a/src/map/Makefile.in
+++ b/src/map/Makefile.in
@@ -31,7 +31,7 @@ MAP_OBJ = map.o chrif.o clif.o pc.o status.o npc.o \
storage.o skill.o atcommand.o battle.o battleground.o \
intif.o trade.o party.o vending.o guild.o pet.o \
log.o mail.o date.o unit.o homunculus.o mercenary.o quest.o instance.o \
- buyingstore.o searchstore.o duel.o pc_groups.o
+ buyingstore.o searchstore.o duel.o pc_groups.o elemental.o
MAP_SQL_OBJ = $(MAP_OBJ:%=obj_sql/%) \
obj_sql/mapreg_sql.o
MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \
@@ -41,7 +41,7 @@ MAP_H = map.h chrif.h clif.h pc.h status.h npc.h \
log.h mail.h date.h unit.h homunculus.h mercenary.h quest.h instance.h mapreg.h \
buyingstore.h searchstore.h duel.h pc_groups.h \
config/core.h config/renewal.h config/secure.h config/const.h \
- config/classes/general.h config/classes/swordsman.h
+ config/classes/general.h config/classes/swordsman.h elemental.h
HAVE_MYSQL=@HAVE_MYSQL@
ifeq ($(HAVE_MYSQL),yes)
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index 8a918633a..c19164d1a 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -34,6 +34,7 @@
#include "homunculus.h"
#include "mail.h"
#include "mercenary.h"
+#include "elemental.h"
#include "party.h"
#include "guild.h"
#include "script.h"
@@ -3764,6 +3765,7 @@ ACMD_FUNC(reloadmobdb)
read_petdb();
merc_reload();
read_mercenarydb();
+ reload_elementaldb();
clif_displaymessage(fd, msg_txt(98)); // Monster database has been reloaded.
return 0;
@@ -3777,6 +3779,7 @@ ACMD_FUNC(reloadskilldb)
nullpo_retr(-1, sd);
skill_reload();
merc_skill_reload();
+ reload_elemental_skilldb();
read_mercenary_skilldb();
clif_displaymessage(fd, msg_txt(99)); // Skill database has been reloaded.
diff --git a/src/map/battle.c b/src/map/battle.c
index 48f8e1cab..cea562661 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -18,6 +18,7 @@
#include "skill.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "mob.h"
#include "itemdb.h"
#include "clif.h"
@@ -103,6 +104,7 @@ int battle_gettarget(struct block_list* bl)
case BL_PET: return ((struct pet_data*)bl)->target_id;
case BL_HOM: return ((struct homun_data*)bl)->ud.target;
case BL_MER: return ((struct mercenary_data*)bl)->ud.target;
+ case BL_ELEM: return ((struct elemental_data*)bl)->ud.target;
}
return 0;
}
@@ -299,8 +301,7 @@ int battle_attr_fix(struct block_list *src, struct block_list *target, int damag
}
ratio = attr_fix_table[def_lv-1][atk_elem][def_type];
- if (sc && sc->count)
- {
+ if (sc && sc->count) {
if(sc->data[SC_VOLCANO] && atk_elem == ELE_FIRE)
ratio += enchant_eff[sc->data[SC_VOLCANO]->val1-1];
if(sc->data[SC_VIOLENTGALE] && atk_elem == ELE_WIND)
@@ -308,8 +309,27 @@ int battle_attr_fix(struct block_list *src, struct block_list *target, int damag
if(sc->data[SC_DELUGE] && atk_elem == ELE_WATER)
ratio += enchant_eff[sc->data[SC_DELUGE]->val1-1];
}
- if( atk_elem == ELE_FIRE && tsc && tsc->count && tsc->data[SC_SPIDERWEB] )
- {
+ if( target && target->type == BL_SKILL ) {
+ if( atk_elem == ELE_FIRE && battle_getcurrentskill(target) == GN_WALLOFTHORN ) {
+ struct skill_unit *su = (struct skill_unit*)target;
+ struct skill_unit_group *sg;
+ struct block_list *src;
+ int x,y;
+
+ if( !su || !su->alive || (sg = su->group) == NULL || !sg || sg->val3 == -1 ||
+ (src = map_id2bl(su->val2)) == NULL || status_isdead(src) )
+ return 0;
+
+ if( sg->unit_id != UNT_FIREWALL ) {
+ x = sg->val3 >> 16;
+ y = sg->val3 & 0xffff;
+ skill_unitsetting(src,su->group->skill_id,su->group->skill_lv,x,y,1);
+ sg->val3 = -1;
+ sg->limit = DIFF_TICK(gettick(),sg->tick)+300;
+ }
+ }
+ }
+ if( atk_elem == ELE_FIRE && tsc && tsc->count && tsc->data[SC_SPIDERWEB] ){
tsc->data[SC_SPIDERWEB]->val1 = 0; // free to move now
if( tsc->data[SC_SPIDERWEB]->val2-- > 0 )
damage <<= 1; // double damage
@@ -932,6 +952,8 @@ int battle_addmastery(struct map_session_data *sd,struct block_list *target,int
case W_DAGGER:
if((skill = pc_checkskill(sd,SM_SWORD)) > 0)
damage += (skill * 4);
+ if((skill = pc_checkskill(sd,GN_TRAINING_SWORD)) > 0)
+ damage += skill * 10;
break;
case W_2HSWORD:
#ifdef RENEWAL
@@ -1486,15 +1508,27 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
if(sd && pc_checkskill(sd,AS_SONICACCEL)>0)
hitrate += hitrate * 50 / 100;
break;
+ case MC_CARTREVOLUTION:
+ case GN_CART_TORNADO:
+ case GN_CARTCANNON:
+ if( sd && pc_checkskill(sd, GN_REMODELING_CART) )
+ hitrate += pc_checkskill(sd, GN_REMODELING_CART) * 4;
+ break;
case GC_VENOMPRESSURE:
hitrate += 10 + 4 * skill_lv;
break;
}
- // Weaponry Research hidden bonus
- if (sd && (skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
- hitrate += hitrate * ( 2 * skill ) / 100;
-
+ if( sd ) {
+ // Weaponry Research hidden bonus
+ if ((skill = pc_checkskill(sd,BS_WEAPONRESEARCH)) > 0)
+ hitrate += hitrate * ( 2 * skill ) / 100;
+
+ if( (sd->status.weapon == W_1HSWORD || sd->status.weapon == W_DAGGER) &&
+ (skill = pc_checkskill(sd, GN_TRAINING_SWORD))>0 )
+ hitrate += 3 * skill;
+ }
+
hitrate = cap_value(hitrate, battle_config.min_hitrate, battle_config.max_hitrate);
if(rnd()%100 >= hitrate)
@@ -2238,6 +2272,76 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
case WM_SOUND_OF_DESTRUCTION:
skillratio += 400;
break;
+ case GN_CART_TORNADO:
+ if( sd )
+ skillratio += 50 * skill_lv + pc_checkskill(sd, GN_REMODELING_CART) * 100 - 100;
+ if( status_get_lv(src) > 100 ) skillratio += skillratio * (status_get_lv(src) - 100) / 200; // Base level bonus.
+ if( sc && sc->data[SC_GN_CARTBOOST] )
+ skillratio += 10 * sc->data[SC_GN_CARTBOOST]->val1;
+ break;
+ case GN_CARTCANNON:
+ if( sd ) skillratio += 250 + 50 * skill_lv + pc_checkskill(sd, GN_REMODELING_CART) * (sstatus->int_ / 2);
+ if( sc && sc->data[SC_GN_CARTBOOST] )
+ skillratio += 10 * sc->data[SC_GN_CARTBOOST]->val1;
+ break;
+ case GN_SPORE_EXPLOSION:
+ skillratio += 200 + 100 * skill_lv;
+ break;
+ case GN_CRAZYWEED_ATK:
+ skillratio += 400 + 100 * skill_lv;
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ if( sd ) {
+ switch( sd->itemid ) {
+ case 13260: // Apple Bomob
+ case 13261: // Coconut Bomb
+ case 13262: // Melon Bomb
+ case 13263: // Pinapple Bomb
+ skillratio += 400; // Unconfirded
+ break;
+ case 13264: // Banana Bomb 2000%
+ skillratio += 1900;
+ break;
+ case 13265: skillratio -= 75; break; // Black Lump 25%
+ case 13266: skillratio -= 25; break; // Hard Black Lump 75%
+ case 13267: skillratio += 100; break; // Extremely Hard Black Lump 200%
+ }
+ } else
+ skillratio += 300; // Bombs
+ break;
+ case SO_VARETYR_SPEAR: //Assumed Formula.
+ skillratio += -100 + 200 * ( sd ? pc_checkskill(sd, SA_LIGHTNINGLOADER) : 1 );
+ if( sc && sc->data[SC_BLAST_OPTION] )
+ skillratio += skillratio * sc->data[SC_BLAST_OPTION]->val2 / 100;
+ break;
+ // Physical Elemantal Spirits Attack Skills
+ case EL_CIRCLE_OF_FIRE:
+ case EL_FIRE_BOMB_ATK:
+ case EL_STONE_RAIN:
+ skillratio += 200;
+ break;
+ case EL_FIRE_WAVE_ATK:
+ skillratio += 500;
+ break;
+ case EL_TIDAL_WEAPON:
+ skillratio += 1400;
+ break;
+ case EL_WIND_SLASH:
+ skillratio += 100;
+ break;
+ case EL_HURRICANE:
+ skillratio += 600;
+ break;
+ case EL_TYPOON_MIS:
+ case EL_WATER_SCREW_ATK:
+ skillratio += 900;
+ break;
+ case EL_STONE_HAMMER:
+ skillratio += 400;
+ break;
+ case EL_ROCK_CRUSHER:
+ skillratio += 700;
+ break;
}
ATK_RATE(skillratio);
@@ -2920,7 +3024,7 @@ static struct Damage battle_calc_weapon_attack(struct block_list *src,struct blo
struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list *target,int skill_num,int skill_lv,int mflag)
{
int i, nk;
- short s_ele;
+ short s_ele = 0;
unsigned int skillratio = 100; //Skill dmg modifiers.
struct map_session_data *sd, *tsd;
@@ -2954,16 +3058,38 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
sd = BL_CAST(BL_PC, src);
tsd = BL_CAST(BL_PC, target);
- //Initialize variables that will be used afterwards
- s_ele = skill_get_ele(skill_num, skill_lv);
-
- 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));
- else if( s_ele == -3 ) //Use random element
- s_ele = rnd()%ELE_MAX;
-
+ if( skill_num == SO_PSYCHIC_WAVE ) {
+ struct status_change *sc = status_get_sc(src);
+ if( sc && sc->count && ( sc->data[SC_HEATER_OPTION] || sc->data[SC_COOLER_OPTION] ||
+ sc->data[SC_BLAST_OPTION] || sc->data[SC_CURSED_SOIL_OPTION] ) ) {
+ if( sc->data[SC_HEATER_OPTION] ) s_ele = sc->data[SC_HEATER_OPTION]->val4;
+ else if( sc->data[SC_COOLER_OPTION] ) s_ele = sc->data[SC_COOLER_OPTION]->val4;
+ else if( sc->data[SC_BLAST_OPTION] ) s_ele = sc->data[SC_BLAST_OPTION]->val3;
+ else if( sc->data[SC_CURSED_SOIL_OPTION] ) s_ele = sc->data[SC_CURSED_SOIL_OPTION]->val4;
+ } else {
+ //#HALP# I didn't get a clue on how to do this without unnecessary adding a overhead of status_change on every call while this is a per-skill case.
+ //, - so i duplicated this code. make yourself comfortable to fix if you have any better ideas.
+ //Initialize variables that will be used afterwards
+ s_ele = skill_get_ele(skill_num, skill_lv);
+
+ 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));
+ else if( s_ele == -3 ) //Use random element
+ s_ele = rnd()%ELE_MAX;
+ }
+ } else {
+ //Initialize variables that will be used afterwards
+ s_ele = skill_get_ele(skill_num, skill_lv);
+
+ 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));
+ else if( s_ele == -3 ) //Use random element
+ s_ele = rnd()%ELE_MAX;
+ }
//Set miscellaneous data that needs be filled
if(sd) {
sd->state.arrow_atk = 0;
@@ -3334,6 +3460,24 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
else
skillratio += 110 + 20 * skill_lv;
break;
+ // Magical Elemental Spirits Attack Skills
+ case EL_FIRE_MANTLE:
+ case EL_WATER_SCREW:
+ skillratio += 900;
+ break;
+ case EL_FIRE_ARROW:
+ case EL_ROCK_CRUSHER_ATK:
+ skillratio += 200;
+ break;
+ case EL_FIRE_BOMB:
+ case EL_ICE_NEEDLE:
+ case EL_HURRICANE_ATK:
+ skillratio += 400;
+ break;
+ case EL_FIRE_WAVE:
+ case EL_TYPOON_MIS_ATK:
+ skillratio += 1100;
+ break;
}
@@ -3493,6 +3637,13 @@ struct Damage battle_calc_magic_attack(struct block_list *src,struct block_list
ad.damage=battle_calc_gvg_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag);
else if( map[target->m].flag.battleground )
ad.damage=battle_calc_bg_damage(src,target,ad.damage,ad.div_,skill_num,skill_lv,ad.flag);
+
+
+ if( skill_num == SO_VARETYR_SPEAR ) { // Physical damage.
+ struct Damage wd = battle_calc_weapon_attack(src,target,skill_num,skill_lv,mflag);
+ ad.damage += wd.damage;
+ }
+
return ad;
}
@@ -3675,7 +3826,15 @@ struct Damage battle_calc_misc_attack(struct block_list *src,struct block_list *
if (sd) md.damage = md.damage + status_get_hp(src);
status_set_sp(src, 0, 0);
break;
-
+ case GN_THORNS_TRAP:
+ md.damage = 100 + 200 * skill_lv + sstatus->int_;
+ break;
+ case GN_BLOOD_SUCKER:
+ md.damage = 200 + 100 * skill_lv + sstatus->int_;
+ break;
+ case GN_HELLS_PLANT_ATK:
+ md.damage = sstatus->int_ * 4 * skill_lv * (10 / (10 - pc_checkskill(sd,AM_CANNIBALIZE)));//Need accurate official formula. [Rytech]
+ break;
}
if (nk&NK_SPLASHSPLIT){ // Divide ATK among targets
@@ -4168,24 +4327,38 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
map_freeblock_lock();
battle_delay_damage(tick, wd.amotion, src, target, wd.flag, 0, 0, damage, wd.dmg_lv, wd.dmotion);
-
- if( tsc && tsc->data[SC_DEVOTION] )
- {
- struct status_change_entry *sce = tsc->data[SC_DEVOTION];
- struct block_list *d_bl = map_id2bl(sce->val1);
-
- if( d_bl && (
- (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == target->id) ||
- (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == target->id)
- ) && check_distance_bl(target, d_bl, sce->val3) )
- {
- clif_damage(d_bl, d_bl, gettick(), 0, 0, damage, 0, 0, 0);
- status_fix_damage(NULL, d_bl, damage, 0);
- }
- else
- status_change_end(target, SC_DEVOTION, INVALID_TIMER);
+ if( tsc ) {
+ if( tsc->data[SC_DEVOTION] ) {
+ struct status_change_entry *sce = tsc->data[SC_DEVOTION];
+ struct block_list *d_bl = map_id2bl(sce->val1);
+
+ if( d_bl && (
+ (d_bl->type == BL_MER && ((TBL_MER*)d_bl)->master && ((TBL_MER*)d_bl)->master->bl.id == target->id) ||
+ (d_bl->type == BL_PC && ((TBL_PC*)d_bl)->devotion[sce->val2] == target->id)
+ ) && check_distance_bl(target, d_bl, sce->val3) )
+ {
+ clif_damage(d_bl, d_bl, gettick(), 0, 0, damage, 0, 0, 0);
+ status_fix_damage(NULL, d_bl, damage, 0);
+ }
+ else
+ status_change_end(target, SC_DEVOTION, INVALID_TIMER);
+ } else if( tsc->data[SC_CIRCLE_OF_FIRE_OPTION] && (wd.flag&BF_SHORT) && target->type == BL_PC ) {
+ struct elemental_data *ed = ((TBL_PC*)target)->ed;
+ if( ed ) {
+ clif_skill_damage(&ed->bl, target, tick, status_get_amotion(src), 0, -30000, 1, EL_CIRCLE_OF_FIRE, tsc->data[SC_CIRCLE_OF_FIRE_OPTION]->val1, 6);
+ skill_attack(BF_MAGIC,&ed->bl,&ed->bl,src,EL_CIRCLE_OF_FIRE,tsc->data[SC_CIRCLE_OF_FIRE_OPTION]->val1,tick,wd.flag);
+ }
+ } else if( tsc->data[SC_WATER_SCREEN_OPTION] && tsc->data[SC_WATER_SCREEN_OPTION]->val1 ) {
+ struct block_list *e_bl = map_id2bl(tsc->data[SC_WATER_SCREEN_OPTION]->val1);
+ if( e_bl && !status_isdead(e_bl) ) {
+ clif_damage(e_bl,e_bl,tick,wd.amotion,wd.dmotion,damage,wd.div_,wd.type,wd.damage2);
+ status_damage(target,e_bl,damage,0,0,0);
+ // Just show damage in target.
+ clif_damage(src, target, tick, wd.amotion, wd.dmotion, damage, wd.div_, wd.type, wd.damage2 );
+ return ATK_NONE;
+ }
+ }
}
-
if (sc && sc->data[SC_AUTOSPELL] && rnd()%100 < sc->data[SC_AUTOSPELL]->val4) {
int sp = 0;
int skillid = sc->data[SC_AUTOSPELL]->val2;
@@ -4314,6 +4487,10 @@ struct block_list* battle_get_master(struct block_list *src)
if (((TBL_MER*)src)->master)
src = (struct block_list*)((TBL_MER*)src)->master;
break;
+ case BL_ELEM:
+ if (((TBL_ELEM*)src)->master)
+ src = (struct block_list*)((TBL_ELEM*)src)->master;
+ break;
case BL_SKILL:
if (((TBL_SKILL*)src)->group && ((TBL_SKILL*)src)->group->src_id)
src = map_id2bl(((TBL_SKILL*)src)->group->src_id);
@@ -4409,6 +4586,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
//Valid targets with no special checks here.
case BL_MER:
case BL_HOM:
+ case BL_ELEM:
break;
//All else not specified is an invalid target.
default:
diff --git a/src/map/chrif.c b/src/map/chrif.c
index c4ff4b662..095129e80 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -22,6 +22,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "chrif.h"
#include "quest.h"
#include "storage.h"
@@ -307,6 +308,8 @@ int chrif_save(struct map_session_data *sd, int flag)
merc_save(sd->hd);
if( sd->md && mercenary_get_lifetime(sd->md) > 0 )
mercenary_save(sd->md);
+ if( sd->ed && elemental_get_lifetime(sd->ed) > 0 )
+ elemental_save(sd->ed);
if( sd->save_quest )
intif_quest_save(sd);
diff --git a/src/map/clif.c b/src/map/clif.c
index 3533c7eff..bfe78a58d 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -36,6 +36,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "log.h"
#include "clif.h"
#include "mail.h"
@@ -273,7 +274,7 @@ static inline unsigned char clif_bl_type(struct block_list *bl) {
case BL_PET: return pcdb_checkid(status_get_viewdata(bl)->class_)?0x0:0x7; //NPC_PET_TYPE
case BL_HOM: return 0x8; //NPC_HOM_TYPE
case BL_MER: return 0x9; //NPC_MERSOL_TYPE
-// case BL_ELEM: return 0xA; //NPC_ELEMENTAL_TYPE
+ case BL_ELEM: return 0xa; //NPC_ELEMENTAL_TYPE
default: return 0x1; //NPC_TYPE
}
}
@@ -5193,44 +5194,60 @@ void clif_skill_produce_mix_list(struct map_session_data *sd, int skillid , int
/// 4 = GN_MIX_COOKING
/// 5 = GN_MAKEBOMB
/// 6 = GN_S_PHARMACY
-void clif_cooking_list(struct map_session_data *sd, int trigger)
+void clif_cooking_list(struct map_session_data *sd, int trigger, int skill_id, int qty, int list_type)
{
int fd;
int i, c;
int view;
-
+
nullpo_retv(sd);
fd = sd->fd;
-
- WFIFOHEAD(fd, 6 + 2*MAX_SKILL_PRODUCE_DB);
+
+ WFIFOHEAD(fd, 6 + 2 * MAX_SKILL_PRODUCE_DB);
WFIFOW(fd,0) = 0x25a;
- WFIFOW(fd,4) = 1; // list type
-
+ WFIFOW(fd,4) = list_type; // list type
+
c = 0;
- for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ )
- {
- if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, 1) )
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( !skill_can_produce_mix(sd,skill_produce_db[i].nameid,trigger, qty) )
continue;
-
+
if( (view = itemdb_viewid(skill_produce_db[i].nameid)) > 0 )
- WFIFOW(fd, 6+2*c)= view;
+ WFIFOW(fd, 6 + 2 * c) = view;
else
- WFIFOW(fd, 6+2*c)= skill_produce_db[i].nameid;
-
+ WFIFOW(fd, 6 + 2 * c) = skill_produce_db[i].nameid;
+
c++;
}
-
- WFIFOW(fd,2) = 6 + 2*c;
- WFIFOSET(fd,WFIFOW(fd,2));
-
- //TODO: replace with proper solution
- if( c > 0 )
- {
- sd->menuskill_id = AM_PHARMACY;
+
+ if( skill_id == AM_PHARMACY ) { // Only send it while Cooking else check for c.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+
+ if( c > 0 ) {
+ sd->menuskill_id = skill_id;
sd->menuskill_val = trigger;
+ if( skill_id != AM_PHARMACY ) {
+ sd->menuskill_val2 = qty; // amount.
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+ }
+ } else {
+ clif_menuskill_clear(sd);
+ if( skill_id != AM_PHARMACY ) { // AM_PHARMACY is used to Cooking.
+ // It fails.
+#if PACKETVER >= 20090922
+ clif_msg_skill(sd,skill_id,0x625);
+#else
+ WFIFOW(fd,2) = 6 + 2 * c;
+ WFIFOSET(fd,WFIFOW(fd,2));
+#endif
+ }
}
}
+
/// Notifies clients of a status change.
/// 0196 <index>.W <id>.L <state>.B (ZC_MSG_STATE_CHANGE) [used for ending status changes and starting them on non-pc units (when needed)]
/// 043f <index>.W <id>.L <state>.B <remain msec>.L { <val>.L }*3 (ZC_MSG_STATE_CHANGE2) [used exclusively for starting statuses on pcs]
@@ -8107,11 +8124,12 @@ void clif_refresh(struct map_session_data *sd)
clif_refreshlook(&sd->bl,sd->bl.id,LOOK_CLOTHES_COLOR,sd->vd.cloth_color,SELF);
if(merc_is_hom_active(sd->hd))
clif_send_homdata(sd,SP_ACK,0);
- if( sd->md )
- {
+ if( sd->md ) {
clif_mercenary_info(sd);
clif_mercenary_skillblock(sd);
}
+ if( sd->ed )
+ clif_elemental_info(sd);
map_foreachinrange(clif_getareachar,&sd->bl,AREA_SIZE,BL_ALL,sd);
clif_weather_check(sd);
if( sd->chatID )
@@ -8255,6 +8273,9 @@ void clif_charnameack (int fd, struct block_list *bl)
// memcpy(WBUFP(buf,6), (struct chat*)->title, NAME_LENGTH);
// break;
return;
+ case BL_ELEM:
+ memcpy(WBUFP(buf,6), ((TBL_ELEM*)bl)->db->name, NAME_LENGTH);
+ break;
default:
ShowError("clif_charnameack: bad type %d(%d)\n", bl->type, bl->id);
return;
@@ -9082,14 +9103,22 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd)
skill_unit_move(&sd->hd->bl,gettick(),1); // apply land skills immediately
}
- if( sd->md )
- {
+ if( sd->md ) {
map_addblock(&sd->md->bl);
clif_spawn(&sd->md->bl);
clif_mercenary_info(sd);
clif_mercenary_skillblock(sd);
}
+ if( sd->ed ) {
+ map_addblock(&sd->ed->bl);
+ clif_spawn(&sd->ed->bl);
+ clif_elemental_info(sd);
+ clif_elemental_updatestatus(sd,SP_HP);
+ clif_hpmeter_single(sd->fd,sd->ed->bl.id,sd->ed->battle_status.hp,sd->ed->battle_status.matk_max);
+ clif_elemental_updatestatus(sd,SP_SP);
+ }
+
if(sd->state.connect_new) {
int lv;
sd->state.connect_new = 0;
@@ -10587,15 +10616,13 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
if( sd->sc.data[SC_BASILICA] && (skillnum != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
return; // On basilica only caster can use Basilica again to stop it.
- if( sd->menuskill_id )
- {
- if( sd->menuskill_id == SA_TAMINGMONSTER )
- sd->menuskill_id = sd->menuskill_val = 0; //Cancel pet capture.
- else if( sd->menuskill_id != SA_AUTOSPELL )
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
return; //Can't use skills while a menu is open.
}
- if( sd->skillitem == skillnum )
- {
+ if( sd->skillitem == skillnum ) {
if( skilllv != sd->skillitemlv )
skilllv = sd->skillitemlv;
if( !(tmp&INF_SELF_SKILL) )
@@ -10606,15 +10633,12 @@ void clif_parse_UseSkillToId(int fd, struct map_session_data *sd)
sd->skillitem = sd->skillitemlv = 0;
- if( skillnum >= GD_SKILLBASE )
- {
+ if( skillnum >= GD_SKILLBASE ) {
if( sd->state.gmaster_flag )
skilllv = guild_checkskill(sd->state.gmaster_flag, skillnum);
else
skilllv = 0;
- }
- else
- {
+ } else {
tmp = pc_checkskill(sd, skillnum);
if( skilllv > tmp )
skilllv = tmp;
@@ -10661,10 +10685,8 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, sho
if( sd->ud.skilltimer != INVALID_TIMER )
return;
- if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 )
- {
- if( sd->skillitem != skillnum )
- {
+ if( DIFF_TICK(tick, sd->ud.canact_tick) < 0 ) {
+ if( sd->skillitem != skillnum ) {
clif_skill_fail(sd, skillnum, USESKILL_FAIL_SKILLINTERVAL, 0);
return;
}
@@ -10676,28 +10698,23 @@ static void clif_parse_UseSkillToPosSub(int fd, struct map_session_data *sd, sho
if( sd->sc.data[SC_BASILICA] && (skillnum != HP_BASILICA || sd->sc.data[SC_BASILICA]->val4 != sd->bl.id) )
return; // On basilica only caster can use Basilica again to stop it.
- if( sd->menuskill_id )
- {
- if( sd->menuskill_id == SA_TAMINGMONSTER )
- sd->menuskill_id = sd->menuskill_val = 0; //Cancel pet capture.
- else if( sd->menuskill_id != SA_AUTOSPELL )
+ if( sd->menuskill_id ) {
+ if( sd->menuskill_id == SA_TAMINGMONSTER ) {
+ clif_menuskill_clear(sd); //Cancel pet capture.
+ } else if( sd->menuskill_id != SA_AUTOSPELL )
return; //Can't use skills while a menu is open.
}
pc_delinvincibletimer(sd);
- if( sd->skillitem == skillnum )
- {
+ if( sd->skillitem == skillnum ) {
if( skilllv != sd->skillitemlv )
skilllv = sd->skillitemlv;
unit_skilluse_pos(&sd->bl, x, y, skillnum, skilllv);
- }
- else
- {
+ } else {
int lv;
sd->skillitem = sd->skillitemlv = 0;
- if( (lv = pc_checkskill(sd, skillnum)) > 0 )
- {
+ if( (lv = pc_checkskill(sd, skillnum)) > 0 ) {
if( skilllv > lv )
skilllv = lv;
unit_skilluse_pos(&sd->bl, x, y, skillnum,skilllv);
@@ -10759,9 +10776,8 @@ void clif_parse_UseSkillMap(int fd, struct map_session_data* sd)
if(skill_num != sd->menuskill_id)
return;
- if( pc_cant_act(sd) )
- {
- sd->menuskill_id = sd->menuskill_val = 0;
+ if( pc_cant_act(sd) ) {
+ clif_menuskill_clear(sd);
return;
}
@@ -10795,12 +10811,12 @@ void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
if (pc_istrading(sd)) {
//Make it fail to avoid shop exploits where you sell something different than you see.
clif_skill_fail(sd,sd->ud.skillid,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
if( skill_can_produce_mix(sd,RFIFOW(fd,2),sd->menuskill_val, 1) )
skill_produce_mix(sd,0,RFIFOW(fd,2),RFIFOW(fd,4),RFIFOW(fd,6),RFIFOW(fd,8), 1);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10813,24 +10829,22 @@ void clif_parse_ProduceMix(int fd,struct map_session_data *sd)
/// 4 = GN_MIX_COOKING
/// 5 = GN_MAKEBOMB
/// 6 = GN_S_PHARMACY
-void clif_parse_Cooking(int fd,struct map_session_data *sd)
-{
- //int type = RFIFOW(fd,2);
+void clif_parse_Cooking(int fd,struct map_session_data *sd) {
+ int type = RFIFOW(fd,2);
int nameid = RFIFOW(fd,4);
-
- if( sd->menuskill_id != AM_PHARMACY ) {
+ int amount = sd->menuskill_val2?sd->menuskill_val2:1;
+ if( type == 6 && sd->menuskill_id != GN_MIX_COOKING && sd->menuskill_id != GN_S_PHARMACY )
return;
- }
-
+
if (pc_istrading(sd)) {
//Make it fail to avoid shop exploits where you sell something different than you see.
clif_skill_fail(sd,sd->ud.skillid,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
- if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, 1) )
- skill_produce_mix(sd,0,nameid,0,0,0,1);
- sd->menuskill_val = sd->menuskill_id = 0;
+ if( skill_can_produce_mix(sd,nameid,sd->menuskill_val, amount) )
+ skill_produce_mix(sd,sd->menuskill_id,nameid,0,0,0,amount);
+ clif_menuskill_clear(sd);
}
@@ -10843,11 +10857,11 @@ void clif_parse_RepairItem(int fd, struct map_session_data *sd)
if (pc_istrading(sd)) {
//Make it fail to avoid shop exploits where you sell something different than you see.
clif_skill_fail(sd,sd->ud.skillid,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
skill_repairweapon(sd,RFIFOW(fd,2));
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10862,12 +10876,12 @@ void clif_parse_WeaponRefine(int fd, struct map_session_data *sd)
if (pc_istrading(sd)) {
//Make it fail to avoid shop exploits where you sell something different than you see.
clif_skill_fail(sd,sd->ud.skillid,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
idx = RFIFOL(fd,packet_db[sd->packet_ver][RFIFOW(fd,0)].pos[0]);
skill_weaponrefine(sd, idx-2);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10952,13 +10966,12 @@ void clif_parse_ItemIdentify(int fd,struct map_session_data *sd)
if (sd->menuskill_id != MC_IDENTIFY)
return;
- if( idx == -1 )
- {// cancel pressed
- sd->menuskill_val = sd->menuskill_id = 0;
+ if( idx == -1 ) {// cancel pressed
+ clif_menuskill_clear(sd);
return;
}
skill_identify(sd,idx-2);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -10969,7 +10982,7 @@ void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
if (pc_istrading(sd)) {
//Make it fail to avoid shop exploits where you sell something different than you see.
clif_skill_fail(sd,sd->ud.skillid,USESKILL_FAIL_LEVEL,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
switch( sd->menuskill_id ) {
@@ -10990,7 +11003,7 @@ void clif_parse_SelectArrow(int fd,struct map_session_data *sd)
break;
}
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -11001,7 +11014,7 @@ void clif_parse_AutoSpell(int fd,struct map_session_data *sd)
if (sd->menuskill_id != SA_AUTOSPELL)
return;
skill_autospell(sd,RFIFOL(fd,2));
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -12074,7 +12087,7 @@ void clif_parse_SelectEgg(int fd, struct map_session_data *sd)
return;
}
pet_select_egg(sd,RFIFOW(fd,2)-2);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -13038,7 +13051,7 @@ void clif_parse_FeelSaveOk(int fd,struct map_session_data *sd)
// clif_misceffect2(&sd->bl, 0x1b0);
// clif_misceffect2(&sd->bl, 0x21f);
clif_feel_info(sd, i, 0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
@@ -15051,6 +15064,94 @@ void clif_parse_LessEffect(int fd, struct map_session_data* sd)
sd->state.lesseffect = ( isLess != 0 );
}
+/// S 07e4 <length>.w <option>.l <val>.l {<index>.w <amount>.w).4b*
+void clif_parse_ItemListWindowSelected(int fd, struct map_session_data* sd) {
+ int n = (RFIFOW(fd,2)-12) / 4;
+ int type = RFIFOL(fd,4);
+ int flag = RFIFOL(fd,8); // Button clicked: 0 = Cancel, 1 = OK
+ unsigned short* item_list = (unsigned short*)RFIFOP(fd,12);
+
+ if( sd->state.trading || sd->npc_shopid )
+ return;
+
+ if( flag == 0 || n == 0) {
+ clif_menuskill_clear(sd);
+ return; // Canceled by player.
+ }
+
+ if( sd->menuskill_id != SO_EL_ANALYSIS && sd->menuskill_id != GN_CHANGEMATERIAL ) {
+ clif_menuskill_clear(sd);
+ return; // Prevent hacking.
+ }
+
+ switch( type ) {
+ case 0: // Change Material
+ skill_changematerial(sd,n,item_list);
+ break;
+ case 1: // Level 1: Pure to Rough
+ case 2: // Level 2: Rough to Pure
+ skill_elementalanalysis(sd,n,type,item_list);
+ break;
+ }
+ clif_menuskill_clear(sd);
+
+ return;
+}
+
+/*==========================================
+ * Elemental System
+ *==========================================*/
+void clif_elemental_updatestatus(struct map_session_data *sd, int type) {
+ struct elemental_data *ed;
+ struct status_data *status;
+ int fd;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+ WFIFOHEAD(fd,8);
+ WFIFOW(fd,0) = 0x81e;
+ WFIFOW(fd,2) = type;
+ switch( type ) {
+ case SP_HP:
+ WFIFOL(fd,4) = status->hp;
+ break;
+ case SP_MAXHP:
+ WFIFOL(fd,4) = status->max_hp;
+ break;
+ case SP_SP:
+ WFIFOL(fd,4) = status->sp;
+ break;
+ case SP_MAXSP:
+ WFIFOL(fd,4) = status->max_sp;
+ break;
+ }
+ WFIFOSET(fd,8);
+}
+
+void clif_elemental_info(struct map_session_data *sd) {
+ int fd;
+ struct elemental_data *ed;
+ struct status_data *status;
+
+ if( sd == NULL || (ed = sd->ed) == NULL )
+ return;
+
+ fd = sd->fd;
+ status = &ed->battle_status;
+
+ WFIFOHEAD(fd,22);
+ WFIFOW(fd, 0) = 0x81d;
+ WFIFOL(fd, 2) = ed->bl.id;
+ WFIFOL(fd, 6) = status->hp;
+ WFIFOL(fd,10) = status->max_hp;
+ WFIFOL(fd,14) = status->sp;
+ WFIFOL(fd,18) = status->max_sp;
+ WFIFOSET(fd,22);
+}
+
/// Buying Store System
///
@@ -15762,6 +15863,35 @@ int clif_autoshadowspell_list(struct map_session_data *sd) {
return 1;
}
+/*===========================================
+ * Skill list for Four Elemental Analysis
+ * and Change Material skills.
+ *------------------------------------------*/
+int clif_skill_itemlistwindow( struct map_session_data *sd, int skill_id, int skill_lv )
+{
+#if PACKETVER >= 20090922
+ int fd;
+
+ nullpo_ret(sd);
+
+ sd->menuskill_id = skill_id; // To prevent hacking.
+ sd->menuskill_val = skill_lv;
+
+ if( skill_id == GN_CHANGEMATERIAL )
+ skill_lv = 0; // Changematerial
+
+ fd = sd->fd;
+ WFIFOHEAD(fd,packet_len(0x7e3));
+ WFIFOW(fd,0) = 0x7e3;
+ WFIFOL(fd,2) = skill_lv;
+ WFIFOL(fd,4) = 0;
+ WFIFOSET(fd,packet_len(0x7e3));
+
+#endif
+
+ return 1;
+
+}
/**
* Sends a new status without a tick (currently used by the new mounts)
**/
@@ -15826,11 +15956,11 @@ void clif_parse_SkillSelectMenu(int fd, struct map_session_data *sd) {
if( pc_istrading(sd) ) {
clif_skill_fail(sd,sd->ud.skillid,0,0);
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
return;
}
skill_select_menu(sd,RFIFOL(fd,2),RFIFOW(fd,6));
- sd->menuskill_val = sd->menuskill_id = 0;
+ clif_menuskill_clear(sd);
}
/*==========================================
@@ -16202,13 +16332,13 @@ static int packetdb_readdb(void)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
//#0x07C0
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#if PACKETVER < 20090617
6, 2, -1, 4, 4, 4, 4, 8, 8,254, 6, 8, 6, 54, 30, 54,
#else // 0x7d9 changed
6, 2, -1, 4, 4, 4, 4, 8, 8,268, 6, 8, 6, 54, 30, 54,
#endif
- 0, 15, 8, 0, 0, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0,
+ 0, 15, 8, 6, -1, 8, 8, 32, -1, 5, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 14, -1, -1, -1, 8, 25, 0, 0, 26, 0,
//#0x0800
#if PACKETVER < 20091229
@@ -16406,6 +16536,7 @@ static int packetdb_readdb(void)
{clif_parse_mercenary_action,"mermenu"},
{clif_parse_progressbar,"progressbar"},
{clif_parse_SkillSelectMenu,"skillselectmenu"},
+ {clif_parse_ItemListWindowSelected,"itemlistwindowselected"},
#if PACKETVER >= 20091229
{clif_parse_PartyBookingRegisterReq,"bookingregreq"},
{clif_parse_PartyBookingSearchReq,"bookingsearchreq"},
diff --git a/src/map/clif.h b/src/map/clif.h
index 575798c6f..3dcab7379 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -429,7 +429,7 @@ void clif_skill_warppoint(struct map_session_data* sd, short skill_num, short sk
void clif_skill_memomessage(struct map_session_data* sd, int type);
void clif_skill_teleportmessage(struct map_session_data *sd, int type);
void clif_skill_produce_mix_list(struct map_session_data *sd, int skillid, int trigger);
-void clif_cooking_list(struct map_session_data *sd, int trigger);
+void clif_cooking_list(struct map_session_data *sd, int trigger, int skill_id, int qty, int list_type);
void clif_produceeffect(struct map_session_data* sd,int flag,int nameid);
@@ -714,35 +714,27 @@ void clif_search_store_info_click_ack(struct map_session_data* sd, short x, shor
**/
void clif_msgtable(int fd, int line);
void clif_msgtable_num(int fd, int line, int num);
-/**
- * Elemental Converter List
- **/
+
int clif_elementalconverter_list(struct map_session_data *sd);
-/**
- * Rune Knight
- **/
+
void clif_millenniumshield(struct map_session_data *sd, short shields );
-/**
- * Warlock
- **/
+
int clif_spellbook_list(struct map_session_data *sd);
-/**
- * Mechanic
- **/
+
int clif_magicdecoy_list(struct map_session_data *sd, int skill_lv, short x, short y);
-/**
- * Guilotine Cross
- **/
+
int clif_poison_list(struct map_session_data *sd, int skill_lv);
-/**
- * Shadow Chaser
- **/
+
int clif_autoshadowspell_list(struct map_session_data *sd);
-/**
- * New Mounts
- **/
+
int clif_status_load_notick(struct block_list *bl,int type,int flag,int val1, int val2, int val3);
int clif_status_load_single(int fd, int id,int type,int flag,int val1, int val2, int val3);
+
+
+int clif_skill_itemlistwindow( struct map_session_data *sd, int skill_id, int skill_lv );
+void clif_elemental_info(struct map_session_data *sd);
+void clif_elemental_updatestatus(struct map_session_data *sd, int type);
+
/**
* Color Table
**/
@@ -753,4 +745,7 @@ enum clif_colors {
};
unsigned long color_table[COLOR_MAX];
int clif_colormes(struct map_session_data * sd, enum clif_colors color, const char* msg);
+
+#define clif_menuskill_clear(sd) (sd)->menuskill_id = (sd)->menuskill_val = (sd)->menuskill_val2 = 0;
+
#endif /* _CLIF_H_ */
diff --git a/src/map/elemental.c b/src/map/elemental.c
new file mode 100644
index 000000000..fc8c17e0e
--- /dev/null
+++ b/src/map/elemental.c
@@ -0,0 +1,825 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#include "../common/cbasetypes.h"
+#include "../common/malloc.h"
+#include "../common/socket.h"
+#include "../common/timer.h"
+#include "../common/nullpo.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/utils.h"
+
+#include "log.h"
+#include "clif.h"
+#include "chrif.h"
+#include "intif.h"
+#include "itemdb.h"
+#include "map.h"
+#include "pc.h"
+#include "status.h"
+#include "skill.h"
+#include "mob.h"
+#include "pet.h"
+#include "battle.h"
+#include "party.h"
+#include "guild.h"
+#include "atcommand.h"
+#include "script.h"
+#include "npc.h"
+#include "trade.h"
+#include "unit.h"
+#include "elemental.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+struct s_elemental_db elemental_db[MAX_ELEMENTAL_CLASS]; // Elemental Database
+
+int elemental_search_index(int class_) {
+ int i;
+ ARR_FIND(0, MAX_ELEMENTAL_CLASS, i, elemental_db[i].class_ == class_);
+ return (i == MAX_ELEMENTAL_CLASS)?-1:i;
+}
+
+bool elemental_class(int class_) {
+ return (bool)(elemental_search_index(class_) > -1);
+}
+
+struct view_data * elemental_get_viewdata(int class_) {
+ int i = elemental_search_index(class_);
+ if( i < 0 )
+ return 0;
+
+ return &elemental_db[i].vd;
+}
+
+int elemental_create(struct map_session_data *sd, int class_, unsigned int lifetime) {
+ struct s_elemental ele;
+ struct s_elemental_db *db;
+ int i;
+
+ nullpo_retr(1,sd);
+
+ if( (i = elemental_search_index(class_)) < 0 )
+ return 0;
+
+ db = &elemental_db[i];
+ memset(&ele,0,sizeof(struct s_elemental));
+
+ ele.char_id = sd->status.char_id;
+ ele.class_ = class_;
+ ele.mode = EL_MODE_PASSIVE; // Initial mode
+ ele.hp = db->status.max_hp;
+ ele.sp = db->status.max_sp;
+ ele.life_time = lifetime;
+
+ // Request Char Server to create this elemental
+ intif_elemental_create(&ele);
+
+ return 1;
+}
+
+int elemental_get_lifetime(struct elemental_data *ed) {
+ const struct TimerData * td;
+ if( ed == NULL || ed->summon_timer == INVALID_TIMER )
+ return 0;
+
+ td = get_timer(ed->summon_timer);
+ return (td != NULL) ? DIFF_TICK(td->tick, gettick()) : 0;
+}
+
+int elemental_save(struct elemental_data *ed) {
+ ed->elemental.hp = ed->battle_status.hp;
+ ed->elemental.sp = ed->battle_status.sp;
+ ed->elemental.life_time = elemental_get_lifetime(ed);
+
+ intif_elemental_save(&ed->elemental);
+ return 1;
+}
+
+static int elemental_summon_end(int tid, unsigned int tick, int id, intptr data) {
+ struct map_session_data *sd;
+ struct elemental_data *ed;
+
+ if( (sd = map_id2sd(id)) == NULL )
+ return 1;
+ if( (ed = sd->ed) == NULL )
+ return 1;
+
+ if( ed->summon_timer != tid ) {
+ ShowError("elemental_summon_end %d != %d.\n", ed->summon_timer, tid);
+ return 0;
+ }
+
+ ed->summon_timer = INVALID_TIMER;
+ elemental_delete(ed, 0); // Elemental's summon time is over.
+
+ return 0;
+}
+
+void elemental_summon_stop(struct elemental_data *ed) {
+ nullpo_retv(ed);
+ if( ed->summon_timer != INVALID_TIMER )
+ delete_timer(ed->summon_timer, elemental_summon_end);
+ ed->summon_timer = INVALID_TIMER;
+}
+
+int elemental_delete(struct elemental_data *ed, int reply) {
+ struct map_session_data *sd;
+
+ nullpo_ret(ed);
+
+ sd = ed->master;
+ ed->elemental.life_time = 0;
+
+ elemental_clean_effect(ed);
+ elemental_summon_stop(ed);
+
+ if( !sd )
+ return unit_free(&ed->bl, 0);
+
+ sd->ed = NULL;
+ sd->status.ele_id = 0;
+
+ return unit_remove_map(&ed->bl, 0);
+}
+
+void elemental_summon_init(struct elemental_data *ed) {
+ if( ed->summon_timer == INVALID_TIMER )
+ ed->summon_timer = add_timer(gettick() + ed->elemental.life_time, elemental_summon_end, ed->master->bl.id, 0);
+
+ ed->regen.state.block = 0;
+}
+
+int elemental_data_received(struct s_elemental *ele, bool flag) {
+ struct map_session_data *sd;
+ struct elemental_data *ed;
+ struct s_elemental_db *db;
+ int i = elemental_search_index(ele->class_);
+
+ if( (sd = map_charid2sd(ele->char_id)) == NULL )
+ return 0;
+
+ if( !flag || i < 0 ) { // Not created - loaded - DB info
+ sd->status.ele_id = 0;
+ return 0;
+ }
+
+ db = &elemental_db[i];
+ if( !sd->ed ) { // Initialize it after first summon.
+ sd->ed = ed = (struct elemental_data*)aCalloc(1,sizeof(struct elemental_data));
+ ed->bl.type = BL_ELEM;
+ ed->bl.id = npc_get_new_npc_id();
+ ed->master = sd;
+ ed->db = db;
+ memcpy(&ed->elemental, ele, sizeof(struct s_elemental));
+ status_set_viewdata(&ed->bl, ed->elemental.class_);
+ ed->vd->head_mid = 10; // Why?
+ status_change_init(&ed->bl);
+ unit_dataset(&ed->bl);
+ ed->ud.dir = sd->ud.dir;
+
+ ed->bl.m = sd->bl.m;
+ ed->bl.x = sd->bl.x;
+ ed->bl.y = sd->bl.y;
+ unit_calc_pos(&ed->bl, sd->bl.x, sd->bl.y, sd->ud.dir);
+ ed->bl.x = ed->ud.to_x;
+ ed->bl.y = ed->ud.to_y;
+
+ map_addiddb(&ed->bl);
+ status_calc_elemental(ed,1);
+ ed->last_thinktime = gettick();
+ ed->summon_timer = INVALID_TIMER;
+ ed->battle_status.mode = ele->mode = EL_MODE_PASSIVE; // Initial mode.
+ elemental_summon_init(ed);
+ } else {
+ memcpy(&sd->ed->elemental, ele, sizeof(struct s_elemental));
+ ed = sd->ed;
+ }
+
+ sd->status.ele_id = ele->elemental_id;
+ ed->battle_status.mode = ele->mode = EL_MODE_PASSIVE; // Initial mode.
+
+ if( ed->bl.prev == NULL && sd->bl.prev != NULL ) {
+ map_addblock(&ed->bl);
+ clif_spawn(&ed->bl);
+ clif_elemental_info(sd);
+ clif_elemental_updatestatus(sd,SP_HP);
+ clif_hpmeter_single(sd->fd,ed->bl.id,ed->battle_status.hp,ed->battle_status.matk_max);
+ clif_elemental_updatestatus(sd,SP_SP);
+ }
+
+ return 1;
+}
+
+int elemental_clean_single_effect(struct elemental_data *ed, int skill_num) {
+ struct block_list *bl;
+ sc_type type = status_skill2sc(skill_num);
+
+ nullpo_ret(ed);
+
+ bl = battle_get_master(&ed->bl);
+
+ if( type ) {
+ switch( type ) {
+ // Just remove status change.
+ case SC_PYROTECHNIC_OPTION:
+ case SC_HEATER_OPTION:
+ case SC_TROPIC_OPTION:
+ case SC_FIRE_CLOAK_OPTION:
+ case SC_AQUAPLAY_OPTION:
+ case SC_WATER_SCREEN_OPTION:
+ case SC_COOLER_OPTION:
+ case SC_CHILLY_AIR_OPTION:
+ case SC_GUST_OPTION:
+ case SC_WIND_STEP_OPTION:
+ case SC_BLAST_OPTION:
+ case SC_WATER_DROP_OPTION:
+ case SC_WIND_CURTAIN_OPTION:
+ case SC_WILD_STORM_OPTION:
+ case SC_PETROLOGY_OPTION:
+ case SC_SOLID_SKIN_OPTION:
+ case SC_CURSED_SOIL_OPTION:
+ case SC_STONE_SHIELD_OPTION:
+ case SC_UPHEAVAL_OPTION:
+ case SC_CIRCLE_OF_FIRE_OPTION:
+ case SC_TIDAL_WEAPON_OPTION:
+ if( bl ) status_change_end(bl,type,-1); // Master
+ status_change_end(&ed->bl,type-1,-1); // Elemental Spirit
+ break;
+ case SC_ZEPHYR:
+ if( bl ) status_change_end(bl,type,-1);
+ break;
+ }
+ }
+ if( skill_get_unit_id(skill_num,0) )
+ skill_clear_unitgroup(&ed->bl);
+
+ return 1;
+}
+
+int elemental_clean_effect(struct elemental_data *ed) {
+ struct map_session_data *sd;
+
+ nullpo_ret(ed);
+
+ // Elemental side
+ status_change_end(&ed->bl, SC_TROPIC, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_HEATER, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_AQUAPLAY, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_COOLER, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_CHILLY_AIR, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_PYROTECHNIC, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_FIRE_CLOAK, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WATER_DROP, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WATER_SCREEN, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_GUST, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WIND_STEP, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_BLAST, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WIND_CURTAIN, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_WILD_STORM, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_PETROLOGY, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_SOLID_SKIN, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_CURSED_SOIL, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_STONE_SHIELD, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_UPHEAVAL, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_CIRCLE_OF_FIRE, INVALID_TIMER);
+ status_change_end(&ed->bl, SC_TIDAL_WEAPON, INVALID_TIMER);
+
+ skill_clear_unitgroup(&ed->bl);
+
+ if( (sd = ed->master) == NULL )
+ return 0;
+
+ // Master side
+ status_change_end(&sd->bl, SC_TROPIC_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_HEATER_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_AQUAPLAY_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_COOLER_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_CHILLY_AIR_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_PYROTECHNIC_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_FIRE_CLOAK_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WATER_DROP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WATER_SCREEN_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_GUST_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WIND_STEP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_BLAST_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WATER_DROP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WIND_CURTAIN_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WILD_STORM_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_ZEPHYR, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_WIND_STEP_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_PETROLOGY_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_SOLID_SKIN_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_CURSED_SOIL_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_STONE_SHIELD_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_UPHEAVAL_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_CIRCLE_OF_FIRE_OPTION, INVALID_TIMER);
+ status_change_end(&sd->bl, SC_TIDAL_WEAPON_OPTION, INVALID_TIMER);
+
+ return 1;
+}
+
+int elemental_action(struct elemental_data *ed, struct block_list *bl, unsigned int tick) {
+ short skillnum, skilllv;
+ int i;
+
+ nullpo_ret(ed);
+ nullpo_ret(bl);
+
+ if( !ed->master )
+ return 0;
+
+ if( ed->target_id )
+ elemental_unlocktarget(ed); // Remove previous target.
+
+ ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&EL_SKILLMODE_AGGRESSIVE));
+ if( i == MAX_ELESKILLTREE )
+ return 0;
+
+ skillnum = ed->db->skill[i].id;
+ skilllv = ed->db->skill[i].lv;
+
+ if( elemental_skillnotok(skillnum, ed) )
+ return 0;
+
+ if( ed->ud.skilltimer != -1 )
+ return 0;
+ else if( DIFF_TICK(tick, ed->ud.canact_tick) < 0 )
+ return 0;
+
+ ed->target_id = ed->ud.skilltarget = bl->id; // Set new target
+ ed->last_thinktime = tick;
+
+ // Not in skill range.
+ if( !battle_check_range(&ed->bl,bl,skill_get_range(skillnum,skilllv)) ) {
+ // Try to walk to the target.
+ if( !unit_walktobl(&ed->bl, bl, skill_get_range(skillnum,skilllv), 2) )
+ elemental_unlocktarget(ed);
+ else {
+ // Walking, waiting to be in range. Client don't handle it, then we must handle it here.
+ int walk_dist = distance_bl(&ed->bl,bl) - skill_get_range(skillnum,skilllv);
+ ed->ud.skillid = skillnum;
+ ed->ud.skilllv = skilllv;
+
+ if( skill_get_inf(skillnum) & INF_GROUND_SKILL )
+ ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_pos, ed->bl.id, 0 );
+ else
+ ed->ud.skilltimer = add_timer( tick+status_get_speed(&ed->bl)*walk_dist, skill_castend_id, ed->bl.id, 0 );
+ }
+ return 1;
+
+ }
+ //Otherwise, just cast the skill.
+ if( skill_get_inf(skillnum) & INF_GROUND_SKILL )
+ unit_skilluse_pos(&ed->bl, bl->x, bl->y, skillnum, skilllv);
+ else
+ unit_skilluse_id(&ed->bl, bl->id, skillnum, skilllv);
+
+ // Reset target.
+ ed->target_id = 0;
+
+ return 1;
+}
+
+/*===============================================================
+ * Action that elemental perform after changing mode.
+ * Activates one of the skills of the new mode.
+ *-------------------------------------------------------------*/
+int elemental_change_mode_ack(struct elemental_data *ed, int mode) {
+ struct block_list *bl = &ed->master->bl;
+ short skillnum, skilllv;
+ int i;
+
+ nullpo_ret(ed);
+
+ if( !bl )
+ return 0;
+
+ // Select a skill.
+ ARR_FIND(0, MAX_ELESKILLTREE, i, ed->db->skill[i].id && (ed->db->skill[i].mode&mode));
+ if( i == MAX_ELESKILLTREE )
+ return 0;
+
+ skillnum = ed->db->skill[i].id;
+ skilllv = ed->db->skill[i].lv;
+
+ if( elemental_skillnotok(skillnum, ed) )
+ return 0;
+
+ if( ed->ud.skilltimer != -1 )
+ return 0;
+ else if( DIFF_TICK(gettick(), ed->ud.canact_tick) < 0 )
+ return 0;
+
+ ed->target_id = bl->id; // Set new target
+ ed->last_thinktime = gettick();
+
+ if( skill_get_inf(skillnum) & INF_GROUND_SKILL )
+ unit_skilluse_pos(&ed->bl, bl->x, bl->y, skillnum, skilllv);
+ else
+ unit_skilluse_id(&ed->bl,bl->id,skillnum,skilllv);
+
+ ed->target_id = 0; // Reset target after casting the skill to avoid continious attack.
+
+ return 1;
+}
+
+/*===============================================================
+ * Change elemental mode.
+ *-------------------------------------------------------------*/
+int elemental_change_mode(struct elemental_data *ed, int mode) {
+ nullpo_ret(ed);
+
+ // Remove target
+ elemental_unlocktarget(ed);
+
+ // Removes the effects of the previous mode.
+ if(ed->elemental.mode != mode ) elemental_clean_effect(ed);
+
+ ed->battle_status.mode = ed->elemental.mode = mode;
+
+ // Normalize elemental mode to elemental skill mode.
+ if( mode == EL_MODE_AGGRESSIVE ) mode = EL_SKILLMODE_AGGRESSIVE; // Aggressive spirit mode -> Aggressive spirit skill.
+ else if( mode == EL_MODE_ASSIST ) mode = EL_SKILLMODE_ASSIST; // Assist spirit mode -> Assist spirit skill.
+ else mode = EL_SKILLMODE_PASIVE; // Passive spirit mode -> Passive spirit skill.
+
+ // Use a skill inmediately after every change mode.
+ if( mode != EL_SKILLMODE_AGGRESSIVE )
+ elemental_change_mode_ack(ed,mode);
+ return 1;
+}
+
+void elemental_damage(struct elemental_data *ed, struct block_list *src, int hp, int sp) {
+ if( hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( sp )
+ clif_elemental_updatestatus(ed->master, SP_SP);
+}
+
+void elemental_heal(struct elemental_data *ed, int hp, int sp) {
+ if( hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( sp )
+ clif_elemental_updatestatus(ed->master, SP_SP);
+}
+
+int elemental_dead(struct elemental_data *ed, struct block_list *src) {
+ elemental_delete(ed, 1);
+ return 0;
+}
+
+int elemental_unlocktarget(struct elemental_data *ed) {
+ nullpo_ret(ed);
+
+ ed->target_id = 0;
+ elemental_stop_attack(ed);
+ elemental_stop_walking(ed,1);
+ return 0;
+}
+
+int elemental_skillnotok(int skillid, struct elemental_data *ed) {
+ int i = skill_get_index(skillid);
+ nullpo_retr(1,ed);
+
+ if (i == 0)
+ return 1; // invalid skill id
+
+ if( ed->blockskill[i] > 0 )
+ return 1;
+
+ return skillnotok(skillid, ed->master);
+}
+
+int elemental_set_target( struct map_session_data *sd, struct block_list *bl ) {
+ struct elemental_data *ed = sd->ed;
+
+ nullpo_ret(ed);
+ nullpo_ret(bl);
+
+ if( ed->bl.m != bl->m || !check_distance_bl(&ed->bl, bl, ed->db->range2) )
+ return 0;
+
+ if( !status_check_skilluse(&ed->bl, bl, 0, 0) )
+ return 0;
+
+ if( ed->target_id == 0 )
+ ed->target_id = bl->id;
+
+ return 1;
+}
+
+static int elemental_ai_sub_timer_activesearch(struct block_list *bl, va_list ap) {
+ struct elemental_data *ed;
+ struct block_list **target;
+ int dist;
+
+ nullpo_ret(bl);
+
+ ed = va_arg(ap,struct elemental_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(&ed->bl, bl, 0, 0) )
+ return 0;
+
+ if( battle_check_target(&ed->bl,bl,BCT_ENEMY) <= 0 )
+ return 0;
+
+ switch( bl->type ) {
+ case BL_PC:
+ if( !map_flag_vs(ed->bl.m) )
+ return 0;
+ default:
+ dist = distance_bl(&ed->bl, bl);
+ if( ((*target) == NULL || !check_distance_bl(&ed->bl, *target, dist)) && battle_check_range(&ed->bl,bl,ed->db->range2) ) { //Pick closest target?
+ (*target) = bl;
+ ed->target_id = bl->id;
+ ed->min_chase = dist + ed->db->range3;
+ if( ed->min_chase > AREA_SIZE )
+ ed->min_chase = AREA_SIZE;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int elemental_ai_sub_timer(struct elemental_data *ed, struct map_session_data *sd, unsigned int tick) {
+ struct block_list *target = NULL;
+ int master_dist, view_range, mode;
+
+ nullpo_ret(ed);
+ nullpo_ret(sd);
+
+ if( ed->bl.prev == NULL || sd == NULL || sd->bl.prev == NULL )
+ return 0;
+
+ if( DIFF_TICK(tick,ed->last_thinktime) < MIN_ELETHINKTIME )
+ return 0;
+
+ ed->last_thinktime = tick;
+
+ if( ed->ud.skilltimer != -1 )
+ return 0;
+
+ if( ed->ud.walktimer != -1 && ed->ud.walkpath.path_pos <= 2 )
+ return 0; //No thinking when you just started to walk.
+
+ if(ed->ud.walkpath.path_pos < ed->ud.walkpath.path_len && ed->ud.target == sd->bl.id)
+ return 0; //No thinking until be near the master.
+
+ if( ed->sc.count && ed->sc.data[SC_BLIND] )
+ view_range = 3;
+ else
+ view_range = ed->db->range2;
+
+ mode = status_get_mode(&ed->bl);
+
+ master_dist = distance_bl(&sd->bl, &ed->bl);
+ if( master_dist > AREA_SIZE ) { // Master out of vision range.
+ elemental_unlocktarget(ed);
+ unit_warp(&ed->bl,sd->bl.m,sd->bl.x,sd->bl.y,3);
+ return 0;
+ } else if( master_dist > MAX_ELEDISTANCE ) { // Master too far, chase.
+ short x = sd->bl.x, y = sd->bl.y;
+ if( ed->target_id )
+ elemental_unlocktarget(ed);
+ if( ed->ud.walktimer != -1 && ed->ud.target == sd->bl.id )
+ return 0; //Already walking to him
+ if( DIFF_TICK(tick, ed->ud.canmove_tick) < 0 )
+ return 0; //Can't move yet.
+ if( map_search_freecell(&ed->bl, sd->bl.m, &x, &y, MIN_ELEDISTANCE, MIN_ELEDISTANCE, 1)
+ && unit_walktoxy(&ed->bl, x, y, 0) )
+ return 0;
+ }
+
+ if( mode == EL_MODE_AGGRESSIVE ) {
+ target = map_id2bl(ed->ud.target);
+
+ if( !target )
+ map_foreachinrange(elemental_ai_sub_timer_activesearch, &ed->bl, ed->db->range2, BL_CHAR, ed, &target, status_get_mode(&ed->bl));
+
+ if( !target ) { //No targets available.
+ elemental_unlocktarget(ed);
+ return 1;
+ }
+
+ if( battle_check_range(&ed->bl,target,ed->db->range2) && rand()%100 < 2 ) { // 2% chance to cast attack skill.
+ if( elemental_action(ed,target,tick) )
+ return 1;
+ }
+
+ //Attempt to attack.
+ //At this point we know the target is attackable, we just gotta check if the range matches.
+ if( ed->ud.target == target->id && ed->ud.attacktimer != -1 ) //Already locked.
+ return 1;
+
+ if( battle_check_range(&ed->bl, target, ed->base_status.rhw.range) ) {//Target within range, engage
+ unit_attack(&ed->bl,target->id,1);
+ return 1;
+ }
+
+ //Follow up if possible.
+ if( !unit_walktobl(&ed->bl, target, ed->base_status.rhw.range, 2) )
+ elemental_unlocktarget(ed);
+ }
+
+ return 0;
+}
+
+static int elemental_ai_sub_foreachclient(struct map_session_data *sd, va_list ap) {
+ unsigned int tick = va_arg(ap,unsigned int);
+ if(sd->status.ele_id && sd->ed)
+ elemental_ai_sub_timer(sd->ed,sd,tick);
+
+ return 0;
+}
+
+static int elemental_ai_timer(int tid, unsigned int tick, int id, intptr data) {
+ map_foreachpc(elemental_ai_sub_foreachclient,tick);
+
+ return 0;
+}
+
+int read_elementaldb(void) {
+ FILE *fp;
+ char line[1024], *p;
+ char *str[26];
+ int i, j = 0, k = 0, ele;
+ struct s_elemental_db *db;
+ struct status_data *status;
+
+ sprintf(line, "%s/%s", db_path, "elemental_db.txt");
+ memset(elemental_db,0,sizeof(elemental_db));
+
+ fp = fopen(line, "r");
+ if( !fp ) {
+ ShowError("read_elementaldb : can't read elemental_db.txt\n");
+ return -1;
+ }
+
+ while( fgets(line, sizeof(line), fp) && j < MAX_ELEMENTAL_CLASS ) {
+ k++;
+ if( line[0] == '/' && line[1] == '/' )
+ continue;
+
+ i = 0;
+ p = strtok(line, ",");
+ while( p != NULL && i < 26 ) {
+ str[i++] = p;
+ p = strtok(NULL, ",");
+ }
+ if( i < 26 ) {
+ ShowError("read_elementaldb : Incorrect number of columns at elemental_db.txt line %d.\n", k);
+ continue;
+ }
+
+ db = &elemental_db[j];
+ db->class_ = atoi(str[0]);
+ strncpy(db->sprite, str[1], NAME_LENGTH);
+ strncpy(db->name, str[2], NAME_LENGTH);
+ db->lv = atoi(str[3]);
+
+ status = &db->status;
+ db->vd.class_ = db->class_;
+
+ status->max_hp = atoi(str[4]);
+ status->max_sp = atoi(str[5]);
+ status->rhw.range = atoi(str[6]);
+ status->rhw.atk = atoi(str[7]);
+ status->rhw.atk2 = status->rhw.atk + atoi(str[8]);
+ status->def = atoi(str[9]);
+ status->mdef = atoi(str[10]);
+ status->str = atoi(str[11]);
+ status->agi = atoi(str[12]);
+ status->vit = atoi(str[13]);
+ status->int_ = atoi(str[14]);
+ status->dex = atoi(str[15]);
+ status->luk = atoi(str[16]);
+ db->range2 = atoi(str[17]);
+ db->range3 = atoi(str[18]);
+ status->size = atoi(str[19]);
+ status->race = atoi(str[20]);
+
+ ele = atoi(str[21]);
+ status->def_ele = ele%10;
+ status->ele_lv = ele/20;
+ if( status->def_ele >= ELE_MAX ) {
+ ShowWarning("Elemental %d has invalid element type %d (max element is %d)\n", db->class_, status->def_ele, ELE_MAX - 1);
+ status->def_ele = ELE_NEUTRAL;
+ }
+ if( status->ele_lv < 1 || status->ele_lv > 4 ) {
+ ShowWarning("Elemental %d has invalid element level %d (max is 4)\n", db->class_, status->ele_lv);
+ status->ele_lv = 1;
+ }
+
+ status->aspd_rate = 1000;
+ status->speed = atoi(str[22]);
+ status->adelay = atoi(str[23]);
+ status->amotion = atoi(str[24]);
+ status->dmotion = atoi(str[25]);
+
+ j++;
+ }
+
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' elementals in '"CL_WHITE"db/elemental_db.txt"CL_RESET"'.\n",j);
+
+ return 0;
+}
+
+int read_elemental_skilldb(void) {
+ FILE *fp;
+ char line[1024], *p;
+ char *str[4];
+ struct s_elemental_db *db;
+ int i, j = 0, k = 0, class_;
+ int skillid, skilllv, skillmode;
+
+ sprintf(line, "%s/%s", db_path, "elemental_skill_db.txt");
+ fp = fopen(line, "r");
+ if( !fp ) {
+ ShowError("read_elemental_skilldb : can't read elemental_skill_db.txt\n");
+ return -1;
+ }
+
+ while( fgets(line, sizeof(line), fp) ) {
+ k++;
+ if( line[0] == '/' && line[1] == '/' )
+ continue;
+
+ i = 0;
+ p = strtok(line, ",");
+ while( p != NULL && i < 4 ) {
+ str[i++] = p;
+ p = strtok(NULL, ",");
+ }
+ if( i < 4 ) {
+ ShowError("read_elemental_skilldb : Incorrect number of columns at elemental_skill_db.txt line %d.\n", k);
+ continue;
+ }
+
+ class_ = atoi(str[0]);
+ ARR_FIND(0, MAX_ELEMENTAL_CLASS, i, class_ == elemental_db[i].class_);
+ if( i == MAX_ELEMENTAL_CLASS ) {
+ ShowError("read_elemental_skilldb : Class not found in elemental_db for skill entry, line %d.\n", k);
+ continue;
+ }
+
+ skillid = atoi(str[1]);
+ if( skillid < EL_SKILLBASE || skillid >= EL_SKILLBASE + MAX_ELEMENTALSKILL ) {
+ ShowError("read_elemental_skilldb : Skill out of range, line %d.\n", k);
+ continue;
+ }
+
+ db = &elemental_db[i];
+ skilllv = atoi(str[2]);
+
+ skillmode = atoi(str[3]);
+ if( skillmode < EL_SKILLMODE_PASIVE || skillmode > EL_SKILLMODE_AGGRESSIVE ) {
+ ShowError("read_elemental_skilldb : Skillmode out of range, line %d.\n",k);
+ skillmode = EL_SKILLMODE_PASIVE;
+ continue;
+ }
+ ARR_FIND( 0, MAX_ELESKILLTREE, i, db->skill[i].id == 0 || db->skill[i].id == skillid );
+ if( i == MAX_ELESKILLTREE ) {
+ ShowWarning("Unable to load skill %d into Elemental %d's tree. Maximum number of skills per elemental has been reached.\n", skillid, class_);
+ continue;
+ }
+ db->skill[i].id = skillid;
+ db->skill[i].lv = skilllv;
+ db->skill[i].mode = skillmode;
+ j++;
+ }
+
+ fclose(fp);
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"db/elemental_skill_db.txt"CL_RESET"'.\n",j);
+ return 0;
+}
+
+void reload_elementaldb(void) {
+ read_elementaldb();
+ reload_elemental_skilldb();
+}
+
+void reload_elemental_skilldb(void) {
+ read_elemental_skilldb();
+}
+
+int do_init_elemental(void) {
+ read_elementaldb();
+ read_elemental_skilldb();
+
+ add_timer_func_list(elemental_ai_timer,"elemental_ai_timer");
+ add_timer_interval(gettick()+MIN_ELETHINKTIME,elemental_ai_timer,0,0,MIN_ELETHINKTIME);
+
+ return 0;
+}
+
+void do_final_elemental(void) {
+ return;
+}
diff --git a/src/map/elemental.h b/src/map/elemental.h
new file mode 100644
index 000000000..9f8ef1e22
--- /dev/null
+++ b/src/map/elemental.h
@@ -0,0 +1,95 @@
+// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifndef _ELEMENTAL_H_
+#define _ELEMENTAL_H_
+
+#include "status.h" // struct status_data, struct status_change
+#include "unit.h" // struct unit_data
+
+#define MIN_ELETHINKTIME 100
+#define MIN_ELEDISTANCE 2
+#define MAX_ELEDISTANCE 6
+
+#define EL_MODE_AGGRESSIVE (MD_CANMOVE|MD_AGGRESSIVE|MD_CANATTACK)
+#define EL_MODE_ASSIST (MD_CANMOVE|MD_ASSIST)
+#define EL_MODE_PASSIVE MD_CANMOVE
+
+#define EL_SKILLMODE_PASIVE 0x1
+#define EL_SKILLMODE_ASSIST 0x2
+#define EL_SKILLMODE_AGGRESSIVE 0x4
+
+struct elemental_skill {
+ unsigned short id, lv;
+ short mode;
+};
+
+struct s_elemental_db {
+ int class_;
+ char sprite[NAME_LENGTH], name[NAME_LENGTH];
+ unsigned short lv;
+ short range2, range3;
+ struct status_data status;
+ struct view_data vd;
+ struct elemental_skill skill[MAX_ELESKILLTREE];
+};
+
+extern struct s_elemental_db elemental_db[MAX_ELEMENTAL_CLASS];
+
+struct elemental_data {
+ struct block_list bl;
+ struct unit_data ud;
+ struct view_data *vd;
+ struct status_data base_status, battle_status;
+ struct status_change sc;
+ struct regen_data regen;
+
+ struct s_elemental_db *db;
+ struct s_elemental elemental;
+ char blockskill[MAX_SKILL];
+
+ struct map_session_data *master;
+ int summon_timer;
+ int skill_timer;
+
+ unsigned last_thinktime, last_linktime;
+ short min_chase;
+ int target_id, attacked_id;
+};
+
+bool elemental_class(int class_);
+struct view_data * elemental_get_viewdata(int class_);
+
+int elemental_create(struct map_session_data *sd, int class_, unsigned int lifetime);
+int elemental_data_received(struct s_elemental *ele, bool flag);
+int elemental_save(struct elemental_data *ed);
+
+int elemental_change_mode_ack(struct elemental_data *ed, int mode);
+int elemental_change_mode(struct elemental_data *ed, int mode);
+
+void elemental_damage(struct elemental_data *ed, struct block_list *src, int hp, int sp);
+void elemental_heal(struct elemental_data *ed, int hp, int sp);
+int elemental_dead(struct elemental_data *ed, struct block_list *src);
+
+int elemental_delete(struct elemental_data *ed, int reply);
+void elemental_summon_stop(struct elemental_data *ed);
+
+int elemental_get_lifetime(struct elemental_data *ed);
+
+int elemental_unlocktarget(struct elemental_data *ed);
+int elemental_skillnotok(int skillid, struct elemental_data *ed);
+int elemental_set_target( struct map_session_data *sd, struct block_list *bl );
+int elemental_clean_single_effect(struct elemental_data *ed, int skill_num);
+int elemental_clean_effect(struct elemental_data *ed);
+int elemental_action(struct elemental_data *ed, struct block_list *bl, unsigned int tick);
+
+#define elemental_stop_walking(ed, type) unit_stop_walking(&(ed)->bl, type)
+#define elemental_stop_attack(ed) unit_stop_attack(&(ed)->bl)
+
+int read_elemental_skilldb(void);
+void reload_elementaldb(void);
+void reload_elemental_skilldb(void);
+int do_init_elemental(void);
+void do_final_elemental(void);
+
+#endif /* _ELEMENTAL_H_ */
diff --git a/src/map/intif.c b/src/map/intif.c
index 78f3d585c..ce0ad8e6a 100644
--- a/src/map/intif.c
+++ b/src/map/intif.c
@@ -21,6 +21,7 @@
#include "atcommand.h"
#include "mercenary.h"
#include "homunculus.h"
+#include "elemental.h"
#include "mail.h"
#include "quest.h"
@@ -40,7 +41,7 @@ static const int packet_len_table[]={
-1, 0, 0,14, 0, 0, 0, 0, -1,74,-1,11, 11,-1, 0, 0, //0x3840
-1,-1, 7, 7, 7,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3850 Auctions [Zephyrus]
-1, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3860 Quests [Kevin] [Inkfish]
- -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3870 Mercenaries [Zephyrus]
+ -1, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 3, 3, 0, //0x3870 Mercenaries [Zephyrus] / Elemental [pakpil]
11,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3880
-1,-1, 7, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0x3890 Homunculus [albator]
};
@@ -1988,6 +1989,94 @@ int intif_parse_mercenary_saved(int fd)
return 0;
}
+/*==========================================
+ * Elemental's System
+ *------------------------------------------*/
+int intif_elemental_create(struct s_elemental *ele)
+{
+ int size = sizeof(struct s_elemental) + 4;
+
+ if( CheckForCharServer() )
+ return 0;
+
+ WFIFOHEAD(inter_fd,size);
+ WFIFOW(inter_fd,0) = 0x307c;
+ WFIFOW(inter_fd,2) = size;
+ memcpy(WFIFOP(inter_fd,4), ele, sizeof(struct s_elemental));
+ WFIFOSET(inter_fd,size);
+ return 0;
+}
+
+int intif_parse_elemental_received(int fd)
+{
+ int len = RFIFOW(fd,2) - 5;
+ if( sizeof(struct s_elemental) != len )
+ {
+ if( battle_config.etc_log )
+ ShowError("intif: create elemental data size error %d != %d\n", sizeof(struct s_elemental), len);
+ return 0;
+ }
+
+ elemental_data_received((struct s_elemental*)RFIFOP(fd,5), RFIFOB(fd,4));
+ return 0;
+}
+
+int intif_elemental_request(int ele_id, int char_id)
+{
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,10);
+ WFIFOW(inter_fd,0) = 0x307d;
+ WFIFOL(inter_fd,2) = ele_id;
+ WFIFOL(inter_fd,6) = char_id;
+ WFIFOSET(inter_fd,10);
+ return 0;
+}
+
+int intif_elemental_delete(int ele_id)
+{
+ if (CheckForCharServer())
+ return 0;
+
+ WFIFOHEAD(inter_fd,6);
+ WFIFOW(inter_fd,0) = 0x307e;
+ WFIFOL(inter_fd,2) = ele_id;
+ WFIFOSET(inter_fd,6);
+ return 0;
+}
+
+int intif_parse_elemental_deleted(int fd)
+{
+ if( RFIFOB(fd,2) != 1 )
+ ShowError("Elemental data delete failure\n");
+
+ return 0;
+}
+
+int intif_elemental_save(struct s_elemental *ele)
+{
+ int size = sizeof(struct s_elemental) + 4;
+
+ if( CheckForCharServer() )
+ return 0;
+
+ WFIFOHEAD(inter_fd,size);
+ WFIFOW(inter_fd,0) = 0x307f;
+ WFIFOW(inter_fd,2) = size;
+ memcpy(WFIFOP(inter_fd,4), ele, sizeof(struct s_elemental));
+ WFIFOSET(inter_fd,size);
+ return 0;
+}
+
+int intif_parse_elemental_saved(int fd)
+{
+ if( RFIFOB(fd,2) != 1 )
+ ShowError("Elemental data save failure\n");
+
+ return 0;
+}
+
//-----------------------------------------------------------------
// inter serverからの通信
// エラーがあれば0(false)を返すこと
@@ -2076,7 +2165,11 @@ int intif_parse(int fd)
case 0x3870: intif_parse_mercenary_received(fd); break;
case 0x3871: intif_parse_mercenary_deleted(fd); break;
case 0x3872: intif_parse_mercenary_saved(fd); break;
-
+// Elemental System
+ case 0x387c: intif_parse_elemental_received(fd); break;
+ case 0x387d: intif_parse_elemental_deleted(fd); break;
+ case 0x387e: intif_parse_elemental_saved(fd); break;
+
case 0x3880: intif_parse_CreatePet(fd); break;
case 0x3881: intif_parse_RecvPetData(fd); break;
case 0x3882: intif_parse_SavePetOk(fd); break;
diff --git a/src/map/intif.h b/src/map/intif.h
index 847523593..c5f6cb158 100644
--- a/src/map/intif.h
+++ b/src/map/intif.h
@@ -11,6 +11,7 @@ struct guild_position;
struct s_pet;
struct s_homunculus;
struct s_mercenary;
+struct s_elemental;
struct mail_message;
struct auction_data;
@@ -97,6 +98,11 @@ int intif_Auction_register(struct auction_data *auction);
int intif_Auction_cancel(int char_id, unsigned int auction_id);
int intif_Auction_close(int char_id, unsigned int auction_id);
int intif_Auction_bid(int char_id, const char* name, unsigned int auction_id, int bid);
+// ELEMENTAL SYSTEM
+int intif_elemental_create(struct s_elemental *ele);
+int intif_elemental_request(int ele_id, int char_id);
+int intif_elemental_delete(int ele_id);
+int intif_elemental_save(struct s_elemental *ele);
int CheckForCharServer(void);
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
index f2e6ae61d..390954e3f 100644
--- a/src/map/itemdb.h
+++ b/src/map/itemdb.h
@@ -160,6 +160,7 @@ struct item_data* itemdb_exists(int nameid);
#define itemdb_is_poison(n) (n >= 12717 && n <= 12724)
#define itemid_isgemstone(id) ( (id) >= ITEMID_YELLOW_GEMSTONE && (id) <= ITEMID_BLUE_GEMSTONE )
#define itemdb_iscashfood(id) ( (id) >= 12202 && (id) <= 12207 )
+#define itemdb_is_GNbomb(n) (n >= 13260 && n <= 13267)
const char* itemdb_typename(int type);
int itemdb_group_bonus(struct map_session_data* sd, int itemid);
diff --git a/src/map/map.c b/src/map/map.c
index fd0d43aa4..d0b454a52 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -41,6 +41,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "atcommand.h"
#include "log.h"
#include "mail.h"
@@ -1722,8 +1723,14 @@ int map_quit(struct map_session_data *sd)
// Return loot to owner
if( sd->pd ) pet_lootitem_drop(sd->pd, sd);
+
if( sd->state.storage_flag == 1 ) sd->state.storage_flag = 0; // No need to Double Save Storage on Quit.
+ if( sd->ed ) {
+ elemental_clean_effect(sd->ed);
+ unit_remove_map(&sd->ed->bl,CLR_TELEPORT);
+ }
+
unit_remove_map_pc(sd,CLR_TELEPORT);
if( map[sd->bl.m].instance_id )
@@ -3588,6 +3595,7 @@ void do_final(void)
do_final_unit();
do_final_battleground();
do_final_duel();
+ do_final_elemental();
map_db->destroy(map_db, map_db_final);
@@ -3914,7 +3922,8 @@ int do_init(int argc, char *argv[])
do_init_unit();
do_init_battleground();
do_init_duel();
-
+ do_init_elemental();
+
npc_event_do_oninit(); // npcのOnInitイベント?行
if( console )
diff --git a/src/map/map.h b/src/map/map.h
index 660174055..b78e71b0c 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -233,12 +233,13 @@ enum bl_type {
BL_SKILL = 0x040,
BL_NPC = 0x080,
BL_CHAT = 0x100,
-
+ BL_ELEM = 0x200,
+
BL_ALL = 0xFFF,
};
//For common mapforeach calls. Since pets cannot be affected, they aren't included here yet.
-#define BL_CHAR (BL_PC|BL_MOB|BL_HOM|BL_MER)
+#define BL_CHAR (BL_PC|BL_MOB|BL_HOM|BL_MER|BL_ELEM)
enum npc_subtype { WARP, SHOP, SCRIPT, CASHSHOP };
@@ -738,6 +739,7 @@ typedef struct skill_unit TBL_SKILL;
typedef struct pet_data TBL_PET;
typedef struct homun_data TBL_HOM;
typedef struct mercenary_data TBL_MER;
+typedef struct elemental_data TBL_ELEM;
#define BL_CAST(type_, bl) \
( ((bl) == (struct block_list*)NULL || (bl)->type != (type_)) ? (T ## type_ *)NULL : (T ## type_ *)(bl) )
diff --git a/src/map/mob.c b/src/map/mob.c
index 95b357e4f..ee5bda342 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -23,6 +23,7 @@
#include "mob.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "guild.h"
#include "itemdb.h"
#include "skill.h"
@@ -1896,6 +1897,15 @@ void mob_log_damage(struct mob_data *md, struct block_list *src, int damage)
md->attacked_id = src->id;
break;
}
+ case BL_ELEM:
+ {
+ struct elemental_data *ele = (TBL_ELEM*)src;
+ if( ele->master )
+ char_id = ele->master->status.char_id;
+ if( damage )
+ md->attacked_id = src->id;
+ break;
+ }
default: //For all unhandled types.
md->attacked_id = src->id;
}
diff --git a/src/map/npc_chat.c b/src/map/npc_chat.c
index 77c875a7e..84a6fab22 100644
--- a/src/map/npc_chat.c
+++ b/src/map/npc_chat.c
@@ -14,7 +14,7 @@
#include "pc.h" // struct map_session_data
#include "script.h" // set_var()
-#include <pcre.h>
+#include "pcre.h"
#include <stdio.h>
#include <stdlib.h>
diff --git a/src/map/pc.c b/src/map/pc.c
index 9e548b038..7a0fc67fa 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -29,6 +29,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "npc.h" // fake_nd
#include "pet.h" // pet_unlocktarget()
#include "party.h" // party_search()
@@ -1213,7 +1214,9 @@ int pc_reg_received(struct map_session_data *sd)
intif_homunculus_requestload(sd->status.account_id, sd->status.hom_id);
if( sd->status.mer_id > 0 )
intif_mercenary_request(sd->status.mer_id, sd->status.char_id);
-
+ if( sd->status.ele_id > 0 )
+ intif_elemental_request(sd->status.ele_id, sd->status.char_id);
+
map_addiddb(&sd->bl);
map_delnickdb(sd->status.char_id, sd->status.name);
if (!chrif_auth_finished(sd))
@@ -6155,6 +6158,9 @@ void pc_damage(struct map_session_data *sd,struct block_list *src,unsigned int h
if( sd->status.pet_id > 0 && sd->pd && battle_config.pet_damage_support )
pet_target_check(sd,src,1);
+ if( sd->status.ele_id > 0 )
+ elemental_set_target(sd,src);
+
sd->canlog_tick = gettick();
}
@@ -6189,6 +6195,9 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
if( sd->md )
merc_delete(sd->md, 3); // Your mercenary soldier has ran away.
+ if( sd->ed )
+ elemental_delete(sd->ed, 0);
+
// Leave duel if you die [LuzZza]
if(battle_config.duel_autoleave_when_die) {
if(sd->duel_group > 0)
diff --git a/src/map/pc.h b/src/map/pc.h
index e74d4a5d0..39ac3100a 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -205,7 +205,7 @@ struct map_session_data {
short cook_mastery; // range: [0,1999] [Inkfish]
unsigned char blockskill[MAX_SKILL];
int cloneskill_id, reproduceskill_id;
- int menuskill_id, menuskill_val;
+ int menuskill_id, menuskill_val, menuskill_val2;
int invincible_timer;
unsigned int canlog_tick;
@@ -377,6 +377,7 @@ struct map_session_data {
struct pet_data *pd;
struct homun_data *hd; // [blackhole89]
struct mercenary_data *md;
+ struct elemental_data *ed;
struct{
int m; //-1 - none, other: map index corresponding to map name.
diff --git a/src/map/script.c b/src/map/script.c
index d58233dc5..a2ffb54a0 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -7835,7 +7835,7 @@ BUILDIN_FUNC(cooking)
return 0;
trigger=script_getnum(st,2);
- clif_cooking_list(sd, trigger);
+ clif_cooking_list(sd, trigger, AM_PHARMACY, 1, 1);
return 0;
}
/*==========================================
diff --git a/src/map/skill.c b/src/map/skill.c
index d3ddc5562..252bb4bf1 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -20,6 +20,7 @@
#include "pet.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "mob.h"
#include "npc.h"
#include "battle.h"
@@ -49,6 +50,8 @@
#define MC_SKILLRANGEMAX (MC_SKILLRANGEMIN+MAX_MERCSKILL)
#define HM_SKILLRANGEMIN 700
#define HM_SKILLRANGEMAX (HM_SKILLRANGEMIN+MAX_HOMUNSKILL)
+#define EL_SKILLRANGEMIN MC_SKILLRANGEMAX + 1
+#define EL_SKILLRANGEMAX EL_SKILLRANGEMIN + MAX_ELEMENTALSKILL
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]
@@ -132,20 +135,19 @@ int skill_get_index( int id )
// avoid ranges reserved for mapping guild/homun/mercenary skills
if( (id >= GD_SKILLRANGEMIN && id <= GD_SKILLRANGEMAX)
|| (id >= HM_SKILLRANGEMIN && id <= HM_SKILLRANGEMAX)
- || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX) )
+ || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX)
+ || (id >= EL_SKILLRANGEMIN && id <= EL_SKILLRANGEMAX) )
return 0;
-
+
// map skill id to skill db index
if( id >= GD_SKILLBASE )
id = GD_SKILLRANGEMIN + id - GD_SKILLBASE;
- else
- if( id >= MC_SKILLBASE )
+ else if( id >= EL_SKILLBASE )
+ id = EL_SKILLRANGEMIN + id - EL_SKILLBASE;
+ else if( id >= MC_SKILLBASE )
id = MC_SKILLRANGEMIN + id - MC_SKILLBASE;
- else
- if( id >= HM_SKILLBASE )
+ else if( id >= HM_SKILLBASE )
id = HM_SKILLRANGEMIN + id - HM_SKILLBASE;
- else
- ; // identity
// validate result
if( id <= 0 || id >= MAX_SKILL_DB )
@@ -1294,6 +1296,50 @@ int skill_additional_effect (struct block_list* src, struct block_list *bl, int
status_change_end(bl, SC_UNLIMITEDHUMMINGVOICE, INVALID_TIMER);
}
break;
+ case SO_EARTHGRAVE:
+ sc_start(bl, SC_BLEEDING, 5 * skilllv, skilllv, skill_get_time2(skillid, skilllv)); // Need official rate. [LimitLine]
+ break;
+ case SO_DIAMONDDUST:
+ rate = 5 + 5 * skilllv;
+ if( sc && sc->data[SC_COOLER_OPTION] )
+ rate += rate * sc->data[SC_COOLER_OPTION]->val2 / 100;
+ sc_start(bl, SC_CRYSTALIZE, rate, skilllv, skill_get_time2(skillid, skilllv));
+ break;
+ case SO_VARETYR_SPEAR:
+ sc_start(bl, SC_STUN, 5 + 5 * skilllv, skilllv, skill_get_time2(skillid, skilllv));
+ break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ if( sd ) {
+ switch( sd->itemid ) { // Starting SCs here instead of do it in skill_additional_effect to simplify the code.
+ case 13261:
+ sc_start(bl, SC_STUN, 100, skilllv, skill_get_time2(GN_SLINGITEM, skilllv));
+ sc_start(bl, SC_BLEEDING, 100, skilllv, skill_get_time2(GN_SLINGITEM, skilllv));
+ break;
+ case 13262:
+ sc_start(bl, SC_MELON_BOMB, 100, skilllv, skill_get_time(GN_SLINGITEM, skilllv)); // Reduces ASPD and moviment speed
+ break;
+ case 13264:
+ sc_start(bl, SC_BANANA_BOMB, 100, skilllv, skill_get_time(GN_SLINGITEM, skilllv)); // Reduces LUK タ?Needed confirm it, may be it's bugged in kRORE?
+ sc_start(bl, SC_BANANA_BOMB_SITDOWN, 75, skilllv, skill_get_time(GN_SLINGITEM_RANGEMELEEATK,skilllv)); // Sitdown for 3 seconds.
+ break;
+ }
+ sd->itemid = -1;
+ }
+ break;
+ case EL_WIND_SLASH: // Non confirmed rate.
+ sc_start(bl, SC_BLEEDING, 25, skilllv, skill_get_time(skillid,skilllv));
+ break;
+ case EL_STONE_HAMMER:
+ rate = 10 * skilllv;
+ sc_start(bl, SC_STUN, rate, skilllv, skill_get_time(skillid,skilllv));
+ break;
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ sc_start(bl,status_skill2sc(skillid),50,skilllv,skill_get_time(EL_ROCK_CRUSHER,skilllv));
+ break;
+ case EL_TYPOON_MIS:
+ sc_start(bl,SC_SILENCE,10*skilllv,skilllv,skill_get_time(skillid,skilllv));
+ break;
}
if (md && battle_config.summons_trigger_autospells && md->master_id && md->special_state.ai)
@@ -2303,8 +2349,32 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
break;
case LG_OVERBRAND_BRANDISH:
case LG_OVERBRAND_PLUSATK:
+ case EL_FIRE_BOMB:
+ case EL_FIRE_BOMB_ATK:
+ case EL_FIRE_WAVE:
+ case EL_FIRE_WAVE_ATK:
+ case EL_FIRE_MANTLE:
+ case EL_CIRCLE_OF_FIRE:
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WATER_SCREW:
+ case EL_WATER_SCREW_ATK:
+ case EL_WIND_SLASH:
+ case EL_TIDAL_WEAPON:
+ case EL_ROCK_CRUSHER:
+ case EL_ROCK_CRUSHER_ATK:
+ case EL_HURRICANE:
+ case EL_HURRICANE_ATK:
+ case EL_TYPOON_MIS:
+ case EL_TYPOON_MIS_ATK:
dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skillid,-1,5);
break;
+ case GN_SLINGITEM_RANGEMELEEATK:
+ dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,GN_SLINGITEM,-2,6);
+ break;
+ case EL_STONE_RAIN:
+ dmg.dmotion = clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skillid,-1,(flag&1)?8:5);
+ break;
case WM_SEVERE_RAINSTORM_MELEE:
dmg.dmotion = clif_skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,WM_SEVERE_RAINSTORM,skilllv,5);
break;
@@ -2314,7 +2384,7 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
break;
case AB_DUPLELIGHT_MELEE:
case AB_DUPLELIGHT_MAGIC:
- dmg.amotion = 300;
+ dmg.amotion = 300;/* makes the damage value not overlap with previous damage (when displayed by the client) */
default:
if( flag&SD_ANIMATION && dmg.div_ < 2 ) //Disabling skill animation doesn't works on multi-hit.
type = 5;
@@ -2348,6 +2418,12 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
case WM_SEVERE_RAINSTORM_MELEE:
copy_skill = WM_SEVERE_RAINSTORM;
break;
+ case GN_CRAZYWEED_ATK:
+ copy_skill = GN_CRAZYWEED;
+ break;
+ case GN_HELLS_PLANT_ATK:
+ copy_skill = GN_HELLS_PLANT;
+ break;
case LG_OVERBRAND_BRANDISH:
case LG_OVERBRAND_PLUSATK:
copy_skill = LG_OVERBRAND;
@@ -2446,6 +2522,8 @@ int skill_attack (int attack_type, struct block_list* src, struct block_list *ds
case SC_TRIANGLESHOT:
case LG_OVERBRAND:
case SR_KNUCKLEARROW:
+ case GN_WALLOFTHORN:
+ case EL_FIRE_MANTLE:
direction = unit_getdir(bl);// backwards
break;
case WL_CRIMSONROCK:
@@ -3051,6 +3129,10 @@ static int skill_timerskill(int tid, unsigned int tick, int id, intptr_t data)
case SR_KNUCKLEARROW:
skill_attack(BF_WEAPON, src, src, target, skl->skill_id, skl->skill_lv, tick, skl->flag|SD_LEVEL);
break;
+ case GN_SPORE_EXPLOSION:
+ map_foreachinrange(skill_area_sub, target, skill_get_splash(skl->skill_id, skl->skill_lv), BL_CHAR,
+ src, skl->skill_id, skl->skill_lv, 0, skl->flag|1|BCT_ENEMY, skill_castend_damage_id);
+ break;
default:
skill_attack(skl->type,src,src,target,skl->skill_id,skl->skill_lv,tick,skl->flag);
break;
@@ -3315,6 +3397,7 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
case SR_GENTLETOUCH_QUIET:
case WM_SEVERE_RAINSTORM_MELEE:
case WM_GREAT_ECHO:
+ case GN_SLINGITEM_RANGEMELEEATK:
skill_attack(BF_WEAPON,src,src,bl,skillid,skilllv,tick,flag);
break;
@@ -3538,8 +3621,10 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
case SR_RIDEINLIGHTNING:
case WM_REVERBERATION:
case WM_SOUND_OF_DESTRUCTION:
- if( flag&1 )
- { //Recursive invocation
+ case SO_VARETYR_SPEAR:
+ case GN_CART_TORNADO:
+ case GN_CARTCANNON:
+ if( flag&1 ) {//Recursive invocation
// skill_area_temp[0] holds number of targets in area
// skill_area_temp[1] holds the id of the original target
// skill_area_temp[2] counts how many targets have already been processed
@@ -3550,20 +3635,26 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills)
heal = skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, sflag);
- if( skillid == NPC_VAMPIRE_GIFT && heal > 0 )
- {
+ if( skillid == NPC_VAMPIRE_GIFT && heal > 0 ) {
clif_skill_nodamage(NULL, src, AL_HEAL, heal, 1);
status_heal(src,heal,0,0);
}
- }
- else {
- if( skillid == NJ_BAKUENRYU || skillid == LG_EARTHDRIVE )
- clif_skill_nodamage(src,bl,skillid,skilllv,1);
- else if( skillid == LG_MOONSLASHER )
- clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
- //FIXME: Isn't EarthQuake a ground skill after all?
- else if( skillid == NPC_EARTHQUAKE )
- skill_addtimerskill(src,tick+250,src->id,0,0,skillid,skilllv,2,flag|BCT_ENEMY|SD_SPLASH|1);
+ } else {
+ switch ( skillid ) {
+ case NJ_BAKUENRYU:
+ case LG_EARTHDRIVE:
+ case GN_CARTCANNON:
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ break;
+ case LG_MOONSLASHER:
+ clif_skill_damage(src,bl,tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ break;
+ case NPC_EARTHQUAKE://FIXME: Isn't EarthQuake a ground skill after all?
+ skill_addtimerskill(src,tick+250,src->id,0,0,skillid,skilllv,2,flag|BCT_ENEMY|SD_SPLASH|1);
+ break;
+ default:
+ break;
+ }
skill_area_temp[0] = 0;
skill_area_temp[1] = bl->id;
@@ -3777,13 +3868,15 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
case NPC_SMOKING:
case GS_FLING:
case NJ_ZENYNAGE:
+ case GN_THORNS_TRAP:
+ case GN_BLOOD_SUCKER:
+ case GN_HELLS_PLANT_ATK:
skill_attack(BF_MISC,src,src,bl,skillid,skilllv,tick,flag);
break;
/**
* Rune Knight
**/
- case RK_DRAGONBREATH:
- {
+ case RK_DRAGONBREATH: {
struct status_change *tsc = NULL;
if( (tsc = status_get_sc(bl)) && (tsc->data[SC_HIDING] )) {
clif_skill_nodamage(src,src,skillid,skilllv,1);
@@ -4237,7 +4330,135 @@ int skill_castend_damage_id (struct block_list* src, struct block_list *bl, int
sc_start(bl,status_skill2sc(skillid),100,skilllv,skill_get_time(skillid,skilllv));
break;
- case 0:
+ case SO_POISON_BUSTER: {
+ struct status_change *tsc = status_get_sc(bl);
+ if( tsc && tsc->data[SC_POISON] ) {
+ skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag);
+ status_change_end(bl, SC_POISON, -1);
+ }
+ else if( sd )
+ clif_skill_fail(sd, skillid, 0, 0);
+ }
+ break;
+
+ case GN_SPORE_EXPLOSION:
+ if( flag&1 )
+ skill_attack(skill_get_type(skillid), src, src, bl, skillid, skilllv, tick, flag);
+ else {
+ clif_skill_nodamage(src, bl, skillid, 0, 1);
+ skill_addtimerskill(src, gettick() + skill_get_time(skillid, skilllv) - 1000, bl->id, 0, 0, skillid, skilllv, 0, 0);
+ }
+ break;
+
+ case GN_CRAZYWEED:
+ if( rand()%100 < 75 ) {
+ if( bl->type == BL_SKILL ) {
+ struct skill_unit *su = (struct skill_unit *)bl;
+ if( !su )
+ break;
+ if( skill_get_inf2(su->group->skill_id)&INF2_TRAP ) {// Still need confirm it.
+ skill_delunit(su);
+ break;
+ }
+
+ switch( su->group->skill_id ) {// Unconfirmed list, based on info from irowiki.
+ case GN_WALLOFTHORN:
+ case GN_THORNS_TRAP:
+ case SC_BLOODYLUST:
+ case SC_CHAOSPANIC:
+ case SC_MAELSTROM:
+ case WZ_FIREPILLAR:
+ case SA_LANDPROTECTOR:
+ case SA_VOLCANO:
+ case SA_DELUGE:
+ case SA_VIOLENTGALE:
+ case MG_SAFETYWALL:
+ case AL_PNEUMA:
+ skill_delunit(su);
+ break;
+ }
+ break;
+ }
+ else
+ skill_attack(BF_WEAPON,src,src,bl,GN_CRAZYWEED_ATK,skilllv,tick,flag);
+ }
+ break;
+
+ case EL_FIRE_BOMB:
+ case EL_FIRE_WAVE:
+ case EL_WATER_SCREW:
+ case EL_HURRICANE:
+ case EL_TYPOON_MIS:
+ if( flag&1 )
+ skill_attack(skill_get_type(skillid+1),src,src,bl,skillid+1,skilllv,tick,flag);
+ else {
+ int i = skill_get_splash(skillid,skilllv);
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( rand()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ }
+ break;
+
+ case EL_ROCK_CRUSHER:
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( rand()%100 < 50 )
+ skill_attack(BF_MAGIC,src,src,bl,skillid,skilllv,tick,flag);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,EL_ROCK_CRUSHER_ATK,skilllv,tick,flag);
+ break;
+
+ case EL_STONE_RAIN:
+ if( flag&1 )
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ else {
+ int i = skill_get_splash(skillid,skilllv);
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( rand()%100 < 30 )
+ map_foreachinrange(skill_area_sub,bl,i,BL_CHAR,src,skillid,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ }
+ break;
+
+ case EL_FIRE_ARROW:
+ case EL_ICE_NEEDLE:
+ case EL_WIND_SLASH:
+ case EL_STONE_HAMMER:
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ break;
+
+ case EL_TIDAL_WEAPON:
+ if( src->type == BL_ELEM ) {
+ struct elemental_data *ele = BL_CAST(BL_ELEM,src);
+ struct status_change *sc = status_get_sc(&ele->bl);
+ struct status_change *tsc = status_get_sc(bl);
+ sc_type type = status_skill2sc(skillid), type2;
+ type2 = type-1;
+
+ clif_skill_nodamage(src,battle_get_master(src),skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skillid);
+ }
+ if( rand()%100 < 50 )
+ skill_attack(skill_get_type(skillid),src,src,bl,skillid,skilllv,tick,flag);
+ else {
+ sc_start(src,type2,100,skilllv,skill_get_time(skillid,skilllv));
+ sc_start(battle_get_master(src),type,100,ele->bl.id,skill_get_time(skillid,skilllv));
+ }
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ }
+ break;
+
+
+ case 0:/* no skill - basic/normal attack */
if(sd) {
if (flag & 3){
if (bl->id != skill_area_temp[1])
@@ -4895,9 +5116,19 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
case SR_GENTLETOUCH_ENERGYGAIN:
case SR_GENTLETOUCH_CHANGE:
case SR_GENTLETOUCH_REVITALIZE:
+ case GN_CARTBOOST:
clif_skill_nodamage(src,bl,skillid,skilllv,
sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv)));
break;
+
+ case SO_STRIKING:
+ if (sd) {
+ int bonus = 25 + 10 * skilllv;
+ bonus += (pc_checkskill(sd, SA_FLAMELAUNCHER)+pc_checkskill(sd, SA_FROSTWEAPON)+pc_checkskill(sd, SA_LIGHTNINGLOADER)+pc_checkskill(sd, SA_SEISMICWEAPON))*5;
+ clif_skill_nodamage( src, bl, skillid, skilllv, sc_start2(bl, type, 100, skilllv, bonus, skill_get_time(skillid,skilllv)) );
+ }
+ break;
+
case NPC_STOP:
if( clif_skill_nodamage(src,bl,skillid,skilllv,
sc_start2(bl,type,100,skilllv,src->id,skill_get_time(skillid,skilllv)) ) )
@@ -5204,6 +5435,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
status_change_end(src,SC_OVERHEAT,-1);
break;
case SR_WINDMILL:
+ case GN_CART_TORNADO:
clif_skill_nodamage(src,bl,skillid,skilllv,1);
case SR_EARTHSHAKER:
case NC_INFRAREDSCAN:
@@ -6030,6 +6262,7 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
break;
case SA_CASTCANCEL:
+ case SO_SPELLFIST:
clif_skill_nodamage(src,bl,skillid,skilllv,1);
unit_skillcastcancel(src,1);
if(sd) {
@@ -6037,6 +6270,8 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
sp = sp * (90 - (skilllv-1)*20) / 100;
if(sp < 0) sp = 0;
status_zap(src, 0, sp);
+ if( skillid == SO_SPELLFIST )
+ sc_start4(src,type,100,skilllv+1,skilllv,(sd->skillid_old)?sd->skillid_old:MG_FIREBOLT,(sd->skilllv_old)?sd->skilllv_old:skilllv,skill_get_time(skillid,skilllv));
}
break;
case SA_SPELLBREAKER:
@@ -8079,6 +8314,240 @@ int skill_castend_nodamage_id (struct block_list *src, struct block_list *bl, in
}
break;
+ case SO_ARRULLO:
+ if( flag&1 )
+ sc_start2(bl, type, 88 + 2 * skilllv, skilllv, 1, skill_get_time(skillid, skilllv));
+ else {
+ clif_skill_nodamage(src, bl, skillid, 0, 1);
+ 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 SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ if( sd ) {
+ int elemental_class = skill_get_elemental_type(skillid,skilllv);
+
+ // Remove previous elemental fisrt.
+ if( sd->ed && elemental_delete(sd->ed,0) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ // Summoning the new one.
+ if( !elemental_create(sd,elemental_class,skill_get_time(skillid,skilllv)) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SO_EL_CONTROL:
+ if( sd ) {
+ int mode = EL_MODE_PASSIVE; // Standard mode.
+ if( !sd->ed ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if( skilllv == 4 ) {// At level 4 delete elementals.
+ if( elemental_delete(sd->ed, 0) )
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ switch( skilllv ) {// Select mode bassed on skill level used.
+ case 1: mode = EL_MODE_PASSIVE; break;
+ case 2: mode = EL_MODE_ASSIST; break;
+ case 3: mode = EL_MODE_AGGRESSIVE; break;
+ }
+ if( !elemental_change_mode(sd->ed,mode) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SO_EL_ACTION:
+ if( sd ) {
+ if( !sd->ed )
+ break;
+ elemental_action(sd->ed, bl, tick);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case SO_EL_CURE:
+ if( sd ) {
+ struct elemental_data *ed = sd->ed;
+ int s_hp = sd->battle_status.hp * 10 / 100, s_sp = sd->battle_status.sp * 10 / 100;
+ int e_hp, e_sp;
+ if( !ed ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ if( !status_charge(&sd->bl,s_hp,s_sp) ) {
+ clif_skill_fail(sd,skillid,0,0);
+ break;
+ }
+ e_hp = ed->battle_status.max_hp * 10 / 100;
+ e_sp = ed->battle_status.max_sp * 10 / 100;
+ status_heal(&ed->bl,e_hp,e_sp,3);
+ clif_skill_nodamage(src,&ed->bl,skillid,skilllv,1);
+ }
+ break;
+
+ case GN_CHANGEMATERIAL:
+ case SO_EL_ANALYSIS:
+ if( sd ) {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_itemlistwindow(sd,skillid,skilllv);
+ }
+ break;
+
+ case GN_BLOOD_SUCKER:
+ if( skill_unitsetting(src, skillid, skilllv, bl->x, bl->y, 0) ) { // Do we need this unit setting? [Xazax]
+ clif_skill_nodamage(src, bl, skillid, skilllv, 1);
+ sc_start2(bl, type, 100, skilllv, src->id, skill_get_time(skillid,skilllv));
+ } else if( sd ) {
+ clif_skill_fail(sd, skillid, 0, 0);
+ break;
+ }
+ break;
+
+ case GN_MANDRAGORA:
+ if( flag&1 ) {
+ if ( clif_skill_nodamage(bl, src, skillid, skilllv,
+ sc_start(bl, type, 35 + 10 * skilllv, skilllv, skill_get_time(skillid, skilllv))) )
+ status_zap(bl, 0, status_get_max_sp(bl) / 100 * 25 + 5 * skilllv);
+ } else
+ 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 GN_SLINGITEM:
+ if( sd ) {
+ short ammo_id;
+ i = sd->equip_index[EQI_AMMO];
+ if( i <= 0 )
+ break; // No ammo.
+ ammo_id = sd->inventory_data[i]->nameid;
+ if( ammo_id <= 0 )
+ break;
+ sd->itemid = ammo_id;
+ if( itemdb_is_GNbomb(ammo_id) ) {
+ if(battle_check_target(src,bl,BCT_ENEMY) > 0) {// Only attack if the target is an enemy.
+ if( ammo_id == 13263 )
+ map_foreachincell(skill_area_sub,bl->m,bl->x,bl->y,BL_CHAR,src,GN_SLINGITEM_RANGEMELEEATK,skilllv,tick,flag|BCT_ENEMY|1,skill_castend_damage_id);
+ else
+ skill_attack(BF_WEAPON,src,src,bl,GN_SLINGITEM_RANGEMELEEATK,skilllv,tick,flag);
+ } else //Otherwise, it fails, shows animation and removes items.
+ clif_skill_fail(sd,GN_SLINGITEM_RANGEMELEEATK,0xa,0);
+ } else {
+ struct script_code *script = sd->inventory_data[i]->script;
+ if( !script )
+ break;
+ if( dstsd )
+ run_script(script,0,dstsd->bl.id,fake_nd->bl.id);
+ else
+ run_script(script,0,src->id,0);
+ }
+ }
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);// This packet is received twice actually, I think it is to show the animation.
+ break;
+
+ case GN_MIX_COOKING:
+ if( sd ) {
+ clif_cooking_list(sd,27,skillid,(skilllv == 2) ? 10 : 1,6);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case GN_MAKEBOMB:
+ if( sd ) {
+ clif_cooking_list(sd,28,skillid,(skilllv==2) ? 10 : 1,5);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case GN_S_PHARMACY:
+ if( sd ) {
+ sd->skillid_old = skillid;
+ sd->skilllv_old = skilllv;
+ clif_cooking_list(sd,29,skillid,1,6);
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ }
+ break;
+
+ case EL_CIRCLE_OF_FIRE:
+ case EL_PYROTECHNIC:
+ case EL_HEATER:
+ case EL_TROPIC:
+ case EL_AQUAPLAY:
+ case EL_COOLER:
+ case EL_CHILLY_AIR:
+ case EL_GUST:
+ case EL_BLAST:
+ case EL_WILD_STORM:
+ case EL_PETROLOGY:
+ case EL_CURSED_SOIL:
+ case EL_UPHEAVAL:
+ case EL_FIRE_CLOAK:
+ case EL_WATER_DROP:
+ case EL_WIND_CURTAIN:
+ case EL_SOLID_SKIN:
+ case EL_STONE_SHIELD:
+ case EL_WIND_STEP: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ sc_type type2 = type-1;
+ struct status_change *sc = status_get_sc(&ele->bl);
+
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skillid);
+ } else {
+ clif_skill_nodamage(src,bl,skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ if( skillid == EL_WIND_STEP ) // There aren't telemport, just push to the master.
+ skill_blown(src,bl,skill_get_blewcount(skillid,skilllv),(map_calc_dir(src,bl->x,bl->y)+4)%8,0);
+ sc_start(src,type2,100,skilllv,skill_get_time(skillid,skilllv));
+ sc_start(bl,type,100,skilllv,skill_get_time(skillid,skilllv));
+ }
+ }
+ }
+ break;
+
+ case EL_FIRE_MANTLE:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ skill_unitsetting(src,skillid,skilllv,bl->x,bl->y,0);
+ break;
+
+ case EL_WATER_SCREEN: {
+ struct elemental_data *ele = BL_CAST(BL_ELEM, src);
+ if( ele ) {
+ struct status_change *sc = status_get_sc(&ele->bl);
+ sc_type type2 = type-1;
+
+ clif_skill_nodamage(src,src,skillid,skilllv,1);
+ if( (sc && sc->data[type2]) || (tsc && tsc->data[type]) ) {
+ elemental_clean_single_effect(ele, skillid);
+ } else {
+ // This not heals at the end.
+ clif_skill_damage(src, src, tick, status_get_amotion(src), 0, -30000, 1, skillid, skilllv, 6);
+ sc_start(src,type2,100,skilllv,skill_get_time(skillid,skilllv));
+ sc_start(bl,type,100,src->id,skill_get_time(skillid,skilllv));
+ }
+ }
+ }
+ break;
+
default:
ShowWarning("skill_castend_nodamage_id: Unknown skill used:%d\n",skillid);
clif_skill_nodamage(src,bl,skillid,skilllv,1);
@@ -8142,8 +8611,8 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
return 0;
}
- if(ud->skillid != SA_CASTCANCEL )
- {// otherwise handled in unit_skillcastcancel()
+ if(ud->skillid != SA_CASTCANCEL &&
+ !(ud->skillid == SO_SPELLFIST && (sd && (sd->skillid_old == MG_FIREBOLT || sd->skillid_old == MG_COLDBOLT || sd->skillid_old == MG_LIGHTNINGBOLT))) ) {// otherwise handled in unit_skillcastcancel()
if( ud->skilltimer != tid ) {
ShowError("skill_castend_id: Timer mismatch %d!=%d!\n", ud->skilltimer, tid);
ud->skilltimer = INVALID_TIMER;
@@ -8188,6 +8657,11 @@ int skill_castend_id(int tid, unsigned int tick, int id, intptr_t data)
}
ud->skilltimer=tid;
return skill_castend_pos(tid,tick,id,data);
+ case GN_WALLOFTHORN:
+ ud->skillx = target->x;
+ ud->skilly = target->y;
+ ud->skilltimer = tid;
+ return skill_castend_pos(tid,tick,id,data);
}
if(ud->skillid == RG_BACKSTAP) {
@@ -8736,6 +9210,18 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
case WM_REVERBERATION:
case WM_SEVERE_RAINSTORM:
case WM_POEMOFNETHERWORLD:
+ case SO_PSYCHIC_WAVE:
+ case SO_VACUUM_EXTREME:
+ case GN_WALLOFTHORN:
+ case GN_THORNS_TRAP:
+ case GN_DEMONIC_FIRE:
+ case GN_HELLS_PLANT:
+ case SO_EARTHGRAVE:
+ case SO_DIAMONDDUST:
+ case SO_FIRE_INSIGNIA:
+ case SO_WATER_INSIGNIA:
+ case SO_WIND_INSIGNIA:
+ case SO_EARTH_INSIGNIA:
flag|=1;//Set flag to 1 to prevent deleting ammo (it will be deleted on group-delete).
case GS_GROUNDDRIFT: //Ammo should be deleted right away.
skill_unitsetting(src,skillid,skilllv,x,y,0);
@@ -8768,6 +9254,12 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
map_foreachinarea(skill_graffitiremover,src->m,x-i,y-i,x+i,y+i,BL_SKILL);
break;
+ case SO_CLOUD_KILL:
+ case SO_WARMER:
+ flag|=(skillid == SO_WARMER)?8:4;
+ skill_unitsetting(src,skillid,skilllv,x,y,0);
+ break;
+
case WZ_METEOR: {
int area = skill_get_splash(skillid, skilllv);
short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0;
@@ -9131,6 +9623,55 @@ int skill_castend_pos2(struct block_list* src, int x, int y, int skillid, int sk
map_foreachinrange(skill_area_sub, src, skill_get_splash(skillid,skilllv),BL_CHAR, src, skillid, skilllv, tick, flag|BCT_ENEMY, skill_castend_damage_id);
break;
+ case GN_CRAZYWEED:
+ i = skill_get_splash(skillid,skilllv);
+ map_foreachinarea(skill_area_sub,src->m,x-i,y-i,x+i,y+i,BL_CHAR|BL_SKILL,
+ src,skillid,skilllv,tick,flag|BCT_ENEMY|1,
+ skill_castend_damage_id);
+ break;
+
+ case GN_FIRE_EXPANSION: {
+ int i;
+ struct unit_data *ud = unit_bl2ud(src);
+
+ if( !ud ) break;
+
+ for( i = 0; i < MAX_SKILLUNITGROUP && ud->skillunit[i]; i ++ ) {
+ if( ud->skillunit[i]->skill_id == GN_DEMONIC_FIRE &&
+ distance_xy(x, y, ud->skillunit[i]->unit->bl.x, ud->skillunit[i]->unit->bl.y) < 4 ) {
+ switch( skilllv ) {
+ case 3:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_SMOKE_POWDER;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_SMOKE_POWDER);
+ break;
+ case 4:
+ ud->skillunit[i]->unit_id = UNT_FIRE_EXPANSION_TEAR_GAS;
+ clif_changetraplook(&ud->skillunit[i]->unit->bl, UNT_FIRE_EXPANSION_TEAR_GAS);
+ break;
+ case 5:
+ map_foreachinarea(skill_area_sub, src->m,
+ ud->skillunit[i]->unit->bl.x - 3, ud->skillunit[i]->unit->bl.y - 3,
+ ud->skillunit[i]->unit->bl.x + 3, ud->skillunit[i]->unit->bl.y + 3, BL_CHAR,
+ src, CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : skilllv, tick, flag|BCT_ENEMY|1|SD_LEVEL, skill_castend_damage_id);
+ skill_delunit(ud->skillunit[i]->unit);
+ break;
+ default:
+ ud->skillunit[i]->unit->val2 = skilllv;
+ ud->skillunit[i]->unit->group->val2 = skilllv;
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK:
+ if( sc && sc->data[type] )
+ status_change_end(src,type,-1);
+ clif_skill_nodamage(src, src ,skillid, skilllv,
+ sc_start2(src, type, 100, skillid, skilllv, skill_get_time(skillid, skilllv)));
+ break;
default:
ShowWarning("skill_castend_pos2: Unknown skill used:%d\n",skillid);
@@ -9683,6 +10224,18 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli
if( map_getcell(src->m, x, y, CELL_CHKLANDPROTECTOR) )
return NULL;
break;
+ case SO_CLOUD_KILL:
+ skill_clear_group(src, 4);
+ break;
+ case SO_WARMER:
+ skill_clear_group(src, 8);
+ break;
+
+ case GN_WALLOFTHORN:
+ if( flag&1 )
+ limit = 3000;
+ val3 = (x<<16)|y;
+ break;
}
nullpo_retr(NULL, group=skill_initunitgroup(src,layout->count,skillid,skilllv,skill_get_unit_id(skillid,flag&1)+subunt, limit, interval));
@@ -9784,6 +10337,10 @@ struct skill_unit_group* skill_unitsetting (struct block_list *src, short skilli
case WM_REVERBERATION:
val1 = 1;
break;
+ case GN_WALLOFTHORN:
+ val1 = 1000 * skilllv; // Need official value. [LimitLine]
+ val2 = src->id;
+ break;
default:
if (group->state.song_dance&0x1)
val2 = unit_flag&(UF_DANCE|UF_SONG); //Store whether this is a song/dance
@@ -10084,7 +10641,10 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns
{
int count=0;
const int x = bl->x, y = bl->y;
-
+
+ if( sg->skill_id == GN_WALLOFTHORN && !map_flag_vs(bl->m) )
+ break;
+
//Take into account these hit more times than the timer interval can handle.
do
skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick+count*sg->interval,0);
@@ -10524,6 +11084,86 @@ int skill_unit_onplace_timer (struct skill_unit *src, struct block_list *bl, uns
sc_start(bl, type, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
}
break;
+ case UNT_THORNS_TRAP:
+ if( tsc ) {
+ if( !sg->val2 ) {
+ int sec = skill_get_time2(sg->skill_id, sg->skill_lv);
+ if( status_change_start(bl, type, 10000, sg->skill_lv, 0, 0, 0, sec, 0) ) {
+ const struct TimerData* td = tsc->data[type]?get_timer(tsc->data[type]->timer):NULL;
+ 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?
+ sg->limit = DIFF_TICK(tick, sg->tick) + sec;
+ } else if( tsc->data[SC_THORNSTRAP] && bl->id == sg->val2 )
+ skill_attack(skill_get_type(GN_THORNS_TRAP), ss, ss, bl, sg->skill_id, sg->skill_lv, tick, SD_LEVEL|SD_ANIMATION);
+ }
+ break;
+
+ case UNT_DEMONIC_FIRE: {
+ TBL_PC* sd = BL_CAST(BL_PC, ss);
+ switch( sg->val2 ) {
+ case 1:
+ case 2:
+ default:
+ sc_start(bl, SC_BURNING, 4 + 4 * sg->skill_lv, sg->skill_lv,
+ skill_get_time2(sg->skill_id, sg->skill_lv));
+ skill_attack(skill_get_type(sg->skill_id), ss, &src->bl, bl,
+ sg->skill_id, sg->skill_lv + 10 * sg->val2, tick, 0);
+ break;
+ case 3:
+ skill_attack(skill_get_type(CR_ACIDDEMONSTRATION), ss, &src->bl, bl,
+ CR_ACIDDEMONSTRATION, sd ? pc_checkskill(sd, CR_ACIDDEMONSTRATION) : sg->skill_lv, tick, 0);
+ break;
+
+ }
+ }
+ break;
+
+ case UNT_FIRE_EXPANSION_SMOKE_POWDER:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_SMOKE_POWDER), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_FIRE_EXPANSION_TEAR_GAS:
+ sc_start(bl, status_skill2sc(GN_FIRE_EXPANSION_TEAR_GAS), 100, sg->skill_lv, 1000);
+ break;
+
+ case UNT_HELLS_PLANT:
+ if( battle_check_target(&src->bl,bl,BCT_ENEMY) > 0 )
+ skill_attack(skill_get_type(GN_HELLS_PLANT_ATK), ss, &src->bl, bl, GN_HELLS_PLANT_ATK, sg->skill_lv, tick, 0);
+ sg->limit = DIFF_TICK(tick, sg->tick) + 100;
+ break;
+
+ case UNT_CLOUD_KILL:
+ if(tsc && !tsc->data[type])
+ status_change_start(bl,type,10000,sg->skill_lv,sg->group_id,0,0,skill_get_time2(sg->skill_id,sg->skill_lv),8);
+ skill_attack(skill_get_type(sg->skill_id),ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
+
+ case UNT_WARMER:
+ if( bl->type == BL_PC && !battle_check_undead(tstatus->race, tstatus->def_ele) && tstatus->race != RC_DEMON ) {
+ int hp = 125 * sg->skill_lv; // Officially is 125 * skill_lv.
+ struct status_change *ssc = status_get_sc(ss);
+ if( ssc && ssc->data[SC_HEATER_OPTION] )
+ hp += hp * ssc->data[SC_HEATER_OPTION]->val3 / 100;
+ status_heal(bl, hp, 0, 0);
+ if( tstatus->hp != tstatus->max_hp )
+ clif_skill_nodamage(&src->bl, bl, AL_HEAL, hp, 0);
+ sc_start(bl, SC_WARMER, 100, sg->skill_lv, skill_get_time2(sg->skill_id,sg->skill_lv));
+ }
+ break;
+
+ case UNT_VACUUM_EXTREME:
+ sc_start(bl, SC_VACUUM_EXTREME, 100, sg->skill_lv, sg->limit);
+ break;
+
+ case UNT_FIRE_MANTLE:
+ if( battle_check_target(&src->bl, bl, BCT_ENEMY) )
+ skill_attack(BF_MAGIC,ss,&src->bl,bl,sg->skill_id,sg->skill_lv,tick,0);
+ break;
}
@@ -10639,6 +11279,9 @@ static int skill_unit_onleft (int skill_id, struct block_list *bl, unsigned int
case NJ_SUITON:
case SC_MAELSTROM:
case SC_BLOODYLUST:
+ case EL_WATER_BARRIER:
+ case EL_ZEPHYR:
+ case EL_POWER_OF_GAIA:
if (sce)
status_change_end(bl, type, INVALID_TIMER);
break;
@@ -10968,20 +11611,26 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
return 1;
}
- if( sd->menuskill_id == AM_PHARMACY )
- {
- switch( skill )
- {
+ switch( sd->menuskill_id ) {
case AM_PHARMACY:
- case AC_MAKINGARROW:
- case BS_REPAIRWEAPON:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- return 0;
- }
+ switch( skill ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill )
+ return 0;
+ break;
}
-
status = &sd->battle_status;
sc = &sd->sc;
if( !sc->count )
@@ -11041,10 +11690,8 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
sd->state.arrow_atk = require.ammo?1:0;
// Check the skills that can be used while mounted on a warg
- if( pc_isridingwug(sd) )
- {
- switch( skill )
- {
+ if( pc_isridingwug(sd) ) {
+ switch( skill ) {
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_SPRINGTRAP: case RA_DETONATOR: case RA_CLUSTERBOMB:
@@ -11057,473 +11704,493 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
}
// perform skill-specific checks (and actions)
- switch( skill )
- {
- case SA_CASTCANCEL:
- if(sd->ud.skilltimer == INVALID_TIMER) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case AL_WARP:
- 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,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case MO_FINGEROFFENSIVE:
- case GS_FLING:
- case SR_RAMPAGEBLASTER:
- case SR_RIDEINLIGHTNING:
- if( sd->spiritball > 0 && sd->spiritball < require.spiritball )
- sd->spiritball_old = require.spiritball = sd->spiritball;
- else
- sd->spiritball_old = require.spiritball;
- break;
- case MO_CHAINCOMBO:
- if(!sc)
- return 0;
- if(sc->data[SC_BLADESTOP])
+ switch( skill ) {
+ case SA_CASTCANCEL:
+ case SO_SPELLFIST:
+ if(sd->ud.skilltimer == INVALID_TIMER) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
break;
- if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK)
+ case AL_WARP:
+ 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;
- return 0;
- case MO_COMBOFINISH:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO))
- return 0;
- break;
- case CH_TIGERFIST:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH))
- return 0;
- break;
- case CH_CHAINCRUSH:
- if(!(sc && sc->data[SC_COMBO]))
- return 0;
- if(sc->data[SC_COMBO]->val1 != MO_COMBOFINISH && sc->data[SC_COMBO]->val1 != CH_TIGERFIST)
+ case MO_CALLSPIRITS:
+ if(sd->spiritball >= lv) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case MO_FINGEROFFENSIVE:
+ case GS_FLING:
+ case SR_RAMPAGEBLASTER:
+ case SR_RIDEINLIGHTNING:
+ if( sd->spiritball > 0 && sd->spiritball < require.spiritball )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else
+ sd->spiritball_old = require.spiritball;
+ break;
+ case MO_CHAINCOMBO:
+ if(!sc)
+ return 0;
+ if(sc->data[SC_BLADESTOP])
+ break;
+ if(sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_TRIPLEATTACK)
+ break;
return 0;
- break;
- case MO_EXTREMITYFIST:
-// if(sc && sc->data[SC_EXTREMITYFIST]) //To disable Asura during the 5 min skill block uncomment this...
-// return 0;
- if( sc && sc->data[SC_BLADESTOP] )
+ case MO_COMBOFINISH:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_CHAINCOMBO))
+ return 0;
break;
- if( sc && sc->data[SC_COMBO] )
- {
- switch(sc->data[SC_COMBO]->val1) {
- case MO_COMBOFINISH:
- case CH_TIGERFIST:
- case CH_CHAINCRUSH:
+ case CH_TIGERFIST:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == MO_COMBOFINISH))
+ return 0;
+ break;
+ case CH_CHAINCRUSH:
+ if(!(sc && sc->data[SC_COMBO]))
+ 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]) //To disable Asura during the 5 min skill block uncomment this...
+ // return 0;
+ if( sc && sc->data[SC_BLADESTOP] )
+ break;
+ if( sc && sc->data[SC_COMBO] )
+ {
+ switch(sc->data[SC_COMBO]->val1) {
+ case MO_COMBOFINISH:
+ case CH_TIGERFIST:
+ case CH_CHAINCRUSH:
+ break;
+ default:
+ return 0;
+ }
+ }
+ else if( !unit_can_move(&sd->bl) )
+ { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex]
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_MISSION:
+ if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON )
+ {// Cannot be used by Non-Taekwon classes
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,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 )
+ {// Soul Linkers cannot use this skill
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case TK_TURNKICK:
+ case TK_STORMKICK:
+ case TK_DOWNKICK:
+ case TK_COUNTER:
+ if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
+ return 0; //Anti-Soul Linker check in case you job-changed with Stances active.
+ if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK)
+ return 0; //Combo needs to be ready
+
+ if (sc->data[SC_COMBO]->val3) { //Kick chain
+ //Do not repeat a kick.
+ if (sc->data[SC_COMBO]->val3 != skill)
break;
- default:
+ status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER);
+ return 0;
+ }
+ if(sc->data[SC_COMBO]->val1 != skill && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait.
+ unit_cancel_combo(&sd->bl);
+ return 0;
+ }
+ break; //Combo ready.
+ case BD_ADAPTATION:
+ {
+ int time;
+ if(!(sc && sc->data[SC_DANCING]))
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ time = 1000*(sc->data[SC_DANCING]->val3>>16);
+ if (skill_get_time(
+ (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID
+ (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV
+ - time < skill_get_time2(skill,lv))
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
+ }
}
- }
- else if( !unit_can_move(&sd->bl) )
- { //Placed here as ST_MOVE_ENABLE should not apply if rooted or on a combo. [Skotlex]
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
+ break;
- case TK_MISSION:
- if( (sd->class_&MAPID_UPPERMASK) != MAPID_TAEKWON )
- {// Cannot be used by Non-Taekwon classes
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
+ case PR_BENEDICTIO:
+ if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2)
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,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 )
- {// Soul Linkers cannot use this skill
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
+ case SL_SMA:
+ if(!(sc && sc->data[SC_SMA]))
+ return 0;
+ break;
- case TK_TURNKICK:
- case TK_STORMKICK:
- case TK_DOWNKICK:
- case TK_COUNTER:
- if ((sd->class_&MAPID_UPPERMASK) == MAPID_SOUL_LINKER)
- return 0; //Anti-Soul Linker check in case you job-changed with Stances active.
- if(!(sc && sc->data[SC_COMBO]) || sc->data[SC_COMBO]->val1 == TK_JUMPKICK)
- return 0; //Combo needs to be ready
+ case HT_POWER:
+ if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill))
+ return 0;
+ break;
- if (sc->data[SC_COMBO]->val3) { //Kick chain
- //Do not repeat a kick.
- if (sc->data[SC_COMBO]->val3 != skill)
- break;
- status_change_end(&sd->bl, SC_COMBO, INVALID_TIMER);
- return 0;
- }
- if(sc->data[SC_COMBO]->val1 != skill && !( sd && sd->status.base_level >= 90 && pc_famerank(sd->status.char_id, MAPID_TAEKWON) )) { //Cancel combo wait.
- unit_cancel_combo(&sd->bl);
- return 0;
- }
- break; //Combo ready.
- case BD_ADAPTATION:
- {
- int time;
- if(!(sc && sc->data[SC_DANCING]))
+ case CG_HERMODE:
+ if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill, lv)))
{
clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
}
- time = 1000*(sc->data[SC_DANCING]->val3>>16);
- if (skill_get_time(
- (sc->data[SC_DANCING]->val1&0xFFFF), //Dance Skill ID
- (sc->data[SC_DANCING]->val1>>16)) //Dance Skill LV
- - time < skill_get_time2(skill,lv))
+ 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,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ }
+ }
+ break;
+ case PR_REDEMPTIO:
+ {
+ int exp;
+ if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) ||
+ ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,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,USESKILL_FAIL_LEVEL,0);
return 0;
}
- }
- break;
-
- case PR_BENEDICTIO:
- if (skill_check_pc_partner(sd, skill, &lv, 1, 0) < 2)
- {
+ break;
+ case SG_SUN_WARM:
+ case SG_MOON_WARM:
+ case SG_STAR_WARM:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill-SG_SUN_WARM;
+ if (sd->bl.m == sd->feel_map[i].m)
+ break;
clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
- }
- break;
-
- case SL_SMA:
- if(!(sc && sc->data[SC_SMA]))
+ break;
+ case SG_SUN_COMFORT:
+ case SG_MOON_COMFORT:
+ case SG_STAR_COMFORT:
+ if (sc && sc->data[SC_MIRACLE])
+ break;
+ i = skill-SG_SUN_COMFORT;
+ if (sd->bl.m == sd->feel_map[i].m &&
+ (battle_config.allow_skill_without_day || sg_info[i].day_func()))
+ break;
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
return 0;
- break;
-
- case HT_POWER:
- if(!(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == skill))
+ case SG_FUSION:
+ if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR)
+ break;
+ //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex]
+ //Only invoke on skill begin cast (instant cast skill). [Kevin]
+ if( require.sp > 0 )
+ {
+ if (status->sp < (unsigned int)require.sp)
+ clif_skill_fail(sd,skill,USESKILL_FAIL_SP_INSUFFICIENT,0);
+ else
+ status_zap(&sd->bl, 0, require.sp);
+ }
return 0;
- break;
+ case GD_BATTLEORDER:
+ case GD_REGENERATION:
+ case GD_RESTORE:
+ if (!map_flag_gvg2(sd->bl.m)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case GD_EMERGENCYCALL:
+ // other checks were already done in skillnotok()
+ if (!sd->status.guild_id || !sd->state.gmaster_flag)
+ return 0;
+ break;
- case CG_HERMODE:
- if(!npc_check_areanpc(1,sd->bl.m,sd->bl.x,sd->bl.y,skill_get_splash(skill, lv)))
- {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,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,USESKILL_FAIL_LEVEL,0);
+ case GS_GLITTERING:
+ if(sd->spiritball >= 10) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ISSEN:
+ if (status->hp < 2) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ case NJ_BUNSINJYUTSU:
+ if (!(sc && sc->data[SC_NEN])) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+
+ case NJ_ZENYNAGE:
+ if(sd->status.zeny < require.zeny) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_MONEY,0);
+ return 0;
+ }
+ break;
+ case PF_HPCONVERSION:
+ if (status->sp == status->max_sp)
+ return 0; //Unusable when at full SP.
+ break;
+ case AM_CALLHOMUN: //Can't summon if a hom is already out
+ if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80%
+ if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100))
+ {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Arch Bishop
+ **/
+ case AB_ANCILLA:
+ {
+ int count = 0;
+ for( i = 0; i < MAX_INVENTORY; i ++ )
+ if( sd->status.inventory[i].nameid == ITEMID_ANCILLA )
+ count += sd->status.inventory[i].amount;
+ if( count >= 3 ) {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_ANCILLA_NUMOVER, 0);
return 0;
}
}
- }
- break;
- case PR_REDEMPTIO:
- {
- int exp;
- if( ((exp = pc_nextbaseexp(sd)) > 0 && get_percentage(sd->status.base_exp, exp) < 1) ||
- ((exp = pc_nextjobexp(sd)) > 0 && get_percentage(sd->status.job_exp, exp) < 1)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0); //Not enough exp.
+ break;
+ /**
+ * Keeping as a note:
+ * Bug Report #17 provides a link to a sep-2011 changelog that shows this requirement was removed
+ **/
+ //case AB_LAUDAAGNUS:
+ //case AB_LAUDARAMUS:
+ // if( !sd->status.party_id ) {
+ // clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ // return 0;
+ // }
+ // break;
+
+ case AB_ADORAMUS:
+ /**
+ * Warlock
+ **/
+ case WL_COMET:
+ if( skill_check_pc_partner(sd,skill,&lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) )
+ {
+ //clif_skill_fail(sd,skill,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
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,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case SG_SUN_WARM:
- case SG_MOON_WARM:
- case SG_STAR_WARM:
- if (sc && sc->data[SC_MIRACLE])
+ case WL_SUMMONFB:
+ case WL_SUMMONBL:
+ case WL_SUMMONWB:
+ case WL_SUMMONSTONE:
+ if( sc )
+ {
+ ARR_FIND(SC_SPHERE_1,SC_SPHERE_5+1,i,!sc->data[i]);
+ if( i == SC_SPHERE_5+1 )
+ { // No more free slots
+ clif_skill_fail(sd,skill,USESKILL_FAIL_SUMMON,0);
+ return 0;
+ }
+ }
break;
- i = skill-SG_SUN_WARM;
- if (sd->bl.m == sd->feel_map[i].m)
+ /**
+ * Guilotine Cross
+ **/
+ case GC_HALLUCINATIONWALK:
+ if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
break;
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- break;
- case SG_SUN_COMFORT:
- case SG_MOON_COMFORT:
- case SG_STAR_COMFORT:
- if (sc && sc->data[SC_MIRACLE])
+ case GC_COUNTERSLASH:
+ case GC_WEAPONCRUSH:
+ if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING) ) {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_GC_WEAPONBLOCKING, 0);
+ return 0;
+ }
break;
- i = skill-SG_SUN_COMFORT;
- if (sd->bl.m == sd->feel_map[i].m &&
- (battle_config.allow_skill_without_day || sg_info[i].day_func()))
+ case GC_CROSSRIPPERSLASHER:
+ if( !(sc && sc->data[SC_ROLLINGCUTTER]) ) {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_CONDITION, 0);
+ return 0;
+ }
break;
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- case SG_FUSION:
- if (sc && sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_STAR)
+ case GC_POISONSMOKE:
+ case GC_VENOMPRESSURE:
+ if( !(sc && sc->data[SC_POISONINGWEAPON]) ) {
+ clif_skill_fail(sd, skill, USESKILL_FAIL_GC_POISONINGWEAPON, 0);
+ return 0;
+ }
break;
- //Auron insists we should implement SP consumption when you are not Soul Linked. [Skotlex]
- //Only invoke on skill begin cast (instant cast skill). [Kevin]
- if( require.sp > 0 )
- {
- if (status->sp < (unsigned int)require.sp)
- clif_skill_fail(sd,skill,USESKILL_FAIL_SP_INSUFFICIENT,0);
- else
- status_zap(&sd->bl, 0, require.sp);
- }
- return 0;
- case GD_BATTLEORDER:
- case GD_REGENERATION:
- case GD_RESTORE:
- if (!map_flag_gvg2(sd->bl.m)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case GD_EMERGENCYCALL:
- // other checks were already done in skillnotok()
- if (!sd->status.guild_id || !sd->state.gmaster_flag)
- return 0;
- break;
-
- case GS_GLITTERING:
- if(sd->spiritball >= 10) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case NJ_ISSEN:
- if (status->hp < 2) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- case NJ_BUNSINJYUTSU:
- if (!(sc && sc->data[SC_NEN])) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
-
- case NJ_ZENYNAGE:
- if(sd->status.zeny < require.zeny) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_MONEY,0);
- return 0;
- }
- break;
- case PF_HPCONVERSION:
- if (status->sp == status->max_sp)
- return 0; //Unusable when at full SP.
- break;
- case AM_CALLHOMUN: //Can't summon if a hom is already out
- if (sd->status.hom_id && sd->hd && !sd->hd->homunculus.vaporize) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case AM_REST: //Can't vapo homun if you don't have an active homunc or it's hp is < 80%
- if (!merc_is_hom_active(sd->hd) || sd->hd->battle_status.hp < (sd->hd->battle_status.max_hp*80/100))
- {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Arch Bishop
- **/
- case AB_ANCILLA:
- {
- int count = 0;
- for( i = 0; i < MAX_INVENTORY; i ++ )
- if( sd->status.inventory[i].nameid == ITEMID_ANCILLA )
- count += sd->status.inventory[i].amount;
- if( count >= 3 ) {
- clif_skill_fail(sd, skill, USESKILL_FAIL_ANCILLA_NUMOVER, 0);
+ /**
+ * Ranger
+ **/
+ case RA_SENSITIVEKEEN:
+ if(!pc_iswug(sd)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_CONDITION,0);
return 0;
}
- }
- break;
- /**
- * Keeping as a note:
- * Bug Report #17 provides a link to a sep-2011 changelog that shows this requirement was removed
- **/
- //case AB_LAUDAAGNUS:
- //case AB_LAUDARAMUS:
- // if( !sd->status.party_id ) {
- // clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- // return 0;
- // }
- // break;
-
- case AB_ADORAMUS:
- /**
- * Warlock
- **/
- case WL_COMET:
- if( skill_check_pc_partner(sd,skill,&lv,1,0) <= 0 && ((i = pc_search_inventory(sd,require.itemid[0])) < 0 || sd->status.inventory[i].amount < require.amount[0]) )
- {
- //clif_skill_fail(sd,skill,USESKILL_FAIL_NEED_ITEM,require.amount[0],require.itemid[0]);
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case WL_SUMMONFB:
- case WL_SUMMONBL:
- case WL_SUMMONWB:
- case WL_SUMMONSTONE:
- if( sc )
- {
- ARR_FIND(SC_SPHERE_1,SC_SPHERE_5+1,i,!sc->data[i]);
- if( i == SC_SPHERE_5+1 )
- { // No more free slots
- clif_skill_fail(sd,skill,USESKILL_FAIL_SUMMON,0);
+ break;
+ case RA_WUGMASTERY:
+ if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGRIDER:
+ if( !pc_isridingwug(sd) && !pc_iswug(sd) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case RA_WUGDASH:
+ if(!pc_isridingwug(sd)) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ /**
+ * Royal Guard
+ **/
+ case LG_BANDING:
+ if( sc && sc->data[SC_INSPIRATION] ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_PRESTIGE:
+ if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case LG_RAGEBURST:
+ if( sd->spiritball == 0 ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_SKILLINTERVAL,0);
return 0;
}
- }
- break;
- /**
- * Guilotine Cross
- **/
- case GC_HALLUCINATIONWALK:
- if( sc && (sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY]) ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case GC_COUNTERSLASH:
- case GC_WEAPONCRUSH:
- if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == GC_WEAPONBLOCKING) ) {
- clif_skill_fail(sd, skill, USESKILL_FAIL_GC_WEAPONBLOCKING, 0);
- return 0;
- }
- break;
- case GC_CROSSRIPPERSLASHER:
- if( !(sc && sc->data[SC_ROLLINGCUTTER]) ) {
- clif_skill_fail(sd, skill, USESKILL_FAIL_CONDITION, 0);
- return 0;
- }
- break;
- case GC_POISONSMOKE:
- case GC_VENOMPRESSURE:
- if( !(sc && sc->data[SC_POISONINGWEAPON]) ) {
- clif_skill_fail(sd, skill, USESKILL_FAIL_GC_POISONINGWEAPON, 0);
- return 0;
- }
- break;
- /**
- * Ranger
- **/
- case RA_SENSITIVEKEEN:
- if(!pc_iswug(sd)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_CONDITION,0);
- return 0;
- }
- break;
- case RA_WUGMASTERY:
- if( pc_isfalcon(sd) || pc_isridingwug(sd) || sd->sc.data[SC__GROOMY]) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RA_WUGRIDER:
- if( !pc_isridingwug(sd) && !pc_iswug(sd) ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case RA_WUGDASH:
- if(!pc_isridingwug(sd)) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- /**
- * Royal Guard
- **/
- case LG_BANDING:
- if( sc && sc->data[SC_INSPIRATION] ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case LG_PRESTIGE:
- if( sc && (sc->data[SC_BANDING] || sc->data[SC_INSPIRATION]) ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case LG_RAGEBURST:
- if( sd->spiritball == 0 ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_SKILLINTERVAL,0);
- return 0;
- }
- sd->spiritball_old = require.spiritball = sd->spiritball;
- break;
- case LG_RAYOFGENESIS:
- if( sc && sc->data[SC_INSPIRATION] )
- return 1; // Don't check for partner.
- if( !(sc && sc->data[SC_BANDING]) ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL,0);
- return 0;
- } else if( skill_check_pc_partner(sd,skill,&lv,skill_get_range(skill,lv),0) < 1 )
- return 0; // Just fails, no msg here.
- break;
- case LG_HESPERUSLIT:
- if( !sc || !sc->data[SC_BANDING] ) {
- clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
- case SR_FALLENEMPIRE:
- if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) )
- return 0;
- break;
- case SR_CRESCENTELBOW:
- if( sc && sc->data[SC_CRESCENTELBOW] ) {
- clif_skill_fail(sd,skill,0,0);
- return 0;
- }
- break;
- case SR_CURSEDCIRCLE:
- if( sd->spiritball > 0 )
sd->spiritball_old = require.spiritball = sd->spiritball;
- else {
- clif_skill_fail(sd,skill,0,0);
- return 0;
- }
- break;
- case SR_GATEOFHELL:
- if( sd->spiritball > 0 )
- sd->spiritball_old = require.spiritball;
- break;
- case SC_MANHOLE:
- case SC_DIMENSIONDOOR:
- if( sc && sc->data[SC_MAGNETICFIELD] ) {
- clif_skill_fail(sd,skill,0,0);
- return 0;
- }
- break;
- case WM_GREAT_ECHO: {
- int count;
- count = skill_check_pc_partner(sd, skill, &lv, skill_get_splash(skill,lv), 0);
- if( count < 1 ) {
- clif_skill_fail(sd,skill,0x11,0);
+ break;
+ case LG_RAYOFGENESIS:
+ if( sc && sc->data[SC_INSPIRATION] )
+ return 1; // Don't check for partner.
+ if( !(sc && sc->data[SC_BANDING]) ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL,0);
return 0;
- } else
- require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party.
- }
- break;
+ } else if( skill_check_pc_partner(sd,skill,&lv,skill_get_range(skill,lv),0) < 1 )
+ return 0; // Just fails, no msg here.
+ break;
+ case LG_HESPERUSLIT:
+ if( !sc || !sc->data[SC_BANDING] ) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
+ case SR_FALLENEMPIRE:
+ if( !(sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_DRAGONCOMBO) )
+ return 0;
+ break;
+ case SR_CRESCENTELBOW:
+ if( sc && sc->data[SC_CRESCENTELBOW] ) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case SR_CURSEDCIRCLE:
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball = sd->spiritball;
+ else {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case SR_GATEOFHELL:
+ if( sd->spiritball > 0 )
+ sd->spiritball_old = require.spiritball;
+ break;
+ case SC_MANHOLE:
+ case SC_DIMENSIONDOOR:
+ if( sc && sc->data[SC_MAGNETICFIELD] ) {
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
+ case WM_GREAT_ECHO: {
+ int count;
+ count = skill_check_pc_partner(sd, skill, &lv, skill_get_splash(skill,lv), 0);
+ if( count < 1 ) {
+ clif_skill_fail(sd,skill,0x11,0);
+ return 0;
+ } else
+ require.sp -= require.sp * 20 * count / 100; // -20% each W/M in the party.
+ }
+ break;
+ case SO_FIREWALK:
+ case SO_ELECTRICWALK: // Can't be casted until you've walked all cells.
+ if( sc && sc->data[SC_PROPERTYWALK] &&
+ sc->data[SC_PROPERTYWALK]->val3 < skill_get_maxcount(sc->data[SC_PROPERTYWALK]->val1,sc->data[SC_PROPERTYWALK]->val2) ) {
+ clif_skill_fail(sd,skill,0x0,0);
+ return 0;
+ }
+ break;
+ case SO_EL_CONTROL:
+ if( !sd->status.ele_id || !sd->ed ) {
+ clif_skill_fail(sd,skill,0x00,0);
+ return 0;
+ }
+ break;
+ case RETURN_TO_ELDICASTES:
+ if( sd->sc.option&OPTION_MADOGEAR ) { //Cannot be used if Mado is equipped.
+ clif_skill_fail(sd,skill,0,0);
+ return 0;
+ }
+ break;
}
switch(require.state) {
@@ -11647,12 +12314,12 @@ int skill_check_condition_castbegin(struct map_session_data* sd, short skill, sh
/**
* Sorcerer
**/
- //case ST_ELEMENTALSPIRIT:
- // if(!sd->ed) {
- // clif_skill_fail(sd,skill,USESKILL_FAIL_EL_SUMMON,0);
- // return 0;
- // }
- // break;
+ case ST_ELEMENTALSPIRIT:
+ if(!sd->ed) {
+ clif_skill_fail(sd,skill,USESKILL_FAIL_EL_SUMMON,0);
+ return 0;
+ }
+ break;
}
if(require.mhp > 0 && get_percentage(status->hp, status->max_hp) > require.mhp) {
@@ -11704,18 +12371,25 @@ int skill_check_condition_castend(struct map_session_data* sd, short skill, shor
return 1;
}
- if( sd->menuskill_id == AM_PHARMACY )
- { // Cast start or cast end??
- switch( skill )
- {
+ switch( sd->menuskill_id ) { // Cast start or cast end??
case AM_PHARMACY:
- case AC_MAKINGARROW:
- case BS_REPAIRWEAPON:
- case AM_TWILIGHT1:
- case AM_TWILIGHT2:
- case AM_TWILIGHT3:
- return 0;
- }
+ switch( skill ) {
+ case AM_PHARMACY:
+ case AC_MAKINGARROW:
+ case BS_REPAIRWEAPON:
+ case AM_TWILIGHT1:
+ case AM_TWILIGHT2:
+ case AM_TWILIGHT3:
+ return 0;
+ }
+ break;
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
+ case GN_CHANGEMATERIAL:
+ if( sd->menuskill_id != skill )
+ return 0;
+ break;
}
if( sd->skillitem == skill ) // Casting finished (Item skill or Hocus-Pocus)
@@ -11994,6 +12668,17 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short
if( itemid_isgemstone(skill_db[j].itemid[i]) && skill_check_pc_partner(sd,skill,&lv, 1, 0) )
continue;
break;
+ case GN_FIRE_EXPANSION:
+ if( i < 5 )
+ continue;
+ break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ if( i < 3 )
+ continue;
+ break;
}
req.itemid[i] = skill_db[j].itemid[i];
@@ -12083,6 +12768,12 @@ struct skill_condition skill_get_requirement(struct map_session_data* sd, short
if( sc && sc->data[SC_COMBO] && sc->data[SC_COMBO]->val1 == SR_FALLENEMPIRE )
req.sp -= req.sp * 10 / 100;
break;
+ case SO_SUMMON_AGNI:
+ case SO_SUMMON_AQUA:
+ case SO_SUMMON_VENTUS:
+ case SO_SUMMON_TERA:
+ req.sp -= req.sp * (5 + 5 * pc_checkskill(sd,SO_EL_SYMPATHY)) / 100;
+ break;
}
return req;
@@ -12837,6 +13528,14 @@ int skill_clear_group (struct block_list *bl, int flag)
if (flag&1)
group[count++]= ud->skillunit[i];
break;
+ case SO_CLOUD_KILL:
+ if( flag&4 )
+ group[count++]= ud->skillunit[i];
+ break;
+ case SO_WARMER:
+ if( flag&8 )
+ 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];
@@ -12866,6 +13565,8 @@ struct skill_unit_group *skill_locate_element_field(struct block_list *bl)
case SA_VIOLENTGALE:
case SA_LANDPROTECTOR:
case NJ_SUITON:
+ case SO_WARMER:
+ case SO_CLOUD_KILL:
return ud->skillunit[i];
}
}
@@ -13906,7 +14607,7 @@ int skill_unit_move_sub (struct block_list* bl, va_list ap)
if( !unit->alive || target->prev == NULL )
return 0;
- if( unit->group->skill_id == PF_SPIDERWEB && flag&1 )
+ if( flag&1 && ( unit->group->skill_id == PF_SPIDERWEB || unit->group->skill_id == GN_THORNS_TRAP ) )
return 0; // Fiberlock is never supposed to trigger on skill_unit_move. [Inkfish]
dissonance = skill_dance_switch(unit, 0);
@@ -14182,7 +14883,7 @@ int skill_can_produce_mix (struct map_session_data *sd, int nameid, int trigger,
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;
+ int i,sc,ele,idx,equip,wlv,make_per,flag = 0, firstQty = qty;
int num = -1; // exclude the recipe
struct status_data *status;
@@ -14267,7 +14968,7 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
}while( j>=0 && x>0 );
}
- if((equip=itemdb_isequip(nameid)))
+ if( (equip = (itemdb_isequip(nameid) && skill_id != GN_CHANGEMATERIAL && skill_id != GN_MAKEBOMB )) )
wlv = itemdb_wlv(nameid);
if(!equip) {
switch(skill_id){
@@ -14363,6 +15064,57 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
make_per = 3000 + 500 * pc_checkskill(sd,GC_RESEARCHNEWPOISON);
qty = 1+rnd()%pc_checkskill(sd,GC_RESEARCHNEWPOISON);
break;
+ case GN_MIX_COOKING:
+ make_per = 3000; //As I can see this is not affectd by dex or int
+ break;
+ case GN_MAKEBOMB:
+ // TODO: find a proper chance.
+ make_per = (5000 + 50*status->dex + 30*status->luk); //Custom rate value.
+ break;
+ case GN_CHANGEMATERIAL:
+ switch( nameid ) {
+ case 1010:
+ qty *= 8;
+ break;
+ case 1061:
+ qty *= 2;
+ break;
+ // Throwable potions
+ case 13275:
+ case 13276:
+ case 13277:
+ case 13278:
+ case 13279:
+ case 13280:
+ case 13281:
+ case 13282:
+ case 13283:
+ qty *= 10;
+ break;
+ }
+ make_per = 100000; //100% success rate.
+ break;
+ case GN_S_PHARMACY:
+ // Note: This is not the chosen skill level but the highest available. Need confirmation/fix.
+ switch( sd->skilllv_old ) {
+ case 6:
+ case 7:
+ case 8:
+ qty = 3;
+ break; //3 items to make at once.
+ case 9:
+ qty = 3 + rand()%3;
+ break; //3~5 items to make at once.
+ case 10:
+ qty = 4 + rand()%3;
+ break; //4~6 items to make at once.
+ default:
+ qty = 2;
+ break; //2 item to make at once.
+ }
+ make_per = 100000; //100% success rate.
+ sd->skillid_old = sd->skilllv_old = 0;
+ break;
default:
if (sd->menuskill_id == AM_PHARMACY &&
sd->menuskill_val > 10 && sd->menuskill_val <= 20)
@@ -14429,6 +15181,9 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
case AM_TWILIGHT1:
case AM_TWILIGHT2:
case AM_TWILIGHT3:
+ case GN_MIX_COOKING:
+ case GN_MAKEBOMB:
+ case GN_S_PHARMACY:
flag = battle_config.produce_item_name_input&0x2;
break;
case AL_HOLYWATER:
@@ -14465,36 +15220,43 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
} else {
int fame = 0;
tmp_item.amount = 0;
- for (i=0; i< qty; i++)
- { //Apply quantity modifiers.
- if (rnd()%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( skill_id == GN_MIX_COOKING && firstQty > 1 ) {// Mix Cooking level 2.
+ // Success. As I see the chance as level 2 is global, not indiviual.
+ if( rand()%10000 < make_per )
+ tmp_item.amount = 5 + rand()%5;
+ } else {
+ for (i=0; i< qty; i++) { //Apply quantity modifiers.
+ if (rnd()%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 &&
+ skill_id != GN_MIX_COOKING &&
+ skill_id != GN_MAKEBOMB &&
+ skill_id != GN_S_PHARMACY )
+ 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);
@@ -14519,6 +15281,12 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
clif_produceeffect(sd,2,nameid);
clif_misceffect(&sd->bl,5);
break;
+ case GN_MAKEBOMB:
+ case GN_MIX_COOKING:
+ clif_msg_skill(sd,skill_id,0x627);
+ break;
+ case GN_S_PHARMACY:
+ break; // No effects here.
default: //Those that don't require a skill?
if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20)
{ //Cooking items.
@@ -14568,6 +15336,30 @@ int skill_produce_mix (struct map_session_data *sd, int skill_id, int nameid, in
clif_produceeffect(sd,3,nameid);
clif_misceffect(&sd->bl,6);
break;
+ case GN_MIX_COOKING: {
+ struct item tmp_item;
+ const int products[5][2] = {{13265,6500},{13266,4000},{13267,3000},{13268,500},{12435,500}};
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = nameid;
+ do {
+ i = rand()%5;
+ tmp_item.nameid = products[i][0];
+ }
+ while( rand()%10000 >= products[i][1] );
+ tmp_item.amount = (firstQty > 1 )? 5 + rand()%5 : 1; // When it fails it gives a random amount of items.
+ tmp_item.identify = 1;
+ if( pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_PRODUCE) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ clif_msg_skill(sd,skill_id,0x628);
+ }
+ break;
+ case GN_S_PHARMACY:
+ break; // No effects here.
+ case GN_MAKEBOMB:
+ clif_msg_skill(sd,skill_id,0x628);
+ break;
default:
if( skill_produce_db[idx].itemlv > 10 && skill_produce_db[idx].itemlv <= 20 )
{ //Cooking items.
@@ -14785,6 +15577,114 @@ int skill_select_menu(struct map_session_data *sd,int flag,int skill_id) {
sc_start4(&sd->bl,SC__AUTOSHADOWSPELL,100,id,lv,prob,0,skill_get_time(SC_AUTOSHADOWSPELL,aslvl));
return 0;
}
+int skill_elementalanalysis(struct map_session_data* sd, int n, int skill_lv, unsigned short* item_list) {
+ int i;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ if( n <= 0 )
+ return 1;
+
+ for( i = 0; i < n; i++ ) {
+ int nameid, add_amount, del_amount, idx, product, flag;
+ struct item tmp_item;
+
+ idx = item_list[i*2+0]-2;
+ del_amount = item_list[i*2+1];
+
+ if( skill_lv == 2 )
+ del_amount -= (del_amount % 10);
+ add_amount = (skill_lv == 1) ? del_amount * (5 + rand()%5) : del_amount / 10 ;
+
+ if( (nameid = sd->status.inventory[idx].nameid) <= 0 || del_amount > sd->status.inventory[idx].amount ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+ switch( nameid ) {
+ // Level 1
+ case 994: product = 990; break; // Flame Heart -> Red Blood.
+ case 995: product = 991; break; // Mystic Frozen -> Crystal Blue.
+ case 996: product = 992; break; // Rough Wind -> Wind of Verdure.
+ case 997: product = 993; break; // Great Nature -> Green Live.
+ // Level 2
+ case 990: product = 994; break; // Red Blood -> Flame Heart.
+ case 991: product = 995; break; // Crystal Blue -> Mystic Frozen.
+ case 992: product = 996; break; // Wind of Verdure -> Rough Wind.
+ case 993: product = 997; break; // Green Live -> Great Nature.
+ default:
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+ if( pc_delitem(sd,idx,del_amount,1,0,LOG_TYPE_CONSUME) ) {
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+ if( skill_lv == 2 && rand()%100 < 25 ) { // At level 2 have a fail chance. You loose your items if it fails.
+ clif_skill_fail(sd,SO_EL_ANALYSIS,0,0);
+ return 1;
+ }
+
+
+ memset(&tmp_item,0,sizeof(tmp_item));
+ tmp_item.nameid = product;
+ tmp_item.amount = add_amount;
+ tmp_item.identify = 1;
+
+ if( tmp_item.amount ) {
+ if( (flag = pc_additem(sd,&tmp_item,tmp_item.amount,LOG_TYPE_CONSUME)) ) {
+ clif_additem(sd,0,0,flag);
+ map_addflooritem(&tmp_item,tmp_item.amount,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0);
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list) {
+ int i, j, k, c, p, nameid, amount;
+
+ nullpo_ret(sd);
+ nullpo_ret(item_list);
+
+ // Search for objects that can be created.
+ for( i = 0; i < MAX_SKILL_PRODUCE_DB; i++ ) {
+ if( skill_produce_db[i].itemlv == 26 ) {
+ p = 0;
+ do {
+ c = 0;
+ // Verification of overlap between the objects required and the list submitted.
+ for( j = 0; j < MAX_PRODUCE_RESOURCE; j++ ) {
+ if( skill_produce_db[i].mat_id[j] > 0 ) {
+ for( k = 0; k < n; k++ ) {
+ int idx = item_list[k*2+0]-2;
+ nameid = sd->status.inventory[idx].nameid;
+ amount = item_list[k*2+1];
+
+ if( nameid == skill_produce_db[i].mat_id[j] && (amount-p*skill_produce_db[i].mat_amount[j]) >= skill_produce_db[i].mat_amount[j] )
+ c++; // match
+ }
+ }
+ else
+ break; // No more items required
+ }
+ p++;
+ } while(n == j && c == n);
+ p--;
+ if ( p > 0 ) {
+ skill_produce_mix(sd,GN_CHANGEMATERIAL,skill_produce_db[i].nameid,0,0,0,p);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
/**
* for Royal Guard's LG_TRAMPLE
**/
@@ -15071,146 +15971,162 @@ void skill_init_unit_layout (void)
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:
- case WL_EARTHSTRAIN://Warlock
- // these will be handled later
- break;
- case PR_SANCTUARY:
- case NPC_EVILLAND:
- {
- 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;
+ if( i >= HM_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ int skill = i;
+
+ if( i >= EL_SKILLRANGEMIN && i <= EL_SKILLRANGEMAX ) {
+ skill -= EL_SKILLRANGEMIN;
+ skill += EL_SKILLBASE;
}
- case NJ_KAENSIN:
- {
- static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
- static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2};
- skill_unit_layout[pos].count = 24;
+ if( skill == EL_FIRE_MANTLE ) {
+ static const int dx[] = {-1, 0, 1, 1, 1, 0,-1,-1};
+ static const int dy[] = { 1, 1, 1, 0,-1,-1,-1, 0};
+ skill_unit_layout[pos].count = 8;
memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
- break;
}
- case NJ_TATAMIGAESHI:
- {
- //Level 1 (count 4, cross of 3x3)
- static const int dx1[] = {-1, 1, 0, 0};
- static const int dy1[] = { 0, 0,-1, 1};
- //Level 2-3 (count 8, cross of 5x5)
- static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0};
- static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2};
- //Level 4-5 (count 12, cross of 7x7
- static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0};
- static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3};
- //lv1
- j = 0;
- skill_unit_layout[pos].count = 4;
- memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1));
- memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1));
- skill_db[i].unit_layout_type[j] = pos;
- //lv2/3
- j++;
- pos++;
- skill_unit_layout[pos].count = 8;
- memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2));
- memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2));
- skill_db[i].unit_layout_type[j] = pos;
- skill_db[i].unit_layout_type[++j] = pos;
- //lv4/5
- j++;
- pos++;
- skill_unit_layout[pos].count = 12;
- memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3));
- memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3));
- skill_db[i].unit_layout_type[j] = pos;
- skill_db[i].unit_layout_type[++j] = pos;
- //Fill in the rest using lv 5.
- for (;j<MAX_SKILL_LEVEL;j++)
- skill_db[i].unit_layout_type[j] = pos;
- //Skip, this way the check below will fail and continue to the next skill.
- pos++;
- break;
+ } else {
+ switch (i) {
+ case MG_FIREWALL:
+ case WZ_ICEWALL:
+ case WL_EARTHSTRAIN://Warlock
+ // these will be handled later
+ break;
+ case PR_SANCTUARY:
+ case NPC_EVILLAND: {
+ 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;
+ case NJ_KAENSIN: {
+ static const int dx[] = {-2,-1, 0, 1, 2,-2,-1, 0, 1, 2,-2,-1, 1, 2,-2,-1, 0, 1, 2,-2,-1, 0, 1, 2};
+ static const int dy[] = { 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0,-1,-1,-1,-1,-1,-2,-2,-2,-2,-2};
+ skill_unit_layout[pos].count = 24;
+ memcpy(skill_unit_layout[pos].dx,dx,sizeof(dx));
+ memcpy(skill_unit_layout[pos].dy,dy,sizeof(dy));
+ }
+ break;
+ case NJ_TATAMIGAESHI: {
+ //Level 1 (count 4, cross of 3x3)
+ static const int dx1[] = {-1, 1, 0, 0};
+ static const int dy1[] = { 0, 0,-1, 1};
+ //Level 2-3 (count 8, cross of 5x5)
+ static const int dx2[] = {-2,-1, 1, 2, 0, 0, 0, 0};
+ static const int dy2[] = { 0, 0, 0, 0,-2,-1, 1, 2};
+ //Level 4-5 (count 12, cross of 7x7
+ static const int dx3[] = {-3,-2,-1, 1, 2, 3, 0, 0, 0, 0, 0, 0};
+ static const int dy3[] = { 0, 0, 0, 0, 0, 0,-3,-2,-1, 1, 2, 3};
+ //lv1
+ j = 0;
+ skill_unit_layout[pos].count = 4;
+ memcpy(skill_unit_layout[pos].dx,dx1,sizeof(dx1));
+ memcpy(skill_unit_layout[pos].dy,dy1,sizeof(dy1));
+ skill_db[i].unit_layout_type[j] = pos;
+ //lv2/3
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 8;
+ memcpy(skill_unit_layout[pos].dx,dx2,sizeof(dx2));
+ memcpy(skill_unit_layout[pos].dy,dy2,sizeof(dy2));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //lv4/5
+ j++;
+ pos++;
+ skill_unit_layout[pos].count = 12;
+ memcpy(skill_unit_layout[pos].dx,dx3,sizeof(dx3));
+ memcpy(skill_unit_layout[pos].dy,dy3,sizeof(dy3));
+ skill_db[i].unit_layout_type[j] = pos;
+ skill_db[i].unit_layout_type[++j] = pos;
+ //Fill in the rest using lv 5.
+ for (;j<MAX_SKILL_LEVEL;j++)
+ skill_db[i].unit_layout_type[j] = pos;
+ //Skip, this way the check below will fail and continue to the next skill.
+ pos++;
+ }
+ break;
+ case GN_WALLOFTHORN: {
+ static const int dx[] = {-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2, 2, 2, 1, 0};
+ static const int dy[] = { 2, 2, 1, 0,-1,-2,-2,-2,-2,-2,-1, 0, 1, 2, 2, 2};
+ skill_unit_layout[pos].count = 16;
+ 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;
}
- default:
- ShowError("unknown unit layout at skill %d\n",i);
- break;
}
if (!skill_unit_layout[pos].count)
continue;
@@ -15364,6 +16280,21 @@ int skill_stasis_check(struct block_list *bl, int src_id, int skillid) {
return 0; // Can Cast anything else like Weapon Skills
}
+int skill_get_elemental_type( int skill_id , int skill_lv ) {
+ int type = 0;
+
+ switch( skill_id ) {
+ case SO_SUMMON_AGNI: type = 2114; break;
+ case SO_SUMMON_AQUA: type = 2117; break;
+ case SO_SUMMON_VENTUS: type = 2120; break;
+ case SO_SUMMON_TERA: type = 2123; break;
+ }
+
+ type += skill_lv - 1;
+
+ return type;
+}
+
/**
* reload stored skill cooldowns when a player logs in.
* @param sd the affected player structure
@@ -15408,8 +16339,8 @@ static bool skill_parse_row_skilldb(char* split[], int columns, int current)
int i;
if( (id >= GD_SKILLRANGEMIN && id <= GD_SKILLRANGEMAX)
|| (id >= HM_SKILLRANGEMIN && id <= HM_SKILLRANGEMAX)
- || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX) )
- {
+ || (id >= MC_SKILLRANGEMIN && id <= MC_SKILLRANGEMAX)
+ || (id >= EL_SKILLRANGEMIN && id <= EL_SKILLRANGEMAX) ) {
ShowWarning("skill_parse_row_skilldb: Skill id %d is forbidden (interferes with guild/homun/mercenary skill mapping)!\n", id);
return false;
}
diff --git a/src/map/skill.h b/src/map/skill.h
index 1275b3910..e7108e2f7 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -1705,49 +1705,49 @@ enum {
UNT_EVILLAND,
UNT_DARK_RUNNER, //TODO
UNT_DARK_TRANSFER, //TODO
- UNT_EPICLESIS, //TODO
- UNT_EARTHSTRAIN, //TODO
- UNT_MANHOLE, //TODO
- UNT_DIMENSIONDOOR, //TODO
- UNT_CHAOSPANIC, //TODO
- UNT_MAELSTROM, //TODO
- UNT_BLOODYLUST, //TODO
- UNT_FEINTBOMB, //TODO
- UNT_MAGENTATRAP, //TODO
- UNT_COBALTTRAP, //TODO
- UNT_MAIZETRAP, //TODO
- UNT_VERDURETRAP, //TODO
- UNT_FIRINGTRAP, //TODO
- UNT_ICEBOUNDTRAP, //TODO
- UNT_ELECTRICSHOCKER, //TODO
- UNT_CLUSTERBOMB, //TODO
- UNT_REVERBERATION, //TODO
- UNT_SEVERE_RAINSTORM, //TODO
- UNT_FIREWALK, //TODO
- UNT_ELECTRICWALK, //TODO
- UNT_NETHERWORLD, //TODO
- UNT_PSYCHIC_WAVE, //TODO
- UNT_CLOUD_KILL, //TODO
- UNT_POISONSMOKE, //TODO
- UNT_NEUTRALBARRIER, //TODO
- UNT_STEALTHFIELD, //TODO
- UNT_WARMER, //TODO
- UNT_THORNS_TRAP, //TODO
- UNT_WALLOFTHORN, //TODO
- UNT_DEMONIC_FIRE, //TODO
- UNT_FIRE_EXPANSION_SMOKE_POWDER, //TODO
- UNT_FIRE_EXPANSION_TEAR_GAS, //TODO
- UNT_HELLS_PLANT, //TODO
- UNT_VACUUM_EXTREME, //TODO
- UNT_BANDING, //TODO
- UNT_FIRE_MANTLE, //TODO
- UNT_WATER_BARRIER, //TODO
- UNT_ZEPHYR, //TODO
- UNT_POWER_OF_GAIA, //TODO
- UNT_FIRE_INSIGNIA, //TODO
- UNT_WATER_INSIGNIA, //TODO
- UNT_WIND_INSIGNIA, //TODO
- UNT_EARTH_INSIGNIA, //TODO
+ UNT_EPICLESIS,
+ UNT_EARTHSTRAIN,
+ UNT_MANHOLE,
+ UNT_DIMENSIONDOOR,
+ UNT_CHAOSPANIC,
+ UNT_MAELSTROM,
+ UNT_BLOODYLUST,
+ UNT_FEINTBOMB,
+ UNT_MAGENTATRAP,
+ UNT_COBALTTRAP,
+ UNT_MAIZETRAP,
+ UNT_VERDURETRAP,
+ UNT_FIRINGTRAP,
+ UNT_ICEBOUNDTRAP,
+ UNT_ELECTRICSHOCKER,
+ UNT_CLUSTERBOMB,
+ UNT_REVERBERATION,
+ UNT_SEVERE_RAINSTORM,
+ UNT_FIREWALK,
+ UNT_ELECTRICWALK,
+ UNT_NETHERWORLD,
+ UNT_PSYCHIC_WAVE,
+ UNT_CLOUD_KILL,
+ UNT_POISONSMOKE,
+ UNT_NEUTRALBARRIER,
+ UNT_STEALTHFIELD,
+ UNT_WARMER,
+ UNT_THORNS_TRAP,
+ UNT_WALLOFTHORN,
+ UNT_DEMONIC_FIRE,
+ UNT_FIRE_EXPANSION_SMOKE_POWDER,
+ UNT_FIRE_EXPANSION_TEAR_GAS,
+ UNT_HELLS_PLANT,
+ UNT_VACUUM_EXTREME,
+ UNT_BANDING,
+ UNT_FIRE_MANTLE,
+ UNT_WATER_BARRIER,
+ UNT_ZEPHYR,
+ UNT_POWER_OF_GAIA,
+ UNT_FIRE_INSIGNIA,
+ UNT_WATER_INSIGNIA,
+ UNT_WIND_INSIGNIA,
+ UNT_EARTH_INSIGNIA,
UNT_POISON_MIST,
UNT_LAVA_SLIDE,
UNT_VOLCANIC_ASH,
@@ -1823,4 +1823,9 @@ enum gx_poison {
* Auto Shadow Spell (Shadow Chaser)
**/
int skill_select_menu(struct map_session_data *sd,int flag,int skill_id);
+
+int skill_elementalanalysis(struct map_session_data *sd, int n, int type, unsigned short *item_list); // Sorcerer Four Elemental Analisys.
+int skill_changematerial(struct map_session_data *sd, int n, unsigned short *item_list); // Genetic Change Material.
+int skill_get_elemental_type(int skill_id, int skill_lv);
+
#endif /* _SKILL_H_ */
diff --git a/src/map/status.c b/src/map/status.c
index 162765aa3..972553d94 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -29,6 +29,7 @@
#include "unit.h"
#include "homunculus.h"
#include "mercenary.h"
+#include "elemental.h"
#include "vending.h"
#include <time.h>
@@ -645,28 +646,57 @@ void initChangeTables(void)
set_sc( WM_MELODYOFSINK , SC_MELODYOFSINK , SI_MELODYOFSINK , SCB_BATK|SCB_MATK );
set_sc( WM_BEYOND_OF_WARCRY , SC_BEYONDOFWARCRY , SI_WARCRYOFBEYOND , SCB_BATK|SCB_MATK );
set_sc( WM_UNLIMITED_HUMMING_VOICE, SC_UNLIMITEDHUMMINGVOICE, SI_UNLIMITEDHUMMINGVOICE, SCB_NONE );
- ///**
- // * Sorcerer
- // **/
- //set_sc( SO_FIREWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
- //set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
- //set_sc( SO_SPELLFIST , SC_SPELLFIST , SI_SPELLFIST , SCB_NONE );
- //set_sc( SO_CLOUD_KILL , SC_POISON , SI_CLOUDKILL , SCB_NONE );
- //set_sc( SO_STRIKING , SC_STRIKING , SI_STRIKING , SCB_WATK|SCB_CRI );
- //set_sc( SO_WARMER , SC_WARMER , SI_WARMER , SCB_NONE );
- //set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SI_VACUUM_EXTREME , SCB_NONE );
- //set_sc( SO_ARRULLO , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE );
- ///**
- // * Genetic
- // **/
- //set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SI_CARTSBOOST , SCB_SPEED );
- //set_sc( GN_THORNS_TRAP , SC_THORNSTRAP , SI_THORNTRAP , SCB_NONE );
- //set_sc( GN_BLOOD_SUCKER , SC_BLOODSUCKER , SI_BLOODSUCKER , SCB_NONE );
- //set_sc( GN_WALLOFTHORN , SC_STOP , SI_BLANK , SCB_NONE );
- //set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_SMOKEPOWDER , SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_NONE );
- //set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_TEARGAS , SI_FIRE_EXPANSION_TEAR_GAS , SCB_NONE );
- //set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SI_MANDRAGORA , SCB_INT );
-
+ /**
+ * Sorcerer
+ **/
+ set_sc( SO_FIREWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
+ set_sc( SO_ELECTRICWALK , SC_PROPERTYWALK , SI_PROPERTYWALK , SCB_NONE );
+ set_sc( SO_SPELLFIST , SC_SPELLFIST , SI_SPELLFIST , SCB_NONE );
+ set_sc( SO_DIAMONDDUST , SC_CRYSTALIZE , SI_COLD , SCB_NONE );//Will add flags in major balance update 8 [Rytech]
+ set_sc( SO_CLOUD_KILL , SC_POISON , SI_CLOUDKILL , SCB_NONE );
+ set_sc( SO_STRIKING , SC_STRIKING , SI_STRIKING , SCB_WATK|SCB_CRI );
+ set_sc( SO_WARMER , SC_WARMER , SI_WARMER , SCB_NONE );
+ set_sc( SO_VACUUM_EXTREME , SC_VACUUM_EXTREME , SI_VACUUM_EXTREME , SCB_NONE );
+ set_sc( SO_ARRULLO , SC_DEEPSLEEP , SI_DEEPSLEEP , SCB_NONE );
+ /**
+ * Genetic
+ **/
+ set_sc( GN_CARTBOOST , SC_GN_CARTBOOST, SI_CARTSBOOST , SCB_SPEED );
+ set_sc( GN_THORNS_TRAP , SC_THORNSTRAP , SI_THORNTRAP , SCB_NONE );
+ set_sc( GN_BLOOD_SUCKER , SC_BLOODSUCKER , SI_BLOODSUCKER , SCB_NONE );
+ set_sc( GN_WALLOFTHORN , SC_STOP , SI_BLANK , SCB_NONE );
+ set_sc( GN_FIRE_EXPANSION_SMOKE_POWDER, SC_SMOKEPOWDER , SI_FIRE_EXPANSION_SMOKE_POWDER, SCB_NONE );
+ set_sc( GN_FIRE_EXPANSION_TEAR_GAS , SC_TEARGAS , SI_FIRE_EXPANSION_TEAR_GAS , SCB_NONE );
+ set_sc( GN_MANDRAGORA , SC_MANDRAGORA , SI_MANDRAGORA , SCB_INT );
+
+ // Elemental Spirit summoner's 'side' status changes.
+ set_sc( EL_CIRCLE_OF_FIRE , SC_CIRCLE_OF_FIRE_OPTION, SI_CIRCLE_OF_FIRE_OPTION, SCB_NONE );
+ set_sc( EL_FIRE_CLOAK , SC_FIRE_CLOAK_OPTION , SI_FIRE_CLOAK_OPTION , SCB_ALL );
+ set_sc( EL_WATER_SCREEN , SC_WATER_SCREEN_OPTION , SI_WATER_SCREEN_OPTION , SCB_NONE );
+ set_sc( EL_WATER_DROP , SC_WATER_DROP_OPTION , SI_WATER_DROP_OPTION , SCB_ALL );
+ set_sc( EL_WATER_BARRIER , SC_WATER_BARRIER , SI_WATER_BARRIER , SCB_MDEF|SCB_WATK|SCB_MATK|SCB_FLEE );
+ set_sc( EL_WIND_STEP , SC_WIND_STEP_OPTION , SI_WIND_STEP_OPTION , SCB_SPEED|SCB_FLEE );
+ set_sc( EL_WIND_CURTAIN , SC_WIND_CURTAIN_OPTION , SI_WIND_CURTAIN_OPTION , SCB_ALL );
+ set_sc( EL_ZEPHYR , SC_ZEPHYR , SI_ZEPHYR , SCB_FLEE );
+ set_sc( EL_SOLID_SKIN , SC_SOLID_SKIN_OPTION , SI_SOLID_SKIN_OPTION , SCB_DEF|SCB_MAXHP );
+ set_sc( EL_STONE_SHIELD , SC_STONE_SHIELD_OPTION , SI_STONE_SHIELD_OPTION , SCB_ALL );
+ set_sc( EL_POWER_OF_GAIA , SC_POWER_OF_GAIA , SI_POWER_OF_GAIA , SCB_MAXHP|SCB_DEF|SCB_SPEED );
+ set_sc( EL_PYROTECHNIC , SC_PYROTECHNIC_OPTION , SI_PYROTECHNIC_OPTION , SCB_WATK );
+ set_sc( EL_HEATER , SC_HEATER_OPTION , SI_HEATER_OPTION , SCB_WATK );
+ set_sc( EL_TROPIC , SC_TROPIC_OPTION , SI_TROPIC_OPTION , SCB_WATK );
+ set_sc( EL_AQUAPLAY , SC_AQUAPLAY_OPTION , SI_AQUAPLAY_OPTION , SCB_MATK );
+ set_sc( EL_COOLER , SC_COOLER_OPTION , SI_COOLER_OPTION , SCB_MATK );
+ set_sc( EL_CHILLY_AIR , SC_CHILLY_AIR_OPTION , SI_CHILLY_AIR_OPTION , SCB_MATK );
+ set_sc( EL_GUST , SC_GUST_OPTION , SI_GUST_OPTION , SCB_NONE );
+ set_sc( EL_BLAST , SC_BLAST_OPTION , SI_BLAST_OPTION , SCB_NONE );
+ set_sc( EL_WILD_STORM , SC_WILD_STORM_OPTION , SI_WILD_STORM_OPTION , SCB_NONE );
+ set_sc( EL_PETROLOGY , SC_PETROLOGY_OPTION , SI_PETROLOGY_OPTION , SCB_NONE );
+ set_sc( EL_CURSED_SOIL , SC_CURSED_SOIL_OPTION , SI_CURSED_SOIL_OPTION , SCB_NONE );
+ set_sc( EL_UPHEAVAL , SC_UPHEAVAL_OPTION , SI_UPHEAVAL_OPTION , SCB_NONE );
+ set_sc( EL_TIDAL_WEAPON , SC_TIDAL_WEAPON_OPTION , SI_TIDAL_WEAPON_OPTION , SCB_ALL );
+ set_sc( EL_ROCK_CRUSHER , SC_ROCK_CRUSHER , SI_ROCK_CRUSHER , SCB_DEF );
+ set_sc( EL_ROCK_CRUSHER_ATK, SC_ROCK_CRUSHER_ATK , SI_ROCK_CRUSHER_ATK , SCB_SPEED );
+
// Storing the target job rather than simply SC_SPIRIT simplifies code later on.
SkillStatusChangeTable[SL_ALCHEMIST] = (sc_type)MAPID_ALCHEMIST,
SkillStatusChangeTable[SL_MONK] = (sc_type)MAPID_MONK,
@@ -882,11 +912,20 @@ void initChangeTables(void)
StatusChangeFlagTable[SC_SIROMA_ICE_TEA] |= SCB_DEX;
StatusChangeFlagTable[SC_DROCERA_HERB_STEAMED] |= SCB_AGI;
StatusChangeFlagTable[SC_PUTTI_TAILS_NOODLES] |= SCB_LUK;
+ StatusChangeFlagTable[SC_BOOST500] |= SCB_ASPD;
+ StatusChangeFlagTable[SC_FULL_SWING_K] |= SCB_BATK;
+ StatusChangeFlagTable[SC_MANA_PLUS] |= SCB_MATK;
+ StatusChangeFlagTable[SC_MUSTLE_M] |= SCB_MAXHP;
+ StatusChangeFlagTable[SC_LIFE_FORCE_F] |= SCB_MAXSP;
+ StatusChangeFlagTable[SC_EXTRACT_WHITE_POTION_Z] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_VITATA_500] |= SCB_REGEN;
+ StatusChangeFlagTable[SC_EXTRACT_SALAMINE_JUICE] |= SCB_ASPD;
#ifdef RENEWAL_EDP
// renewal EDP increases your atk and weapon atk
StatusChangeFlagTable[SC_EDP] |= SCB_BATK|SCB_WATK;
#endif
+
if( !battle_config.display_hallucination ) //Disable Hallucination.
StatusIconChangeTable[SC_HALLUCINATION] = SI_BLANK;
}
@@ -1077,6 +1116,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break;
case BL_HOM: merc_damage((TBL_HOM*)target,src,hp,sp); break;
case BL_MER: mercenary_damage((TBL_MER*)target,src,hp,sp); break;
+ case BL_ELEM: elemental_damage((TBL_ELEM*)target,src,hp,sp); break;
}
if( target->type == BL_PC && ((TBL_PC*)target)->disguise && src )
@@ -1102,6 +1142,7 @@ int status_damage(struct block_list *src,struct block_list *target,int hp, int s
case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break;
case BL_HOM: flag = merc_hom_dead((TBL_HOM*)target,src); break;
case BL_MER: flag = mercenary_dead((TBL_MER*)target,src); break;
+ case BL_ELEM: flag = elemental_dead((TBL_ELEM*)target,src); break;
default: //Unhandled case, do nothing to object.
flag = 0;
break;
@@ -1239,6 +1280,7 @@ int status_heal(struct block_list *bl,int hp,int sp, int flag)
case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
case BL_HOM: merc_hom_heal((TBL_HOM*)bl,hp,sp); break;
case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
+ case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break;
}
return hp+sp;
@@ -1378,6 +1420,8 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int
//on dead characters, said checks are left to skill.c [Skotlex]
if (target && status_isdead(target))
return 0;
+ if( src && (sc = status_get_sc(src)) && sc->data[SC_CRYSTALIZE] )
+ return 0;
}
if (skill_num == PA_PRESSURE && flag && target) {
@@ -1394,12 +1438,11 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int
&& (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_num))
return 0;
- if (src) sc = status_get_sc(src);
+ if ( src ) sc = status_get_sc(src);
- if(sc && sc->count)
- {
- if(sc->opt1 >0 && sc->opt1 != OPT1_BURNING && skill_num != SR_GENTLETOUCH_CURE)
- { //Stuned/Frozen/etc
+ if( sc && sc->count ) {
+
+ if( sc->opt1 >0 && sc->opt1 != OPT1_BURNING && skill_num != SR_GENTLETOUCH_CURE ) { //Stuned/Frozen/etc
if (flag != 1) //Can't cast, casted stuff can't damage.
return 0;
if (!(skill_get_inf(skill_num)&INF_GROUND_SKILL))
@@ -1570,11 +1613,12 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, int
return 0;
case BL_HOM:
case BL_MER:
+ case BL_ELEM:
if( target->type == BL_HOM && skill_num && battle_config.hom_setting&0x1 && skill_get_inf(skill_num)&INF_SUPPORT_SKILL && battle_get_master(target) != src )
return 0; // Can't use support skills on Homunculus (only Master/Self)
if( target->type == BL_MER && (skill_num == PR_ASPERSIO || (skill_num >= SA_FLAMELAUNCHER && skill_num <= SA_SEISMICWEAPON)) && battle_get_master(target) != src )
return 0; // Can't use Weapon endow skills on Mercenary (only Master)
- if( target->type == BL_MER && skill_num == AM_POTIONPITCHER )
+ if( skill_num == AM_POTIONPITCHER && ( target->type == BL_MER || target->type == BL_ELEM) )
return 0; // Can't use Potion Pitcher on Mercenaries
default:
//Check for chase-walk/hiding/cloaking opponents.
@@ -2848,8 +2892,7 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
}
if(sc->count){
- if(sc->data[SC_CONCENTRATE])
- { //Update the card-bonus data
+ if(sc->data[SC_CONCENTRATE]) { //Update the card-bonus data
sc->data[SC_CONCENTRATE]->val3 = sd->param_bonus[1]; //Agi
sc->data[SC_CONCENTRATE]->val4 = sd->param_bonus[4]; //Dex
}
@@ -2869,20 +2912,38 @@ int status_calc_pc_(struct map_session_data* sd, bool first)
sd->subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2;
sd->subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2;
}
- if(sc->data[SC_ARMOR_ELEMENT])
- { //This status change should grant card-type elemental resist.
+ if(sc->data[SC_ARMOR_ELEMENT]) { //This status change should grant card-type elemental resist.
sd->subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT]->val1;
sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT]->val2;
sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT]->val3;
sd->subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT]->val4;
}
- if(sc->data[SC_ARMOR_RESIST])
- { // Undead Scroll
+ if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll
sd->subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1;
sd->subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2;
sd->subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3;
sd->subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4;
}
+ if( sc->data[SC_FIRE_CLOAK_OPTION] ) {
+ i = sc->data[SC_FIRE_CLOAK_OPTION]->val2;
+ sd->subele[ELE_FIRE] += i;
+ sd->subele[ELE_EARTH] -= i;
+ }
+ if( sc->data[SC_WATER_DROP_OPTION] ) {
+ i = sc->data[SC_WATER_DROP_OPTION]->val2;
+ sd->subele[ELE_WATER] += i;
+ sd->subele[ELE_WIND] -= i;
+ }
+ if( sc->data[SC_WIND_CURTAIN_OPTION] ) {
+ i = sc->data[SC_WIND_CURTAIN_OPTION]->val2;
+ sd->subele[ELE_WIND] += i;
+ sd->subele[ELE_WATER] -= i;
+ }
+ if( sc->data[SC_STONE_SHIELD_OPTION] ) {
+ i = sc->data[SC_STONE_SHIELD_OPTION]->val2;
+ sd->subele[ELE_EARTH] += i;
+ sd->subele[ELE_FIRE] -= i;
+ }
}
status_cpy(&sd->battle_status, status);
@@ -3006,6 +3067,45 @@ int status_calc_homunculus_(struct homun_data *hd, bool first)
return 1;
}
+int status_calc_elemental_(struct elemental_data *ed, bool first) {
+ struct status_data *status = &ed->base_status;
+ struct s_elemental *ele = &ed->elemental;
+ struct map_session_data *sd = ed->master;
+
+ if( !sd )
+ return 0;
+
+ status->str = ele->str;
+ status->agi = ele->agi;
+ status->vit = ele->vit;
+ status->dex = ele->dex;
+ status->int_ = ele->int_;
+ status->luk = ele->luk;
+
+ if( first ) {
+ memcpy(status, &ed->db->status, sizeof(struct status_data));
+ status->mode = MD_CANMOVE|MD_CANATTACK;
+ status->max_hp += 4000 + 500 * pc_checkskill(sd,SO_EL_SYMPATHY);
+ status->max_sp += 300 + 50 * pc_checkskill(sd,SO_EL_SYMPATHY);
+ status->hp = status->max_hp;
+ status->sp = status->max_sp;
+ status->str += sd->base_status.str * 25 / 100;
+ status->agi += sd->base_status.agi * 25 / 100;
+ status->vit += sd->base_status.vit * 25 / 100;
+ status->int_ += sd->base_status.int_ * 25 / 100;
+ status->def += sd->base_status.dex * 25 / 100;
+ status->luk += sd->base_status.luk * 25 / 100;
+
+ status_calc_misc(&ed->bl, status, ed->db->lv);
+ memcpy(&ed->battle_status,status,sizeof(struct status_data));
+ } else {
+ status_calc_misc(&ed->bl, status, ed->db->lv);
+ status_cpy(&ed->battle_status, status);
+ }
+
+ 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);
@@ -3107,27 +3207,28 @@ void status_calc_regen(struct block_list *bl, struct status_data *status, struct
sregen->sp = cap_value(val, 0, SHRT_MAX);
}
- if( bl->type == BL_HOM )
- {
+ if( bl->type == BL_HOM ) {
struct homun_data *hd = (TBL_HOM*)bl;
- if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 )
- {
+ if( (skill = merc_hom_checkskill(hd,HAMI_SKIN)) > 0 ) {
val = regen->hp*(100+5*skill)/100;
regen->hp = cap_value(val, 1, SHRT_MAX);
}
- if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 )
- {
+ if( (skill = merc_hom_checkskill(hd,HLIF_BRAIN)) > 0 ) {
val = regen->sp*(100+3*skill)/100;
regen->sp = cap_value(val, 1, SHRT_MAX);
}
- }
- else if( bl->type == BL_MER )
- {
+ } else if( bl->type == BL_MER ) {
val = (status->max_hp * status->vit / 10000 + 1) * 6;
regen->hp = cap_value(val, 1, SHRT_MAX);
val = (status->max_sp * (status->int_ + 10) / 750) + 1;
regen->sp = cap_value(val, 1, SHRT_MAX);
+ } else if( bl->type == BL_ELEM ) {
+ val = (status->max_hp * status->vit / 10000 + 1) * 6;
+ regen->hp = cap_value(val, 1, SHRT_MAX);
+
+ val = (status->max_sp * (status->int_ + 10) / 750) + 1;
+ regen->sp = cap_value(val, 1, SHRT_MAX);
}
}
@@ -3241,7 +3342,7 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
if(flag&SCB_VIT) {
status->vit = status_calc_vit(bl, sc, b_status->vit);
flag|=SCB_DEF2|SCB_MDEF2;
- if( bl->type&(BL_PC|BL_HOM|BL_MER) )
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
flag |= SCB_MAXHP;
if( bl->type&BL_HOM )
flag |= SCB_DEF;
@@ -3250,7 +3351,7 @@ void status_calc_bl_main(struct block_list *bl, /*enum scb_flag*/int flag)
if(flag&SCB_INT) {
status->int_ = status_calc_int(bl, sc, b_status->int_);
flag|=SCB_MATK|SCB_MDEF2;
- if( bl->type&(BL_PC|BL_HOM|BL_MER) )
+ if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
flag |= SCB_MAXSP;
if( bl->type&BL_HOM )
flag |= SCB_MDEF;
@@ -3582,15 +3683,14 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
status = status_get_status_data(bl);
memcpy(&b_status, status, sizeof(struct status_data));
- if( flag&SCB_BASE )
- {// calculate the object's base status too
- switch( bl->type )
- {
+ if( flag&SCB_BASE ) {// calculate the object's base status too
+ switch( bl->type ) {
case BL_PC: status_calc_pc_(BL_CAST(BL_PC,bl), first); break;
case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), first); break;
case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), first); break;
case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl), first); break;
case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), first); break;
+ case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), first); break;
}
}
@@ -3671,17 +3771,11 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
clif_updatestatus(sd,SP_HP);
if(b_status.sp != status->sp)
clif_updatestatus(sd,SP_SP);
- }
- else
- if( bl->type == BL_HOM )
- {
+ } else if( bl->type == BL_HOM ) {
TBL_HOM* hd = BL_CAST(BL_HOM, bl);
if( hd->master && memcmp(&b_status, status, sizeof(struct status_data)) != 0 )
clif_hominfo(hd->master,hd,0);
- }
- else
- if( bl->type == BL_MER )
- {
+ } else if( bl->type == BL_MER ) {
TBL_MER* md = BL_CAST(BL_MER, bl);
if( b_status.rhw.atk != status->rhw.atk || b_status.rhw.atk2 != status->rhw.atk2 )
clif_mercenary_updatestatus(md->master, SP_ATK1);
@@ -3707,7 +3801,17 @@ void status_calc_bl_(struct block_list* bl, enum scb_flag flag, bool first)
clif_mercenary_updatestatus(md->master, SP_HP);
if( b_status.sp != status->sp )
clif_mercenary_updatestatus(md->master, SP_SP);
- }
+ } else if( bl->type == BL_ELEM ) {
+ TBL_ELEM* ed = BL_CAST(BL_ELEM, bl);
+ if( b_status.max_hp != status->max_hp )
+ clif_elemental_updatestatus(ed->master, SP_MAXHP);
+ if( b_status.max_sp != status->max_sp )
+ clif_elemental_updatestatus(ed->master, SP_MAXSP);
+ if( b_status.hp != status->hp )
+ clif_elemental_updatestatus(ed->master, SP_HP);
+ if( b_status.sp != status->sp )
+ clif_mercenary_updatestatus(ed->master, SP_SP);
+ }
}
/*==========================================
@@ -4641,6 +4745,10 @@ static unsigned short status_calc_speed(struct block_list *bl, struct status_cha
val = max( val, 75 );
if( sc->data[SC_CLOAKINGEXCEED] )
val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3);
+ if( sc->data[SC_HOVERING] )
+ val = max( val, 10 );
+ if( sc->data[SC_GN_CARTBOOST] )
+ val = max( val, sc->data[SC_GN_CARTBOOST]->val2 );
if( sc->data[SC_GT_REVITALIZE] )
val = max( val, sc->data[SC_GT_REVITALIZE]->val2 );
if( sc->data[SC_SWINGDANCE] )
@@ -5005,8 +5113,7 @@ static unsigned short status_calc_mode(struct block_list *bl, struct status_chan
return cap_value(mode,0,USHRT_MAX);
}
-const char* status_get_name(struct block_list *bl)
-{
+const char* status_get_name(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
case BL_PC: return ((TBL_PC *)bl)->fakename[0] != '\0' ? ((TBL_PC*)bl)->fakename : ((TBL_PC*)bl)->status.name;
@@ -5022,17 +5129,16 @@ const char* status_get_name(struct block_list *bl)
* 対象のClassを返す(汎用)
* 戻りは整数で0以上
*------------------------------------------*/
-int status_get_class(struct block_list *bl)
-{
+int status_get_class(struct block_list *bl) {
nullpo_ret(bl);
- switch( bl->type )
- {
- case BL_PC: return ((TBL_PC*)bl)->status.class_;
- case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob.
- case BL_PET: return ((TBL_PET*)bl)->pet.class_;
- case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_;
- case BL_MER: return ((TBL_MER*)bl)->mercenary.class_;
- case BL_NPC: return ((TBL_NPC*)bl)->class_;
+ switch( bl->type ) {
+ case BL_PC: return ((TBL_PC*)bl)->status.class_;
+ case BL_MOB: return ((TBL_MOB*)bl)->vd->class_; //Class used on all code should be the view class of the mob.
+ case BL_PET: return ((TBL_PET*)bl)->pet.class_;
+ case BL_HOM: return ((TBL_HOM*)bl)->homunculus.class_;
+ case BL_MER: return ((TBL_MER*)bl)->mercenary.class_;
+ case BL_NPC: return ((TBL_NPC*)bl)->class_;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->elemental.class_;
}
return 0;
}
@@ -5040,8 +5146,7 @@ int status_get_class(struct block_list *bl)
* 対象のレベルを返す(汎用)
* 戻りは整数で0以上
*------------------------------------------*/
-int status_get_lv(struct block_list *bl)
-{
+int status_get_lv(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
case BL_PC: return ((TBL_PC*)bl)->status.base_level;
@@ -5049,6 +5154,7 @@ int status_get_lv(struct block_list *bl)
case BL_PET: return ((TBL_PET*)bl)->pet.level;
case BL_HOM: return ((TBL_HOM*)bl)->homunculus.level;
case BL_MER: return ((TBL_MER*)bl)->db->lv;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->db->lv;
}
return 1;
}
@@ -5060,6 +5166,7 @@ struct regen_data *status_get_regen_data(struct block_list *bl)
case BL_PC: return &((TBL_PC*)bl)->regen;
case BL_HOM: return &((TBL_HOM*)bl)->regen;
case BL_MER: return &((TBL_MER*)bl)->regen;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->regen;
default:
return NULL;
}
@@ -5075,6 +5182,7 @@ struct status_data *status_get_status_data(struct block_list *bl)
case BL_PET: return &((TBL_PET*)bl)->status;
case BL_HOM: return &((TBL_HOM*)bl)->battle_status;
case BL_MER: return &((TBL_MER*)bl)->battle_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->battle_status;
default:
return &dummy_status;
}
@@ -5089,6 +5197,7 @@ struct status_data *status_get_base_status(struct block_list *bl)
case BL_PET: return &((TBL_PET*)bl)->db->status;
case BL_HOM: return &((TBL_HOM*)bl)->base_status;
case BL_MER: return &((TBL_MER*)bl)->base_status;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->base_status;
default:
return NULL;
}
@@ -5111,115 +5220,120 @@ unsigned short status_get_speed(struct block_list *bl)
return status_get_status_data(bl)->speed;
}
-int status_get_party_id(struct block_list *bl)
-{
+int status_get_party_id(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->status.party_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->status.party_id;
- break;
- case BL_MOB:
- {
- struct mob_data *md=(TBL_MOB*)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;
- }
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->status.party_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->status.party_id;
- break;
- case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->party_id;
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.party_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.party_id;
+ break;
+ case BL_MOB: {
+ struct mob_data *md=(TBL_MOB*)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;
+ }
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.party_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.party_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->party_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.party_id;
+ break;
}
return 0;
}
-int status_get_guild_id(struct block_list *bl)
-{
+int status_get_guild_id(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->status.guild_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->status.guild_id;
- break;
- case 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]
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->status.guild_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->status.guild_id;
- break;
- case BL_NPC:
- if (((TBL_NPC*)bl)->subtype == SCRIPT)
- return ((TBL_NPC*)bl)->u.scr.guild_id;
- break;
- case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->guild_id;
+ case BL_PC:
+ return ((TBL_PC*)bl)->status.guild_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->status.guild_id;
+ break;
+ case 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]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->status.guild_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->status.guild_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT)
+ return ((TBL_NPC*)bl)->u.scr.guild_id;
+ break;
+ case BL_SKILL:
+ return ((TBL_SKILL*)bl)->group->guild_id;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->status.guild_id;
+ break;
}
return 0;
}
-int status_get_emblem_id(struct block_list *bl)
-{
+int status_get_emblem_id(struct block_list *bl) {
nullpo_ret(bl);
switch (bl->type) {
- case BL_PC:
- return ((TBL_PC*)bl)->guild_emblem_id;
- case BL_PET:
- if (((TBL_PET*)bl)->msd)
- return ((TBL_PET*)bl)->msd->guild_emblem_id;
- break;
- case 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->emblem_id;
- if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
- return msd->guild_emblem_id; //Alchemist's mobs [Skotlex]
- }
- break;
- case BL_HOM:
- if (((TBL_HOM*)bl)->master)
- return ((TBL_HOM*)bl)->master->guild_emblem_id;
- break;
- case BL_MER:
- if (((TBL_MER*)bl)->master)
- return ((TBL_MER*)bl)->master->guild_emblem_id;
- break;
- case BL_NPC:
- if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
- struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
- if (g)
- return g->emblem_id;
- }
- break;
+ case BL_PC:
+ return ((TBL_PC*)bl)->guild_emblem_id;
+ case BL_PET:
+ if (((TBL_PET*)bl)->msd)
+ return ((TBL_PET*)bl)->msd->guild_emblem_id;
+ break;
+ case 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->emblem_id;
+ if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
+ return msd->guild_emblem_id; //Alchemist's mobs [Skotlex]
+ }
+ break;
+ case BL_HOM:
+ if (((TBL_HOM*)bl)->master)
+ return ((TBL_HOM*)bl)->master->guild_emblem_id;
+ break;
+ case BL_MER:
+ if (((TBL_MER*)bl)->master)
+ return ((TBL_MER*)bl)->master->guild_emblem_id;
+ break;
+ case BL_NPC:
+ if (((TBL_NPC*)bl)->subtype == SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
+ struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
+ if (g)
+ return g->emblem_id;
+ }
+ break;
+ case BL_ELEM:
+ if (((TBL_ELEM*)bl)->master)
+ return ((TBL_ELEM*)bl)->master->guild_emblem_id;
+ break;
}
return 0;
}
@@ -5271,6 +5385,7 @@ struct view_data* status_get_viewdata(struct block_list *bl)
case BL_NPC: return ((TBL_NPC*)bl)->vd;
case BL_HOM: return ((TBL_HOM*)bl)->vd;
case BL_MER: return ((TBL_MER*)bl)->vd;
+ case BL_ELEM: return ((TBL_ELEM*)bl)->vd;
}
return NULL;
}
@@ -5287,6 +5402,8 @@ void status_set_viewdata(struct block_list *bl, int class_)
vd = merc_get_hom_viewdata(class_);
else if (merc_class(class_))
vd = merc_get_viewdata(class_);
+ else if (elemental_class(class_))
+ vd = elemental_get_viewdata(class_);
else
vd = NULL;
@@ -5394,6 +5511,15 @@ void status_set_viewdata(struct block_list *bl, int class_)
ShowError("status_set_viewdata (MERCENARY): No view data for class %d\n", class_);
}
break;
+ case BL_ELEM:
+ {
+ struct elemental_data *ed = (struct elemental_data*)bl;
+ if (vd)
+ ed->vd = vd;
+ else
+ ShowError("status_set_viewdata (ELEMENTAL): No view data for class %d\n", class_);
+ }
+ break;
}
vd = status_get_viewdata(bl);
if (vd && vd->cloth_color && (
@@ -5405,15 +5531,15 @@ void status_set_viewdata(struct block_list *bl, int class_)
}
/// Returns the status_change data of bl or NULL if it doesn't exist.
-struct status_change *status_get_sc(struct block_list *bl)
-{
+struct status_change *status_get_sc(struct block_list *bl) {
if( bl )
switch (bl->type) {
- case BL_PC: return &((TBL_PC*)bl)->sc;
- case BL_MOB: return &((TBL_MOB*)bl)->sc;
- case BL_NPC: return &((TBL_NPC*)bl)->sc;
- case BL_HOM: return &((TBL_HOM*)bl)->sc;
- case BL_MER: return &((TBL_MER*)bl)->sc;
+ case BL_PC: return &((TBL_PC*)bl)->sc;
+ case BL_MOB: return &((TBL_MOB*)bl)->sc;
+ case BL_NPC: return &((TBL_NPC*)bl)->sc;
+ case BL_HOM: return &((TBL_HOM*)bl)->sc;
+ case BL_MER: return &((TBL_MER*)bl)->sc;
+ case BL_ELEM: return &((TBL_ELEM*)bl)->sc;
}
return NULL;
}
@@ -9336,8 +9462,7 @@ int status_change_timer(int tid, unsigned int tick, int id, intptr_t data)
break;
case SC_BLOODSUCKER:
- if( --(sce->val4) >= 0 )
- {
+ if( --(sce->val4) >= 0 ) {
struct block_list *src = map_id2bl(sce->val2);
int damage;
if( !src || (src && (status_isdead(src) || src->m != bl->m || distance_bl(src, bl) >= 12)) )
diff --git a/src/map/status.h b/src/map/status.h
index 9b90c88b7..fd4c32319 100644
--- a/src/map/status.h
+++ b/src/map/status.h
@@ -592,6 +592,8 @@ typedef enum sc_type {
SC_SOULCOLD, //510
SC_HAWKEYES,
SC_ODINS_POWER,
+
+
SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
} sc_type;
@@ -1038,7 +1040,7 @@ enum si_type {
SI_COLD = 437,
SI_GLOOMYDAY = 438,
SI_SONGOFMANA = 439,
- SI_CLOUD_KILL = 440,
+ SI_CLOUDKILL = 440,
SI_DANCEWITHWUG = 441,
SI_RUSHWINDMILL = 442,
SI_ECHOSONG = 443,
@@ -1059,10 +1061,10 @@ enum si_type {
SI_FREEZE_SP = 458,
SI_GN_TRAINING_SWORD = 459,
SI_GN_REMODELING_CART = 460,
- SI_GN_CARTBOOST = 461,
+ SI_CARTSBOOST = 461,
SI_FIXEDCASTINGTM_REDUCE = 462,
- SI_THORNS_TRAP = 463,
- SI_BLOOD_SUCKER = 464,
+ SI_THORNTRAP = 463,
+ SI_BLOODSUCKER = 464,
SI_SPORE_EXPLOSION = 465,
SI_DEMONIC_FIRE = 466,
SI_FIRE_EXPANSION_SMOKE_POWDER = 467,
@@ -1132,11 +1134,11 @@ enum si_type {
SI_STONE_SHIELD = 531,
SI_STONE_SHIELD_OPTION = 532,
SI_POWER_OF_GAIA = 533,
- SI_EL_WAIT = 534,
- SI_EL_PASSIVE = 535,
- SI_EL_DEFENSIVE = 536,
- SI_EL_OFFENSIVE = 537,
- SI_EL_COST = 538,
+ // SI_EL_WAIT = 534,
+ // SI_EL_PASSIVE = 535,
+ // SI_EL_DEFENSIVE = 536,
+ // SI_EL_OFFENSIVE = 537,
+ // SI_EL_COST = 538,
SI_PYROTECHNIC = 539,
SI_PYROTECHNIC_OPTION = 540,
SI_HEATER = 541,
@@ -1671,6 +1673,7 @@ int status_change_clear_buffs(struct block_list* bl, int type);
#define status_calc_pc(sd, first) status_calc_bl_(&(sd)->bl, SCB_ALL, first)
#define status_calc_homunculus(hd, first) status_calc_bl_(&(hd)->bl, SCB_ALL, first)
#define status_calc_mercenary(md, first) status_calc_bl_(&(md)->bl, SCB_ALL, first)
+#define status_calc_elemental(ed, first) status_calc_bl_(&(ed)->bl, SCB_ALL, first)
void status_calc_bl_(struct block_list *bl, enum scb_flag flag, bool first);
int status_calc_mob_(struct mob_data* md, bool first);
@@ -1678,6 +1681,7 @@ int status_calc_pet_(struct pet_data* pd, bool first);
int status_calc_pc_(struct map_session_data* sd, bool first);
int status_calc_homunculus_(struct homun_data *hd, bool first);
int status_calc_mercenary_(struct mercenary_data *md, bool first);
+int status_calc_elemental_(struct elemental_data *ed, bool first);
void status_calc_misc(struct block_list *bl, struct status_data *status, int level);
void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen);
diff --git a/src/map/unit.c b/src/map/unit.c
index 6224af787..2dd33af10 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -16,6 +16,7 @@
#include "homunculus.h"
#include "instance.h"
#include "mercenary.h"
+#include "elemental.h"
#include "skill.h"
#include "clif.h"
#include "duel.h"
@@ -50,6 +51,7 @@ struct unit_data* unit_bl2ud(struct block_list *bl)
if( bl->type == BL_NPC) return &((struct npc_data*)bl)->ud;
if( bl->type == BL_HOM) return &((struct homun_data*)bl)->ud;
if( bl->type == BL_MER) return &((struct mercenary_data*)bl)->ud;
+ if( bl->type == BL_ELEM) return &((struct elemental_data*)bl)->ud;
return NULL;
}
@@ -2006,148 +2008,155 @@ int unit_remove_map_(struct block_list *bl, clr_type clrtype, const char* file,
skill_cleartimerskill(bl);
}
- switch( bl->type )
- {
- case BL_PC:
- {
- struct map_session_data *sd = (struct map_session_data*)bl;
-
- //Leave/reject all invitations.
- if(sd->chatID)
- chat_leavechat(sd,0);
- if(sd->trade_partner)
- trade_tradecancel(sd);
- vending_closevending(sd);
- buyingstore_close(sd);
- searchstore_close(sd);
- if(sd->state.storage_flag == 1)
- storage_storage_quit(sd,0);
- else if (sd->state.storage_flag == 2)
- storage_guild_storage_quit(sd,0);
- sd->state.storage_flag = 0; //Force close it when being warped.
- if(sd->party_invite>0)
- party_reply_invite(sd,sd->party_invite,0);
- if(sd->guild_invite>0)
- guild_reply_invite(sd,sd->guild_invite,0);
- if(sd->guild_alliance>0)
- guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
- if(sd->menuskill_id)
- sd->menuskill_id = sd->menuskill_val = 0;
- if( sd->touching_id )
- npc_touchnext_areanpc(sd,true);
-
- sd->npc_shopid = 0;
- sd->adopt_invite = 0;
+ switch( bl->type ) {
+ case BL_PC: {
+ struct map_session_data *sd = (struct map_session_data*)bl;
- if(sd->pvp_timer != INVALID_TIMER) {
- delete_timer(sd->pvp_timer,pc_calc_pvprank_timer);
- sd->pvp_timer = INVALID_TIMER;
- sd->pvp_rank = 0;
- }
- if(sd->duel_group > 0)
- duel_leave(sd->duel_group, sd);
+ //Leave/reject all invitations.
+ if(sd->chatID)
+ chat_leavechat(sd,0);
+ if(sd->trade_partner)
+ trade_tradecancel(sd);
+ vending_closevending(sd);
+ buyingstore_close(sd);
+ searchstore_close(sd);
+ if(sd->state.storage_flag == 1)
+ storage_storage_quit(sd,0);
+ else if (sd->state.storage_flag == 2)
+ storage_guild_storage_quit(sd,0);
+ sd->state.storage_flag = 0; //Force close it when being warped.
+ if(sd->party_invite>0)
+ party_reply_invite(sd,sd->party_invite,0);
+ if(sd->guild_invite>0)
+ guild_reply_invite(sd,sd->guild_invite,0);
+ if(sd->guild_alliance>0)
+ guild_reply_reqalliance(sd,sd->guild_alliance_account,0);
+ if(sd->menuskill_id)
+ sd->menuskill_id = sd->menuskill_val = 0;
+ if( sd->touching_id )
+ npc_touchnext_areanpc(sd,true);
+
+ sd->npc_shopid = 0;
+ sd->adopt_invite = 0;
+
+ if(sd->pvp_timer != INVALID_TIMER) {
+ delete_timer(sd->pvp_timer,pc_calc_pvprank_timer);
+ sd->pvp_timer = INVALID_TIMER;
+ sd->pvp_rank = 0;
+ }
+ if(sd->duel_group > 0)
+ duel_leave(sd->duel_group, sd);
- if(pc_issit(sd)) {
- pc_setstand(sd);
- skill_sit(sd,0);
- }
- party_send_dot_remove(sd);//minimap dot fix [Kevin]
- guild_send_dot_remove(sd);
- bg_send_dot_remove(sd);
+ if(pc_issit(sd)) {
+ pc_setstand(sd);
+ skill_sit(sd,0);
+ }
+ party_send_dot_remove(sd);//minimap dot fix [Kevin]
+ guild_send_dot_remove(sd);
+ bg_send_dot_remove(sd);
- if( map[bl->m].users <= 0 || sd->state.debug_remove_map )
- {// this is only place where map users is decreased, if the mobs were removed too soon then this function was executed too many times [FlavioJS]
- if( sd->debug_file == NULL || !(sd->state.debug_remove_map) )
+ if( map[bl->m].users <= 0 || sd->state.debug_remove_map )
+ {// this is only place where map users is decreased, if the mobs were removed too soon then this function was executed too many times [FlavioJS]
+ if( sd->debug_file == NULL || !(sd->state.debug_remove_map) )
+ {
+ sd->debug_file = "";
+ sd->debug_line = 0;
+ sd->debug_func = "";
+ }
+ ShowDebug("unit_remove_map: unexpected state when removing player AID/CID:%d/%d"
+ " (active=%d connect_new=%d rewarp=%d changemap=%d debug_remove_map=%d)"
+ " from map=%s (users=%d)."
+ " Previous call from %s:%d(%s), current call from %s:%d(%s)."
+ " Please report this!!!\n",
+ sd->status.account_id, sd->status.char_id,
+ sd->state.active, sd->state.connect_new, sd->state.rewarp, sd->state.changemap, sd->state.debug_remove_map,
+ map[bl->m].name, map[bl->m].users,
+ sd->debug_file, sd->debug_line, sd->debug_func, file, line, func);
+ }
+ else
+ if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
+ map_removemobs(bl->m);
+ if( !(sd->sc.option&OPTION_INVISIBLE) )
+ {// decrement the number of active pvp players on the map
+ --map[bl->m].users_pvp;
+ }
+ if( map[bl->m].instance_id )
{
- sd->debug_file = "";
- sd->debug_line = 0;
- sd->debug_func = "";
+ instance[map[bl->m].instance_id].users--;
+ instance_check_idle(map[bl->m].instance_id);
}
- ShowDebug("unit_remove_map: unexpected state when removing player AID/CID:%d/%d"
- " (active=%d connect_new=%d rewarp=%d changemap=%d debug_remove_map=%d)"
- " from map=%s (users=%d)."
- " Previous call from %s:%d(%s), current call from %s:%d(%s)."
- " Please report this!!!\n",
- sd->status.account_id, sd->status.char_id,
- sd->state.active, sd->state.connect_new, sd->state.rewarp, sd->state.changemap, sd->state.debug_remove_map,
- map[bl->m].name, map[bl->m].users,
- sd->debug_file, sd->debug_line, sd->debug_func, file, line, func);
- }
- else
- if (--map[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
- map_removemobs(bl->m);
- if( !(sd->sc.option&OPTION_INVISIBLE) )
- {// decrement the number of active pvp players on the map
- --map[bl->m].users_pvp;
- }
- if( map[bl->m].instance_id )
- {
- instance[map[bl->m].instance_id].users--;
- instance_check_idle(map[bl->m].instance_id);
- }
- sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS]
- sd->debug_file = file;
- sd->debug_line = line;
- sd->debug_func = func;
+ sd->state.debug_remove_map = 1; // temporary state to track double remove_map's [FlavioJS]
+ sd->debug_file = file;
+ sd->debug_line = line;
+ sd->debug_func = func;
- break;
- }
- case BL_MOB:
- {
- struct mob_data *md = (struct mob_data*)bl;
- // Drop previous target mob_slave_keep_target: no.
- if (!battle_config.mob_slave_keep_target)
- md->target_id=0;
+ break;
+ }
+ case BL_MOB: {
+ struct mob_data *md = (struct mob_data*)bl;
+ // Drop previous target mob_slave_keep_target: no.
+ if (!battle_config.mob_slave_keep_target)
+ md->target_id=0;
- md->attacked_id=0;
- md->state.skillstate= MSS_IDLE;
+ md->attacked_id=0;
+ md->state.skillstate= MSS_IDLE;
- break;
- }
- case BL_PET:
- {
- struct pet_data *pd = (struct pet_data*)bl;
- if( pd->pet.intimate <= 0 && !(pd->msd && !pd->msd->state.active) )
- { //If logging out, this is deleted on unit_free
- clif_clearunit_area(bl,clrtype);
- map_delblock(bl);
- unit_free(bl,CLR_OUTSIGHT);
- map_freeblock_unlock();
- return 0;
+ break;
}
+ case BL_PET: {
+ struct pet_data *pd = (struct pet_data*)bl;
+ if( pd->pet.intimate <= 0 && !(pd->msd && !pd->msd->state.active) )
+ { //If logging out, this is deleted on unit_free
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,CLR_OUTSIGHT);
+ map_freeblock_unlock();
+ return 0;
+ }
- break;
- }
- case BL_HOM:
- {
- struct homun_data *hd = (struct homun_data *)bl;
- ud->canact_tick = ud->canmove_tick; //It appears HOM do reset the can-act tick.
- if( !hd->homunculus.intimacy && !(hd->master && !hd->master->state.active) )
- { //If logging out, this is deleted on unit_free
- clif_emotion(bl, E_SOB);
- clif_clearunit_area(bl,clrtype);
- map_delblock(bl);
- unit_free(bl,CLR_OUTSIGHT);
- map_freeblock_unlock();
- return 0;
+ break;
}
- break;
- }
- case BL_MER:
- {
- struct mercenary_data *md = (struct mercenary_data *)bl;
- ud->canact_tick = ud->canmove_tick;
- if( mercenary_get_lifetime(md) <= 0 && !(md->master && !md->master->state.active) )
- {
- clif_clearunit_area(bl,clrtype);
- map_delblock(bl);
- unit_free(bl,CLR_OUTSIGHT);
- map_freeblock_unlock();
- return 0;
+ case BL_HOM: {
+ struct homun_data *hd = (struct homun_data *)bl;
+ ud->canact_tick = ud->canmove_tick; //It appears HOM do reset the can-act tick.
+ if( !hd->homunculus.intimacy && !(hd->master && !hd->master->state.active) )
+ { //If logging out, this is deleted on unit_free
+ clif_emotion(bl, E_SOB);
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,CLR_OUTSIGHT);
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
}
- break;
- }
- default: ;// do nothing
+ case BL_MER: {
+ struct mercenary_data *md = (struct mercenary_data *)bl;
+ ud->canact_tick = ud->canmove_tick;
+ if( mercenary_get_lifetime(md) <= 0 && !(md->master && !md->master->state.active) )
+ {
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,CLR_OUTSIGHT);
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+ }
+ case BL_ELEM: {
+ struct elemental_data *ed = (struct elemental_data *)bl;
+ ud->canact_tick = ud->canmove_tick;
+ if( elemental_get_lifetime(ed) <= 0 && !(ed->master && !ed->master->state.active) )
+ {
+ clif_clearunit_area(bl,clrtype);
+ map_delblock(bl);
+ unit_free(bl,0);
+ map_freeblock_unlock();
+ return 0;
+ }
+ break;
+ }
+ default: break;// do nothing
}
/**
* BL_MOB is handled by mob_dead unless the monster is not dead.
@@ -2391,6 +2400,24 @@ int unit_free(struct block_list *bl, clr_type clrtype)
merc_contract_stop(md);
break;
}
+ case BL_ELEM: {
+ struct elemental_data *ed = (TBL_ELEM*)bl;
+ struct map_session_data *sd = ed->master;
+ if( clrtype >= 0 ) {
+ if( elemental_get_lifetime(ed) > 0 )
+ elemental_save(ed);
+ else {
+ intif_elemental_delete(ed->elemental.elemental_id);
+ if( sd )
+ sd->status.ele_id = 0;
+ }
+ }
+ if( sd )
+ sd->ed = NULL;
+
+ elemental_summon_stop(ed);
+ break;
+ }
}
skill_clear_unitgroup(bl);