let current = GetCurrentScriptDirectory;
let DEBUG = false;
let CutinDifficulty = "";
let SpellAttack_img = current~"./SpellAttack.png"; 
let img_SpellAttack = current~"./SpellAttackText.png"; // Spell Declare
let Alpha_HUD = [255, 255]; //top, bottom

let NAZRIN = "NAZRIN"; // UFO cutin
let BYAKUREN = "BYAKUREN"; // byakuren's cutin
let KANAKO = "KANAKO"; // MOF cutin
let TENSHI = "TENSHI"; // SWR cutin
let KOKORO = "KOKORO"; // HM cutin
let MURASA = "MURASA"; // goes to the top of the screen, "archs" then falls quickly
let SHINKI = "SHINKI"; // like byakuren but goes from bottom to top
let HINA = "HINA"; // THE CUTIN IS SPINNING AS IT GOES ACROSS THE SCREEN
let GHOST = "GHOST"; // the cutin spins around the center like a spooky ghost
let BUNBUNMARU = "BUNBUNMARU"; // cutin image spins like the newspaper in HM
let AYA = "AYA"; // only spellcard text
let MOKOU = "MOKOU"; // mokou's "Resurrection" cutin
let REIMU = "REIMU"; // CTC cutin
let YABUSAME = "LENEN"; //len'en cutin

let LENEN = "LENEN";

//If you have any questions or requests, send them to gtbot/TheGtbot (I go by either on different places)
//Version 1.3

task TCutin(SpellName,image,cuttype,color,sy)
{
	ObjCutin_SetSpellcardS3(SpellName,color[0],color[1],color[2]);
	CutinS3(cuttype, image, "Contest", sy);
}

task TCutinEX(SpellName,image,cuttype,color)
{
	ObjCutin_SetSpellcardS4(SpellName,image,cuttype,color[0],color[1],color[2]);
}

task ObjCutin_SetSpellcardS3(SpellName, R, G, B){
	let r = IntToString(R);
	let g = IntToString(G);
	let b = IntToString(B);
	let boss = GetEnemyBossObjectID[0];
	let spells = Obj_GetValueD(boss, "Spellcards", []);
	spells = spells~[[SpellName, r, g, b, "", ""]];
	Obj_SetValue(boss, "Spellcards", spells);
}

task ObjCutin_SetSpellcardS4(SpellName, image, cuttype, R, G, B){
	let r = IntToString(R);
	let g = IntToString(G);
	let b = IntToString(B);
	let boss = GetEnemyBossObjectID[0];
	let spells = Obj_GetValueD(boss, "Spellcards", []);
	spells = spells~[[SpellName, r, g, b, cuttype, image]];
	Obj_SetValue(boss, "Spellcards", spells);
}

task ObjCutin_LaunchS3(type, image, difficulty){
	CutinS3(type, image, difficulty, 0);
}

