OSP Code Optimisation SimpleLoot

Users who are viewing this thread

xenoargh

Grandmaster Knight
This is code to replace script_party_calculate_loot in module_scripts.  It's short, sweet and simple.  In fact, after testing it, I haven't the foggiest why I didn't do this months ago; the looting code has always been one of the more problematic things in Warband.

Why am I sharing this one out?  If you've ever looked at that script in Native, you know that it's anything but short, sweet and simple; it relies on the calculation of party shares, which is prone to various issues, and because it uses troop_loot_troop, it doesn't give access to the full range of equipment that should be available.  This uses a very different method to iterate through the Troop items, using a random chance based on Looting skill.  No more pile of 20 worthless Rocks when you've just fought 40 Looters fighting alongside some well-equipped Deserters- that problem is fixed, amongst others.

This code could very easily be extended to allow for special treasures held by enemy Heroes and other fun things, and it allows horses to be looted just like anything else.


Code:
    #script_party_calculate_loot:
    # INPUT:
    # param1: Party-id
    # Returns num looted items in reg(0)
     ("party_calculate_loot",
      [
		(store_script_param_1, ":enemy_party"), #Enemy Party_id
		(troop_clear_inventory,"trp_temp_troop"),#Clear the temp troop inventory
        (assign, ":num_looted_items",0),
		(assign, ":loot_slot", 10), 
        
		#Get looting skill
        (party_get_skill_level, ":player_party_looting", "p_main_party", "skl_looting"),
        (val_mul, ":player_party_looting", 3),
        
        (party_get_num_companion_stacks, ":num_stacks",":enemy_party"),
        (try_for_range, ":i_stack", 0, ":num_stacks"),
			(lt, ":loot_slot", 40),#Don't overfill loot
          (party_stack_get_troop_id, ":stack_troop",":enemy_party",":i_stack"),
			(try_for_range, ":current_inv", 0, 30),
				(troop_get_inventory_slot,":my_inv",":stack_troop",":current_inv"),#Search for items
				(gt, ":my_inv", 0),#We have a valid item
				(store_random_in_range, ":rand", 1, 135),
				(val_add, ":rand", ":player_party_looting"),
				(try_begin),
					(ge, ":rand", 100),
					(troop_set_inventory_slot, "trp_temp_troop", ":loot_slot", ":my_inv"),
					(val_add, ":loot_slot", 1),#Bump the slot upwards, so that we don't overwrite
					(val_add, ":num_looted_items", 1),
				(try_end),	
			(try_end),
		(try_end),
		
		#Add random trade goods where appropriate
        (try_begin),
			(this_or_next|party_slot_eq, "$g_enemy_party", slot_party_type, spt_kingdom_caravan),
			(this_or_next|party_slot_eq, "$g_enemy_party", slot_party_type, spt_bandit_lair),
			(party_slot_eq, "$g_enemy_party", slot_party_type, spt_village_farmer),

			(party_get_skill_level, ":player_looting", "p_main_party", "skl_looting"),
			(store_random_in_range, ":plunder_amount", 1, 5),
			(val_add, ":plunder_amount", ":player_looting"),
			(troop_add_merchandise, "trp_temp_troop", itp_type_goods, ":plunder_amount"),
			(val_add, ":num_looted_items", ":plunder_amount"),
        (try_end),		  
          
         (assign, reg0, ":num_looted_items"),
      ]),
 
Not sure how troop_loot_troop works (or how efficient it is), but your code doesn't take into account how many troops are in each stack (you'll get as many items from a caravan master as 100 caravan guards) and limits the inventory slot at 30 so troops with item variation above a certain point won't be counted properly. You can also remove the val_add in the loop and have something like this:
Code:
(party_get_skill_level, ":player_party_looting", "p_main_party", "skl_looting"),
(val_mul, ":player_party_looting", 3),
(val_add, ":player_party_looting", 1),
...
(store_random_in_range, ":rand", ":player_party_looting", 135),
...
(val_div, ":player_party_looting", 3),
(store_random_in_range, ":plunder_amount", ":player_party_looting", 5),
 
