{
================================================================
Script name: neo's Inscription trainer
Author: neo
Version: 1.1
Client tested with: Stealth Interface 7.0.29.2
Stealth version tested with: 4.3.6
Shard: OSI
Revision Date: March 26, 2013
Public Release: March 23, 2013
Purpose: Trains Inscription from 32.5 to GM
Special thanks: Crome who has helped me A LOT, teaching me the
                ropes on Stealth and giving inumerous pointers.
                Cheers! :) 
================================================================
Version 1.1       - Modified the setup part
                  - Will put books inside secure when skillcap
                    is reached
                  - Removed the need for a full spellbook. Now
                    you just need a book with recall and mark.
                  - A few minor improvements
----------------------------------------------------------------
Version 1.0       - Public release
================================================================
Instructions: - Have 2 secures close to you: one with reagents,
                blank scrolls and ingots. The other
                is where your crafted scrolls and super slayer 
                books will be stored (you can add more properties
                to check for. Check 'GoodPropertiesArray' and add
                more if needed.
              - Have a trash barrel nearby. You will need to trash
                crappy books. 
              - Have at least 1 tinker tools inside your backpack
                before you start.
              - When you hit play, the script will ask you to
                open the containers one by one(material, books and trash)
              - You NEED A FULL SPELLBOOK in your bag for this!!
------------------------------------------------------------------------
32.5 to 60.0 -> Crafts Recall scrolls
60 to 65 -> Crafts Mark scrolls
65+ -> Crafts Spellbook, keeps Super Slayers and trashes the rest
========================================================================
Only use this script if downloaded from the following places:
http://www.scriptuo.com/index.php?topic=11033.0
http://stealth.od.ua/forum/viewtopic.php?f=15&t=2495
========================================================================
}
//===== Variables =====
Program Inscription;

var LastUnload : Boolean;
var LastCont : Cardinal;
var GoodPropertiesArray : Array of String;
var SelfID : Cardinal;
var CurrentSkill : Double;
var IsMeditating : Boolean;
var Secure : Cardinal;
var SafeContainer : Cardinal;
var Trash : Cardinal;
var gi : TGumpInfo;
var StartTime : TDateTime;
var StartingSkill : Double;
var Status : String;
var LoadLimit : Integer;
var ScrollAmount : Integer;
var ReagentAmount : Integer;
//===== Constants =====
//----- Craft Buttons and Info -----
const CraftRecallButton = 32;
const RecallArgs = '#1044412';
const RecallElemNum = 87;
const cat2button = 9002; //third/fourth circle
const CraftMarkButton = 45;
const MarkArgs = '#1044425';
const MarkElemNum = 78;
const CraftSpellBookButton = 202;
const SpellBookArgs = '#1023834';
const SpellBookElemNum = 49; 
const cat6button = 9006; //Other - Spellbook
const cat3button = 9003; //fifth/sixth circle - Tinker tools
const PenButton = 30;
const TinkerButton = 11;
const TinkerArgs = '#1044164';
const TinkerElemNum = 58;
//----- Item Types -----
const PenType = $0FBF;
const BloodMoss = $0F7B;
const BlackPearl = $0F7A;
const MandrakeRoot = $0F86;
const BlankScroll = $0EF3;
const Ingots = $1BF2;
const TinkerToolsType = $1EB9;
const RecallScrollType = $1F4C;
const MarkScrollType = $1F59; 
const SpellBookType = $0EFA;
//----- Buff/Debuff Info -----
const ActiveMeditationID = 1013;
//----- General Gump Info -----
const CraftingGumpID = $01CC;
const InscriptionCliloc = 1044009;
const TinkeringCliloc = 1044007;  
//----- other -----
const MinimumMana = 12;
const MarkMana = 18;
const ShortWait = 1;
const MedWait = 10;
const LongWait = 20;
const WeightOffset = 5;
const FullBook = '~1_NUMBERS_OF_SPELLS~ Spells';
//===== menu stuff =====
var Menu : TSTForm;
var GeneralInfoBox : TSTGroupBox;
var StatusBox : TSTGroupBox;
var StartingSkillLabel : TSTLabel;
var SkillGainsLabel : TSTLabel;
var CurrentSkillLabel : TSTLabel;
var TimeRunningLabel : TSTLabel;
var StatusLabel : TSTLabel;
var StartingSkillLbl : TSTLabel;
var SkillGainsLbl : TSTLabel;
var CurrentSkillLbl : TSTLabel;

