瀏覽代碼

Implemented get_percentage() for compact and safe calculation of percentual values.
Fixed integer arithmetic overflows that were occuring in several supernovice checks (bugreport:1135).

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@12679 54d463be-8e91-2dee-dedb-b68131a5f0ec

ultramage 17 年之前
父節點
當前提交
d5775e7e11
共有 10 個文件被更改,包括 41 次插入32 次删除
  1. 16 0
      src/common/utils.c
  2. 3 0
      src/common/utils.h
  3. 5 3
      src/map/clif.c
  4. 1 1
      src/map/mercenary.c
  5. 6 6
      src/map/mob.c
  6. 3 3
      src/map/pc.c
  7. 6 6
      src/map/pet.c
  8. 1 1
      src/map/skill.c
  9. 0 10
      src/map/status.c
  10. 0 2
      src/map/status.h

+ 16 - 0
src/common/utils.c

@@ -221,3 +221,19 @@ uint32 MakeDWord(uint16 word0, uint16 word1)
 		( (uint32)(word0        ) )|
 		( (uint32)(word1 << 0x10) );
 }
+
+
+/// calculates the value of A / B, in percent (rounded down)
+unsigned int percent(const unsigned int A, const unsigned int B)
+{
+	if( B == 0 )
+	{
+		ShowError("percent(): divison by zero!\n");
+		return -1;
+	}
+
+	if( A < UINT_MAX/100 )
+		return 100*A/B;
+	else
+		return A/(B/100);
+}

+ 3 - 0
src/common/utils.h

@@ -18,6 +18,9 @@ void findfile(const char *p, const char *pat, void (func)(const char*));
 //Caps values to min/max
 #define cap_value(a, min, max) ((a >= max) ? max : (a <= min) ? min : a)
 
+/// calculates the value of A / B, in percent (rounded down)
+unsigned int get_percentage(const unsigned int A, const unsigned int B);
+
 //////////////////////////////////////////////////////////////////////////
 // byte word dword access [Shinomori]
 //////////////////////////////////////////////////////////////////////////

+ 5 - 3
src/map/clif.c

@@ -7131,7 +7131,7 @@ int clif_charnameack (int fd, struct block_list *bl)
 				if (battle_config.show_mob_info&1)
 					str_p += sprintf(str_p, "HP: %u/%u | ", md->status.hp, md->status.max_hp);
 				if (battle_config.show_mob_info&2)
