Copy to Clipboard Test

Simple Shop^2 Code

#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;
}