Procedure GUI_OnClose(Sender: TObject; var Action : TCloseAction);
begin
	Menu.Free();
	raiseException(erCustomError,'Menu was closed. Halting!');
	Exit;
end;

Procedure MenuInit();
begin
	Menu := TSTForm.Create();
	Menu.BorderStyle := bsSingle;
	Menu.Color := clBtnFace;
	Menu.Height := 151;
	Menu.Left := 0;
	Menu.Top := 0;
	Menu.Width := 319;
	Menu.Caption := 'neo''s Inscription Trainer'
	Menu.OnClose := @GUI_OnClose;
	GeneralInfoBox := TSTGroupBox.Create(Menu);
	GeneralInfoBox.Parent := Menu;
	GeneralInfoBox.Height := 65;
	GeneralInfoBox.Left := 8;
	GeneralInfoBox.Top := 8;
	GeneralInfoBox.Width := 297;
	GeneralInfoBox.Caption := 'General Info';
	StatusBox := TSTGroupBox.Create(Menu);
	StatusBox.Parent := Menu;
	StatusBox.Height := 34;
	StatusBox.Left := 8;
	StatusBox.Top := 79;
	StatusBox.Width := 297;
	StatusBox.Caption := 'Status';
	StartingSkillLabel := TSTLabel.Create(GeneralInfoBox);
	StartingSkillLabel.Parent := (GeneralInfoBox);
	StartingSkillLabel.Height := 13;
	StartingSkillLabel.Left := 11;
	StartingSkillLabel.Top := 16;
	StartingSkillLabel.Width := 62;
	StartingSkillLabel.Caption := 'Starting Skill:';
	SkillGainsLabel := TSTLabel.Create(GeneralInfoBox);
	SkillGainsLabel.Parent := (GeneralInfoBox);
	SkillGainsLabel.Height := 13;
	SkillGainsLabel.Left := 11;
	SkillGainsLabel.Top := 35;
	SkillGainsLabel.Width := 50;
	SkillGainsLabel.Caption := 'Skill Gains:';
	CurrentSkillLabel := TSTLabel.Create(GeneralInfoBox);
	CurrentSkillLabel.Parent := (GeneralInfoBox);
	CurrentSkillLabel.Height := 13;
	CurrentSkillLabel.Left := 152;
	CurrentSkillLabel.Top := 16;
	CurrentSkillLabel.Width := 61;
	CurrentSkillLabel.Caption := 'Current Skill:';
	TimeRunningLabel := TSTLabel.Create(GeneralInfoBox);
	TimeRunningLabel.Parent := (GeneralInfoBox);
	TimeRunningLabel.Height := 13;
	TimeRunningLabel.Left := 152;
	TimeRunningLabel.Top := 35;
	TimeRunningLabel.Width := 115;
	TimeRunningLabel.Caption := 'Time Running: 00:00:00';
	StatusLabel := TSTLabel.Create(StatusBox);
	StatusLabel.Parent := (StatusBox);
	StatusLabel.Height := 13;
	StatusLabel.Left := 11;
	StatusLabel.Top := 16;
	StatusLabel.Width := 100;
	StatusLabel.Caption := '';
	StartingSkillLbl := TSTLabel.Create(GeneralInfoBox);
	StartingSkillLbl.Parent := (GeneralInfoBox);
	StartingSkillLbl.Height := 13;
	StartingSkillLbl.Left := 80;
	StartingSkillLbl.Top := 16;
	StartingSkillLbl.Width := 31;
	StartingSkillLbl.Caption := FloatToStrF(StartingSkill,ffFixed,12,1);
	SkillGainsLbl := TSTLabel.Create(GeneralInfoBox);
	SkillGainsLbl.Parent := (GeneralInfoBox);
	SkillGainsLbl.Height := 13;
	SkillGainsLbl.Left := 80;
	SkillGainsLbl.Top := 35;
	SkillGainsLbl.Width := 31;
	SkillGainsLbl.Caption := '';
	CurrentSkillLbl := TSTLabel.Create(GeneralInfoBox);
	CurrentSkillLbl.Parent := (GeneralInfoBox);
	CurrentSkillLbl.Height := 13;
	CurrentSkillLbl.Left := 226;
	CurrentSkillLbl.Top := 16;
	CurrentSkillLbl.Width := 31;
	CurrentSkillLbl.Caption := '';
	Menu.Show();	