-					str_p += sprintf(str_p, "HP: %d%% | ", status_calc_life(md->status.hp, md->status.max_hp));
+					str_p += sprintf(str_p, "HP: %d%% | ", percent(md->status.hp, md->status.max_hp));
 				//Even thought mobhp ain't a name, we send it as one so the client
 				//can parse it. [Skotlex]
 				if (str_p != mobhp) {
@@ -8182,8 +8182,10 @@ void clif_parse_GlobalMessage(int fd, struct map_session_data* sd)
 	// check for special supernovice phrase
 	if( (sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE )
 	{
-		int next = pc_nextbaseexp(sd);
-		if( next > 0 && (sd->status.base_exp * 1000 / next)% 100 == 0 ) { // 0%, 10%, 20%, ...
+		unsigned int next = pc_nextbaseexp(sd);
+		if( next == 0 ) next = pc_thisbaseexp(sd);
+		if( percent(sd->status.base_exp, next)% 10 == 0 ) // 0%, 10%, 20%, ...
+		{
 			switch (sd->state.snovice_call_flag) {
 			case 0:
 					if( strstr(message, msg_txt(504)) ) // "Guardian Angel, can you hear my voice? ^^;"

+ 1 - 1
src/map/mercenary.c

@@ -95,7 +95,7 @@ int merc_hom_vaporize(struct map_session_data *sd, int flag)
 	if (status_isdead(&hd->bl))
 		return 0; //Can't vaporize a dead homun.
 
-	if (flag && status_calc_life(hd->battle_status.hp, hd->battle_status.max_hp)< 80)
+	if (flag && percent(hd->battle_status.hp, hd->battle_status.max_hp) < 80)
 		return 0;
 
 	hd->regen.state.block = 3; //Block regen while vaporized.

+ 6 - 6
src/map/mob.c

@@ -2688,7 +2688,7 @@ int mob_class_change (struct mob_data *md, int class_)
 	if (md->class_ == class_)
 		return 0; //Nothing to change.
 
-	hp_rate = status_calc_life(md->status.hp, md->status.max_hp);
+	hp_rate = percent(md->status.hp, md->status.max_hp);
 	md->class_ = class_;
 	md->db = mob_db(class_);
 	if (battle_config.override_mob_names==1)
@@ -2823,7 +2823,7 @@ int mob_summonslave(struct mob_data *md2,int *value,int amount,int skill_id)
 	
 	if (!battle_config.monster_class_change_recover &&
 		(skill_id == NPC_TRANSFORMATION || skill_id == NPC_METAMORPHOSIS))
-		hp_rate = status_calc_life(md2->status.hp, md2->status.max_hp);
+		hp_rate = percent(md2->status.hp, md2->status.max_hp);
 
 	for(;k<amount;k++) {
 		short x,y;
@@ -2927,7 +2927,7 @@ int mob_getfriendhprate_sub(struct block_list *bl,va_list ap)
 	if (battle_check_target(&md->bl,bl,BCT_ENEMY)>0)
 		return 0;
 	
-	rate = status_calc_life(status_get_hp(bl), status_get_max_hp(bl));
+	rate = percent(status_get_hp(bl), status_get_max_hp(bl));
 	
 	if (rate >= min_rate && rate <= max_rate)
 		(*fr) = bl;
@@ -2954,7 +2954,7 @@ struct block_list *mob_getmasterhpltmaxrate(struct mob_data *md,int rate)
 	if( md && md->master_id > 0 )
 	{
 		struct block_list *bl = map_id2bl(md->master_id);
-		if( bl && status_calc_life(status_get_hp(bl), status_get_max_hp(bl)) < rate )
+		if( bl && percent(status_get_hp(bl), status_get_max_hp(bl)) < rate )
 			return bl;
 	}
 
@@ -3060,11 +3060,11 @@ int mobskill_use(struct mob_data *md, unsigned int tick, int event)
 				case MSC_ALWAYS:
 					flag = 1; break;
 				case MSC_MYHPLTMAXRATE:		// HP< maxhp%
-					flag = status_calc_life(md->status.hp, md->status.max_hp);
+					flag = percent(md->status.hp, md->status.max_hp);
 					flag = (flag <= c2);
 				  	break;
 				case MSC_MYHPINRATE:
-					flag = status_calc_life(md->status.hp, md->status.max_hp);
+					flag = percent(md->status.hp, md->status.max_hp);
 					flag = (flag >= c2 && flag <= ms[i].val[0]);
 					break;
 				case MSC_MYSTATUSON:		// status[num] on

+ 3 - 3
src/map/pc.c

@@ -5082,9 +5082,9 @@ int pc_dead(struct map_session_data *sd,struct block_list *src)
 	// activate Steel body if a super novice dies at 99+% exp [celest]
 	if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && !sd->state.snovice_dead_flag)
   	{
-		if ((i=pc_nextbaseexp(sd))<=0)
-			i=sd->status.base_exp;
-		if (i>0 && (j=sd->status.base_exp*1000/i)>=990 && j<1000 && !map_flag_gvg(sd->bl.m))
+		unsigned int next = pc_nextbaseexp(sd);
+		if( next == 0 ) next = pc_thisbaseexp(sd);
+		if( percent(sd->status.base_exp,next) >= 99 && !map_flag_gvg(sd->bl.m) )
 			sd->state.snovice_dead_flag = 1;
 	}
 

+ 6 - 6
src/map/pet.c

@@ -554,7 +554,7 @@ int pet_catch_process2(struct map_session_data* sd, int target_id)
 		return 1;
 	}
 
-	pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - status_calc_life(md->status.hp, md->status.max_hp))/100;
+	pet_catch_rate = (pet_db[i].capture + (sd->status.base_level - md->level)*30 + sd->battle_status.luk*20)*(200 - percent(md->status.hp, md->status.max_hp))/100;
 
 	if(pet_catch_rate < 1) pet_catch_rate = 1;
 	if(battle_config.pet_catch_rate != 100)
@@ -1147,7 +1147,7 @@ int pet_heal_timer(int tid, unsigned int tick, int id, intptr data)
 	struct map_session_data *sd=map_id2sd(id);
 	struct status_data *status;
 	struct pet_data *pd;
-	short rate = 100;
+	unsigned int rate = 100;
 	
 	if(sd==NULL || sd->pd == NULL || sd->pd->s_skill == NULL)
 		return 1;
@@ -1162,8 +1162,8 @@ int pet_heal_timer(int tid, unsigned int tick, int id, intptr data)
 	status = status_get_status_data(&sd->bl);
 	
 	if(pc_isdead(sd) ||
-		(rate = status_calc_life(status->sp, status->max_sp)) > pd->s_skill->sp ||
-		(rate = status_calc_life(status->hp, status->max_hp)) > pd->s_skill->hp ||
+		(rate = percent(status->sp, status->max_sp)) > pd->s_skill->sp ||
+		(rate = percent(status->hp, status->max_hp)) > pd->s_skill->hp ||
 		(rate = (pd->ud.skilltimer != -1)) //Another skill is in effect
 	) {  //Wait (how long? 1 sec for every 10% of remaining)
 		pd->s_skill->timer=add_timer(gettick()+(rate>10?rate:10)*100,pet_heal_timer,sd->bl.id,0);
@@ -1205,8 +1205,8 @@ int pet_skill_support_timer(int tid, unsigned int tick, int id, intptr data)
 	}
 	
 	if(pc_isdead(sd) ||
-		(rate = status_calc_life(status->sp, status->max_sp)) > pd->s_skill->sp ||
-		(rate = status_calc_life(status->hp, status->max_hp)) > pd->s_skill->hp ||
+		(rate = percent(status->sp, status->max_sp)) > pd->s_skill->sp ||
+		(rate = percent(status->hp, status->max_hp)) > pd->s_skill->hp ||
 		(rate = (pd->ud.skilltimer != -1)) //Another skill is in effect
 	) {  //Wait (how long? 1 sec for every 10% of remaining)
 		pd->s_skill->timer=add_timer(tick+(rate>10?rate:10)*100,pet_skill_support_timer,sd->bl.id,0);

+ 1 - 1
src/map/skill.c

@@ -7582,7 +7582,7 @@ int skill_check_condition(struct map_session_data* sd, short skill, short lv, in
 		itemid[i] = skill_db[j].itemid[i];
 		amount[i] = skill_db[j].amount[i];
 	}
-	if(mhp > 0 && status_calc_life(status->hp, status->max_hp) > mhp) {
+	if(mhp > 0 && percent(status->hp, status->max_hp) > mhp) {
 		//mhp is the max-hp-requirement, that is,
 		//you must have this % or less of HP to cast it.
 		clif_skill_fail(sd,skill,2,0);

+ 0 - 10
src/map/status.c

@@ -929,16 +929,6 @@ int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per
 	return 1;
 }
 
-//calculates the base/max ratio as a value between 0->100 (percent), using
-//different approaches to avoid overflows.
-//NOTE: The -1 case (0 max hp) should never trigger!
-char status_calc_life(unsigned int base, unsigned int max)
-{
-	if (!max) return -1;
-	if (max < 10000) return 100*base/max;
-	return base/(max/100);
-}
-
 /*==========================================
  * Checks whether the src can use the skill on the target,
  * taking into account status/option of both source/target. [Skotlex]

+ 0 - 2
src/map/status.h

@@ -708,8 +708,6 @@ int status_set_sp(struct block_list *bl, unsigned int sp, int flag);
 int status_heal(struct block_list *bl,int hp,int sp, int flag);
 int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp);
 
-char status_calc_life(unsigned int base, unsigned int max);
-
 //Define for copying a status_data structure from b to a, without overwriting current Hp and Sp
 #define status_cpy(a, b) \
 	memcpy(&((a)->max_hp), &((b)->max_hp), sizeof(struct status_data)-(sizeof((a)->hp)+sizeof((a)->sp)))