//************************************************************************************************************
//	GLOBAL LIBRARY (first half)
//	Global player library of useful functions/tasks
//	Author: Helepolis
//	Version: 1.0
//  Library:
//		wait					- Yields the desired number of frames.
//		SetDebugKey				- Grants control over player behaviour. I / U for toggling godmode. F for maximum lives/bombs. H for skipping boss cards.
//		rand_int				- Truncates a value between min and max value into an integer with no decimals.
//		GetCenterX/Y			- Gets the centre of the field.
//		GetClipMaxX/Y			- Gets the maximum X/Y coordinates of the field.
//		GetClipMinX/Y			- Gets the minimum X/Y coordinates of the field. 
//		GetDistanceXY			- Gets the distance between specified X and Y coordinates. 
//		GetPointToPointAngle	- Uses atan2 to get the angle between two objects X/Y positions. 
//		ObjMove_Special			- Special move code to move 2D sprites. Similar to moving object lasers.
//		quakeCam2DPlay			- Global camera to shake the screen in 2D. Cannot be used for 3D stages.
//		quakeCam3DPlay			- Global camera to shake the screen in 3D. Can be combined with 2D stages.
//************************************************************************************************************

//************************************************************************************************************
// 	SPELL CARD AND OTHER EFFECTS LIBRARY (2nd half)
//	Spell card handling and other effects.
//	Author: Helepolis, Drake
//	Version: 1.0
//  Library:
//		colorMixer				- Randomizes rapidly R G B values for disco effect. 
//		scatterMasterSparkLaser	- Spell card code: Scattering mini laser for Marisa's Disco Spark.
//		danceVarSet				- Rare function to be used for Alice's doll dancing animation.
//		dancingShangaiDoll		- Spell card code: Shanghai doll object for Alice's spell card.
//		shanghaiBombAnim		- Handler task for Shanghai dancing animation and damage. 
//		playerGlitterEffect		- Flexible task for summoning glitters. 
//		playerBreakEffect		- Break effect handler when the player spell card animation ends. (Similar to boss break effect) 
//		playerPichuunEffect		- Effect task when player goes pichuun.
//		magicCircleMagic		- Marisa and Alice's trademark magic circle when bombing. 
//		magicCircleMini			- Shanghai dolls trademark magic circle when bombing.
//		setReimuGlow			- Reimu's glowing aura animation.
//		hakkeroPiece			- Function to call and load sprite for hakkero in a object. Returns id: obj.
//		magicCircleBorder		- Reimu and Yukari's trademark magic circle when bombing. 
//		fadeInOutHandler 		- Hakkero animation fade in/out handler.
//		magicCircleBoom			- Hakkero ending animation. (Thanks to Drake)
//		reimuGlowSpriteEx		- Reimu's own body glow aura.
//		reimuGlowSprite			- Reimu's Musou Tensei aura.
//		reimuDiscoBall			- Reimu's Musou Fuuin ball.	(Adjustments and fixes by Drake)
//		reimuBallGlitterHandle	- Glitter spawner for Disco ball.
//		reimuBallGlow			- Glowing animation for Reimu's Disco balls.
//		sukimaGenerator			- Generates Yukari's gaps.
//		yukariGap				- Yukari gap object.
//		sukimaAnimation			- Gap objects unfolding/fading animation.
//		sukimaLaser				- Temporary laser firing from the gaps (experimental)
//		playerFunkyCircle		- Funky spell card spell circle animation (outer ring) 
//		playerFunkyCircle2		- Funky spell card spell circle animation 2 (inner ring) 
//		pichuunHandler			- Handles fancy glitters when player goes pichuun
//		invincEffect			- Shows blinking effect when player respawns
//************************************************************************************************************

#include "script/thdcs/functions/function_loaddata.txt"

// Da ze!
function wait(w) { loop(w) { yield; } } 

// Debug keys for testing purposes. U/I toggles godmode. H kills current attack. F grants maximum lives and bombs. G adds 100 power.
task SetDebugKey(obj) {
	let textobj = ObjText_Create;
	ObjRender_SetPosition(textobj,5,420,0);
	ObjText_SetText(textobj,"");

	while(!Obj_IsDeleted(obj)) {
		if(GetKeyState(KEY_I) == KEY_PUSH) { SetCommonData("Debug_Unrivaled", true); ObjText_SetText(textobj,"GODMODE"); }
		if(GetKeyState(KEY_U) == KEY_PUSH) { SetCommonData("Debug_Unrivaled", false); ObjText_SetText(textobj,"NORMAL"); }
		if(GetKeyState(KEY_F) == KEY_PUSH) { SetPlayerLife(8); SetPlayerSpell(8); }
		if(GetKeyState(KEY_H) == KEY_PUSH) { ObjEnemy_SetLife(obj, 0); }
		if(GetCommonData("Debug_Unrivaled", false)) { SetPlayerInvincibilityFrame(max(1,GetPlayerInvincibilityFrame)); }
		yield;
	}
}

// 0.12m personal transition for rand_int.
function rand_int(min,max) {
	let rand_int_result;
	rand_int_result = truncate(rand(min,max));
	return rand_int_result;
}

// Gets the centre X of stage field
function GetCenterX() {
	let calc = GetStgFrameWidth / 2;
	return calc;
}

// Gets the centre of stage field
function GetCenterY() {
	let calc = GetStgFrameHeight / 2;
	return calc;
}

// Gets minimum X of stage field ( x = 0 )
function GetClipMinX() {
	let calc = GetStgFrameWidth - GetStgFrameWidth;
	return calc;
}

// Gets maximum X of stage field
function GetClipMaxX() {
	let calc = GetStgFrameWidth;
	return calc;
}

// Gets minimum Y of stage field
function GetClipMinY() {
	let calc = GetStgFrameHeight - GetStgFrameHeight;
	return calc;
}

// Gets maximum Y of stage field
function GetClipMaxY() {
	let calc = GetStgFrameHeight;
	return calc;
}

function GetDistanceXY(x1,y1,x2,y2) {
	let distance = (((x2-x1)^2+(y2-y1)^2)^(1/2));
	return distance;
}

function GetPointToPointAngle(x1,y1,x2,y2) {
	let dir = atan2(y1-y2,x1-x2);
	return dir;
}