end;

Procedure UpdateMenu;
begin
	StatusLabel.Caption := Status;
	SkillGainsLbl.Caption := '+' + FloatToStrF((CurrentSkill - StartingSkill),ffFixed,12,1);
	CurrentSkillLbl.Caption := FloatToStrF (CurrentSkill,ffFixed,12,1);
	TimeRunningLabel.Caption := 'Time Running: ' + TimeToStr(Now - StartTime);
end;
//=======================================

//===== WeightCheck =====
Procedure WeightCheck(ItemType:Cardinal);
var b : Integer;
var c : Integer;
var IsGood : Boolean;
var GoodTrigger : String;
var BookRec : TClilocRec;
var BookItemRec : TClilocItemRec;
var MyBook : Boolean;
var BackPackRec : TClilocRec;
var BackPackItemRec : TClilocItemRec;
var BackPackItems : Integer;
begin
	Status := 'Checking weight and backpack load'
	UpdateMenu;
	BackPackRec := GetToolTipRec(BackPack);
  BackPackItemRec := BackPackRec.Items[2];
  BackPackItems := StrToInt(BackPackItemRec.Params[0]);
	if ( Weight >= (MaxWeight - WeightOffset)) or ( BackPackItems >= 123 ) or ( LastUnload )then
	begin
    if LastUnload = False then
		  Status := 'Unloading items';
    if LastUnload = True then
      Status := 'Putting books away';
		UpdateMenu;
		repeat           
      MyBook := False;
			IsGood := False;
			FindType(ItemType,BackPack);
			if (FindCount > 0) then
			begin
				if ItemType = SpellBookType then
				begin
					BookRec := GetToolTipRec(FindItem);
					for b := 0 to (Length(BookRec.Items)-1) do
					begin
						BookItemRec := BookRec.Items[b];
						for c := 0 to (Length(GoodPropertiesArray)-1) do
						begin
						  if LowerCase(GoodPropertiesArray[c]) = LowerCase(GetClilocByID(BookItemRec.ClilocID)) then
						  begin
						    IsGood := True;
						    GoodTrigger := GoodPropertiesArray[c];
						  end;
              if LowerCase(FullBook) = LowerCase(GetClilocByID(BookItemRec.ClilocID)) then
              begin
                if ( (StrToInt(BookItemRec.Params[0])) > 0 ) then
                begin
                  MyBook := True;
                  Ignore(FindItem);
                end;
              end;
						end;
					end;
					if (IsGood = True) AND ( MyBook = False ) then
					begin
					  Status := GoodTrigger + ' found in property. Keeping book';
					  UpdateMenu;
					  MoveItem(FindItem,1,SafeContainer,0,0,0);
					  wait(1050);
					end;
					if ( IsGood = False ) AND ( MyBook <> True ) then
					begin
						Status := 'Trashing books';
						UpdateMenu;
						MoveItem(FindItem,1,Trash,0,0,0);
						wait(1050);
					end;
				end
				else
				begin
					Status := 'Moving scrolls to secure container';
					UpdateMenu;
					MoveItem(FindItem,Count(ItemType),SafeContainer,0,0,0);
					wait(1050);
				end;
			end;
		until FindCount = 0;
	end;
