Issue information

Issue ID
#2954
Status
Working as Intended
Severity
None
Started
Hercules Elf Bot
Apr 8, 2009 11:50
Last Post
Hercules Elf Bot
Mar 5, 2012 16:54
Confirmation
N/A

Hercules Elf Bot - Apr 8, 2009 11:50

Originally posted by [b]elbrunito[/b]
http://www.eathena.ws/board/index.php?autocom=bugtracker&showbug=2954

The possible bug is in the thief skill "Steal". When using drop rates higher than 1x, when two items get the same drop probability of 100%, they are not stolen with the same probability. I'll try to explain it with an example.

Mob: Alligator
Drops (1x): Zargon (10%), Anolian Skin (20%)
Drops (10x): Zargon (100%), Anolian Skin (100%)

With drops 1x, you steal 1000 zargon, and you should steal 2000 anolian skin. Steal works fine.
With drops 10x, you steal 1000 zargon, and you should steal 1000 anolian skin. Steal doesn't work fine, it steals 1000 zargon and about 500 anolian skin. It's always around that number.

I thougth it was because the natural drop of an item was higher than the other, but when I tried with these, that were the results. Every mob does the same thing.

I took a look at the code (src/map/pc.c, line 3717, function pc_steal_item):

CODE
    // base skill success chance (percentual)
    rate = (sd_status->dex - md_status->dex)/2 + lv*6 + 4;
    rate += sd->add_steal_rate;
        
    if( rate < 1 )
        return 0;

    // Try dropping one item, in the order from first to last possible slot.
    // Droprate is affected by the skill success rate.
    for( i = 0; i < MAX_STEAL_DROP; i++ )
        if( md->db->dropitem[i].nameid > 0 && itemdb_exists(md->db->dropitem[i].nameid) && rand() % 10000 < md->db->dropitem[i].p * rate/100. )
            break;


The way it's performing the steal shows that in order to get the Anolian skin you have to fail the first steal attempt of stealing Zargon.

I think that instead of putting a break in that "for", you shoul add the item to a container, and after that randomly choose one of them, in order to have the same probability of getting an item.

Can someone see the same problem as me? It depends on how the items are ordered in the array md>db->dropitem, not only on their probability.

Edit: I've tried putting higher rates (everything al 100% drop rate). 100 poring stealed, 70 jellopies, 28 dagger, 1 apple. I've seen the content of the array, and this is the item order:

Jellopy
Knife
Sticky Mucus
Apple
Empty Bottle
Apple
Unripe Apple
[ID 0]
[ID 0]
Poring Card


So, that confirms my theory. It's giving the items in the order of the array.

Here it is, a solution to this bug:
CODE
int pc_steal_item(struct map_session_data *sd,struct block_list *bl, int lv)
{
    int i,itemid,flag;
    double rate;
    struct status_data *sd_status, *md_status;
    struct mob_data *md;
    struct item tmp_item;
+    int steal_items[MAX_STEAL_DROP];
+    int steal_items_count = 0;

    if(!sd || !bl || bl->type!=BL_MOB)
        return 0;

    md = (TBL_MOB *)bl;

    if(md->state.steal_flag == UCHAR_MAX || md->sc.opt1) //already stolen from / status change check
        return 0;
    
    sd_status= status_get_status_data(&sd->bl);
    md_status= status_get_status_data(bl);

    if( md->master_id || md_status->mode&MD_BOSS ||
        (md->class_ >= 1324 && md->class_ < 1364) || // Treasure Boxes WoE
        (md->class_ >= 1938 && md->class_ < 1946) || // Treasure Boxes WoE SE
        map[bl->m].flag.nomobloot || // check noloot map flag [Lorky]
        (battle_config.skill_steal_max_tries && //Reached limit of steal attempts. [Lupus]
            md->state.steal_flag++ >= battle_config.skill_steal_max_tries)
      ) { //Can't steal from
        md->state.steal_flag = UCHAR_MAX;
        return 0;
    }

    // base skill success chance (percentual)
    rate = (sd_status->dex - md_status->dex)/2 + lv*6 + 4;
    rate += sd->add_steal_rate;
        
    if( rate < 1 )
        return 0;

    // Try dropping one item, in the order from first to last possible slot.
    // Droprate is affected by the skill success rate.
    for( i = 0; i < MAX_STEAL_DROP; i++ )
        if( md->db->dropitem[i].nameid > 0 && itemdb_exists(md->db->dropitem[i].nameid) && rand() % 10000 < md->db->dropitem[i].p * rate/100. )
+            steal_items[steal_items_count++] = md->db->dropitem[i].nameid;
-            break;

+    if( steal_items_count == 0 )
-    if( i == MAX_STEAL_DROP )
        return 0;

+    //randomly choose one of the sucessfully stolen items
+    i = rand() % steal_items_count;
+    itemid = steal_items[i];
+    //get mob slot for the stolen item. Store it in variable "i"
+    for(i=0; i<MAX_STEAL_DROP && itemid==md->db->dropitem[i].nameid; i++);
-    itemid = md->db->dropitem[i].nameid;
        
    memset(&tmp_item,0,sizeof(tmp_item));
    tmp_item.nameid = itemid;
    tmp_item.amount = 1;
    tmp_item.identify = itemdb_isidentified(itemid);
    flag = pc_additem(sd,&tmp_item,1);

    //TODO: Should we disable stealing when the item you stole couldn't be added to your inventory? Perhaps players will figure out a way to exploit this behaviour otherwise?
    md->state.steal_flag = UCHAR_MAX; //you can't steal from this mob any more

    if(flag) { //Failed to steal due to overweight
        clif_additem(sd,0,0,flag);
        return 0;
    }
    
    if(battle_config.show_steal_in_same_party)
        party_foreachsamemap(pc_show_steal,sd,AREA_SIZE,sd,tmp_item.nameid);

    //Logs items, Stolen from mobs [Lupus]
    if(log_config.enable_logs&0x80) {
        log_pick_mob(md, "M", itemid, -1, NULL);
        log_pick_pc(sd, "P", itemid, 1, NULL);
    }
        
    //A Rare Steal Global Announce by Lupus
    if(md->db->dropitem[i].p<=battle_config.rare_drop_announce) {
        struct item_data *i_data;
        char message[128];
        i_data = itemdb_search(itemid);
        sprintf (message, msg_txt(542), (sd->status.name != NULL)?sd->status.name :"GM", md->db->jname, i_data->jname, (float)md->db->dropitem[i].p/100);
        //MSG: "'%s' stole %s's %s (chance: %0.02f%%)"
        intif_GMmessage(message,strlen(message)+1,0);
    }
    return 1;
}


This post has been edited by elbrunito: Apr 8 2009, 07:05 AM