// Because ObjMove_SetSpeed does not work with Primitive 2D Sprites, I decided to make my own move code.
task ObjMove_Special(obj,x,y,v,dir) {
	let vx = v * cos(dir);
	let vy = v * sin(dir);

	while(! Obj_IsDeleted(obj)) {
		ObjRender_SetPosition(obj,x,y,0);
		yield;
		x += vx;
		y += vy;
	}
}

// Quake cam 2D to use for spell cards
task quakeCam2DPlay(m,t) {
	let schud2Dx = Get2DCameraX;
	let schud2Dy = Get2DCameraY;
	let zoom2D = Get2DCameraRatio;

	loop(t) {
		Set2DCameraFocusX(round(rand(schud2Dx-m,schud2Dx+m)));
		Set2DCameraFocusY(round(rand(schud2Dy-m,schud2Dy+m)));
		yield;
	}
	Reset2DCamera;
}

// Quake cam 3D to use for spell cards
task quakeCam3DPlay(m,t) {
	let azuSchud = GetCameraAzimuthAngle;
	let eleSchud = GetCameraElevationAngle;

	loop(t) {
		//SetCameraAzimuthAngle(rand(azuSchud-m,azuSchud+m));
		SetCameraElevationAngle(rand(eleSchud-m,eleSchud+m));
		yield;
	}
	//SetCameraAzimuthAngle(azuSchud);
	SetCameraElevationAngle(eleSchud);
}

//----------------------------------------------------------------------------------------------------------------------------------------
// Magic Team Spell card Code

// Randomizer task to mix colours.
task colorMixer(obj,alpha,red,green,blue) {
	while(!Obj_IsDeleted(obj)) {
		ascent(i in 0..4) { ObjRender_SetColor(obj,red,green,blue); }
		red = rand(16,255); green = rand(16,255); blue = rand(16,255);
		if((red == 255) && (green == 255) && (blue == 255)) { red = 16; green = 255; blue = 255; }
		wait(3);
	}
}

// Marisa's spell card attack
task scatterMasterSparkLaser(x,y,dir,red,green,blue) {
	let obj = ObjPrim_Create(OBJ_PRIMITIVE_2D);

	let alpha = 250;
	let fold = 32;

	ObjRender_SetPosition(obj,x,y,0);
	ObjPrim_SetTexture(obj,spelltex);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	Obj_SetRenderPriorityI(obj,40);
	ObjRender_SetPosition(obj,x,y,0);
	ObjRender_SetAngleZ(obj,dir);
	ObjRender_SetScaleXYZ(obj,2,2,0);
	ObjSprite2D_SetSourceRect(obj,1,1,256,128);
	ObjSprite2D_SetDestRect(obj,16,-32,256,32);

	ObjPrim_SetVertexCount(obj,4);
	ObjPrim_SetPrimitiveType(obj,PRIMITIVE_TRIANGLESTRIP);

	colorMixer(obj,alpha,red,green,blue);

    // 0,15 dmg per beam.
    let hit = ObjSpell_Create();
    ObjSpell_SetEraseShot(hit,true);
    ObjSpell_SetDamage(hit,2.50);
    ObjSpell_SetIntersectionCircle(hit,GetPlayerX,GetPlayerY,32);
    ObjSpell_SetIntersectionLine(hit,GetPlayerX-16,GetPlayerY,GetPlayerX+16,GetPlayerY-1000,500);

	while(!Obj_IsDeleted(obj)) {
		ObjPrim_SetVertexPosition(obj, 0, 0, -fold,0);
		ObjPrim_SetVertexUVT(obj, 0, 1, 1);
		ObjPrim_SetVertexPosition(obj, 1, 400, -fold,0);
		ObjPrim_SetVertexUVT(obj, 1, 256, 1);
		ObjPrim_SetVertexPosition(obj, 2, 0, fold,0);
		ObjPrim_SetVertexUVT(obj, 2, 1, 128);
		ObjPrim_SetVertexPosition(obj, 3, 400, fold,0);
		ObjPrim_SetVertexUVT(obj, 3, 256, 128);

		ObjRender_SetPosition(obj,GetPlayerX,GetPlayerY,0);

		fold--;
		if(fold <= 0) { break; }
		yield;
	}
    Obj_Delete(hit);
    Obj_Delete(obj);
}

// Extraordinary variable and function specifically for Alice's dolls. 
let dance = 0;
function danceVarSet { dance = 1; }