end;         
 

//===== ButtonExistsNot (returns False if button exists in gump) =====
Function ButtonExistsNot(ButtonNumber:Integer;Arguments:String):Boolean;
var TokLength : Integer;
var i : Integer;
begin
    Result := True;
    GetGumpInfo((GetGumpsCount-1),gi);
    TokLength := Length(gi.XmfHTMLTok);
    for i := 0 to (TokLength-1) do
    begin
        if (gi.XmfHTMLTok[i].ElemNum = ButtonNumber) AND (gi.XmfHTMLTok[i].Arguments = Arguments) then
        begin
            Result := False;
        end;
    end;
end;
//===== Init =====
Procedure Init;
begin
    UseObject(BackPack);
    wait(2000);
    LastUnload := False;
    LoadLimit := (( MaxWeight - Weight ) - WeightOffset);
    ScrollAmount := Round( LoadLimit / 2 );
    ReagentAmount := LoadLimit; 
    DropDelay := 10;
    SelfID := Self;
    LastCont := LastContainer;
    ClientPrint('Open your resource container');
    while LastCont = LastContainer do
      wait(1);
    Secure := LastContainer;
    LastCont := LastContainer;
    ClientPrint('Open your container to keep scrolls and super slayers');
    While LastCont = LastContainer do
      wait(1);
    SafeContainer := LastContainer; 
    LastCont := LastContainer;
    ClientPrint('Open your trash barrel');
    while LastContainer = LastCont do
      wait(1);
    Trash := LastContainer;
end;
//===== GumpCheck (will return false if gump IS OPEN =====
Function GumpCheck(GumpID:Cardinal;GumpCliloc:Integer):Boolean;
begin
  if ((GetGumpID(GetGumpsCount-1)) = GumpID) then
  begin
    GetGumpInfo((GetGumpsCount-1),gi);
    if (((gi.XmfHTMLGumpColor[0].Cliloc_id)) <> GumpCliloc) then
    begin
      CloseSimpleGump(GetGumpsCount-1);
      Result := True;
    end
    else
    begin
      Result := False;
    end;              
  end
  else
  begin
    Result := True;
  end;
end;
//===== GumpWait =====
Function GumpWait(GumpID:Cardinal;GumpCliloc:Integer):Boolean;
var Timeout : Cardinal;
begin
  Timeout := Timer;
  While ((GetGumpID(GetGumpsCount-1)) <> GumpID) and (Timer < (Timeout + 3000)) do
    wait(1);
  if ( (GetGumpID(GetGumpsCount-1)) = GumpID) then
  begin
    If GumpCliloc <> 0 then
    begin
      GetGumpInfo((GetGumpsCount-1),gi);
      if (((gi.XmfHTMLGumpColor[0].Cliloc_id)) = GumpCliloc) then
      begin
        Result := True;
      end
      else
      begin
        Result := False;
      end;
    end
    else
    begin
      Result := True;
    end;
  end;
  wait(ShortWait);
end;
//===== Craft =====
Procedure Craft(Tool:Cardinal;Button,ButtonNum,CatButton:Integer;Args:String);
begin
  if ( CurrentSkill <= 60.0 ) then
    Status := 'Crafting recall scrolls'; 
  if ( CurrentSkill > 60.0 ) AND ( CurrentSkill < 65.0 ) then
    Status := 'Crafting mark scrolls';
  if ( CurrentSkill >= 65.0 ) AND ( CurrentSkill < 100.0) then
	Status := 'Crafting spell books';
  UpdateMenu;
	repeat
		if GumpCheck(CraftingGumpID,InscriptionCliloc) then
		begin
			FindType(PenType,BackPack);
			wait(ShortWait);
			UseObject(FindItem);
			GumpWait(CraftingGumpID,InscriptionCliloc);
		end;
		if ButtonExistsNot(ButtonNum,Args) then
		begin 
      NumGumpButton((GetGumpsCount-1),CatButton);
		end;
	until GumpWait(CraftingGumpID,InscriptionCliloc);
	NumGumpButton((GetGumpsCount-1),Button);
	GumpWait(CraftingGumpID,InscriptionCliloc);