Not sure how troop_loot_troop works (or how efficient it is), but your code doesn't take into account how many troops are in each stack (you'll get as many items from a caravan master as 100 caravan guards)
This is true, but if you test it, you'll see that, on average, you get better loot, most of the time.  Of course you can always shake it down by percentages, or if the stack's really big, give a smallish chance of getting two of a thing.  I don't think it's necessary for my purposes, but I can see lots of ways that people can use it to juggle things in a different way.

limits the inventory slot at 30 so troops with item variation above a certain point won't be counted properly
I don't think there's any practical way to avoid having to make choices in that regard.  If you have 10 different troops, with merely 10 items apiece... you run out of slots pretty fast.  A sorter could be used to discard cheaper items, but it's a double-edged sword; most likely, there are reasonably-cheap things that you'd like to show up in the loot.  I may write one that goes back for a second round of randomness; I think it's better to have some cheap junk mixed up with the good stuff, because players often want to equip their Companions from looted items.

 
(val_mul, ":player_party_looting", 3),
...
(lt, ":loot_slot", 40), - can overfill your inventory
...
(store_random_in_range, ":rand", 1, 135),
..
(store_random_in_range, ":plunder_amount", 1, 5), - for trade goods only
(ge, ":rand", 100),
 
@Dusk Voyager thanks, its working great, but i need one more thing.
How to make loot to depend also on number of soldiers in stack? For now, we get staff only from one soldier per kind as @Somebody mentioned earlier.
Any ideas?
 
    #script_party_calculate_loot:
    # INPUT:
    # param1: Party-id
    # Returns num looted items in reg(0)
    ("party_calculate_loot",
      [
(store_script_param_1, ":enemy_party"), #Enemy Party_id
(troop_clear_inventory,"trp_temp_troop"),#Clear the temp troop inventory
        (assign, ":num_looted_items",0),
(assign, ":loot_slot", 10),
       
#Get looting skill
        (party_get_skill_level, ":player_party_looting", "p_main_party", "skl_looting"),
        (val_mul, ":player_party_looting", 3),
       
        (party_get_num_companion_stacks, ":num_stacks",":enemy_party"),
        (try_for_range, ":i_stack", 0, ":num_stacks"),
(lt, ":loot_slot", 40),#Don't overfill loot
          (party_stack_get_troop_id, ":stack_troop",":enemy_party",":i_stack"),
(try_for_range, ":current_inv", 0, 30),
(troop_get_inventory_slot,":my_inv",":stack_troop",":current_inv"),#Search for items
(gt, ":my_inv", 0),#We have a valid item
(store_random_in_range, ":rand", 1, 135),
(val_add, ":rand", ":player_party_looting"),

              (party_stack_get_size, ":size_modifier", ":enemy_party", ":i_stack"),
                (val_sub, ":size_modifier", 1),
                (val_mul, ":size_modifier", 5),
                (val_add, ":rand", ":size_modifier"),


(try_begin),
(ge, ":rand", 100),
(troop_set_inventory_slot, "trp_temp_troop", ":loot_slot", ":my_inv"),
(val_add, ":loot_slot", 1),#Bump the slot upwards, so that we don't overwrite
(val_add, ":num_looted_items", 1),
(try_end),
(try_end),
(try_end),

#Add random trade goods where appropriate
        (try_begin),
(this_or_next|party_slot_eq, "$g_enemy_party", slot_party_type, spt_kingdom_caravan),
(this_or_next|party_slot_eq, "$g_enemy_party", slot_party_type, spt_bandit_lair),
(party_slot_eq, "$g_enemy_party", slot_party_type, spt_village_farmer),

(party_get_skill_level, ":player_looting", "p_main_party", "skl_looting"),
(store_random_in_range, ":plunder_amount", 1, 5),
(val_add, ":plunder_amount", ":player_looting"),

              (party_get_num_companions, ":size_mod_for_goods", "$g_enemy_party"),
                  (val_sub, ":size_mod_for_goods", 1),
                  (val_div, ":size_mod_for_goods", 6),
                  (val_add, ":plunder_amount", ":size_mod_for_goods"),


(troop_add_merchandise, "trp_temp_troop", itp_type_goods, ":plunder_amount"),
(val_add, ":num_looted_items", ":plunder_amount"),
        (try_end),  
         
        (assign, reg0, ":num_looted_items"),
      ]),
