OSP Code SP (need more tests and optimizations) Improved caravans trade.

Users who are viewing this thread

bisthebis25

Knight
A simple script who changes the caravans destination : A caravan will calculate benefits with 2 towns, not one. Before, caravan choosed the best town. Now, they will choose a town for this AND potential tariffs from this town to another.

Repalce the old script_cf_select_most_profitable_town_at_peace_with_faction_in_trade_route by this.
Code:
("cf_select_most_profitable_town_at_peace_with_faction_in_trade_route",
    [
      (store_script_param, ":town_no", 1),
      (store_script_param, ":faction_no", 2),

      (assign, ":result", -1),
	  (assign, ":best_town_score", 0),
      (store_sub, ":item_to_price_slot", slot_town_trade_good_prices_begin, trade_goods_begin),

      (try_for_range, ":cur_slot", slot_town_trade_routes_begin, slot_town_trade_routes_end),
        (party_get_slot, ":cur_town", ":town_no", ":cur_slot"),
        (gt, ":cur_town", 0),
		
        (store_faction_of_party, ":cur_faction", ":cur_town"),
        (store_relation, ":reln", ":cur_faction", ":faction_no"),
        (ge, ":reln", 0),

		(assign, ":cur_town_score", 0),
		(try_for_range, ":cur_goods", trade_goods_begin, trade_goods_end),
			(neq, ":cur_goods", "itm_butter"), #Don't count perishables
			(neq, ":cur_goods", "itm_cattle_meat"),
			(neq, ":cur_goods", "itm_chicken"),
			(neq, ":cur_goods", "itm_pork"),
			
            (store_add, ":cur_goods_price_slot", ":cur_goods", ":item_to_price_slot"),
			(party_get_slot, ":origin_price", ":town_no", ":cur_goods_price_slot"),
			(party_get_slot, ":destination_price", ":cur_town", ":cur_goods_price_slot"),
						
			(gt, ":destination_price", ":origin_price"),
			(store_sub, ":price_dif", ":destination_price", ":origin_price"),
			
			(try_begin), #weight luxury goods double
				(this_or_next|eq, ":cur_goods", "itm_spice"),
					(eq, ":cur_goods", "itm_velvet"),
				(val_mul, ":price_dif", 2),
			(try_end),
			(val_add, ":cur_town_score", ":price_dif"),
		(try_end),
		
##		(try_begin),
##			(eq, "$cheat_mode", 1),
##			(str_store_party_name, s10, ":town_no"),
##			(str_store_party_name, s11, ":cur_town"),
##			(assign, reg3, ":cur_town_score"),
##			(display_message, "str_caravan_in_s10_considers_s11_total_price_dif_=_reg3"),
##		(try_end),
		
		(gt, ":cur_town_score", ":best_town_score"),
		(assign, ":best_town_score", ":cur_town_score"),
		(assign, ":result", ":cur_town"),

#------------------------------#
#------------------------------#
#----Script 2nd town begins----#
#------------------------------#
#------------------------------#	

#town_no : origin of caravan
#cur_town : first potential step
#cur_town2 : second potential step
	
	
		(assign, ":town_2_best", 0),
		(assign, ":town_2_best_score", 0),
		(try_for_range, ":cur_slot", slot_town_trade_routes_begin, slot_town_trade_routes_end),
			#(party_get_slot, ":cur_town2", ":town_no", ":cur_slot"),
			(party_get_slot, ":cur_town2", ":result", ":cur_slot"),
			(gt, ":cur_town2", 0),
			
			(store_faction_of_party, ":cur_faction", ":cur_town2"),
			(store_relation, ":reln", ":cur_faction", ":faction_no"),
			(ge, ":reln", 0),
			
			(assign, ":cur_town2_score", 0),
			(try_for_range, ":cur_goods", trade_goods_begin, trade_goods_end),
				(neq, ":cur_goods", "itm_butter"), #Don't count perishables
				(neq, ":cur_goods", "itm_cattle_meat"),
				(neq, ":cur_goods", "itm_chicken"),
				(neq, ":cur_goods", "itm_pork"),
			
				(store_add, ":cur_goods_price_slot", ":cur_goods", ":item_to_price_slot"),
				(party_get_slot, ":origin_price", ":cur_town", ":cur_goods_price_slot"),
				(party_get_slot, ":destination_price", ":cur_town2", ":cur_goods_price_slot"),
						
				(gt, ":destination_price", ":origin_price"),
				(store_sub, ":price_dif", ":destination_price", ":origin_price"),
			
				(try_begin), #weight luxury goods double
					(this_or_next|eq, ":cur_goods", "itm_spice"),
					(eq, ":cur_goods", "itm_velvet"),
					(val_mul, ":price_dif", 2),
				(try_end),
			
			(val_add, ":cur_town2_score", ":price_dif"),
		
			(try_end),
			
			(gt, ":cur_town2_score" ,":town_2_best_score"),
			(assign, ":town_2_best_score", ":cur_town2_score"),
			(assign, ":best_town2", ":cur_town2"),
			(assign, reg1, ":cur_town2"),
		(try_end),
		
		(val_add, ":cur_town_score", ":best_town2"),
		
		(gt, ":cur_town_score", ":best_town_score"),
		(assign, ":best_town_score", ":cur_town_score"),
		(assign, ":result", ":cur_town"),

		
		

#------------------------------#
#------------------------------#
#-----Script 2nd town ends-----#
#------------------------------#
#------------------------------#

In module_simple_triggers, change this (line 2016 on native 1.153 MS :
Code:
(call_script, "script_cf_select_most_profitable_town_at_peace_with_faction_in_trade_route", ":cur_center", ":merchant_faction"),
             (assign, ":target_center", reg0),

By
Code:
(call_script, "script_cf_select_most_profitable_town_at_peace_with_faction_in_trade_route", ":cur_center", ":merchant_faction"),
             (assign, ":target_center", reg0),
			 (party_set_slot, ":party_no", slot_caravan_future_destination, reg1),

create this constant
Code:
slot_caravan_future_destination = 900,

In dialogs, change this ; 
Code:
We are coming from {s11} and heading to {s12}.{s14}
by this :
Code:
We are coming from {s11} and heading to {s12}.{s14} ^^ After, we think we will go to {s17}

In conditions of this line, add this :
(party_get_slot, ":eek:rigin", "$g_encountered_party", slot_party_last_traded_center),
    (party_get_slot, ":destination", "$g_encountered_party", slot_party_ai_object),
  (str_store_party_name, s11, ":eek:rigin"),
  (str_store_party_name, s12, ":destination"),
 
  (str_store_string, s14, "str___we_believe_that_there_is_money_to_be_made_selling_"),
      (store_sub, ":item_to_price_slot", slot_town_trade_good_prices_begin, trade_goods_begin),
  (assign, ":at_least_one_item_found", 0),
  (try_for_range, ":cur_goods", trade_goods_begin, trade_goods_end),
        (store_add, ":cur_goods_price_slot", ":cur_goods", ":item_to_price_slot"),
(party_get_slot, ":eek:rigin_price", ":eek:rigin", ":cur_goods_price_slot"),
(party_get_slot, ":destination_price", ":destination", ":cur_goods_price_slot"),

(gt, ":destination_price", ":eek:rigin_price"),
(store_sub, ":price_dif", ":destination_price", ":eek:rigin_price"),

(gt, ":price_dif", 200),
(str_store_item_name, s15, ":cur_goods"),
(str_store_string, s14, "str_s14s15_"),

(assign, ":at_least_one_item_found", 1),
  (try_end),

  (try_begin),
(eq, ":at_least_one_item_found", 0),
    (str_store_string, s14, "str__we_carry_a_selection_of_goods_although_the_difference_in_prices_for_each_is_not_so_great_we_hope_to_make_a_profit_off_of_the_whole"),
  (else_try),
(str_store_string, s14, "str_s14and_other_goods"),
 
 
  (party_get_slot, ":future_destination","$g_encountered_party", slot_caravan_future_destination),
  (str_store_party_name, s17, ":future_destination"),

  (try_end),
 

I will add screenshots this evening.

PS : i know i can improves the script  :oops:
 
Er, does it actually change the caravan behavior?
Correct me if I'm wrong, but at first glance the code only adds additional information about next potential destination to dialogs.
The first target does not depend on the second, does not it?

 
It's very good additional info, indeed even it may not change the caravan behavior. May be, you can add more relation check between you and the caravan's faction for the caravan leader to decide if he will reveal his next destination to you or not. BTW, in the time the caravan reach the 1st town, the prices can be changed, and he can change his mind about the destination.
 
BTW, in the time the caravan reach the 1st town, the prices can be changed, and he can change his mind about the destination.

Yes that's why the slot_caravan_future_destination is only used for the dialog. The script will use potentiel tariffs between 1st town and potential 2nd for choose the first.
A quick-made schema for explain (without price change):
(Red points are towns, black lines trade routes, text are tariffs from north to south, green lines trade routes chosen by Native script, blue lines by my script. I do not consider the possibility of a Comeback to original town. It's possible in Calradia, not in my scheme :wink: Tariffs are in an abstract unit, by myself)
fig1e.png
A second, with changes prices during a travel. I consider that price will be changed when caravan reach first town (middle line). The first number is original price, the second price afterchange. (Green lines, native behavior, blue lines, planned travel with my script, orange lines, real travel. It will be better with a 3rd town, but they aren't here)
fig2.png

I don't know if you undertsand me, but my script is flexible :smile:

PS : I made dialogs for test, mostly. Free to modders to disable or add conditions if they use this)
 
Ok, that makes sense.

Here's my take on it.
Whether caravans maximize their profits is largely irrelevant to the player, although by asking them for their routes, a shrewd player may discover what are (or were :smile:) profitable trade runs, especially if you expand the dialogs with expected buy/sell prices.
What would help the player more is if you do the same thing for the "Assess prices" dialog in the town merchant menu. Find him more complex (or simply your two town routes) that would make most money, using the same code. Allow for returning to the same town.
Simplify the code by using one loop, executed twice.
 
(Sorry if I don't undertsand all, my english is poor and I'm tired, though it's only 10 PM)

Whether caravans maximize their profits is largely irrelevant to the player.

For a simple trader, it's useless, but for a lord, it's very useful, beacause caravans are very important for taxes in cities. (and, ingame, caravans don't gains money, only gives taxes, change prices and increase prosperity). But, it's true that influence of caravans is very little for a merchant player without cities.

although by asking them for their routes, a shrewd player may discover what are (or were :smile:) profitable trade runs, especially if you expand the dialogs with expected buy/sell prices.What would help the player more is if you do the same thing for the "Assess prices" dialog in the town merchant menu. Find him more complex (or simply your two town routes) that would make most money, using the same code.

AI's trade is very, very different that human's trade. Firstly, caravans don't transport real goods, they just do this virtually. So, they don't have quantity limits. But an "assess price" for caravans (with more precisions, only for origin town ? And destination ? Or 2-3 most recents destinations.) can be interesting. this is far from what I wanted, but i will - maybe - do that.

Allow for returning to the same town.
Oh, with actual script it is possible to return to origin town (AI sometimes do this in my tests with actual script). But i didn't include this possibility in my schemas, for simplify. (Oh, i see that my 2nd schema was a fail for tariffs  :roll: :roll:)

Simplify the code by using one loop, executed twice.
I have little experience in scripting. I don't know how I can do that...

 
Ah, I see your point now. But no offense, I prefer native way. Counting total potential tarif for 2 towns ahead 's great, but potential tarif of 2nd town will be out of date when he arrive to 1st town, as he decided the 1st town not always the best tarif he would get, then the scheme 's not always goes to his fortune. May be he even could get better total tarif if he went to the native scheme. It's still 50-50 chance, I think.
It gives me idea, may be we should count the distance as a factor to decide the destination. Let's say town A Will give 100 tarif, but it's on the other side of the world map. By the same time, the caravan could go to town B, then town C that each gives him 60 and 50, and total tarif he will get will be bigger with the same distance of traveling.
 
:smile:
You may be thinking too hard about how to make caravans efficient - it really doesn't matter. They may travel randomly with random goods and that may be even better for the game, as the prices would allow for more lucrative trading for the player. Now, trade runs are hardly worth it - that's the real issue that needs to be addressed (also, adjusting the village prices down, they are too high).

But, if we go with your optimization, in addition to distance and travel time, you may work in some bias for the second run, as prices are uncertain, like this:
Total profit = First_run_profit + 0.8 * Second_run_profit
Where 0.8 means: there is a 80% chance that by the time the first run is completed, the second run will make the calculated profit. You may get a better estimate (than 80%), if you write code that collects data about the expected and real profitability of the second runs, and let the game play itself for a while.
 
MadVader said:
Now, trade runs are hardly worth it - that's the real issue that needs to be addressed (also, adjusting the village prices down, they are too high).

you consider that calradian trade is too basic and randomly ?
 
bisthebis25 said:
MadVader said:
Now, trade runs are hardly worth it - that's the real issue that needs to be addressed (also, adjusting the village prices down, they are too high).

you consider that calradian trade is too basic and randomly ?
It's not worthwhile for the player. If you read what players do, is they trade iron from Curaw to Tulga, and use a few other profitable routes.
Trading is important in the early game, and the player party's typically low Trade skill doesn't help there, so there is no profit to be made for most goods.
Trading should be made more profitable for a wide variety of trade goods. Village prices are typically too high for foods, compared to town prices - this should be corrected too.

EDIT: Also different enterprises should be more profitable in different towns. Now it's all velvet and a couple of tanneries.
 
MadVader said:
(also, adjusting the village prices down, they are too high).
Then we should lower slot_item_rural_demand for some food items. Even -1 do make sense for villages, as they should able to produce their own food and the villagers 're too simple persons to want eat foods they can't produce.

BTW. I don't want to optimize the caravan's profit. I want them more random moving between the towns, and make unique effect to prices to the towns. This is my concept:

1.
#script_do_party_center_trade
# INPUT: arg1 = party_no, arg2 = center_no, arg3 = percentage_change_in_center
I want arg3 's set from caravan's party slot. It's like their money power that define the amount of trading values they can do. It should grow as he successfully make a trading trip until it reach a limit. Then there will be once more good reason to protect them. One more, the slot will effect the caravan's guard quantity too.

2. (try_for_range, ":cur_good", trade_goods_begin, trade_goods_end),
It's assumed that the caravan make trading for all goods. Well, we need specializations. Some caravan will only trade foods, some will only trade tools, some will do for raw materials. It may effect enterprise too as not all caravans come to lower raw materials price for them.


 
1.
#script_do_party_center_trade
# INPUT: arg1 = party_no, arg2 = center_no, arg3 = percentage_change_in_center
I want arg3 's set from caravan's party slot. It's like their money power that define the amount of trading values they can do. It should grow as he successfully make a trading trip until it reach a limit. Then there will be once more good reason to protect them. One more, the slot will effect the caravan's guard quantity too.

The actual percentage is 4. It's very little, no ? (with a price difference of 400, it will be increased/decreased by 16 only with caravans... You want "rich caravan" with an higher percentage, and poor (or little) caravans ? Interesting. If caravans use their money, it can be great.

2. (try_for_range, ":cur_good", trade_goods_begin, trade_goods_end),
It's assumed that the caravan make trading for all goods. Well, we need specializations. Some caravan will only trade foods, some will only trade tools, some will do for raw materials. It may effect enterprise too as not all caravans come to lower raw materials price for them.
What do you want ? more spt : food's caravans, luxury (spices, silk,...) caravans, raw materials caravans, ... ? Or just caravans who will only transport the most 10 prfitables godds ?
 
bisthebis25 said:
The actual percentage is 4. It's very little, no ? (with a price difference of 400, it will be increased/decreased by 16 only with caravans... You want "rich caravan" with an higher percentage, and poor (or little) caravans ? Interesting. If caravans use their money, it can be great.
Poor caravan will have 1%. After some trips it will be raised to 2%. We can make 4% as the limit.

bisthebis25 said:
What do you want ? more spt : food's caravans, luxury (spices, silk,...) caravans, raw materials caravans, ... ? Or just caravans who will only transport the most 10 prfitables godds ?

Caravans will have types.

Code:
(try_begin),
   (party_slot_eq, ":party_no", slot_caravan_type, caravan_type_general),
   (assign, ":lo_limit",  trade_goods_begin),
   (assign, ":hi_limit",  trade_goods_end),
(else_try),
   (party_slot_eq, ":party_no", slot_caravan_type, caravan_type_cloth),
   (assign, ":lo_limit",  itm_wool),
   (assign, ":hi_limit",  itm_iron),
(else_try),
   (party_slot_eq, ":party_no", slot_caravan_type, caravan_type_tool),
   (assign, ":lo_limit",  itm_iron),
   (assign, ":hi_limit",  itm_raw_leather),
(try_end),
# and may be we should rearrange the module_items,py first to make it easier to define the range.

# and caravans do trade only for items they buy/sell :
(try_for_range, ":cur_good",":lo_limit", ":hi_limit"),
    (store_add, ":cur_good_price_slot", ":cur_good", ":item_to_price_slot"),
 
specialized type caravans should have bigger percentage for price change than general caravans.
 
I see.
Why do you have a type_general in your example ?

What types are good ? food ? luxuries ? cloth ? raw materials ?
 
bisthebis25 said:
I see.
Why do you have a type_general in your example ?

What types are good ? food ? luxuries ? cloth ? raw materials ?
Because, there are merchants that do business to all goods.

And yes, the types will be like you mentions. My example 's just prof of concept. Some goods can be qualified as more than 1 type, so simple range won't work. A slot_item_trading_good_type to the items and bitmasking to the values will be more practical.
 
Because, there are merchants that do business to all goods.
They will gain more than others, so ? Or it will be better to give them a lower percentage of prices changes ?

And yes, the types will be like you mentions. My example 's just prof of concept. Some goods can be qualified as more than 1 type, so simple range won't work. A slot_item_trading_good_type to the items and bitmasking to the values will be more practical.
bitmsaking ? I don't understant this word, sorry  :oops:

But we will need a slot_item_trading_good_type_1, slot_item_trading_good_type_2 and maybe 3 if we want goods of 2 or 3 types, no ?
With a (this_or_next|item_slot_eq, ...

I will make a (temporary) list of items types. I will need your(s) opinion(s) (maybe in 1 hour, but i keep connected, for others things to discuss here :smile: )
 
Quick-made list. I need opinions about divisions/fusions, and assignations.

LG : luxury googs
RM : raw material
HWG : handworked goods
LF : luxury food
BF : basic food
SpicesLG
SaltLG (RM for dired meat ? not sure)
OilLG and HWG
PoteryHWG
Raw flaxRM
LinenHWG
WoolRM
Wool clothsRM, cloths
Raw silk??? RM ? it's only RM for a luxury good
Raw dyesthe same.
VelvetHWG, LG and/or cloths ?
IronRM
ToolsHWG
Leather (raw)RM
Leather (worked)cloths ? HWG
dat fruitsLF
furscloths ?
WineHWG ? LG ?
AleHWG
Smoked fishBF
cheeseLF
HoneyLF
sausagesLF
cabbagesBF
Dred meatLF
fruitsBF
grapesLF, RM
olivesLF, RM
grainBF, RM
BreadBF, HWG

What do you think ?
 
Nice list.

Then, let :
BF =  1
LF  = 2
RM = 4
HWG = 8
LG = 16
Cloth = 32
caravan_type_general = BF|LF|RM|HWG|LG|Cloth
caravan_type_luxury = LF|LG
caravan_type_food = BF|LF

So we can set on item initialization :
(item_set_slot, itm_oil, slot_item_trading_type, LG|HWG),
(item_set_slot, itm_spice, slot_item_trading_type, LG),


Caravan type can be defined at party creation  :
Let a caravan is luxury things trader :
(party_set_slot, ":party_no", slot_caravan_type,  caravan_type_luxury),  # caravan_type_luxury = LG|LF

Let a caravan is food trader :
(party_set_slot, ":party_no", slot_caravan_type,  caravan_type_food),



Then in the looping for goods we can check :

(party_get_slot, ":caravan_type", ":party_no", slot_caravan_type),
(try_for_range, ":cur_good", trade_goods_begin, trade_goods_end),
    (item_get_slot, ":good_type", ":cur_good", slot_item_trading_type),
    (val_and, ":good_type", ":caravan_type"),  #
    (gt, ":good_type", 0),                                    # only for goods that caravan do business to
    (store_add, ":cur_good_price_slot", ":cur_good", ":item_to_price_slot"),
 
Back
Top Bottom