diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index 2712b23bb5..c33c2c9f0c 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -6,8 +6,19 @@ GOES INTO TRUNK AND WILL BE MERGED INTO STABLE BY VALARIS AND WIZPUTER. -- VALAR 2006/02/09 - * SQL compile fix (Skotlex, check line 470 in charsave.c, - I've added here rate value = 10000) [Komurka] + * Code rewrites in mob_damage and party_exp_even_share for correctly + handling overflow issues. Now uses UINT_MAX for range comparisons, as it + should be. [Skotlex] + * Also modified the mob_db reading to use UINT_MAX for exp limits, changed + their exp/job exp fields to unsigned int as well. [Skotlex] + * Modified multi_level_up behaviour to work as specified by Kyoki. That is, + on a level up, the max carry-over exp is the exp needed for the previous + level -1. [Skotlex] + * Modified the skill attack display of Meteor Assault and the Warm Skills + (I think the caster should no longer do fancy animations now on each hit) + [Skotlex] + * Added back water elemental targets being inmune to SC_FREEZE [Skotlex] + * SQL compile fix [Komurka] 2006/02/08 * Added battle config option skill_caster_check, which does a diff --git a/conf-tmpl/battle/monster.conf b/conf-tmpl/battle/monster.conf index 0ba19d6c27..2ffe400159 100644 --- a/conf-tmpl/battle/monster.conf +++ b/conf-tmpl/battle/monster.conf @@ -149,7 +149,7 @@ mob_remove_delay: 300000 mob_clear_delay: 0 // Defines on who the mob npc_event gets executed when a mob is killed. -// Type 1: On theplayer that killed the mob (if killed by a non-player, resorts to type 0) +// Type 1: On the player that killed the mob (if killed by a non-player, resorts to type 0) // Type 0: On the player that did the most damage to the mob. // NOTE: This affects who gains the Castle when the Emperium is broken. mob_npc_event_type: 1 diff --git a/src/map/charsave.c b/src/map/charsave.c index 1d163612ca..fde1f66ed5 100644 --- a/src/map/charsave.c +++ b/src/map/charsave.c @@ -467,8 +467,8 @@ int charsave_load_scdata(int account_id, int char_id) continue; } - status_change_start(&sd->bl, atoi(sql_row[0]), 10000, atoi(sql_row[2]), atoi(sql_row[3]), - atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 7); + status_change_start(&sd->bl, atoi(sql_row[0]), 100, atoi(sql_row[2]), atoi(sql_row[3]), + atoi(sql_row[4]), atoi(sql_row[5]), atoi(sql_row[1]), 15); } } diff --git a/src/map/clif.c b/src/map/clif.c index 1ba814652b..a97f279416 100644 --- a/src/map/clif.c +++ b/src/map/clif.c @@ -365,7 +365,7 @@ int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) { for (i = 0; i < fd_max; i++) { if (session[i] && (sd = (struct map_session_data *)session[i]->session_data) != NULL && sd->state.auth) { if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version - WFIFOHEAD(i, len); + WFIFOHEAD(i, len); memcpy(WFIFOP(i,0), buf, len); WFIFOSET(i,len); } @@ -412,7 +412,7 @@ int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) { if (packet_db[cd->usersd[i]->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version if (cd->usersd[i]->fd >0 && session[cd->usersd[i]->fd]) // Added check to see if session exists [PoW] { - WFIFOHEAD(cd->usersd[i]->fd,len); + WFIFOHEAD(cd->usersd[i]->fd,len); memcpy(WFIFOP(cd->usersd[i]->fd,0), buf, len); WFIFOSET(cd->usersd[i]->fd,len); } @@ -424,6 +424,7 @@ int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) { for(i=1; isession_data) != NULL && sd->state.mainchat && sd->fd) { + WFIFOHEAD(sd->fd, len); memcpy(WFIFOP(sd->fd,0), buf, len); WFIFOSET(sd->fd, len); } @@ -472,7 +473,7 @@ int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) { if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL && sd->state.auth && sd->fd && sd->partyspy) { if (sd->partyspy == p->party_id) { if (sd->fd && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version - WFIFOHEAD(sd->fd,len); + WFIFOHEAD(sd->fd,len); memcpy(WFIFOP(sd->fd,0), buf, len); WFIFOSET(sd->fd,len); } @@ -483,7 +484,7 @@ int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) { break; case SELF: if (sd && sd->fd && packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version - WFIFOHEAD(sd->fd,len); + WFIFOHEAD(sd->fd,len); memcpy(WFIFOP(sd->fd,0), buf, len); WFIFOSET(sd->fd,len); } @@ -533,7 +534,7 @@ int clif_send (unsigned char *buf, int len, struct block_list *bl, int type) { if (session[i] && (sd = (struct map_session_data*)session[i]->session_data) != NULL && sd->state.auth && sd->fd && sd->guildspy) { if (sd->guildspy == g->guild_id) { if (packet_db[sd->packet_ver][RBUFW(buf,0)].len) { // packet must exist for the client version - WFIFOHEAD(sd->fd,len); + WFIFOHEAD(sd->fd,len); memcpy(WFIFOP(sd->fd,0), buf, len); WFIFOSET(sd->fd,len); } @@ -8809,7 +8810,7 @@ void clif_parse_LoadEndAck(int fd,struct map_session_data *sd) pc_checkskill(sd,SG_STAR_COMFORT)) status_calc_pc(sd,0); - if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobafter(sd)) + if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) clif_status_load(&sd->bl, SI_DEVIL, 1); //blindness [Komurka] map_foreachinarea(clif_getareachar,sd->bl.m,sd->bl.x-AREA_SIZE,sd->bl.y-AREA_SIZE,sd->bl.x+AREA_SIZE,sd->bl.y+AREA_SIZE,BL_ALL,sd); diff --git a/src/map/mob.c b/src/map/mob.c index 0ce92e813f..4f70ef3497 100644 --- a/src/map/mob.c +++ b/src/map/mob.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "timer.h" #include "socket.h" @@ -2176,7 +2177,8 @@ int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type) struct map_session_data *sd = NULL,*tmpsd[DAMAGELOG_SIZE]; struct { struct party *p; - int id,base_exp,job_exp,zeny; + int id,zeny; + unsigned int base_exp,job_exp; } pt[DAMAGELOG_SIZE]; int pnum=0; int mvp_damage,max_hp; @@ -2422,7 +2424,7 @@ int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type) // 経験値の分配 for(i=0;ibl.m != md->bl.m || pc_isdead(tmpsd[i])) @@ -2436,8 +2438,8 @@ int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type) if (count>1) per *= (9.+(double)((count > 6)? 6:count))/10.; //attackers count bonus. - base_exp = (unsigned long)md->db->base_exp; - job_exp = (unsigned long)md->db->job_exp; + base_exp = md->db->base_exp; + job_exp = md->db->job_exp; if (ret) per += per*ret/100.; //SC_RICHMANKIM bonus. [Skotlex] @@ -2477,21 +2479,24 @@ int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type) else if(md->special_state.size==2 && zeny >1) zeny*=2; } - if(battle_config.mobs_level_up && md->level > md->db->lv) { // [Valaris] - base_exp+=(unsigned long) (((md->level-md->db->lv)*((md->db->base_exp))*(battle_config.mobs_level_up_exp_rate/100))); - job_exp+=(unsigned long) (((md->level-md->db->lv)*((md->db->job_exp))*(battle_config.mobs_level_up_exp_rate/100))); - } + if(battle_config.mobs_level_up && md->level > md->db->lv) // [Valaris] + per+= per*(md->level-md->db->lv)*battle_config.mobs_level_up_exp_rate/100; } if (per > 4) per = 4; //Limit gained exp to quadro the mob's exp. [3->4 Komurka] - base_exp = (unsigned long)(base_exp*per); - job_exp = (unsigned long)(job_exp*per); - - if (base_exp > 0x7fffffff) base_exp = 0x7fffffff; - else if (base_exp < 1) base_exp = 1; - if (job_exp > 0x7fffffff) job_exp = 0x7fffffff; - else if (job_exp < 1) job_exp = 1; + if (base_exp*per > UINT_MAX) + base_exp = UINT_MAX; + else + base_exp = (unsigned int)(base_exp*per); + + if (job_exp*per > UINT_MAX) + job_exp = UINT_MAX; + else + job_exp = (unsigned int)(job_exp*per); + + if (base_exp < 1) base_exp = 1; + if (job_exp < 1) job_exp = 1; //mapflags: noexp check [Lorky] if (map[md->bl.m].flag.nobaseexp == 1) base_exp=0; @@ -2514,14 +2519,16 @@ int mob_damage(struct block_list *src,struct mob_data *md,int damage,int type) flag=0; } }else{ // いるときは公平 - if (pt[j].base_exp +base_exp < 0x7fffffff) + if (pt[j].base_exp > UINT_MAX - base_exp) + pt[j].base_exp=UINT_MAX; + else pt[j].base_exp+=base_exp; + + if (pt[j].job_exp > UINT_MAX - job_exp) + pt[j].job_exp=UINT_MAX; else - pt[j].base_exp = 0x7fffffff; - if (pt[j].job_exp +job_exp < 0x7fffffff) pt[j].job_exp+=job_exp; - else - pt[j].job_exp = 0x7fffffff; + if(battle_config.zeny_from_mobs) pt[j].zeny+=zeny; // zeny share [Valaris] flag=0; @@ -4241,14 +4248,20 @@ static int mob_readdb(void) mob_db_data[class_]->max_sp = atoi(str[5]); exp = (double)atoi(str[6]) * (double)battle_config.base_exp_rate / 100.; - if (exp < 0) exp = 0; - else if (exp > 0x7fffffff) exp = 0x7fffffff; - mob_db_data[class_]->base_exp = (int)exp; + if (exp < 0) + mob_db_data[class_]->base_exp = 0; + if (exp > UINT_MAX) + mob_db_data[class_]->base_exp = UINT_MAX; + else + mob_db_data[class_]->base_exp = (unsigned int)exp; exp = (double)atoi(str[7]) * (double)battle_config.job_exp_rate / 100.; - if (exp < 0) exp = 0; - else if (exp > 0x7fffffff) exp = 0x7fffffff; - mob_db_data[class_]->job_exp = (int)exp; + if (exp < 0) + mob_db_data[class_]->job_exp = 0; + else if (exp > UINT_MAX) + mob_db_data[class_]->job_exp = UINT_MAX; + else + mob_db_data[class_]->job_exp = (unsigned int)exp; mob_db_data[class_]->range=atoi(str[8]); mob_db_data[class_]->atk1=atoi(str[9]); @@ -4811,14 +4824,20 @@ static int mob_read_sqldb(void) mob_db_data[class_]->max_sp = TO_INT(5); exp = (double)TO_INT(6) * (double)battle_config.base_exp_rate / 100.; - if (exp < 0) exp = 0; - else if (exp > 0x7fffffff) exp = 0x7fffffff; - mob_db_data[class_]->base_exp = (int)exp; + if (exp < 0) + mob_db_data[class_]->base_exp = 0; + else if (exp > UINT_MAX) + mob_db_data[class_]->base_exp = UINT_MAX; + else + mob_db_data[class_]->base_exp = (unsigned int)exp; exp = (double)TO_INT(7) * (double)battle_config.job_exp_rate / 100.; - if (exp < 0) exp = 0; - else if (exp > 0x7fffffff) exp = 0x7fffffff; - mob_db_data[class_]->job_exp = (int)exp; + if (exp < 0) + mob_db_data[class_]->job_exp = 0; + else if (exp > UINT_MAX) + mob_db_data[class_]->job_exp = UINT_MAX; + else + mob_db_data[class_]->job_exp = (unsigned int)exp; mob_db_data[class_]->range = TO_INT(8); mob_db_data[class_]->atk1 = TO_INT(9); diff --git a/src/map/mob.h b/src/map/mob.h index bbdd66105a..2a084f082d 100644 --- a/src/map/mob.h +++ b/src/map/mob.h @@ -31,7 +31,7 @@ struct mob_db { char name[NAME_LENGTH],jname[NAME_LENGTH]; short lv; int max_hp,max_sp; - int base_exp,job_exp; + unsigned int base_exp,job_exp; int atk1,atk2; int def,mdef; int str,agi,vit,int_,dex,luk; diff --git a/src/map/party.c b/src/map/party.c index d5c6b800b7..be8520d8fb 100644 --- a/src/map/party.c +++ b/src/map/party.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "../common/timer.h" #include "../common/socket.h" @@ -639,12 +640,11 @@ int party_send_xy_clear(struct party *p) } // exp share and added zeny share [Valaris] -int party_exp_share(struct party *p,int map,int base_exp,int job_exp,int zeny) +int party_exp_share(struct party *p,int map,unsigned int base_exp,unsigned int job_exp,int zeny) { struct map_session_data* sd[MAX_PARTY]; int i; short c, bonus =100; // modified [Valaris] - unsigned long base, job; nullpo_retr(0, p); @@ -660,15 +660,22 @@ int party_exp_share(struct party *p,int map,int base_exp,int job_exp,int zeny) bonus += (battle_config.party_even_share_bonus*c*(c-1)/10); //Changed Valaris's bonus switch to an equation [Skotlex] else //Official kRO/iRO sites state that the even share bonus is 10% per additional party member. bonus += (c-1)*10; - base = (unsigned long)(base_exp/c)*bonus/100; - job = (unsigned long)(job_exp/c)*bonus/100; - if (base > 0x7fffffff) - base = 0x7fffffff; - if (job > 0x7fffffff) - job = 0x7fffffff; + + base_exp/=c; + job_exp/=c; + if (base_exp/100 > UINT_MAX/bonus) + base_exp= UINT_MAX; //Exp overflow + else + base_exp = base_exp*bonus/100; + + if (job_exp/100 > UINT_MAX/bonus) + job_exp = UINT_MAX; + else + job_exp = job_exp*bonus/100; + for (i = 0; i < c; i++) { - pc_gainexp(sd[i], base, job); + pc_gainexp(sd[i], base_exp, job_exp); if (battle_config.zeny_from_mobs) // zeny from mobs [Valaris] pc_getzeny(sd[i],bonus*zeny/(c*100)); } diff --git a/src/map/party.h b/src/map/party.h index 4e9601d687..b98731c18e 100644 --- a/src/map/party.h +++ b/src/map/party.h @@ -38,7 +38,7 @@ int party_recv_message(int party_id,int account_id,char *mes,int len); int party_check_conflict(struct map_session_data *sd); int party_skill_check(struct map_session_data *sd, int party_id, int skillid, int skilllv); int party_send_xy_clear(struct party *p); -int party_exp_share(struct party *p,int map,int base_exp,int job_exp,int zeny); +int party_exp_share(struct party *p,int map,unsigned int base_exp,unsigned int job_exp,int zeny); int party_send_dot_remove(struct map_session_data *sd); int party_sub_count(struct block_list *bl, va_list ap); void party_foreachsamemap(int (*func)(struct block_list *,va_list),struct map_session_data *sd,int type,...); diff --git a/src/map/pc.c b/src/map/pc.c index 09ed0d7f70..46fe462d85 100644 --- a/src/map/pc.c +++ b/src/map/pc.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "socket.h" // [Valaris] #include "timer.h" @@ -4597,11 +4598,13 @@ int pc_checkbaselevelup(struct map_session_data *sd) nullpo_retr(0, sd); if(sd->status.base_exp >= next && next > 0){ - - // base側レベルアップ?理 sd->status.base_exp -= next; + //Kyoki pointed out that the max overcarry exp is the exp needed for the previous level -1. [Skotlex] + if(!battle_config.multi_level_up && sd->status.base_exp > next-1) + sd->status.base_exp = next-1; sd->status.base_level ++; + if (battle_config.pet_lv_rate && sd->pd) // update pet's level status_calc_pet(sd,0); if (battle_config.use_statpoint_table) @@ -4653,9 +4656,13 @@ int pc_checkjoblevelup(struct map_session_data *sd) nullpo_retr(0, sd); if(sd->status.job_exp >= next && next > 0){ - // job側レベルアップ?理 sd->status.job_exp -= next; + //Kyoki pointed out that the max overcarry exp is the exp needed for the previous level -1. [Skotlex] + if(!battle_config.multi_level_up && sd->status.job_exp > next-1) + sd->status.job_exp = next-1; + sd->status.job_level ++; + clif_updatestatus(sd,SP_JOBLEVEL); clif_updatestatus(sd,SP_NEXTJOBEXP); sd->status.skill_point ++; @@ -4663,7 +4670,7 @@ int pc_checkjoblevelup(struct map_session_data *sd) status_calc_pc(sd,0); clif_misceffect(&sd->bl,1); - if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobafter(sd)) + if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) clif_status_change(&sd->bl,SI_DEVIL, 1); //Permanent blind effect from SG_DEVIL. if (script_config.event_script_type == 0) { @@ -4697,34 +4704,41 @@ int pc_gainexp(struct map_session_data *sd,unsigned int base_exp,unsigned int jo if(sd->bl.prev == NULL || pc_isdead(sd)) return 0; - if((battle_config.pvp_exp == 0) && map[sd->bl.m].flag.pvp) // [MouseJstr] + if(!battle_config.pvp_exp && map[sd->bl.m].flag.pvp) // [MouseJstr] return 0; // no exp on pvp maps if(sd->status.guild_id>0){ // ギルドに上納 base_exp-=guild_payexp(sd,base_exp); } - if(!battle_config.multi_level_up && pc_nextbaseafter(sd) && sd->status.base_exp+base_exp >= pc_nextbaseafter(sd)) { - base_exp = pc_nextbaseafter(sd) - sd->status.base_exp; + if(sd->state.showexp){ + nextb = pc_nextbaseexp(sd); + nextj = pc_nextjobexp(sd); + if (nextb > 0) + nextbp = (float) base_exp / (float) nextb; + if (nextj > 0) + nextjp = (float) job_exp / (float) nextj; } - nextb = pc_nextbaseexp(sd); - nextj = pc_nextjobexp(sd); - if (nextb > 0) - nextbp = (float) base_exp / (float) nextb; - if (nextj > 0) - nextjp = (float) job_exp / (float) nextj; - - sd->status.base_exp += base_exp; - + + //Overflow checks... think we'll ever really need'em? [Skotlex] + if (base_exp > 0 && sd->status.base_exp > UINT_MAX - base_exp) + sd->status.base_exp = UINT_MAX; + else if (base_exp < 0 && sd->status.base_exp > base_exp) + sd->status.base_exp = 0; + else + sd->status.base_exp += base_exp; + while(pc_checkbaselevelup(sd)) ; clif_updatestatus(sd,SP_BASEEXP); - if(!battle_config.multi_level_up && pc_nextjobafter(sd) && sd->status.job_exp+job_exp >= pc_nextjobafter(sd)) { - job_exp = pc_nextjobafter(sd) - sd->status.job_exp; - } - - sd->status.job_exp += job_exp; + //Overflow checks... think we'll ever really need'em? [Skotlex] + if (job_exp > 0 && sd->status.job_exp > UINT_MAX - job_exp) + sd->status.job_exp = UINT_MAX; + else if (job_exp < 0 && sd->status.job_exp > job_exp) + sd->status.job_exp = 0; + else + sd->status.job_exp += job_exp; while(pc_checkjoblevelup(sd)) ; @@ -4779,33 +4793,6 @@ unsigned int pc_nextjobexp(struct map_session_data *sd) return exp_table[sd->status.class_][1][sd->status.job_level-1]; } -/*========================================== - * base level after next [Valaris] - *------------------------------------------ - */ -unsigned int pc_nextbaseafter(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - if(sd->status.base_level>=pc_maxbaselv(sd) || sd->status.base_level<=0) - return 0; - - return exp_table[sd->status.class_][0][sd->status.base_level]; -} - -/*========================================== - * job level after next [Valaris] - *------------------------------------------ - */ -unsigned int pc_nextjobafter(struct map_session_data *sd) -{ - nullpo_retr(0, sd); - - if(sd->status.job_level>=pc_maxjoblv(sd) || sd->status.job_level<=0) - return 0; - - return exp_table[sd->status.class_][1][sd->status.job_level]; -} /*========================================== * 必要ステ?タスポイント計算 @@ -5208,7 +5195,7 @@ int pc_resetskill(struct map_session_data* sd) int i, skill, inf2; nullpo_retr(0, sd); - if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobafter(sd)) + if (pc_checkskill(sd, SG_DEVIL) && !pc_nextjobexp(sd)) clif_status_load(&sd->bl, SI_DEVIL, 0); //Remove perma blindness due to skill-reset. [Skotlex] for (i = 1; i < MAX_SKILL; i++) { diff --git a/src/map/pc.h b/src/map/pc.h index 11e7539f81..f184f3f5d2 100644 --- a/src/map/pc.h +++ b/src/map/pc.h @@ -123,9 +123,7 @@ int pc_checkbaselevelup(struct map_session_data *sd); int pc_checkjoblevelup(struct map_session_data *sd); int pc_gainexp(struct map_session_data*,unsigned int,unsigned int); unsigned int pc_nextbaseexp(struct map_session_data *); -unsigned int pc_nextbaseafter(struct map_session_data *); // [Valaris] unsigned int pc_nextjobexp(struct map_session_data *); -unsigned int pc_nextjobafter(struct map_session_data *); // [Valaris] int pc_need_status_point(struct map_session_data *,int); int pc_statusup(struct map_session_data*,int); int pc_statusup2(struct map_session_data*,int,int); diff --git a/src/map/skill.c b/src/map/skill.c index 6822c83273..44e78e51f9 100644 --- a/src/map/skill.c +++ b/src/map/skill.c @@ -1766,6 +1766,10 @@ int skill_attack( int attack_type, struct block_list* src, struct block_list *ds //武器スキル?ここまで switch(skillid){ case AS_SPLASHER: + case ASC_METEORASSAULT: + case SG_SUN_WARM: + case SG_MOON_WARM: + case SG_STAR_WARM: clif_skill_damage(dsrc,bl,tick,dmg.amotion,dmg.dmotion, damage, dmg.div_, skillid, -1, 5); break; case ASC_BREAKER: // [celest] diff --git a/src/map/status.c b/src/map/status.c index f9c79cc8c6..c2ed134e13 100644 --- a/src/map/status.c +++ b/src/map/status.c @@ -1618,7 +1618,7 @@ int status_calc_pc(struct map_session_data* sd,int first) // Relative modifiers from passive skills if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0) sd->aspd_rate -= (skill/2); - if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobafter(sd)) + if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd)) sd->aspd_rate -= (skill*3); if(pc_isriding(sd)) @@ -3740,8 +3740,10 @@ int status_change_start(struct block_list *bl,int type,int rate,int val1,int val //Check for inmunities / sc fails switch (type) { - case SC_STONE: case SC_FREEZE: + if (elem == 1 && !(flag&1)) + return 0; //Can't freeze water elementals. + case SC_STONE: //I've been informed that undead chars are inmune to stone curse too. [Skotlex] if (undead_flag && !(flag&1)) return 0;