Fixed it all up and simplified it a bit.
Code: Select all
SHOP PSEUDOCODE
cur_time = Get_Minutes();
last_accessed = shop.last_accessed;
if(last_accessed > cur_time)
cur_time = 1;
time_dif = cur_time - last_accessed;
if(time_dif < shop.refresh_period){
Skip_Updates();
return;
}
else{
for(i=0;i<shop.total_items;i++){
new_quantity = shop.item[i].current_quantity;
if(item[i].refresh_period <= time_dif){
if(item[i].standard_quantity > shop.item[i].current_quantity){ //try to normalize towards standard
new_quantity += time_dif/item[i].refresh_period;
if(new_quantity > item[i].standard_quantity)
new_quantity = item[i].standard_quantity; //If you overshoot, then just set to standard
}
else{
new_quantity -= time_dif/item[i].refresh_period;
if(new_quantity < item[i].standard_quantity)
new_quantity = item[i].standard_quantity;
}
quantity_rand = RNG(-1:1);
temp = quantity_rand * item[i].swing; //multiply sets by RNG
new_quantity = abs(new_quantity + temp - (item[i].swing/2));
}
price_mod = item[i].standard_price * (item[i].standard_quantity - new_quantity);
if(item[i].standard_quantity > 2){
price_mod = price_mod / item[i].standard_quantity; //if it's a high-stock item, divide price_mod
}
else{
price_mod = abs((price_mod / 3)); //otherwise divide by 3
}
price_mod+= item[i].standard_price;
if(price_mod > 65000)
price_mod = 65000;
refresh_cycles = time_dif / shop.refresh_period;
if(refresh_cycles > 4)
refresh_cycles = 4; //Cap refresh_cycles to 4
final_price = (price_mod - shop.item[i].prior_price) * refresh_cycles;
if(final_price < 0)
final_price+=3;
final_price = final_price/4; //Divide by 4
price_rand = RNG(0:10);
final_price = price_rand + shop.item[i].prior_price+ price_rand - 5;
other_price = (2/3) * item[i].standard_price;
if(final_price < (other_price/2))
final_price = (other_price/2);
if(final_price>65000)
final_price = 65000;
item[i].current_price = final_price;
item[i].current_quantity = new_quantity;
}
}
I was missing a few key details in the prior version. Before purchasing for the first time, the initialization code is fairly straightforward. The main idea is that price is set based on the new quantity, which has one of three possible values from the RNG. The "swing" of an item is the amount that it varies as a result of this RNG. It is centered at 0, but the "high-stock" value should be about half of the swing value above the standard quantity, while the "low-stock" value will be 1.5x the swing value below the standard. The average case is .5x swing value below standard. Price follows pretty simply for initialization.
If you're revisiting the shop, however, things get a bit trickier. Items follow a "restocking" or "consumption" pattern if you've bought/sold the good before. You can think of it as the shop gradually moving back towards its standard quantity, either by consuming excess goods or accumulating missing stock. This would normally by awesome, however the shop also keeps track of the last price you purchased your good for and uses it as part of the calculations. This prevents the price from suddenly skyrocketing if you buy up all their goods; the change is gradual, and only accumulates as the shop cycles accumulate. It's critical to note that the shop cycle is what determines the pricing, and not the item cycles.
I have full tables for the trading posts with the standard quantities, prices, sets, and refresh periods. You can check them out here:
https://docs.google.com/spreadsheets/d/ ... sp=sharing
As far as analysis of this, I'm still working on it. I at least reproduced something to predict shop prices, but I might just be missing something as far as the quantity RNG. Looking at the code it seems like there should be 3 distinct possibilities, but in practice I only seem to ever see 2. As far as I can tell the math doesn't try to make the RNG value positive, so I'm not sure why it doesn't show up. In any case, this should get us started on determining best trading practices.
EDIT: Below is the disassembly block related to that RNG call, so let me know if I missed something along the way.
Code: Select all
ROM:0010E714 Quantity_RNG: # CODE XREF: sub_10E4CC:loc_10E708j
ROM:0010E714 ori $2, 0x4C2C # Set up RNG Call
ROM:0010E718 lui $8, 0x8012
ROM:0010E71C jalr $2 # RNG CALL 1, loaded into r2
ROM:0010E720 sw $2, 0x8012008C
ROM:0010E724 andi $18, ITEM_COUNT, 0xFF
ROM:0010E728 sll $3, $18, 1
ROM:0010E72C addu $3, $18
ROM:0010E730 sll $3, 2 # Address stuff
ROM:0010E734 addu $3, TRADER_ADDRESS, $3 # Add to base addr (80016bd0)
ROM:0010E738 lbu $3, 0xBC($3)
ROM:0010E73C move $4, $2 # Hang on to it for later
ROM:0010E740 sll $2, $3, 2
ROM:0010E744 addu $2, $3
ROM:0010E748 sll $2, 1
ROM:0010E74C addu $2, $22 #set up addresses and structure indexing
ROM:0010E750 lbu $5, 4($2) # Item swing
ROM:0010E754 bgez $4, loc_10E764 # If RNG positive... branch
ROM:0010E758 sra $2, $4, 14 # If RNG negative...
ROM:0010E75C addiu $4, 0x3FFF # Add 0x3FFF to value before right shifting
ROM:0010E760 sra $2, $4, 14
ROM:0010E764
ROM:0010E764 loc_10E764: # CODE XREF: sub_10E4CC+288j
ROM:0010E764 mult $5, $2 # Mult by -1,0, or 1
ROM:0010E768 srl $2, $5, 1 # Cut Swing in half...
ROM:0010E76C andi Final_Price, $23, 0xFF
ROM:0010E770 mflo $8
ROM:0010E774 addu $3, Final_Quantity, $8 # Add mult amount to prior quantity
ROM:0010E778 subu Final_Quantity, $3, $2 # then subtract half of the swing
EDIT2: Running through a few things for price, there are still some inconsistencies, most notably when the standard quantity is -1. I'll keep trying to iron it out.
EDIT3: Figured out why the negative branch for the random quantity adjustment never gets hit. The RNG function they use ANDs the result with 0x7FFF before passing it along. This makes it so that it will never get interpreted as a negative value, and thus the RNG will only ever turn out to be 0 or 1, instead of a spread between 0, 1, and -1. It works out fine in the end, but it may be a bug since they pretty obviously tried to account for it in the code. Either that or it is an artifact from different points in development.