end;
//===== Check for tools =====
Procedure CheckTools;
begin
  Status := 'Checking for tools';
  UpdateMenu;
	FindType(PenType,BackPack);
	wait(ShortWait);
	if (FindCount = 0) then
	begin 
  Status := 'No pens found. Crafting more'
  UpdateMenu;
	repeat
    FindType(Ingots,BackPack);
    wait(ShortWait);
    if ( FindCount = 0) OR (Count(Ingots)<10) then
    begin
        FindType(Ingots,Secure);
        if (FindCount>0) AND (CountEx(Ingots,$FFFF,Secure)>=100) then
        begin
            MoveItem(FindItem, 100, BackPack, 0, 0, 0);
            wait(1100);
        end;
    end;
		FindType(TinkerToolsType,BackPack);
		wait(ShortWait);
		if (FindCount = 0) then
		begin
			ShowMessage('You must have Tinker Tools inside your bag. Halting.');
			exit;
		end;
		if (FindCount = 1) then
		begin
    Status := 'Found only 1 Tinker Tools in backpack. Crafting more';
    UpdateMenu;
			repeat
				repeat
					if GumpCheck(CraftingGumpID,TinkeringCliloc) then
					begin
						FindType(TinkerToolsType,BackPack);
						wait(ShortWait);
						UseObject(FindItem);
						GumpWait(CraftingGumpID,TinkeringCliloc);
					end;
				if ButtonExistsNot(TinkerElemNum,TinkerArgs) then
				begin
					NumGumpButton((GetGumpsCount-1),cat3button);
				end;
				until GumpWait(CraftingGumpID,TinkeringCliloc);
				NumGumpButton((GetGumpsCount-1),TinkerButton);
				GumpWait(CraftingGumpID,TinkeringCliloc);
				FindType(TinkerToolsType,BackPack);
				wait(ShortWait);
			until (FindCount >= 2);
		end;
    Status := 'Crafting more scribe pens';
    UpdateMenu;
		repeat
			if GumpCheck(CraftingGumpID,TinkeringCliloc) then
			begin
				FindType(TinkerToolsType,BackPack);
				wait(ShortWait);
				UseObject(FindItem);
				GumpWait(CraftingGumpID,TinkeringCliloc);
			end;
			if ButtonExistsNot(TinkerElemNum,TinkerArgs) then
			begin
				NumGumpButton((GetGumpsCount-1),cat3button);
			end;
		until GumpWait(CraftingGumpID,TinkeringCliloc);
		NumGumpButton((GetGumpsCount-1),PenButton);
		GumpWait(CraftingGumpID,TinkeringCliloc);
		FindType(PenType,BackPack);
		wait(ShortWait);
	until (FindCount >= 3);
	end;
end;
//===== Check for Items in backpack and restocks if necessary =====
Procedure CheckItems(CurrentItem:Cardinal;Amount:Integer);
begin
  Status := 'Checking items';
  UpdateMenu;
  FindType(CurrentItem,BackPack);
  wait(MedWait);
  if (FindCount = 0) OR (Count(CurrentItem)<10) then
  begin                                             
    Status := 'Not enough items inside backpack. Restocking';
    UpdateMenu;
    FindType(CurrentItem,Secure);
    wait(ShortWait);
    if (FindCount > 0) AND (CountEx(CurrentItem,$FFFF,Secure)>=Amount) then
    begin
      MoveItem(FindItem, Amount, BackPack, 0, 0, 0);
      wait(1000);   
      end
    else
    begin
        ShowMessage('You are out of material. Halting.');
        Exit;
    end;
  end;  