// Alice spell card attack
task dancingShangaiDoll(x,y,dir,stringL) {
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let c = 0;
	let stringLength = stringL;
	let maxStringLength = 0;

	let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
	let colKies = 0;
	
	ObjPrim_SetTexture(obj,spelltex);
	ObjRender_SetBlendType(obj,BLEND_ALPHA);
	ObjRender_SetAlpha(obj,255);
	Obj_SetRenderPriorityI(obj,40);
	ObjRender_SetAngleZ(obj,0);
	ObjRender_SetScaleXYZ(obj,1,1,0);	
	ObjSprite2D_SetSourceRect(obj,0,208,32,256);
	ObjSprite2D_SetDestCenter(obj);

	ObjRender_SetPosition(obj,x,y,0);

	shanghaiBombAnim(obj);
	magicCircleMini(obj);

	while(!Obj_IsDeleted(obj)) {

		// Control the shanghai dolls to increase their distance from Alice
		if(ObjRender_GetX(obj) < GetClipMaxX-16 || ObjRender_GetX(obj) > GetClipMinX+16 || ObjRender_GetY(obj) > GetClipMinY+16 || ObjRender_GetY(obj) < GetClipMaxY-16) {
			if(c < 60 && GetVirtualKeyState(VK_SLOWMOVE) == KEY_HOLD) { maxStringLength = stringLength; stringLength+=3; }
			ObjRender_SetPosition(obj,GetPlayerX+maxStringLength*cos(dir),GetPlayerY+maxStringLength*sin(dir),0);
		}

		// Control time is over, time to explode
		if(c == 200) { PlaySFX(SFX_SHANGHAIBOOM); }
		if(c > 250) {
			PlaySFX(SFX_EXPL00);
			quakeCam2DPlay(2,30);
			quakeCam3DPlay(1,30);
			ascent(i in 0..16) {
				colKies = rand_int(0,7);
				playerGlitterEffect(ObjRender_GetX(obj),ObjRender_GetY(obj),rand(1,3),rand_int(0,360),8,255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
			}
			let hit = ObjSpell_Create();
			ObjSpell_SetDamage(hit,60);
			DeleteShotInCircle(TYPE_SHOT,TYPE_IMMEDIATE,ObjRender_GetX(obj),ObjRender_GetY(obj),128);
			ObjSpell_SetIntersectionCircle(hit,ObjRender_GetX(obj),ObjRender_GetY(obj),64);
			yield;
			Obj_Delete(obj);
			Obj_Delete(hit);
			break;
		}
		c++;
		yield;
	}
	Obj_Delete(obj);
}

// Fever animation for shanghai dolls
task shanghaiBombAnim(obj) {
	let animL = 0;
	
	let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
	let colKies = 0;

	while(!Obj_IsDeleted(obj)) {
		
		ObjSprite2D_SetSourceRect(obj,0+animL,208,32+animL,256);
		animL+=32;
		if(animL == 128) { animL = 0; } 
		
		colKies = rand_int(0,7);
		playerGlitterEffect(ObjRender_GetX(obj),ObjRender_GetY(obj),rand(1,3),rand_int(0,360),1,255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);

		wait(10);
	}
}

// break effect glitter
task playerGlitterEffect(x,y,v,dir,parts,alpha,red,green,blue) {

	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let partSizeScale = parts;
	let partc = 0;

	ObjPrim_SetTexture(obj,circglit);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	Obj_SetRenderPriorityI(obj,30);
	ObjRender_SetPosition(obj,x,y,0);
	ObjMove_SetSpeed(obj,v);
	ObjMove_SetAngle(obj,dir);
	ObjRender_SetScaleXYZ(obj,parts,parts,0);
	ObjSprite2D_SetSourceRect(obj,0,0,64,64);
	ObjSprite2D_SetDestCenter(obj);

	ObjMove_Special(obj,x,y,v,dir);

	while(! Obj_IsDeleted(obj)) {
		ObjRender_SetColor(obj,red,green,blue);
		ObjRender_SetAlpha(obj,alpha);
		ObjRender_SetScaleXYZ(obj,parts,parts,0);

		// glittering effect
		parts = rand(partSizeScale,(partSizeScale+0.5))*sin(partc*rand_int(10,20));
		partc++;

		// remove timer
		if(partc > round(rand(90,180))){ Obj_Delete(obj); }

		yield;
	}
}

// Player breakeffect
task playerBreakEffect {
	let dir = 0;
	let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
	let colKies = 0;

	loop(6){
		colKies = rand_int(0,7);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		dir+=360/24;
	}
	yield;
	loop(6){
		colKies = rand_int(0,7);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		dir+=360/24;
	}
	yield;
	loop(6){
		colKies = rand_int(0,7);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		dir+=360/24;
	}
	yield;
	loop(6){
		colKies = rand_int(0,7);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		playerGlitterEffect(GetPlayerX+156*cos(dir),GetPlayerY+156*sin(dir),rand(0.5,2),dir,round(rand(2,4)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		dir+=360/24;
	}
}

// Player death breakeffect
task playerPichuunEffect {
	let dir = 0;
	let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
	let colKies = 0;

	loop(12){
		colKies = rand_int(0,7);
		playerGlitterEffect(GetPlayerX,GetPlayerY,rand(0.2,2.0),dir,round(rand(0.5,2)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		playerGlitterEffect(GetPlayerX,GetPlayerY,rand(0.2,2.0),dir,round(rand(0.5,2)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		dir+=360/7;
	}
	yield;
	loop(12){
		colKies = rand_int(0,7);
		playerGlitterEffect(GetPlayerX,GetPlayerY,rand(0.2,2.0),dir,round(rand(0.5,2)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		playerGlitterEffect(GetPlayerX,GetPlayerY,rand(0.2,2.0),dir,round(rand(0.5,2)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		dir+=360/13;
	}
}

// Spinning star image under player during bomb. Shrinks over time.
task magicCircleMagic(x,y) {
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let circSpin = 0;
	let circScale = 0;
	let circSwitch = 0;
	let alpha = 255;
	let red = 255;
	let green = 255;
	let blue = 255;

	ObjPrim_SetTexture(obj,spelltex);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	Obj_SetRenderPriorityI(obj,20);
	ObjRender_SetPosition(obj,x,y,0);
	ObjRender_SetScaleXYZ(obj,circScale,circScale,0);
	ObjSprite2D_SetSourceRect(obj,0,136,68,204);
	ObjSprite2D_SetDestCenter(obj);

	colorMixer(obj,alpha,red,green,blue);

	while(!Obj_IsDeleted(obj)) {

		ObjRender_SetPosition(obj,GetPlayerX,GetPlayerY,0);
		ObjRender_SetAngleZ(obj,circSpin);
		ObjRender_SetScaleXYZ(obj,circScale,circScale,0);

		circSpin+=5;

		if(circSwitch == 0) { if(circScale < 4) { circScale+=0.5; } wait(1); if(circScale == 4) { circSwitch = 1; } }
		if(circSwitch == 1) { circScale-=0.014; }
		if(circScale <= 0) { Obj_Delete(obj); }
		yield;
	}
}

// Spinning star image under shanghai dolls. Static.
task magicCircleMini(obj) {
	let obj2 = ObjPrim_Create(OBJ_SPRITE_2D);
	let circSpin = 0;
	let alpha = 255;
	let red = 0; let green = 0; let blue = 0;

	ObjPrim_SetTexture(obj2,spelltex);
	ObjRender_SetBlendType(obj2,BLEND_ADD_ARGB);
	Obj_SetRenderPriorityI(obj2,20);
	ObjRender_SetScaleXYZ(obj2,1,1,0);
	ObjSprite2D_SetSourceRect(obj2,0,136,68,204);
	ObjSprite2D_SetDestCenter(obj2);

	colorMixer(obj2,alpha,red,green,blue);

	while(!Obj_IsDeleted(obj)) {
		ObjRender_SetPosition(obj2,ObjRender_GetX(obj),ObjRender_GetY(obj),0);
		ObjRender_SetAngleZ(obj2,circSpin);
		circSpin+=5;
		yield;
	}
	Obj_Delete(obj2);
}

//----------------------------------------------------------------------------------------------------------------------------------------
// Border Team Spell Card code

// Reimu glow and Musou Tensei spawner
task setReimuGlow {
	let dir = 0;
	reimuGlowSpriteEx(GetPlayerX,GetPlayerY);
	loop(45) {
		reimuGlowSprite(GetPlayerX,GetPlayerY);
		reimuGlowSprite(GetPlayerX,GetPlayerY);
		wait(5);
	}
}

// Hakkero piece function
function hakkeroPiece(texture,startAlpha,r,g,b) {
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	ObjPrim_SetTexture(obj,texture);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	ObjRender_SetAlpha(obj,startAlpha);
	ObjRender_SetColor(obj,r,g,b);
	Obj_SetRenderPriorityI(obj,20);
	ObjRender_SetScaleXYZ(obj,1,1,0);
	ObjSprite2D_SetSourceRect(obj,0,0,256,256);
	ObjSprite2D_SetDestCenter(obj);
	return obj;
}

// 8 sided border / hakkero animation for Border team
task magicCircleBorder(x,y) {

	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let obj2 = ObjPrim_Create(OBJ_SPRITE_2D);
		
	let circSpin = 0;
	let circScale = 0;
	let circSwitch = 0;

	// Border magic circle yin yang
	ObjPrim_SetTexture(obj,mCircleBorder);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	ObjRender_SetAlpha(obj,255);
	ObjRender_SetColor(obj,255,255,255);
	Obj_SetRenderPriorityI(obj,21);
	ObjRender_SetScaleXYZ(obj,circScale,circScale,0);
	ObjSprite2D_SetSourceRect(obj,0,0,128,128);
	ObjSprite2D_SetDestCenter(obj);

	// Border magic circle yin yang glow
	ObjPrim_SetTexture(obj2,texBallGlow);
	ObjRender_SetBlendType(obj2,BLEND_ADD_ARGB);
	ObjRender_SetAlpha(obj,255);
	Obj_SetRenderPriorityI(obj2,20);
	ObjRender_SetScaleXYZ(obj2,circScale+0.1,circScale+0.1,0);
	ObjRender_SetColor(obj,205,205,205);
	ObjSprite2D_SetSourceRect(obj2,0,0,128,128);
	ObjSprite2D_SetDestCenter(obj2);
	
	colorMixer(obj2,0,205,205,205);

	// Hakkero pieces being loaded as separate objects
	let texAnim0 = hakkeroPiece(spelltexBorder0,0,255,255,255);
	let texAnim1 = hakkeroPiece(spelltexBorder1,0,255,255,255);
	let texAnim2 = hakkeroPiece(spelltexBorder2,0,255,255,255);

	fadeInOutHandler(texAnim0,0);
	fadeInOutHandler(texAnim1,10);
	fadeInOutHandler(texAnim2,20);

	colorMixer(texAnim0,0,255,255,255);
	colorMixer(texAnim1,0,255,255,255);
	colorMixer(texAnim2,0,255,255,255);

	while(!Obj_IsDeleted(obj)) {
		ObjRender_SetPosition(obj,GetPlayerX,GetPlayerY,0);
		ObjRender_SetScaleXYZ(obj,circScale,circScale,0);
		ObjRender_SetPosition(obj2,GetPlayerX,GetPlayerY,0);	
		ObjRender_SetScaleXYZ(obj2,circScale+0.1,circScale+0.1,0);

		ObjRender_SetPosition(texAnim0,GetPlayerX,GetPlayerY,0);
		ObjRender_SetPosition(texAnim1,GetPlayerX,GetPlayerY,0);
		ObjRender_SetPosition(texAnim2,GetPlayerX,GetPlayerY,0);

		ObjRender_SetAngleZ(obj,-circSpin);
		ObjRender_SetAngleZ(texAnim0,circSpin);
		ObjRender_SetAngleZ(texAnim1,circSpin);
		ObjRender_SetAngleZ(texAnim2,circSpin);
		circSpin+=2;

		if(circSwitch == 0) { circScale+=0.1; yield; if(circScale > 1) { circSwitch = 1; } }
		if(circSwitch == 1) { circScale-=0.005; }
		if(circScale <= 0) {
			Obj_Delete(obj);
			magicCircleBoom(texAnim0);
			magicCircleBoom(texAnim1);
			magicCircleBoom(texAnim2);
		}
		yield;
	}
	Obj_Delete(obj); Obj_Delete(obj2);
}

// Hakkero piece fader handler
task fadeInOutHandler(obj,t) {
	let alpha = 0;
	let switch = 0;

	wait(t);

	while(!Obj_IsDeleted(obj)) {
		if(alpha < 248 && switch == 0) {
		    alpha+=8;
		    if(alpha == 248) {
		        switch = 1;
            }
        }
		if(alpha > 0 && switch == 1) {
		    alpha-=8;
		    if(alpha == 0) {
		        switch = 0;
            }
        }
		ObjRender_SetAlpha(obj,alpha);
		yield;
	}
}

// Ending Hakkero fade
task magicCircleBoom(obj){
	let scale = 1;
	let alpha = 192;

	while(scale < 3){
		ObjRender_SetColor(obj,alpha,alpha,alpha);
		ObjRender_SetScaleXYZ(obj,scale,scale,0);
		scale+=0.1;
		alpha-=12;
		yield;
	}
	Obj_Delete(obj);
}

// Reimu glow (her own body)
task reimuGlowSpriteEx(x,y) {
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let dir = 0;
	let scale = 1;
	let scaleFrame = 0;
	let alpha = 255;
	let counter = 0;

	ObjPrim_SetTexture(obj,teamtex2);
	ObjRender_SetBlendType(obj,BLEND_ALPHA);
	ObjRender_SetAlpha(obj,alpha);
	ObjRender_SetColor(obj,255,255,255);
	Obj_SetRenderPriorityI(obj,20);
	ObjRender_SetScaleXYZ(obj,scale,scale,0);
	ObjSprite2D_SetSourceRect(obj,0,0,32,48);
	ObjSprite2D_SetDestCenter(obj);
	ObjMove_SetAcceleration(obj,0.1);
	ObjMove_SetMaxSpeed(obj,3);

	colorMixer(obj,0,255,255,255);

	while(!Obj_IsDeleted(obj)) {
		ObjRender_SetPosition(obj,GetPlayerX,GetPlayerY,0);
		ObjRender_SetScaleXYZ(obj,scale,scale,0);

		scale += (8*sin(scaleFrame)/220);
		scaleFrame += 5;
		if(counter == 300) { Obj_Delete(obj); }

		counter++;
		yield;
	}
}

// Reimu aura of Musou Tensei
task reimuGlowSprite(x,y) {
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let dir = 0;
	let v = 0;
	let alpha = 255;

	let vx = v*cos(dir);
	let vy = v*sin(dir);

	ObjPrim_SetTexture(obj,teamtex2);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	ObjRender_SetAlpha(obj,alpha);
	ObjRender_SetColor(obj,255,255,255);
	Obj_SetRenderPriorityI(obj,20);
	ObjRender_SetScaleXYZ(obj,1,1,0);
	ObjSprite2D_SetSourceRect(obj,0,0,32,48);
	ObjSprite2D_SetDestCenter(obj);
	ObjMove_SetAcceleration(obj,0.1);
	ObjMove_SetMaxSpeed(obj,3);

	dir = rand(0,360);
	colorMixer(obj,0,255,255,255);

	while(!Obj_IsDeleted(obj)) {
		ObjRender_SetPosition(obj,x,y,0);
		ObjRender_SetAlpha(obj,alpha);
		vx = v*cos(dir);
		vy = v*sin(dir);
		v+=0.1;
		if(alpha > 0) { alpha-=4; }
		if(alpha <= 0) { Obj_Delete(obj); }
		yield;

		x += vx;
		y += vy;
	}
}

// Yin Yang ball for Musou Tensei (does spell damage)
task reimuDiscoBall(source,x,y,dir,ballnum) {
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let counter = 0;
	let r = 0;
	let diracc = 1;
	let v = 0;
	let vx = v*cos(dir);
	let vy = v*sin(dir);
	let ballDir = dir;
	let ballAim = 0;
	let ballSpin = rand_int(0,359);
	let scale = 0.5;
	let fireTime = 174 + ballnum*6;

	// Reimu ball object
	ObjPrim_SetTexture(obj,texBall);
	ObjRender_SetBlendType(obj,BLEND_ALPHA);
	ObjRender_SetAlpha(obj,255);
	ObjRender_SetColor(obj,128,128,128);
	Obj_SetRenderPriorityI(obj,41);
	ObjRender_SetScaleXYZ(obj,scale,scale,0);
	ObjSprite2D_SetSourceRect(obj,0,0,128,128);
	ObjSprite2D_SetDestCenter(obj);
	ObjRender_SetPosition(obj,x,y,0);
	ObjRender_SetPosition(obj,GetPlayerX+r*cos(dir),GetPlayerY+r*sin(dir),0);

	reimuBallGlow(obj,ObjMove_GetX(obj),ObjMove_GetY(obj),0.5,255,255,255,255);
	reimuBallGlitterHandle(obj);
	colorMixer(obj,255,255,255,255);

	while(!Obj_IsDeleted(obj)) {
		ObjRender_SetAngleZ(obj,ballSpin);
		ballSpin += 5;
		if(r < 128) { r+=1.2; }

		// Ball behaviour
		if(counter < fireTime) {
			ObjRender_SetPosition(obj,GetPlayerX+r*cos(dir),GetPlayerY+r*sin(dir),0);
			dir -= diracc;
			if(r > 48) { ObjSpell_SetDamage(source,0.5); }
			diracc += 0.05;
			DeleteShotInCircle(TYPE_SHOT,TYPE_IMMEDIATE,ObjRender_GetX(obj),ObjRender_GetY(obj),32);
			ObjSpell_SetIntersectionCircle(source,ObjRender_GetX(obj),ObjRender_GetY(obj),32);
		}
		if(counter == fireTime) { x = ObjRender_GetX(obj); y = ObjRender_GetY(obj); }
		if(counter > fireTime) {
			vx = v*cos(dir);
			vy = v*sin(dir);
			x += vx;
			y += vy;
			v = 25;
			ObjRender_SetPosition(obj, x, y, 0);

			// Aim at first thing on screen.
			if(length(scanScreen) > 0) {
				ballAim = scanScreen[0];
				dir = GetPointToPointAngle(ObjMove_GetX(ballAim),ObjMove_GetY(ballAim),ObjRender_GetX(obj),ObjRender_GetY(obj));

				if(GetObjectDistance(obj,ballAim) < 12) {
					if(ballnum == 0) { quakeCam2DPlay(3,90); quakeCam3DPlay(3,90); }
					PlaySFX(SFX_EXPL00);
					let colSet = [[255, 16, 16], [16,255,16], [16,16,255], [16,255,255], [255,16,255], [255,255,16], [255,128,16]];
					let colKies = 0;
					ascent(i in 0..6) {
						colKies = rand_int(0,7);
						playerGlitterEffect(ObjRender_GetX(obj), ObjRender_GetY(obj), rand(1,3), rand_int(0,360), 8, 255, colSet[colKies][0], colSet[colKies][1], colSet[colKies][2]);
					}
					let hit = ObjSpell_Create();
					ObjSpell_SetDamage(hit,25);
					DeleteShotInCircle(TYPE_SHOT,TYPE_IMMEDIATE,ObjRender_GetX(obj),ObjRender_GetY(obj),64);
					ObjSpell_SetIntersectionCircle(hit,ObjRender_GetX(obj),ObjRender_GetY(obj),64);
					yield;
					Obj_Delete(obj);
					Obj_Delete(hit);
				}
			}
			// Ball has left the field and will be deleted
			if(ObjRender_GetX(obj) < GetClipMinX-128 || ObjRender_GetX(obj) > GetClipMaxX+128 || ObjRender_GetY(obj) < GetClipMinY-128 || ObjRender_GetY(obj) > GetClipMaxY+128) {
				Obj_Delete(obj);
			}
		}
		// Deletion code incase the ball gets stuck somewhere (happens)
		if(counter > 300) {
			PlaySFX(SFX_EXPL00);
			let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
			let colKies = 0;
			ascent(i in 0..6) {
			colKies = rand_int(0,7);
				playerGlitterEffect(ObjRender_GetX(obj),ObjRender_GetY(obj),rand(1,3),rand_int(0,360),8,255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
			}			
			ObjSpell_SetDamage(source,25);
			DeleteShotInCircle(TYPE_SHOT,TYPE_IMMEDIATE,ObjRender_GetX(obj),ObjRender_GetY(obj),64);
			ObjSpell_SetIntersectionCircle(source,ObjRender_GetX(obj),ObjRender_GetY(obj),64);
			Obj_Delete(obj);		
		}

		counter++;

		yield;
	}

	Obj_Delete(obj);
}

//Reimu ball glitter handling
task reimuBallGlitterHandle(obj) {
	let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
	let colKies = 0;
	while(!Obj_IsDeleted(obj)) {
		colKies = rand_int(0,7);
		playerGlitterEffect(ObjRender_GetX(obj),ObjRender_GetY(obj),rand(1,3),rand_int(0,360),2,255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		wait(15);
	}
}

// break effect glitter
task reimuBallGlow(followObj,x,y,parts,alpha,red,green,blue) {

	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let partSizeScale = parts;

	ObjPrim_SetTexture(obj,texBallGlow);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	Obj_SetRenderPriorityI(obj,41);
	ObjRender_SetScaleXYZ(obj,parts,parts,0);
	ObjSprite2D_SetSourceRect(obj,0,0,128,128);
	ObjSprite2D_SetDestCenter(obj);
	ObjRender_SetPosition(obj,ObjMove_GetX(followObj),ObjMove_GetY(followObj),0);

	colorMixer(obj,255,255,255,255);

	while(!Obj_IsDeleted(followObj)) {
		ObjRender_SetPosition(obj,ObjRender_GetX(followObj),ObjRender_GetY(followObj),0);

		ObjRender_SetAlpha(obj,alpha);
		ObjRender_SetScaleXYZ(obj,parts,parts,0);

		// glittering effect
		parts = rand(partSizeScale,(partSizeScale+0.2));
		yield;
	}
	Obj_Delete(obj);
}


// Spawns the gaps in some area around the player
task sukimaGenerator {
	PlaySFX(SFX_SUKIMA);
	yukariGap(GetPlayerX,GetPlayerY,-96,-48);
	wait(15);
	yukariGap(GetPlayerX,GetPlayerY,32,48);	
	wait(15);
	yukariGap(GetPlayerX,GetPlayerY,-96,32);
	wait(15);
	yukariGap(GetPlayerX,GetPlayerY,96,-16);
	wait(15);	
	yukariGap(GetPlayerX,GetPlayerY,-72,-32);
	wait(15);	
	yukariGap(GetPlayerX,GetPlayerY,72,32);	
	wait(15);	
	yukariGap(GetPlayerX,GetPlayerY,-96,0);	
	wait(15);	
	yukariGap(GetPlayerX,GetPlayerY,96,-32);
	wait(15);	
	yukariGap(GetPlayerX,GetPlayerY,-72,32);
	wait(15);	
	yukariGap(GetPlayerX,GetPlayerY,-96,-48);
	wait(15);
	yukariGap(GetPlayerX,GetPlayerY,32,48);	
	wait(15);	
}

// Yukari's gap object
task yukariGap(x,y,offsetX,offsetY) {
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let obj2 = ObjPrim_Create(OBJ_SPRITE_2D);

	ObjPrim_SetTexture(obj,texSukima0);
	ObjRender_SetBlendType(obj,BLEND_ALPHA);
	ObjRender_SetAlpha(obj,254);
	ObjRender_SetColor(obj,128,128,128);
	Obj_SetRenderPriorityI(obj,20);
	ObjRender_SetScaleXYZ(obj,0.3,0.3,0);
	ObjSprite2D_SetSourceRect(obj,0,0,512,256);
	ObjSprite2D_SetDestCenter(obj);
	ObjRender_SetPosition(obj,x,y,0);
	ObjRender_SetAngleXYZ(obj,90,0,0);
	
	ObjPrim_SetTexture(obj2,texSukima1);
	ObjRender_SetBlendType(obj2,BLEND_ADD_ARGB);
	ObjRender_SetAlpha(obj2,0);
	ObjRender_SetColor(obj2,255,255,255);
	Obj_SetRenderPriorityI(obj2,21);
	ObjRender_SetScaleXYZ(obj2,0.3,0.3,0);
	ObjSprite2D_SetSourceRect(obj2,0,0,512,256);
	ObjSprite2D_SetDestCenter(obj2);
	ObjRender_SetPosition(obj2,x,y,0);
	ObjRender_SetAngleXYZ(obj2,90,0,0);	

	sukimaAnimation(obj,false,false);
	sukimaAnimation(obj2,true,false);
	
	while(!Obj_IsDeleted(obj)) { 
		ObjRender_SetPosition(obj,GetPlayerX+offsetX,GetPlayerY+offsetY,0);
		ObjRender_SetPosition(obj2,GetPlayerX+offsetX,GetPlayerY+offsetY,0);
		yield;
	} 	
}	

// Sukima folding animation and alpha setting/fading.
task sukimaAnimation(obj,anim,laseronly) {
	let foldX = 90;
	let alpha = 0;
	let laserAim = 0;
	loop(30) { 
		ObjRender_SetAngleX(obj,foldX);
		foldX-=3;
		yield;
	}		
	if(anim) { 
		sukimaLaserHandle(ObjRender_GetX(obj),ObjRender_GetY(obj),0,2048,24);
		alpha = 0;
		while(alpha < 254) {
			ObjRender_SetAlpha(obj,alpha);
			alpha+=8;
			yield; 
		}
	}
	if(!anim) { 
		alpha = 254;
		while(alpha > 128) {
			ObjRender_SetAlpha(obj,alpha);
			alpha-=8; yield;
			} 
	}
	loop(10) { 
		ObjRender_SetAngleX(obj,foldX);
		foldX+=9;
		yield;
	}	
	Obj_Delete(obj);
}

// Sukima laser handle + spell damage
task sukimaLaserHandle(x,y,dir,lengte,dikte) {
	let obj = ObjPrim_Create(OBJ_PRIMITIVE_2D);
	let laserAim = 0;
	let snijl = lengte / 2;
	let maxsnijd = dikte - 12;
	let snijd = 0;
	let glide = 0;
	let c = 0;

	// Laser primitive object
	ObjPrim_SetPrimitiveType(obj,PRIMITIVE_TRIANGLESTRIP);
	ObjPrim_SetVertexCount(obj,4);	
	ObjPrim_SetTexture(obj,sukimalaser); 
	ObjPrim_SetTexture(obj,sukimalaserglow); 
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	ObjRender_SetAlpha(obj,255);
	Obj_SetRenderPriorityI(obj,40);	
	ObjRender_SetPosition(obj,x,y,0);

	reimuBallGlow(obj,x,y,0.3,255,255,255,255);

	// If there are enemies on the screen
	if(length(scanScreen) > 0) {
		laserAim = scanScreen[rand_int(0,length(scanScreen))];
		dir = GetPointToPointAngle(ObjMove_GetX(laserAim),ObjMove_GetY(laserAim),ObjRender_GetX(obj),ObjRender_GetY(obj));
		ObjRender_SetAngleZ(obj,dir-90);
	}
	// If there are no enemies, lasers are randomly scattered.
	else { dir = rand_int(240,300); }
	
	// Set the angle depending on the previous
	ObjRender_SetAngleZ(obj,dir-90);
	
	sukimaLaserHandleGlow(x,y,dir,lengte,dikte);
	
	// SFX and quaking
	PlaySFX(SFX_LAZER02);
	quakeCam2DPlay(3,30); quakeCam3DPlay(3,30);
	
	// Spell effect hitting. 
	let hit = ObjSpell_Create();
	ObjSpell_SetDamage(hit,20);
	
	// Intersection circle is at the gap it self.
	ObjSpell_SetIntersectionCircle(hit,ObjRender_GetX(obj),ObjRender_GetY(obj),64);
	DeleteShotInCircle(TYPE_SHOT,TYPE_ITEM,ObjRender_GetX(obj),ObjRender_GetY(obj),64);
	
	// Intersection line is drawn between the laser base (gap) and enemy object scanned.
	ObjSpell_SetIntersectionLine(hit,ObjRender_GetX(obj),ObjRender_GetY(obj),ObjMove_GetX(laserAim),ObjMove_GetY(laserAim),32);			
	
	if(length(scanScreen) > 0) {
		PlaySFX(SFX_EXPL00);
		let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
		let colKies = 0;
		ascent(i in 0..6) {
			colKies = rand_int(0,7);
			playerGlitterEffect(ObjRender_GetX(laserAim),ObjRender_GetY(laserAim),rand(0.5,1),rand_int(0,360),4,255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		}
	}
	
	while(! Obj_IsDeleted(obj)) {
		ObjPrim_SetVertexPosition(obj, 0, -snijd, snijl,0);
		ObjPrim_SetVertexUVT(obj, 0, 0, 0+glide);
		
		ObjPrim_SetVertexPosition(obj, 1, snijd, snijl,0);
		ObjPrim_SetVertexUVT(obj, 1, 64, 0+glide);
		
		ObjPrim_SetVertexPosition(obj, 2, -snijd, snijl-snijl,0);
		ObjPrim_SetVertexUVT(obj, 2, 0, 256+glide);
		
		ObjPrim_SetVertexPosition(obj, 3, snijd, snijl-snijl,0);
		ObjPrim_SetVertexUVT(obj, 3, 64, 256+glide);
		
		if(c < 60) { if(snijd < maxsnijd) { snijd+=4; } }
		if(c >= 60) { 
			snijd-=2;
			if(snijd == 0) { Obj_Delete(obj); } 
		}
		c++;
		glide+=2;
		yield;
	}
}

// Effect for the rushing ballerinas
task sukimaLaserHandleGlow(x,y,dir,lengte,dikte) {
	let obj = ObjPrim_Create(OBJ_PRIMITIVE_2D);
	let snijl = lengte / 2;
	let maxsnijd = dikte;
	let snijd = 0;
	let glide = 0;
	let c = 0;

	ObjPrim_SetPrimitiveType(obj,PRIMITIVE_TRIANGLESTRIP);
	ObjPrim_SetVertexCount(obj,4);	
	ObjPrim_SetTexture(obj,sukimalaserglow); 
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	ObjRender_SetAlpha(obj,255);
	Obj_SetRenderPriorityI(obj,40);	
	ObjRender_SetPosition(obj,x,y,0);
	ObjRender_SetAngleZ(obj,dir-90);

	colorMixer(obj,255,255,255,255); 
	
	let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
	let colKies = 0;
	ascent(i in 0..6) {
		colKies = rand_int(0,7);
		playerGlitterEffect(ObjRender_GetX(obj),ObjRender_GetY(obj),rand(1,3),rand_int(0,360),4,255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
	}
	
	while(! Obj_IsDeleted(obj)) {
		ObjPrim_SetVertexPosition(obj, 0, -snijd, snijl,0);
		ObjPrim_SetVertexUVT(obj, 0, 0, 0+glide);
		
		ObjPrim_SetVertexPosition(obj, 1, snijd, snijl,0);
		ObjPrim_SetVertexUVT(obj, 1, 64, 0+glide);
		
		ObjPrim_SetVertexPosition(obj, 2, -snijd, snijl-snijl,0);
		ObjPrim_SetVertexUVT(obj, 2, 0, 256+glide);
		
		ObjPrim_SetVertexPosition(obj, 3, snijd, snijl-snijl,0);
		ObjPrim_SetVertexUVT(obj, 3, 64, 256+glide);
		
		if(c < 60) { if(snijd < maxsnijd) { snijd+=4; } }
		if(c >= 60) { 
			snijd-=2;
			if(snijd == 0) { Obj_Delete(obj); } 
		}
		c++;
		glide+=2;
		yield;
	}
}

// ===============================================================================================
// Funky circles for player

// Player outer funky circle
task playerFunkyCircle {
	let obj = ObjPrim_Create(OBJ_PRIMITIVE_2D);
	let circc = -5;
	let circsize = 40;
	let circsize2 = 0;
	let circd = 0;
	let circx = 0;
	let circdraai = 0;
	let circswitch = 0;

	ObjPrim_SetPrimitiveType(obj,PRIMITIVE_TRIANGLESTRIP);
	ObjPrim_SetVertexCount(obj,80*2+2);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	Obj_SetRenderPriority(obj,0.3);
	ObjPrim_SetTexture(obj,playerCircTex);

	while(circx < 81) {
		ObjPrim_SetVertexUVT(obj,circx*2,54,circd*2/80);
		ObjPrim_SetVertexUVT(obj,circx*2+1,68,circd*2/80);
		circd+=-128;
		circx++;
	}
	while(!Obj_IsDeleted(obj)) {
		while(circx < 81) {
			ObjPrim_SetVertexPosition(obj,circx*2,circsize*cos(circx*360/80),circsize*sin(circx*360/80),0);
			ObjPrim_SetVertexPosition(obj,circx*2+1,(circsize2)*cos(circx*360/80),(circsize2)*sin(circx*360/80),0);
			ObjPrim_SetVertexColor(obj,circx*2+1,50,50,50);
			circx++;
		}
		if(circswitch == 0) { circdraai-=4; circsize += (-cos(circc))*3.1; if(circc == 45){ circswitch = 1; } }
		if(circswitch == 1) { circdraai-=4; circsize += (-cos(circc))*3.2; circsize2 += (-cos(circc))*8.2; if(circc == 90){ circswitch = 2; } }
		circc++;

		ObjRender_SetPosition(obj,GetPlayerX,GetPlayerY,0);
		ObjRender_SetAngleZ(obj,circdraai);
		circdraai -= 5;
		circx = 0;

		if(breakOut) { Obj_Delete(obj); }
		yield;
	}
	Obj_Delete(obj);
}

// Player inner funky circle
task playerFunkyCircle2 {
	let obj = ObjPrim_Create(OBJ_PRIMITIVE_2D);
	let circsize = 85;
	let circd = 0;
	let circx = 0;
	let circdraai = 0;
	let circswitch = 0;
	let circc = 17;

	ObjPrim_SetPrimitiveType(obj,PRIMITIVE_TRIANGLESTRIP);
	ObjPrim_SetVertexCount(obj,80*2+2);
	ObjRender_SetBlendType(obj,BLEND_ADD_RGB);
	Obj_SetRenderPriority(obj,0.3);
	ObjPrim_SetTexture(obj,playerCircTex);

	while(circx < 81) {
		ObjPrim_SetVertexUVT(obj,circx*2,40,circd*3/80);
		ObjPrim_SetVertexUVT(obj,circx*2+1,54,circd*3/80);
		circd+=-256;
		circx++;
	}

	while(!Obj_IsDeleted(obj)) {
		while(circx < 81) {
			ObjPrim_SetVertexPosition(obj, circx*2, circsize*cos(circx*360/80), circsize*sin(circx*360/80),0);
			ObjPrim_SetVertexPosition(obj, circx*2+1, (circsize+20)*cos(circx*360/80), (circsize+20)*sin(circx*360/80),0);
			ObjPrim_SetVertexColor(obj,circx*2+1,180,180,180);
			circx++;
		}
		if(circc > 0 && circswitch == 0) { circsize += circc*cos(360/circsize); circc-=0.5; if(circc == 0) { circswitch = 1; } }
		if(circc < 9.6 && circswitch == 1) { circsize -= circc*cos(360/circsize); circc+=0.2; if(circc >= 9.5 ){ circswitch = 2; } }

		ObjRender_SetPosition(obj,GetPlayerX,GetPlayerY,0);

		ObjRender_SetAngleZ(obj,circdraai);
		circdraai += 3;
		circx = 0;

		if(breakOut) { Obj_Delete(obj); }
		yield;
	}
	Obj_Delete(obj);
}

// Hitbox 
task plyHitbox(x,y) {
	let alpha = 0;
	let spinValue = 0;
	
	ObjPrim_SetTexture(hitboxObj,hitbox);
	ObjRender_SetBlendType(hitboxObj,BLEND_ALPHA);
	Obj_SetRenderPriorityI(hitboxObj,50);
	ObjRender_SetAlpha(hitboxObj,0);
	ObjMove_SetSpeed(hitboxObj,0);
	ObjMove_SetAngle(hitboxObj,0);
	ObjRender_SetScaleXYZ(hitboxObj,1,1,0);
	ObjSprite2D_SetSourceRect(hitboxObj,0,0,64,64);
	ObjSprite2D_SetDestCenter(hitboxObj);	
	
	while(!Obj_IsDeleted(hitboxObj)) {
		ObjRender_SetPosition(hitboxObj,GetPlayerX,GetPlayerY,0);
		ObjRender_SetAngleZ(hitboxObj,spinValue);
		spinValue+=2;
		yield;
	}
}

// Handles the player death respawn effect
task pichuunHandler {
	SetForbidPlayerShot(true);
	SetForbidPlayerSpell(true);

	ObjRender_SetAlpha(playerObj,0);
	wait(60);

	let dir = 0;
	let colSet = [[255, 16, 16],[16,255,16],[16,16,255],[16,255,255],[255,16,255],[255,255,16],[255,128,16]];
	let colKies = 0;
	ObjRender_SetPosition(playerObj,GetCenterX,GetClipMaxY+32,0);
	ObjRender_SetAlpha(playerObj,255);
	yield;

	loop(16) {
		colKies = rand_int(0,7);
		playerGlitterEffect(GetPlayerX+72*cos(dir),GetPlayerY+72*sin(dir),-1,dir,round(rand(1,2)),255,colSet[colKies][0],colSet[colKies][1],colSet[colKies][2]);
		dir+=360/16;
	}

	SetForbidPlayerShot(false);
	SetForbidPlayerSpell(false);
}

// Handles the rebirth invinc effect
task invincEffect(t) {
	ascent(i in 0..t/10) {
		ObjRender_SetColor(playerObj,64,64,255);
		wait(5);
		ObjRender_SetColor(playerObj,255,255,255);
		wait(5);
	}
}

// Handles the revenge wave which is emitted when the player is pichuun
task DeleteShotAllEX(type,fade,x,y) { 
	let obj = ObjPrim_Create(OBJ_SPRITE_2D);
	let r = 8; 
	let s = 0;
	
	ObjPrim_SetTexture(obj,texDeathWave);
	ObjRender_SetBlendType(obj,BLEND_ADD_ARGB);
	Obj_SetRenderPriorityI(obj,50);
	ObjRender_SetAlpha(obj,250);
	ObjRender_SetScaleXYZ(obj,s,s,0);
	ObjSprite2D_SetSourceRect(obj,0,0,128,128);
	ObjSprite2D_SetDestCenter(obj);	
	ObjRender_SetPosition(obj,x,y,0);
	
	colorMixer(obj,255,255,255,255);
	
	while(r < 600) { 
		let dWaveShotID = GetShotIdInCircleA2(x,y,r,TARGET_ENEMY);

		ascent(i in 0..length(dWaveShotID)) { 
			if(!ObjShot_IsSpellResist(dWaveShotID[i])) { 
				playerGlitterEffect(ObjRender_GetX(dWaveShotID[i]),ObjRender_GetY(dWaveShotID[i]),rand(0.1,0.5),rand(0,360),rand(0.1,3),155,255,32,32); 
				Obj_Delete(dWaveShotID[i]);
			}
		}
		ObjRender_SetScaleXYZ(obj,s,s,0);

		r+=8; 
		if(r > 100) { s+=0.4; }

		yield; 
	} 
	Obj_Delete(obj);
}