#include "std.zh"
//Simple Shop Script
//Once the script is loaded into the FFC there is tooltips now, press the buttons!
//A few constants to configure:
//offsets from the FFC for where the item is shown and where the price is listed.
const int S_SHOP_DISPLAY_X = 0;
const int S_SHOP_DISPLAY_Y = -8;
const int S_SHOP_PRICE_X = 0;
const int S_SHOP_PRICE_Y = -16;
const int S_SHOP_TEXT_CSET = 0x0F;
//Y coordinates for the item name display tag/box
const int S_SHOP_NAME_TEXT_Y = 8*18;
//If you don't want the shop to display the item, set this to 0:
const int S_SHOP_DISPLAY_ITEM = 1;
//same for price
const int S_SHOP_DISPLAY_PRICE = 1;
//Default font choice, for the proper values see std_constants.zh or ask a scripter (I guess).
const int S_SHOP_FONT = 0;
//Text Shadow colour. Set to "0" for none. ( 0x0F is the last colour in CSet 1)
const int S_SHOP_TXT_SHADOW = 0x0F;
//Default counter reference (if you want shops to be able to cost life you NEED to set this to "0")
const int S_SHOP_CR = 1; //CR_RUPEES
//The FFC changes to this combo ID and turns invisible right away, unless set to 0.
const int S_SHOP_HIDE = 1;
//Sound effects
const int S_SHOP_ERROR_SFX = 0; // This plays if you already own an item but try to purchase it, and the FFC toggle to disable that is active.
//Wealth medals only apply if the price is in Rupees if this is on. Set to 0 for off, 1 for on.
const int S_SHOP_WMEDAL_A = 1;
//Max Counter costs don't validate regular cost. If you turn this off (set to 0), you need the price amount as well as the max counter amount.
const int S_SHOP_MAX_C_VAL = 1;
@InitDHelp0 ("The Item ID of the item on Sale. If set to -1 use the Screen Catchall (special item) instead. -2 for the regular screen item (bad idea). If set to -10 to -17 reads Screen->D[] 0 to 7 for ID instead. If set to -20 to -27 reads Screen->InitD[] 0 to 7. And if set to -30 to -40 reads Screen->Data[] 0 to 10 for ID instead (This can be edited from the editor). "),
@InitDHelp1 ("Price of the item."),
@InitDHelp2 ("String that plays when you buy the item / can't afford. Former on the left side of the decimal, latter on the right side (Scope: four digits) as an example: 10.0002 would print String 10 for description and string 2 as error"),
@InitDHelp3 ("Set to 1 to display Item name on screen when browsing."),
@InitDHelp4 ("Button to press to buy: 0=A 1=B 2=L 3=R"),
@InitDHelp5 ("Font Colour on the left side of the decimal point, Font ID (3 digits) on the right side."),
@InitDHelp6 ("The counter ID that you use to buy with. 0 uses shop default defined in the script file (Rupees if unchanged). Set decimal number 1 if you want the shop to cost MAX of a counter. Note: Counters have sanity checks, Max can't be reduced below 0 for most, nor below 1 heart for life."),
@InitDHelp7 ("Set to 1 to prevent you from buying items you already own. Set to 2 to also hide the shop if you own it. Any over 2 makes it use screen ExState[] to track if you bought the item or not. |||Advanced:||| To replace shop with a different shop if you own the item, set to a negative number that corresponds to an FFC on the same screen with the new shop data. The FFC should not have a script assigned.")
ffc script SimpleShop{
void run(int itemID, int input_price, int StringIDs, int Tagging, int input, int Font_Data, int CounterID, int DisallowMultiPurchase){
Trace(CounterID);
//Changes drawing of the shop FFC to hide.
if(S_SHOP_HIDE > 0){
this->Data = S_SHOP_HIDE;
this->Flags[FFCF_LENSVIS] = true;
this->Flags[FFCF_LENSINVIS] = true;
}
this->Flags[FFCF_IGNOREHOLDUP] = true;
SetItemID(itemID, true);
//Loading in next shop ID data if you already own the item.
if(DisallowMultiPurchase < 0){
ffc NextItemShop;
int InCaseOfStupid = 0; //To prevent infinite loops on bad configuration.
while(DisallowMultiPurchase < 0 && Link->Item[itemID] && InCaseOfStupid < 300){
NextItemShop = Screen->LoadFFC(DisallowMultiPurchase - (DisallowMultiPurchase * 2));
itemID = NextItemShop->InitD[0];
input_price = NextItemShop->InitD[1];
StringIDs = NextItemShop->InitD[2];
Tagging = NextItemShop->InitD[3];
input = NextItemShop->InitD[4];
Font_Data = NextItemShop->InitD[5];
CounterID = NextItemShop->InitD[6];
DisallowMultiPurchase = NextItemShop->InitD[7];
SetItemID(itemID, false);
InCaseOfStupid ++;
if(InCaseOfStupid >= 280) return;
}
if(DisallowMultiPurchase < 0) DisallowMultiPurchase = 2;
}
//Exit shop if item is owned or is invalid.
if(DisallowMultiPurchase == 2 && Link->Item[itemID]) return;
else if(DisallowMultiPurchase >= 3 && Screen->ExState[DisallowMultiPurchase]){
return;
}
else if(itemID < 0) return;
Trace(CounterID);
// Dealing with D[] pointers that have extra data, usually by seperating.
int TEXT_COLOUR = Floor(Font_Data);
int TEXT_FONT = (Font_Data - TEXT_COLOUR) * 1000;
if(TEXT_FONT == 0) TEXT_FONT = S_SHOP_FONT;
int price = input_price;
int StringID = Floor(StringIDs);
int StringNotEnough = (StringIDs - StringID) * 10000;
bool CostMAX = false;
int CounterID_Dec = (CounterID - Floor(CounterID)) * 10;
if(CounterID_Dec == 1) CostMAX = true;
CounterID = Floor(CounterID);
if(CounterID <= 0) CounterID = S_SHOP_CR;
itemdata I_Data = Game->LoadItemData(itemID);
int PriceTag = Floor(Tagging);
//Applying wealth medal effects.
if(S_SHOP_WMEDAL_A == 0 || CounterID == 1){
itemdata WM;
int WM_ID = GetHighestLevelItemOwned(57);
if(WM_ID > -1){
WM = Game->LoadItemData(WM_ID);
if(WM->Flags[0]){
price = Floor(input_price * (WM->Attributes[0] / 100));
}
else{
price = Floor(input_price + WM->Attributes[0]);
}
}
}
int loc = ComboAt(this->X + 8,this->Y + 8);
if(TEXT_COLOUR <= 0) TEXT_COLOUR = S_SHOP_TEXT_CSET;
//Drawing of shop item functionality.
/* This is the old method, and it sucks.
int PriceOffset = 0;
if(price < 10) PriceOffset = PriceOffset +4;
else if(price > 99999) PriceOffset = PriceOffset -16;
else if(price > 9999) PriceOffset = PriceOffset -12;
else if(price > 999) PriceOffset = PriceOffset -8;
else if(price > 99) PriceOffset = PriceOffset -4;
*/
char32 PriceString[] = "Blah Blah";
sprintf(PriceString, "%d", price);
char32 NameString[99];
I_Data->GetDisplayName(NameString);
itemsprite DisplayGoods;
if(S_SHOP_DISPLAY_ITEM > 0){
DisplayGoods = CreateItemAt(itemID, this->X + S_SHOP_DISPLAY_X, this->Y + S_SHOP_DISPLAY_Y);
DisplayGoods->Pickup = IP_DUMMY;
DisplayGoods->HitWidth = 1;
DisplayGoods->HitHeight = 1;
DisplayGoods->HitXOffset = -512;
}
while(true){
if(S_SHOP_DISPLAY_PRICE > 0){
//Screen->DrawInteger(4, this->X + S_SHOP_PRICE_X + PriceOffset, this->Y + S_SHOP_PRICE_Y, S_SHOP_FONT, TEXT_COLOUR, -1, 0, 0, price, 0, OP_OPAQUE);
if(S_SHOP_TXT_SHADOW > 0) Screen->DrawString(4, this->X + S_SHOP_PRICE_X + 8 + 1, this->Y + S_SHOP_PRICE_Y + 1, TEXT_FONT, S_SHOP_TXT_SHADOW, -1, TF_CENTERED, PriceString, OP_OPAQUE);
Screen->DrawString(4, this->X + S_SHOP_PRICE_X + 8, this->Y + S_SHOP_PRICE_Y, TEXT_FONT, TEXT_COLOUR, -1, TF_CENTERED, PriceString, OP_OPAQUE);
}
//If Hero is in front of the item and can buy.
if(Link->Z == 0 && Link->Dir == DIR_UP && Link->Y > this->Y && Link->Y < this->Y + 14 && Link->X > this->X - 4 && Link->X < this->X + 4){
if(PriceTag == 1){
if(S_SHOP_TXT_SHADOW > 0) Screen->DrawString(6, 16*8 + 1, S_SHOP_NAME_TEXT_Y + 1, TEXT_FONT, S_SHOP_TXT_SHADOW, -1, TF_CENTERED, NameString, OP_OPAQUE);
Screen->DrawString(6, 16*8, S_SHOP_NAME_TEXT_Y, TEXT_FONT, TEXT_COLOUR, -1, TF_CENTERED, NameString, OP_OPAQUE);
}
//If the correct purchasing button is pressed.
if(Shop_SelectPressInput(input)){
bool Buying = false;
bool FailString = false;
bool ReduceMax = false;
Shop_SetInput(input,false);
if(CostMAX){ //Special handler for purchases that reduces MCounter.
if(CounterID == 0 && Game->MCounter[CounterID] < price + Game->Generic[GEN_HP_PER_HEART]){
FailString = true;
}
else if(Game->MCounter[CounterID] < price){
FailString = true;
}
else if(S_SHOP_MAX_C_VAL == 0 && Game->Counter[CounterID] < price){
FailString = true;
}
else{
ReduceMax = true;
}
}
if(BottleFiller(itemID) && !OwnEmptyBottle()){ //Main Shop purchase handler.
if(S_SHOP_ERROR_SFX > 0) Game->PlaySound(S_SHOP_ERROR_SFX);
}
else if(DisallowMultiPurchase == 1 && Link->Item[itemID]){
if(S_SHOP_ERROR_SFX > 0) Game->PlaySound(S_SHOP_ERROR_SFX);
}
else if(Game->Counter[CounterID] >= price || price <= 0){
Buying = true;
}
else{
FailString = true;
}
if(!Buying){
if(FailString) Screen->Message(StringNotEnough);
}
else if(Buying == true){
if(ReduceMax){
Game->MCounter[CounterID] -= price;
if(Game->Counter[CounterID] > Game->MCounter[CounterID]){
Game->Counter[CounterID] = Game->MCounter[CounterID];
}
}
else{
Game->DCounter[CounterID] -= price;
}
item shpitm = CreateItemAt(itemID, Link->X, Link->Y);
shpitm->Pickup = IP_HOLDUP;
shpitm->HitWidth = 128;
shpitm->HitHeight = 128;
shpitm->HitZHeight = 999;
shpitm->HitXOffset = -32;
shpitm->HitYOffset = -32;
Screen->Message(StringID);
if(DisallowMultiPurchase >= 3) Screen->ExState[DisallowMultiPurchase] = true;
}
}
}
//Exit shop if item is purchased under certain conditions
if(DisallowMultiPurchase == 2 && Link->Item[itemID]){
DisplayGoods->X = -42;
DisplayGoods->Y = -92;
return;
}
else if(DisallowMultiPurchase >= 3 && Screen->ExState[DisallowMultiPurchase]){
DisplayGoods->X = -42;
DisplayGoods->Y = -92;
return;
}
Waitframe();
}
}
bool AgainstFFCBase(int FFCX, int FFCY){
return Link->Z == 0 && Link->Dir == DIR_UP && Link->Y > FFCY && Link->Y < FFCY + 16 && Link->X > FFCY - 2 && Link->X < FFCY + 2;
}
//What's the point of this? the script should never exit from while(true)...?
bool AgainstComboBase(int loc){
return Link->Z == 0 && (Link->Dir == DIR_UP && Link->Y == ComboY(loc)+8 && Abs(Link->X-ComboX(loc)) < 8);
}
bool BottleFiller(int ItemID){
//Bottle fillers are by default Item Class: 271
itemdata Check = Game->LoadItemData(ItemID);
if(Check->Type == 271) return true;
return false;
}
bool OwnEmptyBottle(){
itemdata Check;
for(int i = 0; i < 256; i++){
Check = Game->LoadItemData(i);
if(Check->Type == IC_BOTTLE && Hero->Item[i]){
if(Game->BottleState[Check->Attributes[0]] == 0){
return true;
}
}
}
return false;
}
int SetItemID(int itemID, bool WaitAFrame){
if(WaitAFrame) Waitframe(); //This is to make sure any other script setting these have a chance to run on screen init.
if(itemID < 0){
int Fill = 0;
if(itemID == -1){
itemID = Screen->Catchall;
}
else if(itemID == -2){
itemID = Screen->Item;
}
else if(itemID <= -10 && itemID >= -17){
Fill = (itemID - (itemID * 2)) - 10;
itemID = Screen->D[Fill];
}
else if(itemID <= -20 && itemID >= -27){
Fill = (itemID - (itemID * 2)) - 20;
itemID = Screen->InitD[Fill];
}
else if(itemID <= -30 && itemID >= -40){
Fill = (itemID - (itemID * 2)) - 30;
if(Screen->DataSize < Fill) return -9999;
itemID = Screen->Data[Fill];
}
else{
Trace(4);
return -9999;
}
}
return itemID;
}
}
// This was made to use it's own unique functions instead, mostly since the functio names were too generic.
bool Shop_SelectPressInput(int input){
if(input == 0) return Link->PressA;
else if(input == 1) return Link->PressB;
else if(input == 2) return Link->PressL;
else if(input == 3) return Link->PressR;
return false;
}
void Shop_SetInput(int input, bool state){
if(input == 0) Link->InputA = state;
else if(input == 1) Link->InputB = state;
else if(input == 2) Link->InputL = state;
else if(input == 3) Link->InputR = state;
}