end;
//===== GetSkillValue, takes human JoAT into account =====
Function CalculateSkill(SkillName:String):Double;
var Skill : Double;
begin
  Skill := GetSkillValue(SkillName);
  if ( Skill <= 20.0 ) and ( Race = 1 ) then
    Result := 20.0
  else
    Result := Skill;
end;
//===== CheckMana =====
Procedure CheckMana(ReqMana:Integer);
begin
  Status := 'Checking mana';
  UpdateMenu;
  if ( Mana <= (ReqMana) ) then
  begin      
    Status := 'Mana is low. Attempting to meditate';
    UpdateMenu;
    SetEventProc(evBuff_DebuffSystem,'CheckActiveMeditation');
    While ( Mana < MaxMana ) do
    begin 
      if ( IsMeditating = False ) then
      begin
        UseSkill('Meditation');
        wait(1000);
        if (IsMeditating = False) then
          wait(10000);
      end;
      if IsMeditating then
      begin
        Status := 'Meditating until mana is full';
        UpdateMenu;
      end;
    end;
  end;
end;
//===== Check for Buffs/Debuffs =====
Procedure CheckActiveMeditation(ID,Attribute_ID : Cardinal; IsEnabled : Boolean);
begin
  if ( ( ID = SelfID)) then
  begin
    IsMeditating := ((Attribute_ID = ActiveMeditationID) and (IsEnabled <> False) )
  end;
end;
//===== begin =====
begin
//===== Array of properties to check for in books, you can add your own if you'd like =====
GoodPropertiesArray:=['Repond Slayer','Demon Slayer','Undead Slayer','Arachnid Slayer','Elemental Slayer','Reptile Slayer'];
//===== core below, don't modify :) =====
StartingSkill := CalculateSkill('Inscription');
Init;
MenuInit();
UseObject(Secure);
wait(1100);
StartTime := now;
while NOT dead do
begin
  CurrentSkill := CalculateSkill('Inscription');
  if ( CurrentSkill >= 32.5 ) then 
  begin
    if ( CurrentSkill <= 60.0 ) then //use other scrolls instead?
    begin
      WeightCheck(RecallScrollType);
      CheckItems(Ingots,100);
      CheckItems(BloodMoss,ReagentAmount);
      CheckItems(BlackPearl,ReagentAmount);
      CheckItems(MandrakeRoot,ReagentAmount);
      CheckItems(BlankScroll,ScrollAmount);
      CheckMana(MinimumMana);
      CheckTools;
      Craft(PenType,CraftRecallButton,RecallElemNum,cat2button,RecallArgs);
    end;
    if ( CurrentSkill > 60.0 ) AND (CurrentSkill < 65.0) then
    begin
	    WeightCheck(MarkScrollType);
      CheckItems(Ingots,100);
      CheckItems(BlankScroll,ReagentAmount);
      CheckItems(BloodMoss,ReagentAmount);
      CheckItems(BlackPearl,ReagentAmount);
      CheckItems(MandrakeRoot,ScrollAmount);
      CheckMana(MarkMana);
      CheckTools;
      Craft(PenType,CraftMarkButton,MarkElemNum,cat3button,MarkArgs);
    end; 
    if ( CurrentSkill >= 65.0 ) AND (CurrentSkill < 100.0) then
    begin 
      WeightCheck(SpellBookType);
      CheckItems(Ingots,100);
      CheckItems(BlankScroll,150);
      CheckTools;
      Craft(PenType,CraftSpellBookButton,SpellBookElemNum,cat6button,SpellBookArgs);  
    end;
    if ( CurrentSkill = 100.0 ) then
    begin
      LastUnload := True;
      WeightCheck(SpellBookType);
      WeightCheck(MarkScrollType);
      WeightCheck(RecallScrollType);
      ShowMessage('You have reached the skill cap. Thank you for using this script.');
      Exit;
    end;
  end
  else
  begin
    ShowMessage('You need at least 32.5 skill to use this script. Halting!');
    Exit;
  end;
end;  
end.