Beware that the number of lines has changed, so it's not save-compatible.

PS. edited once more
 
Thanks a lot xeno and Dusk! Does this also replace  «#script_calculate_main_party_shares:» or just «#script_party_calculate_loot:»?
 
Thanks again, Dusk! I've been testing the script and this much smaller code allows for much better loot, indeed! It looks way better. Short and simple. I like it.

However, I'm having a minor setback, over here. Apparently, the new code also allows for some items to be looted, items which are not supposed to. I tried flagging them as unique, changing their abundance to 0 and even removing the itp_merchandise flag, but to no avail...

Think that there might be anything else that I could try, anyone?
 
Haha! Thanks, somebody. You always seem to riddle me with your answers. I always get the feeling that I'm close, but I can never make it o:

I tried researching the forums for «itp_unique» and stuff, but no luck... It seems this issue is specific to this script.

For some reason the native looting system doesn't seem to loot «itp_unique» items, but, noob as I am, I still couldn't find out how it does it, yet. I can't see nothing on the code even slightly related. Maybe it's because it doesn't allow you to loot equipped items?

The closest I could get was «item_get_type» and the command database (http://mbcommands.ollclan.eu/) doesn't seem to help much either...

Now, if I could find out a way to get the looting system to tell apart items flagged «itp_unique» and make them unlootable that would be great! Would anyone care to point me in the right direction? :smile:
 
The original looting script uses the operation troop_loot_troop, to which items with the flag itp_unique is apparently "immune".

But you can detect whether an item has it by giving it to a dummy troop at the start of the game, having the temp troop "loot" it via troop_loot_troop with the probability of 100, and then checking if the temp troop has managed to gain it. Finally, save the results to an item slot (0 = failed = item has the flag, 1 = success = no such flag or vice versa).

Generally, if it's impossible to directly recognize a flag or another property of an item, create a viable scenario that showcases its effects.
 
I see now, thanks a lot, again!

Although, I think it might be too messy for what I desired. Maybe I'll revert back to native looting or maybe I'll try figuring out how to use that native operation in a simple way o:
 
I tried writing a script like what I've outlined before, but looks like either looted items can't be tracked with operations returning the items in the inventory, or it has something to do with the delay between adding an item and it showing up in it. Thinking of adding a bunch of junk code or doing a header hack to add a ti_on_init_item trigger to every item setting an item slot to 1 to detect them in the loot screen, but can't bother anymore.
 
I hate when you need delays to get a value that's already there. I hope that's not the case...

Is this the solution? Is it really unique or unlootable?
http://forums.taleworlds.com/index.php/topic,296455.0.html

If so, could you give me an example to make it work for this script, pretty please? I couldn't understand much, save that you made a dummy troop and messed around with the inventory...
 
To be honest, I locked it long ago when it didn't stand up to testing. I should have checked it before creating the thread, not the other way around, but really thought it should have worked. Looks like items added as loot don't quite count as inventory items, so the only way out would be determining if the item is on the loot screen when a script opens it instead. Add a ti_on_init_item trigger to itm_no_item (if it can be done without problems) so that it increments a counter by 1 every time. Make a dummy troop with no items temporarily the player just before the screen appears. You can return the number of "your" and the temp troop's item capacity, identifying what's happened. Then store the final result into a new item slot, clear inventories and go back to the PC as the player troop.

Then again, I've read that unique items aren't exactly unlootable, but extremely hard to loot, so maybe setting the looting probability to the max just makes it always-lootable just like any other lootable item.
 
Back
Top Bottom