task CutinS3(Type, mimage, difficulty,sy){
	let const_imgPri = 0.74; //image priority (in 0-1 format)
	let const_textPri = 0.75; //spellcard text priority (in 0-1 format)
	let c_imgPri = const_imgPri;
	let c_textPri = const_textPri;
	let c_imgObj = ID_INVALID;
	let spells = Obj_GetValueD(GetEnemyBossObjectID[0], "Spellcards", []);
	let boss = GetEnemyBossObjectID[0];
	let spcount = length(spells);
	CutinDifficulty = difficulty;
	let IsSpellAttackAnimation = false;
	if(!IsCommonDataAreaExists("cutin_History")){
		//CreateCommonDataArea("cutin_History");
	}
	//LoadCommonDataAreaA1("cutin_History");


	//if(length(Images)>6)
	//	Images = [Images];
	////	Colors = [Colors];
	//
	//let Colors = [[r, g, b]];

	descent(i in 0..spcount){SpellText(6*i, i);}
	SpawnCutinImage(Type, mimage);
	
	
	let delay = 6;
	while(!Obj_IsDeleted(boss)){
		spells = Obj_GetValueD(GetEnemyBossObjectID[0], "Spellcards", []);
		if(length(spells)!=spcount&&delay==0){
			delay = 6;
			SpellText(0, spcount);
			let newtype = spells[spcount][4];
			if(newtype == ""){newtype = Type;}
			SpawnCutinImage(newtype, spells[spcount][5]);
			spcount++;
		}
		delay = max(delay-1, 0);
		yield;
	}
	
	
	
	task FireCutinA1(x, y, angle, ispeed, itime, mspeed, mtime, img){	
		if(IsSpellAttackAnimation){return;}	
		let SpellCutin = CreateSimple2DImageA1(c_imgPri, img);
		ObjRender_SetPosition(SpellCutin, x, y, 0);
		ObjRender_SetAlpha(SpellCutin, 0);
		
		c_imgObj = SpellCutin;
		
		let len = itime;
		ascent(x in 0..len){
			let mod = x/len;
			let locs = [ObjRender_GetX(SpellCutin), ObjRender_GetY(SpellCutin)];
			ObjRender_SetPosition(SpellCutin, floor(locs[0]+ispeed*cos(angle)), floor(locs[1]+ispeed*sin(angle)), 0);
			
			let alph = min(255, 255*(mod*2));
			ObjRender_SetAlpha(SpellCutin, alph);
			yield;
		}
		
		len = mtime;
		let nx = ObjRender_GetX(SpellCutin);
		let ny = ObjRender_GetY(SpellCutin);
		ascent(x in 0..len){
			nx += mspeed*cos(angle);
			ny += mspeed*sin(angle);
			ObjRender_SetPosition(SpellCutin, floor(nx), floor(ny), 0);
			yield;
		}
		//return;
		len = mtime;
		ascent(x in 0..len){
			let mod = x/len;
			let locs = [ObjRender_GetX(SpellCutin), ObjRender_GetY(SpellCutin)];
			ObjRender_SetPosition(SpellCutin, floor(locs[0]+ispeed*cos(angle)), floor(locs[1]+ispeed*sin(angle)), 0);
			yield;
		}
		Obj_Delete(SpellCutin);
	}

	task FireCutinA2(x, y, alpha, img, details){
		if(IsSpellAttackAnimation){return;}
		//[spd, spdinc, ang, anginc, alphachange, time]
		let SpellCutin = CreateSimple2DImageA1(c_imgPri, img);
		ObjRender_SetPosition(SpellCutin, x, y, 0);
		ObjRender_SetAlpha(SpellCutin, alpha);
		
		c_imgObj = SpellCutin;
		
		ascent(i in 0..length(details)){
			let nx = ObjRender_GetX(SpellCutin);
			let ny = ObjRender_GetY(SpellCutin);
			let info = details[i];
			let len = info[5];
			let alphinc = -(alpha-info[4])/len;
			let spdinc = -(info[0]-info[1])/len;
			let mspeed = info[0];
			let cangle = info[2];
			ascent(x in 0..len){
				mspeed+=spdinc;
				cangle+=info[3];
				nx += mspeed*cos(cangle);
				ny += mspeed*sin(cangle);
				ObjRender_SetPosition(SpellCutin, nx, ny, 0);
				yield;
				alpha = min(max(alpha+alphinc, 0), 255);
				ObjRender_SetAlpha(SpellCutin, alpha);
			}
		}
		Obj_Delete(SpellCutin);
	}

	task FireCutinB2(x, y, alpha, img, details){
		if(IsSpellAttackAnimation){return;}
		//[spd, spdinc, ang, anginc, alphachange, time]
		let SpellCutin = CreateSimple2DImageA1(c_imgPri, img);
		ObjRender_SetPosition(SpellCutin, x, y, 0);
		ObjRender_SetAlpha(SpellCutin, alpha);
		
		c_imgObj = SpellCutin;
		
		ascent(i in 0..length(details)){
			let nx = ObjRender_GetX(SpellCutin);
			let ny = ObjRender_GetY(SpellCutin);
			let info = details[i];
			let len = info[5];
			let alphinc = -(alpha-info[4])/len;
			let spdinc = -(info[0]-info[1])/len;
			let mspeed = info[0];
			let cangle = info[2];
			ascent(x in 0..len){
				mspeed+=spdinc;
				cangle+=info[3];
				nx += mspeed*cos(cangle);
				ny += mspeed*sin(cangle);
				ObjRender_SetPosition(SpellCutin, nx, ny, 0);
				yield;
				alpha = min(max(alpha+alphinc, 0), 255);
				ObjRender_SetAlpha(SpellCutin, alpha);
			}
		}
		Obj_Delete(SpellCutin);
	}

	task FireCutinCtCA1(img){
		let SpellCutinBG = CreateSimple2DImageA1(0.32, img);
		let s = [GetTextureWidth(img), GetTextureHeight(img)];
		ObjSprite2D_SetDestRect(SpellCutinBG, -s[0]*0.45, -s[1]*0.3, s[0]*0.55, s[1]*0.7);
		ObjRender_SetPosition(SpellCutinBG, GetStgFrameWidth/2+56, GetStgFrameHeight-16, 0);
		ObjRender_SetAlpha(SpellCutinBG, 64);
		ObjRender_SetScaleXYZ(SpellCutinBG, 3, 3, 3);
		
		let SpellCutin = CreateSimple2DImageA1(0.33, img);
		ObjSprite2D_SetDestRect(SpellCutin, -s[0]*0.625, -s[1], s[0]*0.375, 0);
		ObjRender_SetPosition(SpellCutin, GetStgFrameWidth-32, GetStgFrameHeight, 0);
		ObjRender_SetAlpha(SpellCutin, 0);
		
		c_imgObj = SpellCutin;
		
		descent(i in 0..7){
			let mod = 1+i;
			ObjSprite2D_SetDestRect(SpellCutin, -s[0]*0.8*(1+i*0.5), -s[1]*0.625, s[0]*0.2, s[1]*0.375);
			ObjRender_SetAlpha(SpellCutin, 255-255/7*i);
			
			ObjRender_SetScaleY(SpellCutinBG, 3-3/7*i);
			loop(1){yield;}
		}
		loop(1){yield;}
		
		descent(i in 0..60){
			ObjRender_SetX(SpellCutin, ObjRender_GetX(SpellCutin)+1.5/60*i);
			ObjRender_SetY(SpellCutinBG, ObjRender_GetY(SpellCutinBG)-4/60*i);
			yield;
		}
		yield;
		
		ascent(i in 0..10){
			let mod = max(0, 1-i/7);
			let scl = 3+i/5;
			ObjSprite2D_SetDestRect(SpellCutin, -s[0]*0.8, -s[1]*0.625*mod, s[0]*0.2, s[1]*0.375*mod);
			ObjRender_SetScaleXYZ(SpellCutinBG, scl, scl, scl);
			ObjRender_SetAlpha(SpellCutinBG, 64-64/10*i);
			ObjRender_SetY(SpellCutin, ObjRender_GetY(SpellCutin)-32/2);
			yield;
		}
		
		Obj_Delete(SpellCutin);
		Obj_Delete(SpellCutinBG);
	}

	function SpawnCutinImage(type, image){
		//We use another "type" variable (lowercase t) so that
		//spellcards set after the root function is called will play the
		//new set cutin type as opposed to the original one
		// same for image
		
		
		c_imgPri = const_imgPri;
		c_textPri = const_textPri;
		c_imgObj = ID_INVALID;
		//We reset the priorities because some cutins may alter them when they are called
		
		
		//[spd, spdinc, ang, anginc, alphachange, time]
		if(type == "NAZRIN"){
			FireCutinA2(GetStgFrameWidth+208, GetStgFrameHeight/2-144, 0, image,
			[
				[20.6, 20.6, 158, 0, 255, 20],
				[0.65, 0.65, 158, 0, 255, 90],
				[20.6, 20.6, 158, 0, 255, 20]
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "BYAKUREN"){
			FireCutinA2(-256, GetStgFrameHeight/2, 0, image,
			[
				[14, 14, 0, 0, 255, 30],
				[0.5, 0.5, 0, 0, 255, 90],
				[14, 14, 0, 0, 0, 30]
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "KANAKO"){
			FireCutinA2(GetStgFrameWidth+180, GetStgFrameHeight/2-190, 0, image,
			[
				[30, 8, 144, 0, 192, 23],
				[8, 0, 144, 0, 255, 10],
				[0, 2.1, 270, 0, 255, 90],
				[2.1, 2.1, 270, 0, 255, 20],
				[2.1, 1, 270, 0, 0, 20]
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "TENSHI"){
			SpellAttackEffect(145);
		}
		else if(type == "KOKORO"){
			SpellAttackEffect(145);
		}
		else if(type == "MURASA"){
			FireCutinA2(GetStgFrameWidth/2, GetStgFrameHeight+144, 0, image,
			[
				[9, 6, 270, 0, 255, 20],
				[6, 1, 270, 0, 255, 70],
				[1, 0, 270, 0, 255, 20],
				[0, 3, 90, 0, 255, 10],
				[3, 12, 90, 0, 255, 10],
				[12, 15, 90, 0, 160, 10],
				[0, 0, 90, 0, 0, 20],
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "SHINKI"){
			FireCutinA2(GetStgFrameWidth/2, GetStgFrameHeight+232, 0, image,
			[
				[14, 14, 270, 0, 255, 30],
				[0.5, 0.5, 270, 0, 255, 90],
				[14, 14, 270, 0, 0, 30]
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "HINA"){
			FireCutinA2(GetStgFrameWidth/2, GetStgFrameHeight/2-32, 0, image,
			[
				[7, 7, 0, 7, 208, 40],
				[7, 7, 7*40, 7, 208, 75],
				[7, 7, 7*115, 7, 0, 40]
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "GHOST"){
			FireCutinA2(GetStgFrameWidth/2, GetStgFrameHeight/2-32, 0, image,
			[
				[7, 7, 0, 7, 224, 40],
				[7, 7, 7*40, 7, 224, 75],
				[7, 7, 7*115, 7, 0, 40]
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "BUNBUNMARU"){
			SpellAttackEffect(145);
		}
		else if(type == "AYA"){
			SpellAttackEffect(145);
		}
		else if(type == "MOKOU"){
			//nothing goes here since the SpellText function handles this completely
		}
		else if(type == "REIMU"){
			FireCutinCtCA1(image);
		}
		else if(type == "LENEN" || type == "YABUSAME"){
			FireCutinA2(GetStgFrameWidth+256, GetStgFrameHeight/2+96, 0, image,
			[
				[32, 1, 182, 0.1, 255, 30],
				[1, 1.25, 180, 14, 255, 10],
				[1.25, 1.5, 340, 0, 255, 80],
				[1.5, 2.5, 340, -15.75, 192, 10],
				[2.5, 31, 182.5, 0, 0, 20],
			]
			);
			SpellAttackEffect(145);
		}
		else if(type == "CHROM"){
			CriticalEffect;
			FireCutinA2(GetStgFrameWidth+192+32, GetStgFrameHeight/2+16, 255, image,
			[
				[20, 20, 180, 0, 255, 15],
				[20, 10, 180, 0, 255, 5],
				[1.25, 1.25, 0, 180/70, 255, 70],
				[2, 8, 180, 0, 255, 5],
				[8, 17, 180, 0, 255, 7],
				[17, 17, 180, 0, 255, 40],
			]
			);
		}

		else{RaiseError("Not a valid cutin type.");}
	}
	
	task SpellText(delay, num){
		let tspell = spells[num];
		let SpellName = tspell[0];
		let r = atoi(tspell[1]);
		let g = atoi(tspell[2]);
		let b = atoi(tspell[3]);
		
		let Colors = [[r,g,b]];
		
		//These are long-looking common data group names
		let SpellDataAttempt = SpellName~"|"~CutinDifficulty~"|"~GetPlayerID~"|"~"Attempt";
		let SpellDataGet = SpellName~"|"~CutinDifficulty~"|"~GetPlayerID~"|"~"Get";
		//This is actually the common data retrieval
		let SpellValueAttempt = GetAreaCommonData("cutin_History", SpellDataAttempt, 0);
		let SpellValueGet = GetAreaCommonData("cutin_History", SpellDataGet, 0);
		
		
		if(!IsReplay&&!DEBUG&&num==0){
			SpellValueAttempt++;
			SetAreaCommonData("cutin_History", SpellDataAttempt, SpellValueAttempt);
			SaveCommonDataAreaA1("cutin_History");
		}
	
	
		let SpellBG = CreateSimple2DImageA2(c_textPri+0.01*max(0, 1-num), SpellAttack_img, 0, 0, 255, 31);
		ObjSprite2D_SetDestRect(SpellBG, -224, -12, 32, 20);
		ObjRender_SetPosition(SpellBG, GetStgFrameWidth-24, 320+40*num, 0);
		ObjRender_SetAlpha(SpellBG, 0);
		ObjRender_SetColor(SpellBG, Colors[0][0], Colors[0][1], Colors[0][2]);
		
		let SpellText = ObjText_Create;
		ObjText_SetText(SpellText,SpellName);
		ObjText_SetFontSize(SpellText,12); //12
		ObjText_SetFontColorTop(SpellText,255,255,255);
		ObjText_SetFontColorBottom(SpellText, Colors[0][0], Colors[0][1], Colors[0][2]);
		ObjText_SetFontBorderType(SpellText,BORDER_FULL);
		ObjText_SetFontBorderColor(SpellText,32,32,32);
		ObjText_SetFontBorderWidth(SpellText,1);
		ObjText_SetHorizontalAlignment(SpellText, ALIGNMENT_RIGHT);
		ObjText_SetMaxWidth(SpellText, GetStgFrameWidth-24); //-24
		Obj_SetRenderPriority(SpellText, c_textPri+0.01*max(0, 1-num));
		ObjRender_SetPosition(SpellText, 0, 320+40*num, 0);
		ObjRender_SetAlpha(SpellText, 0);
		
		
		let SpellInfo = ObjPrim_Create(OBJ_SPRITE_LIST_2D);
		let bonus = 0;
		let count = GetNumSize(bonus);
		let scount = max(2, GetNumSize(SpellValueAttempt));
		ObjPrim_SetTexture(SpellInfo, SpellAttack_img);
		ObjRender_SetBlendType(SpellInfo, BLEND_ALPHA);
		Obj_SetRenderPriority(SpellInfo, c_textPri+0.01);
		ObjRender_SetY(SpellInfo, 36);
		ObjRender_SetAlpha(SpellInfo, 0);
		
		//-------------Introduction--------------------------
		
		loop(delay){yield;}
		let len = 30;
		ascent(i in 0..len){
			let mod = i/len;
			let tmod = min(1, ((i*2)/len));
			ObjRender_SetScaleXYZ(SpellBG, 4-3*mod, 4-3*mod, 1);
			ObjRender_SetAlpha(SpellBG, 255*mod);
			ObjRender_SetX(SpellText, (GetStgFrameWidth-24)*(1-mod));
			//ObjRender_SetScaleXYZ(SpellText, 4-3*tmod, 4-3*tmod, 1);
			ObjRender_SetAlpha(SpellText, 255*tmod);
			yield;
		}
		loop(40){yield;}
		let mov = 0;
		
		if(Type == "MOKOU"){SpellcardDeclareMovement([ObjRender_GetY(SpellText), 48+32*num, 0.175, 9]);loop(60){yield;}}
		else{RegularSpellCardDeclare([ObjRender_GetY(SpellText), 32+32*num, 0.135, 12]);}
		//32 is the offset from the top of the screen
		//If using a system where the lifebar is on the boss rather than the top of the screen, 12 is a good value

		
		loop(delay*0.65){yield;}
		
		ascent(i in 0..80){
			let endspeed = 0.5+i/2.5;
			ObjRender_SetX(SpellBG, ObjRender_GetX(SpellBG)+endspeed);
			ObjRender_SetX(SpellText, ObjRender_GetX(SpellText)+endspeed);
			yield;
		}
		
		Obj_Delete(SpellBG);
		Obj_Delete(SpellText);
		
		function SpellcardDeclareMovement(dest){
			ObjRender_SetY(SpellInfo, 24+dest[1]);
			while(dest[0]>dest[1]){
				dest[0] = ObjRender_GetY(SpellText);
				mov = min(mov+dest[2], (dest[0]-dest[1])/dest[3]+0.125);
				ObjRender_SetY(SpellBG, dest[0]-mov);
				ObjRender_SetY(SpellText, dest[0]-mov-sy);
				yield;
			}
		}
		
		function RegularSpellCardDeclare(dest){
			if(num!=0){Obj_Delete(SpellInfo);}
			SpellcardDeclareMovement(dest);
			ascent(i in 0..20){
				ObjRender_SetAlpha(SpellInfo, 255/20*i);
				DrawScoreBonus;
				yield;
			}
			let timerf = ObjEnemyBossScene_GetInfo(GetEnemyBossSceneObjectID, INFO_ORGTIMERF);
			let startdiff = timerf-(timerf-5*60);
			while(!Obj_IsDeleted(boss)){
				ObjRender_SetAlpha(SpellBG, Alpha_HUD[0]);
				ObjRender_SetAlpha(SpellText, Alpha_HUD[0]);
				//ObjRender_SetAlpha(SpellInfo, Alpha_HUD[0]);
				
			//	let modif = min(1, ((ObjEnemyBossScene_GetInfo(GetEnemyBossSceneObjectID, INFO_TIMERF))/(ObjEnemyBossScene_GetInfo(GetEnemyBossSceneObjectID, INFO_ORGTIMERF)-startdiff)));
			//	bonus = ((sscore*0.20)+(sscore*((modif)*0.80)));
				DrawScoreBonus;
				yield;
			}
			Obj_Delete(SpellInfo);
			
			if(bonus>=1&&ObjEnemyBossScene_GetInfo(GetEnemyBossSceneObjectID, INFO_TIMERF)>0){
			//	GotSpellCard(bonus);
			//	AddScore(bonus);
				if(!IsReplay&&!DEBUG&&num==0){
					SpellValueGet++;
					SetAreaCommonData("cutin_History", SpellDataGet, SpellValueGet);
					SaveCommonDataAreaA1("cutin_History");
				}
			}else{
			//	FailedSpellCard;
			}
		}
		sub DrawScoreBonus{
			if(GetPlayerY<128+32*length(spells)){Alpha_HUD[0] = max(Alpha_HUD[0]-10, 85);}else{Alpha_HUD[0] = min(Alpha_HUD[0]+11, 255);}
			
			ObjSpriteList2D_ClearVertexCount(SpellInfo);
				
			ObjSpriteList2D_SetSourceRect(SpellInfo, 0, 48, 128, 60);
			ObjSpriteList2D_SetDestCenter(SpellInfo);
			ObjRender_SetX(SpellInfo, GetStgFrameWidth-130);
			ObjSpriteList2D_AddVertex(SpellInfo);
			//Bonus Score
			bonus = ObjEnemyBossScene_GetInfo(GetEnemyBossSceneObjectID, INFO_SPELL_SCORE);
			count = GetNumSize(bonus);
			let listNum = DigitToArray(min(99999999, bonus), count);
			let objScene = GetEnemyBossSceneObjectID;
			if(ObjEnemyBossScene_GetInfo(objScene, INFO_PLAYER_SHOOTDOWN_COUNT) +
			ObjEnemyBossScene_GetInfo(objScene, INFO_PLAYER_SPELL_COUNT) == 0){
				ascent(iObj in 0 .. count){
					let num = listNum[iObj];
					ObjRender_SetX(SpellInfo, GetStgFrameWidth-94-(8*count) + iObj * 7);
					ObjSpriteList2D_SetSourceRect(SpellInfo, num * 8, 64, (num + 1) * 8, 76);
					ObjSpriteList2D_SetDestCenter(SpellInfo);
					ObjSpriteList2D_AddVertex(SpellInfo);
				}
			}else{
					bonus = 0;
					ObjRender_SetX(SpellInfo, GetStgFrameWidth-131);
					ObjSpriteList2D_SetSourceRect(SpellInfo, 144, 48, 176, 60);
					ObjSpriteList2D_SetDestCenter(SpellInfo);
					ObjSpriteList2D_AddVertex(SpellInfo);
			}
			//History
			listNum = DigitToArray(SpellValueGet, scount) ~ [10] ~ DigitToArray(SpellValueAttempt, scount);
				
			ascent(iObj in 0 .. scount*2+1){
				let num = listNum[iObj];
				ObjRender_SetX(SpellInfo, GetStgFrameWidth-58 + iObj * 8);
				ObjSpriteList2D_SetSourceRect(SpellInfo, num * 8, 64, (num + 1) * 8, 76);
				ObjSpriteList2D_SetDestCenter(SpellInfo);
				ObjSpriteList2D_AddVertex(SpellInfo);
			}
		}
	}
	
	task SpellAttackEffect(alphamax){
		if(IsSpellAttackAnimation){return;}
		IsSpellAttackAnimation = true;
		let alpha = 1;
		let GetCenterX = GetStgFrameWidth/2;
		let GetCenterY = GetStgFrameHeight/2;
		Octagon(GetCenterX*2, GetCenterY*2, -1.15, 180, 1);
		Octagon(GetCenterX*2, GetCenterY*2, 1.05, 240, 0);
		Octagon(GetCenterX*2, GetCenterY*2, 1.05, 295, 0);
		Octagon(GetCenterX/5, GetCenterY/5, 0.75, 170, 1);
		
		ascent (i in 0..5){
			Line(GetCenterX, GetCenterY+i*75-75-95, 1.5);
		}
		ascent (i in 0..5){
			Line(GetCenterX, GetCenterY+i*75-37.5-95, -1.5);
		}
		
		loop(15){yield;}
		ascent(i in 0..20){
			alpha = 0.1+alphamax/20*i;
			yield;
		}
		loop(110){yield;}
		descent(i in 0..20){
			alpha = alphamax/20*i;
			yield;
		}
		
		IsSpellAttackAnimation = false;
		
		task Line(mx, my, posinc){
			let objSpellAttack = ObjPrim_Create(OBJ_SPRITE_2D);
			ObjRender_SetBlendType(objSpellAttack, BLEND_ALPHA);
			Obj_SetRenderPriority(objSpellAttack, 0.75);
			ObjPrim_SetTexture(objSpellAttack, img_SpellAttack);
			ObjSprite2D_SetSourceRect(objSpellAttack, 0, 0, 512, 16);
			let n = 3;
			ObjSprite2D_SetDestRect(objSpellAttack, -(256*n), -8, 256*n, 8);
			let LineX = 0;
			ObjRender_SetPosition(objSpellAttack, mx, my, 0);
			ObjRender_SetAngleZ(objSpellAttack, -35);
			while(alpha>0){
				LineX += posinc;
				ObjSprite2D_SetSourceRect(objSpellAttack, LineX, 0, (512*n)+LineX, 16);
				ObjRender_SetAlpha(objSpellAttack, alpha);
				yield;
			}
			Obj_Delete(objSpellAttack);
		}
	
		task Octagon(mx, my, spininc, dist, size){
		
			let countVertex = 16;
			let listRadius = [];
			loop(countVertex){
				listRadius = listRadius ~ [0];
			}

			let objOutline = ObjPrim_Create(OBJ_PRIMITIVE_2D);
			ObjPrim_SetPrimitiveType(objOutline, PRIMITIVE_TRIANGLESTRIP);
			ObjPrim_SetVertexCount(objOutline, countVertex);
			ObjRender_SetBlendType(objOutline, BLEND_ALPHA);
			Obj_SetRenderPriority(objOutline, 0.76);
			ObjPrim_SetTexture(objOutline, img_SpellAttack);
	
			ascent (iVert in 0..countVertex/2){
				let left = iVert * 128;
				let indexVert = iVert * 2;
				ObjPrim_SetVertexUVT(objOutline, indexVert + 0, left, 0);
				ObjPrim_SetVertexUVT(objOutline, indexVert + 1, left, 16);
			}			
	
			let frame = 0;
			let rRate = 1;
			let spin = 0;
	
			while(alpha>0){
				spin+=spininc;
				VertexSize;
				ObjRender_SetPosition(objOutline, mx, my, 0);
				ObjRender_SetAngleZ(objOutline, spin);
				ObjRender_SetAlpha(objOutline, alpha);
				frame++;
				yield;
			}
			Obj_Delete(objOutline);
			
			task VertexSize{	
				if(frame>=35){return;}
				ascent (iVert in 0..countVertex/2){
					let indexVert = iVert * 2;
					let angle = (360 / (countVertex / 2 - 1) * iVert);

					let vx1 = listRadius[indexVert] * cos(angle);
					let vy1 = listRadius[indexVert] * sin(angle);
					ObjPrim_SetVertexPosition(objOutline, indexVert + 0, vx1, vy1, 0);

					let vx2 = listRadius[indexVert+1] * cos(angle);
					let vy2 = listRadius[indexVert+1] * sin(angle);
					ObjPrim_SetVertexPosition(objOutline, indexVert + 1, vx2, vy2, 0);

					let dr = (dist * rRate - listRadius[indexVert]) / 16;
					listRadius[indexVert] = listRadius[indexVert] + dr;
					if(frame>size){
						listRadius[indexVert + 1] = listRadius[indexVert + 1] + dr;
					}
				}
			}
		}
	}

	task CriticalEffect{
		let CritTexture = "ChromCritChance";
		if(!CreateRenderTarget(CritTexture)){
		RaiseError("Not enough VRAM to proceed.\nTry restarting the game and lowering the graphics quality.");}

		
		c_imgPri = 0.92;
		
		let mask = ObjPrim_Create(OBJ_SPRITE_2D);
		Obj_SetRenderPriorityI(mask, 91);
		ObjPrim_SetTexture(mask, NULL);
		ObjRender_SetColor(mask, 255, 255, 255);
		ObjSprite2D_SetSourceRect(mask, 0, 0, 1, 1);
		ObjSprite2D_SetDestRect(mask, -GetStgFrameWidth/2, -64, GetStgFrameWidth/2, 64);
		ObjRender_SetPosition(mask, GetStgFrameWidth/2+32, GetStgFrameHeight/2-64, 0);
		
		let bg = ObjPrim_Create(OBJ_SPRITE_2D);
		Obj_SetRenderPriorityI(bg, 92);
		ObjPrim_SetTexture(bg, NULL);
		ObjRender_SetColor(bg, 128, 80, 184);
		ObjSprite2D_SetSourceRect(bg, 0, 0, 1, 1);
		ObjSprite2D_SetDestRect(bg, 0, 0, GetStgFrameWidth, GetStgFrameHeight);
		
		let objShader = ObjPrim_Create(OBJ_SPRITE_2D);
		ObjShader_SetShaderF(objShader, GetCurrentScriptDirectory ~ "mask.hlsl");
		ObjShader_SetTechnique(objShader, "TecMask");

		
		Obj_SetVisible(c_imgObj, false);
		Obj_SetVisible(bg, false);
		
		DoRenderTexture;
		
		SetShaderI(objShader, 91, 91);
	
		
		
		ascent(i in 0..5){
			DoRenderTexture;
			ObjRender_SetScaleY(mask, 1.25/5*i);
			yield;
		}
		
		RenderMaskSquish;
		ascent(i in 0..95){
			DoRenderTexture;
			let mod = min(1, 1+cos(i*6-180));
			ObjRender_SetColor(bg, 128+127*mod, 80+175*mod, 184+71*mod);
			yield;
		}
		
		descent(i in 0..10){
			DoRenderTexture;
			ObjRender_SetScaleY(mask, 1/10*i);
			yield;
		}
		
		
		
		task RenderMaskSquish{
			descent(i in 0..50){
				ObjRender_SetScaleY(mask, 1+min(0.25, i^2/10000));
				yield;
			}
		}
		
		sub DoRenderTexture{
			Obj_SetVisible(c_imgObj, true);
			Obj_SetVisible(bg, true);
			SetInvalidRenderPriorityA1(92, 92);
			RenderToTextureA1(CritTexture, 92, 92, true);
			ObjShader_SetTexture(objShader, "textureMask_", CritTexture);
			Obj_SetVisible(c_imgObj, false);
			Obj_SetVisible(bg, false);
			ClearInvalidRenderPriority;
		}
		
	}
}

task FadeInA1(obj, ftime){
	ascent(i in 0..ftime){
		ObjRender_SetAlpha(obj, 255/ftime*i);
		yield;
	}
}
task FadeInB1(obj, alpha, ftime){
	ascent(i in 0..ftime){
		ObjRender_SetAlpha(obj, alpha/ftime*i);
		yield;
	}
}

task FadeOutA1(obj, ftime){
	descent(i in 0..ftime){
		ObjRender_SetAlpha(obj, 255/ftime*i);
		yield;
	}
}

task FadeOutB1(obj, initalpha, ftime){
	descent(i in 0..ftime){
		ObjRender_SetAlpha(obj, initalpha/ftime*i);
		yield;
	}
}



function CreateSimple2DImageA1(pri, image){
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	ObjRender_SetBlendType(obj, BLEND_ALPHA);
	Obj_SetRenderPriority(obj, pri);
	ObjPrim_SetTexture(obj, image);
	let test = [GetTextureWidth(image), GetTextureHeight(image)];
	ObjSprite2D_SetSourceRect(obj, 0, 0, test[0]-1, test[1]-1);
	ObjSprite2D_SetDestRect(obj, -(test[0]/2+0.5), -(test[1]/2), (test[0]/2), (test[1]/2));
	return obj;
}

function CreateSimple2DImageA2(pri, image, x1, y1, x2, y2){
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	ObjRender_SetBlendType(obj, BLEND_ALPHA);
	Obj_SetRenderPriority(obj, pri);
	ObjPrim_SetTexture(obj, image);
	ObjSprite2D_SetSourceRect(obj, x1, y1, x2, y2);
	ObjSprite2D_SetDestCenter(obj);
	return obj;
}


function DigitToArray(digit, count){
	let res = [];
	digit = truncate(digit);

	loop{
		let tnum = truncate(digit % 10);
		digit /= 10;
		res = [tnum] ~ res;
		if(truncate(digit) == 0){break;}
	}

	loop(max(0, count - length(res))){
		res = [0] ~ res;
	}

	return res;
}

function GetNumSize(value){
	if(value<=1){return 1;}
	else{return truncate(log10(value))+1;}
}