summaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/HPMmap.c44
-rw-r--r--src/map/HPMmap.h2
-rw-r--r--src/map/Makefile.in2
-rw-r--r--src/map/atcommand.c44
-rw-r--r--src/map/battle.c57
-rw-r--r--src/map/battle.h8
-rw-r--r--src/map/chrif.c7
-rw-r--r--src/map/clif.c417
-rw-r--r--src/map/clif.h49
-rw-r--r--src/map/guild.c10
-rw-r--r--src/map/intif.c6
-rw-r--r--src/map/itemdb.c6
-rw-r--r--src/map/itemdb.h2
-rw-r--r--src/map/log.h2
-rw-r--r--src/map/map.c146
-rw-r--r--src/map/map.h18
-rw-r--r--src/map/mob.c169
-rw-r--r--src/map/mob.h5
-rw-r--r--src/map/npc.c75
-rw-r--r--src/map/packets.h127
-rw-r--r--src/map/packets_struct.h70
-rw-r--r--src/map/party.c8
-rw-r--r--src/map/path.c95
-rw-r--r--src/map/path.h12
-rw-r--r--src/map/pc.c168
-rw-r--r--src/map/pc.h28
-rw-r--r--src/map/pet.c2
-rw-r--r--src/map/script.c527
-rw-r--r--src/map/script.h8
-rw-r--r--src/map/skill.c210
-rw-r--r--src/map/skill.h1
-rw-r--r--src/map/status.c182
-rw-r--r--src/map/status.h8
-rw-r--r--src/map/storage.c28
-rw-r--r--src/map/storage.h3
-rw-r--r--src/map/unit.c354
-rw-r--r--src/map/unit.h8
37 files changed, 2098 insertions, 810 deletions
diff --git a/src/map/HPMmap.c b/src/map/HPMmap.c
index cb8c979c6..a0701ae45 100644
--- a/src/map/HPMmap.c
+++ b/src/map/HPMmap.c
@@ -74,11 +74,6 @@ struct HPM_atcommand_list {
struct HPM_atcommand_list *atcommand_list = NULL;
unsigned int atcommand_list_items = 0;
-/**
- * (char*) data name -> (unsigned int) HPMDataCheck[] index
- **/
-DBMap *datacheck_db;
-
bool HPM_map_grabHPData(struct HPDataOperationStorage *ret, enum HPluginDataTypes type, void *ptr) {
/* record address */
switch( type ) {
@@ -147,29 +142,6 @@ void HPM_map_atcommands(void) {
}
/**
- * Called by HPM->DataCheck on a plugins incoming data, ensures data structs in use are matching!
- **/
-bool HPM_map_DataCheck (struct s_HPMDataCheck *src, unsigned int size, char *name) {
- unsigned int i, j;
-
- for(i = 0; i < size; i++) {
-
- if( !strdb_exists(datacheck_db, src[i].name) ) {
- ShowError("HPMDataCheck:%s: '%s' was not found\n",name,src[i].name);
- return false;
- } else {
- j = strdb_uiget(datacheck_db, src[i].name);/* not double lookup; exists sets cache to found data */
- if( src[i].size != HPMDataCheck[j].size ) {
- ShowWarning("HPMDataCheck:%s: '%s' size mismatch %u != %u\n",name,src[i].name,src[i].size,HPMDataCheck[j].size);
- return false;
- }
- }
- }
-
- return true;
-}
-
-/**
* Adds a new group permission to the HPM-provided list
**/
void HPM_map_add_group_permission(unsigned int pluginID, char *name, unsigned int *mask) {
@@ -183,17 +155,9 @@ void HPM_map_add_group_permission(unsigned int pluginID, char *name, unsigned in
}
void HPM_map_do_init(void) {
- unsigned int i;
-
- /**
- * Populates datacheck_db for easy lookup later on
- **/
- datacheck_db = strdb_alloc(DB_OPT_BASE,0);
-
- for(i = 0; i < HPMDataCheckLen; i++) {
- strdb_uiput(datacheck_db, HPMDataCheck[i].name, i);
- }
-
+ HPM->load_sub = HPM_map_plugin_load_sub;
+ HPM->grabHPDataSub = HPM_map_grabHPData;
+ HPM->datacheck_init(HPMDataCheck, HPMDataCheckLen, HPMDataCheckVer);
}
void HPM_map_do_final(void) {
@@ -211,5 +175,5 @@ void HPM_map_do_final(void) {
if( pcg->HPMpermissions )
aFree(pcg->HPMpermissions);
- db_destroy(datacheck_db);
+ HPM->datacheck_final();
}
diff --git a/src/map/HPMmap.h b/src/map/HPMmap.h
index 99c4224ff..fa2f625c0 100644
--- a/src/map/HPMmap.h
+++ b/src/map/HPMmap.h
@@ -22,8 +22,6 @@ void HPM_map_do_final(void);
void HPM_map_add_group_permission(unsigned int pluginID, char *name, unsigned int *mask);
-bool HPM_map_DataCheck(struct s_HPMDataCheck *src, unsigned int size, char *name);
-
void HPM_map_do_init(void);
#endif /* MAP_HPMMAP_H */
diff --git a/src/map/Makefile.in b/src/map/Makefile.in
index fc58c9d70..ee8b7ac56 100644
--- a/src/map/Makefile.in
+++ b/src/map/Makefile.in
@@ -104,7 +104,7 @@ map-server: ../../map-server@EXEEXT@
../../map-server@EXEEXT@: $(MAP_SERVER_SQL_DEPENDS) Makefile
@echo " LD $(notdir $@)"
- @$(CC) @LDFLAGS@ -o ../../map-server@EXEEXT@ $(MAP_OBJ) $(COMMON_D)/obj_sql/common_sql.a \
+ @$(CC) @STATIC@ @LDFLAGS@ -o ../../map-server@EXEEXT@ $(MAP_OBJ) $(COMMON_D)/obj_sql/common_sql.a \
$(COMMON_D)/obj_all/common.a $(MT19937AR_OBJ) $(LIBCONFIG_OBJ) @LIBS@ @PCRE_LIBS@ @MYSQL_LIBS@
# map object files
diff --git a/src/map/atcommand.c b/src/map/atcommand.c
index 497e46520..ef528c454 100644
--- a/src/map/atcommand.c
+++ b/src/map/atcommand.c
@@ -583,7 +583,7 @@ ACMD(who) {
iter = mapit_getallusers();
for (pl_sd = (TBL_PC*)mapit->first(iter); mapit->exists(iter); pl_sd = (TBL_PC*)mapit->next(iter)) {
- if (!((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || (pl_sd->sc.option & OPTION_INVISIBLE)) && pc_get_group_level(pl_sd) > level)) { // you can look only lower or same level
+ if (!((pc_has_permission(pl_sd, PC_PERM_HIDE_SESSION) || pc_isinvisible(pl_sd)) && pc_get_group_level(pl_sd) > level)) { // you can look only lower or same level
if (stristr(pl_sd->status.name, player_name) == NULL // search with no case sensitive
|| (map_id >= 0 && pl_sd->bl.m != map_id))
continue;
@@ -689,7 +689,7 @@ ACMD(whogm)
continue;
}
if (pl_level > level) {
- if (pl_sd->sc.option & OPTION_INVISIBLE)
+ if (pc_isinvisible(pl_sd))
continue;
sprintf(atcmd_output, msg_txt(913), pl_sd->status.name); // Name: %s (GM)
clif->message(fd, atcmd_output);
@@ -889,7 +889,7 @@ ACMD(option)
*
*------------------------------------------*/
ACMD(hide) {
- if (sd->sc.option & OPTION_INVISIBLE) {
+ if (pc_isinvisible(sd)) {
sd->sc.option &= ~OPTION_INVISIBLE;
if (sd->disguise != -1 )
status->set_viewdata(&sd->bl, sd->disguise);
@@ -3923,12 +3923,12 @@ ACMD(mount_peco)
clif->message(fd, atcmd_output);
return false;
}
- if( !(sd->sc.option&OPTION_DRAGON1) ) {
+ if (!pc_isridingdragon(sd)) {
clif->message(sd->fd,msg_txt(1119)); // You have mounted your Dragon.
- pc->setoption(sd, sd->sc.option|OPTION_DRAGON1);
+ pc->setridingdragon(sd, OPTION_DRAGON1);
} else {
clif->message(sd->fd,msg_txt(1120)); // You have released your Dragon.
- pc->setoption(sd, sd->sc.option&~OPTION_DRAGON1);
+ pc->setridingdragon(sd, 0);
}
return true;
}
@@ -3940,34 +3940,34 @@ ACMD(mount_peco)
}
if( !pc_isridingwug(sd) ) {
clif->message(sd->fd,msg_txt(1121)); // You have mounted your Warg.
- pc->setoption(sd, sd->sc.option|OPTION_WUGRIDER);
+ pc->setridingwug(sd, true);
} else {
clif->message(sd->fd,msg_txt(1122)); // You have released your Warg.
- pc->setoption(sd, sd->sc.option&~OPTION_WUGRIDER);
+ pc->setridingwug(sd, false);
}
return true;
}
if( (sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC ) {
if( !pc_ismadogear(sd) ) {
clif->message(sd->fd,msg_txt(1123)); // You have mounted your Mado Gear.
- pc->setoption(sd, sd->sc.option|OPTION_MADOGEAR);
+ pc->setmadogear(sd, true);
} else {
clif->message(sd->fd,msg_txt(1124)); // You have released your Mado Gear.
- pc->setoption(sd, sd->sc.option&~OPTION_MADOGEAR);
+ pc->setmadogear(sd, false);
}
return true;
}
if( sd->class_&MAPID_SWORDMAN && sd->class_&JOBL_2 ) {
- if( !pc_isriding(sd) ) { // if actually no peco
+ if (!pc_isridingpeco(sd)) { // if actually no peco
if (!pc->checkskill(sd, KN_RIDING)) {
sprintf(atcmd_output, msg_txt(213), skill->get_desc(KN_RIDING)); // You need %s to mount!
clif->message(fd, atcmd_output);
return false;
}
- pc->setoption(sd, sd->sc.option | OPTION_RIDING);
+ pc->setridingpeco(sd, true);
clif->message(fd, msg_txt(102)); // You have mounted a Peco Peco.
} else {//Dismount
- pc->setoption(sd, sd->sc.option & ~OPTION_RIDING);
+ pc->setridingpeco(sd, false);
clif->message(fd, msg_txt(214)); // You have released your Peco Peco.
}
return true;
@@ -4621,8 +4621,7 @@ ACMD(disguise)
return false;
}
- if(pc_isriding(sd))
- {
+ if (pc_hasmount(sd)) {
clif->message(fd, msg_txt(1144)); // Character cannot be disguised while mounted.
return false;
}
@@ -4709,8 +4708,8 @@ ACMD(disguiseguild)
return false;
}
- for( i = 0; i < g->max_member; i++ )
- if( (pl_sd = g->member[i].sd) && !pc_isriding(pl_sd) )
+ for (i = 0; i < g->max_member; i++)
+ if ((pl_sd = g->member[i].sd) && !pc_hasmount(pl_sd))
pc->disguise(pl_sd, id);
clif->message(fd, msg_txt(122)); // Disguise applied.
@@ -5143,7 +5142,7 @@ ACMD(cleargstorage)
return false;
}
- guild_storage = gstorage->id2storage2(sd->status.guild_id);
+ guild_storage = idb_get(gstorage->db,sd->status.guild_id);
if (guild_storage == NULL) {// Doesn't have opened @gstorage yet, so we skip the deletion since *shouldn't* have any item there.
return false;
}
@@ -8358,15 +8357,16 @@ ACMD(charcommands)
return true;
}
/* for new mounts */
-ACMD(mount2) {
+ACMD(cashmount)
+{
- if( sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR) ) {
+ if (pc_hasmount(sd)) {
clif->message(fd, msg_txt(1476)); // You are already mounting something else
return false;
}
clif->message(sd->fd,msg_txt(1362)); // NOTICE: If you crash with mount your LUA is outdated.
- if( !(sd->sc.data[SC_ALL_RIDING]) ) {
+ if (!sd->sc.data[SC_ALL_RIDING]) {
clif->message(sd->fd,msg_txt(1363)); // You have mounted.
sc_start(NULL,&sd->bl,SC_ALL_RIDING,100,0,-1);
} else {
@@ -9634,7 +9634,7 @@ void atcommand_basecommands(void) {
ACMD_DEF2("rmvperm", addperm),
ACMD_DEF(unloadnpcfile),
ACMD_DEF(cart),
- ACMD_DEF(mount2),
+ ACMD_DEF(cashmount),
ACMD_DEF(join),
ACMD_DEF(channel),
ACMD_DEF(fontcolor),
diff --git a/src/map/battle.c b/src/map/battle.c
index 544d53a1c..73b563d4b 100644
--- a/src/map/battle.c
+++ b/src/map/battle.c
@@ -220,9 +220,9 @@ int battle_delay_damage_sub(int tid, int64 tick, int id, intptr_t data) {
src = map->id2bl(dat->src_id);
//Check to see if you haven't teleported. [Skotlex]
- if( src && target->m == src->m
+ if( src
&& (target->type != BL_PC || ((TBL_PC*)target)->invincible_timer == INVALID_TIMER)
- && check_distance_bl(src, target, dat->distance)
+ && (dat->skill_id == MO_EXTREMITYFIST || (target->m == src->m && check_distance_bl(src, target, dat->distance)) )
) {
map->freeblock_lock();
status_fix_damage(src, target, dat->damage, dat->delay);
@@ -663,10 +663,10 @@ int64 battle_addmastery(struct map_session_data *sd,struct block_list *target,in
break;
case W_1HSPEAR:
case W_2HSPEAR:
- if((skill_lv = pc->checkskill(sd,KN_SPEARMASTERY)) > 0) {
- if(pc_isridingdragon(sd))
+ if ((skill_lv = pc->checkskill(sd,KN_SPEARMASTERY)) > 0) {
+ if (pc_isridingdragon(sd))
damage += (skill_lv * 10);
- else if(pc_isriding(sd))
+ else if (pc_isridingpeco(sd))
damage += (skill_lv * 5);
else
damage += (skill_lv * 4);
@@ -2707,10 +2707,6 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
sc_start2(src,bl,SC_COMBOATTACK,100,GC_WEAPONBLOCKING,src->id,2000);
return 0;
}
- if( sc->data[SC_HOVERING] && skill_id && (skill->get_inf(skill_id)&INF_GROUND_SKILL || skill_id == SR_WINDMILL) ) {
- d->dmg_lv = ATK_BLOCK;
- return 0;
- }
if ((sce=sc->data[SC_AUTOGUARD]) && flag&BF_WEAPON && !(skill->get_nk(skill_id)&NK_NO_CARDFIX_ATK) && rnd()%100 < sce->val2) {
int delay;
struct block_list *d_bl = NULL;
@@ -2783,7 +2779,7 @@ int64 battle_calc_damage(struct block_list *src,struct block_list *bl,struct Dam
return 0;
}
- if((sc->data[SC_HERMODE] || sc->data[SC_HOVERING]) && flag&BF_MAGIC)
+ if((sc->data[SC_HERMODE]) && flag&BF_MAGIC)
return 0;
if(sc->data[SC_NJ_TATAMIGAESHI] && (flag&(BF_MAGIC|BF_LONG)) == BF_LONG)
@@ -3190,8 +3186,9 @@ int64 battle_calc_gvg_damage(struct block_list *src,struct block_list *bl,int64
switch (skill_id) {
#ifndef RENEWAL
case MO_TRIPLEATTACK:
-#endif
case HW_GRAVITATION:
+#endif
+ case TF_DOUBLE:
break;
default:
return 0;
@@ -5105,6 +5102,7 @@ struct Damage battle_calc_weapon_attack(struct block_list *src,struct block_list
}
#endif
+#ifndef RENEWAL // Offensive damage increment in renewal is done somewhere else
if (sd) {
if (skill_id != CR_SHIELDBOOMERANG) //Only Shield boomerang doesn't takes the Star Crumbs bonus.
ATK_ADD2(wd.div_*sd->right_weapon.star, wd.div_*sd->left_weapon.star);
@@ -5126,7 +5124,7 @@ struct Damage battle_calc_weapon_attack(struct block_list *src,struct block_list
ATK_ADD(10*sd->status.inventory[index].refine);
}
}
-
+#endif
//Card Fix, tsd side
if(tsd){ //if player on player then it was already measured above
wd.damage = battle->calc_cardfix(BF_WEAPON, src, target, nk, s_ele, s_ele_, wd.damage, (flag.lh?1:0), wd.flag);
@@ -5814,6 +5812,7 @@ enum damage_lv battle_weapon_attack(struct block_list* src, struct block_list* t
if( --(sc->data[SC_SPELLFIST]->val1) >= 0 ){
struct Damage ad = battle->calc_attack(BF_MAGIC,src,target,sc->data[SC_SPELLFIST]->val3,sc->data[SC_SPELLFIST]->val4,flag|BF_SHORT);
wd.damage = ad.damage;
+ damage_div_fix(wd.damage, wd.div_);
}else
status_change_end(src,SC_SPELLFIST,INVALID_TIMER);
}
@@ -6146,7 +6145,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
TBL_SKILL *su = (TBL_SKILL*)target;
if( !su->group )
return 0;
- if( skill->get_inf2(su->group->skill_id)&INF2_TRAP ) { //Only a few skills can target traps...
+ if( skill->get_inf2(su->group->skill_id)&INF2_TRAP && su->group->unit_id != UNT_USED_TRAPS) { //Only a few skills can target traps...
switch( battle->get_current_skill(src) ) {
case RK_DRAGONBREATH:// it can only hit traps in pvp/gvg maps
case RK_DRAGONBREATH_WATER:
@@ -6245,6 +6244,7 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
break;
case BL_SKILL: {
struct skill_unit *su = (struct skill_unit *)src;
+ struct status_change* sc = status->get_sc(target);
if (!su->group)
return 0;
@@ -6255,6 +6255,11 @@ int battle_check_target( struct block_list *src, struct block_list *target,int f
if (inf2&INF2_TARGET_SELF)
return 1;
}
+ //Status changes that prevent traps from triggering
+ if (sc && sc->count && skill->get_inf2(su->group->skill_id)&INF2_TRAP) {
+ if( sc->data[SC_WZ_SIGHTBLASTER] && sc->data[SC_WZ_SIGHTBLASTER]->val2 > 0 && sc->data[SC_WZ_SIGHTBLASTER]->val4%2 == 0)
+ return -1;
+ }
}
break;
case BL_MER:
@@ -6417,6 +6422,12 @@ bool battle_check_range(struct block_list *src, struct block_list *bl, int range
if( src->m != bl->m )
return false;
+#ifndef CIRCULAR_AREA
+ if( src->type == BL_PC ) { // Range for players' attacks and skills should always have a circular check. [Angezerus]
+ if ( !check_distance_client_bl(src, bl, range) )
+ return false;
+ } else
+#endif
if( !check_distance_bl(src, bl, range) )
return false;
@@ -6451,7 +6462,7 @@ static const struct battle_data {
{ "skill_add_range", &battle_config.skill_add_range, 0, 0, INT_MAX, },
{ "skill_out_range_consume", &battle_config.skill_out_range_consume, 1, 0, 1, },
{ "skillrange_by_distance", &battle_config.skillrange_by_distance, ~BL_PC, BL_NUL, BL_ALL, },
- { "skillrange_from_weapon", &battle_config.use_weapon_skill_range, ~BL_PC, BL_NUL, BL_ALL, },
+ { "skillrange_from_weapon", &battle_config.use_weapon_skill_range, BL_NUL, BL_NUL, BL_ALL, },
{ "player_damage_delay_rate", &battle_config.pc_damage_delay_rate, 100, 0, INT_MAX, },
{ "defunit_not_enemy", &battle_config.defnotenemy, 0, 0, 1, },
{ "gvg_traps_target_all", &battle_config.vs_traps_bctall, BL_PC, BL_NUL, BL_ALL, },
@@ -6628,7 +6639,8 @@ static const struct battle_data {
{ "bone_drop", &battle_config.bone_drop, 0, 0, 2, },
{ "buyer_name", &battle_config.buyer_name, 1, 0, 1, },
{ "skill_wall_check", &battle_config.skill_wall_check, 1, 0, 1, },
- { "cell_stack_limit", &battle_config.cell_stack_limit, 1, 1, 255, },
+ { "official_cell_stack_limit", &battle_config.official_cell_stack_limit, 1, 1, 255, },
+ { "custom_cell_stack_limit", &battle_config.custom_cell_stack_limit, 1, 1, 255, },
{ "dancing_weaponswitch_fix", &battle_config.dancing_weaponswitch_fix, 1, 0, 1, },
// eAthena additions
@@ -6826,6 +6838,10 @@ static const struct battle_data {
{ "guild_castle_expulsion", &battle_config.guild_castle_expulsion, 0, 0, 1, },
{ "song_timer_reset", &battle_config.song_timer_reset, 0, 0, 1, },
{ "snap_dodge", &battle_config.snap_dodge, 0, 0, 1, },
+ { "monster_chase_refresh", &battle_config.mob_chase_refresh, 1, 0, 30, },
+ { "mob_icewall_walk_block", &battle_config.mob_icewall_walk_block, 75, 0, 255, },
+ { "boss_icewall_walk_block", &battle_config.boss_icewall_walk_block, 0, 0, 255, },
+ { "feature.roulette", &battle_config.feature_roulette, 1, 0, 1, },
};
#ifndef STATS_OPT_OUT
/**
@@ -7077,6 +7093,13 @@ void battle_adjust_conf(void) {
}
#endif
+#if PACKETVER < 20141022
+ if( battle_config.feature_roulette ) {
+ ShowWarning("conf/battle/feature.conf roulette is enabled but it requires PACKETVER 2014-10-22 or newer, disabling...\n");
+ battle_config.feature_roulette = 0;
+ }
+#endif
+
#if PACKETVER > 20120000 && PACKETVER < 20130515 /* exact date (when it started) not known */
if( battle_config.feature_auction == 1 ) {
ShowWarning("conf/battle/feature.conf:feature.auction is enabled but it is not stable on PACKETVER "EXPAND_AND_QUOTE(PACKETVER)", disabling...\n");
@@ -7087,8 +7110,8 @@ void battle_adjust_conf(void) {
#ifndef CELL_NOSTACK
- if (battle_config.cell_stack_limit != 1)
- ShowWarning("Battle setting 'cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support (CELL_NOSTACK).\n");
+ if (battle_config.custom_cell_stack_limit != 1)
+ ShowWarning("Battle setting 'custom_cell_stack_limit' takes no effect as this server was compiled without Cell Stack Limit support.\n");
#endif
}
diff --git a/src/map/battle.h b/src/map/battle.h
index 734a6187d..c44e3e19d 100644
--- a/src/map/battle.h
+++ b/src/map/battle.h
@@ -381,7 +381,8 @@ struct Battle_Config {
int allow_skill_without_day; // [Komurka]
int allow_es_magic_pc; // [Skotlex]
int skill_wall_check; // [Skotlex]
- int cell_stack_limit; // [Skotlex]
+ int official_cell_stack_limit; // [Playtester]
+ int custom_cell_stack_limit; // [Skotlex]
int skill_caster_check; // [Skotlex]
int sc_castcancel; // [Skotlex]
int pc_sc_def_rate; // [Skotlex]
@@ -451,6 +452,9 @@ struct Battle_Config {
int mob_size_influence; // Enable modifications on earned experience, drop rates and monster status depending on monster size. [mkbu95]
int bowling_bash_area;
+ int mob_chase_refresh; //How often a monster should refresh its chase [Playtester]
+ int mob_icewall_walk_block; //How a normal monster should be trapped in icewall [Playtester]
+ int boss_icewall_walk_block; //How a boss monster should be trapped in icewall [Playtester]
/** Hercules **/
int skill_trap_type;
@@ -477,6 +481,8 @@ struct Battle_Config {
int song_timer_reset; // [csnv]
int snap_dodge; // Enable or disable dodging damage snapping away [csnv]
+
+ int feature_roulette;
};
extern struct Battle_Config battle_config;
diff --git a/src/map/chrif.c b/src/map/chrif.c
index 34e92bee0..c78b34309 100644
--- a/src/map/chrif.c
+++ b/src/map/chrif.c
@@ -1125,9 +1125,12 @@ bool chrif_save_scdata(struct map_session_data *sd) { //parses the sc_data of th
continue;
if (sc->data[i]->timer != INVALID_TIMER) {
td = timer->get(sc->data[i]->timer);
- if (td == NULL || td->func != status->change_timer || DIFF_TICK(td->tick,tick) < 0)
+ if (td == NULL || td->func != status->change_timer)
continue;
- data.tick = DIFF_TICK32(td->tick,tick); //Duration that is left before ending.
+ if (DIFF_TICK32(td->tick,tick) > 0)
+ data.tick = DIFF_TICK32(td->tick,tick); //Duration that is left before ending.
+ else
+ data.tick = 0; //Negative tick does not necessarily mean that sc has expired
} else
data.tick = -1; //Infinite duration
data.type = i;
diff --git a/src/map/clif.c b/src/map/clif.c
index 018087000..169f30793 100644
--- a/src/map/clif.c
+++ b/src/map/clif.c
@@ -619,7 +619,9 @@ void clif_authok(struct map_session_data *sd)
#if PACKETVER >= 20080102
p.font = sd->status.font;
#endif
-
+#if PACKETVER >= 20141016
+ p.sex = sd->status.sex;
+#endif
clif->send(&p,sizeof(p),&sd->bl,SELF);
}
@@ -2703,14 +2705,15 @@ void read_channels_config(void) {
} else {
unsigned char d = 0, dlen = strlen(irc_server);
char server[40];
-
+ if (dlen > 39)
+ dlen = 39;
memset(server, '\0', sizeof(server));
for(d = 0; d < dlen; d++) {
if(irc_server[d] == ':') {
memcpy(server, irc_server, d);
safestrncpy(hChSys.irc_server, server, 40);
- memcpy(server, &irc_server[d+1], dlen);
+ memcpy(server, &irc_server[d+1], dlen - d - 1);
hChSys.irc_server_port = atoi(server);
break;
}
@@ -3432,7 +3435,6 @@ void clif_arrowequip(struct map_session_data *sd,int val)
nullpo_retv(sd);
- pc_stop_attack(sd); // [Valaris]
#if PACKETVER >= 20121128
clif->status_change(&sd->bl, SI_CLIENT_ONLY_EQUIP_ARROW, 1, INVALID_TIMER, 0, 0, 0);
#endif
@@ -4786,7 +4788,9 @@ int clif_outsight(struct block_list *bl,va_list ap)
}
if (sd && sd->fd) { //sd is watching tbl go out of view.
nullpo_ret(tbl);
- if (((vd=status->get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) &&
+ if(tbl->type == BL_SKILL) //Trap knocked out of sight
+ clif->clearchar_skillunit((struct skill_unit *)tbl,sd->fd);
+ else if (((vd=status->get_viewdata(tbl)) && vd->class_ != INVISIBLE_CLASS) &&
!(tbl->type == BL_NPC && (((TBL_NPC*)tbl)->option&OPTION_INVISIBLE)))
clif->clearunit_single(tbl->id,CLR_OUTSIGHT,sd->fd);
}
@@ -5579,6 +5583,7 @@ void clif_status_change(struct block_list *bl,int type,int flag,int tick,int val
p.index = type;
p.AID = bl->id;
p.state = (unsigned char)flag;
+
#if PACKETVER >= 20120618
p.Total = tick; /* at this stage remain and total are the same value I believe */
#endif
@@ -5599,6 +5604,11 @@ void clif_displaymessage(const int fd, const char* mes) {
if( map->cpsd_active && fd == 0 ) {
ShowInfo("HCP: %s\n",mes);
} else if ( fd > 0 ) {
+ #if PACKETVER == 20141022
+ /** for some reason game client crashes depending on message pattern (only for this packet) **/
+ /** so we redirect to ZC_NPC_CHAT **/
+ clif->colormes(fd,COLOR_DEFAULT,mes);
+ #else
size_t len;
if ( ( len = strnlen(mes, 255) ) > 0 ) { // don't send a void message (it's not displaying on the client chat). @help can send void line.
@@ -5608,6 +5618,7 @@ void clif_displaymessage(const int fd, const char* mes) {
safestrncpy((char *)WFIFOP(fd,4), mes, len + 1);
WFIFOSET(fd, 5 + len);
}
+ #endif
}
}
void clif_displaymessage2(const int fd, const char* mes) {
@@ -5832,12 +5843,12 @@ void clif_pvpset(struct map_session_data *sd,int pvprank,int pvpnum,int type)
unsigned char buf[32];
WBUFW(buf,0) = 0x19a;
WBUFL(buf,2) = sd->bl.id;
- if(sd->sc.option&(OPTION_HIDE|OPTION_CLOAK))
+ if (sd->sc.option&(OPTION_HIDE|OPTION_CLOAK)) // TODO[Haru] Should this be pc_ishiding(sd)? (i.e. include Chase Walk and any new options)
WBUFL(buf,6) = UINT32_MAX; //On client displays as --
else
WBUFL(buf,6) = pvprank;
WBUFL(buf,10) = pvpnum;
- if(sd->sc.option&OPTION_INVISIBLE || sd->disguise != -1) //Causes crashes when a 'mob' with pvp info dies.
+ if (pc_isinvisible(sd) || sd->disguise != -1) //Causes crashes when a 'mob' with pvp info dies.
clif->send(buf,packet_len(0x19a),&sd->bl,SELF);
else if(!type)
clif->send(buf,packet_len(0x19a),&sd->bl,AREA);
@@ -8444,7 +8455,7 @@ void clif_refresh_storagewindow( struct map_session_data *sd ) {
// remain locked forever and nobody will be able to access it
if( sd->state.storage_flag == 2 ) {
struct guild_storage *gstor;
- if( (gstor = gstorage->id2storage2(sd->status.guild_id)) == NULL) {
+ if( (gstor = idb_get(gstorage->db,sd->status.guild_id)) == NULL) {
// Shouldn't happen... The information should already be at the map-server
intif->request_guild_storage(sd->status.account_id,sd->status.guild_id);
} else {
@@ -9339,7 +9350,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) {
sd->state.hpmeter_visible = 1;
}
- if( !(sd->sc.option&OPTION_INVISIBLE) ) { // increment the number of pvp players on the map
+ if (!pc_isinvisible(sd)) { // increment the number of pvp players on the map
map->list[sd->bl.m].users_pvp++;
}
@@ -9360,7 +9371,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) {
if( sd->bg_id ) clif->bg_hp(sd); // BattleGround System
- if(map->list[sd->bl.m].flag.pvp && !(sd->sc.option&OPTION_INVISIBLE)) {
+ if (map->list[sd->bl.m].flag.pvp && !pc_isinvisible(sd)) {
if(!battle_config.pk_mode) { // remove pvp stuff for pk_mode [Valaris]
if (!map->list[sd->bl.m].flag.pvp_nocalcrank)
sd->pvp_timer = timer->add(timer->gettick()+200, pc->calc_pvprank_timer, sd->bl.id, 0);
@@ -9445,11 +9456,11 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) {
clif->updatestatus(sd,SP_SKILLPOINT);
clif->initialstatus(sd);
- if (sd->sc.option&OPTION_FALCON)
+ if (pc_isfalcon(sd))
clif->status_change(&sd->bl, SI_FALCON, 1, 0, 0, 0, 0);
- if (sd->sc.option&(OPTION_RIDING|OPTION_DRAGON))
+ if (pc_isridingpeco(sd) || pc_isridingdragon(sd))
clif->status_change(&sd->bl, SI_RIDING, 1, 0, 0, 0, 0);
- else if (sd->sc.option&OPTION_WUGRIDER)
+ else if (pc_isridingwug(sd))
clif->status_change(&sd->bl, SI_WUGRIDER, 1, 0, 0, 0, 0);
if(sd->status.manner < 0)
@@ -9650,18 +9661,22 @@ void clif_hotkeys_send(struct map_session_data *sd) {
#ifdef HOTKEY_SAVING
const int fd = sd->fd;
int i;
+ int offset = 2;
#if PACKETVER < 20090603
const int cmd = 0x2b9;
-#else
+#elif PACKETVER < 20141022
const int cmd = 0x7d9;
+#else
+ const int cmd = 0xa00;
+ offset = 3;
#endif
if (!fd) return;
- WFIFOHEAD(fd, 2+MAX_HOTKEYS*7);
+ WFIFOHEAD(fd, offset+MAX_HOTKEYS*7);
WFIFOW(fd, 0) = cmd;
for(i = 0; i < MAX_HOTKEYS; i++) {
- WFIFOB(fd, 2 + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill
- WFIFOL(fd, 2 + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID
- WFIFOW(fd, 2 + 5 + i * 7) = sd->status.hotkeys[i].lv; // skill level
+ WFIFOB(fd, offset + 0 + i * 7) = sd->status.hotkeys[i].type; // type: 0: item, 1: skill
+ WFIFOL(fd, offset + 1 + i * 7) = sd->status.hotkeys[i].id; // item or skill ID
+ WFIFOW(fd, offset + 5 + i * 7) = sd->status.hotkeys[i].lv; // item qty or skill level
}
WFIFOSET(fd, packet_len(cmd));
#endif
@@ -10122,7 +10137,8 @@ void clif_parse_ActionRequest_sub(struct map_session_data *sd, int action_type,
)
return;
- pc_stop_walking(sd, 1);
+ if(action_type != 0x00 && action_type != 0x07)
+ pc_stop_walking(sd, 1);
pc_stop_attack(sd);
if(target_id<0 && -target_id == sd->bl.id) // for disguises [Valaris]
@@ -10488,7 +10504,7 @@ void clif_parse_WisMessage(int fd, struct map_session_data* sd)
// if player ignores everyone
if (dstsd->state.ignoreAll && pc_get_group_level(sd) <= pc_get_group_level(dstsd)) {
- if (dstsd->sc.option & OPTION_INVISIBLE && pc_get_group_level(sd) < pc_get_group_level(dstsd))
+ if (pc_isinvisible(dstsd) && pc_get_group_level(sd) < pc_get_group_level(dstsd))
clif->wis_end(fd, 1); // 1: target character is not logged in
else
clif->wis_end(fd, 3); // 3: everyone ignored by target
@@ -11162,16 +11178,17 @@ void clif_parse_GetItemFromCart(int fd,struct map_session_data *sd)
/// 012a
void clif_parse_RemoveOption(int fd,struct map_session_data *sd)
{
- if( !(sd->sc.option&(OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR))
+ if (pc_isridingpeco(sd) || pc_isfalcon(sd) || pc_isridingdragon(sd) || pc_ismadogear(sd)) {
+ // priority to remove this option before we can clear cart
+ pc->setoption(sd,sd->sc.option&~(OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
+ } else {
#ifdef NEW_CARTS
- && sd->sc.data[SC_PUSH_CART] ){
- pc->setcart(sd,0);
-#else
- ){
+ if (sd->sc.data[SC_PUSH_CART])
+ pc->setcart(sd,0);
+#else // not NEW_CARTS
pc->setoption(sd,sd->sc.option&~OPTION_CART);
-#endif
- }else // priority to remove this option before we can clear cart
- pc->setoption(sd,sd->sc.option&~(OPTION_RIDING|OPTION_FALCON|OPTION_DRAGON|OPTION_MADOGEAR));
+#endif // NEW_CARTS
+ }
}
@@ -12234,7 +12251,7 @@ void clif_parse_PartyMessage(int fd, struct map_session_data* sd)
void clif_parse_PartyChangeLeader(int fd, struct map_session_data* sd) {
party->changeleader(sd, map->id2sd(RFIFOL(fd,2)));
}
-
+
/// Party Booking in KRO [Spiria]
///
@@ -15116,7 +15133,8 @@ void clif_parse_Mail_send(int fd, struct map_session_data *sd)
if (body_len > MAIL_BODY_LENGTH)
body_len = MAIL_BODY_LENGTH;
- if( !mail->setattachment(sd, &msg) ) { // Invalid Append condition
+ memset(&msg, 0, sizeof(msg));
+ if (!mail->setattachment(sd, &msg)) { // Invalid Append condition
clif->mail_send(sd->fd, true); // fail
mail->removeitem(sd,0);
mail->removezeny(sd,0);
@@ -15334,9 +15352,10 @@ void clif_parse_Auction_register(int fd, struct map_session_data *sd)
struct auction_data auction;
struct item_data *item;
- if( !battle_config.feature_auction )
+ if (!battle_config.feature_auction)
return;
+ memset(&auction, 0, sizeof(auction));
auction.price = RFIFOL(fd,2);
auction.buynow = RFIFOL(fd,6);
auction.hours = RFIFOW(fd,10);
@@ -15794,19 +15813,34 @@ void clif_parse_PartyTick(int fd, struct map_session_data* sd)
/// 02b1 <packet len>.W <num>.L { <quest id>.L <active>.B }*num
void clif_quest_send_list(struct map_session_data *sd) {
int fd = sd->fd;
- int i;
- int len = sd->avail_quests*5+8;
-
+ int i;
+#if PACKETVER >= 20141022
+ int info_len = 15;
+ int len = sd->avail_quests*info_len+8;
WFIFOHEAD(fd,len);
- WFIFOW(fd, 0) = 0x2b1;
+ WFIFOW(fd, 0) = 0x97a;
+#else
+ int info_len = 5;
+ int len = sd->avail_quests*info_len+8;
+ WFIFOHEAD(fd,len);
+ WFIFOW(fd, 0) = 0x2b1;
+#endif
WFIFOW(fd, 2) = len;
WFIFOL(fd, 4) = sd->avail_quests;
for( i = 0; i < sd->avail_quests; i++ ) {
- WFIFOL(fd, i*5+8) = sd->quest_log[i].quest_id;
- WFIFOB(fd, i*5+12) = sd->quest_log[i].state;
+ #if PACKETVER >= 20141022
+ struct quest_db *qi = quest->db(sd->quest_log[i].quest_id);
+ #endif
+ WFIFOL(fd, i*info_len+8) = sd->quest_log[i].quest_id;
+ WFIFOB(fd, i*info_len+12) = sd->quest_log[i].state;
+ #if PACKETVER >= 20141022
+ WFIFOL(fd, i*info_len+13) = sd->quest_log[i].time - qi->time;
+ WFIFOL(fd, i*info_len+17) = sd->quest_log[i].time;
+ WFIFOW(fd, i*info_len+21) = qi->num_objectives;
+ #endif
}
-
+
WFIFOSET(fd, len);
}
@@ -18254,6 +18288,296 @@ void clif_parse_NPCMarketPurchase(int fd, struct map_session_data *sd) {
clif->npc_market_purchase_ack(sd,p,npc->market_buylist(sd,(p->PacketLength - 4) / sizeof(p->list[0]),p));
#endif
}
+
+void clif_PartyLeaderChanged(struct map_session_data *sd, int prev_leader_aid, int new_leader_aid) {
+ struct packet_party_leader_changed p;
+
+ p.PacketType = partyleaderchangedType;
+
+ p.prev_leader_aid = prev_leader_aid;
+ p.new_leader_aid = new_leader_aid;
+
+ clif->send(&p,sizeof(p),&sd->bl,PARTY);
+}
+
+/* Roulette System [Yommy/Hercules] */
+void clif_parse_RouletteOpen(int fd, struct map_session_data* sd) {
+ struct packet_roulette_open_ack p;
+
+ if( !battle_config.feature_roulette ) {
+ clif->message(fd,"Roulette is disabled");
+ return;
+ }
+
+ p.PacketType = 0xa1a;
+ p.Result = 0;
+ p.Serial = 0;
+ p.Step = sd->roulette.stage - 1;
+ p.Idx = sd->roulette.prizeIdx;
+ p.AdditionItemID = -1; /** TODO **/
+ p.BronzePoint = pc_readglobalreg(sd, script->add_str("TmpRouletteBronze"));
+ p.GoldPoint = pc_readglobalreg(sd, script->add_str("TmpRouletteGold"));
+ p.SilverPoint = pc_readglobalreg(sd, script->add_str("TmpRouletteSilver"));
+
+ clif->send(&p,sizeof(p), &sd->bl, SELF);
+}
+void clif_parse_RouletteInfo(int fd, struct map_session_data* sd) {
+ struct packet_roulette_info_ack p;
+ unsigned short i, j, count = 0;
+
+ if( !battle_config.feature_roulette ) {
+ clif->message(fd,"Roulette is disabled");
+ return;
+ }
+
+ p.PacketType = rouletteinfoackType;
+ p.PacketLength = 8 + (42 * 8);
+ p.RouletteSerial = 1;
+
+ for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+ for(j = 0; j < MAX_ROULETTE_COLUMNS-i; j++) {
+ p.ItemInfo[count].Row = i;
+ p.ItemInfo[count].Position = j;
+ p.ItemInfo[count].ItemId = clif->rd.nameid[i][j];
+ p.ItemInfo[count].Count = clif->rd.qty[i][j];
+ count++;
+ }
+ }
+
+ clif->send(&p,sizeof(p), &sd->bl, SELF);
+ return;
+}
+void clif_parse_RouletteClose(int fd, struct map_session_data* sd) {
+
+ if( !battle_config.feature_roulette ) {
+ clif->message(fd,"Roulette is disabled");
+ return;
+ }
+
+
+ /** What do we need this for? (other than state tracking), game client closes the window without our response. **/
+
+ //ShowDebug("clif_parse_RouletteClose\n");
+
+ return;
+}
+void clif_parse_RouletteGenerate(int fd, struct map_session_data* sd) {
+ unsigned char result = GENERATE_ROULETTE_SUCCESS;
+ short stage = sd->roulette.stage;
+
+ if( !battle_config.feature_roulette ) {
+ clif->message(fd,"Roulette is disabled");
+ return;
+ }
+
+ if( sd->roulette.stage >= MAX_ROULETTE_LEVEL )
+ stage = sd->roulette.stage = 0;
+
+ if( stage == 0 ) {
+ if( pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) <= 0 &&
+ pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) < 10 &&
+ pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) < 10 )
+ result = GENERATE_ROULETTE_NO_ENOUGH_POINT;
+ }
+
+ if( result == GENERATE_ROULETTE_SUCCESS ) {
+
+ if( stage == 0 ) {
+ if( pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) > 0 ) {
+ pc_setglobalreg(sd, script->add_str("TmpRouletteBronze"), pc_readglobalreg(sd, script->add_str("TmpRouletteBronze")) - 1);
+ } else if( pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) > 9 ) {
+ pc_setglobalreg(sd, script->add_str("TmpRouletteSilver"), pc_readglobalreg(sd, script->add_str("TmpRouletteSilver")) - 10);
+ stage = sd->roulette.stage = 2;
+ } else if( pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) > 9 ) {
+ pc_setglobalreg(sd, script->add_str("TmpRouletteGold"), pc_readglobalreg(sd, script->add_str("TmpRouletteGold")) - 10);
+ stage = sd->roulette.stage = 4;
+ }
+ }
+
+ sd->roulette.prizeStage = stage;
+ sd->roulette.prizeIdx = rnd()%clif->rd.items[stage];
+ if( sd->roulette.prizeIdx == 0 ) {
+ struct item it;
+ memset(&it, 0, sizeof(it));
+
+ it.nameid = clif->rd.nameid[stage][0];
+ it.identify = 1;
+
+ pc->additem(sd, &it, clif->rd.qty[stage][0], LOG_TYPE_OTHER);/** TODO maybe a new log type for roulette items? **/
+
+ sd->roulette.stage = 0;
+ result = GENERATE_ROULETTE_LOSING;
+ } else
+ sd->roulette.claimPrize = true;
+ }
+
+ clif->roulette_generate_ack(sd,result,stage,sd->roulette.prizeIdx,0);
+ if( result == GENERATE_ROULETTE_SUCCESS )
+ sd->roulette.stage++;
+}
+/**
+ * Request to cash in!
+ **/
+void clif_parse_RouletteRecvItem(int fd, struct map_session_data* sd) {
+ struct packet_roulette_itemrecv_ack p;
+
+ if( !battle_config.feature_roulette ) {
+ clif->message(fd,"Roulette is disabled");
+ return;
+ }
+
+ p.PacketType = roulettercvitemackType;
+ p.AdditionItemID = 0;/** TODO **/
+
+ if( sd->roulette.claimPrize ) {
+ struct item it;
+ memset(&it, 0, sizeof(it));
+
+ it.nameid = clif->rd.nameid[sd->roulette.prizeStage][sd->roulette.prizeIdx];
+ it.identify = 1;
+
+ switch (pc->additem(sd, &it, clif->rd.qty[sd->roulette.prizeStage][sd->roulette.prizeIdx], LOG_TYPE_OTHER)) {
+ case 0:
+ p.Result = RECV_ITEM_SUCCESS;
+ sd->roulette.claimPrize = false;
+ sd->roulette.prizeStage = 0;
+ sd->roulette.prizeIdx = 0;
+ sd->roulette.stage = 0;
+ break;
+ case 1:
+ case 4:
+ case 5:
+ p.Result = RECV_ITEM_OVERCOUNT;
+ break;
+ case 2:
+ p.Result = RECV_ITEM_OVERWEIGHT;
+ break;
+ default:
+ case 7:
+ p.Result = RECV_ITEM_FAILED;
+ break;
+ }
+ } else
+ p.Result = RECV_ITEM_FAILED;
+
+ clif->send(&p,sizeof(p), &sd->bl, SELF);
+ return;
+}
+
+bool clif_parse_roulette_db(void) {
+ config_t roulette_conf;
+ config_setting_t *roulette = NULL, *levels = NULL;
+ const char *config_filename = "db/roulette_db.conf"; // FIXME hardcoded name
+ int i, j, item_count_t = 0;
+
+ for( i = 0; i < MAX_ROULETTE_LEVEL; i++ ) {
+ clif->rd.items[i] = 0;
+ }
+
+ if (libconfig->read_file(&roulette_conf, config_filename)) {
+ ShowError("can't read %s\n", config_filename);
+ return false;
+ }
+
+ roulette = libconfig->lookup(&roulette_conf, "roulette");
+
+ if( roulette != NULL && (levels = libconfig->setting_get_elem(roulette, 0)) != NULL ) {
+ for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+ config_setting_t *level;
+ char entry_name[10];
+
+ sprintf(entry_name,"level_%d",i+1);
+
+ if( (level = libconfig->setting_get_member(levels, entry_name)) != NULL ) {
+ int k, item_count = libconfig->setting_length(level);
+
+ for(k = 0; k < item_count; k++) {
+ config_setting_t *entry = libconfig->setting_get_elem(level,k);
+ const char *name = config_setting_name(entry);
+ int qty = libconfig->setting_get_int(entry);
+ struct item_data * data = NULL;
+
+ if( qty < 1 ) {
+ ShowWarning("roulette_db: unsupported qty '%d' for entry named '%s' in category '%s'\n", qty, name, entry_name);
+ continue;
+ }
+
+ if( name[0] == 'I' && name[1] == 'D' && strlen(name) <= 7 ) {
+ if( !( data = itemdb->exists(atoi(name+2))) ) {
+ ShowWarning("roulette_db: unknown item id '%s' in category '%s'\n", name+2, entry_name);
+ continue;
+ }
+ } else {
+ if( !( data = itemdb->search_name(name) ) ) {
+ ShowWarning("roulette_db: unknown item name '%s' in category '%s'\n", name, entry_name);
+ continue;
+ }
+ }
+
+ j = clif->rd.items[i];
+ RECREATE(clif->rd.nameid[i],int,++clif->rd.items[i]);
+ RECREATE(clif->rd.qty[i],int,clif->rd.items[i]);
+
+ clif->rd.nameid[i][j] = data->nameid;
+ clif->rd.qty[i][j] = qty;
+
+ item_count_t++;
+ }
+ }
+ }
+
+ libconfig->destroy(&roulette_conf);
+ }
+
+ for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+ int limit = MAX_ROULETTE_COLUMNS-i;
+ if( clif->rd.items[i] == limit ) continue;
+
+ if( clif->rd.items[i] > limit ) {
+ ShowWarning("roulette_db: level %d has %d items, only %d supported, capping...\n",i+1,clif->rd.items[i],limit);
+ clif->rd.items[i] = limit;
+ continue;
+ }
+ /** this scenario = clif->rd.items[i] < limit **/
+ ShowWarning("roulette_db: level %d has %d items, %d are required. filling with apples\n",i+1,clif->rd.items[i],limit);
+
+ clif->rd.items[i] = limit;
+ RECREATE(clif->rd.nameid[i],int,clif->rd.items[i]);
+ RECREATE(clif->rd.qty[i],int,clif->rd.items[i]);
+
+
+ for(j = 0; j < MAX_ROULETTE_COLUMNS-i; j++) {
+ if( clif->rd.qty[i][j] ) continue;
+
+ clif->rd.nameid[i][j] = ITEMID_APPLE;
+ clif->rd.qty[i][j] = 1;
+ }
+ }
+
+
+ ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", item_count_t, config_filename);
+
+ return true;
+}
+
+/**
+ *
+ **/
+void clif_roulette_generate_ack(struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, short bonusItemID) {
+ struct packet_roulette_generate_ack p;
+
+ p.PacketType = roulettgenerateackType;
+ p.Result = result;
+ p.Step = stage;
+ p.Idx = prizeIdx;
+ p.AdditionItemID = bonusItemID;
+ p.RemainBronze = pc_readglobalreg(sd, script->add_str("TmpRouletteBronze"));
+ p.RemainGold = pc_readglobalreg(sd, script->add_str("TmpRouletteGold"));
+ p.RemainSilver = pc_readglobalreg(sd, script->add_str("TmpRouletteSilver"));
+
+ clif->send(&p,sizeof(p), &sd->bl, SELF);
+}
+
/* */
unsigned short clif_decrypt_cmd( int cmd, struct map_session_data *sd ) {
if( sd ) {
@@ -18570,6 +18894,13 @@ void do_final_clif(void) {
}
aFree(clif->cs.data[i]);
}
+
+ for(i = 0; i < MAX_ROULETTE_LEVEL; i++) {
+ if( clif->rd.nameid[i] )
+ aFree(clif->rd.nameid[i]);
+ if( clif->rd.qty[i] )
+ aFree(clif->rd.qty[i]);
+ }
}
void clif_defaults(void) {
@@ -18884,6 +19215,7 @@ void clif_defaults(void) {
clif->party_xy_remove = clif_party_xy_remove;
clif->party_show_picker = clif_party_show_picker;
clif->partyinvitationstate = clif_partyinvitationstate;
+ clif->PartyLeaderChanged = clif_PartyLeaderChanged;
/* guild-specific */
clif->guild_created = clif_guild_created;
clif->guild_belonginfo = clif_guild_belonginfo;
@@ -19070,6 +19402,9 @@ void clif_defaults(void) {
/* NPC Market */
clif->npc_market_open = clif_npc_market_open;
clif->npc_market_purchase_ack = clif_npc_market_purchase_ack;
+ /* */
+ clif->parse_roulette_db = clif_parse_roulette_db;
+ clif->roulette_generate_ack = clif_roulette_generate_ack;
/*------------------------
*- Parse Incoming Packet
*------------------------*/
@@ -19299,6 +19634,12 @@ void clif_defaults(void) {
clif->pBankCheck = clif_parse_BankCheck;
clif->pBankOpen = clif_parse_BankOpen;
clif->pBankClose = clif_parse_BankClose;
+ /* Roulette System [Yommy/Hercules] */
+ clif->pRouletteOpen = clif_parse_RouletteOpen;
+ clif->pRouletteInfo = clif_parse_RouletteInfo;
+ clif->pRouletteClose = clif_parse_RouletteClose;
+ clif->pRouletteGenerate = clif_parse_RouletteGenerate;
+ clif->pRouletteRecvItem = clif_parse_RouletteRecvItem;
/* */
clif->pNPCShopClosed = clif_parse_NPCShopClosed;
/* NPC Market */
diff --git a/src/map/clif.h b/src/map/clif.h
index e4de51a83..17fda9a74 100644
--- a/src/map/clif.h
+++ b/src/map/clif.h
@@ -18,8 +18,6 @@
**/
struct item;
struct item_data;
-struct storage_data;
-struct guild_storage;
struct unit_data;
struct map_session_data;
struct homun_data;
@@ -50,6 +48,8 @@ struct skill_cd;
#define clif_disp_onlyself(sd,mes,len) clif->disp_message( &(sd)->bl, (mes), (len), SELF )
#define clif_viewequip_fail( sd ) clif_msg( (sd), 0x54d );
#define HCHSYS_NAME_LENGTH 20
+#define MAX_ROULETTE_LEVEL 7 /** client-defined value **/
+#define MAX_ROULETTE_COLUMNS 9 /** client-defined value **/
/**
* Enumerations
@@ -478,6 +478,35 @@ enum e_trade_item_ok {
TIO_INDROCKS = 0x9,
};
+enum RECV_ROULETTE_ITEM_REQ {
+ RECV_ITEM_SUCCESS = 0x0,
+ RECV_ITEM_FAILED = 0x1,
+ RECV_ITEM_OVERCOUNT = 0x2,
+ RECV_ITEM_OVERWEIGHT = 0x3,
+};
+
+enum RECV_ROULETTE_ITEM_ACK {
+ RECV_ITEM_NORMAL = 0x0,
+ RECV_ITEM_LOSING = 0x1,
+};
+
+enum GENERATE_ROULETTE_ACK {
+ GENERATE_ROULETTE_SUCCESS = 0x0,
+ GENERATE_ROULETTE_FAILED = 0x1,
+ GENERATE_ROULETTE_NO_ENOUGH_POINT = 0x2,
+ GENERATE_ROULETTE_LOSING = 0x3,
+};
+
+enum OPEN_ROULETTE_ACK{
+ OPEN_ROULETTE_SUCCESS = 0x0,
+ OPEN_ROULETTE_FAILED = 0x1,
+};
+
+enum CLOSE_ROULETTE_ACK {
+ CLOSE_ROULETTE_SUCCESS = 0x0,
+ CLOSE_ROULETTE_FAILED = 0x1,
+};
+
/**
* Structures
**/
@@ -554,6 +583,12 @@ struct clif_interface {
struct hCSData **data[CASHSHOP_TAB_MAX];
unsigned int item_count[CASHSHOP_TAB_MAX];
} cs;
+ /* roulette data */
+ struct {
+ int *nameid[MAX_ROULETTE_LEVEL];//nameid
+ int *qty[MAX_ROULETTE_LEVEL];//qty of nameid
+ int items[MAX_ROULETTE_LEVEL];//number of items in the list for each
+ } rd;
/* */
unsigned int cryptKey[3];
/* */
@@ -866,6 +901,7 @@ struct clif_interface {
void (*party_xy_remove) (struct map_session_data *sd);
void (*party_show_picker) (struct map_session_data * sd, struct item * item_data);
void (*partyinvitationstate) (struct map_session_data* sd);
+ void (*PartyLeaderChanged) (struct map_session_data *sd, int prev_leader_aid, int new_leader_aid);
/* guild-specific */
void (*guild_created) (struct map_session_data *sd,int flag);
void (*guild_belonginfo) (struct map_session_data *sd, struct guild *g);
@@ -1052,6 +1088,9 @@ struct clif_interface {
/* NPC Market */
void (*npc_market_open) (struct map_session_data *sd, struct npc_data *nd);
void (*npc_market_purchase_ack) (struct map_session_data *sd, struct packet_npc_market_purchase *req, unsigned char response);
+ /* */
+ bool (*parse_roulette_db) (void);
+ void (*roulette_generate_ack) (struct map_session_data *sd, unsigned char result, short stage, short prizeIdx, short bonusItemID);
/*------------------------
*- Parse Incoming Packet
*------------------------*/
@@ -1279,6 +1318,12 @@ struct clif_interface {
void (*pBankCheck) (int fd, struct map_session_data *sd);
void (*pBankOpen) (int fd, struct map_session_data *sd);
void (*pBankClose) (int fd, struct map_session_data *sd);
+ /* Roulette System [Yommy/Hercules] */
+ void (*pRouletteOpen) (int fd, struct map_session_data *sd);
+ void (*pRouletteInfo) (int fd, struct map_session_data *sd);
+ void (*pRouletteClose) (int fd, struct map_session_data *sd);
+ void (*pRouletteGenerate) (int fd, struct map_session_data *sd);
+ void (*pRouletteRecvItem) (int fd, struct map_session_data *sd);
/* */
void (*pNPCShopClosed) (int fd, struct map_session_data *sd);
/* NPC Market (by Ind after an extensive debugging of the packet, only possible thanks to Yommy <3) */
diff --git a/src/map/guild.c b/src/map/guild.c
index ac24edeab..1d9cf03a9 100644
--- a/src/map/guild.c
+++ b/src/map/guild.c
@@ -459,6 +459,8 @@ int guild_recv_info(struct guild *sg) {
struct map_session_data *sd;
bool guild_new = false;
struct hChSysCh *aChSysSave = NULL;
+ short *instance_save = NULL;
+ unsigned short instances_save = 0;
nullpo_ret(sg);
@@ -538,10 +540,16 @@ int guild_recv_info(struct guild *sg) {
before=*g;
if( g->channel )
aChSysSave = g->channel;
+ if( g->instance )
+ instance_save = g->instance;
+ if( g->instances )
+ instances_save = g->instances;
}
memcpy(g,sg,sizeof(struct guild));
g->channel = aChSysSave;
+ g->instance = instance_save;
+ g->instances = instances_save;
if(g->max_member > MAX_GUILD) {
ShowError("guild_recv_info: Received guild with %d members, but MAX_GUILD is only %d. Extra guild-members have been lost!\n", g->max_member, MAX_GUILD);
@@ -928,7 +936,7 @@ void guild_retrieveitembound(int char_id,int aid,int guild_id) {
if(sd){ //Character is online
pc->bound_clear(sd,IBT_GUILD);
} else { //Character is offline, ask char server to do the job
- struct guild_storage *gstor = gstorage->id2storage2(guild_id);
+ struct guild_storage *gstor = idb_get(gstorage->db,guild_id);
if(gstor && gstor->storage_status == 1) { //Someone is in guild storage, close them
struct s_mapiterator* iter = mapit_getallusers();
for( sd = (TBL_PC*)mapit->first(iter); mapit->exists(iter); sd = (TBL_PC*)mapit->next(iter) ) {
diff --git a/src/map/intif.c b/src/map/intif.c
index fc38e82e3..c20905bcf 100644
--- a/src/map/intif.c
+++ b/src/map/intif.c
@@ -1098,7 +1098,7 @@ void intif_parse_LoadGuildStorage(int fd)
return;
}
}
- gstor=gstorage->id2storage(guild_id);
+ gstor=gstorage->ensure(guild_id);
if(!gstor) {
ShowWarning("intif_parse_LoadGuildStorage: error guild_id %d not exist\n",guild_id);
return;
@@ -2165,7 +2165,7 @@ void intif_parse_MessageToFD(int fd) {
*------------------------------------------*/
void intif_itembound_req(int char_id,int aid,int guild_id) {
#ifdef GP_BOUND_ITEMS
- struct guild_storage *gstor = gstorage->id2storage2(guild_id);
+ struct guild_storage *gstor = idb_get(gstorage->db,guild_id);
WFIFOHEAD(inter_fd,12);
WFIFOW(inter_fd,0) = 0x3056;
WFIFOL(inter_fd,2) = char_id;
@@ -2183,7 +2183,7 @@ void intif_parse_Itembound_ack(int fd) {
struct guild_storage *gstor;
int guild_id = RFIFOW(fd,6);
- gstor = gstorage->id2storage2(guild_id);
+ gstor = idb_get(gstorage->db,guild_id);
if(gstor)
gstor->lock = 0; //Unlock now that operation is completed
#endif
diff --git a/src/map/itemdb.c b/src/map/itemdb.c
index 0d3146191..41e0a5348 100644
--- a/src/map/itemdb.c
+++ b/src/map/itemdb.c
@@ -1713,7 +1713,7 @@ int itemdb_readdb_libconfig_sub(config_setting_t *it, int n, const char *source)
if( libconfig->setting_lookup_int(it, "Type", &i32) )
id.type = i32;
else if( !inherit )
- id.type = IT_UNKNOWN;
+ id.type = IT_ETC;
if( libconfig->setting_lookup_int(it, "Buy", &i32) )
id.value_buy = i32;
@@ -2248,6 +2248,10 @@ void do_init_itemdb(bool minimal) {
return;
clif->cashshop_load();
+
+ /** it failed? we disable it **/
+ if( !clif->parse_roulette_db() )
+ battle_config.feature_roulette = 0;
}
void itemdb_defaults(void) {
itemdb = &itemdb_s;
diff --git a/src/map/itemdb.h b/src/map/itemdb.h
index 198d7a542..8d8cfd7c2 100644
--- a/src/map/itemdb.h
+++ b/src/map/itemdb.h
@@ -41,7 +41,9 @@ enum item_itemid {
ITEMID_YELLOW_POTION = 503,
ITEMID_WHITE_POTION = 504,
ITEMID_BLUE_POTION = 505,
+ ITEMID_APPLE = 512,
ITEMID_HOLY_WATER = 523,
+ ITEMID_PUMPKIN = 535,
ITEMID_RED_SLIM_POTION = 545,
ITEMID_YELLOW_SLIM_POTION = 546,
ITEMID_WHITE_SLIM_POTION = 547,
diff --git a/src/map/log.h b/src/map/log.h
index 6ab142f87..40dbf6761 100644
--- a/src/map/log.h
+++ b/src/map/log.h
@@ -98,7 +98,7 @@ struct log_interface {
char db_ip[32];
int db_port;
char db_id[32];
- char db_pw[32];
+ char db_pw[100];
char db_name[32];
Sql* mysql_handle;
/* */
diff --git a/src/map/map.c b/src/map/map.c
index 14af392b7..5c0f5d65e 100644
--- a/src/map/map.c
+++ b/src/map/map.c
@@ -395,9 +395,11 @@ int map_moveblock(struct block_list *bl, int x1, int y1, int64 tick) {
/*==========================================
* Counts specified number of objects on given cell.
+ * flag:
+ * 0x1 - only count standing units
* TODO: merge with bl_getall_area
*------------------------------------------*/
-int map_count_oncell(int16 m, int16 x, int16 y, int type) {
+int map_count_oncell(int16 m, int16 x, int16 y, int type, int flag) {
int bx,by;
struct block_list *bl;
int count = 0;
@@ -410,13 +412,27 @@ int map_count_oncell(int16 m, int16 x, int16 y, int type) {
if (type&~BL_MOB)
for( bl = map->list[m].block[bx+by*map->list[m].bxs] ; bl != NULL ; bl = bl->next )
- if(bl->x == x && bl->y == y && bl->type&type)
- count++;
+ if(bl->x == x && bl->y == y && bl->type&type) {
+ if(flag&1) {
+ struct unit_data *ud = unit->bl2ud(bl);
+ if(!ud || ud->walktimer == INVALID_TIMER)
+ count++;
+ } else {
+ count++;
+ }
+ }
if (type&BL_MOB)
for( bl = map->list[m].block_mob[bx+by*map->list[m].bxs] ; bl != NULL ; bl = bl->next )
- if(bl->x == x && bl->y == y)
- count++;
+ if(bl->x == x && bl->y == y) {
+ if(flag&1) {
+ struct unit_data *ud = unit->bl2ud(bl);
+ if(!ud || ud->walktimer == INVALID_TIMER)
+ count++;
+ } else {
+ count++;
+ }
+ }
return count;
}
@@ -1379,7 +1395,7 @@ int map_searchrandfreecell(int16 m,int16 *x,int16 *y,int stack) {
if(map->getcell(m,j+*x,i+*y,CELL_CHKNOPASS) && !map->getcell(m,j+*x,i+*y,CELL_CHKICEWALL))
continue;
//Avoid item stacking to prevent against exploits. [Skotlex]
- if(stack && map->count_oncell(m,j+*x,i+*y, BL_ITEM) > stack)
+ if(stack && map->count_oncell(m,j+*x,i+*y, BL_ITEM, 0) > stack)
continue;
free_cells[free_cell][0] = j+*x;
free_cells[free_cell++][1] = i+*y;
@@ -1473,6 +1489,85 @@ int map_search_freecell(struct block_list *src, int16 m, int16 *x,int16 *y, int1
}
/*==========================================
+ * Locates the closest, walkable cell with no blocks of a certain type on it
+ * Returns true on success and sets x and y to cell found.
+ * Otherwise returns false and x and y are not changed.
+ * type: Types of block to count
+ * flag:
+ * 0x1 - only count standing units
+ *------------------------------------------*/
+bool map_closest_freecell(int16 m, int16 *x, int16 *y, int type, int flag)
+{
+ uint8 dir = 6;
+ int16 tx = *x;
+ int16 ty = *y;
+ int costrange = 10;
+
+ if(!map->count_oncell(m, tx, ty, type, flag))
+ return true; //Current cell is free
+
+ //Algorithm only works up to costrange of 34
+ while(costrange <= 34) {
+ short dx = dirx[dir];
+ short dy = diry[dir];
+
+ //Linear search
+ if(dir%2 == 0 && costrange%MOVE_COST == 0) {
+ tx = *x+dx*(costrange/MOVE_COST);
+ ty = *y+dy*(costrange/MOVE_COST);
+ if(!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m,tx,ty,CELL_CHKPASS)) {
+ *x = tx;
+ *y = ty;
+ return true;
+ }
+ }
+ //Full diagonal search
+ else if(dir%2 == 1 && costrange%MOVE_DIAGONAL_COST == 0) {
+ tx = *x+dx*(costrange/MOVE_DIAGONAL_COST);
+ ty = *y+dy*(costrange/MOVE_DIAGONAL_COST);
+ if(!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m,tx,ty,CELL_CHKPASS)) {
+ *x = tx;
+ *y = ty;
+ return true;
+ }
+ }
+ //One cell diagonal, rest linear (TODO: Find a better algorithm for this)
+ else if(dir%2 == 1 && costrange%MOVE_COST == 4) {
+ tx = *x+dx*((dir%4==3)?(costrange/MOVE_COST):1);
+ ty = *y+dy*((dir%4==1)?(costrange/MOVE_COST):1);
+ if(!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m,tx,ty,CELL_CHKPASS)) {
+ *x = tx;
+ *y = ty;
+ return true;
+ }
+ tx = *x+dx*((dir%4==1)?(costrange/MOVE_COST):1);
+ ty = *y+dy*((dir%4==3)?(costrange/MOVE_COST):1);
+ if(!map->count_oncell(m, tx, ty, type, flag) && map->getcell(m,tx,ty,CELL_CHKPASS)) {
+ *x = tx;
+ *y = ty;
+ return true;
+ }
+ }
+
+ //Get next direction
+ if (dir == 5) {
+ //Diagonal search complete, repeat with higher cost range
+ if(costrange == 14) costrange += 6;
+ else if(costrange == 28 || costrange >= 38) costrange += 2;
+ else costrange += 4;
+ dir = 6;
+ } else if (dir == 4) {
+ //Linear search complete, switch to diagonal directions
+ dir = 7;
+ } else {
+ dir = (dir+2)%8;
+ }
+ }
+
+ return false;
+}
+
+/*==========================================
* Add an item to location (m,x,y)
* Parameters
* @item_data item attributes
@@ -2420,28 +2515,27 @@ uint8 map_calc_dir(struct block_list* src, int16 x, int16 y)
}
else if( dx >= 0 && dy >=0 )
{ // upper-right
- if( dx*2 <= dy ) dir = 0; // up
- else if( dx > dy*2 ) dir = 6; // right
- else dir = 7; // up-right
+ if( dx*2 < dy || dx == 0 ) dir = 0; // up
+ else if( dx > dy*2+1 || dy == 0 ) dir = 6; // right
+ else dir = 7; // up-right
}
else if( dx >= 0 && dy <= 0 )
{ // lower-right
- if( dx*2 <= -dy ) dir = 4; // down
- else if( dx > -dy*2 ) dir = 6; // right
- else dir = 5; // down-right
+ if( dx*2 < -dy || dx == 0 ) dir = 4; // down
+ else if( dx > -dy*2+1 || dy == 0 ) dir = 6; // right
+ else dir = 5; // down-right
}
else if( dx <= 0 && dy <= 0 )
{ // lower-left
- if( dx*2 >= dy ) dir = 4; // down
- else if( dx < dy*2 ) dir = 2; // left
- else dir = 3; // down-left
+ if( dx*2 > dy || dx == 0 ) dir = 4; // down
+ else if( dx < dy*2-1 || dy == 0 ) dir = 2; // left
+ else dir = 3; // down-left
}
else
{ // upper-left
- if( -dx*2 <= dy ) dir = 0; // up
- else if( -dx > dy*2 ) dir = 2; // left
- else dir = 1; // up-left
-
+ if( -dx*2 < dy || dx == 0 ) dir = 0; // up
+ else if( -dx > dy*2+1 || dy == 0) dir = 2; // left
+ else dir = 1; // up-left
}
return dir;
}
@@ -2587,25 +2681,27 @@ int map_getcellp(struct map_data* m,int16 x,int16 y,cell_chk cellchk) {
return (cell.nochat);
case CELL_CHKICEWALL:
return (cell.icewall);
+ case CELL_CHKNOICEWALL:
+ return (cell.noicewall);
// special checks
case CELL_CHKPASS:
#ifdef CELL_NOSTACK
- if (cell.cell_bl >= battle_config.cell_stack_limit) return 0;
+ if (cell.cell_bl >= battle_config.custom_cell_stack_limit) return 0;
#endif
case CELL_CHKREACH:
return (cell.walkable);
case CELL_CHKNOPASS:
#ifdef CELL_NOSTACK
- if (cell.cell_bl >= battle_config.cell_stack_limit) return 1;
+ if (cell.cell_bl >= battle_config.custom_cell_stack_limit) return 1;
#endif
case CELL_CHKNOREACH:
return (!cell.walkable);
case CELL_CHKSTACK:
#ifdef CELL_NOSTACK
- return (cell.cell_bl >= battle_config.cell_stack_limit);
+ return (cell.cell_bl >= battle_config.custom_cell_stack_limit);
#else
return 0;
#endif
@@ -2646,6 +2742,8 @@ void map_setcell(int16 m, int16 x, int16 y, cell_t cell, bool flag) {
case CELL_NOVENDING: map->list[m].cell[j].novending = flag; break;
case CELL_NOCHAT: map->list[m].cell[j].nochat = flag; break;
case CELL_ICEWALL: map->list[m].cell[j].icewall = flag; break;
+ case CELL_NOICEWALL: map->list[m].cell[j].noicewall = flag; break;
+
default:
ShowWarning("map_setcell: invalid cell type '%d'\n", (int)cell);
break;
@@ -5611,10 +5709,7 @@ int do_init(int argc, char *argv[])
map_load_defaults();
HPM_map_do_init();
- HPM->DataCheck = HPM_map_DataCheck;
- HPM->load_sub = HPM_map_plugin_load_sub;
HPM->symbol_defaults_sub = map_hp_symbols;
- HPM->grabHPDataSub = HPM_map_grabHPData;
for( i = 1; i < argc; i++ ) {
const char* arg = argv[i];
if( strcmp(arg, "--load-plugin") == 0 ) {
@@ -6000,6 +6095,7 @@ void map_defaults(void) {
// search and creation
map->get_new_object_id = map_get_new_object_id;
map->search_freecell = map_search_freecell;
+ map->closest_freecell = map_closest_freecell;
//
map->quit = map_quit;
// npc
diff --git a/src/map/map.h b/src/map/map.h
index dba565cc0..1766220af 100644
--- a/src/map/map.h
+++ b/src/map/map.h
@@ -444,6 +444,7 @@ typedef enum {
CELL_NOVENDING,
CELL_NOCHAT,
CELL_ICEWALL,
+ CELL_NOICEWALL,
} cell_t;
@@ -467,6 +468,7 @@ typedef enum {
CELL_CHKNOVENDING,
CELL_CHKNOCHAT,
CELL_CHKICEWALL,
+ CELL_CHKNOICEWALL,
} cell_chk;
@@ -484,7 +486,8 @@ struct mapcell {
landprotector : 1,
novending : 1,
nochat : 1,
- icewall : 1;
+ icewall : 1,
+ noicewall : 1;
#ifdef CELL_NOSTACK
int cell_bl; //Holds amount of bls in this cell.
@@ -658,13 +661,7 @@ struct map_data {
int jexp; // map experience multiplicator
int bexp; // map experience multiplicator
int nocommand; //Blocks @/# commands for non-gms. [Skotlex]
- /**
- * Ice wall reference counter for bugreport:3574
- * - since there are a thousand mobs out there in a lot of maps checking on,
- * - every targeting for icewall on attack path would just be a waste, so,
- * - this counter allows icewall checking be only run when there is a actual ice wall on the map
- **/
- int icewall_num;
+
// Instance Variables
int instance_id;
int instance_src_map;
@@ -853,7 +850,7 @@ struct map_interface {
int server_port;
char server_ip[32];
char server_id[32];
- char server_pw[32];
+ char server_pw[100];
char server_db[32];
Sql* mysql_handle;
@@ -920,11 +917,12 @@ struct map_interface {
int (*delblock) (struct block_list* bl);
int (*moveblock) (struct block_list *bl, int x1, int y1, int64 tick);
//blocklist nb in one cell
- int (*count_oncell) (int16 m,int16 x,int16 y,int type);
+ int (*count_oncell) (int16 m,int16 x,int16 y,int type,int flag);
struct skill_unit * (*find_skill_unit_oncell) (struct block_list* target,int16 x,int16 y,uint16 skill_id,struct skill_unit* out_unit, int flag);
// search and creation
int (*get_new_object_id) (void);
int (*search_freecell) (struct block_list *src, int16 m, int16 *x, int16 *y, int16 rx, int16 ry, int flag);
+ bool (*closest_freecell) (int16 m, int16 *x, int16 *y, int type, int flag);
//
int (*quit) (struct map_session_data *sd);
// npc
diff --git a/src/map/mob.c b/src/map/mob.c
index ffab804a6..2605b414f 100644
--- a/src/map/mob.c
+++ b/src/map/mob.c
@@ -52,7 +52,9 @@ struct mob_interface mob_s;
#define IDLE_SKILL_INTERVAL 10 //Active idle skills should be triggered every 1 second (1000/MIN_MOBTHINKTIME)
-#define MOB_LAZYSKILLPERC 0 // Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute)
+// Probability for mobs far from players from doing their IDLE skill. (rate of 1000 minute)
+// in Aegis, this is 100% for mobs that have been activated by players and none otherwise.
+#define MOB_LAZYSKILLPERC(md) (md->state.spotted?1000:0)
// Move probability for mobs away from players (rate of 1000 minute)
// in Aegis, this is 100% for mobs that have been activated by players and none otherwise.
#define MOB_LAZYMOVEPERC(md) ((md)->state.spotted?1000:0)
@@ -946,7 +948,7 @@ int mob_spawn (struct mob_data *md)
md->state.aggressive = md->status.mode&MD_ANGRY?1:0;
md->state.skillstate = MSS_IDLE;
- md->next_walktime = tick+rnd()%5000+1000;
+ md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME;
md->last_linktime = tick;
md->dmgtick = tick - 5000;
md->last_pcneartime = 0;
@@ -1074,15 +1076,6 @@ int mob_ai_sub_hard_activesearch(struct block_list *bl,va_list ap)
((*target) == NULL || !check_distance_bl(&md->bl, *target, dist)) &&
battle->check_range(&md->bl,bl,md->db->range2)
) { //Pick closest target?
-
- if( map->list[bl->m].icewall_num &&
- !path->search_long(NULL,bl->m,md->bl.x,md->bl.y,bl->x,bl->y,CELL_CHKICEWALL) ) {
-
- if( !check_distance_bl(&md->bl, bl, status_get_range(&md->bl) ) )
- return 0;
-
- }
-
(*target) = bl;
md->target_id=bl->id;
md->min_chase= dist + md->db->range3;
@@ -1286,22 +1279,23 @@ int mob_unlocktarget(struct mob_data *md, int64 tick) {
md->state.skillstate = MSS_IDLE;
case MSS_IDLE:
// Idle skill.
- if ((md->target_id || !(++md->ud.walk_count%IDLE_SKILL_INTERVAL)) &&
- mob->skill_use(md, tick, -1))
+ if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL) && mob->skill_use(md, tick, -1))
break;
//Random walk.
if (!md->master_id &&
DIFF_TICK(md->next_walktime, tick) <= 0 &&
!mob->randomwalk(md,tick))
//Delay next random walk when this one failed.
- md->next_walktime=tick+rnd()%3000;
+ md->next_walktime = tick+rnd()%1000;
break;
default:
mob_stop_attack(md);
- if (battle_config.mob_ai&0x8)
- mob_stop_walking(md,1); //Immediately stop chasing.
+ mob_stop_walking(md,1); //Stop chasing.
md->state.skillstate = MSS_IDLE;
- md->next_walktime=tick+rnd()%3000+3000;
+ if(battle_config.mob_ai&0x8) //Walk instantly after dropping target
+ md->next_walktime = tick+rnd()%1000;
+ else
+ md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME;
break;
}
if (md->target_id) {
@@ -1309,6 +1303,10 @@ int mob_unlocktarget(struct mob_data *md, int64 tick) {
md->ud.target_to = 0;
unit->set_target(&md->ud, 0);
}
+ if(map->count_oncell(md->bl.m, md->bl.x, md->bl.y, BL_CHAR|BL_NPC, 1) > battle_config.official_cell_stack_limit) {
+ unit->walktoxy(&md->bl, md->bl.x, md->bl.y, 8);
+ }
+
return 0;
}
/*==========================================
@@ -1328,6 +1326,7 @@ int mob_randomwalk(struct mob_data *md, int64 tick) {
d =12-md->move_fail_count;
if(d<5) d=5;
+ if(d>7) d=7;
for(i=0;i<retrycount;i++){ // Search of a movable place
int r=rnd();
x=r%(d*2+1)-d;
@@ -1335,7 +1334,7 @@ int mob_randomwalk(struct mob_data *md, int64 tick) {
x+=md->bl.x;
y+=md->bl.y;
- if((map->getcell(md->bl.m,x,y,CELL_CHKPASS)) && unit->walktoxy(&md->bl,x,y,1)){
+ if(((x != md->bl.x) || (y != md->bl.y)) && map->getcell(md->bl.m,x,y,CELL_CHKPASS) && unit->walktoxy(&md->bl,x,y,8)){
break;
}
}
@@ -1358,7 +1357,7 @@ int mob_randomwalk(struct mob_data *md, int64 tick) {
}
md->state.skillstate=MSS_WALK;
md->move_fail_count=0;
- md->next_walktime = tick+rnd()%3000+3000+c;
+ md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME+c;
return 1;
}
@@ -1404,9 +1403,6 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
if (md->ud.skilltimer != INVALID_TIMER)
return false;
- if(md->ud.walktimer != INVALID_TIMER && md->ud.walkpath.path_pos <= 3)
- return false;
-
// Abnormalities
if(( md->sc.opt1 > 0 && md->sc.opt1 != OPT1_STONEWAIT && md->sc.opt1 != OPT1_BURNING && md->sc.opt1 != OPT1_CRYSTALIZE )
|| md->sc.data[SC_DEEP_SLEEP] || md->sc.data[SC_BLADESTOP] || md->sc.data[SC__MANHOLE] || md->sc.data[SC_CURSEDCIRCLE_TARGET]) {
@@ -1434,10 +1430,13 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
|| ((TBL_PC*)tbl)->invincible_timer != INVALID_TIMER)
)
) {
- //Unlock current target.
+ //No valid target
if (mob->warpchase(md, tbl))
return true; //Chasing this target.
- mob->unlocktarget(md, tick-(battle_config.mob_ai&0x8?3000:0)); //Immediately do random walk.
+ if(md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh)
+ && (tbl || md->ud.walkpath.path_pos == 0))
+ return true; //Walk at least "mob_chase_refresh" cells before dropping the target unless target is non-existent
+ mob_unlocktarget(md, tick); //Unlock target
tbl = NULL;
}
}
@@ -1448,12 +1447,14 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
if( md->attacked_id == md->target_id )
{ //Rude attacked check.
if( !battle->check_range(&md->bl, tbl, md->status.rhw.range)
- && ( //Can't attack back and can't reach back.
+ && ( //Can't attack back and can't reach back.
(!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || (md->sc.data[SC_SPIDERWEB] && md->sc.data[SC_SPIDERWEB]->val1)
- || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP]
- || md->sc.data[SC__MANHOLE])) // Not yet confirmed if boss will teleport once it can't reach target.
- || !mob->can_reach(md, tbl, md->min_chase, MSS_RUSH)
+ || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP]
+ || md->sc.data[SC__MANHOLE] // Not yet confirmed if boss will teleport once it can't reach target.
+ || md->walktoxy_fail_count > 0)
)
+ || !mob->can_reach(md, tbl, md->min_chase, MSS_RUSH)
+ )
&& md->state.attacked_count++ >= RUDE_ATTACKED_COUNT
&& !mob->skill_use(md, tick, MSC_RUDEATTACKED) // If can't rude Attack
&& can_move && unit->escape(&md->bl, tbl, rnd()%10 +1)) // Attempt escape
@@ -1471,10 +1472,12 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
|| (battle_config.mob_ai&0x2 && !status->check_skilluse(&md->bl, abl, 0, 0)) // Cannot normal attack back to Attacker
|| (!battle->check_range(&md->bl, abl, md->status.rhw.range) // Not on Melee Range and ...
&& ( // Reach check
- (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || (md->sc.data[SC_SPIDERWEB] && md->sc.data[SC_SPIDERWEB]->val1)
- || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP]
- || md->sc.data[SC__MANHOLE])) // Not yet confirmed if boss will teleport once it can't reach target.
- || !mob->can_reach(md, abl, dist+md->db->range3, MSS_RUSH)
+ (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0 && (battle_config.mob_ai&0x2 || (md->sc.data[SC_SPIDERWEB] && md->sc.data[SC_SPIDERWEB]->val1)
+ || md->sc.data[SC_WUGBITE] || md->sc.data[SC_VACUUM_EXTREME] || md->sc.data[SC_THORNS_TRAP]
+ || md->sc.data[SC__MANHOLE] // Not yet confirmed if boss will teleport once it can't reach target.
+ || md->walktoxy_fail_count > 0)
+ )
+ || !mob->can_reach(md, abl, dist+md->db->range3, MSS_RUSH)
)
)
) {
@@ -1547,7 +1550,7 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
}
}
- //This handles triggering idle walk/skill.
+ //This handles triggering idle/walk skill.
mob->unlocktarget(md, tick);
return true;
}
@@ -1604,48 +1607,56 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
mob->unlocktarget (md,tick);
return true;
}
+
//Attempt to attack.
//At this point we know the target is attackable, we just gotta check if the range matches.
- if (md->ud.target == tbl->id && md->ud.attacktimer != INVALID_TIMER) //Already locked.
+ if (battle->check_range(&md->bl, tbl, md->status.rhw.range) && !(md->sc.option&OPTION_HIDE))
+ { //Target within range and able to use normal attack, engage
+ if (md->ud.target != tbl->id || md->ud.attacktimer == INVALID_TIMER)
+ { //Only attack if no more attack delay left
+ if(tbl->type == BL_PC)
+ mob->log_damage(md, tbl, 0); //Log interaction (counts as 'attacker' for the exp bonus)
+
+ if( !(mode&MD_RANDOMTARGET) )
+ unit->attack(&md->bl,tbl->id,1);
+ else { // Attack once and find a new random target
+ int search_size = (view_range < md->status.rhw.range) ? view_range : md->status.rhw.range;
+ unit->attack(&md->bl,tbl->id, 0);
+ if ((tbl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size))) {
+ md->target_id = tbl->id;
+ md->min_chase = md->db->range3;
+ }
+ }
+ }
return true;
+ }
- if (battle->check_range (&md->bl, tbl, md->status.rhw.range))
- { //Target within range, engage
+ //Monsters in berserk state, unable to use normal attacks, will always attempt a skill
+ if(md->ud.walktimer == INVALID_TIMER && (md->state.skillstate == MSS_BERSERK || md->state.skillstate == MSS_ANGRY)) {
+ if (DIFF_TICK(md->ud.canmove_tick, tick) <= MIN_MOBTHINKTIME && DIFF_TICK(md->ud.canact_tick, tick) < -MIN_MOBTHINKTIME*IDLE_SKILL_INTERVAL)
+ { //Only use skill if able to walk on next tick and not used a skill the last second
+ mob->skill_use(md, tick, -1);
+ }
+ }
- if(tbl->type == BL_PC)
- mob->log_damage(md, tbl, 0); //Log interaction (counts as 'attacker' for the exp bonus)
+ //Target still in attack range, no need to chase the target
+ if(battle->check_range(&md->bl, tbl, md->status.rhw.range))
+ return true;
- if(!(mode&MD_RANDOMTARGET))
- unit->attack(&md->bl,tbl->id,1);
- else { // Attack once and find new random target
- int search_size = (view_range < md->status.rhw.range) ? view_range : md->status.rhw.range;
- unit->attack(&md->bl,tbl->id,0);
- tbl = battle->get_enemy(&md->bl, DEFAULT_ENEMY_TYPE(md), search_size);
- // If no target was found, keep atacking the old one
- if( tbl ) {
- md->target_id = tbl->id;
- md->min_chase = md->db->range3;
- }
- }
+ //Only update target cell / drop target after having moved at least "mob_chase_refresh" cells
+ if(md->ud.walktimer != INVALID_TIMER && (!can_move || md->ud.walkpath.path_pos <= battle_config.mob_chase_refresh))
return true;
- }
//Out of range...
- if (!(mode&MD_CANMOVE))
- { //Can't chase. Attempt an idle skill before unlocking.
- md->state.skillstate = MSS_IDLE;
- if (!mob->skill_use(md, tick, -1))
+ if (!(mode&MD_CANMOVE) || (!can_move && DIFF_TICK(tick, md->ud.canmove_tick) > 0))
+ { //Can't chase. Immobile and trapped mobs should unlock target and use an idle skill.
+ if (md->ud.attacktimer == INVALID_TIMER)
+ { //Only unlock target if no more attack delay left
+ //This handles triggering idle/walk skill.
mob->unlocktarget(md,tick);
+ }
return true;
- }
-
- if (!can_move)
- { //Stuck. Attempt an idle skill
- md->state.skillstate = MSS_IDLE;
- if (!(++md->ud.walk_count%IDLE_SKILL_INTERVAL))
- mob->skill_use(md, tick, -1);
- return true;
- }
+ }
if (md->ud.walktimer != INVALID_TIMER && md->ud.target == tbl->id &&
(
@@ -1655,6 +1666,7 @@ bool mob_ai_sub_hard(struct mob_data *md, int64 tick) {
return true;
//Follow up if possible.
+ //Hint: Chase skills are handled in the walktobl routine
if(!mob->can_reach(md, tbl, md->min_chase, MSS_RUSH) ||
!unit->walktobl(&md->bl, tbl, md->status.rhw.range, 2))
mob->unlocktarget(md,tick);
@@ -1735,20 +1747,17 @@ int mob_ai_sub_lazy(struct mob_data *md, va_list args) {
}
if( DIFF_TICK(md->next_walktime,tick) < 0 && (status_get_mode(&md->bl)&MD_CANMOVE) && unit->can_move(&md->bl) ) {
- if( map->list[md->bl.m].users > 0 )
- {
- if( rnd()%1000 < MOB_LAZYMOVEPERC(md) )
- mob->randomwalk(md, tick);
- else
- if( rnd()%1000 < MOB_LAZYSKILLPERC ) //Chance to do a mob's idle skill.
- mob->skill_use(md, tick, -1);
- }
- else
- {
- if( rnd()%1000 < MOB_LAZYMOVEPERC(md) )
- mob->randomwalk(md, tick);
- }
+ if( rnd()%1000 < MOB_LAZYMOVEPERC(md) )
+ mob_randomwalk(md, tick);
+ }
+ else if( md->ud.walktimer == INVALID_TIMER )
+ {
+ //Because it is not unset when the mob finishes walking.
+ md->state.skillstate = MSS_IDLE;
+ if( rnd()%1000 < MOB_LAZYSKILLPERC(md) ) //Chance to do a mob's idle skill.
+ mob->skill_use(md, tick, -1);
}
+
return 0;
}
@@ -2641,7 +2650,7 @@ void mob_revive(struct mob_data *md, unsigned int hp)
int64 tick = timer->gettick();
md->state.skillstate = MSS_IDLE;
md->last_thinktime = tick;
- md->next_walktime = tick+rnd()%50+5000;
+ md->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME;
md->last_linktime = tick;
md->last_pcneartime = 0;
memset(md->dmglog, 0, sizeof(md->dmglog)); // Reset the damage done on the rebirthed monster, otherwise will grant full exp + damage done. [Valaris]
@@ -3711,6 +3720,10 @@ bool mob_parse_dbrow(char** str) {
if (mstatus->dex < 1) mstatus->dex = 1;
if (mstatus->luk < 1) mstatus->luk = 1;
+ //Tests showed that chase range is effectively 2 cells larger than expected [Playtester]
+ if (db->range3 > 0)
+ db->range3 += 2;
+
db->range2 = atoi(str[20]);
db->range3 = atoi(str[21]);
if (battle_config.view_range_rate != 100) {
diff --git a/src/map/mob.h b/src/map/mob.h
index 728f3d81c..f3937c0d1 100644
--- a/src/map/mob.h
+++ b/src/map/mob.h
@@ -16,7 +16,7 @@
// Change this to increase the table size in your mob_db to accommodate a larger mob database.
// Be sure to note that IDs 4001 to 4048 are reserved for advanced/baby/expanded classes.
// Notice that the last 1000 entries are used for player clones, so always set this to desired value +1000
-#define MAX_MOB_DB 4000
+#define MAX_MOB_DB 5000
//The number of drops all mobs have and the max drop-slot that the steal skill will attempt to steal from.
#define MAX_MOB_DROP 10
@@ -27,6 +27,8 @@
#define MIN_MOBTHINKTIME 100
//Min time before mobs do a check to call nearby friends for help (or for slaves to support their master)
#define MIN_MOBLINKTIME 1000
+//Min time between random walks
+#define MIN_RANDOMWALKTIME 4000
//Distance that slaves should keep from their master.
#define MOB_SLAVEDISTANCE 2
@@ -169,6 +171,7 @@ struct mob_data {
short move_fail_count;
short lootitem_count;
short min_chase;
+ unsigned char walktoxy_fail_count; //Pathfinding succeeds but the actual walking failed (e.g. Icewall lock)
int deletetimer;
int master_id,master_dist;
diff --git a/src/map/npc.c b/src/map/npc.c
index a33db6b99..7378ac470 100644
--- a/src/map/npc.c
+++ b/src/map/npc.c
@@ -1818,11 +1818,11 @@ int npc_buylist(struct map_session_data* sd, int n, unsigned short* item_list) {
for( i = 0; i < n; ++i ) {
int nameid = item_list[i*2+1];
int amount = item_list[i*2+0];
- struct item item_tmp;
- if (itemdb_type(nameid) == IT_PETEGG)
+ if (itemdb_type(nameid) == IT_PETEGG) {
pet->create_egg(sd, nameid);
- else {
+ } else {
+ struct item item_tmp;
memset(&item_tmp,0,sizeof(item_tmp));
item_tmp.nameid = nameid;
item_tmp.identify = 1;
@@ -1933,7 +1933,6 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st
for( i = 0; i < list_size; ++i ) {
int nameid = p->list[i].ITID;
int amount = p->list[i].qty;
- struct item item_tmp;
j = npc_market_qty[i];
@@ -1944,9 +1943,10 @@ int npc_market_buylist(struct map_session_data* sd, unsigned short list_size, st
npc->market_tosql(nd,j);
- if (itemdb_type(nameid) == IT_PETEGG)
+ if (itemdb_type(nameid) == IT_PETEGG) {
pet->create_egg(sd, nameid);
- else {
+ } else {
+ struct item item_tmp;
memset(&item_tmp,0,sizeof(item_tmp));
item_tmp.nameid = nameid;
item_tmp.identify = 1;
@@ -3324,8 +3324,8 @@ int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const c
struct event_data* ev = (struct event_data*)strdb_get(npc->ev_db, eventname);
struct npc_data *nd;
struct script_state *st;
- int i = 0, j = 0, k = 0;
- char *temp;
+ int i = 0, nargs = 0;
+ size_t len;
nullpo_ret(sd);
@@ -3353,27 +3353,29 @@ int npc_do_atcmd_event(struct map_session_data* sd, const char* command, const c
st = script->alloc_state(ev->nd->u.scr.script, ev->pos, sd->bl.id, ev->nd->bl.id);
script->setd_sub(st, NULL, ".@atcmd_command$", 0, (void *)command, NULL);
- // split atcmd parameters based on spaces
- temp = (char*)aMalloc(strlen(message) + 1);
-
- for( i = 0; i < ( strlen( message ) + 1 ) && k < 127; i ++ ) {
- if( message[i] == ' ' || message[i] == '\0' ) {
- if( message[ ( i - 1 ) ] == ' ' ) {
- continue; // To prevent "@atcmd [space][space]" and .@atcmd_numparameters return 1 without any parameter.
- }
- temp[k] = '\0';
- k = 0;
- if( temp[0] != '\0' ) {
- script->setd_sub( st, NULL, ".@atcmd_parameters$", j++, (void *)temp, NULL );
+ len = strlen(message);
+ if (len) {
+ char *temp, *p;
+ p = temp = aStrdup(message);
+ // Sanity check - Skip leading spaces (shouldn't happen)
+ while (i <= len && temp[i] == ' ') {
+ p++;
+ i++;
+ }
+ // split atcmd parameters based on spaces
+ while (i <= len) {
+ if (temp[i] != ' ' && temp[i] != '\0') {
+ i++;
+ continue;
}
- } else {
- temp[k] = message[i];
- k++;
+ temp[i] = '\0';
+ script->setd_sub(st, NULL, ".@atcmd_parameters$", nargs++, (void *)p, NULL);
+ i++;
+ p = temp + i;
}
+ aFree(temp);
}
-
- script->setd_sub(st, NULL, ".@atcmd_numparameters", 0, (void *)h64BPTRSIZE(j), NULL);
- aFree(temp);
+ script->setd_sub(st, NULL, ".@atcmd_numparameters", 0, (void *)h64BPTRSIZE(nargs), NULL);
script->run_main(st);
return 0;
@@ -4179,9 +4181,6 @@ int npc_parsesrcfile(const char* filepath, bool runOnInit) {
if( strcmp(w1,"function") == 0 ) {
p = npc->parse_function(w1, w2, w3, w4, p, buffer, filepath, &success);
} else {
-#ifdef ENABLE_CASE_CHECK
- if( strcasecmp(w1, "function") == 0 ) DeprecationWarning("npc_parsesrcfile", w1, "function", filepath, strline(buffer, p-buffer)); // TODO
-#endif // ENABLE_CASE_CHECK
p = npc->parse_script(w1,w2,w3,w4, p, buffer, filepath,runOnInit?NPO_ONINIT:NPO_NONE, &success);
}
}
@@ -4206,22 +4205,6 @@ int npc_parsesrcfile(const char* filepath, bool runOnInit) {
}
else
{
-#ifdef ENABLE_CASE_CHECK
- if( strcasecmp(w2, "warp") == 0 ) { DeprecationWarning("npc_parsesrcfile", w2, "warp", filepath, strline(buffer, p-buffer)); } // TODO
- else if( strcasecmp(w2,"shop") == 0 ) { DeprecationWarning("npc_parsesrcfile", w2, "shop", filepath, strline(buffer, p-buffer)); } // TODO
- else if( strcasecmp(w2,"cashshop") == 0 ) { DeprecationWarning("npc_parsesrcfile", w2, "cashshop", filepath, strline(buffer, p-buffer)); } // TODO
- else if( strcasecmp(w2, "script") == 0 ) { DeprecationWarning("npc_parsesrcfile", w2, "script", filepath, strline(buffer, p-buffer)); } // TODO
- else if( strcasecmp(w2,"trader") == 0 ) DeprecationWarning("npc_parsesrcfile", w2, "trader", filepath, strline(buffer, p-buffer)) // TODO
- else if( strncasecmp(w2, "duplicate", 9) == 0 ) {
- char temp[10];
- safestrncpy(temp, w2, 10);
- DeprecationWarning("npc_parsesrcfile", temp, "duplicate", filepath, strline(buffer, p-buffer)); // TODO
- }
- else if( strcasecmp(w2,"monster") == 0 ) { DeprecationWarning("npc_parsesrcfile", w2, "monster", filepath, strline(buffer, p-buffer)); } // TODO:
- else if( strcasecmp(w2,"boss_monster") == 0 ) { DeprecationWarning("npc_parsesrcfile", w2, "boss_monster", filepath, strline(buffer, p-buffer)); } // TODO
- else if( strcasecmp(w2, "mapflag") == 0 ) { DeprecationWarning("npc_parsesrcfile", w2, "mapflag", filepath, strline(buffer, p-buffer)); } // TODO
- else
-#endif // ENABLE_CASE_CHECK
ShowError("npc_parsesrcfile: Unable to parse, probably a missing or extra TAB in file '%s', line '%d'. Skipping line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,p-buffer), w1, w2, w3, w4);
p = strchr(p,'\n');// skip and continue
success = EXIT_FAILURE;
@@ -4293,7 +4276,7 @@ void npc_read_event_script(void)
script_event[i].event_count++;
#ifdef ENABLE_CASE_CHECK
} else if( p && strcasecmp(name, p) == 0 ) {
- DeprecationWarning2("npc_read_event_script", p, name, config[i].event_name); // TODO
+ DeprecationCaseWarning("npc_read_event_script", p, name, config[i].event_name); // TODO
#endif // ENABLE_CASE_CHECK
}
}
diff --git a/src/map/packets.h b/src/map/packets.h
index 810f341d4..3240a97a1 100644
--- a/src/map/packets.h
+++ b/src/map/packets.h
@@ -2767,6 +2767,121 @@ packet(0x020d,-1);
packet(0x09DF,7);
#endif
+// 2014-10-22bRagexe - YomRawr
+#if PACKETVER >= 20141022
+ packet(0x0369,7,clif->pActionRequest,2,6);
+ packet(0x083C,10,clif->pUseSkillToId,2,4,6);
+ packet(0x0437,5,clif->pWalkToXY,2);
+ packet(0x035F,6,clif->pTickSend,2);
+ packet(0x08AD,5,clif->pChangeDir,2,4);
+ packet(0x094E,6,clif->pTakeItem,2);
+ packet(0x087D,6,clif->pDropItem,2,4);
+ packet(0x0878,8,clif->pMoveToKafra,2,4);
+ packet(0x08AA,8,clif->pMoveFromKafra,2,4);
+ packet(0x023B,10,clif->pUseSkillToPos,2,4,6,8);
+ packet(0x0366,90,clif->pUseSkillToPosMoreInfo,2,4,6,8,10);
+ packet(0x096A,6,clif->pGetCharNameRequest,2);
+ packet(0x0368,6,clif->pSolveCharName,2);
+ packet(0x0835,12,clif->pSearchStoreInfoListItemClick,2,6,10);
+ packet(0x0940,2,clif->pSearchStoreInfoNextPage,0);
+ packet(0x0819,-1,clif->pSearchStoreInfo,2,4,5,9,13,14,15);
+ packet(0x0811,-1,clif->pReqTradeBuyingStore,2,4,8,12);
+ packet(0x0360,6,clif->pReqClickBuyingStore,2);
+ packet(0x0817,2,clif->pReqCloseBuyingStore,0);
+ packet(0x0815,-1,clif->pReqOpenBuyingStore,2,4,8,9,89);
+ packet(0x0955,18,clif->pPartyBookingRegisterReq,2,4);
+ // packet(0x092B,8); // CZ_JOIN_BATTLE_FIELD
+ packet(0x0281,-1,clif->pItemListWindowSelected,2,4,8);
+ packet(0x093B,19,clif->pWantToConnection,2,6,10,14,18);
+ packet(0x0896,26,clif->pPartyInvite2,2);
+ // packet(0x08AB,4); // CZ_GANGSI_RANK
+ packet(0x091A,26,clif->pFriendsListAdd,2);
+ packet(0x0899,5,clif->pHomMenu,2,4);
+ packet(0x0438,36,clif->pStoragePassword,0);
+#endif
+
+// 2014-10-16aRagexe - YomRawr
+#if PACKETVER >= 20141016
+ packet(0x0369,7,clif->pActionRequest,2,6);
+ packet(0x083C,10,clif->pUseSkillToId,2,4,6);
+ packet(0x0437,5,clif->pWalkToXY,2);
+ packet(0x035F,6,clif->pTickSend,2);
+ packet(0x0967,5,clif->pChangeDir,2,4);
+ packet(0x07E4,6,clif->pTakeItem,2);
+ packet(0x0362,6,clif->pDropItem,2,4);
+ packet(0x07EC,8,clif->pMoveToKafra,2,4);
+ packet(0x022D,8,clif->pMoveFromKafra,2,4);
+ packet(0x0438,10,clif->pUseSkillToPos,2,4,6,8);
+ packet(0x0366,90,clif->pUseSkillToPosMoreInfo,2,4,6,8,10);
+ packet(0x096A,6,clif->pGetCharNameRequest,2);
+ packet(0x0368,6,clif->pSolveCharName,2);
+ packet(0x0838,12,clif->pSearchStoreInfoListItemClick,2,6,10);
+ packet(0x0835,2,clif->pSearchStoreInfoNextPage,0);
+ packet(0x0819,-1,clif->pSearchStoreInfo,2,4,5,9,13,14,15);
+ packet(0x0811,-1,clif->pReqTradeBuyingStore,2,4,8,12);
+ packet(0x0360,6,clif->pReqClickBuyingStore,2);
+ packet(0x0817,2,clif->pReqCloseBuyingStore,0);
+ packet(0x0815,-1,clif->pReqOpenBuyingStore,2,4,8,9,89);
+ packet(0x0365,18,clif->pPartyBookingRegisterReq,2,4);
+ // packet(0x0363,8); // CZ_JOIN_BATTLE_FIELD
+ packet(0x0281,-1,clif->pItemListWindowSelected,2,4,8);
+ packet(0x086E,19,clif->pWantToConnection,2,6,10,14,18);
+ packet(0x0802,26,clif->pPartyInvite2,2);
+ // packet(0x0922,4); // CZ_GANGSI_RANK
+ packet(0x094B,26,clif->pFriendsListAdd,2);
+ packet(0x0364,5,clif->pHomMenu,2,4);
+ packet(0x0936,36,clif->pStoragePassword,0);
+ packet(0x09DF,7);
+ packet(0x0a00,269);
+#endif
+
+// 2014-10-22bRagexe - YomRawr
+#if PACKETVER >= 20141022
+ packet(0x0369,7,clif->pActionRequest,2,6);
+ packet(0x083C,10,clif->pUseSkillToId,2,4,6);
+ packet(0x0437,5,clif->pWalkToXY,2);
+ packet(0x035F,6,clif->pTickSend,2);
+ packet(0x08AD,5,clif->pChangeDir,2,4);
+ packet(0x094E,6,clif->pTakeItem,2);
+ packet(0x087D,6,clif->pDropItem,2,4);
+ packet(0x0878,8,clif->pMoveToKafra,2,4);
+ packet(0x08AA,8,clif->pMoveFromKafra,2,4);
+ packet(0x023B,10,clif->pUseSkillToPos,2,4,6,8);
+ packet(0x0366,90,clif->pUseSkillToPosMoreInfo,2,4,6,8,10);
+ packet(0x096A,6,clif->pGetCharNameRequest,2);
+ packet(0x0368,6,clif->pSolveCharName,2);
+ packet(0x0835,12,clif->pSearchStoreInfoListItemClick,2,6,10);
+ packet(0x0940,2,clif->pSearchStoreInfoNextPage,0);
+ packet(0x0819,-1,clif->pSearchStoreInfo,2,4,5,9,13,14,15);
+ packet(0x0811,-1,clif->pReqTradeBuyingStore,2,4,8,12);
+ packet(0x0360,6,clif->pReqClickBuyingStore,2);
+ packet(0x0817,2,clif->pReqCloseBuyingStore,0);
+ packet(0x0815,-1,clif->pReqOpenBuyingStore,2,4,8,9,89);
+ packet(0x0955,18,clif->pPartyBookingRegisterReq,2,4);
+ // packet(0x092B,8); // CZ_JOIN_BATTLE_FIELD
+ packet(0x0281,-1,clif->pItemListWindowSelected,2,4,8);
+ packet(0x093B,19,clif->pWantToConnection,2,6,10,14,18);
+ packet(0x0896,26,clif->pPartyInvite2,2);
+ // packet(0x08AB,4); // CZ_GANGSI_RANK
+ packet(0x091A,26,clif->pFriendsListAdd,2);
+ packet(0x0899,5,clif->pHomMenu,2,4);
+ packet(0x0438,36,clif->pStoragePassword,0);
+#endif
+
+/* Roulette System [Yommy/Hercules] */
+#if PACKETVER >= 20141016
+ packet(0x0A19,2,clif->pRouletteOpen,0); // HEADER_CZ_REQ_OPEN_ROULETTE
+ packet(0x0A1A,23); // HEADER_ZC_ACK_OPEN_ROULETTE
+ packet(0x0A1B,2,clif->pRouletteInfo,0); // HEADER_CZ_REQ_ROULETTE_INFO
+ packet(0x0A1C,-1); // HEADER_ZC_ACK_ROULEITTE_INFO
+ packet(0x0A1D,2,clif->pRouletteClose,0); // HEADER_CZ_REQ_CLOSE_ROULETTE
+ packet(0x0A1E,3); // HEADER_ZC_ACK_CLOSE_ROULETTE
+ packet(0x0A1F,2,clif->pRouletteGenerate,0); // HEADER_CZ_REQ_GENERATE_ROULETTE
+ packet(0x0A20,21); // HEADER_ZC_ACK_GENERATE_ROULETTE
+ packet(0x0A21,3,clif->pRouletteRecvItem,2); // HEADER_CZ_RECV_ROULETTE_ITEM
+ packet(0x0A22,5); // HEADER_ZC_RECV_ROULETTE_ITEM
+#endif
+
/* PacketKeys: http://hercules.ws/board/topic/1105-hercules-wpe-free-june-14th-patch/ */
#if PACKETVER >= 20110817
packetKeys(0x053D5CED,0x3DED6DED,0x6DED6DED); /* Thanks to Shakto */
@@ -2998,6 +3113,18 @@ packet(0x020d,-1);
packetKeys(0x04810281,0x42814281,0x42814281); /* Themon */
#endif
+#if PACKETVER >= 20141022
+ packetKeys(0x290551EA,0x2B952C75,0x2D67669B); /* YomRawr */
+#endif
+
+#if PACKETVER >= 20141016
+ packetKeys(0x2DFF467C,0x444B37EE,0x2C1B634F); /* YomRawr */
+#endif
+
+#if PACKETVER >= 20141022
+ packetKeys(0x290551EA,0x2B952C75,0x2D67669B); /* YomRawr */
+#endif
+
#if defined(OBFUSCATIONKEY1) && defined(OBFUSCATIONKEY2) && defined(OBFUSCATIONKEY3)
packetKeys(OBFUSCATIONKEY1,OBFUSCATIONKEY2,OBFUSCATIONKEY3);
#endif
diff --git a/src/map/packets_struct.h b/src/map/packets_struct.h
index b32baf53a..1c6deab96 100644
--- a/src/map/packets_struct.h
+++ b/src/map/packets_struct.h
@@ -77,8 +77,10 @@ enum packet_headers {
#endif
#if PACKETVER < 20080102
authokType = 0x73,
-#else
+#elif PACKETVER < 20141022
authokType = 0x2eb,
+#else
+ authokType = 0xa18,
#endif
script_clearType = 0x8d6,
package_item_announceType = 0x7fd,
@@ -209,6 +211,10 @@ enum packet_headers {
#else
wisendType = 0x98,
#endif
+ partyleaderchangedType = 0x7fc,
+ rouletteinfoackType = 0xa1c,
+ roulettgenerateackType = 0xA20,
+ roulettercvitemackType = 0xA22,
};
#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
@@ -297,6 +303,9 @@ struct packet_authok {
#if PACKETVER >= 20080102
short font;
#endif
+#if PACKETVER >= 20141022
+ unsigned char sex;
+#endif
} __attribute__((packed));
struct packet_monster_hp {
@@ -761,6 +770,58 @@ struct packet_banking_withdraw_ack {
int Balance;
} __attribute__((packed));
+/* Roulette System [Yommy/Hercules] */
+struct packet_roulette_open_ack {
+ short PacketType;
+ char Result;
+ int Serial;
+ char Step;
+ char Idx;
+ short AdditionItemID;
+ int GoldPoint;
+ int SilverPoint;
+ int BronzePoint;
+} __attribute__((packed));
+
+struct packet_roulette_info_ack {
+ short PacketType;
+ short PacketLength;
+ unsigned int RouletteSerial;
+ struct {
+ unsigned short Row;
+ unsigned short Position;
+ unsigned short ItemId;
+ unsigned short Count;
+ } ItemInfo[42];
+} __attribute__((packed));
+
+struct packet_roulette_close_ack {
+ short PacketType;
+ unsigned char Result;
+} __attribute__((packed));
+
+struct packet_roulette_generate_ack {
+ short PacketType;
+ unsigned char Result;
+ unsigned short Step;
+ unsigned short Idx;
+ unsigned short AdditionItemID;
+ int RemainGold;
+ int RemainSilver;
+ int RemainBronze;
+} __attribute__((packed));
+
+struct packet_roulette_itemrecv_req {
+ short PacketType;
+ unsigned char Condition;
+} __attribute__((packed));
+
+struct packet_roulette_itemrecv_ack {
+ short PacketType;
+ unsigned char Result;
+ unsigned short AdditionItemID;
+} __attribute__((packed));
+
struct packet_itemlist_normal {
short PacketType;
short PacketLength;
@@ -959,6 +1020,13 @@ struct packet_wis_end {
} __attribute__((packed));
+struct packet_party_leader_changed {
+ short PacketType;
+ unsigned int prev_leader_aid;
+ unsigned int new_leader_aid;
+} __attribute__((packed));
+
+
#if !defined(sun) && (!defined(__NETBSD__) || __NetBSD_Version__ >= 600000000) // NetBSD 5 and Solaris don't like pragma pack but accept the packed attribute
#pragma pack(pop)
#endif // not NetBSD < 6 / Solaris
diff --git a/src/map/party.c b/src/map/party.c
index 7cf340edb..f4a5c870a 100644
--- a/src/map/party.c
+++ b/src/map/party.c
@@ -686,12 +686,10 @@ bool party_changeleader(struct map_session_data *sd, struct map_session_data *ts
//Change leadership.
p->party.member[mi].leader = 0;
- if (p->data[mi].sd->fd)
- clif->message(p->data[mi].sd->fd, msg_txt(284));
-
p->party.member[tmi].leader = 1;
- if (p->data[tmi].sd->fd)
- clif->message(p->data[tmi].sd->fd, msg_txt(285));
+
+ /** update members **/
+ clif->PartyLeaderChanged(p->data[mi].sd, p->data[mi].sd->status.account_id, p->data[tmi].sd->status.account_id);
//Update info.
intif->party_leaderchange(p->party.party_id,p->party.member[tmi].account_id,p->party.member[tmi].char_id);
diff --git a/src/map/path.c b/src/map/path.c
index 5a9ddf9c7..9aeb108fa 100644
--- a/src/map/path.c
+++ b/src/map/path.c
@@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include "map.h"
#include "../common/cbasetypes.h"
@@ -128,9 +129,6 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
spd->x[0] = x0;
spd->y[0] = y0;
- if (md->getcellp(md,x1,y1,cell))
- return false;
-
if (dx > abs(dy)) {
weight = dx;
spd->ry = 1;
@@ -141,8 +139,6 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
while (x0 != x1 || y0 != y1)
{
- if (md->getcellp(md,x0,y0,cell))
- return false;
wx += dx;
wy += dy;
if (wx >= weight) {
@@ -162,6 +158,8 @@ bool path_search_long(struct shootpath_data *spd,int16 m,int16 x0,int16 y0,int16
spd->y[spd->len] = y0;
spd->len++;
}
+ if (md->getcellp(md,x0,y0,cell))
+ return false;
}
return true;
@@ -176,7 +174,7 @@ static void heap_push_node(struct node_heap *heap, struct path_node *node)
{
#ifndef __clang_analyzer__ // TODO: Figure out why clang's static analyzer doesn't like this
BHEAP_ENSURE(*heap, 1, 256);
- BHEAP_PUSH(*heap, node, NODE_MINTOPCMP, swap_ptr);
+ BHEAP_PUSH2(*heap, node, NODE_MINTOPCMP, swap_ptr);
#endif // __clang_analyzer__
}
@@ -189,8 +187,7 @@ static int heap_update_node(struct node_heap *heap, struct path_node *node)
ShowError("heap_update_node: node not found\n");
return 1;
}
- BHEAP_POPINDEX(*heap, i, NODE_MINTOPCMP, swap_ptr);
- BHEAP_PUSH(*heap, node, NODE_MINTOPCMP, swap_ptr);
+ BHEAP_UPDATE(*heap, i, NODE_MINTOPCMP, swap_ptr);
return 0;
}
@@ -251,12 +248,8 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
return false;
md = &map->list[m];
-#ifdef CELL_NOSTACK
//Do not check starting cell as that would get you stuck.
- if (x0 < 0 || x0 >= md->xs || y0 < 0 || y0 >= md->ys)
-#else
if (x0 < 0 || x0 >= md->xs || y0 < 0 || y0 >= md->ys /*|| md->getcellp(md,x0,y0,cell)*/)
-#endif
return false;
// Check destination cell
@@ -304,7 +297,7 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
// A* (A-star) pathfinding
// We always use A* for finding walkpaths because it is what game client uses.
// Easy pathfinding cuts corners of non-walkable cells, but client always walks around it.
-
+
BHEAP_STRUCT_VAR(node_heap, open_set); // 'Open' set
// FIXME: This array is too small to ensure all paths shorter than MAX_WALKPATH
@@ -327,8 +320,8 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
tp[i].flag = SET_OPEN;
heap_push_node(&open_set, &tp[i]); // Put start node to 'open' set
- for(;;)
- {
+
+ for(;;) {
int e = 0; // error flag
// Saves allowed directions for the current cell. Diagonal directions
@@ -347,7 +340,7 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
}
current = BHEAP_PEEK(open_set); // Look for the lowest f_cost node in the 'open' set
- BHEAP_POP(open_set, NODE_MINTOPCMP, swap_ptr); // Remove it from 'open' set
+ BHEAP_POP2(open_set, NODE_MINTOPCMP, swap_ptr); // Remove it from 'open' set
x = current->x;
y = current->y;
@@ -367,24 +360,22 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
#define chk_dir(d) ((allowed_dirs & (d)) == (d))
// Process neighbors of current node
- // TODO: Processing order affects chosen path if there is more than one path with same cost.
- // In few cases path found by server will be different than path found by game client.
- if (chk_dir(DIR_SOUTH))
- e += add_path(&open_set, tp, x, y-1, g_cost + MOVE_COST, current, heuristic(x, y-1, x1, y1)); // (x, y-1) 4
- if (chk_dir(DIR_SOUTH|DIR_WEST) && !md->getcellp(md, x-1, y-1, cell))
- e += add_path(&open_set, tp, x-1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y-1, x1, y1)); // (x-1, y-1) 3
- if (chk_dir(DIR_WEST))
- e += add_path(&open_set, tp, x-1, y, g_cost + MOVE_COST, current, heuristic(x-1, y, x1, y1)); // (x-1, y) 2
- if (chk_dir(DIR_NORTH|DIR_WEST) && !md->getcellp(md, x-1, y+1, cell))
- e += add_path(&open_set, tp, x-1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y+1, x1, y1)); // (x-1, y+1) 1
- if (chk_dir(DIR_NORTH))
- e += add_path(&open_set, tp, x, y+1, g_cost + MOVE_COST, current, heuristic(x, y+1, x1, y1)); // (x, y+1) 0
- if (chk_dir(DIR_NORTH|DIR_EAST) && !md->getcellp(md, x+1, y+1, cell))
- e += add_path(&open_set, tp, x+1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y+1, x1, y1)); // (x+1, y+1) 7
- if (chk_dir(DIR_EAST))
- e += add_path(&open_set, tp, x+1, y, g_cost + MOVE_COST, current, heuristic(x+1, y, x1, y1)); // (x+1, y) 6
if (chk_dir(DIR_SOUTH|DIR_EAST) && !md->getcellp(md, x+1, y-1, cell))
e += add_path(&open_set, tp, x+1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y-1, x1, y1)); // (x+1, y-1) 5
+ if (chk_dir(DIR_EAST))
+ e += add_path(&open_set, tp, x+1, y, g_cost + MOVE_COST, current, heuristic(x+1, y, x1, y1)); // (x+1, y) 6
+ if (chk_dir(DIR_NORTH|DIR_EAST) && !md->getcellp(md, x+1, y+1, cell))
+ e += add_path(&open_set, tp, x+1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x+1, y+1, x1, y1)); // (x+1, y+1) 7
+ if (chk_dir(DIR_NORTH))
+ e += add_path(&open_set, tp, x, y+1, g_cost + MOVE_COST, current, heuristic(x, y+1, x1, y1)); // (x, y+1) 0
+ if (chk_dir(DIR_NORTH|DIR_WEST) && !md->getcellp(md, x-1, y+1, cell))
+ e += add_path(&open_set, tp, x-1, y+1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y+1, x1, y1)); // (x-1, y+1) 1
+ if (chk_dir(DIR_WEST))
+ e += add_path(&open_set, tp, x-1, y, g_cost + MOVE_COST, current, heuristic(x-1, y, x1, y1)); // (x-1, y) 2
+ if (chk_dir(DIR_SOUTH|DIR_WEST) && !md->getcellp(md, x-1, y-1, cell))
+ e += add_path(&open_set, tp, x-1, y-1, g_cost + MOVE_DIAGONAL_COST, current, heuristic(x-1, y-1, x1, y1)); // (x-1, y-1) 3
+ if (chk_dir(DIR_SOUTH))
+ e += add_path(&open_set, tp, x, y-1, g_cost + MOVE_COST, current, heuristic(x, y-1, x1, y1)); // (x, y-1) 4
#undef chk_dir
if (e) {
BHEAP_CLEAR(open_set);
@@ -413,7 +404,7 @@ bool path_search(struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x
//Distance functions, taken from http://www.flipcode.com/articles/article_fastdistance.shtml
-int check_distance(int dx, int dy, int distance)
+bool check_distance(int dx, int dy, int distance)
{
#ifdef CIRCULAR_AREA
//In this case, we just do a square comparison. Add 1 tile grace for diagonal range checks.
@@ -453,6 +444,42 @@ unsigned int distance(int dx, int dy)
return (dx<dy?dy:dx);
#endif
}
+
+/**
+ * The client uses a circular distance instead of the square one. The circular distance
+ * is only used by units sending their attack commands via the client (not monsters).
+ * @param dx: Horizontal distance
+ * @param dy: Vertical distance
+ * @param distance: Distance to check against
+ * @return Within distance(1); Not within distance(0);
+ */
+bool check_distance_client(int dx, int dy, int distance)
+{
+ if(distance < 0) distance = 0;
+
+ return (path->distance_client(dx,dy) <= distance);
+}
+
+/**
+ * The client uses a circular distance instead of the square one. The circular distance
+ * is only used by units sending their attack commands via the client (not monsters).
+ * @param dx: Horizontal distance
+ * @param dy: Vertical distance
+ * @return Circular distance
+ */
+int distance_client(int dx, int dy)
+{
+ double temp_dist = sqrt((double)(dx*dx + dy*dy));
+
+ //Bonus factor used by client
+ //This affects even horizontal/vertical lines so they are one cell longer than expected
+ temp_dist -= 0.0625;
+
+ if(temp_dist < 0) temp_dist = 0;
+
+ return ((int)temp_dist);
+}
+
void path_defaults(void) {
path = &path_s;
@@ -461,4 +488,6 @@ void path_defaults(void) {
path->search = path_search;
path->check_distance = check_distance;
path->distance = distance;
+ path->check_distance_client = check_distance_client;
+ path->distance_client = distance_client;
}
diff --git a/src/map/path.h b/src/map/path.h
index 8d02e6558..4b71f2189 100644
--- a/src/map/path.h
+++ b/src/map/path.h
@@ -32,6 +32,14 @@ struct shootpath_data {
#define distance_blxy(bl, x1, y1) (path->distance((bl)->x - (x1), (bl)->y - (y1)))
#define distance_xy(x0, y0, x1, y1) (path->distance((x0) - (x1), (y0) - (y1)))
+#define check_distance_client_bl(bl1, bl2, distance) (path->check_distance_client((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y, distance))
+#define check_distance_client_blxy(bl, x1, y1, distance) (path->check_distance_client((bl)->x-(x1), (bl)->y-(y1), distance))
+#define check_distance_client_xy(x0, y0, x1, y1, distance) (path->check_distance_client((x0)-(x1), (y0)-(y1), distance))
+
+#define distance_client_bl(bl1, bl2) (path->distance_client((bl1)->x - (bl2)->x, (bl1)->y - (bl2)->y))
+#define distance_client_blxy(bl, x1, y1) (path->distance_client((bl)->x-(x1), (bl)->y-(y1)))
+#define distance_client_xy(x0, y0, x1, y1) (path->distance_client((x0)-(x1), (y0)-(y1)))
+
struct path_interface {
// calculates destination cell for knockback
int (*blownpos) (int16 m, int16 x0, int16 y0, int16 dx, int16 dy, int count);
@@ -39,8 +47,10 @@ struct path_interface {
bool (*search) (struct walkpath_data *wpd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, int flag, cell_chk cell);
// tries to find a shootable path
bool (*search_long) (struct shootpath_data *spd, int16 m, int16 x0, int16 y0, int16 x1, int16 y1, cell_chk cell);
- int (*check_distance) (int dx, int dy, int distance);
+ bool (*check_distance) (int dx, int dy, int distance);
unsigned int (*distance) (int dx, int dy);
+ bool (*check_distance_client) (int dx, int dy, int distance);
+ int (*distance_client) (int dx, int dy);
};
struct path_interface *path;
diff --git a/src/map/pc.c b/src/map/pc.c
index 5fc4e3ddf..1dc032f7c 100644
--- a/src/map/pc.c
+++ b/src/map/pc.c
@@ -1320,7 +1320,7 @@ int pc_reg_received(struct map_session_data *sd)
clif->pLoadEndAck(sd->fd, sd);
}
- if( sd->sc.option & OPTION_INVISIBLE ) {
+ if (pc_isinvisible(sd)) {
sd->vd.class_ = INVISIBLE_CLASS;
clif->message(sd->fd, msg_txt(11)); // Invisible: On
// decrement the number of pvp players on the map
@@ -1729,7 +1729,7 @@ int pc_disguise(struct map_session_data *sd, int class_) {
if (class_ >= 0 && sd->disguise == class_)
return 0;
- if(sd->sc.option&OPTION_INVISIBLE) { //Character is invisible. Stealth class-change. [Skotlex]
+ if (pc_isinvisible(sd)) { //Character is invisible. Stealth class-change. [Skotlex]
sd->disguise = class_; //viewdata is set on uncloaking.
return 2;
}
@@ -3102,11 +3102,11 @@ int pc_bonus2(struct map_session_data *sd,int type,int type2,int val)
}
if(sd->skillfixcastrate[i].id == type2)
- sd->skillfixcastrate[i].val += val;
+ sd->skillfixcastrate[i].val -= val;
else {
sd->skillfixcastrate[i].id = type2;
- sd->skillfixcastrate[i].val = val;
+ sd->skillfixcastrate[i].val -= val;
}
break;
@@ -4474,7 +4474,7 @@ int pc_useitem(struct map_session_data *sd,int n) {
} else {// not yet used item (all slots are initially empty)
sd->item_delay[i].nameid = nameid;
}
- if( !(nameid == ITEMID_REINS_OF_MOUNT && sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR)) )
+ if (!(nameid == ITEMID_REINS_OF_MOUNT && pc_hasmount(sd)))
sd->item_delay[i].tick = tick + sd->inventory_data[n]->delay;
} else {// should not happen
ShowError("pc_useitem: Exceeded item delay array capacity! (nameid=%d, char_id=%d)\n", nameid, sd->status.char_id);
@@ -4731,7 +4731,7 @@ void pc_bound_clear(struct map_session_data *sd, enum e_item_bound_type type) {
ShowError("Helllo! You reached pc_bound_clear for IBT_ACCOUNT, unfortunately no scenario was expected for this!\n");
break;
case IBT_GUILD: {
- struct guild_storage *gstor = gstorage->id2storage(sd->status.guild_id);
+ struct guild_storage *gstor = idb_get(gstorage->db,sd->status.guild_id);
for( i = 0; i < MAX_INVENTORY; i++ ){
if(sd->status.inventory[i].bound == type) {
@@ -7639,6 +7639,10 @@ int pc_itemheal(struct map_session_data *sd,int itemid, int hp,int sp)
// Recovery Potion
if( sd->sc.data[SC_HEALPLUS] )
hp += (int)(hp * sd->sc.data[SC_HEALPLUS]->val1/100.);
+
+ // 2014 Halloween Event : Pumpkin Bonus
+ if ( sd->sc.data[SC_MTF_PUMPKIN] && itemid == ITEMID_PUMPKIN )
+ hp += (int)(hp * sd->sc.data[SC_MTF_PUMPKIN]->val1/100);
}
if(sp) {
bonus = 100 + (sd->battle_status.int_<<1)
@@ -8149,48 +8153,111 @@ int pc_setcart(struct map_session_data *sd,int type) {
return 0;
}
-/*==========================================
- * Give player a falcon
- *------------------------------------------*/
-int pc_setfalcon(TBL_PC* sd, int flag)
+/* FIXME: These setter methods are inconsistent in their class/skill checks.
+ They should be changed so that they all either do or skip the checks.*/
+
+/**
+ * Gives/removes a falcon.
+ *
+ * The target player needs the required skills in order to obtain a falcon.
+ *
+ * @param sd Target player.
+ * @param flag New state.
+ **/
+void pc_setfalcon(TBL_PC* sd, bool flag)
{
- if( flag ){
- if( pc->checkskill(sd,HT_FALCON)>0 ) // add falcon if he have the skill
+ if (flag) {
+ if (pc->checkskill(sd,HT_FALCON) > 0) // add falcon if he have the skill
pc->setoption(sd,sd->sc.option|OPTION_FALCON);
- } else if( pc_isfalcon(sd) ){
+ } else if (pc_isfalcon(sd)) {
pc->setoption(sd,sd->sc.option&~OPTION_FALCON); // remove falcon
}
-
- return 0;
}
-/*==========================================
- * Set player riding
- *------------------------------------------*/
-int pc_setriding(TBL_PC* sd, int flag)
+/**
+ * Mounts/dismounts a Peco or Gryphon.
+ *
+ * The target player needs the required skills in order to mount a peco.
+ *
+ * @param sd Target player.
+ * @param flag New state.
+ **/
+void pc_setridingpeco(TBL_PC* sd, bool flag)
{
- if( flag ){
- if( pc->checkskill(sd,KN_RIDING) > 0 ) // add peco
+ if (flag) {
+ if (pc->checkskill(sd, KN_RIDING))
pc->setoption(sd, sd->sc.option|OPTION_RIDING);
- } else if( pc_isriding(sd) ){
- pc->setoption(sd, sd->sc.option&~OPTION_RIDING);
+ } else if (pc_isridingpeco(sd)) {
+ pc->setoption(sd, sd->sc.option&~OPTION_RIDING);
}
-
- return 0;
}
/**
- * Gives player a mado
- * @param flag 1 Set mado
+ * Gives/removes a Mado Gear.
+ *
+ * The target player needs to be the correct class in order to obtain a mado gear.
+ *
+ * @param sd Target player.
+ * @param flag New state.
**/
-void pc_setmadogear( struct map_session_data *sd, int flag ) {
- if( flag ) {
- if( (sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC )
+void pc_setmadogear(struct map_session_data *sd, bool flag)
+{
+ if (flag) {
+ if ((sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC)
pc->setoption(sd, sd->sc.option|OPTION_MADOGEAR);
- } else if( pc_ismadogear(sd) )
+ } else if (pc_ismadogear(sd)) {
pc->setoption(sd, sd->sc.option&~OPTION_MADOGEAR);
+ }
+}
- return;
+/**
+ * Mounts/dismounts a dragon.
+ *
+ * The target player needs the required skills in order to mount a dragon.
+ *
+ * @param sd Target player.
+ * @param type New state. This must be a valid OPTION_DRAGON* or 0.
+ **/
+void pc_setridingdragon(TBL_PC* sd, unsigned int type)
+{
+ if (type&OPTION_DRAGON) {
+ // Ensure only one dragon is set at a time.
+ if (type&OPTION_DRAGON1)
+ type = OPTION_DRAGON1;
+ else if (type&OPTION_DRAGON2)
+ type = OPTION_DRAGON2;
+ else if (type&OPTION_DRAGON3)
+ type = OPTION_DRAGON3;
+ else if (type&OPTION_DRAGON4)
+ type = OPTION_DRAGON4;
+ else if (type&OPTION_DRAGON5)
+ type = OPTION_DRAGON5;
+ else
+ type = OPTION_DRAGON1;
+
+ if (pc->checkskill(sd, RK_DRAGONTRAINING))
+ pc->setoption(sd, (sd->sc.option&~OPTION_DRAGON)|type);
+ } else if (pc_isridingdragon(sd)) {
+ pc->setoption(sd,sd->sc.option&~OPTION_DRAGON); // remove dragon
+ }
+}
+
+/**
+ * Mounts/dismounts a wug.
+ *
+ * The target player needs the required skills in order to mount a wug.
+ *
+ * @param sd Target player.
+ * @param flag New state.
+ **/
+void pc_setridingwug(TBL_PC* sd, bool flag)
+{
+ if (flag) {
+ if (pc->checkskill(sd, RA_WUGRIDER) > 0)
+ pc->setoption(sd,sd->sc.option|OPTION_WUGRIDER);
+ } else if (pc_isridingwug(sd)) {
+ pc->setoption(sd,sd->sc.option&~OPTION_WUGRIDER); // remove wug
+ }
}
/**
@@ -9116,8 +9183,15 @@ int pc_unequipitem(struct map_session_data *sd,int n,int flag) {
//OnUnEquip script [Skotlex]
if (sd->inventory_data[n]) {
- if (sd->inventory_data[n]->unequip_script)
- script->run(sd->inventory_data[n]->unequip_script,0,sd->bl.id,npc->fake_nd->bl.id);
+ if (sd->inventory_data[n]->unequip_script) {
+ if ( battle_config.unequip_restricted_equipment & 1 ) {
+ ARR_FIND(0, map->list[sd->bl.m].zone->disabled_items_count, i, map->list[sd->bl.m].zone->disabled_items[i] == sd->status.inventory[n].nameid);
+ if ( i == map->list[sd->bl.m].zone->disabled_items_count )
+ script->run(sd->inventory_data[n]->unequip_script,0,sd->bl.id,npc->fake_nd->bl.id);
+ }
+ else
+ script->run(sd->inventory_data[n]->unequip_script,0,sd->bl.id,npc->fake_nd->bl.id);
+ }
if(itemdb_isspecial(sd->status.inventory[n].card[0]))
; //No cards
else {
@@ -9127,8 +9201,16 @@ int pc_unequipitem(struct map_session_data *sd,int n,int flag) {
continue;
if ( ( data = itemdb->exists(sd->status.inventory[n].card[i]) ) != NULL ) {
- if( data->unequip_script )
- script->run(data->unequip_script,0,sd->bl.id,npc->fake_nd->bl.id);
+ if ( data->unequip_script ) {
+ if ( battle_config.unequip_restricted_equipment & 2 ) {
+ int j;
+ ARR_FIND(0, map->list[sd->bl.m].zone->disabled_items_count, j, map->list[sd->bl.m].zone->disabled_items[j] == sd->status.inventory[n].card[i]);
+ if ( j == map->list[sd->bl.m].zone->disabled_items_count )
+ script->run(data->unequip_script,0,sd->bl.id,npc->fake_nd->bl.id);
+ }
+ else
+ script->run(data->unequip_script,0,sd->bl.id,npc->fake_nd->bl.id);
+ }
}
}
@@ -9203,7 +9285,7 @@ int pc_checkitem(struct map_session_data *sd)
}
if (sd->guild) {
- struct guild_storage *guild_storage = gstorage->id2storage2(sd->guild->guild_id);
+ struct guild_storage *guild_storage = idb_get(gstorage->db,sd->guild->guild_id);
if (guild_storage) {
for( i = 0; i < MAX_GUILD_STORAGE; i++ ) {
id = guild_storage->items[i].nameid;
@@ -9284,8 +9366,8 @@ int pc_calc_pvprank_sub(struct block_list *bl,va_list ap)
sd1=(struct map_session_data *)bl;
sd2=va_arg(ap,struct map_session_data *);
- if( sd1->sc.option&OPTION_INVISIBLE || sd2->sc.option&OPTION_INVISIBLE )
- {// cannot register pvp rank for hidden GMs
+ if (pc_isinvisible(sd1) ||pc_isinvisible(sd2)) {
+ // cannot register pvp rank for hidden GMs
return 0;
}
@@ -9319,8 +9401,8 @@ int pc_calc_pvprank_timer(int tid, int64 tick, int id, intptr_t data) {
return 0;
sd->pvp_timer = INVALID_TIMER;
- if( sd->sc.option&OPTION_INVISIBLE )
- {// do not calculate the pvp rank for a hidden GM
+ if (pc_isinvisible(sd)) {
+ // do not calculate the pvp rank for a hidden GM
return 0;
}
@@ -10958,8 +11040,10 @@ void pc_defaults(void) {
pc->setoption = pc_setoption;
pc->setcart = pc_setcart;
pc->setfalcon = pc_setfalcon;
- pc->setriding = pc_setriding;
+ pc->setridingpeco = pc_setridingpeco;
pc->setmadogear = pc_setmadogear;
+ pc->setridingdragon = pc_setridingdragon;
+ pc->setridingwug = pc_setridingwug;
pc->changelook = pc_changelook;
pc->equiplookall = pc_equiplookall;
diff --git a/src/map/pc.h b/src/map/pc.h
index c36704b4f..e613feec5 100644
--- a/src/map/pc.h
+++ b/src/map/pc.h
@@ -543,6 +543,13 @@ struct map_session_data {
bool vars_ok;
bool vars_dirty;
+ struct {
+ short stage;
+ short prizeIdx;
+ short prizeStage;
+ bool claimPrize;
+ } roulette;
+
// temporary debugging of bug #3504
const char* delunit_prevfile;
int delunit_prevline;
@@ -589,19 +596,20 @@ struct map_session_data {
#endif
#define pc_isfalcon(sd) ( (sd)->sc.option&OPTION_FALCON )
-#define pc_isriding(sd) ( (sd)->sc.option&OPTION_RIDING )
#define pc_isinvisible(sd) ( (sd)->sc.option&OPTION_INVISIBLE )
#define pc_is50overweight(sd) ( (sd)->weight*100 >= (sd)->max_weight*battle->bc->natural_heal_weight_rate )
#define pc_is90overweight(sd) ( (sd)->weight*10 >= (sd)->max_weight*9 )
#define pc_maxparameter(sd) ( (((sd)->class_&MAPID_UPPERMASK) == MAPID_KAGEROUOBORO || ((sd)->class_&MAPID_UPPERMASK) == MAPID_REBELLION || ((sd)->class_&MAPID_THIRDMASK) == MAPID_SUPER_NOVICE_E) ? battle->bc->max_extended_parameter : (sd)->class_&JOBL_THIRD ? ((sd)->class_&JOBL_BABY ? battle->bc->max_baby_third_parameter : battle->bc->max_third_parameter) : ((sd)->class_&JOBL_BABY ? battle->bc->max_baby_parameter : battle->bc->max_parameter) )
-/**
- * Ranger
- **/
+/// Generic check for mounts
+#define pc_hasmount(sd) ( (sd)->sc.option&(OPTION_RIDING|OPTION_WUGRIDER|OPTION_DRAGON|OPTION_MADOGEAR) )
+/// Knight classes Peco / Gryphon
+#define pc_isridingpeco(sd) ( (sd)->sc.option&(OPTION_RIDING) )
+/// Ranger Warg
#define pc_iswug(sd) ( (sd)->sc.option&OPTION_WUG )
#define pc_isridingwug(sd) ( (sd)->sc.option&OPTION_WUGRIDER )
-// Mechanic Magic Gear
+/// Mechanic Magic Gear
#define pc_ismadogear(sd) ( (sd)->sc.option&OPTION_MADOGEAR )
-// Rune Knight Dragon
+/// Rune Knight Dragon
#define pc_isridingdragon(sd) ( (sd)->sc.option&OPTION_DRAGON )
#define pc_stop_walking(sd, type) (unit->stop_walking(&(sd)->bl, (type)))
@@ -897,9 +905,11 @@ struct pc_interface {
int (*jobchange) (struct map_session_data *sd,int job, int upper);
int (*setoption) (struct map_session_data *sd,int type);
int (*setcart) (struct map_session_data* sd, int type);
- int (*setfalcon) (struct map_session_data* sd, int flag);
- int (*setriding) (struct map_session_data* sd, int flag);
- void (*setmadogear) (struct map_session_data* sd, int flag);
+ void (*setfalcon) (struct map_session_data *sd, bool flag);
+ void (*setridingpeco) (struct map_session_data *sd, bool flag);
+ void (*setmadogear) (struct map_session_data *sd, bool flag);
+ void (*setridingdragon) (struct map_session_data *sd, unsigned int type);
+ void (*setridingwug) (struct map_session_data *sd, bool flag);
int (*changelook) (struct map_session_data *sd,int type,int val);
int (*equiplookall) (struct map_session_data *sd);
diff --git a/src/map/pet.c b/src/map/pet.c
index 9275a6de5..d90727b97 100644
--- a/src/map/pet.c
+++ b/src/map/pet.c
@@ -821,7 +821,7 @@ int pet_randomwalk(struct pet_data *pd, int64 tick) {
else
c+=pd->status.speed;
}
- pd->next_walktime = tick+rnd()%3000+3000+c;
+ pd->next_walktime = tick+rnd()%1000+MIN_RANDOMWALKTIME+c;
return 1;
}
diff --git a/src/map/script.c b/src/map/script.c
index f46ee78fe..5e368e0ad 100644
--- a/src/map/script.c
+++ b/src/map/script.c
@@ -526,7 +526,7 @@ int script_add_str(const char* p)
}
}
if( existingentry ) {
- DeprecationWarning2("script_add_str", p, existingentry, script->parser_current_file); // TODO
+ DeprecationCaseWarning("script_add_str", p, existingentry, script->parser_current_file); // TODO
}
#endif // ENABLE_CASE_CHECK
@@ -772,6 +772,8 @@ const char* parse_callfunc(const char* p, int require_paren, int is_custom)
script->addl(func);
script->addc(C_ARG);
arg = script->buildin[script->str_data[func].val];
+ if (script->str_data[func].deprecated)
+ DeprecationWarning(p);
if( !arg ) arg = &null_arg; // Use a dummy, null string
} else if( script->str_data[func].type == C_USERFUNC || script->str_data[func].type == C_USERFUNC_POS ) {
// script defined function
@@ -1349,7 +1351,7 @@ const char* parse_curly_close(const char* p)
char label[256];
int l;
// Remove temporary variables
- sprintf(label,"set $@__SW%x_VAL,0;",script->syntax.curly[pos].index);
+ sprintf(label,"__setr $@__SW%x_VAL,0;",script->syntax.curly[pos].index);
script->syntax.curly[script->syntax.curly_count++].type = TYPE_NULL;
script->parse_line(label);
script->syntax.curly_count--;
@@ -1431,10 +1433,6 @@ const char* parse_syntax(const char* p)
// Closing decision if, for , while
p = script->parse_syntax_close(p + 1);
return p;
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 5 && strncasecmp(p, "break", 5) == 0 ) {
- disp_deprecation_message("parse_syntax", "break", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
break;
case 'c':
@@ -1506,7 +1504,7 @@ const char* parse_syntax(const char* p)
disp_error_message("parse_syntax: dup 'case'",p);
linkdb_insert(&script->syntax.curly[pos].case_label, (void*)h64BPTRSIZE(v), (void*)1);
- sprintf(label,"set $@__SW%x_VAL,0;",script->syntax.curly[pos].index);
+ sprintf(label,"__setr $@__SW%x_VAL,0;",script->syntax.curly[pos].index);
script->syntax.curly[script->syntax.curly_count++].type = TYPE_NULL;
script->parse_line(label);
@@ -1545,12 +1543,6 @@ const char* parse_syntax(const char* p)
//Closing decision if, for , while
p = script->parse_syntax_close(p + 1);
return p;
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 4 && strncasecmp(p, "case", 4) == 0 ) {
- disp_deprecation_message("parse_syntax", "case", p); // TODO
- } else if( p2 - p == 8 && strncasecmp(p, "continue", 8) == 0 ) {
- disp_deprecation_message("parse_syntax", "continue", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
break;
case 'd':
@@ -1604,12 +1596,6 @@ const char* parse_syntax(const char* p)
script->set_label(l,script->pos,p);
script->syntax.curly_count++;
return p;
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 7 && strncasecmp(p, "default", 7) == 0 ) {
- disp_deprecation_message("parse_syntax", "default", p); // TODO
- } else if( p2 - p == 2 && strncasecmp(p, "do", 2) == 0 ) {
- disp_deprecation_message("parse_syntax", "do", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
break;
case 'f':
@@ -1647,7 +1633,7 @@ const char* parse_syntax(const char* p)
} else {
// Skip to the end point if the condition is false
sprintf(label,"__FR%x_FIN",script->syntax.curly[pos].index);
- script->addl(script->add_str("jump_zero"));
+ script->addl(script->add_str("__jump_zero"));
script->addc(C_ARG);
p=script->parse_expr(p);
p=script->skip_space(p);
@@ -1749,12 +1735,6 @@ const char* parse_syntax(const char* p)
{
disp_error_message("expect ';' or '{' at function syntax",p);
}
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 3 && strncasecmp(p, "for", 3) == 0 ) {
- disp_deprecation_message("parse_syntax", "for", p); // TODO
- } else if( p2 - p == 8 && strncasecmp(p, "function", 8) == 0 ) {
- disp_deprecation_message("parse_syntax", "function", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
break;
case 'i':
@@ -1772,17 +1752,13 @@ const char* parse_syntax(const char* p)
script->syntax.curly[script->syntax.curly_count].flag = 0;
sprintf(label,"__IF%x_%x",script->syntax.curly[script->syntax.curly_count].index,script->syntax.curly[script->syntax.curly_count].count);
script->syntax.curly_count++;
- script->addl(script->add_str("jump_zero"));
+ script->addl(script->add_str("__jump_zero"));
script->addc(C_ARG);
p=script->parse_expr(p);
p=script->skip_space(p);
script->addl(script->add_str(label));
script->addc(C_FUNC);
return p;
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 2 && strncasecmp(p, "if", 2) == 0 ) {
- disp_deprecation_message("parse_syntax", "if", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
break;
case 's':
@@ -1800,7 +1776,7 @@ const char* parse_syntax(const char* p)
script->syntax.curly[script->syntax.curly_count].flag = 0;
sprintf(label,"$@__SW%x_VAL",script->syntax.curly[script->syntax.curly_count].index);
script->syntax.curly_count++;
- script->addl(script->add_str("set"));
+ script->addl(script->add_str("__setr"));
script->addc(C_ARG);
script->addl(script->add_str(label));
p=script->parse_expr(p);
@@ -1810,10 +1786,6 @@ const char* parse_syntax(const char* p)
}
script->addc(C_FUNC);
return p + 1;
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 6 && strncasecmp(p, "switch", 6) == 0 ) {
- disp_deprecation_message("parse_syntax", "switch", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
break;
case 'w':
@@ -1837,17 +1809,13 @@ const char* parse_syntax(const char* p)
// Skip to the end point if the condition is false
sprintf(label,"__WL%x_FIN",script->syntax.curly[script->syntax.curly_count].index);
script->syntax.curly_count++;
- script->addl(script->add_str("jump_zero"));
+ script->addl(script->add_str("__jump_zero"));
script->addc(C_ARG);
p=script->parse_expr(p);
p=script->skip_space(p);
script->addl(script->add_str(label));
script->addc(C_FUNC);
return p;
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 5 && strncasecmp(p, "while", 5) == 0 ) {
- disp_deprecation_message("parse_syntax", "while", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
break;
}
@@ -1909,7 +1877,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag)
disp_error_message("need '('",p);
}
sprintf(label,"__IF%x_%x",script->syntax.curly[pos].index,script->syntax.curly[pos].count);
- script->addl(script->add_str("jump_zero"));
+ script->addl(script->add_str("__jump_zero"));
script->addc(C_ARG);
p=script->parse_expr(p);
p=script->skip_space(p);
@@ -1917,10 +1885,6 @@ const char* parse_syntax_close_sub(const char* p,int* flag)
script->addc(C_FUNC);
*flag = 0;
return p;
-#ifdef ENABLE_CASE_CHECK
- } else if( p2 - p == 2 && strncasecmp(p, "if", 2) == 0 ) {
- disp_deprecation_message("parse_syntax", "if", p); // TODO
-#endif // ENABLE_CASE_CHECK
} else {
// else
if(!script->syntax.curly[pos].flag) {
@@ -1929,10 +1893,6 @@ const char* parse_syntax_close_sub(const char* p,int* flag)
return p;
}
}
-#ifdef ENABLE_CASE_CHECK
- } else if( !script->syntax.curly[pos].flag && p2 - p == 4 && strncasecmp(p, "else", 4) == 0 ) {
- disp_deprecation_message("parse_syntax", "else", p); // TODO
-#endif // ENABLE_CASE_CHECK
}
// Close if
script->syntax.curly_count--;
@@ -1959,9 +1919,6 @@ const char* parse_syntax_close_sub(const char* p,int* flag)
p = script->skip_space(p);
p2 = script->skip_word(p);
if( p2 - p != 5 || strncmp(p, "while", 5) != 0 ) {
-#ifdef ENABLE_CASE_CHECK
- if( p2 - p == 5 && strncasecmp(p, "while", 5) == 0 ) disp_deprecation_message("parse_syntax", "while", p); // TODO
-#endif // ENABLE_CASE_CHECK
disp_error_message("parse_syntax: need 'while'",p);
}
@@ -1974,7 +1931,7 @@ const char* parse_syntax_close_sub(const char* p,int* flag)
script->parse_nextline(false, p);
sprintf(label,"__DO%x_FIN",script->syntax.curly[pos].index);
- script->addl(script->add_str("jump_zero"));
+ script->addl(script->add_str("__jump_zero"));
script->addc(C_ARG);
p=script->parse_expr(p);
p=script->skip_space(p);
@@ -5681,7 +5638,7 @@ BUILDIN(copyarray);
/// The value is converted to the type of the variable.
///
/// set(<variable>,<value>) -> <variable>
-BUILDIN(setr) {
+BUILDIN(__setr) {
TBL_PC* sd = NULL;
struct script_data* data;
//struct script_data* datavalue;
@@ -5691,7 +5648,7 @@ BUILDIN(setr) {
data = script_getdata(st,2);
//datavalue = script_getdata(st,3);
- if( !data_isreference(data) || reference_toconstant(data) ) {
+ if (!data_isreference(data) || reference_toconstant(data)) {
ShowError("script:set: not a variable\n");
script->reportdata(script_getdata(st,2));
st->state = END;
@@ -5702,9 +5659,9 @@ BUILDIN(setr) {
name = reference_getname(data);
prefix = *name;
- if( not_server_variable(prefix) ) {
+ if (not_server_variable(prefix)) {
sd = script->rid2sd(st);
- if( sd == NULL ) {
+ if (sd == NULL) {
ShowError("script:set: no player attached for player variable '%s'\n", name);
return true;
}
@@ -5712,19 +5669,19 @@ BUILDIN(setr) {
#if 0
// TODO: see de43fa0f73be01080bd11c08adbfb7c158324c81
- if( data_isreference(datavalue) ) {
+ if (data_isreference(datavalue)) {
// the value being referenced is a variable
const char* namevalue = reference_getname(datavalue);
- if( !not_array_variable(*namevalue) ) {
+ if (!not_array_variable(*namevalue)) {
// array variable being copied into another array variable
- if( sd == NULL && not_server_variable(*namevalue) && !(sd = script->rid2sd(st)) ) {
+ if (sd == NULL && not_server_variable(*namevalue) && !(sd = script->rid2sd(st))) {
// player must be attached in order to copy a player variable
ShowError("script:set: no player attached for player variable '%s'\n", namevalue);
return true;
}
- if( is_string_variable(namevalue) != is_string_variable(name) ) {
+ if (is_string_variable(namevalue) != is_string_variable(name)) {
// non-matching array value types
ShowWarning("script:set: two array variables do not match in type.\n");
return true;
@@ -5739,9 +5696,9 @@ BUILDIN(setr) {
}
#endif
- if( script_hasdata(st, 4) ) {
+ if (script_hasdata(st, 4)) {
// Optional argument used by post-increment/post-decrement constructs to return the previous value
- if( is_string_variable(name) ) {
+ if (is_string_variable(name)) {
script_pushstrcopy(st, script_getstr(st, 4));
} else {
script_pushint(st, script_getnum(st, 4));
@@ -5751,7 +5708,7 @@ BUILDIN(setr) {
script_pushcopy(st,2);
}
- if( is_string_variable(name) )
+ if (is_string_variable(name))
script->set_reg(st,sd,num,name,(void*)script_getstr(st,3),script_getref(st,2));
else
script->set_reg(st,sd,num,name,(void*)h64BPTRSIZE(script_getnum(st,3)),script_getref(st,2));
@@ -7013,28 +6970,25 @@ BUILDIN(delitem) {
TBL_PC *sd;
struct item it;
- if( script_hasdata(st,4) )
- {
+ if (script_hasdata(st,4)) {
int account_id = script_getnum(st,4);
sd = map->id2sd(account_id); // <account id>
- if( sd == NULL )
- {
+ if (sd == NULL) {
ShowError("script:delitem: player not found (AID=%d).\n", account_id);
st->state = END;
return false;
}
- }
- else
- {
+ } else {
sd = script->rid2sd(st);// attached player
- if( sd == NULL )
+ if (sd == NULL)
return true;
}
- if( script_isstringtype(st, 2) ) {
+ memset(&it, 0, sizeof(it));
+ if (script_isstringtype(st, 2)) {
const char* item_name = script_getstr(st, 2);
struct item_data* id = itemdb->search_name(item_name);
- if( id == NULL ) {
+ if (id == NULL) {
ShowError("script:delitem: unknown item \"%s\".\n", item_name);
st->state = END;
return false;
@@ -7042,8 +6996,7 @@ BUILDIN(delitem) {
it.nameid = id->nameid;// "<item name>"
} else {
it.nameid = script_getnum(st, 2);// <item id>
- if( !itemdb->exists( it.nameid ) )
- {
+ if (!itemdb->exists(it.nameid)) {
ShowError("script:delitem: unknown item \"%d\".\n", it.nameid);
st->state = END;
return false;
@@ -7074,26 +7027,25 @@ BUILDIN(delitem2) {
TBL_PC *sd;
struct item it;
- if( script_hasdata(st,11) ) {
+ if (script_hasdata(st,11)) {
int account_id = script_getnum(st,11);
sd = map->id2sd(account_id); // <account id>
- if( sd == NULL ) {
+ if (sd == NULL) {
ShowError("script:delitem2: player not found (AID=%d).\n", account_id);
st->state = END;
return false;
}
- }
- else
- {
+ } else {
sd = script->rid2sd(st);// attached player
if( sd == NULL )
return true;
}
- if( script_isstringtype(st, 2) ) {
+ memset(&it, 0, sizeof(it));
+ if (script_isstringtype(st, 2)) {
const char* item_name = script_getstr(st, 2);
struct item_data* id = itemdb->search_name(item_name);
- if( id == NULL ) {
+ if (id == NULL) {
ShowError("script:delitem2: unknown item \"%s\".\n", item_name);
st->state = END;
return false;
@@ -7658,6 +7610,29 @@ BUILDIN(getbrokenid)
}
/*==========================================
+ * getbrokencount
+ *------------------------------------------*/
+BUILDIN(getbrokencount)
+{
+ int i, counter = 0;
+ TBL_PC *sd;
+
+ sd = script->rid2sd(st);
+
+ if (sd == NULL)
+ return true;
+
+ for (i = 0; i < MAX_INVENTORY; i++) {
+ if (sd->status.inventory[i].attribute)
+ counter++;
+ }
+
+ script_pushint(st, counter);
+
+ return true;
+}
+
+/*==========================================
* repair [Valaris]
*------------------------------------------*/
BUILDIN(repair)
@@ -8612,10 +8587,10 @@ BUILDIN(checkfalcon)
TBL_PC* sd;
sd = script->rid2sd(st);
- if( sd == NULL )
+ if (sd == NULL)
return true;// no player attached, report source
- if( pc_isfalcon(sd) )
+ if (pc_isfalcon(sd))
script_pushint(st, 1);
else
script_pushint(st, 0);
@@ -8630,15 +8605,15 @@ BUILDIN(checkfalcon)
/// setfalcon;
BUILDIN(setfalcon)
{
- int flag = 1;
+ bool flag = true;
TBL_PC* sd;
sd = script->rid2sd(st);
- if( sd == NULL )
+ if (sd == NULL)
return true;// no player attached, report source
- if( script_hasdata(st,2) )
- flag = script_getnum(st,2);
+ if (script_hasdata(st,2))
+ flag = script_getnum(st,2) ? true : false;
pc->setfalcon(sd, flag);
@@ -8655,10 +8630,10 @@ BUILDIN(checkriding)
TBL_PC* sd;
sd = script->rid2sd(st);
- if( sd == NULL )
- return true;// no player attached, report source
+ if (sd == NULL)
+ return true; // no player attached, report source
- if( pc_isriding(sd) || pc_isridingwug(sd) || pc_isridingdragon(sd) )
+ if (pc_hasmount(sd))
script_pushint(st, 1);
else
script_pushint(st, 0);
@@ -8677,12 +8652,143 @@ BUILDIN(setriding)
TBL_PC* sd;
sd = script->rid2sd(st);
- if( sd == NULL )
+
+ if (sd == NULL)
return true;// no player attached, report source
- if( script_hasdata(st,2) )
+ if (script_hasdata(st,2))
flag = script_getnum(st,2);
- pc->setriding(sd, flag);
+ pc->setridingpeco(sd, flag ? true : false);
+
+ return true;
+}
+
+enum setmount_type {
+ SETMOUNT_TYPE_AUTODETECT = -1,
+ SETMOUNT_TYPE_NONE = 0,
+ SETMOUNT_TYPE_PECO = 1,
+ SETMOUNT_TYPE_WUG = 2,
+ SETMOUNT_TYPE_MADO = 3,
+ SETMOUNT_TYPE_DRAGON_GREEN = 4,
+ SETMOUNT_TYPE_DRAGON_BROWN = 5,
+ SETMOUNT_TYPE_DRAGON_GRAY = 6,
+ SETMOUNT_TYPE_DRAGON_BLUE = 7,
+ SETMOUNT_TYPE_DRAGON_RED = 8,
+ SETMOUNT_TYPE_MAX,
+ SETMOUNT_TYPE_DRAGON = SETMOUNT_TYPE_DRAGON_GREEN,
+};
+
+/**
+ * Checks if the player is riding a combat mount.
+ *
+ * Returns 0 if the player isn't riding, and non-zero if it is.
+ * The exact returned values are the same used as flag in setmount, except for
+ * dragons, where SETMOUNT_TYPE_DRAGON is returned, regardless of color.
+ */
+BUILDIN(checkmount)
+{
+ TBL_PC* sd;
+
+ sd = script->rid2sd(st);
+ if (sd == NULL)
+ return true; // no player attached, report source
+
+ if (!pc_hasmount(sd)) {
+ script_pushint(st, SETMOUNT_TYPE_NONE);
+ } else if (pc_isridingpeco(sd)) {
+ script_pushint(st, SETMOUNT_TYPE_PECO);
+ } else if (pc_isridingwug(sd)) {
+ script_pushint(st, SETMOUNT_TYPE_WUG);
+ } else if (pc_ismadogear(sd)) {
+ script_pushint(st, SETMOUNT_TYPE_MADO);
+ } else { // if (pc_isridingdragon(sd))
+ script_pushint(st, SETMOUNT_TYPE_DRAGON);
+ }
+
+ return true;
+}
+
+/**
+ * Mounts or dismounts a combat mount.
+ *
+ * setmount <flag>;
+ * setmount;
+ *
+ * Accepted values for flag:
+ * MOUNT_NONE - dismount
+ * MOUNT_PECO - Peco Peco / Grand Peco / Gryphon (depending on the class)
+ * MOUNT_WUG - Wug (Rider)
+ * MOUNT_MADO - Mado Gear
+ * MOUNT_DRAGON - Dragon (default color)
+ * MOUNT_DRAGON_GREEN - Green Dragon
+ * MOUNT_DRAGON_BROWN - Brown Dragon
+ * MOUNT_DRAGON_GRAY - Gray Dragon
+ * MOUNT_DRAGON_BLUE - Blue Dragon
+ * MOUNT_DRAGON_RED - Red Dragon
+ *
+ * If an invalid value or no flag is specified, the appropriate mount is
+ * auto-detected. As a result of this, there is no need to specify a flag at
+ * all, unless it is a dragon color other than green.
+ */
+BUILDIN(setmount)
+{
+ int flag = SETMOUNT_TYPE_AUTODETECT;
+ TBL_PC* sd;
+
+ sd = script->rid2sd(st);
+
+ if (sd == NULL)
+ return true;// no player attached, report source
+
+ if (script_hasdata(st,2))
+ flag = script_getnum(st,2);
+
+ // Color variants for Rune Knight dragon mounts.
+ if (flag != SETMOUNT_TYPE_NONE) {
+ if (flag < SETMOUNT_TYPE_AUTODETECT || flag >= SETMOUNT_TYPE_MAX) {
+ ShowWarning("script_setmount: Unknown flag %d specified. Using auto-detected value.\n", flag);
+ flag = SETMOUNT_TYPE_AUTODETECT;
+ }
+ // Sanity checks and auto-detection
+ if ((sd->class_&MAPID_THIRDMASK) == MAPID_RUNE_KNIGHT) {
+ if (pc->checkskill(sd, RK_DRAGONTRAINING)) {
+ // Rune Knight (Dragon)
+ unsigned int option;
+ option = ( flag == SETMOUNT_TYPE_DRAGON_GREEN ? OPTION_DRAGON1 :
+ flag == SETMOUNT_TYPE_DRAGON_BROWN ? OPTION_DRAGON2 :
+ flag == SETMOUNT_TYPE_DRAGON_GRAY ? OPTION_DRAGON3 :
+ flag == SETMOUNT_TYPE_DRAGON_RED ? OPTION_DRAGON4 :
+ flag == SETMOUNT_TYPE_DRAGON_RED ? OPTION_DRAGON5 :
+ OPTION_DRAGON1); // default value
+ pc->setridingdragon(sd, option);
+ }
+ } else if ((sd->class_&MAPID_THIRDMASK) == MAPID_RANGER) {
+ // Ranger (Warg)
+ if (pc->checkskill(sd, RA_WUGRIDER))
+ pc->setridingwug(sd, true);
+ } else if ((sd->class_&MAPID_THIRDMASK) == MAPID_MECHANIC) {
+ // Mechanic (Mado Gear)
+ if (pc->checkskill(sd, NC_MADOLICENCE))
+ pc->setmadogear(sd, true);
+ } else {
+ // Knight / Crusader (Peco Peco)
+ if (pc->checkskill(sd, KN_RIDING))
+ pc->setridingpeco(sd, true);
+ }
+ } else if (pc_hasmount(sd)) {
+ if (pc_isridingdragon(sd)) {
+ pc->setridingdragon(sd, 0);
+ }
+ if (pc_isridingwug(sd)) {
+ pc->setridingwug(sd, false);
+ }
+ if (pc_ismadogear(sd)) {
+ pc->setmadogear(sd, false);
+ }
+ if (pc_isridingpeco(sd)) {
+ pc->setridingpeco(sd, false);
+ }
+ }
return true;
}
@@ -8734,15 +8840,15 @@ BUILDIN(checkmadogear)
/// setmadogear;
BUILDIN(setmadogear)
{
- int flag = 1;
+ bool flag = true;
TBL_PC* sd;
sd = script->rid2sd(st);
- if( sd == NULL )
+ if (sd == NULL)
return true;// no player attached, report source
- if( script_hasdata(st,2) )
- flag = script_getnum(st,2);
+ if (script_hasdata(st,2))
+ flag = script_getnum(st,2) ? true : false;
pc->setmadogear(sd, flag);
return true;
@@ -13094,14 +13200,14 @@ BUILDIN(checkequipedcard)
return true;
}
-BUILDIN(jump_zero)
+BUILDIN(__jump_zero)
{
int sel;
sel=script_getnum(st,2);
- if(!sel) {
+ if (!sel) {
int pos;
- if( !data_islabel(script_getdata(st,3)) ) {
- ShowError("script: jump_zero: not label !\n");
+ if (!data_islabel(script_getdata(st,3))) {
+ ShowError("script: jump_zero: not a label !\n");
st->state=END;
return false;
}
@@ -14499,7 +14605,7 @@ BUILDIN(replacestr)
}
if(script_hasdata(st, 6)) {
- if (!script_isinttype(st, 5) || (count = script_getnum(st, 6) == 0)) {
+ if (!script_isinttype(st, 6) || (count = script_getnum(st, 6)) == 0) {
ShowError("script:replacestr: Invalid count value. Expected int.\n");
st->state = END;
return false;
@@ -14729,6 +14835,36 @@ BUILDIN(distance)
// <--- [zBuffer] List of mathematics commands
+BUILDIN(min)
+{
+ int i, min;
+
+ min = script_getnum(st, 2);
+ for (i = 3; script_hasdata(st, i); i++) {
+ int next = script_getnum(st, i);
+ if (next < min)
+ min = next;
+ }
+ script_pushint(st, min);
+
+ return true;
+}
+
+BUILDIN(max)
+{
+ int i, max;
+
+ max = script_getnum(st, 2);
+ for (i = 3; script_hasdata(st, i); i++) {
+ int next = script_getnum(st, i);
+ if (next > max)
+ max = next;
+ }
+ script_pushint(st, max);
+
+ return true;
+}
+
BUILDIN(md5)
{
const char *tmpstr;
@@ -15658,8 +15794,6 @@ BUILDIN(unittalk) {
StrBuf->Init(&sbuf);
StrBuf->Printf(&sbuf, "%s : %s", status->get_name(bl), message);
clif->disp_overhead(bl, StrBuf->Value(&sbuf));
- if( bl->type == BL_PC )
- clif->message(((TBL_PC*)bl)->fd, StrBuf->Value(&sbuf));
StrBuf->Destroy(&sbuf);
}
@@ -16283,21 +16417,45 @@ BUILDIN(setquest) {
BUILDIN(erasequest) {
struct map_session_data *sd = script->rid2sd(st);
+ int quest_id;
if( sd == NULL )
return false;
- quest->delete(sd, script_getnum(st, 2));
+ if (script_hasdata(st, 3)) {
+ if (script_getnum(st, 3) < script_getnum(st, 2)) {
+ ShowError("buildin_erasequest: The second quest id must be greater than the id of the first.\n");
+ return false;
+ }
+ for (quest_id = script_getnum(st, 2); quest_id < script_getnum(st, 3); quest_id++) {
+ quest->delete(sd, quest_id);
+ }
+ } else {
+ quest->delete(sd, script_getnum(st, 2));
+ }
+
return true;
}
BUILDIN(completequest) {
struct map_session_data *sd = script->rid2sd(st);
+ int quest_id;
if( sd == NULL )
return false;
- quest->update_status(sd, script_getnum(st, 2), Q_COMPLETE);
+ if (script_hasdata(st, 3)) {
+ if (script_getnum(st, 3) < script_getnum(st, 2)) {
+ ShowError("buildin_completequest: The second quest id must be greater than the id of the first.\n");
+ return false;
+ }
+ for (quest_id = script_getnum(st, 2); quest_id < script_getnum(st, 3); quest_id++) {
+ quest->update_status(sd, quest_id, Q_COMPLETE);
+ }
+ } else {
+ quest->update_status(sd, script_getnum(st, 2), Q_COMPLETE);
+ }
+
return true;
}
@@ -16311,6 +16469,8 @@ BUILDIN(changequest) {
return true;
}
+// Deprecated
+// Please use questprogress instead.
BUILDIN(checkquest) {
struct map_session_data *sd = script->rid2sd(st);
enum quest_check_type type = HAVEQUEST;
@@ -16326,6 +16486,50 @@ BUILDIN(checkquest) {
return true;
}
+BUILDIN(questactive) {
+ struct map_session_data *sd = script->rid2sd(st);
+ int quest_progress = 0;
+
+ if (sd == NULL)
+ return false;
+
+ if (quest->check(sd, script_getnum(st, 2), HAVEQUEST) == Q_ACTIVE)
+ script_pushint(st, 1);
+ else
+ script_pushint(st, 0);
+
+ script_pushint(st, quest_progress);
+
+ return true;
+}
+
+BUILDIN(questprogress) {
+ struct map_session_data *sd = script->rid2sd(st);
+ enum quest_check_type type = HAVEQUEST;
+ int quest_progress = 0;
+
+ if (sd == NULL)
+ return false;
+
+ if (script_hasdata(st, 3))
+ type = (enum quest_check_type)script_getnum(st, 3);
+
+ quest_progress = quest->check(sd, script_getnum(st, 2), type);
+
+ // "Fix" returned quest state value to make more sense.
+ // 0 = Not Started, 1 = In Progress, 2 = Completed.
+ if (quest_progress == -1) // Not found
+ quest_progress = 0;
+ else if (quest_progress == 0 || quest_progress == 1)
+ quest_progress = 1;
+ else
+ quest_progress = 2;
+
+ script_pushint(st, quest_progress);
+
+ return true;
+}
+
BUILDIN(showevent) {
TBL_PC *sd = script->rid2sd(st);
struct npc_data *nd = map->id2nd(st->oid);
@@ -17271,9 +17475,10 @@ BUILDIN(setdragon) {
}
/**
- * ismounting() returns 1 if mounting a new mount or 0 otherwise
+ * hascashmount() returns 1 if mounting a cash mount or 0 otherwise
**/
-BUILDIN(ismounting) {
+BUILDIN(hascashmount)
+{
TBL_PC* sd;
if( (sd = script->rid2sd(st)) == NULL )
return true;
@@ -17285,20 +17490,22 @@ BUILDIN(ismounting) {
}
/**
- * setmounting() returns 1 on success or 0 otherwise
- * - Toggles new mounts on a player when he can mount
- * - Will fail if the player is mounting a non-new mount, e.g. dragon, peco, wug, etc.
- * - Will unmount the player is he is already mounting
+ * setcashmount() returns 1 on success or 0 otherwise
+ *
+ * - Toggles cash mounts on a player when he can mount
+ * - Will fail if the player is already riding a standard mount e.g. dragon, peco, wug, mado, etc.
+ * - Will unmount the player is he is already mounting a cash mount
**/
-BUILDIN(setmounting) {
+BUILDIN(setcashmount)
+{
TBL_PC* sd;
- if( (sd = script->rid2sd(st)) == NULL )
+ if ((sd = script->rid2sd(st)) == NULL)
return true;
- if( sd->sc.option&(OPTION_WUGRIDER|OPTION_RIDING|OPTION_DRAGON|OPTION_MADOGEAR) ) {
+ if (pc_hasmount(sd)) {
clif->msgtable(sd->fd, 0X78b);
script_pushint(st,0);//can't mount with one of these
} else {
- if( sd->sc.data[SC_ALL_RIDING] )
+ if (sd->sc.data[SC_ALL_RIDING])
status_change_end(&sd->bl, SC_ALL_RIDING, INVALID_TIMER);
else
sc_start(NULL,&sd->bl, SC_ALL_RIDING, 100, 0, -1);
@@ -17306,6 +17513,7 @@ BUILDIN(setmounting) {
}
return true;
}
+
/**
* Retrieves quantity of arguments provided to callfunc/callsub.
* getargcount() -> amount of arguments received in a function
@@ -17788,6 +17996,7 @@ BUILDIN(montransform) {
struct block_list* bl;
char msg[CHAT_SIZE_MAX];
int mob_id, val1, val2, val3, val4;
+ val1 = val2 = val3 = val4 = 0;
if( (bl = map->id2bl(st->rid)) == NULL )
return true;
@@ -17807,12 +18016,17 @@ BUILDIN(montransform) {
}
tick = script_getnum(st, 3);
- type = (sc_type)script_getnum(st, 4);
- val1 = val2 = val3 = val4 = 0;
- if( !(type > SC_NONE && type < SC_MAX) ) {
- ShowWarning("buildin_montransform: Unsupported status change id %d\n", type);
- return false;
+ if (script_hasdata(st, 4))
+ type = (sc_type)script_getnum(st, 4);
+ else
+ type = SC_NONE;
+
+ if (script_hasdata(st, 4)) {
+ if( !(type > SC_NONE && type < SC_MAX) ) {
+ ShowWarning("buildin_montransform: Unsupported status change id %d\n", type);
+ return false;
+ }
}
if (script_hasdata(st, 5))
@@ -17848,8 +18062,11 @@ BUILDIN(montransform) {
clif->ShowScript(&sd->bl, msg);
status_change_end(bl, SC_MONSTER_TRANSFORM, INVALID_TIMER); // Clear previous
sc_start2(NULL, bl, SC_MONSTER_TRANSFORM, 100, mob_id, type, tick);
- sc_start4(NULL, bl, type, 100, val1, val2, val3, val4, tick);
+
+ if (script_hasdata(st, 4))
+ sc_start4(NULL, bl, type, 100, val1, val2, val3, val4, tick);
}
+
return true;
}
@@ -18739,7 +18956,7 @@ bool script_add_builtin(const struct script_function *buildin, bool override) {
script->buildin[offset] = NULL;
} else {
// Adding new function
- if( strcmp(buildin->name, "setr") == 0 ) script->buildin_set_ref = n;
+ if( strcmp(buildin->name, "__setr") == 0 ) script->buildin_set_ref = n;
else if( strcmp(buildin->name, "callsub") == 0 ) script->buildin_callsub_ref = n;
else if( strcmp(buildin->name, "callfunc") == 0 ) script->buildin_callfunc_ref = n;
else if( strcmp(buildin->name, "getelementofarray") == 0 ) script->buildin_getelementofarray_ref = n;
@@ -18755,6 +18972,7 @@ bool script_add_builtin(const struct script_function *buildin, bool override) {
}
script->str_data[n].func = buildin->func;
+ script->str_data[n].deprecated = (buildin->deprecated ? 1 : 0);
/* we only store the arguments, its the only thing used out of this */
if( slen ) {
@@ -18772,13 +18990,20 @@ bool script_hp_add(char *name, char *args, bool (*func)(struct script_state *st)
buildin.name = name;
buildin.arg = args;
buildin.func = func;
+ buildin.deprecated = false;
return script->add_builtin(&buildin, true);
}
-#define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args }
-#define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args }
+#define BUILDIN_DEF(x,args) { buildin_ ## x , #x , args, false }
+#define BUILDIN_DEF2(x,x2,args) { buildin_ ## x , x2 , args, false }
+#define BUILDIN_DEF_DEPRECATED(x,args) { buildin_ ## x , #x , args, true }
+#define BUILDIN_DEF2_DEPRECATED(x,x2,args) { buildin_ ## x , x2 , args, true }
void script_parse_builtin(void) {
struct script_function BUILDIN[] = {
+ /* Commands for internal use by the script engine */
+ BUILDIN_DEF(__jump_zero,"il"),
+ BUILDIN_DEF(__setr,"rv?"),
+
// NPC interaction
BUILDIN_DEF(mes,"s*"),
BUILDIN_DEF(next,""),
@@ -18803,8 +19028,7 @@ void script_parse_builtin(void) {
BUILDIN_DEF(warpguild,"siii"), // [Fredzilla]
BUILDIN_DEF(setlook,"ii"),
BUILDIN_DEF(changelook,"ii"), // Simulates but don't Store it
- BUILDIN_DEF2(setr,"set","rv"),
- BUILDIN_DEF(setr,"rv?"), // Not meant to be used directly, required for var++/var--
+ BUILDIN_DEF2(__setr,"set","rv"),
BUILDIN_DEF(setarray,"rv*"),
BUILDIN_DEF(cleararray,"rvi"),
BUILDIN_DEF(copyarray,"rri"),
@@ -18846,6 +19070,7 @@ void script_parse_builtin(void) {
BUILDIN_DEF(getequipid,"i"),
BUILDIN_DEF(getequipname,"i"),
BUILDIN_DEF(getbrokenid,"i"), // [Valaris]
+ BUILDIN_DEF(getbrokencount,""),
BUILDIN_DEF(repair,"i"), // [Valaris]
BUILDIN_DEF(repairall,""),
BUILDIN_DEF(getequipisequiped,"i"),
@@ -18882,12 +19107,14 @@ void script_parse_builtin(void) {
BUILDIN_DEF(checkcart,""),
BUILDIN_DEF(setfalcon,"?"),
BUILDIN_DEF(checkfalcon,""),
- BUILDIN_DEF(setriding,"?"),
- BUILDIN_DEF(checkriding,""),
+ BUILDIN_DEF_DEPRECATED(setriding,"?"), // Deprecated 2014-10-30 [Haru]
+ BUILDIN_DEF_DEPRECATED(checkriding,""), // Deprecated 2014-10-30 [Haru]
+ BUILDIN_DEF(setmount,"?"),
+ BUILDIN_DEF(checkmount,""),
BUILDIN_DEF(checkwug,""),
- BUILDIN_DEF(checkmadogear,""),
- BUILDIN_DEF(setmadogear,"?"),
- BUILDIN_DEF2(savepoint,"save","sii"),
+ BUILDIN_DEF_DEPRECATED(checkmadogear,""), // Deprecated 2014-10-30 [Haru]
+ BUILDIN_DEF_DEPRECATED(setmadogear,"?"), // Deprecated 2014-10-30 [Haru]
+ BUILDIN_DEF2_DEPRECATED(savepoint,"save","sii"), // Deprecated 2014-11-02 [Haru]
BUILDIN_DEF(savepoint,"sii"),
BUILDIN_DEF(gettimetick,"i"),
BUILDIN_DEF(gettime,"i"),
@@ -18905,7 +19132,7 @@ void script_parse_builtin(void) {
BUILDIN_DEF(clone,"siisi????"),
BUILDIN_DEF(doevent,"s"),
BUILDIN_DEF(donpcevent,"s"),
- BUILDIN_DEF(cmdothernpc,"ss"),
+ BUILDIN_DEF_DEPRECATED(cmdothernpc,"ss"), // Deprecated 2014-11-02 [Haru]
BUILDIN_DEF(addtimer,"is"),
BUILDIN_DEF(deltimer,"s"),
BUILDIN_DEF(addtimercount,"si"),
@@ -18949,8 +19176,8 @@ void script_parse_builtin(void) {
BUILDIN_DEF2(waitingroomkickall,"kickwaitingroomall","?"),
BUILDIN_DEF(enablewaitingroomevent,"?"),
BUILDIN_DEF(disablewaitingroomevent,"?"),
- BUILDIN_DEF2(enablewaitingroomevent,"enablearena",""), // Added by RoVeRT
- BUILDIN_DEF2(disablewaitingroomevent,"disablearena",""), // Added by RoVeRT
+ BUILDIN_DEF2_DEPRECATED(enablewaitingroomevent,"enablearena",""), // Deprecated 2014-11-02 [Haru]
+ BUILDIN_DEF2_DEPRECATED(disablewaitingroomevent,"disablearena",""), // Deprecated 2014-11-02 [Haru]
BUILDIN_DEF(getwaitingroomstate,"i?"),
BUILDIN_DEF(warpwaitingpc,"sii?"),
BUILDIN_DEF(attachrid,"i"),
@@ -19005,7 +19232,7 @@ void script_parse_builtin(void) {
BUILDIN_DEF(petskillbonus,"iiii"), // [Valaris]
BUILDIN_DEF(petrecovery,"ii"), // [Valaris]
BUILDIN_DEF(petloot,"i"), // [Valaris]
- BUILDIN_DEF(petheal,"iiii"), // [Valaris]
+ BUILDIN_DEF_DEPRECATED(petheal,"iiii"), // Deprecated 2014-10-27 [Haru]
BUILDIN_DEF(petskillattack,"viii"), // [Skotlex]
BUILDIN_DEF(petskillattack2,"viiii"), // [Valaris]
BUILDIN_DEF(petskillsupport,"viiii"), // [Skotlex]
@@ -19055,7 +19282,7 @@ void script_parse_builtin(void) {
BUILDIN_DEF(gethominfo,"i"),
BUILDIN_DEF(getmercinfo,"i?"),
BUILDIN_DEF(checkequipedcard,"i"),
- BUILDIN_DEF(jump_zero,"il"), //for future jA script compatibility
+ BUILDIN_DEF2_DEPRECATED(__jump_zero,"jump_zero","il"), // Deprecated 2014-10-27 [Haru]
BUILDIN_DEF(globalmes,"s?"), //end jA addition
BUILDIN_DEF(unequip,"i"), // unequip command [Spectre]
BUILDIN_DEF(getstrlen,"s"), //strlen [Valaris]
@@ -19086,6 +19313,8 @@ void script_parse_builtin(void) {
BUILDIN_DEF(pow,"ii"),
BUILDIN_DEF(distance,"iiii"),
// <--- [zBuffer] List of mathematics commands
+ BUILDIN_DEF(min, "i*"),
+ BUILDIN_DEF(max, "i*"),
BUILDIN_DEF(md5,"s"),
// [zBuffer] List of dynamic var commands --->
BUILDIN_DEF(getd,"s"),
@@ -19207,10 +19436,10 @@ void script_parse_builtin(void) {
* 3rd-related
**/
BUILDIN_DEF(makerune,"i"),
- BUILDIN_DEF(checkdragon,""),//[Ind]
- BUILDIN_DEF(setdragon,"?"),//[Ind]
- BUILDIN_DEF(ismounting,""),//[Ind]
- BUILDIN_DEF(setmounting,""),//[Ind]
+ BUILDIN_DEF_DEPRECATED(checkdragon,""), // Deprecated 2014-10-30 [Haru]
+ BUILDIN_DEF_DEPRECATED(setdragon,"?"), // Deprecated 2014-10-30 [Haru]
+ BUILDIN_DEF(hascashmount,""),//[Ind]
+ BUILDIN_DEF(setcashmount,""),//[Ind]
BUILDIN_DEF(checkre,"i"),
/**
* rAthena and beyond!
@@ -19243,9 +19472,11 @@ void script_parse_builtin(void) {
//Quest Log System [Inkfish]
BUILDIN_DEF(questinfo, "ii??"),
BUILDIN_DEF(setquest, "i"),
- BUILDIN_DEF(erasequest, "i"),
- BUILDIN_DEF(completequest, "i"),
- BUILDIN_DEF(checkquest, "i?"),
+ BUILDIN_DEF(erasequest, "i?"),
+ BUILDIN_DEF(completequest, "i?"),
+ BUILDIN_DEF_DEPRECATED(checkquest, "i?"), // Deprecated 2014-10-28 [Haru]
+ BUILDIN_DEF(questprogress, "i?"),
+ BUILDIN_DEF(questactive, "i"),
BUILDIN_DEF(changequest, "ii"),
BUILDIN_DEF(showevent, "i?"),
@@ -19269,7 +19500,7 @@ void script_parse_builtin(void) {
BUILDIN_DEF(stand, "?"),
BUILDIN_DEF(issit, "?"),
- BUILDIN_DEF(montransform, "vii????"), // Monster Transform [malufett/Hercules]
+ BUILDIN_DEF(montransform, "vi?????"), // Monster Transform [malufett/Hercules]
/* New BG Commands [Hercules] */
BUILDIN_DEF(bg_create_team,"sii"),
diff --git a/src/map/script.h b/src/map/script.h
index 48abf1487..1a46ba02b 100644
--- a/src/map/script.h
+++ b/src/map/script.h
@@ -26,9 +26,9 @@ struct eri;
// TODO: Remove temporary code
#define ENABLE_CASE_CHECK
#define get_script_source(source) ((source) ? (source) : "Unknown (Possibly source or variables stored in database")
-#define DeprecationWarning(func, bad, good, file, line) ShowError("%s: use of deprecated keyword '%s' (use '%s' instead) in file '%s', line '%d'.\n", (func), (bad), (good), get_script_source(file), (line));
-#define DeprecationWarning2(func, bad, good, where) ShowError("%s: detected possible use of wrong case in a script. Found '%s', probably meant to be '%s' (in '%s').\n", (func), (bad), (good), get_script_source(where));
-#define disp_deprecation_message(func, good, p) disp_warning_message(func": use of deprecated keyword (use '"good"' instead).", (p));
+#define DeprecationCaseWarning(func, bad, good, where) ShowError("%s: detected possible use of wrong case in a script. Found '%s', probably meant to be '%s' (in '%s').\n", (func), (bad), (good), get_script_source(where))
+
+#define DeprecationWarning(p) disp_warning_message("This command is deprecated and it will be removed in a future update. Please see the script documentation for an alternative.\n", (p))
#define NUM_WHISPER_VAR 10
@@ -433,6 +433,7 @@ struct script_function {
bool (*func)(struct script_state *st);
char *name;
char *arg;
+ bool deprecated;
};
// String buffer structures.
@@ -445,6 +446,7 @@ struct str_data_struct {
bool (*func)(struct script_state *st);
int val;
int next;
+ uint8 deprecated : 1;
};
struct script_label_entry {
diff --git a/src/map/skill.c b/src/map/skill.c
index 4ab0ca1c7..d3c00a75b 100644
--- a/src/map/skill.c
+++ b/src/map/skill.c
@@ -2123,6 +2123,7 @@ int skill_magic_reflect(struct block_list* src, struct block_list* bl, int type)
* packet shouldn't display a skill animation)
* flag&0x2000 is used to signal that the skill_lv should be passed as -1 to the
* client (causes player characters to not scream skill name)
+ * flag&0x4000 - Return 0 if damage was reflected
*-------------------------------------------------------------------------*/
int skill_attack(int attack_type, struct block_list* src, struct block_list *dsrc, struct block_list *bl, uint16 skill_id, uint16 skill_lv, int64 tick, int flag) {
struct Damage dmg;
@@ -2459,6 +2460,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr
case EL_HURRICANE_ATK:
case EL_TYPOON_MIS:
case EL_TYPOON_MIS_ATK:
+ case GN_CRAZYWEED_ATK:
case KO_BAKURETSU:
case NC_MAGMA_ERUPTION:
dmg.dmotion = clif->skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,5);
@@ -2469,9 +2471,6 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr
case SC_FEINTBOMB:
dmg.dmotion = clif->skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,1,skill_id,skill_lv,5);
break;
- case GN_CRAZYWEED_ATK:
- dmg.dmotion = clif->skill_damage(src,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id, -2, 6);
- break;
case EL_STONE_RAIN:
dmg.dmotion = clif->skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion,damage,dmg.div_,skill_id,-1,(flag&1)?8:5);
break;
@@ -2544,7 +2543,7 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr
break;
case WM_SEVERE_RAINSTORM_MELEE:
copy_skill = WM_SEVERE_RAINSTORM;
- break;
+ break;
case GN_CRAZYWEED_ATK:
copy_skill = GN_CRAZYWEED;
break;
@@ -2796,6 +2795,9 @@ int skill_attack(int attack_type, struct block_list* src, struct block_list *dsr
map->freeblock_unlock();
+ if ((flag&0x4000) && rmdamage == 1)
+ return 0; //Should return 0 when damage was reflected
+
return (int)cap_value(damage,INT_MIN,INT_MAX);
}
@@ -3301,6 +3303,11 @@ int skill_timerskill(int tid, int64 tick, int id, intptr_t data) {
else if( path->search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
skill->unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,skl->flag);
break;
+ case GN_CRAZYWEED_ATK: {
+ int dummy = 1, i = skill->get_unit_range(skl->skill_id,skl->skill_lv);
+
+ map->foreachinarea(skill->cell_overlap,src->m,skl->x-i,skl->y-i,skl->x+i,skl->y+i,BL_SKILL,skl->skill_id,&dummy,src);
+ }
// fall through ...
case WL_EARTHSTRAIN:
skill->unitsetting(src,skl->skill_id,skl->skill_lv,skl->x,skl->y,(skl->type<<16)|skl->flag);
@@ -3310,14 +3317,6 @@ int skill_timerskill(int tid, int64 tick, int id, intptr_t data) {
map->foreachinpath(skill->attack_area,src->m,src->x,src->y,skl->x,skl->y,4,2,BL_CHAR,
skill->get_type(skl->skill_id),src,src,skl->skill_id,skl->skill_lv,tick,skl->flag,BCT_ENEMY);
break;
- case GN_CRAZYWEED:
- if( skl->type >= 0 ) {
- int x = skl->type>>16, y = skl->type&0xFFFF;
- if( path->search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
- skill->castend_pos2(src, x, y, GN_CRAZYWEED_ATK, skl->skill_lv, tick, skl->flag);
- } else if( path->search_long(NULL, src->m, src->x, src->y, skl->x, skl->y, CELL_CHKWALL) )
- skill->castend_pos2(src, skl->x, skl->y, GN_CRAZYWEED_ATK, skl->skill_lv, tick, skl->flag);
- break;
}
}
} while (0);
@@ -3564,7 +3563,6 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1
case SR_GENTLETOUCH_QUIET:
case WM_SEVERE_RAINSTORM_MELEE:
case WM_GREAT_ECHO:
- case GN_CRAZYWEED_ATK:
case GN_SLINGITEM_RANGEMELEEATK:
case KO_JYUMONJIKIRI:
case KO_SETSUDAN:
@@ -3808,11 +3806,15 @@ int skill_castend_damage_id(struct block_list* src, struct block_list *bl, uint1
// skill->area_temp[1] holds the id of the original target
// skill->area_temp[2] counts how many targets have already been processed
int sflag = skill->area_temp[0] & 0xFFF, heal;
+ struct status_change *tsc = status->get_sc(bl);
if( flag&SD_LEVEL )
sflag |= SD_LEVEL; // -1 will be used in packets instead of the skill level
if( (skill->area_temp[1] != bl->id && !(skill->get_inf2(skill_id)&INF2_NPC_SKILL)) || flag&SD_ANIMATION )
sflag |= SD_ANIMATION; // original target gets no animation (as well as all NPC skills)
+ if ( tsc && tsc->data[SC_HOVERING] && ( skill_id == SR_WINDMILL || skill_id == LG_MOONSLASHER ) )
+ break;
+
heal = skill->attack(skill->get_type(skill_id), src, src, bl, skill_id, skill_lv, tick, sflag);
if( skill_id == NPC_VAMPIRE_GIFT && heal > 0 ) {
clif->skill_nodamage(NULL, src, AL_HEAL, heal, 1);
@@ -6626,9 +6628,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
if( skill_id == GC_WEAPONCRUSH){
d = skill->get_time(skill_id,skill_lv);
if(bl->type == BL_PC)
- d += skill_lv * 15 + (sstatus->dex - tstatus->dex);
+ d += 1000 * ( skill_lv * 15 + ( sstatus->dex - tstatus->dex ) );
else
- d += skill_lv * 30 + (sstatus->dex - tstatus->dex) / 2;
+ d += 1000 * ( skill_lv * 30 + ( sstatus->dex - tstatus->dex ) / 2 );
}else
d = skill->get_time(skill_id,skill_lv) + (sstatus->dex - tstatus->dex)*500;
@@ -6949,7 +6951,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
}
clif->skill_nodamage(src,bl,TK_HIGHJUMP,skill_lv,1);
- if(!map->count_oncell(src->m,x,y,BL_PC|BL_NPC|BL_MOB) && map->getcell(src->m,x,y,CELL_CHKREACH)) {
+ if(!map->count_oncell(src->m,x,y,BL_PC|BL_NPC|BL_MOB,0) && map->getcell(src->m,x,y,CELL_CHKREACH)) {
clif->slide(src,x,y);
unit->movepos(src, x, y, 1, 0);
}
@@ -8326,7 +8328,7 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
if( flag&1 || (splash = skill->get_splash(skill_id, skill_lv)) < 1 ) {
int i;
//As of the behavior in official server Clearance is just a super version of Dispell skill. [Jobbie]
- if( bl->type != BL_MOB && battle->check_target(src,bl,BCT_PARTY) <= 0 ) // Only affect mob or party.
+ if( bl->type != BL_MOB && battle->check_target(src,bl,BCT_PARTY) <= 0 && sd ) // Only affect mob, party or self.
break;
clif->skill_nodamage(src,bl,skill_id,skill_lv,1);
@@ -8538,9 +8540,9 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
break;
case NC_SELFDESTRUCTION:
- if( sd ) {
- if( pc_ismadogear(sd) )
- pc->setmadogear(sd, 0);
+ if (sd) {
+ if (pc_ismadogear(sd))
+ pc->setmadogear(sd, false);
clif->skill_nodamage(src, bl, skill_id, skill_lv, 1);
skill->castend_damage_id(src, src, skill_id, skill_lv, tick, flag);
status->set_sp(src, 0, 0);
@@ -8932,10 +8934,13 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
}
break;
case SR_GENTLETOUCH_CHANGE:
- case SR_GENTLETOUCH_REVITALIZE:
clif->skill_nodamage(src,bl,skill_id,skill_lv,
sc_start2(src,bl,type,100,skill_lv,bl->id,skill->get_time(skill_id,skill_lv)));
break;
+ case SR_GENTLETOUCH_REVITALIZE:
+ clif->skill_nodamage(src,bl,skill_id,skill_lv,
+ sc_start2(src,bl,type,100,skill_lv,status_get_vit(src),skill->get_time(skill_id,skill_lv)));
+ break;
case SR_FLASHCOMBO:
{
const int combo[] = {
@@ -9446,17 +9451,16 @@ int skill_castend_nodamage_id(struct block_list *src, struct block_list *bl, uin
break;
case KO_KYOUGAKU:
- {
- int rate = max(5, (45 + 5 * skill_lv - status_get_int(bl) / 10));
- if( sd && !map_flag_gvg2(src->m) ){
- clif->skill_fail(sd, skill_id, USESKILL_FAIL_SIZE, 0);
- break;
- }
- if( dstsd && tsc && !tsc->data[type] && rand()%100 < rate ){
- clif->skill_nodamage(src, bl, skill_id, skill_lv,
- sc_start(src, bl, type, 100, skill_lv, skill->get_time(skill_id, skill_lv)));
- }else if( sd )
- clif->skill_fail(sd, skill_id, USESKILL_FAIL_LEVEL, 0);
+ if (!map_flag_vs(src->m) || !dstsd) {
+ if (sd) clif->skill_fail(sd, skill_id, USESKILL_FAIL_SIZE, 0);
+ break;
+ } else {
+ int time;
+ int rate = 45+ 5*skill_lv - status_get_int(bl)/10;
+ if (rate < 5) rate = 5;
+
+ time = skill->get_time(skill_id, skill_lv) - 1000*status_get_int(bl)/20;
+ sc_start(src,bl, type, rate, skill_lv, time);
}
break;
@@ -10077,7 +10081,6 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
case MG_THUNDERSTORM:
case AL_PNEUMA:
- case WZ_ICEWALL:
case WZ_FIREPILLAR:
case WZ_QUAGMIRE:
case WZ_VERMILION:
@@ -10186,6 +10189,11 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
sc_start(src,src,SC_NO_SWITCH_EQUIP,100,0,skill->get_time(skill_id,skill_lv));
skill->unitsetting(src,skill_id,skill_lv,x,y,0);
break;
+ case WZ_ICEWALL:
+ flag |= 1;
+ if( skill->unitsetting(src,skill_id,skill_lv,x,y,0) )
+ map->list[src->m].setcell(src->m, x, y, CELL_NOICEWALL, true);
+ break;
case RG_GRAFFITI: /* Graffiti [Valaris] */
skill->clear_unitgroup(src);
skill->unitsetting(src,skill_id,skill_lv,x,y,0);
@@ -10380,7 +10388,7 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
// Plant Cultivation [Celest]
case CR_CULTIVATION:
if (sd) {
- if( map->count_oncell(src->m,x,y,BL_CHAR) > 0 ) {
+ if( map->count_oncell(src->m,x,y,BL_CHAR,0) > 0 ) {
clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
return 1;
}
@@ -10599,33 +10607,17 @@ int skill_castend_pos2(struct block_list* src, int x, int y, uint16 skill_id, ui
case GN_CRAZYWEED:
{
int area = skill->get_splash(skill_id, skill_lv);
- short tmpx = 0, tmpy = 0, x1 = 0, y1 = 0;
for( r = 0; r < 3 + (skill_lv>>1); r++ ) {
// Creates a random Cell in the Splash Area
- tmpx = x - area + rnd()%(area * 2 + 1);
- tmpy = y - area + rnd()%(area * 2 + 1);
-
- if( r > 0 )
- skill->addtimerskill(src,tick+r*250,0,tmpx,tmpy,GN_CRAZYWEED,skill_lv,(x1<<16)|y1,flag);
+ int tmpx = x - area + rnd()%(area * 2 + 1);
+ int tmpy = y - area + rnd()%(area * 2 + 1);
- x1 = tmpx;
- y1 = tmpy;
+ skill_addtimerskill(src,tick+r*250,0,tmpx,tmpy,GN_CRAZYWEED_ATK,skill_lv,-1,0);
}
-
- skill->addtimerskill(src,tick+r*250,0,tmpx,tmpy,GN_CRAZYWEED,skill_lv,-1,flag);
}
break;
- case GN_CRAZYWEED_ATK: {
- int dummy = 1;
- //Enable if any unique animation gets added to this skill ID in the future. [Rytech]
- //clif_skill_poseffect(src,skillid,skilllv,x,y,tick);
- r = skill->get_splash(skill_id, skill_lv);
- map->foreachinarea(skill->cell_overlap, src->m, x-r, y-r, x+r, y+r, BL_SKILL, skill_id, &dummy, src);
- map->foreachinarea(skill->area_sub, src->m, x-r, y-r, x+r, y+r, BL_CHAR, src, skill_id, skill_lv, tick, flag|BCT_ENEMY|1, skill->castend_damage_id);
- }
- break;
case GN_FIRE_EXPANSION: {
int i;
struct unit_data *ud = unit->bl2ud(src);
@@ -10801,28 +10793,6 @@ bool skill_dance_switch(struct skill_unit* su, int flag) {
return true;
}
-/**
- * Upon Ice Wall cast it checks all nearby mobs to find any who may be blocked by the IW
- **/
-int skill_icewall_block(struct block_list *bl,va_list ap) {
- struct block_list *target = NULL;
- struct mob_data *md = ((TBL_MOB*)bl);
-
- nullpo_ret(bl);
- nullpo_ret(md);
- if( !md->target_id || ( target = map->id2bl(md->target_id) ) == NULL )
- return 0;
-
- if( path->search_long(NULL,bl->m,bl->x,bl->y,target->x,target->y,CELL_CHKICEWALL) )
- return 0;
-
- if( !check_distance_bl(bl, target, status_get_range(bl) ) ) {
- mob->unlocktarget(md,timer->gettick());
- mob_stop_walking(md,1);
- }
-
- return 0;
-}
/*==========================================
* Initializes and sets a ground skill.
* flag&1 is used to determine when the skill 'morphs' (Warp portal becomes active, or Fire Pillar becomes active)
@@ -11032,11 +11002,11 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_
val1 += pc->checkskill(sd,BA_MUSICALLESSON);
break;
case DC_SERVICEFORYOU:
- val1 = 15+skill_lv+(st->int_/10); // MaxSP percent increase TO-DO: this INT bonus value is guessed
+ val1 = 15+skill_lv+(st->int_/10); // MaxSP percent increase
val2 = 20+3*skill_lv+(st->int_/10); // SP cost reduction
if(sd){
- val1 += (pc->checkskill(sd,DC_DANCINGLESSON) + 1) / 2;
- val2 += (pc->checkskill(sd,DC_DANCINGLESSON) + 1) / 2;
+ val1 += pc->checkskill(sd,DC_DANCINGLESSON) / 2;
+ val2 += pc->checkskill(sd,DC_DANCINGLESSON) / 2;
}
break;
case BA_ASSASSINCROSS:
@@ -11322,9 +11292,6 @@ struct skill_unit_group* skill_unitsetting(struct block_list *src, uint16 skill_
//success, unit created.
switch( skill_id ) {
- case WZ_ICEWALL:
- map->foreachinrange(skill->icewall_block, src, AREA_SIZE, BL_MOB);
- break;
case NJ_TATAMIGAESHI: //Store number of tiles.
group->val1 = group->alive_count;
break;
@@ -11363,6 +11330,9 @@ int skill_unit_onplace(struct skill_unit *src, struct block_list *bl, int64 tick
if (sc && sc->data[SC_VACUUM_EXTREME] && map->getcell(bl->m, bl->x, bl->y, CELL_CHKLANDPROTECTOR))
status_change_end(bl, SC_VACUUM_EXTREME, INVALID_TIMER);
+ if ( sc && sc->data[SC_HOVERING] && ( sg->skill_id == SO_VACUUM_EXTREME || sg->skill_id == SO_ELECTRICWALK || sg->skill_id == SO_FIREWALK || sg->skill_id == WZ_QUAGMIRE ) )
+ return 0;
+
type = status->skill2sc(sg->skill_id);
sce = (sc && type != -1)?sc->data[type]:NULL;
skill_id = sg->skill_id; //In case the group is deleted, we need to return the correct skill id, still.
@@ -11583,9 +11553,6 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6
tsc = status->get_sc(bl);
ssc = status->get_sc(ss); // Status Effects for Unit caster.
- if ( tsc && tsc->data[SC_HOVERING] )
- return 0; //Under hovering characters are immune to trap and ground target skills.
-
// Maestro or Wanderer is unaffected by traps of trappers he or she charmed [SuperHulk]
if ( ssc && ssc->data[SC_SIREN] && ssc->data[SC_SIREN]->val2 == bl->id && (skill->get_inf2(sg->skill_id)&INF2_TRAP) )
return 0;
@@ -11594,6 +11561,15 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6
type = status->skill2sc(sg->skill_id);
skill_id = sg->skill_id;
+ if ( tsc && tsc->data[SC_HOVERING] ) {
+ switch ( skill_id ) {
+ case HT_SKIDTRAP: case HT_LANDMINE: case HT_ANKLESNARE: case HT_FLASHER: case HT_SHOCKWAVE:
+ case HT_SANDMAN: case HT_FREEZINGTRAP: case HT_BLASTMINE: case HT_CLAYMORETRAP: case HW_GRAVITATION:
+ case SA_DELUGE: case SA_VOLCANO: case SA_VIOLENTGALE: case NJ_SUITON:
+ return 0;
+ }
+ }
+
if (sg->interval == -1) {
switch (sg->unit_id) {
case UNT_ANKLESNARE: //These happen when a trap is splash-triggered by multiple targets on the same cell.
@@ -11615,7 +11591,7 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6
ts->tick = tick+sg->interval;
if ((skill_id==CR_GRANDCROSS || skill_id==NPC_GRANDDARKNESS) && !battle_config.gx_allhit)
- ts->tick += sg->interval*(map->count_oncell(bl->m,bl->x,bl->y,BL_CHAR)-1);
+ ts->tick += sg->interval*(map->count_oncell(bl->m,bl->x,bl->y,BL_CHAR,0)-1);
}
switch (sg->unit_id) {
@@ -12197,9 +12173,11 @@ int skill_unit_onplace_timer(struct skill_unit *src, struct block_list *bl, int6
sg->limit -= 100 * tstatus->str/20;
sc_start(ss, bl, SC_VACUUM_EXTREME, 100, sg->skill_lv, sg->limit);
- if (unit->movepos(bl, sg->val1, sg->val2, 0, 0)) {
- clif->slide(bl, sg->val1, sg->val2);
- clif->fixpos(bl);
+ if ( !map_flag_gvg(bl->m) && !map->list[bl->m].flag.battleground && !is_boss(bl) ) {
+ if (unit->movepos(bl, sg->val1, sg->val2, 0, 0)) {
+ clif->slide(bl, sg->val1, sg->val2);
+ clif->fixpos(bl);
+ }
}
}
break;
@@ -13199,17 +13177,6 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
}
}
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_id,USESKILL_FAIL_LEVEL,0);
- // return 0;
- // }
- // break;
case AB_ADORAMUS:
/**
@@ -13326,20 +13293,17 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
}
break;
case LG_RAYOFGENESIS:
+ case LG_HESPERUSLIT:
if( sc && sc->data[SC_INSPIRATION] )
return 1; // Don't check for partner.
if( !(sc && sc->data[SC_BANDING]) ) {
clif->skill_fail(sd,skill_id,USESKILL_FAIL,0);
return 0;
- } else if( skill->check_pc_partner(sd,skill_id,&skill_lv,skill->get_range(skill_id,skill_lv),0) < 1 )
+ }
+ if( sc->data[SC_BANDING] &&
+ sc->data[SC_BANDING]->val2 < (skill_id == LG_RAYOFGENESIS ? 2 : 3) )
return 0; // Just fails, no msg here.
break;
- case LG_HESPERUSLIT:
- if( !sc || !sc->data[SC_BANDING] ) {
- clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
- return 0;
- }
- break;
case SR_FALLENEMPIRE:
if( sc && sc->data[SC_COMBOATTACK] ) {
if( sc->data[SC_COMBOATTACK]->val1 == SR_DRAGONCOMBO )
@@ -13397,6 +13361,14 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
return 0;
}
break;
+ case NC_HOVERING:
+ if (( sd->equip_index[EQI_ACC_L] >= 0 && sd->status.inventory[sd->equip_index[EQI_ACC_L]].nameid == ITEMID_HOVERING_BOOSTER ) ||
+ ( sd->equip_index[EQI_ACC_R] >= 0 && sd->status.inventory[sd->equip_index[EQI_ACC_R]].nameid == ITEMID_HOVERING_BOOSTER ));
+ else {
+ clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
+ return 0;
+ }
+ break;
case SO_FIREWALK:
case SO_ELECTRICWALK: // Can't be casted until you've walked all cells.
if( sc && sc->data[SC_PROPERTYWALK] &&
@@ -13468,7 +13440,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
}
break;
case ST_RIDING:
- if(!pc_isriding(sd) && !pc_isridingdragon(sd)) {
+ if (!pc_isridingpeco(sd) && !pc_isridingdragon(sd)) {
clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
return 0;
}
@@ -13583,7 +13555,7 @@ int skill_check_condition_castbegin(struct map_session_data* sd, uint16 skill_id
return 0;
}
case ST_PECO:
- if(!pc_isriding(sd)) {
+ if (!pc_isridingpeco(sd)) {
clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
return 0;
}
@@ -14336,9 +14308,8 @@ int skill_vfcastfix(struct block_list *bl, double time, uint16 skill_id, uint16
break;
}
for( i = 0; i < ARRAYLENGTH(sd->skillfixcastrate) && sd->skillfixcastrate[i].id; i++ )
-
if( sd->skillfixcastrate[i].id == skill_id ){ // bonus2 bFixedCastrate
- fixcast_r = sd->skillfixcastrate[i].val; // just speculation
+ fixcast_r = sd->skillfixcastrate[i].val;
break;
}
}
@@ -15000,7 +14971,7 @@ int skill_frostjoke_scream(struct block_list *bl, va_list ap) {
return 0;
if (bl->type == BL_PC) {
struct map_session_data *sd = (struct map_session_data *)bl;
- if ( sd && sd->sc.option&(OPTION_INVISIBLE|OPTION_MADOGEAR) )
+ if (sd && (pc_isinvisible(sd) || pc_ismadogear(sd)))
return 0;//Frost Joke / Scream cannot target invisible or MADO Gear characters [Ind]
}
//It has been reported that Scream/Joke works the same regardless of woe-setting. [Skotlex]
@@ -15282,9 +15253,10 @@ int skill_cell_overlap(struct block_list *bl, va_list ap) {
break;
}
break;
+ case WZ_ICEWALL:
case HP_BASILICA:
- if (su->group->skill_id == HP_BASILICA) {
- //Basilica can't be placed on top of itself to avoid map-cell stacking problems. [Skotlex]
+ if (su->group->skill_id == skill_id) {
+ //These can't be placed on top of themselves (duration can't be refreshed)
(*alive) = 0;
return 1;
}
@@ -15602,7 +15574,6 @@ struct skill_unit *skill_initunit (struct skill_unit_group *group, int idx, int
map->setgatcell(su->bl.m,su->bl.x,su->bl.y,5);
clif->changemapcell(0,su->bl.m,su->bl.x,su->bl.y,5,AREA);
skill->unitsetmapcell(su,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,true);
- map->list[su->bl.m].icewall_num++;
break;
case SA_LANDPROTECTOR:
skill->unitsetmapcell(su,SA_LANDPROTECTOR,group->skill_lv,CELL_LANDPROTECTOR,true);
@@ -15651,10 +15622,10 @@ int skill_delunit (struct skill_unit* su) {
}
break;
case WZ_ICEWALL:
+ map->list[su->bl.m].setcell(su->bl.m, su->bl.x, su->bl.y, CELL_NOICEWALL, false);
map->setgatcell(su->bl.m,su->bl.x,su->bl.y,su->val2);
clif->changemapcell(0,su->bl.m,su->bl.x,su->bl.y,su->val2,ALL_SAMEMAP); // hack to avoid clientside cell bug
skill->unitsetmapcell(su,WZ_ICEWALL,group->skill_lv,CELL_ICEWALL,false);
- map->list[su->bl.m].icewall_num--;
// AS_CLOAKING in low levels requires a wall to be cast, thus it needs to be
// checked again when a wall disapears! issue:8182 [Panikon]
map->foreachinarea(skill->check_cloaking_end, su->bl.m,
@@ -15802,7 +15773,7 @@ int skill_delunitgroup(struct skill_unit_group *group, const char* file, int lin
return 0;
}
- if( !status->isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) {
+ if( src->type == BL_PC && !status->isdead(src) && ((TBL_PC*)src)->state.warping && !((TBL_PC*)src)->state.changemap ) {
switch( group->skill_id ) {
case BA_DISSONANCE:
case BA_POEMBRAGI:
@@ -17525,12 +17496,12 @@ int skill_blockpc_start_(struct map_session_data *sd, uint16 skill_id, int tick)
} else {
int i;
- for(i = 0; i < MAX_SKILL_TREE; i++) {
+ for(i = 0; i < cd->cursor; i++) {
if( cd->entry[i] && cd->entry[i]->skidx == idx )
break;
}
- if( i != MAX_SKILL_TREE ) {/* duplicate, update necessary */
+ if( i != cd->cursor ) {/* duplicate, update necessary */
cd->entry[i]->duration = tick;
#if PACKETVER >= 20120604
cd->entry[i]->total = tick;
@@ -18600,7 +18571,7 @@ int do_init_skill(bool minimal) {
skill->unit_ers = ers_new(sizeof(struct skill_unit_group),"skill.c::skill_unit_ers",ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
skill->timer_ers = ers_new(sizeof(struct skill_timerskill),"skill.c::skill_timer_ers",ERS_OPT_NONE|ERS_OPT_FLEX_CHUNK);
skill->cd_ers = ers_new(sizeof(struct skill_cd),"skill.c::skill_cd_ers",ERS_OPT_CLEAR|ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
- skill->cd_entry_ers = ers_new(sizeof(struct skill_cd_entry),"skill.c::skill_cd_entry_ers",ERS_OPT_CLEAR|ERS_OPT_FLEX_CHUNK);
+ skill->cd_entry_ers = ers_new(sizeof(struct skill_cd_entry),"skill.c::skill_cd_entry_ers",ERS_OPT_CLEAR|ERS_OPT_CLEAN|ERS_OPT_FLEX_CHUNK);
ers_chunk_size(skill->cd_ers, 25);
ers_chunk_size(skill->cd_entry_ers, 100);
@@ -18806,7 +18777,6 @@ void skill_defaults(void) {
skill->frostjoke_scream = skill_frostjoke_scream;
skill->greed = skill_greed;
skill->destroy_trap = skill_destroy_trap;
- skill->icewall_block = skill_icewall_block;
skill->unitgrouptickset_search = skill_unitgrouptickset_search;
skill->dance_switch = skill_dance_switch;
skill->check_condition_char_sub = skill_check_condition_char_sub;
diff --git a/src/map/skill.h b/src/map/skill.h
index 6373d9275..4ec742bd0 100644
--- a/src/map/skill.h
+++ b/src/map/skill.h
@@ -1984,7 +1984,6 @@ struct skill_interface {
int (*frostjoke_scream) (struct block_list *bl, va_list ap);
int (*greed) (struct block_list *bl, va_list ap);
int (*destroy_trap) ( struct block_list *bl, va_list ap );
- int (*icewall_block) (struct block_list *bl,va_list ap);
struct skill_unit_group_tickset *(*unitgrouptickset_search) (struct block_list *bl, struct skill_unit_group *group, int64 tick);
bool (*dance_switch) (struct skill_unit* su, int flag);
int (*check_condition_char_sub) (struct block_list *bl, va_list ap);
diff --git a/src/map/status.c b/src/map/status.c
index a8a0f066d..66f7db3fa 100644
--- a/src/map/status.c
+++ b/src/map/status.c
@@ -643,7 +643,7 @@ void initChangeTables(void) {
set_sc( SR_RAISINGDRAGON , SC_RAISINGDRAGON , SI_RAISINGDRAGON , SCB_REGEN|SCB_MAXHP|SCB_MAXSP );
set_sc( SR_GENTLETOUCH_ENERGYGAIN, SC_GENTLETOUCH_ENERGYGAIN , SI_GENTLETOUCH_ENERGYGAIN, SCB_NONE );
set_sc( SR_GENTLETOUCH_CHANGE , SC_GENTLETOUCH_CHANGE , SI_GENTLETOUCH_CHANGE , SCB_ASPD|SCB_MDEF|SCB_MAXHP );
- set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GENTLETOUCH_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_REGEN );
+ set_sc( SR_GENTLETOUCH_REVITALIZE, SC_GENTLETOUCH_REVITALIZE , SI_GENTLETOUCH_REVITALIZE, SCB_MAXHP|SCB_DEF2|SCB_REGEN );
set_sc( SR_FLASHCOMBO , SC_FLASHCOMBO , SI_FLASHCOMBO , SCB_WATK );
/**
* Wanderer / Minstrel
@@ -935,13 +935,16 @@ void initChangeTables(void) {
status->ChangeFlagTable[SC_INCHITRATE] |= SCB_HIT;
status->ChangeFlagTable[SC_INCFLEE] |= SCB_FLEE;
status->ChangeFlagTable[SC_INCFLEERATE] |= SCB_FLEE;
+ status->ChangeFlagTable[SC_MTF_HITFLEE] |= SCB_HIT|SCB_FLEE;
status->ChangeFlagTable[SC_CRITICALPERCENT] |= SCB_CRI;
status->ChangeFlagTable[SC_INCASPDRATE] |= SCB_ASPD;
status->ChangeFlagTable[SC_PLUSAVOIDVALUE] |= SCB_FLEE2;
status->ChangeFlagTable[SC_INCMHPRATE] |= SCB_MAXHP;
status->ChangeFlagTable[SC_INCMSPRATE] |= SCB_MAXSP;
status->ChangeFlagTable[SC_INCMHP] |= SCB_MAXHP;
+ status->ChangeFlagTable[SC_MTF_MHP] |= SCB_MAXHP;
status->ChangeFlagTable[SC_INCMSP] |= SCB_MAXSP;
+ status->ChangeFlagTable[SC_MTF_MSP] |= SCB_MAXSP;
status->ChangeFlagTable[SC_INCATKRATE] |= SCB_BATK|SCB_WATK;
status->ChangeFlagTable[SC_INCMATKRATE] |= SCB_MATK;
status->ChangeFlagTable[SC_INCDEFRATE] |= SCB_DEF;
@@ -1795,9 +1798,9 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, uin
//If targeting, cloak+hide protect you, otherwise only hiding does.
hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK);
- // There is no NF for ground skills, but every earth type skill out there
- // affects hidding except Stone Curse
- if( skill->get_ele(skill_id,1) == ELE_EARTH && skill_id != MG_STONECURSE)
+ // Applies even if the target hides
+ if ((skill->get_ele(skill_id,1) == ELE_EARTH && skill_id != MG_STONECURSE) // Ground type
+ || (flag&1 && skill->get_nk(skill_id)&NK_NO_DAMAGE && skill_id != ALL_RESURRECTION)) // Buff/debuff skills started before hiding
hide_flag &= ~OPTION_HIDE;
switch( target->type ) {
@@ -1809,7 +1812,6 @@ int status_check_skilluse(struct block_list *src, struct block_list *target, uin
return 0;
if( tsc ) {
if (tsc->option&hide_flag && !is_boss &&
- !(flag&1 && skill->get_nk(skill_id)&NK_NO_DAMAGE) && // Buff/debuff skills that started casting before hiding still applies
((sd->special_state.perfect_hiding || !is_detect) ||
(tsc->data[SC_CLOAKINGEXCEED] && is_detect)))
return 0;
@@ -2485,13 +2487,14 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) {
bstatus->mode = MD_MASK&~(MD_BOSS|MD_PLANT|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK);
bstatus->size = (sd->class_&JOBL_BABY)?SZ_SMALL:SZ_MEDIUM;
- if (battle_config.character_size && (pc_isriding(sd) || pc_isridingdragon(sd)) ) { //[Lupus]
+ if (battle_config.character_size && (pc_isridingpeco(sd) || pc_isridingdragon(sd))) { //[Lupus]
if (sd->class_&JOBL_BABY) {
if (battle_config.character_size&SZ_BIG)
bstatus->size++;
- } else
+ } else {
if(battle_config.character_size&SZ_MEDIUM)
bstatus->size++;
+ }
}
bstatus->aspd_rate = 1000;
bstatus->ele_lv = 1;
@@ -2780,9 +2783,10 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) {
sd->left_weapon.atkmods[1] = status->atkmods[1][sd->weapontype2];
sd->left_weapon.atkmods[2] = status->atkmods[2][sd->weapontype2];
- if( (pc_isriding(sd) || pc_isridingdragon(sd)) &&
- (sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR))
- { //When Riding with spear, damage modifier to mid-class becomes
+ if ((pc_isridingpeco(sd) || pc_isridingdragon(sd))
+ && (sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR)
+ ) {
+ //When Riding with spear, damage modifier to mid-class becomes
//same as versus large size.
sd->right_weapon.atkmods[1] = sd->right_weapon.atkmods[2];
sd->left_weapon.atkmods[1] = sd->left_weapon.atkmods[2];
@@ -3062,9 +3066,9 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) {
if((skill_lv=pc->checkskill(sd,GS_SINGLEACTION))>0 &&
(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
bstatus->aspd_rate -= ((skill_lv+1)/2) * 10;
- if(pc_isriding(sd))
+ if (pc_isridingpeco(sd))
bstatus->aspd_rate += 500-100*pc->checkskill(sd,KN_CAVALIERMASTERY);
- else if(pc_isridingdragon(sd))
+ else if (pc_isridingdragon(sd))
bstatus->aspd_rate += 250-50*pc->checkskill(sd,RK_DRAGONTRAINING);
#else // needs more info
if((skill_lv=pc->checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
@@ -3074,9 +3078,9 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) {
if((skill_lv=pc->checkskill(sd,GS_SINGLEACTION))>0 &&
(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
bstatus->aspd_rate += ((skill_lv+1)/2) * 10;
- if(pc_isriding(sd))
+ if (pc_isridingpeco(sd))
bstatus->aspd_rate -= 500-100*pc->checkskill(sd,KN_CAVALIERMASTERY);
- else if(pc_isridingdragon(sd))
+ else if (pc_isridingdragon(sd))
bstatus->aspd_rate -= 250-50*pc->checkskill(sd,RK_DRAGONTRAINING);
#endif
bstatus->adelay = 2*bstatus->amotion;
@@ -3094,7 +3098,7 @@ int status_calc_pc_(struct map_session_data* sd, enum e_status_calc_opt opt) {
// Weight
if((skill_lv=pc->checkskill(sd,MC_INCCARRY))>0)
sd->max_weight += 2000*skill_lv;
- if(pc_isriding(sd) && pc->checkskill(sd,KN_RIDING)>0)
+ if (pc_isridingpeco(sd) && pc->checkskill(sd,KN_RIDING) > 0)
sd->max_weight += 10000;
else if(pc_isridingdragon(sd))
sd->max_weight += 5000+2000*pc->checkskill(sd,RK_DRAGONTRAINING);
@@ -3603,8 +3607,7 @@ void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, str
regen->flag&=~sce->val4; //Remove regen as specified by val4
}
if(sc->data[SC_GENTLETOUCH_REVITALIZE]) {
- regen->hp = cap_value(regen->hp*sc->data[SC_GENTLETOUCH_REVITALIZE]->val3/100, 1, SHRT_MAX);
- regen->state.walk= 1;
+ regen->hp += regen->hp * ( 30 * sc->data[SC_GENTLETOUCH_REVITALIZE]->val1 + 50 ) / 100;
}
if ((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1) //if insignia lvl 1
|| (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
@@ -4232,7 +4235,7 @@ unsigned short status_calc_str(struct block_list *bl, struct status_change *sc,
if(sc->data[SC_STOMACHACHE])
str -= sc->data[SC_STOMACHACHE]->val1;
if(sc->data[SC_KYOUGAKU])
- str -= sc->data[SC_KYOUGAKU]->val2;
+ str -= sc->data[SC_KYOUGAKU]->val3;
if(sc->data[SC_FULL_THROTTLE])
str += str * 20 / 100;
@@ -4287,7 +4290,7 @@ unsigned short status_calc_agi(struct block_list *bl, struct status_change *sc,
if(sc->data[SC_STOMACHACHE])
agi -= sc->data[SC_STOMACHACHE]->val1;
if(sc->data[SC_KYOUGAKU])
- agi -= sc->data[SC_KYOUGAKU]->val2;
+ agi -= sc->data[SC_KYOUGAKU]->val3;
if(sc->data[SC_MARSHOFABYSS])
agi -= agi * sc->data[SC_MARSHOFABYSS]->val2 / 100;
@@ -4335,7 +4338,7 @@ unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc,
if(sc->data[SC_STOMACHACHE])
vit -= sc->data[SC_STOMACHACHE]->val1;
if(sc->data[SC_KYOUGAKU])
- vit -= sc->data[SC_KYOUGAKU]->val2;
+ vit -= sc->data[SC_KYOUGAKU]->val3;
if(sc->data[SC_NOEQUIPARMOR])
vit -= vit * sc->data[SC_NOEQUIPARMOR]->val2/100;
@@ -4393,7 +4396,7 @@ unsigned short status_calc_int(struct block_list *bl, struct status_change *sc,
if(sc->data[SC_STOMACHACHE])
int_ -= sc->data[SC_STOMACHACHE]->val1;
if(sc->data[SC_KYOUGAKU])
- int_ -= sc->data[SC_KYOUGAKU]->val2;
+ int_ -= sc->data[SC_KYOUGAKU]->val3;
if(bl->type != BL_PC){
if(sc->data[SC_NOEQUIPHELM])
@@ -4455,7 +4458,7 @@ unsigned short status_calc_dex(struct block_list *bl, struct status_change *sc,
if(sc->data[SC_STOMACHACHE])
dex -= sc->data[SC_STOMACHACHE]->val1;
if(sc->data[SC_KYOUGAKU])
- dex -= sc->data[SC_KYOUGAKU]->val2;
+ dex -= sc->data[SC_KYOUGAKU]->val3;
if(sc->data[SC_MARSHOFABYSS])
dex -= dex * sc->data[SC_MARSHOFABYSS]->val2 / 100;
@@ -4503,7 +4506,7 @@ unsigned short status_calc_luk(struct block_list *bl, struct status_change *sc,
if(sc->data[SC_STOMACHACHE])
luk -= sc->data[SC_STOMACHACHE]->val1;
if(sc->data[SC_KYOUGAKU])
- luk -= sc->data[SC_KYOUGAKU]->val2;
+ luk -= sc->data[SC_KYOUGAKU]->val3;
if(sc->data[SC_LAUDARAMUS])
luk += 4 + sc->data[SC_LAUDARAMUS]->val1;
@@ -4712,6 +4715,8 @@ unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc,
if( !viewable ){
/* some statuses that are hidden in the status window */
+ if (sc->data[SC_MINDBREAKER])
+ matk += matk * sc->data[SC_MINDBREAKER]->val2/100;
return (unsigned short)cap_value(matk,0,USHRT_MAX);
}
@@ -4736,8 +4741,6 @@ unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc,
if (sc->data[SC_IZAYOI])
matk += 25 * sc->data[SC_IZAYOI]->val1;
#endif
- if (sc->data[SC_MINDBREAKER])
- matk += matk * sc->data[SC_MINDBREAKER]->val2/100;
if( sc->data[SC_ZANGETSU] )
matk += sc->data[SC_ZANGETSU]->val3;
if (sc->data[SC_MAGICPOWER] && sc->data[SC_MAGICPOWER]->val4)
@@ -4804,6 +4807,8 @@ signed short status_calc_hit(struct block_list *bl, struct status_change *sc, in
if(sc->data[SC_INCHIT])
hit += sc->data[SC_INCHIT]->val1;
+ if(sc->data[SC_MTF_HITFLEE])
+ hit += sc->data[SC_MTF_HITFLEE]->val1;
if(sc->data[SC_FOOD_BASICHIT])
hit += sc->data[SC_FOOD_BASICHIT]->val1;
if(sc->data[SC_TRUESIGHT])
@@ -4855,6 +4860,8 @@ signed short status_calc_flee(struct block_list *bl, struct status_change *sc, i
if(sc->data[SC_INCFLEE])
flee += sc->data[SC_INCFLEE]->val1;
+ if(sc->data[SC_MTF_HITFLEE])
+ flee += sc->data[SC_MTF_HITFLEE]->val2;
if(sc->data[SC_FOOD_BASICAVOIDANCE])
flee += sc->data[SC_FOOD_BASICAVOIDANCE]->val1;
if(sc->data[SC_WHISTLE])
@@ -4955,8 +4962,6 @@ defType status_calc_def(struct block_list *bl, struct status_change *sc, int def
def -= def * 50 / 100;
if( sc->data[SC_NEUTRALBARRIER] )
def += def * (10 + 5*sc->data[SC_NEUTRALBARRIER]->val1) / 100;
- if( sc && sc->data[SC_GENTLETOUCH_REVITALIZE] && sc->data[SC_GENTLETOUCH_REVITALIZE]->val4 )
- def += 2 * sc->data[SC_GENTLETOUCH_REVITALIZE]->val4;
if( sc->data[SC_FORCEOFVANGUARD] )
def += def * 2 * sc->data[SC_FORCEOFVANGUARD]->val1 / 100;
if(sc->data[SC_DEFSET])
@@ -5060,6 +5065,8 @@ signed short status_calc_def2(struct block_list *bl, struct status_change *sc, i
#endif
if( sc && sc->data[SC_CAMOUFLAGE] )
def2 -= def2 * 5 * (10-sc->data[SC_CAMOUFLAGE]->val4) / 100;
+ if(sc->data[SC_GENTLETOUCH_REVITALIZE])
+ def2 += sc->data[SC_GENTLETOUCH_REVITALIZE]->val2;
if(sc->data[SC_DEFSET])
return sc->data[SC_DEFSET]->val1;
#ifdef RENEWAL
@@ -5232,16 +5239,16 @@ unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc
{
int val = 0;
- if( sc->data[SC_FUSION] )
+ if(sc->data[SC_FUSION]) {
val = 25;
- else if( sd ) {
- if( pc_isriding(sd) || sd->sc.option&(OPTION_DRAGON) || sd->sc.data[SC_ALL_RIDING] )
+ } else if (sd) {
+ if (pc_isridingpeco(sd) || pc_isridingdragon(sd) || sd->sc.data[SC_ALL_RIDING])
val = 25;//Same bonus
- else if( pc_isridingwug(sd) )
+ else if (pc_isridingwug(sd))
val = 15 + 5 * pc->checkskill(sd, RA_WUGRIDER);
- else if( pc_ismadogear(sd) ) {
+ else if (pc_ismadogear(sd)) {
val = (- 10 * (5 - pc->checkskill(sd,NC_MADOLICENCE)));
- if( sc->data[SC_ACCELERATION] )
+ if (sc->data[SC_ACCELERATION])
val += 25;
}
}
@@ -5699,6 +5706,8 @@ unsigned int status_calc_maxhp(struct block_list *bl, struct status_change *sc,
maxhp += maxhp * sc->data[SC_INCMHPRATE]->val1/100;
if(sc->data[SC_INCMHP])
maxhp += (sc->data[SC_INCMHP]->val1);
+ if(sc->data[SC_MTF_MHP])
+ maxhp += (sc->data[SC_MTF_MHP]->val1);
if(sc->data[SC_APPLEIDUN])
maxhp += maxhp * sc->data[SC_APPLEIDUN]->val2/100;
if(sc->data[SC_DELUGE])
@@ -5766,6 +5775,8 @@ unsigned int status_calc_maxsp(struct block_list *bl, struct status_change *sc,
maxsp += maxsp * sc->data[SC_INCMSPRATE]->val1/100;
if(sc->data[SC_INCMSP])
maxsp += (sc->data[SC_INCMSP]->val1);
+ if(sc->data[SC_MTF_MSP])
+ maxsp += (sc->data[SC_MTF_MSP]->val1);
if(sc->data[SC_SERVICEFORYOU])
maxsp += maxsp * sc->data[SC_SERVICEFORYOU]->val2/100;
if(sc->data[SC_MER_SP])
@@ -6014,7 +6025,9 @@ int status_get_party_id(struct block_list *bl) {
return ((TBL_MER*)bl)->master->status.party_id;
break;
case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->party_id;
+ if (((TBL_SKILL*)bl)->group)
+ return ((TBL_SKILL*)bl)->group->party_id;
+ break;
case BL_ELEM:
if (((TBL_ELEM*)bl)->master)
return ((TBL_ELEM*)bl)->master->status.party_id;
@@ -6057,7 +6070,9 @@ int status_get_guild_id(struct block_list *bl) {
return ((TBL_NPC*)bl)->u.scr.guild_id;
break;
case BL_SKILL:
- return ((TBL_SKILL*)bl)->group->guild_id;
+ if (((TBL_SKILL*)bl)->group)
+ return ((TBL_SKILL*)bl)->group->guild_id;
+ break;
case BL_ELEM:
if (((TBL_ELEM*)bl)->master)
return ((TBL_ELEM*)bl)->master->status.guild_id;
@@ -6179,7 +6194,7 @@ void status_set_viewdata(struct block_list *bl, int class_)
{
TBL_PC* sd = (TBL_PC*)bl;
if (pcdb_checkid(class_)) {
- if (sd->sc.option&OPTION_RIDING) {
+ if (pc_isridingpeco(sd)) {
switch (class_) { //Adapt class to a Mounted one.
case JOB_KNIGHT:
class_ = JOB_KNIGHT2;
@@ -6559,9 +6574,6 @@ int status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_typ
case SC_SIREN:
tick_def2 = (status->get_lv(bl) * 100) + ((bl->type == BL_PC)?((TBL_PC*)bl)->status.job_level : 0);
break;
- case SC_KYOUGAKU:
- tick_def2 = st->int_ * 50;
- break;
case SC_NEEDLE_OF_PARALYZE:
tick_def2 = (st->vit + st->luk) * 50;
break;
@@ -6876,7 +6888,7 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
)
return 0;
case SC_VACUUM_EXTREME:
- if(sc->data[SC_HALLUCINATIONWALK] || sc->data[SC_HOVERING])
+ if(sc->data[SC_HALLUCINATIONWALK])
return 0;
break;
case SC_STONE:
@@ -7885,8 +7897,8 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
case SC_RUWACH:
case SC_WZ_SIGHTBLASTER:
val3 = skill->get_splash(val2, val1); //Val2 should bring the skill-id.
- val2 = tick/250;
- tick_time = 10; // [GodLesZ] tick time
+ val2 = tick/20;
+ tick_time = 20; // [GodLesZ] tick time
break;
//Permanent effects.
@@ -8585,14 +8597,22 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
val2 = 20 + 10 * val1; //ASPD. Need to confirm if Movement Speed reduction is the same. [Jobbie]
val3 = 20 * val1; //HIT
if( sd ) { // Removes Animals
- if( pc_isriding(sd) ) pc->setriding(sd, 0);
- if( pc_isridingdragon(sd) ) pc->setoption(sd, sd->sc.option&~OPTION_DRAGON);
- if( pc_iswug(sd) ) pc->setoption(sd, sd->sc.option&~OPTION_WUG);
- if( pc_isridingwug(sd) ) pc->setoption(sd, sd->sc.option&~OPTION_WUGRIDER);
- if( pc_isfalcon(sd) ) pc->setoption(sd, sd->sc.option&~OPTION_FALCON);
- if( sd->status.pet_id > 0 ) pet->menu(sd, 3);
- if( homun_alive(sd->hd) ) homun->vaporize(sd,HOM_ST_REST);
- if( sd->md ) mercenary->delete(sd->md,3);
+ if (pc_isridingpeco(sd))
+ pc->setridingpeco(sd, false);
+ if (pc_isridingdragon(sd))
+ pc->setridingdragon(sd, 0);
+ if (pc_iswug(sd))
+ pc->setoption(sd, sd->sc.option&~OPTION_WUG);
+ if (pc_isridingwug(sd))
+ pc->setridingwug(sd, false);
+ if (pc_isfalcon(sd))
+ pc->setfalcon(sd, false);
+ if (sd->status.pet_id > 0)
+ pet->menu(sd, 3);
+ if (homun_alive(sd->hd))
+ homun->vaporize(sd,HOM_ST_REST);
+ if (sd->md)
+ mercenary->delete(sd->md,3);
}
break;
case SC__LAZINESS:
@@ -8689,8 +8709,10 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
if ( !val3 )
val3 = 50;
if( sd ) {
- if( pc_isriding(sd) ) pc->setriding(sd, 0);
- if( pc_isridingdragon(sd) ) pc->setoption(sd, sd->sc.option&~OPTION_DRAGON);
+ if (pc_isridingpeco(sd))
+ pc->setridingpeco(sd, false);
+ if (pc_isridingdragon(sd))
+ pc->setridingdragon(sd, false);
}
}
break;
@@ -8786,18 +8808,16 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
struct block_list * src2;
val3 = st->agi * val1 / 60; // ASPD increase: [(Target AGI x Skill Level) / 60] %
if( (src2 = map->id2bl(val2)) ){
- val4 = ( 200/status_get_int(src2)?status_get_int(src2):1 ) * val1;// MDEF decrease: MDEF [(200 / Caster INT) x Skill Level]
+ val4 = ( 200/(status_get_int(src2)?status_get_int(src2):1) ) * val1;// MDEF decrease: MDEF [(200 / Caster INT) x Skill Level]
val2 = ( status_get_dex(src2)/4 + status_get_str(src2)/2 ) * val1 / 5; // ATK increase: ATK [{(Caster DEX / 4) + (Caster STR / 2)} x Skill Level / 5]
}
}
break;
case SC_GENTLETOUCH_REVITALIZE:
- {// take note there is no vit,aspd,speed increase as skill desc says. [malufett]
- struct block_list * src2;
- val3 = val1 * 30 + 150; // Natural HP recovery increase: [(Skill Level x 30) + 50] %
- if( (src2 = map->id2bl(val2)) ) // the stat def is not shown in the status window and it is process differently
- val4 = ( status_get_vit(src2)/4 ) * val1; // STAT DEF increase: [(Caster VIT / 4) x Skill Level]
- }
+ if(val2 < 0)
+ val2 = 0;
+ else
+ val2 = val2 / 4 * val1; // STAT DEF increase: [(Caster VIT / 4) x Skill Level]
break;
case SC_PYROTECHNIC_OPTION:
val2 = 60;
@@ -8910,9 +8930,13 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
val4 = tick / 10000;
tick_time = 10000; // [GodLesZ] tick time
break;
- case SC_KYOUGAKU:
- val2 = 2*val1 + rand()%(3 * val1);
- clif->status_change(bl, SI_ACTIVE_MONSTER_TRANSFORM, 1, 0, 1002, 0, 0); // Poring in disguise
+ case SC_KYOUGAKU: {
+ int min = val1*2;
+ int max = val1*3;
+ val3 = rnd()%(max-min)+min;
+ val2 = val1;
+ val1 = 1002; // Monster ID
+ }
break;
case SC_KAGEMUSYA:
val3 = val1 * 2;
@@ -9059,9 +9083,6 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
case SC_KAAHI:
val4 = INVALID_TIMER;
break;
- case SC_KYOUGAKU:
- clif->status_change(bl, SI_ACTIVE_MONSTER_TRANSFORM, 1, 0, 1002, 0, 0); // Poring in disguise
- break;
}
}
@@ -9182,6 +9203,9 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
case SC_WATER_BARRIER:
val_flag |= 1|2|4;
break;
+ case SC_KYOUGAKU:
+ val_flag |= 1;
+ break;
case SC_CASH_PLUSEXP:
case SC_CASH_PLUSONLYJOBEXP:
case SC_MONSTER_TRANSFORM:
@@ -9240,7 +9264,6 @@ int status_change_start(struct block_list *src, struct block_list *bl, enum sc_t
case SC_CURSEDCIRCLE_TARGET:
case SC_FEAR:
case SC_MEIKYOUSISUI:
- case SC_KYOUGAKU:
case SC_NEEDLE_OF_PARALYZE:
case SC_DEATHBOUND:
unit->stop_walking(bl,1);
@@ -10133,9 +10156,6 @@ int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const
}
}
break;
- case SC_KYOUGAKU:
- clif->sc_end(&sd->bl,sd->bl.id,AREA,SI_ACTIVE_MONSTER_TRANSFORM);
- break;
case SC_CLAIRVOYANCE:
calc_flag = SCB_ALL;/* required for overlapping */
break;
@@ -10516,14 +10536,17 @@ int status_change_timer(int tid, int64 tick, int id, intptr_t data) {
case SC_SIGHT:
case SC_RUWACH:
case SC_WZ_SIGHTBLASTER:
- if(type == SC_WZ_SIGHTBLASTER)
+ if(type == SC_WZ_SIGHTBLASTER) {
+ //Restore trap immunity
+ if(sce->val4%2)
+ sce->val4--;
map->foreachinrange(status->change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick);
- else
+ } else
map->foreachinrange(status->change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick);
if( --(sce->val2)>0 ){
- sce->val4 += 250; // use for Shadow Form 2 seconds checking.
- sc_timer_next(250+tick, status->change_timer, bl->id, data);
+ sce->val4 += 20; // use for Shadow Form 2 seconds checking.
+ sc_timer_next(20+tick, status->change_timer, bl->id, data);
return 0;
}
break;
@@ -11318,10 +11341,13 @@ int status_change_timer_sub(struct block_list* bl, va_list ap) {
if (battle->check_target( src, bl, BCT_ENEMY ) > 0
&& status->check_skilluse(src, bl, WZ_SIGHTBLASTER, 2)
) {
- if (sce && !(bl->type&BL_SKILL) //The hit is not counted if it's against a trap
- && skill->attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,1,tick,0)
- ){
- sce->val2 = 0; //This signals it to end.
+ struct skill_unit *su = (struct skill_unit *)bl;
+ if (sce && skill->attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,sce->val1,tick,0x4000)
+ && (!su || !su->group || !(skill->get_inf2(su->group->skill_id)&INF2_TRAP))) { // The hit is not counted if it's against a trap
+ sce->val2 = 0; // This signals it to end.
+ } else if ((bl->type&BL_SKILL) && sce && sce->val4%2 == 0) {
+ //Remove trap immunity temporarily so it triggers if you still stand on it
+ sce->val4++;
}
}
break;
@@ -11368,7 +11394,7 @@ int status_get_weapon_atk(struct block_list *bl, struct weapon_atk *watk, int fl
if( !(flag&1) ){
if( max > min )
- max = min + rnd()%(max - min);
+ max = min + rnd()%(max - min + 1);
else
max = min;
}
diff --git a/src/map/status.h b/src/map/status.h
index 623ba7eb3..e96894e9f 100644
--- a/src/map/status.h
+++ b/src/map/status.h
@@ -730,7 +730,7 @@ typedef enum sc_type {
SC__FEINTBOMB_MASTER,
SC_FALLENEMPIRE,
- SC_FLASHCOMBO,
+ SC_FLASHCOMBO, // 580
//Vellum Weapon reductions
SC_DEFSET,
@@ -738,6 +738,12 @@ typedef enum sc_type {
SC_NO_SWITCH_EQUIP,
+ // 2014 Halloween Event
+ SC_MTF_MHP,
+ SC_MTF_MSP,
+ SC_MTF_PUMPKIN,
+ SC_MTF_HITFLEE,
+
SC_MAX, //Automatically updated max, used in for's to check we are within bounds.
} sc_type;
diff --git a/src/map/storage.c b/src/map/storage.c
index 217f14a3a..523f64cc8 100644
--- a/src/map/storage.c
+++ b/src/map/storage.c
@@ -351,7 +351,7 @@ DBData create_guildstorage(DBKey key, va_list args)
return DB->ptr2data(gs);
}
-struct guild_storage *guild2storage(int guild_id)
+struct guild_storage *guild2storage_ensure(int guild_id)
{
struct guild_storage *gs = NULL;
if(guild->search(guild_id) != NULL)
@@ -359,11 +359,6 @@ struct guild_storage *guild2storage(int guild_id)
return gs;
}
-//For just locating a storage without creating one. [Skotlex]
-struct guild_storage *guild2storage2(int guild_id) {
- return (struct guild_storage*)idb_get(gstorage->db,guild_id);
-}
-
int guild_storage_delete(int guild_id) {
idb_remove(gstorage->db,guild_id);
return 0;
@@ -393,7 +388,7 @@ int storage_guild_storageopen(struct map_session_data* sd)
return 1;
}
- if((gstor = gstorage->id2storage2(sd->status.guild_id)) == NULL) {
+ if((gstor = idb_get(gstorage->db,sd->status.guild_id)) == NULL) {
intif->request_guild_storage(sd->status.account_id,sd->status.guild_id);
return 0;
}
@@ -511,7 +506,7 @@ int storage_guild_storageadd(struct map_session_data* sd, int index, int amount)
struct guild_storage *stor;
nullpo_ret(sd);
- nullpo_ret(stor=gstorage->id2storage2(sd->status.guild_id));
+ nullpo_ret(stor=idb_get(gstorage->db,sd->status.guild_id));
if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE )
return 0;
@@ -551,7 +546,7 @@ int storage_guild_storageget(struct map_session_data* sd, int index, int amount)
int flag;
nullpo_ret(sd);
- nullpo_ret(stor=guild2storage2(sd->status.guild_id));
+ nullpo_ret(stor=idb_get(gstorage->db,sd->status.guild_id));
if(!stor->storage_status)
return 0;
@@ -591,7 +586,7 @@ int storage_guild_storageaddfromcart(struct map_session_data* sd, int index, int
struct guild_storage *stor;
nullpo_ret(sd);
- nullpo_ret(stor=guild2storage2(sd->status.guild_id));
+ nullpo_ret(stor=idb_get(gstorage->db,sd->status.guild_id));
if( !stor->storage_status || stor->storage_amount > MAX_GUILD_STORAGE )
return 0;
@@ -623,7 +618,7 @@ int storage_guild_storagegettocart(struct map_session_data* sd, int index, int a
struct guild_storage *stor;
nullpo_ret(sd);
- nullpo_ret(stor=guild2storage2(sd->status.guild_id));
+ nullpo_ret(stor=idb_get(gstorage->db,sd->status.guild_id));
if(!stor->storage_status)
return 0;
@@ -651,7 +646,7 @@ int storage_guild_storagegettocart(struct map_session_data* sd, int index, int a
*------------------------------------------*/
int storage_guild_storagesave(int account_id, int guild_id, int flag)
{
- struct guild_storage *stor = guild2storage2(guild_id);
+ struct guild_storage *stor = idb_get(gstorage->db,guild_id);
if(stor)
{
@@ -674,7 +669,7 @@ int storage_guild_storagesaved(int guild_id)
{
struct guild_storage *stor;
- if((stor=gstorage->id2storage2(guild_id)) != NULL) {
+ if((stor=idb_get(gstorage->db,guild_id)) != NULL) {
if (stor->dirty && stor->storage_status == 0)
{ //Storage has been correctly saved.
stor->dirty = 0;
@@ -689,7 +684,7 @@ int storage_guild_storageclose(struct map_session_data* sd) {
struct guild_storage *stor;
nullpo_ret(sd);
- nullpo_ret(stor=gstorage->id2storage2(sd->status.guild_id));
+ nullpo_ret(stor=idb_get(gstorage->db,sd->status.guild_id));
clif->storageclose(sd);
if (stor->storage_status) {
@@ -708,7 +703,7 @@ int storage_guild_storage_quit(struct map_session_data* sd, int flag) {
struct guild_storage *stor;
nullpo_ret(sd);
- nullpo_ret(stor=gstorage->id2storage2(sd->status.guild_id));
+ nullpo_ret(stor=idb_get(gstorage->db,sd->status.guild_id));
if(flag) {
//Only during a guild break flag is 1 (don't save storage)
@@ -765,8 +760,7 @@ void gstorage_defaults(void) {
gstorage->init = do_init_gstorage;
gstorage->final = do_final_gstorage;
/* */
- gstorage->id2storage = guild2storage;
- gstorage->id2storage2 = guild2storage2;
+ gstorage->ensure = guild2storage_ensure;
gstorage->delete = guild_storage_delete;
gstorage->open = storage_guild_storageopen;
gstorage->additem = guild_storage_additem;
diff --git a/src/map/storage.h b/src/map/storage.h
index 186f21263..fcf9a52e4 100644
--- a/src/map/storage.h
+++ b/src/map/storage.h
@@ -34,8 +34,7 @@ struct storage_interface *storage;
struct guild_storage_interface {
struct DBMap* db; // int guild_id -> struct guild_storage*
/* */
- struct guild_storage *(*id2storage) (int guild_id);
- struct guild_storage *(*id2storage2) (int guild_id);
+ struct guild_storage *(*ensure) (int guild_id);
/* */
void (*init) (bool minimal);
void (*final) (void);
diff --git a/src/map/unit.c b/src/map/unit.c
index 1f81e0d2f..044d7a43c 100644
--- a/src/map/unit.c
+++ b/src/map/unit.c
@@ -95,6 +95,8 @@ int unit_walktoxy_sub(struct block_list *bl)
ud = unit->bl2ud(bl);
if(ud == NULL) return 0;
+ memset(&wpd, 0, sizeof(wpd));
+
if( !path->search(&wpd,bl->m,bl->x,bl->y,ud->to_x,ud->to_y,ud->state.walk_easy,CELL_CHKNOPASS) )
return 0;
@@ -106,11 +108,11 @@ int unit_walktoxy_sub(struct block_list *bl)
uint8 dir;
//Trim the last part of the path to account for range,
//but always move at least one cell when requested to move.
- for (i = ud->chaserange*10; i > 0 && ud->walkpath.path_len>1;) {
+ for (i = (ud->chaserange*10)-10; i > 0 && ud->walkpath.path_len>1;) {
ud->walkpath.path_len--;
dir = ud->walkpath.path[ud->walkpath.path_len];
if(dir&1)
- i -= MOVE_DIAGONAL_COST;
+ i -= MOVE_COST*20; //When chasing, units will target a diamond-shaped area in range [Playtester]
else
i -= MOVE_COST;
ud->to_x -= dirx[dir];
@@ -137,9 +139,79 @@ int unit_walktoxy_sub(struct block_list *bl)
return 1;
}
+/**
+ * Triggered on full step if stepaction is true and executes remembered action.
+ * @param tid: Timer ID
+ * @param tick: Unused
+ * @param id: ID of bl to do the action
+ * @param data: Not used
+ * @return 1: Success 0: Fail (No valid bl)
+ */
+int unit_step_timer(int tid, int64 tick, int id, intptr_t data)
+{
+ struct block_list *bl;
+ struct unit_data *ud;
+ int target_id;
+
+ bl = map->id2bl(id);
+
+ if (!bl || bl->prev == NULL)
+ return 0;
+
+ ud = unit_bl2ud(bl);
+
+ if(!ud)
+ return 0;
+
+ if(ud->steptimer != tid) {
+ ShowError("unit_step_timer mismatch %d != %d\n",ud->steptimer,tid);
+ return 0;
+ }
+
+ ud->steptimer = INVALID_TIMER;
+
+ if(!ud->stepaction)
+ return 0;
+
+ //Set to false here because if an error occurs, it should not be executed again
+ ud->stepaction = false;
+
+ if(!ud->target_to)
+ return 0;
+
+ //Flush target_to as it might contain map coordinates which should not be used by other functions
+ target_id = ud->target_to;
+ ud->target_to = 0;
+
+ //If stepaction is set then we remembered a client request that should be executed on the next step
+ //Execute request now if target is in attack range
+ if(ud->stepskill_id && skill->get_inf(ud->stepskill_id) & INF_GROUND_SKILL) {
+ //Execute ground skill
+ struct map_data *md = &map->list[bl->m];
+ unit->skilluse_pos(bl, target_id%md->xs, target_id/md->xs, ud->stepskill_id, ud->stepskill_lv);
+ } else {
+ //If a player has target_id set and target is in range, attempt attack
+ struct block_list *tbl = map->id2bl(target_id);
+ if (!tbl || !status->check_visibility(bl, tbl)) {
+ return 0;
+ }
+ if(ud->stepskill_id == 0) {
+ //Execute normal attack
+ unit->attack(bl, tbl->id, (ud->state.attack_continue) + 2);
+ } else {
+ //Execute non-ground skill
+ unit->skilluse_id(bl, tbl->id, ud->stepskill_id, ud->stepskill_lv);
+ }
+ }
+
+ return 1;
+}
+
+
int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
int i;
int x,y,dx,dy;
+ unsigned char icewall_walk_block;
uint8 dir;
struct block_list *bl;
struct map_session_data *sd;
@@ -178,9 +250,34 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
dx = dirx[(int)dir];
dy = diry[(int)dir];
- if(map->getcell(bl->m,x+dx,y+dy,CELL_CHKNOPASS))
+ //Get icewall walk block depending on boss mode (players can't be trapped)
+ if(md && md->status.mode&MD_BOSS)
+ icewall_walk_block = battle_config.boss_icewall_walk_block;
+ else if(md)
+ icewall_walk_block = battle_config.mob_icewall_walk_block;
+ else
+ icewall_walk_block = 0;
+
+ //Monsters will walk into an icewall from the west and south if they already started walking
+ if(map->getcell(bl->m,x+dx,y+dy,CELL_CHKNOPASS)
+ && (icewall_walk_block == 0 || !map->getcell(bl->m,x+dx,y+dy,CELL_CHKICEWALL) || dx < 0 || dy < 0))
return unit->walktoxy_sub(bl);
+ //Monsters can only leave icewalls to the west and south
+ //But if movement fails more than icewall_walk_block times, they can ignore this rule
+ if(md && md->walktoxy_fail_count < icewall_walk_block && map->getcell(bl->m,x,y,CELL_CHKICEWALL) && (dx > 0 || dy > 0)) {
+ //Needs to be done here so that rudeattack skills are invoked
+ md->walktoxy_fail_count++;
+ clif->fixpos(bl);
+ //Monsters in this situation first use a chase skill, then unlock target and then use an idle skill
+ if (!(++ud->walk_count%WALK_SKILL_INTERVAL))
+ mob->skill_use(md, tick, -1);
+ mob->unlocktarget(md, tick);
+ if (!(++ud->walk_count%WALK_SKILL_INTERVAL))
+ mob->skill_use(md, tick, -1);
+ return 0;
+ }
+
//Refresh view for all those we lose sight
map->foreachinmovearea(clif->outsight, bl, AREA_SIZE, dx, dy, sd?BL_ALL:BL_PC, bl);
@@ -231,6 +328,8 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
sd->hd->masterteleport_timer = 0;
}
} else if (md) {
+ //Movement was successful, reset walktoxy_fail_count
+ md->walktoxy_fail_count = 0;
if( map->getcell(bl->m,x,y,CELL_CHKNPC) ) {
if( npc->touch_areanpc2(md) ) return 0; // Warped
} else
@@ -240,9 +339,11 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//But avoid triggering on stop-walk calls.
if(tid != INVALID_TIMER &&
!(ud->walk_count%WALK_SKILL_INTERVAL) &&
+ map->list[bl->m].users > 0 &&
mob->skill_use(md, tick, -1))
{
- if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER))
+ if (!(ud->skill_id == NPC_SELFDESTRUCTION && ud->skilltimer != INVALID_TIMER)
+ && md->state.skillstate != MSS_WALK) //Walk skills are supposed to be used while walking
{ //Skill used, abort walking
clif->fixpos(bl); //Fix position as walk has been canceled.
return 0;
@@ -275,6 +376,21 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
if(tid == INVALID_TIMER) //A directly invoked timer is from battle_stop_walking, therefore the rest is irrelevant.
return 0;
+ //If stepaction is set then we remembered a client request that should be executed on the next step
+ if (ud->stepaction && ud->target_to) {
+ //Delete old stepaction even if not executed yet, the latest command is what counts
+ if(ud->steptimer != INVALID_TIMER) {
+ timer->delete(ud->steptimer, unit->step_timer);
+ ud->steptimer = INVALID_TIMER;
+ }
+ //Delay stepactions by half a step (so they are executed at full step)
+ if(ud->walkpath.path[ud->walkpath.path_pos]&1)
+ i = status->get_speed(bl)*14/20;
+ else
+ i = status->get_speed(bl)/2;
+ ud->steptimer = timer->add(tick+i, unit->step_timer, bl->id, 0);
+ }
+
if(ud->state.change_walk_target)
return unit->walktoxy_sub(bl);
@@ -294,7 +410,7 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//Keep trying to run.
if ( !(unit->run(bl, NULL, SC_RUN) || unit->run(bl, sd, SC_WUGDASH)) )
ud->state.running = 0;
- } else if (ud->target_to) {
+ } else if (!ud->stepaction && ud->target_to) {
//Update target trajectory.
struct block_list *tbl = map->id2bl(ud->target_to);
if (!tbl || !status->check_visibility(bl, tbl)) {
@@ -323,6 +439,16 @@ int unit_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//Stopped walking. Update to_x and to_y to current location [Skotlex]
ud->to_x = bl->x;
ud->to_y = bl->y;
+
+ if(map->count_oncell(bl->m, x, y, BL_CHAR|BL_NPC, 1) > battle_config.official_cell_stack_limit) {
+ //Walked on occupied cell, call unit_walktoxy again
+ if(ud->steptimer != INVALID_TIMER) {
+ //Execute step timer on next step instead
+ timer->delete(ud->steptimer, unit_step_timer);
+ ud->steptimer = INVALID_TIMER;
+ }
+ return unit->walktoxy(bl, x, y, 8);
+ }
}
return 0;
}
@@ -340,6 +466,7 @@ int unit_delay_walktoxy_timer(int tid, int64 tick, int id, intptr_t data) {
//&1 -> 1/0 = easy/hard
//&2 -> force walking
//&4 -> Delay walking if the reason you can't walk is the canwalk delay
+//&8 -> Search for an unoccupied cell and cancel if none available
int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
{
struct unit_data* ud = NULL;
@@ -352,6 +479,9 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
if( ud == NULL) return 0;
+ if ((flag&8) && !map->closest_freecell(bl->m, &x, &y, BL_CHAR|BL_NPC, 1)) //This might change x and y
+ return 0;
+
if (!path->search(&wpd, bl->m, bl->x, bl->y, x, y, flag&1, CELL_CHKNOPASS)) // Count walk path cells
return 0;
@@ -377,7 +507,7 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
ud->state.walk_easy = flag&1;
ud->to_x = x;
ud->to_y = y;
- unit->set_target(ud, 0);
+ unit->stop_attack(bl); //Sets target to 0
sc = status->get_sc(bl);
if( sc ) {
@@ -394,11 +524,6 @@ int unit_walktoxy( struct block_list *bl, short x, short y, int flag)
return 1;
}
- if(ud->attacktimer != INVALID_TIMER) {
- timer->delete( ud->attacktimer, unit->attack_timer );
- ud->attacktimer = INVALID_TIMER;
- }
-
return unit->walktoxy_sub(bl);
}
@@ -453,7 +578,7 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int
ud->target_to = tbl->id;
ud->chaserange = range; //Note that if flag&2, this SHOULD be attack-range
ud->state.attack_continue = flag&2?1:0; //Chase to attack.
- unit->set_target(ud, 0);
+ unit->stop_attack(bl); //Sets target to 0
sc = status->get_sc(bl);
if (sc && (sc->data[SC_CONFUSION] || sc->data[SC__CHAOS])) //Randomize the target position
@@ -474,11 +599,6 @@ int unit_walktobl(struct block_list *bl, struct block_list *tbl, int range, int
if(!unit->can_move(bl))
return 0;
- if(ud->attacktimer != INVALID_TIMER) {
- timer->delete( ud->attacktimer, unit->attack_timer );
- ud->attacktimer = INVALID_TIMER;
- }
-
if (unit->walktoxy_sub(bl)) {
set_mobstate(bl, flag&2);
return 1;
@@ -548,7 +668,7 @@ bool unit_run( struct block_list *bl, struct map_session_data *sd, enum sc_type
break;
//if sprinting and there's a PC/Mob/NPC, block the path [Kevin]
- if( map->count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC) )
+ if( map->count_oncell(bl->m, to_x+dir_x, to_y+dir_y, BL_PC|BL_MOB|BL_NPC, 0) )
break;
to_x += dir_x;
@@ -700,6 +820,7 @@ int unit_blown(struct block_list* bl, int dx, int dy, int count, int flag)
}
if( sd ) {
+ unit->stop_stepaction(bl); //Stop stepaction when knocked back
sd->ud.to_x = nx;
sd->ud.to_y = ny;
}
@@ -930,7 +1051,7 @@ int unit_can_move(struct block_list *bl) {
|| sc->data[SC_ELECTRICSHOCKER]
|| sc->data[SC_WUGBITE]
|| sc->data[SC_THORNS_TRAP]
- || sc->data[SC_MAGNETICFIELD]
+ || ( sc->data[SC_MAGNETICFIELD] && !sc->data[SC_HOVERING] )
|| sc->data[SC__MANHOLE]
|| sc->data[SC_CURSEDCIRCLE_ATKER]
|| sc->data[SC_CURSEDCIRCLE_TARGET]
@@ -939,7 +1060,6 @@ int unit_can_move(struct block_list *bl) {
|| (sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 < 3 && !(sc->data[SC_CAMOUFLAGE]->val3&1))
|| sc->data[SC_MEIKYOUSISUI]
|| sc->data[SC_KG_KAGEHUMI]
- || sc->data[SC_KYOUGAKU]
|| sc->data[SC_NEEDLE_OF_PARALYZE]
|| sc->data[SC_VACUUM_EXTREME]
|| (sc->data[SC_FEAR] && sc->data[SC_FEAR]->val2 > 0)
@@ -965,6 +1085,17 @@ int unit_can_move(struct block_list *bl) {
return 0;
}
+
+ // Icewall walk block special trapped monster mode
+ if(bl->type == BL_MOB) {
+ struct mob_data *md = BL_CAST(BL_MOB, bl);
+ if(md && ((md->status.mode&MD_BOSS && battle_config.boss_icewall_walk_block == 1 && map->getcell(bl->m,bl->x,bl->y,CELL_CHKICEWALL))
+ || (!(md->status.mode&MD_BOSS) && battle_config.mob_icewall_walk_block == 1 && map->getcell(bl->m,bl->x,bl->y,CELL_CHKICEWALL)))) {
+ md->walktoxy_fail_count = 1; //Make sure rudeattacked skills are invoked
+ return 0;
+ }
+ }
+
return 1;
}
@@ -1000,12 +1131,6 @@ int unit_set_walkdelay(struct block_list *bl, int64 tick, int delay, int type) {
struct unit_data *ud = unit->bl2ud(bl);
if (delay <= 0 || !ud) return 0;
- /**
- * MvP mobs have no walk delay
- **/
- if( bl->type == BL_MOB && (((TBL_MOB*)bl)->status.mode&MD_BOSS) )
- return 0;
-
if (type) {
if (DIFF_TICK(ud->canmove_tick, tick+delay) > 0)
return 0;
@@ -1013,6 +1138,9 @@ int unit_set_walkdelay(struct block_list *bl, int64 tick, int delay, int type) {
//Don't set walk delays when already trapped.
if (!unit->can_move(bl))
return 0;
+ //Immune to being stopped for double the flinch time
+ if (DIFF_TICK(ud->canmove_tick, tick-delay) > 0)
+ return 0;
}
ud->canmove_tick = tick + delay;
if (ud->walktimer != INVALID_TIMER)
@@ -1028,7 +1156,7 @@ int unit_set_walkdelay(struct block_list *bl, int64 tick, int delay, int type) {
}
else
{
- unit->stop_walking(bl,2|4);
+ unit->stop_walking(bl,4);
if(ud->target)
timer->add(ud->canmove_tick+1, unit->walktobl_sub, bl->id, ud->target);
}
@@ -1103,6 +1231,17 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
return 0;
}
break;
+ case GC_WEAPONCRUSH:
+ if( sc && sc->data[SC_COMBOATTACK] && sc->data[SC_COMBOATTACK]->val1 == GC_WEAPONBLOCKING ) {
+ if( (target=map->id2bl(sc->data[SC_COMBOATTACK]->val2)) == NULL ) {
+ clif->skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0);
+ return 0;
+ }
+ } else {
+ clif->skill_fail(sd,skill_id,USESKILL_FAIL_GC_WEAPONBLOCKING,0);
+ return 0;
+ }
+ break;
}
if (target)
target_id = target->id;
@@ -1213,6 +1352,18 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
else
range = skill->get_range2(src, skill_id, skill_lv); // Skill cast distance from database
+ // New action request received, delete previous action request if not executed yet
+ if(ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(src);
+ // Remember the skill request from the client while walking to the next cell
+ if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && !battle->check_range(src, target, range-1)) {
+ ud->stepaction = true;
+ ud->target_to = target_id;
+ ud->stepskill_id = skill_id;
+ ud->stepskill_lv = skill_lv;
+ return 0; // Attacking will be handled by unit_walktoxy_timer in this case
+ }
+
//Check range when not using skill on yourself or is a combo-skill during attack
//(these are supposed to always have the same range as your attack)
if( src->id != target_id && (!temp || ud->attacktimer == INVALID_TIMER) ) {
@@ -1269,6 +1420,12 @@ int unit_skilluse_id2(struct block_list *src, int target_id, uint16 skill_id, ui
}
}
break;
+ case AB_CLEARANCE:
+ if( target->type != BL_MOB && battle->check_target(src,target,BCT_PARTY) <= 0 && sd ) {
+ clif->skill_fail(sd, skill_id, USESKILL_FAIL_TOTARGET, 0);
+ return 0;
+ }
+ break;
case SR_GATEOFHELL:
case SR_TIGERCANNON:
if (sc && sc->data[SC_COMBOATTACK] &&
@@ -1469,6 +1626,12 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
clif->skill_fail(sd,skill_id,USESKILL_FAIL_LEVEL,0);
return 0;
}
+ /**
+ * "WHY IS IT HEREE": ice wall cannot be canceled past this point, the client displays the animation even,
+ * if we cancel it from castend_pos, so it has to be here for it to not display the animation.
+ **/
+ if ( skill_id == WZ_ICEWALL && map->getcell(src->m, skill_x, skill_y, CELL_CHKNOICEWALL) )
+ return 0;
}
if (!status->check_skilluse(src, NULL, skill_id, 0))
@@ -1491,10 +1654,24 @@ int unit_skilluse_pos2( struct block_list *src, short skill_x, short skill_y, ui
else
range = skill->get_range2(src, skill_id, skill_lv); // Skill cast distance from database
+ // New action request received, delete previous action request if not executed yet
+ if(ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(src);
+ // Remember the skill request from the client while walking to the next cell
+ if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && !battle->check_range(src, &bl, range-1)) {
+ struct map_data *md = &map->list[src->m];
+ // Convert coordinates to target_to so we can use it as target later
+ ud->stepaction = true;
+ ud->target_to = (skill_x + skill_y*md->xs);
+ ud->stepskill_id = skill_id;
+ ud->stepskill_lv = skill_lv;
+ return 0; // Attacking will be handled by unit_walktoxy_timer in this case
+ }
+
if( skill->get_state(ud->skill_id) == ST_MOVE_ENABLE ) {
if( !unit->can_reach_bl(src, &bl, range + 1, 1, NULL, NULL) )
return 0; //Walk-path check failed.
- } else if( !battle->check_range(src, &bl, range + 1) )
+ } else if( !battle->check_range(src, &bl, range) )
return 0; //Arrow-path check failed.
unit->stop_attack(src);
@@ -1576,18 +1753,51 @@ int unit_set_target(struct unit_data* ud, int target_id)
return 0;
}
-int unit_stop_attack(struct block_list *bl)
+/**
+ * Stop a unit's attacks
+ * @param bl: Object to stop
+ */
+void unit_stop_attack(struct block_list *bl)
{
- struct unit_data *ud = unit->bl2ud(bl);
- nullpo_ret(bl);
+ struct unit_data *ud;
+ nullpo_retv(bl);
+ ud = unit_bl2ud(bl);
+ nullpo_retv(ud);
- if(!ud || ud->attacktimer == INVALID_TIMER)
- return 0;
+ //Clear target
+ unit_set_target(ud, 0);
- timer->delete( ud->attacktimer, unit->attack_timer );
+ if(ud->attacktimer == INVALID_TIMER)
+ return;
+
+ //Clear timer
+ timer->delete(ud->attacktimer, unit->attack_timer);
ud->attacktimer = INVALID_TIMER;
- unit->set_target(ud, 0);
- return 0;
+}
+
+/**
+ * Stop a unit's step action
+ * @param bl: Object to stop
+ */
+void unit_stop_stepaction(struct block_list *bl)
+{
+ struct unit_data *ud;
+ nullpo_retv(bl);
+ ud = unit_bl2ud(bl);
+ nullpo_retv(ud);
+
+ //Clear remembered step action
+ ud->stepaction = false;
+ ud->target_to = 0;
+ ud->stepskill_id = 0;
+ ud->stepskill_lv = 0;
+
+ if(ud->steptimer == INVALID_TIMER)
+ return;
+
+ //Clear timer
+ timer->delete(ud->steptimer, unit->step_timer);
+ ud->steptimer = INVALID_TIMER;
}
//Means current target is unattackable. For now only unlocks mobs.
@@ -1596,6 +1806,7 @@ int unit_unattackable(struct block_list *bl)
struct unit_data *ud = unit->bl2ud(bl);
if (ud) {
ud->state.attack_continue = 0;
+ ud->state.step_attack = 0;
unit->set_target(ud, 0);
}
@@ -1613,6 +1824,7 @@ int unit_unattackable(struct block_list *bl)
int unit_attack(struct block_list *src,int target_id,int continuous) {
struct block_list *target;
struct unit_data *ud;
+ int range;
nullpo_ret(ud = unit->bl2ud(src));
@@ -1641,19 +1853,30 @@ int unit_attack(struct block_list *src,int target_id,int continuous) {
unit->unattackable(src);
return 1;
}
- ud->state.attack_continue = continuous;
+ ud->state.attack_continue = (continuous&1)?1:0;
+ ud->state.step_attack = (continuous&2)?1:0;
unit->set_target(ud, target_id);
+ range = status_get_range(src);
+
if (continuous) //If you're to attack continuously, set to auto-case character
- ud->chaserange = status_get_range(src);
+ ud->chaserange = range;
//Just change target/type. [Skotlex]
if(ud->attacktimer != INVALID_TIMER)
return 0;
- //Set Mob's ANGRY/BERSERK states.
- if(src->type == BL_MOB)
- ((TBL_MOB*)src)->state.skillstate = ((TBL_MOB*)src)->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ // New action request received, delete previous action request if not executed yet
+ if(ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(src);
+ // Remember the attack request from the client while walking to the next cell
+ if(src->type == BL_PC && ud->walktimer != INVALID_TIMER && !battle->check_range(src, target, range-1)) {
+ ud->stepaction = true;
+ ud->target_to = ud->target;
+ ud->stepskill_id = 0;
+ ud->stepskill_lv = 0;
+ return 0; // Attacking will be handled by unit_walktoxy_timer in this case
+ }
if(DIFF_TICK(ud->attackabletime, timer->gettick()) > 0)
//Do attack next time it is possible. [Skotlex]
@@ -1855,15 +2078,19 @@ int unit_attack_timer_sub(struct block_list* src, int tid, int64 tick) {
}
sstatus = status->get_status_data(src);
- range = sstatus->rhw.range + 1;
+ range = sstatus->rhw.range;
- if( unit->is_walking(target) )
- range++; //Extra range when chasing
- if( !check_distance_bl(src,target,range) ) { //Chase if required.
- if(sd)
- clif->movetoattack(sd,target);
- else if(ud->state.attack_continue)
- unit->walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
+ if( (unit_is_walking(target) || ud->state.step_attack)
+ && (target->type == BL_PC || !map->getcell(target->m,target->x,target->y,CELL_CHKICEWALL)) )
+ range++; // Extra range when chasing (does not apply to mobs locked in an icewall)
+
+ if(sd && !check_distance_client_bl(src,target,range)) {
+ // Player tries to attack but target is too far, notify client
+ clif->movetoattack(sd,target);
+ return 1;
+ } else if(md && !check_distance_bl(src,target,range)) {
+ // Monster: Chase if required
+ unit_walktobl(src,target,ud->chaserange,ud->state.walk_easy|2);
return 1;
}
if( !battle->check_range(src,target,range) ) {
@@ -1885,8 +2112,15 @@ int unit_attack_timer_sub(struct block_list* src, int tid, int64 tick) {
if(ud->walktimer != INVALID_TIMER)
unit->stop_walking(src,1);
if(md) {
- if (mob->skill_use(md,tick,-1))
- return 1;
+ //First attack is always a normal attack
+ if(md->state.skillstate == MSS_ANGRY || md->state.skillstate == MSS_BERSERK) {
+ if (mob->skill_use(md,tick,-1))
+ return 1;
+ } else {
+ // Set mob's ANGRY/BERSERK states.
+ md->state.skillstate = md->state.aggressive?MSS_ANGRY:MSS_BERSERK;
+ }
+
if (sstatus->mode&MD_ASSIST && DIFF_TICK(md->last_linktime, tick) < MIN_MOBLINKTIME)
{ // Link monsters nearby [Skotlex]
md->last_linktime = tick;
@@ -2003,6 +2237,7 @@ void unit_dataset(struct block_list *bl) {
ud->walktimer = INVALID_TIMER;
ud->skilltimer = INVALID_TIMER;
ud->attacktimer = INVALID_TIMER;
+ ud->steptimer = INVALID_TIMER;
ud->attackabletime =
ud->canact_tick =
ud->canmove_tick = timer->gettick();
@@ -2068,15 +2303,19 @@ int unit_remove_map(struct block_list *bl, clr_type clrtype, const char* file, i
map->freeblock_lock();
- unit->set_target(ud, 0);
-
if (ud->walktimer != INVALID_TIMER)
unit->stop_walking(bl,0);
- if (ud->attacktimer != INVALID_TIMER)
- unit->stop_attack(bl);
if (ud->skilltimer != INVALID_TIMER)
unit->skillcastcancel(bl,0);
+ //Clear target even if there is no timer
+ if (ud->target || ud->attacktimer != INVALID_TIMER)
+ unit_stop_attack(bl);
+
+ //Clear stepaction even if there is no timer
+ if (ud->stepaction || ud->steptimer != INVALID_TIMER)
+ unit->stop_stepaction(bl);
+
// Do not reset can-act delay. [Skotlex]
ud->attackabletime = ud->canmove_tick /*= ud->canact_tick*/ = timer->gettick();
if(sc && sc->count ) { //map-change/warp dispells.
@@ -2200,7 +2439,7 @@ int unit_remove_map(struct block_list *bl, clr_type clrtype, const char* file, i
sd->debug_file, sd->debug_line, sd->debug_func, file, line, func);
} else if (--map->list[bl->m].users == 0 && battle_config.dynamic_mobs) //[Skotlex]
map->removemobs(bl->m);
- if( !(sd->sc.option&OPTION_INVISIBLE) ) {
+ if (!(pc_isinvisible(sd))) {
// decrement the number of active pvp players on the map
--map->list[bl->m].users_pvp;
}
@@ -2589,6 +2828,7 @@ int do_init_unit(bool minimal) {
timer->add_func_list(unit->walktoxy_timer,"unit_walktoxy_timer");
timer->add_func_list(unit->walktobl_sub, "unit_walktobl_sub");
timer->add_func_list(unit->delay_walktoxy_timer,"unit_delay_walktoxy_timer");
+ timer->add_func_list(unit->step_timer,"unit_step_timer");
return 0;
}
@@ -2622,6 +2862,8 @@ void unit_defaults(void) {
unit->warp = unit_warp;
unit->stop_walking = unit_stop_walking;
unit->skilluse_id = unit_skilluse_id;
+ unit->step_timer = unit_step_timer;
+ unit->stop_stepaction = unit_stop_stepaction;
unit->is_walking = unit_is_walking;
unit->can_move = unit_can_move;
unit->resume_running = unit_resume_running;
diff --git a/src/map/unit.h b/src/map/unit.h
index 9b95bae41..881fa16f4 100644
--- a/src/map/unit.h
+++ b/src/map/unit.h
@@ -30,6 +30,9 @@ struct unit_data {
int attacktimer;
int walktimer;
int chaserange;
+ bool stepaction; //Action should be executed on step [Playtester]
+ int steptimer; //Timer that triggers the action [Playtester]
+ uint16 stepskill_id,stepskill_lv; //Remembers skill that should be casted on step [Playtester]
int64 attackabletime;
int64 canact_tick;
int64 canmove_tick;
@@ -40,6 +43,7 @@ struct unit_data {
unsigned change_walk_target : 1 ;
unsigned skillcastcancel : 1 ;
unsigned attack_continue : 1 ;
+ unsigned step_attack : 1;
unsigned walk_easy : 1 ;
unsigned running : 1;
unsigned speed_changed : 1;
@@ -93,6 +97,8 @@ struct unit_interface {
int (*warp) (struct block_list *bl, short m, short x, short y, clr_type type);
int (*stop_walking) (struct block_list *bl, int type);
int (*skilluse_id) (struct block_list *src, int target_id, uint16 skill_id, uint16 skill_lv);
+ int (*step_timer) (int tid, int64 tick, int id, intptr_t data);
+ void (*stop_stepaction) (struct block_list *bl);
int (*is_walking) (struct block_list *bl);
int (*can_move) (struct block_list *bl);
int (*resume_running) (int tid, int64 tick, int id, intptr_t data);
@@ -101,7 +107,7 @@ struct unit_interface {
int (*skilluse_pos) (struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv);
int (*skilluse_pos2) (struct block_list *src, short skill_x, short skill_y, uint16 skill_id, uint16 skill_lv, int casttime, int castcancel);
int (*set_target) (struct unit_data *ud, int target_id);
- int (*stop_attack) (struct block_list *bl);
+ void (*stop_attack) (struct block_list *bl);
int (*unattackable) (struct block_list *bl);
int (*attack) (struct block_list *src, int target_id, int continuous);
int (*cancel_combo) (struct block_list *bl);