import * as mc from '@minecraft/server';
import * as ui from '@minecraft/server-ui';
import * as eventmanager from './event_manager';

let friend_exe = null;
let friend_exeId = null;

export let friend_exeHasTarget = false;
export let friend_noTargetTime = 0;

//Math Functions
export function dotProduct(v1, v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; }
export function invertVector(v) { return { x: -v.x, y: -v.y, z: -v.z }; }
export const randInt = (min, max) => { return Math.floor(Math.random() * (max - min + 1)) + min; }
export const randFloat = (min, max) => { return Math.random() * (max - min) + min; }

export function Distance(location1, location2) {
	const difference = { x: location1.x - location2.x, y: location1.y - location2.y, z: location1.z - location2.z };
	return Math.sqrt(difference.x ** 2 + difference.y ** 2 + difference.z ** 2);
}

export function clamp(value, min, max) {
	return Math.max(min, Math.min(max, value));
}

let intro = false;
const introTimeline = [
	{ time: 0, key: "text.blocklab_friend_exe:intro.0", duration: 0.5 },
	{ time: 0.5, key: "text.blocklab_friend_exe:intro.1", duration: 4.5 },
	{ time: 5, key: "text.blocklab_friend_exe:intro.2", duration: 8 },
	{ time: 13.5, key: "text.blocklab_friend_exe:intro.3", duration: 3.5 },
	{ time: 17.5, key: "text.blocklab_friend_exe:intro.4", duration: 8.5 },
	{ time: 26.5, key: "text.blocklab_friend_exe:intro.5", duration: 6.5 },
	{ time: 33.5, key: "text.blocklab_friend_exe:intro.6", duration: 6.5 },
	{ time: 40.5, key: "text.blocklab_friend_exe:intro.7", duration: 1.5 },
	{ time: 42.5, key: "text.blocklab_friend_exe:intro.8", duration: 7.5 },
	{ time: 50.5, key: "text.blocklab_friend_exe:intro.9", duration: 5.5 },
	{ time: 56.5, key: "text.blocklab_friend_exe:intro.10", duration: 8.5 },
	{ time: 65.5, key: "text.blocklab_friend_exe:intro.11", duration: 8.5 },
	{ time: 75, key: "text.blocklab_friend_exe:intro.12", duration: 4.5 }
];

function showActionBarForDuration(key, durationSeconds) {
	const tickInterval = 40; // every 2 seconds
	const totalTicks = durationSeconds * 20;
	let elapsed = 0;

	const runId = mc.system.runInterval(() => {
		mc.world.getPlayers().forEach(player => {
			player.runCommandAsync(`titleraw @a actionbar {"rawtext":[{"translate":"${key}"}]}`);
		});

		elapsed += tickInterval;
		if (elapsed >= totalTicks) {
			mc.system.clearRun(runId);
		}
	}, tickInterval);
}

function startIntroSequence() {
	intro = true;
	for (const line of introTimeline) {
		const ticksUntilStart = line.time * 20;

		mc.system.runTimeout(() => {
			showActionBarForDuration(line.key, line.duration);
		}, ticksUntilStart);
	}
}

// Get AI Friend
export function Friend_Exe() {
	if (friend_exe) return friend_exe;
	friend_exe = friend_exeId ? mc.world.getEntity(friend_exeId) : null;
	return friend_exe;
}

//Mood Based Sound
function ChangeMoodStageSound(friend_exe, prevMoodStage, moodStage) {
	if (prevMoodStage == moodStage) return;

	let sound = `mob.ai-friend.stage${moodStage}.`;

	sound += moodStage > prevMoodStage ? "moodup" : "mooddown";

	friend_exe.runCommand(`playsound ${sound} @a ~ ~ ~ 1`);
}

// Update AI Friend Mood Stage
function friend_exeUpdateMoodStage(friend_exe, moodStage) {
	const prevMoodStage = friend_exe.getProperty("blocklab_friend_exe:moodstage");

	ChangeMoodStageSound(friend_exe, prevMoodStage, moodStage);

	friend_exe.setProperty("blocklab_friend_exe:moodstage", moodStage);

	const stage = Stage(friend_exe);
	if (stage == 0) {
		friend_exe.triggerEvent(`blocklab_friend_exe:set_moodstage_${moodStage}`);
	}

	//malfunction event hier starten 
	if (moodStage == 0 && !eventmanager.isEventActive(1)) {
		eventmanager.startEvent(eventmanager.getMalfunction(), friend_exe, true);
	}
	randomEmoteCounter = 20;

	UpdateEmoticon();
}

function UpdateEmoticon() {
	if (!friend_exe) return;
	const moodStage = MoodStage(Mood(friend_exe));
	mc.world.getPlayers().forEach((player) => {
		player.runCommand(`scriptevent blocklab_friend_exe:playerdata t.emoticon_face = ${5 - moodStage}`);
	});
}

// Get AI Friend Mood
export function Mood(friend_exe) {
	return Number(friend_exe.getProperty("blocklab_friend_exe:mood"));
}

export function Lives(friend_exe) {
	return Number(friend_exe.getProperty("blocklab_friend_exe:lives"));
}

export function setLives(friend_exe, lives) {
	friend_exe.setProperty("blocklab_friend_exe:lives", clamp(lives, 0, 100));
}

export function setMoodStage(friend_exe, stage, offset = 0) {
	const clampedStage = Math.max(0, Math.min(5, stage));
	const baseMood = clampedStage * 20; // Mindestwert der Stage
	friend_exe.setProperty("blocklab_friend_exe:mood", Math.min(100, baseMood + offset));
}

export function playTimeclockSound() {
	if (friend_exe) friend_exe.runCommand(`playsound mob.ai-friend.timeclock @a ~ ~ ~ 1`);
}

let particleCount = 2;

export function setMood(friend_exe, mood, clampMalfunction = false) {
	let clampedMood = Math.max(0, Math.min(100, mood));
	const currentMood = Mood(friend_exe);

	const stage = Stage(friend_exe);
	if (stage > 0) return;

	//In den ersten Tagen kein Horror oder Broken
	const day = mc.world.getDay();
	if (day < 1 && clampedMood < 30 || clampMalfunction) {
		clampedMood = clamp(clampedMood, 30, clampedMood);
	}
	if (day == 0 && mc.world.getTimeOfDay() < 12000)
		clampedMood = clamp(clampedMood, 50, 100);

	//Horror Stage skippen wenn stage 0 
	if (stage == 0 && clampedMood < 30 && clampedMood > 9) {
		clampedMood = (clampedMood < currentMood) ? 9 : 30;
	}

	clampedMood = clamp(clampedMood, 9, 100);

	particleCount--;
	if (particleCount <= 0) {
		if (currentMood > clampedMood) {
			friend_exe.runCommand(`particle blocklab_friend_exe:mooddown`);
		}
		particleCount = randInt(2, 4);
	}
	if (currentMood < clampedMood) {
		friend_exe.runCommand(`particle blocklab_friend_exe:moodup`);
	}

	friend_exe.setProperty("blocklab_friend_exe:mood", clampedMood);
}

export function randomJumpscare(friend_exe) {
	PlayEmote("stage0_headshive", 4);
}

export let stage1RandomTime = 10;
export let stage1RandomCound = 3;

export function setStage(friend_exe, stage) {
	if (stage > 3) return;
	const currentStage = Stage(friend_exe);
	if (currentStage == stage) return;

	lookAtCount++;

	Crouch(false);
	if (stage > 0) {
		eventmanager.stopEvent(friend_exe);
		setMoodStage(friend_exe, 1);
		randomAmbientSound(60);
		stage1repairAmount = randInt(4, 7);
		stage1repairTransformAmount = randInt(7, 12);
	}
	giveFoodToPlayerCount = randInt(10, 30);
	cookeCooldown = 20;

	if (stage == 1) {
		friend_exe.runCommand("particle blocklab_friend_exe:dust");
		friend_noTargetTime = 0;
		stage1RandomCound = randInt(1, 4);
		stage1RandomTime = randInt(10, 30);
		particleStage1Count = 2;
		setLives(friend_exe, randInt(8, 14)); // für stage 2
	} else if (stage == 2) {
		setLives(friend_exe, randInt(10, 15)); // für stage 3
	} else if (stage == 3) {
		setLives(friend_exe, randInt(20, 26));// für stage 0
	}
	//reset
	friend_exe.runCommand(`effect @s clear slowness`);
	randomLoseMoodCount = randInt(20, 30);
	resetLookAtCount();
	PlayEmote("stopemote");
	friend_exe.triggerEvent("blocklab_friend_exe:resetjumpscare");
	if ((currentStage == 1 || currentStage == 3) && stage == 0) {
		cookeCooldown = 70;
		const player = mc.world.getPlayers()[0];
		const spawnPos = player.location;
		const target = mc.world.getDimension(player.dimension.id).spawnEntity("blocklab_friend_exe:ai_friend_target", spawnPos);
		mc.system.runTimeout(() => {
			target.runCommand("teleport ~ ~ -30 ~");
			target.kill();
		}, 70);
		friend_exe.runCommand(`effect @s slowness ${currentStage == 3 ? 8 : 4} 7 true`);
		if (currentStage == 3) {
			giveFoodToPlayerCount = 20 * 12;
			mc.system.runTimeout(() => {
				setMood(friend_exe, 100);
				for (const online of mc.world.getAllPlayers()) {
					online.sendMessage({
						rawtext: [
							{
								translate: `text.blocklab_friend_exe:join.stage0.${randInt(0, 2)}`, with: { rawtext: [{ text: String(online.name) }] }
							}
						]
					});
				}
			}, 20 * 11);
		}
	}
	//set stage
	friend_exe.setProperty("blocklab_friend_exe:stage", stage);
	friend_exe.triggerEvent(`blocklab_friend_exe:set_stage_${stage}`);
	if (stage == 2 || stage == 3) {
		friend_exe.triggerEvent("blocklab_friend_exe:aggressive");
		searchMessageCount = randInt(40, 110);
	}
	if (stage == 0) {
		sneakCount = 2;
		randomEmoteCounter = 20;
		mc.system.runTimeout(() => {
			setMood(friend_exe, 80);
		}, 10);
		eventmanager.resetTimer("stage0");
	}
	if (stage == 1) {
		friend_exe.runCommand(`effect @s slowness 8 7 true`);
	}
	if (stage > 1) {
		friend_exe.runCommand(`effect @s slowness 2 7 true`);
	}
	if (stage > 0) {
		randomHeadturn = randInt(20, 60);
	}
	UpdateAtmosphere(stage);
}

export function UpdateAtmosphere(stage) {
	mc.world.getPlayers().forEach((player) => {
		UpdateAtmospherePlayer(player, stage);
	});
}

export function UpdateAtmospherePlayer(player, stage) {
	if (stage == 0) {
		player.runCommand(`fog @s remove stage1fog`);
	} else if (stage == 1 || stage == 2) {
		player.runCommand(`fog @s push blocklab_friend_exe:stage1_fog_setting stage1fog`);
	}
}

export function Stage(friend_exe) {
	return friend_exe.getProperty("blocklab_friend_exe:stage");
}

// Convert Mood to Stage (100% -> 5 Stages)
export function MoodStage(mood) {
	return Math.round(mood / 20);
}

// Mood Stage String Mapping
const MoodStageStrings = ["mood_m", "mood_e", "mood_u", "mood_o", "mood_f", "mood_h"];
export function MoodStageString(moodStage) {
	return MoodStageStrings[moodStage] || MoodStageStrings[0];
}

function giveItem(player, itemID, keep = false) {
	const item = new mc.ItemStack(itemID, 1);
	item.keepOnDeath = keep;
	player.dimension.spawnItem(item, player.location);
}

// Check if it is Night
export function isNighttime() {
	const time = mc.world.getTimeOfDay();
	return time >= 13000 && time < 23300;
}

export function isFirstDay() {
	return mc.world.getDay() == 0;
}

//set ai friend rotation for darkness
export function rotate_to_player(friend_exe, player) {
	const aiPos = friend_exe.location;
	const playerPos = player.location;

	const dx = playerPos.x - aiPos.x;
	const dz = playerPos.z - aiPos.z;

	let yaw = Math.atan2(-dx, dz) * (180 / Math.PI); // <- note the negative dx
	if (yaw < 0) yaw += 360;

	friend_exe.setRotation({ x: 0, y: yaw });
}

// Special Attack Functions
export function exec_special_attack(friend_exe) {
	const maxDistance = 3; // How far the punch can reach
	const coneAngleDegrees = 60; // How wide the punch is (cone angle)
	//friend_exe.runCommand("/playanimation @s animation.blocklab_friend_exe.stage0_specialattack");
	friend_exe.setProperty("blocklab_friend_exe:is_special_attacking", true);

}

export function special_attack_damage(friend_exe) {
	const maxDistance = 5; // How far the punch can reach
	const coneAngleDegrees = 90; // Width of the cone
	const coneAngleRadians = (coneAngleDegrees / 2) * (Math.PI / 180); // Convert to radians
	const MIN_DISTANCE_OVERRIDE = 1.2;

	// Use rotation-based forward vector
	function getForwardVectorFromRotation(entity) {
		const yaw = entity.getRotation().y * (Math.PI / 180); // Convert to radians
		return {
			x: -Math.sin(yaw),
			y: 0,
			z: Math.cos(yaw),
		};
	}

	const viewDirection = getForwardVectorFromRotation(friend_exe);

	const nearbyEntities = friend_exe.dimension.getEntities({
		location: friend_exe.location,
		maxDistance: maxDistance,
		excludeTypes: ["minecraft:player"],
		excludeSelf: true,
	});

	for (const otherEntity of nearbyEntities) {
		const vectorToTarget = {
			x: otherEntity.location.x - friend_exe.location.x,
			y: otherEntity.location.y - friend_exe.location.y,
			z: otherEntity.location.z - friend_exe.location.z,
		};

		const distance = Math.sqrt(
			vectorToTarget.x ** 2 + vectorToTarget.y ** 2 + vectorToTarget.z ** 2
		);

		if (distance === 0) {
			continue; // Skip self or overlapping entity
		}

		const normalizedVector = {
			x: vectorToTarget.x / distance,
			y: vectorToTarget.y / distance,
			z: vectorToTarget.z / distance,
		};

		const dot =
			viewDirection.x * normalizedVector.x +
			viewDirection.y * normalizedVector.y +
			viewDirection.z * normalizedVector.z;

		const angle = Math.acos(dot);

		if (distance <= MIN_DISTANCE_OVERRIDE || angle <= coneAngleRadians) {
			const knockbackStrength = 5;
			const knockback = {
				x: normalizedVector.x * knockbackStrength,
				z: normalizedVector.z * knockbackStrength,
			};
			otherEntity.applyImpulse({ x: knockback.x, y: 0.5, z: knockback.z });
			otherEntity.applyDamage(5, {
				damagingEntity: friend_exe,
				cause: "entityAttack"
			});
		}
	}
	const punchDistance = 3;
	const effectPoint = {
		x: friend_exe.location.x + viewDirection.x * punchDistance,
		y: friend_exe.location.y + viewDirection.y * punchDistance,
		z: friend_exe.location.z + viewDirection.z * punchDistance,
	};

	friend_exe.dimension.spawnParticle("minecraft:knockback_roar_particle", effectPoint);
}

export function special_attack_deactivate(friend_exe) {
	friend_exe.setProperty("blocklab_friend_exe:is_special_attacking", false);
}

export function randomAmbientSound(wait = 0) {
	const randomX = randInt(-10, 10);
	const randomZ = randInt(-10, 10);
	mc.system.runTimeout(() => {
		const randomSound = randInt(0, 99);
		if (randomSound < 10) {
			friend_exe.runCommand(`playsound ambient.basalt_deltas.mood @a ~${randomX} ~ ~${randomZ}`);
		} else {
			friend_exe.runCommand(`playsound mob.ai-friend.atmosphere.carpet.passive.passive_carpet @a ~${randomX} ~ ~${randomZ}`);
		}
	}, wait);
}

export function randomLookatSound() {
	friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive @s ~ ~ ~ 1");
}

//Events

//debug
/* mc.world.afterEvents.dataDrivenEntityTrigger.subscribe((event) => {
	mc.world.sendMessage(`data trigger ${event.eventId}`);
}); */

// Global scoreboard Constants
export const SCOREBOARD_ITEM_LEVEL = "blocklab_friend_exeItemLevel";
export const SCOREBOARD_ITEM_PROGRESS = "blocklab_friend_exeItemprogressInLevel";
export const SCOREBOARD_DIFFICULTY = "blocklab_friend_exeDifficulty";
export const SCOREBOARD_FALLBACK = "blocklab_friend_exeFallback";
export const PARTICIPANT_NAME = "friend_exe";

mc.world.afterEvents.entitySpawn.subscribe((event) => {
	const entity = event.entity;
	if (entity.typeId == "blocklab_friend_exe:ai_friend") {
		friend_exeId = entity.id;
		entity.getComponent('tameable').tame(mc.world.getPlayers()[0]);

		const scoreboard = mc.world.scoreboard;

		// Utility to get or create an objective
		function ensureObjective(id) {
			let obj = scoreboard.getObjective(id);
			if (!obj) obj = scoreboard.addObjective(id, id);
			return obj;
		}

		// Ensure objectives exist
		const itemLevel = ensureObjective(SCOREBOARD_ITEM_LEVEL);
		const progress = ensureObjective(SCOREBOARD_ITEM_PROGRESS);
		const difficulty = ensureObjective(SCOREBOARD_DIFFICULTY);
		const fallback = ensureObjective(SCOREBOARD_FALLBACK);


		// Reset scores
		itemLevel.setScore(PARTICIPANT_NAME, 0);
		progress.setScore(PARTICIPANT_NAME, 0);
		difficulty.setScore(PARTICIPANT_NAME, 1);
		fallback.setScore(PARTICIPANT_NAME, 0);

		eventmanager.resetTimer("spawn");
	}
});


// AI Friend and Package Load Event
mc.world.afterEvents.entityLoad.subscribe((event) => {
	const entity = event.entity;
	if (entity.typeId == "blocklab_friend_exe:ai_friend") {
		friend_exeId = entity.id;
		entity.triggerEvent("blocklab_friend_exe:remove_events");
		const stage = entity.getProperty("blocklab_friend_exe:stage");
		entity.triggerEvent(`blocklab_friend_exe:set_stage_${stage}`);
		if (stage == 2 || stage == 3) {
			entity.triggerEvent("blocklab_friend_exe:aggressive");
			searchMessageCount = randInt(40, 110);
		}
		if (stage == 0) {
			const prevMoodStage = entity.getProperty("blocklab_friend_exe:moodstage");

			entity.triggerEvent(`blocklab_friend_exe:set_moodstage_${prevMoodStage}`);

		}
		//emoticon update
		Friend_Exe();
		UpdateEmoticon();
		eventmanager.resetTimer("load");
	} else if (entity.typeId == "blocklab_friend_exe:ai_friend_target") {//soll immer gelöscht werdem
		entity.remove();
	} else if (entity.typeId == "blocklab_friend_exe:ai_package") {
		const stage = entity.getProperty("blocklab_friend_exe:stage");

		if (stage > 0 && stage < 4) entity.setProperty("blocklab_friend_exe:stage", 0);
	}
});

// Package Interact Logic
mc.world.afterEvents.entityHitEntity.subscribe((event) => {
	const { damagingEntity, hitEntity } = event;
	if (damagingEntity instanceof mc.Player && hitEntity.typeId == "blocklab_friend_exe:ai_package") {

		const stage = hitEntity.getProperty("blocklab_friend_exe:stage");
		if (stage == 0) {
			if (friend_exe || friend_exe?.isValid) {
				hitEntity.runCommand("particle blocklab_friend_exe:dust");
				damagingEntity.dimension.spawnItem(new mc.ItemStack('blocklab_friend_exe:ai_package_spawn_egg', 1), hitEntity.location);
				hitEntity.remove();
				damagingEntity.sendMessage({
					rawtext: [
						{ translate: "text.blocklab_friend_exe:ai_friend.exist" }
					]
				});
			} else {
				hitEntity.setProperty("blocklab_friend_exe:stage", 1);
				startIntroSequence();
				hitEntity.runCommand(`time set 0`);
				mc.system.runTimeout(() => {
					hitEntity.runCommand("playsound mob.ai-friend.voiceover @a ~ ~1 ~");
					hitEntity.runCommand("effect @a resistance 80 10 true");

					mc.system.runTimeout(() => {
						hitEntity.setProperty("blocklab_friend_exe:stage", 2);
						mc.system.runTimeout(() => { hitEntity.runCommand("particle blocklab_friend_exe:confetti"); }, 5);
					}, 1110);
					mc.system.runTimeout(() => {
						for (const online of mc.world.getAllPlayers()) {
							giveItem(online, "blocklab_friend_exe:wrench", true);
							online.runCommand("playsound note.bit @s ~ ~ ~")
						}
					}, 740);
					mc.system.runTimeout(() => {
						hitEntity.setProperty("blocklab_friend_exe:stage", 3);
					}, 1300);
					mc.system.runTimeout(() => {
						hitEntity.setProperty("blocklab_friend_exe:stage", 4);
						hitEntity.runCommand(`summon blocklab_friend_exe:ai_friend ~ ~ ~ ${hitEntity.getRotation().y}`);
						hitEntity.runCommand("particle blocklab_friend_exe:dust");
					}, 1500);
					mc.system.runTimeout(() => {
						intro = false;
						mc.system.runTimeout(() => {
							PlayEmote("stage0_happy.1", 7, 4);
							PlayEmoteSound("happy");
							for (const online of mc.world.getAllPlayers()) {
								giveItem(online, "blocklab_friend_exe:emoticon", true);
								online.runCommand("playsound mob.ai-friend.happyemote_happy @s ~ ~1 ~");
								online.sendMessage({
									rawtext: [
										{
											translate: "text.blocklab_friend_exe:intro.text.0", with:
											{
												rawtext: [
													{ text: String(online.nameTag) }
												]
											}
										}
									]
								});
							}
						}, 60);
					}, 1570);
				}, 10);
			}
		} else if (stage == 4) {
			hitEntity.runCommand("particle blocklab_friend_exe:dust");
			hitEntity.remove();
		}
	} else if (hitEntity instanceof mc.Player && damagingEntity.typeId == "blocklab_friend_exe:ai_friend") {
		const health = hitEntity.getComponent(mc.EntityComponentTypes.Health);
		const stage = Stage(damagingEntity);
		if (stage < 2) return;
		if (health.currentValue <= 3 && health.currentValue > 0) {
			JumpscareDeath(hitEntity, damagingEntity, health);
		}
	}
});

function JumpscareDeath(player, friend_exe, health) {
	friend_exe.triggerEvent("blocklab_friend_exe:jumpscare");
	friend_exe.runCommand(`effect @s slowness 4 7 true`);
	player.runCommand("inputpermission set @s dismount disabled");
	player.runCommand("inputpermission set @s jump disabled");
	player.runCommand("ride @s start_riding @e[type=blocklab_friend_exe:ai_friend]");
	mc.system.runTimeout(() => {
		player.applyDamage(60, { cause: mc.EntityDamageCause.entityAttack, damagingEntity: friend_exe });
		player.runCommand("inputpermission set @s dismount enabled");
		player.runCommand("inputpermission set @s jump enabled");
		friend_exe.triggerEvent("blocklab_friend_exe:resetjumpscare");
	}, 40);
}


//Cake place
mc.world.afterEvents.playerPlaceBlock.subscribe((event) => {
	const { player, block } = event;//starte das cake event automatisch wenn genug zeit ist!
	if (block.typeId != "minecraft:cake") return;

	if (friend_exe) {
		if (eventmanager.isEventActive(4)) {//plaziert wähend es schon existiert
			eventmanager.tickingEvent.placeCake(event, player, friend_exe);
		} else if ((!eventmanager.tickingEvent) && eventmanager.nextEventTime <= 270 && friend_exeHasTarget == false) {//schauen ob kein target oder event
			eventmanager.startEvent(eventmanager.getCake(), friend_exe, true, true);
			if (eventmanager.tickingEvent)
				eventmanager.tickingEvent.placeCake(event, player, friend_exe);
			//starte event und plaeCake ausführen
		}

	}

});

mc.world.afterEvents.entityDie.subscribe((event) => {
	const { deadEntity, damageSource } = event;
	if (damageSource.cause != mc.EntityDamageCause.entityAttack) return;
	if (damageSource.damagingEntity.typeId == "blocklab_friend_exe:ai_friend") {

		if (deadEntity instanceof mc.Player) {
			deadEntity.runCommand("particle blocklab_friend_exe:confetti");
			giveFoodToPlayerCount = 30


			deadEntity.runCommand("playsound mob.ai-friend.atmosphere.impact.long_impact");


			//hier dann logik transformation zurück
			mc.system.runTimeout(() => {
				const stage = Stage(friend_exe);
				if (stage == 2) {
					if (mc.world.getPlayers().length > 1) {
						if (randInt(0, 10) > 3) setStage(friend_exe, 1);
					} else {
						setStage(friend_exe, 1);
					}
				}
			}, 60);

		}
	}
});

//Cake remove
mc.world.afterEvents.playerBreakBlock.subscribe((event) => {
	const { player, block } = event;

	if (block.typeId != "minecraft:cake") return;
	const entities = block.dimension.getEntitiesAtBlockLocation(block.location);
	entities.forEach((entity) => {
		if (entity.typeId == "blocklab_friend_exe:ai_friend_target") entity.kill();
	});
});

let firstTimePlaceBed = false;

mc.world.afterEvents.playerPlaceBlock.subscribe((event) => {
	const { block, player } = event;

	if (friend_exe) {
		const stage = Stage(friend_exe);
		//DEBUG BED
		if (stage <= 2) {
			if (block.typeId == "minecraft:bed" && (!player.getSpawnPoint()) && firstTimePlaceBed == false) {
				firstTimePlaceBed = true;
				if (randInt(0, 99) > 30) {
					mc.system.runTimeout(() => {
						player.runCommand("playsound mob.ai-friend.atmosphere.impact.unsettling_impact @s ~ ~ ~");
					}, 140);
				}
				mc.system.runTimeout(() => {
					if (Stage(friend_exe) > 0) return;
					PlayEmoteWithSound("stage0_happy.2", 3, 2);
					player.sendMessage({
						rawtext: [
							{
								translate: `text.blocklab_friend_exe:bed.${randInt(0, 3)}`, with: { rawtext: [{ text: String(player.name) }] }
							}
						]
					});
				}, 60);
			}
		}
	}

})

let joinPlayer = false;
// Player Join Event
mc.world.afterEvents.playerSpawn.subscribe((event) => {
	const { player } = event;
	if (eventmanager.isEventActive(6)) {
		player.addEffect("darkness", 20 * 30);
	} else {
		player.removeEffect("darkness");
	}
	if (friend_exe) friend_exe.setProperty("blocklab_friend_exe:dance", 0);
	if (friend_exe) {
		UpdateAtmospherePlayer(player, Stage(friend_exe));
	} else {
		UpdateAtmospherePlayer(player, 0);
	}
	player.runCommand("inputpermission set @s dismount enabled");
	player.runCommand("inputpermission set @s jump enabled");

	if (!player.hasTag("blocklab_friend_exe:join")) {
		Friend_Exe();
		player.runCommand("function blocklab/friend_exe/setup_world");
		giveItem(player, "blocklab_friend_exe:manuals", true);
		if (friend_exe) {
			giveItem(player, "blocklab_friend_exe:emoticon", true);
		} else {
			giveItem(player, "blocklab_friend_exe:ai_package_spawn_egg", true);
		}
		player.addTag("blocklab_friend_exe:join");
	} else {
		if (joinPlayer == false) {
			mc.system.runTimeout(() => {
				if (friend_exe) {
					const stage = Stage(friend_exe);
					const mood = MoodStage(Mood(friend_exe));
					if (stage == 0) {
						player.sendMessage({
							rawtext: [
								{
									translate: `text.blocklab_friend_exe:join.stage0.${randInt(0, 2)}`, with: { rawtext: [{ text: String(player.name) }] }
								}
							]
						});
						ChangeMoodStageSound(friend_exe, mood, mood + 1);
					} else {
						player.sendMessage({
							rawtext: [
								{
									translate: `text.blocklab_friend_exe:join.stage1.${randInt(0, 2)}`, with: { rawtext: [{ text: String(player.name) }] }
								}
							]
						});
						randomAmbientSound();
					}
				}
			}, 150);
			joinPlayer = true;
		}
	}

	mc.system.runTimeout(() => {
		if (friend_exe) {
			const moodStage = MoodStage(Mood(friend_exe));
			const tickEvent = eventmanager.tickingEvent ? 1 : 0;
			player.runCommand(`scriptevent blocklab_friend_exe:playerdata t.emoticon_face = ${5 - moodStage};t.objective = ${tickEvent}`);
		}
	}, 60);

});

let friend_exe_hurt_count = 0;
let glitchHurtEffectCount = 2;
export function Hurtfriend_exe(hurtByPlayer = false) {
	const stage = Stage(friend_exe);
	const day = mc.world.getDay();
	friend_exe_hurt_count++;
	if (randomEmoteCounter <= 4) randomEmoteCounter += 6;

	if (stage == 0) {
		friend_exe.runCommand("playsound mob.ai-friend.hurt @a ~ ~ ~ 1");
	} else {
		friend_exe.runCommand("playsound mob.ai-friend.horror_hurt @a ~ ~ ~ 1");
		if (hurtByPlayer) {
			setLives(friend_exe, Lives(friend_exe) - 1);
			const lives = Lives(friend_exe);
			if (stage == 2) {
				if (lives == 4) PlayEmote("stage2_roar", 8, 2);

				if (lives == 3) friend_exe.runCommand("playsound mob.ai-friend.atmosphere.intervall.agressive2 @a ~ ~ ~ 1");
			}
		}
	}
	if (stage == 1) {
		glitchHurtEffectCount--;
		if (glitchHurtEffectCount <= 0) {
			randomGlitchJumpscareStage1(false);
			glitchHurtEffectCount = randInt(1, 3);
		}
	}

	if (stage == 2) {
		glitchHurtEffectCount--;
		if (glitchHurtEffectCount <= 0) {
			if (randInt(0, 10) < 5) {
				PlayEmote("stage1_headtwitch.2", 4);
			} else {
				PlayEmote("stage1_headtwitch.1", 4);
			}
			glitchHurtEffectCount = randInt(4, 7);
		}
	}

	if (day == 0 && friend_exe_hurt_count == 6) {
		mc.system.runTimeout(() => {
			feedBackSound();
			mc.world.sendMessage({
				rawtext: [
					{ translate: `text.blocklab_friend_exe:hurt.iron` }
				]
			});
		}, 60);
	}

	if (Lives(friend_exe) <= 0) {
		setLives(friend_exe, randInt(10, 20));
		if (stage == 1) {
			setStage(friend_exe, 2);
		} else if (stage == 2) {
			if (day <= 1) {
				setStage(friend_exe, 1);
			} else {
				setStage(friend_exe, 3);
			}
		} else if (stage == 3) {
			friend_exe.runCommand("effect @s resistance 10 10 true");
			setStage(friend_exe, 0);
			friend_exe.dimension.spawnItem(new mc.ItemStack("minecraft:iron_nugget", 7), friend_exe.location);
			friend_exe.dimension.spawnItem(new mc.ItemStack("minecraft:iron_ingot", 2), friend_exe.location);
			friend_exe.dimension.spawnItem(new mc.ItemStack("minecraft:nether_star", 1), friend_exe.location);
			for (let i = 0; i < 100; i++) {
				friend_exe.runCommand(`summon xp_orb ~ ~ ~`);
			}

		}
	}
}

let lookAtCount = 3;
function resetLookAtCount(stage) {
	if (stage == 0) {
		lookAtCount = randInt(3, 5);
	} else {
		lookAtCount = randInt(1, 3);
	}
}


let nearTicks = 0;
let awayTicks = 0;
function handlePlayerProximityCheck(isWithinRange) {
	const stage = Stage(friend_exe);
	const day = mc.world.getDay();
	const night = isNighttime();
	const mood = Mood(friend_exe);
	if (isWithinRange) {
		if (awayTicks > 6 && stage == 0) {
			if (day > 0) {
				setStage(friend_exe, 1);
				feedBackSound();
				mc.world.sendMessage({
					rawtext: [
						{ translate: `text.blocklab_friend_exe:alone.1` }
					]
				});
			}
		}
		nearTicks++;
		awayTicks = 0;
	} else {
		awayTicks++;
		nearTicks = 0;
	}
	if (stage == 1) {
		if ([2, 3, 4, 5, 7, 8, 9, 10].includes(awayTicks)) friend_exe.runCommand(`execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive1 @s ~ ~ ~ 1 1`);
		if (awayTicks == 2) {
			//friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive2_3 @s ~ ~ ~ 1");
			feedBackSound();
			mc.world.sendMessage({
				rawtext: [
					{ translate: `text.blocklab_friend_exe:away.stage1.${randInt(0, 3)}` }
				]
			});
		}
		if (awayTicks == 4) {
			if (day > 1)
				setStage(friend_exe, 2);
		}
		if (nearTicks == 1) {
			//oh wieder da
			friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive2 @s ~ ~ ~ 1");
		}
	}

	if (stage == 0) {
		if (nearTicks == 2) {
			setMood(friend_exe, mood + randInt(2, 8));
			friend_exe.runCommand(`particle minecraft:heart_particle ~ ~2.2 ~`);
		}
		if (awayTicks == 2 && randInt(0, 10) > 3) {
			feedBackSound();
			mc.world.sendMessage({
				rawtext: [
					{ translate: `text.blocklab_friend_exe:away.${randInt(0, 3)}` }
				]
			});
		}
		if (awayTicks == 9) {//lange weg... ohoh
			let loc = friend_exe.location;
			feedBackSound();
			tryTeleportBehindPlayer(closestPlayer, friend_exe);
		}
		if (awayTicks > 0) {
			setMood(friend_exe, mood - (day == 0 ? randInt(0, 2) : randInt(1, 3)), true);
		}
	}
}

// Custom Script Events
mc.system.afterEvents.scriptEventReceive.subscribe((event) => {
	const friend_exe = Friend_Exe();
	const day = mc.world.getDay();
	const firstDay = isFirstDay();
	if (!friend_exe) return;
	if (event.id === "blocklab_friend_exe:hurt_by_mob") {
		let currentMood = friend_exe.getProperty("blocklab_friend_exe:mood");
		friend_exe.setProperty("blocklab_friend_exe:damage", true);
		if (randInt(0, 10) > 3) setMood(friend_exe, currentMood - (firstDay ? 1 : randInt(1, 2)));
		Hurtfriend_exe();
	}
	if (event.id === "blocklab_friend_exe:player_wait") {
		handlePlayerProximityCheck(event.message == "1" ? true : false);
	}
	if (event.id === "blocklab_friend_exe:emote") {
		PlayEmote(event.message, 4);
	}
	if (event.id === "blocklab_friend_exe:reset_to_first_day") {
		mc.world.sendMessage("§eThe AI Friend world was successfully reset to Day 0.");
		eventmanager.stopEvent(friend_exe);
		eventmanager.resetTimer();
		friend_exe = null;
		friend_exeId = null;
	}
	if (event.id === "blocklab_friend_exe:playerdata") {
		let random_1 = Math.random() * 1e3 << 0
		let random_2 = Math.random() * 1e3 << 0
		let random_3 = Math.random() * 1e3 << 0
		event.sourceEntity.playAnimation("animation.blocklab_friend_exe.sny_bc.data_transfer.empty",
			{
				stopExpression: `t.player_name='${event.sourceEntity.name}';v.__random_1__=${random_1};v.__random_2__=${random_2};v.__random_3__=${random_3};${event.message};return 0;`, // the magic
			}
		)
	}
	if (event.id === "blocklab_friend_exe:hurt_by_player") {
		if (eventmanager.isEventActive(6)) {
			friend_exe.setProperty("blocklab_friend_exe:darkness_hurt_by_player", true);
		} else {
			let currentMood = friend_exe.getProperty("blocklab_friend_exe:mood");
			let moodReduction = firstDay ? 1 : randInt(3, 6);
			friend_exe.setProperty("blocklab_friend_exe:damage", true);
			setMood(friend_exe, currentMood - moodReduction);
		}
		Hurtfriend_exe(true);
	}
	if (event.id === "blocklab_friend_exe:findevent") {
		eventmanager.FindNextEvent();
	}
	if (event.id === "blocklab_friend_exe:resettimer") {
		eventmanager.resetTimer("test");
	}
	if (event.id === "blocklab_friend_exe:event") {
		const id = parseInt(event.message);
		if (!isNaN(id)) {
			const nextEvent = eventmanager.getEvemtFromID(id);
			eventmanager.startEvent(nextEvent, friend_exe, true);
		}
	}
	if (event.id === "blocklab_friend_exe:target_acquired") {
		mc.system.runTimeout(() => {
			if (randInt(0, 99) < 40) {
				event.sourceEntity.runCommand("playsound mob.ai-friend.atmosphere.impact.anticipate_impact");
			} else {
				event.sourceEntity.runCommand("playsound mob.ai-friend.atmosphere.impact.creepy_impact");
			}
		}, 10);
		if (randInt(0, 99) > 20 && Stage(friend_exe) == 2) {
			friend_exe.triggerEvent("blocklab_friend_exe:stage2_run");
			PlayEmote("stage2_roar", 8, 2);
			friend_exe.runCommand("camerashake add @a");
		}
	}
	if (event.id === "blocklab_friend_exe:kill_entity") {
		if (randInt(0, 99) < 70) return
		if (randInt(0, 1) == 0) {
			PlayEmote("idleemote1", 4);
		} else {
			PlayEmote("idleemote2", 4, 3);
		}
	}
	if (event.id === "blocklab_friend_exe:lookat") {
		const entity = event.sourceEntity;
		const moodStage = MoodStage(Mood(friend_exe));
		const stage = Stage(friend_exe);
		const night = isNighttime();
		lookAtCount--;
		if (lookAtCount == 1) {
			if (stage == 1)
				friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive3 @s ~ ~ ~ 1");
		}
		if (lookAtCount <= 0) {
			resetLookAtCount(stage);
			if (stage == 0) {
				const random = randInt(0, 99);
				if (!night) {
					if (moodStage >= 4) {
						friend_exe.runCommand(`particle minecraft:heart_particle ~ ~2.2 ~`);
						if (random < 20) {
							PlayEmote("stage0_headspin", 4);
						}
					} else {
						if (random > 40) {
							PlayEmote("stage0_headspin", 4);
						} else {
							if (randInt(0, 99) < 30)
								PlayEmote("stage0_headshiver", 4);
						}
					}
				} else if (night && moodStage < 4) {

					if (randInt(0, 10) < 3) {
						mc.world.getPlayers().forEach(player => {
							player.addEffect("darkness", 20);
						});
					}

					if (random < 20) {
						PlayEmote("stage0_face_emote1", 4);
					} else {
						PlayEmote("stage0_headshiver", 4);
					}
				}
			} else if (stage == 1) {
				friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive3 @s ~ ~ ~ 1");
				const random = randInt(0, 99);
				if (random < 82) {
					randomLookatSound();
				}
				if (randInt(0, 10) < 5) {
					PlayEmote("stage1_wave", 8);
				} else {
					PlayEmote("stage1_grimasse", 3);
				}
			} else if (stage == 2) {
				if (randInt(0, 99) < 40) PlayEmote("stage2_roar", 8, 2);
			}
		}
	}
	if (event.id === "blocklab_friend_exe:heartbeat") {
		const pitch = randFloat(0.8, 1.7);
		friend_exe.runCommand(`playsound mob.ai-friend.agressive_heartbeat @a ~ ~ ~ 1 ${pitch}`);
		mc.system.runTimeout(() => {
			friend_exe.runCommand(`playsound mob.ai-friend.agressive_heartbeat @a ~ ~ ~ 1 ${pitch}`);
		}, 40);

	}
	if (event.id === "blocklab_friend_exe:has_target") {
		friend_exeHasTarget = true;
		friend_noTargetTime = 0;
		if (Stage(friend_exe) == 0) friend_exe.runCommand(`playsound note.bit @a ~ ~ ~ ${randFloat(0.4, 1.0)} ${randFloat(0.65, 0.76)}`);
	}
	if (event.id === "blocklab_friend_exe:eat") {
		let currentMood = friend_exe.getProperty("blocklab_friend_exe:mood");
		setMood(friend_exe, currentMood + randInt(2, 4));
		//hier zu not replace item air.
	}
	if (event.id === "blocklab_friend_exe:no_target") {
		friend_exeHasTarget = false;
	}
	if (event.id === "blocklab_friend_exe:interact") {
		interact(event.sourceEntity, friend_exe);
	}
	if (event.id === "blocklab_friend_exe:mood") {
		const mood = parseInt(event.message);
		if (!isNaN(mood)) setMood(friend_exe, mood);
	}
	if (event.id === "blocklab_friend_exe:stage") {
		const stage = parseInt(event.message);
		if (!isNaN(stage)) {
			setStage(friend_exe, stage);
		}
	}
	if (event.id === "blocklab_friend_exe:texture") {
		const texture = parseInt(event.message);
		if (!isNaN(texture)) friend_exe.setProperty("blocklab_friend_exe:texture", texture);
	}
	if (event.id === "blocklab_friend_exe:randomevent") {//start random event
		const randomEvent = eventmanager.getRandomEventFromType(eventmanager.EVENT_TYPE.CUTE);
		mc.world.sendMessage(`event: ${randomEvent.eventID} chance ${randomEvent.chance * 100}%`);

	}
	if (event.id == "blocklab_friend_exe:rotate_to_player") {
		rotate_to_player(friend_exe, mc.world.getPlayers()[0]);
	}
	if (event.id === "blocklab_friend_exe:exec_special_attack") {
		exec_special_attack(friend_exe);
	}
	if (event.id === "blocklab_friend_exe:special_attack_damage") {
		special_attack_damage(friend_exe);
	}
	if (event.id === "blocklab_friend_exe:special_attack_deactivate") {
		special_attack_deactivate(friend_exe);
	}
	if (event.id === "blocklab_friend_exe:stage1TrafoExec") {
		setStage(friend_exe, 1);
	}

});

//Player Functions

export function rewardPlayer(array) {
	const reward = array[Math.floor(Math.random() * array.length)];
	if (reward.id && reward.amount > 0) {
		const item = new mc.ItemStack(reward.id, reward.amount);
		friend_exe.dimension.spawnItem(item, friend_exe.location);
	}
	if (reward.xp > 0) {
		for (let i = 0; i < reward.xp; i++) {
			friend_exe.runCommand(`summon xp_orb ~ ~ ~`);
		}
	}
}

export function reward(xpamount = 0) {
	if (xpamount > 0) {
		for (let i = 0; i < xpamount; i++) {
			friend_exe.runCommand(`summon xp_orb ~ ~ ~`);
		}
	}
}

export function removeItem(player, item, removeAmount) {
	if (removeAmount <= 0) return;

	if (item.amount <= removeAmount) {
		player.getComponent(mc.EntityComponentTypes.Equippable).setEquipment(mc.EquipmentSlot.Mainhand, undefined);
	} else {
		item.amount -= removeAmount;
		player.getComponent(mc.EntityComponentTypes.Equippable).setEquipment(mc.EquipmentSlot.Mainhand, item);
	}
}
const RAW_TO_COOKED_ITEMS = [
	{ raw: "minecraft:chicken", cooked: "minecraft:cooked_chicken" },
	{ raw: "minecraft:porkchop", cooked: "minecraft:cooked_porkchop" },
	{ raw: "minecraft:beef", cooked: "minecraft:cooked_beef" },
	{ raw: "minecraft:mutton", cooked: "minecraft:cooked_mutton" }
]

let cookeCooldown = 20;

function getCookedVersion(itemTypeId) {
	for (const entry of RAW_TO_COOKED_ITEMS) {
		if (entry.raw === itemTypeId) {
			return entry.cooked;
		}
	}
	return null;
}

let ironCooldown = 0;

function interact(player, friend_exe) {
	const item = player.getComponent(mc.EntityComponentTypes.Equippable).getEquipment(mc.EquipmentSlot.Mainhand);
	if (!item) return;

	const stage = Stage(friend_exe);

	const mood = Mood(friend_exe);
	const moodStage = MoodStage(mood);
	if (eventmanager.isEventActive(2) && (item.typeId == 'minecraft:apple')) {
		friend_exe.runCommand("replaceitem entity @s slot.weapon.mainhand 1 apple");
		eventmanager.tickingEvent.giveApple(friend_exe);
		removeItem(player, item, 1);
	} else if (eventmanager.isEventActive(5)) {
		const giveItem = eventmanager.tickingEvent.giveItem(player, friend_exe, item);
	} else if ((item.typeId == 'minecraft:iron_nugget' || item.typeId == 'minecraft:iron_ingot')) {
		if (stage == 0 && !eventmanager.tickingEvent && waitEmote <= 0) {
			if (ironCooldown <= 0) {
				const nugget = item.typeId == 'minecraft:iron_nugget';
				friend_exe.runCommand(`replaceitem entity @s slot.weapon.mainhand 1 ${item.typeId}`);
				mc.system.runTimeout(() => {
					friend_exe.runCommand("playsound random.eat @a ~ ~ ~");
					friend_exe.runCommand("replaceitem entity @s slot.weapon.mainhand 1 air");
				}, 40);
				removeItem(player, item, 1);
				setMood(friend_exe, mood + (nugget ? (randInt(0, 10) > 5 ? 1 : 0) : randInt(1, 3)));
				PlayEmote("stage0_happy.2", 3, 2);
				if (randInt(0, 10) < 3) PlayEmoteSound("happy");
				if (nugget) {
					ironCooldown = 2;
				} else {
					ironCooldown = 5;
				}
			} else {
				player.runCommand("playsound random.break @s ~ ~ ~ 1 2");
			}
		} else {
			player.runCommand("playsound random.break @s ~ ~ ~ 1 2");
		}
	} else if (stage == 0) {
		const cookedVersion = getCookedVersion(item.typeId);
		if (cookedVersion && !eventmanager.tickingEvent && waitEmote <= 0) {
			if (cookeCooldown <= 0) {
				const amount = item.amount;
				removeItem(player, item, amount);
				PlayEmote("microwave", 10, 10);

				mc.system.runTimeout(() => {
					const item = new mc.ItemStack(cookedVersion, amount);
					friend_exe.dimension.spawnItem(item, friend_exe.location);
				}, 132);
				cookeCooldown = 20 * 5;
			}
			//Microwave
		}
	}
	if (item.typeId == 'blocklab_friend_exe:wrench') {
		if (eventmanager.isEventActive(1)) {
			if (repairWithWrench(player, item)) {
				eventmanager.tickingEvent.repair(player, friend_exe, mood, moodStage);
				friend_exe.runCommand("particle blocklab_friend_exe:repair");
			}
		} else if (stage == 1) {
			if (repairWithWrench(player, item, 78)) {
				stage1repairAmount--;
				stage1repairTransformAmount--;
				if (randInt(0, 10) < 6) randomAmbientSound();
				if (randInt(0, 20) < 5) randomGlitchJumpscareStage1();
				if (stage1repairTransformAmount <= 0) {
					//OH OH SOUND
					setStage(friend_exe, 2);
				} else if (stage1repairAmount <= 0) {
					setStage(friend_exe, 0);
				}
				friend_exe.runCommand("particle blocklab_friend_exe:repair");
				if (randInt(0, 10) < 5) {
					PlayEmote("stage1_headtwitch.2", 4);
				} else {
					PlayEmote("stage1_headtwitch.1", 4);
				}

			}
		} else {
			if (stage == 0) {
				// gay
				play.sendMessage({
					rawtext: [
						{ translate: `text.blocklab_friend_exe:no_repair.${randInt(0, 5)}` }
					]
				});

				friend_exe.runCommand(`playsound note.bass @a ~ ~ ~ 1 0.4`);
			} else {
				friend_exe.runCommand(`playsound mob.ai-friend.alarm_malfunction @a ~ ~ ~`);
			}
		}
	}
}

let stage1repairAmount = randInt(3, 5);
let stage1repairTransformAmount = randInt(5, 20);

function repairWithWrench(player, item, amount = 60) {
	const cooldown = item.getComponent(mc.ItemComponentTypes.Cooldown);
	const currentCooldown = player.getItemCooldown(cooldown.cooldownCategory);

	if (currentCooldown == 0) {
		player.startItemCooldown(cooldown.cooldownCategory, 30);
		if (player.getGameMode() != mc.GameMode.creative) {
			const durability = item.getComponent(mc.ItemComponentTypes.Durability);
			item.getComponent(mc.ItemComponentTypes.Durability).damage = Math.min(durability.damage + amount, durability.maxDurability);
			const remove = durability.damage + amount >= durability.maxDurability;
			player.getComponent(mc.EntityComponentTypes.Equippable).setEquipment(mc.EquipmentSlot.Mainhand, (remove ? undefined : item));
			if (remove) player.runCommand("playsound random.break");
		}
		//ERSETZEN
		player.runCommand(`scriptevent blocklab_friend_exe:playerdata t.repair = 1`);
		//player.setProperty("blocklab_friend_exe:repair", true);
		mc.system.runTimeout(() => { if (player?.isValid) player.runCommand(`scriptevent blocklab_friend_exe:playerdata t.repair = 0`); }, 10);
	}
	return currentCooldown == 0;
}


let randomEmoteCounter = randInt(10, 30);

export function PlayMoodStageEmote(moodStage) {
	let animationString;
	if (moodStage == 5 || moodStage == 4) {
		animationString = "stage0_happy.1";
	} else if (moodStage == 3) {
		animationString = "stage0_sad.2"
	} else if (moodStage == 2) {
		animationString = "stage0_sad.1"
	}
	randomEmoteCounter = randInt(20, 40);

	if (animationString) {
		PlayEmote(animationString, 7, 4);
	}
}


let checkWaitDuration = 0;

function isAir(location) {
	let block = friend_exe.dimension.getBlock(location);
	return block?.typeId == "minecraft:air";
}

function checkBlock(friend_exe, stage) {
	let location = friend_exe.location;

	//auch hier abfragen ob er eingebaut wurde :C
	//first check inside
	const inside1 = !isAir({ x: location.x, y: location.y + 0.2, z: location.z });
	const inside2 = !isAir({ x: location.x, y: location.y + 1.2, z: location.z });
	if (inside1 && inside2) {
		//mc.world.sendMessage("Ai Friend wurde eingebaut das mag er nicht :C");
	}
	return;

	if (checkWaitDuration > 0) checkWaitDuration--;

	if (checkWaitDuration > 0) return;

	if (stage == 1) {//from stage 1 to 2 logic
		//first scenario
		let firstlocation = {
			x: location.x + friend_exe.getViewDirection().x * 1.4,
			y: location.y,
			z: location.z + friend_exe.getViewDirection().z * 1.4
		};
		const above = !isAir({ x: firstlocation.x, y: firstlocation.y + 2.2, z: firstlocation.z });
		const front = isAir({ x: firstlocation.x, y: firstlocation.y + 1.5, z: firstlocation.z });

		if (above && front) {
			checkWaitDuration = 7;
			setStage(friend_exe, 2);

			mc.world.sendMessage("first scenario: von stage 1 zu 2 weil zu wenig platz");
			//zu stage 2 transform
		} else {
			//second scenario
			let seclocation = {
				x: location.x + friend_exe.getViewDirection().x * 1.4,
				y: location.y,
				z: location.z + friend_exe.getViewDirection().z * 1.4
			};
			const above = isAir({ x: seclocation.x, y: seclocation.y + 1.2, z: seclocation.z });
			const above2 = !isAir({ x: seclocation.x, y: seclocation.y + 3.2, z: seclocation.z });
			const above3 = !isAir({ x: location.x, y: location.y + 3.2, z: location.z });
			const front = !isAir({ x: seclocation.x, y: seclocation.y + 0.2, z: seclocation.z });

			if (front && above && above2 && above3) {
				checkWaitDuration = 7;
				setStage(friend_exe, 2);

				mc.world.sendMessage("second scenario: von stage 1 zu 2 weil zu wenig platz");
				//zu stage 2 transform
			}

		}
	} else if (stage == 2) {//das genau anders herum also erst für stage 2zu3 verwandlung abfragen und dann danach für 2zu1

		//first scenario stage 2 to 1 (when no aggressive)
		let firstlocation = {
			x: location.x,
			y: location.y,
			z: location.z
		};
		const above = isAir({ x: firstlocation.x, y: firstlocation.y + 2.2, z: firstlocation.z });
		const front = isAir({ x: firstlocation.x, y: firstlocation.y + 1.2, z: firstlocation.z });
		if (above && front) {// & aggressive later!
			//zu stage 2 zu 1 transform
			setStage(friend_exe, 1);
			checkWaitDuration = 7;
			mc.world.sendMessage("von stage 2 zu 1 weil genug platz");
		} else {
			//second scenario stage 2 to 3 two posible

			let seclocation = {
				x: location.x + friend_exe.getViewDirection().x * 1.4,
				y: location.y,
				z: location.z + friend_exe.getViewDirection().z * 1.4
			};
			const front = isAir({ x: seclocation.x, y: seclocation.y + 0.2, z: seclocation.z });
			const above = !isAir({ x: seclocation.x, y: seclocation.y + 1.2, z: seclocation.z });
			if (front && above) {//vor ihm ist air und darüber ist ein block
				//von stage 2 zu 3
				setStage(friend_exe, 3);
				checkWaitDuration = 7;
				mc.world.sendMessage("first scenario: stage 2 zu 3 wäre möglich!");
			} else if (!(front && above)) {//genau invertiert
				//von stage 2 zu 3
				setStage(friend_exe, 3);
				checkWaitDuration = 7;
				mc.world.sendMessage("sec scenario: stage 2 zu 3 wäre möglich!");
			}

		}
	} else if (stage == 3) {
		let firstlocation = {
			x: location.x,
			y: location.y,
			z: location.z
		};
		const above = isAir({ x: firstlocation.x, y: firstlocation.y + 1.2, z: firstlocation.z });
		const front = isAir({ x: firstlocation.x, y: firstlocation.y + 0.2, z: firstlocation.z });
		if (above && front) {// & aggressive later!
			//zu stage 3 zu 2 transform
			setStage(friend_exe, 2);
			mc.world.sendMessage("stage 3 zu 2 wäre möglich!");
			checkWaitDuration = 7;
		}

	}
}

// AI Friend Events/Mood & Display Loop
let lastMoodStage = 5;
let searchMessageCount = randInt(4, 10);
let randomLoseMoodCount = randInt(40, 80);
let randomAmbientSoundCount = randInt(20, 30);
let particleStage1Count = 0;
mc.system.runInterval(() => {
	Friend_Exe();
	if (!friend_exe) return (lastMoodStage = 5);
	/*
	if (!friend_exe.isValid()) {
		mc.system.runTimeout(() => {
			mc.world.getPlayers()[0].runCommand("summon blocklab_friend_exe:ai_friend ~ ~ ~");
		}, 10);
		//friend_exe = null;
		return;
	}
	*/
	if (!friend_exe.isValid()) {
		friend_exe = null;
		return
	}
	const stage = Stage(friend_exe);
	const mood = Mood(friend_exe);
	const moodStage = MoodStage(mood);

	if (friend_exe.getProperty("blocklab_friend_exe:damage")) friend_exe.setProperty("blocklab_friend_exe:damage", false);

	if (lastMoodStage != moodStage) friend_exeUpdateMoodStage(friend_exe, moodStage);
	lastMoodStage = moodStage;

	//Emote stuff
	if (waitEmote > 0) waitEmote--;
	if (randomEmoteCounter > 0) randomEmoteCounter--;
	if (randomEmoteCounter <= 0) {
		if (stage != 0) {
			randomEmoteCounter += 30;
		} else if (friend_exeHasTarget || eventmanager.tickingEvent) {
			randomEmoteCounter += 4;
		} else {
			PlayMoodStageEmote(moodStage);
		}
	}

	//Display Stuff
	const day = mc.world.getDay();
	const night = isNighttime();

	mc.world.getPlayers().forEach((player) => {
		const item = player.getComponent(mc.EntityComponentTypes.Equippable).getEquipment(mc.EquipmentSlot.Mainhand);
		if (item) {
			if (item.typeId == 'blocklab_friend_exe:emoticon') {
				if (!intro) player.onScreenDisplay.setActionBar(`Day ${day}`);
			}
		}
	});
	randomAmbientSoundCount--;
	if (randomAmbientSoundCount <= 0) {
		randomAmbientSoundCount = randInt(8, 30);
		if (stage == 0) {
			if (night) {
				randomAmbientSound();
			}
			randomAmbientSoundCount = randInt(43, 60);
		} else {
			randomAmbientSound();
		}
	}
	if (day > 0 && stage == 0) {
		if (!(friend_exeHasTarget || eventmanager.tickingEvent)) {
			randomLoseMoodCount--;
			if (randomLoseMoodCount <= 0) {
				randomLoseMoodCount = randInt(30, 40);
				setMood(friend_exe, mood - randInt(1, 2));
			}
		}
	}

	if (stage == 0) {
		UpdateCrouch(moodStage);
		Stage0GiveFoodPlayer(moodStage);
		cookeCooldown--;
		ironCooldown--;

		//owner wechseln
		switchOwnerCount--;
		if (startWait < 0) {
			if (friend_exeHasTarget) {
				switchOwnerCount = 3;
			} else if (eventmanager.tickingEvent) {
				switchOwnerCount = 10;
			} else if (mood < 70) {
				switchOwnerCount = 10;
			} else if (switchOwnerCount <= 0) {
				switchOwnerCount = 30;
				friend_exe.getComponent('tameable').tame(closestPlayer);
			}
		}
	}

	//random stage1 to stage 2 transform
	if (stage == 1) {
		Stage1RandomTransformUpdate();
		headTurn(friend_exe);//neigen
		particleStage1Count++;
		if (particleStage1Count >= 3) {
			particleStage1Count = 0;
			friend_exe.runCommand(`particle blocklab_friend_exe:aura`);
		}
	}
	if (stage == 3) {
		if (randInt(0, 99) > 40)
			friend_exe.runCommand(`particle blocklab_friend_exe:sparkle ~ ~-1.32 ~`);
	}
	//random animation
	if (stage == 1 && randInt(0, 100) < 3) {
		randomGlitchJumpscareStage1();
	}
	if (stage == 2 || stage == 3) {
		if (friend_exeHasTarget) {
			searchMessageCount = randInt(30, 60);
		} else {
			searchMessageCount--;
			if (searchMessageCount <= 0) {
				feedBackSound("agg");
				mc.world.sendMessage({
					rawtext: [
						{ translate: `text.blocklab_friend_exe:no_target.${randInt(0, 7)}` }
					]
				});
				searchMessageCount = randInt(60, 130);
			}
		}
	}
	if (stage == 2) {
		if (!friend_exeHasTarget) friend_noTargetTime++;

		if (friend_noTargetTime == 3)
			friend_exe.runCommand(`execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive1 @s ~ ~ ~ 1 1`);
		if (friend_noTargetTime == 60 || friend_noTargetTime == 30) {
			PlayEmote("stage2_roar", 8, 2);
		}
		if (friend_noTargetTime == 68) {
			setStage(friend_exe, 1);
		}

		headTurn(friend_exe);
		if (friend_exeHasTarget) {
			if (randInt(0, 99) < 30) {
				friend_exe.triggerEvent("blocklab_friend_exe:stage2_run");
			}
		}
	}

	UpdateDayBehavior(day, night, stage, mood, moodStage);

	checkBlock(friend_exe, stage);

	eventmanager.update(friend_exe, stage, day, mood, moodStage);
}, 20);

let switchOwnerCount = 10;

function Crouch(bool = false) {
	friend_exe.setProperty("blocklab_friend_exe:crouch", bool);
}

let sneakCount = 3;
function UpdateCrouch(moodStage) {
	sneakCount--;
	const sneak = friend_exe.getProperty("blocklab_friend_exe:crouch");
	if (sneakCount == 0 && sneak) {
		Crouch(false)
		//hört auf zu sneaken!
	}
	if (eventmanager.tickingEvent) return;
	if (closestDistance <= 4 && moodStage > 2 && sneak == false && sneakCount <= 0) {
		if (closestPlayer && closestPlayer.isSneaking) {
			Crouch(true);
			friend_exe.runCommand("playsound mob.ai-friend.shortbuzz @a");
			mc.system.runTimeout(() => {
				Crouch(false);
				if (randInt(0, 10) > 3) {
					mc.system.runTimeout(() => {
						Crouch(true);
						friend_exe.runCommand("playsound mob.ai-friend.shortbuzz @a");
						mc.system.runTimeout(() => {
							Crouch(false);
						}, 4);
					}, 3);
				}
			}, 4);
			sneakCount = 5;
		}
	}
}

let giveFoodToPlayerCount = 20;
function Stage0GiveFoodPlayer(moodStage) {
	giveFoodToPlayerCount--;
	if (moodStage >= 3) {
		if (closestDistance <= 5 && giveFoodToPlayerCount <= 0) {
			const health = closestPlayer.getComponent(mc.EntityComponentTypes.Health);
			if (health.currentValue <= 6) {

				if (randInt(0, 10) > 3)
					closestPlayer.sendMessage({
						rawtext: [
							{
								translate: `text.blocklab_friend_exe:helpfood.${randInt(0, 3)}`, with: { rawtext: [{ text: String(closestPlayer.name) }] }
							}
						]
					});
				const cookie = new mc.ItemStack("minecraft:cookie", randInt(1, 4));
				cookie.nameTag = "AI Friend Cookie";
				friend_exe.dimension.spawnItem(cookie, friend_exe.location);
				feedBackSound();
				giveFoodToPlayerCount = randInt(50, 70);
			}

		}
	}
}

let randomHeadturn = randInt(20, 60);
function headTurn(friend_exe) {
	const headturn = friend_exe.getProperty("blocklab_friend_exe:headturn");
	randomHeadturn--;

	if (headturn && randomHeadturn == 0) {
		friend_exe.setProperty("blocklab_friend_exe:headturn", false);
		randomHeadturn = randInt(50, 90);
	} else if (randomHeadturn == 0) {
		friend_exe.setProperty("blocklab_friend_exe:headturn", true);
		randomHeadturn = randInt(8, 14);
	}

}

export function isSunrise() {
	const time = mc.world.getTimeOfDay();
	return time >= 22800 && time < 24000;
}

export function isSunset() {
	const time = mc.world.getTimeOfDay();
	return time >= 12500 && time < 13000;
}


export function isDark() {
	const time = mc.world.getTimeOfDay();
	return time >= 14300 && time < 22000;
}


let wasDark = isDark();
let wasSunrise = isSunrise();
let wasSunset = isSunset();
let wasNight = isNighttime();
function UpdateDayBehavior(day, night, stage, mood, moodStage) {
	const timeOfDay = mc.world.getTimeOfDay();
	const isCurrentlyDark = isDark();
	const isCurrentlySunrise = isSunrise();
	const isCurrentlySunset = isSunset();
	const isCurrentlyNight = isNighttime();


	if (!wasDark && isCurrentlyDark) {
		if (day != 0) {
			if (stage == 0) {
				if (randInt(0, 10) > 4 && moodStage < 4) setStage(friend_exe, 1);
			} else if (stage == 1) {
				if (randInt(0, 10) > 5) setStage(friend_exe, 2);
			}

		}
	}

	if (!wasSunset && isCurrentlySunset) {
		if (stage == 0) {
			if (day <= 2) {
				if (moodStage >= 4) {
					mc.world.sendMessage({
						rawtext: [
							{
								translate: `text.blocklab_friend_exe:sunset.help.${randInt(0, 3)}`
							}
						]
					});
				} else if (moodStage > 0) {
					PlayMoodStageEmote(moodStage);
					if (eventmanager.tickingEvent) return;
					mc.world.sendMessage({
						rawtext: [
							{
								translate: `text.blocklab_friend_exe:sunset.maybehelp.${randInt(0, 3)}`
							}
						]
					});
				}
			}
		}
	}

	if (wasDark && !isCurrentlyDark) { }

	if (wasNight && !isCurrentlyNight) {
		eventmanager.resetTimer("day");
		if (stage == 0) {
			feedBackSound();
			mc.world.sendMessage({
				rawtext: [
					{
						translate: `text.blocklab_friend_exe:goodmorning.${randInt(0, 3)}`
					}
				]
			});
		} else if (stage == 1) {
			if (stage == 3 && friend_exeHasTarget == false) {
				setStage(friend_exe, 2);
			} else if (stage == 1) {
				if (randInt(0, 10) > 2) setStage(friend_exe, 0);
			}
		}
		//durchgeschlafen?!
	}

	if (!wasNight && isCurrentlyNight) {
		eventmanager.resetTimer("night");
	}


	if (!wasSunrise && isCurrentlySunrise) {
		if (stage == 3 && friend_exeHasTarget == false) {
			setStage(friend_exe, 2);
		} else if (stage == 1) {
			if (randInt(0, 10) > 2) setStage(friend_exe, 0);
		}
	}

	// Zustand für nächsten Tick speichern
	wasDark = isCurrentlyDark;
	wasSunrise = isCurrentlySunrise;
	wasSunset = isCurrentlySunset;
	wasNight = isCurrentlyNight;

}


//Emotes
export let waitEmote = 0;

export function PlayEmoteSound(string) {
	let playsoundString;
	if (string == "happy") {
		playsoundString = "mob.ai-friend.happyemote_happy";
	} else if (string == "okay") {
		playsoundString = "mob.ai-friend.sademote_okay";
	} else if (string == "unhappy" || string == "sad") {
		playsoundString = "mob.ai-friend.sademote_unhappy";
	}
	if (playsoundString)
		friend_exe.runCommand(`playsound ${playsoundString} @a ~ ~1 ~`);
}

export function PlayEmoteWithSound(string, wait = 0, slowness = 0) {

	let playsoundString;
	if (string == "stage0_happy.1") {
		playsoundString = "mob.ai-friend.happyemote_happy";
	} else if (string == "stage0_sad.2") {
		playsoundString = "mob.ai-friend.sademote_okay";
	} else if (string == "stage0_sad.1") {
		playsoundString = "mob.ai-friend.sademote_unhappy";
	}

	if (playsoundString)
		friend_exe.runCommand(`playsound ${playsoundString} @a ~ ~1 ~`);


	PlayEmote(string, wait, slowness);
}

export function PlayEmote(string, wait = 0, slowness = 0) {
	if (!friend_exe) return;
	if (friend_exe.getProperty("is_special_attacking")) return;

	if (waitEmote == 0) {
		friend_exe.runCommand(`playanimation @s animation.blocklab_friend_exe.${string}`);
		waitEmote += wait;
		randomEmoteCounter += wait;
	}
	if (slowness > 0) {
		friend_exe.runCommand(`effect @s slowness ${slowness} 7 true`);
	}
}

export function feedBackSound(context = "normal") {
	let pitch = randFloat(0.7, 0.9);
	if (context == "agg") {
		pitch = randFloat(0.4, 0.6)
	}
	friend_exe.runCommand(`execute as @a at @s run playsound note.bit @s ~ ~ ~ 1 ${pitch}`);
}

let intervallPassive = 0;
let intervallSec = 10;
function Stage1RandomTransformUpdate() {
	stage1RandomTime--;
	if (intervallPassive > 0) {
		intervallSec--;
		if (intervallSec <= 0) {
			intervallPassive--;
			intervallSec = 3;
			//friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive3 @s ~ ~ ~ 1");
			if (closestDistance <= 24)
				friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.agressive @s ~ ~ ~ 1");
			if (intervallPassive <= 0) {
				if (stage1RandomCound == 0) {
					setStage(friend_exe, 2);
				} else {
					stage1RandomCound--;
				}
			}

		}
		//abspielen!
	}
	if (stage1RandomTime <= 0) {
		stage1RandomTime = randInt(10, 30);

		intervallSec = 1;
		intervallPassive = 3;
		//friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.passive3 @s ~ ~ ~ 1");
		//friend_exe.runCommand("execute as @a at @s run playsound mob.ai-friend.atmosphere.intervall.agressive @s ~ ~ ~ 1");
		//mob.ai-friend.atmosphere.intervall.agressive
	}
}

function randomGlitchJumpscareStage1(arm = true) {
	const randomH = randInt(0, 99);
	if (randomH < 33) {
		PlayEmote("stage1_headtwitch.1", 4);
	} else if (randInt > 66) {
		PlayEmote("stage1_headtwitch.2", 4);
	} else {
		if (arm) {
			PlayEmote("stage1_armtwitch", 4);
		} else {
			PlayEmote("stage1_headtwitch.2", 4);
		}
	}
}

const previousSneakState = new Map();
const previousY = new Map();
const jumpCooldown = new Map(); // player.name => last jump tick

function isBlockSolid(pos) {
	const blockBelow = mc.world.getDimension("overworld").getBlock({
		x: Math.floor(pos.x),
		y: Math.floor(pos.y) - 1,
		z: Math.floor(pos.z),
	});

	if (!blockBelow) return false;

	const airBlocks = ["minecraft:air", "minecraft:cave_air", "minecraft:void_air"];
	return !airBlocks.includes(blockBelow.typeId);
}

function yawToDirection(yawDegrees) {
	const yawRadians = (yawDegrees + 90) * (Math.PI / 180);
	return {
		x: Math.cos(yawRadians),
		z: Math.sin(yawRadians),
	};
}

function tryTeleportBehindPlayer(player, friend_exe) {
	const rotation = player.getRotation(); // Vector2
	const yaw = rotation.x; // Yaw only (horizontal rotation)
	friend_exe.teleport(player.location);
	friend_exe.runCommand(`playsound mob.endermen.portal @a ~ ~ ~`);
	friend_exe.runCommand(`playsound mob.ai-friend.atmosphere.impact.unsettling_impact @s ~ ~ ~ 10000`);

	if (Stage(friend_exe) > 1) {
		setStage(friend_exe, 0);
		mc.system.runTimeout(() => {
			feedBackSound();
			mc.world.sendMessage({
				rawtext: [
					{ translate: `text.blocklab_friend_exe:reset_memory.${randInt(0, 4)}` }
				]
			});
			const cookie = new mc.ItemStack("minecraft:cookie", randInt(2, 4));
			cookie.nameTag = "§kAI Friend Cookie";
			friend_exe.dimension.spawnItem(cookie, friend_exe.location);
		}, 60);
	}
}


let closestPlayer = mc.world.getPlayers()[0];
let closestDistance = Infinity;

let startWait = 20 * 6;

mc.system.runInterval(() => {
	startWait--;
	if (startWait >= 0) return;

	if (!friend_exe || !friend_exe?.isValid) return;

	let isDancing = false;
	const danceEvent = eventmanager.isEventActive(3);
	let distanceCheck = false;
	const currentTick = mc.system.currentTick;
	closestDistance = Infinity;
	mc.world.getPlayers().forEach((player) => {
		const distance = Distance(player.location, friend_exe.location);

		if (distance < closestDistance) {
			closestDistance = distance;
			closestPlayer = player;
		}
	});
	//erst der closed player ermitteln und vll da dann teleportieren
	if (closestDistance > 80) {
		tryTeleportBehindPlayer(closestPlayer, friend_exe);
	}


	mc.world.getPlayers().forEach((player) => {
		if (player.isSleeping) {
			//mc.world.sendMessage("Debug: player ist am schlafen... mal aufwecken? :3");
			//player.teleport(player.location);
		}
		const distance = Distance(player.location, friend_exe.location);

		if (!danceEvent || distance > 5) return;

		// --- Jump Detection via Y-delta + cooldown
		const prevY = previousY.get(player.name) ?? player.location.y;
		const dy = player.location.y - prevY;
		previousY.set(player.name, player.location.y);

		const lastJumpTick = jumpCooldown.get(player.name) ?? 0;
		const jumpCooldownTicks = 10;

		if (dy > 0.2 && currentTick - lastJumpTick > jumpCooldownTicks) {
			isDancing = true;
			jumpCooldown.set(player.name, currentTick);
		}

		// --- Sneak press detection
		const wasSneaking = previousSneakState.get(player.name) || false;
		const isSneaking = player.isSneaking;

		if (!wasSneaking && isSneaking) {
			isDancing = true;
		}

		previousSneakState.set(player.name, isSneaking);
	});

	if (danceEvent && isDancing) {
		eventmanager.tickingEvent.dancePlayer(friend_exe);
	}
}, 1); // Run every tick


const moodData = {
	mood_h: {
		text: "text.blocklab_friend_exe:emoticon.happy"
	},
	mood_f: {
		text: "text.blocklab_friend_exe:emoticon.friendly"
	},
	mood_o: {
		text: "text.blocklab_friend_exe:emoticon.okay"
	},
	mood_u: {
		text: "text.blocklab_friend_exe:emoticon.unhappy"
	},
	mood_e: {
		text: "text.blocklab_friend_exe:emoticon.evil"
	}
};

// custom components
mc.world.beforeEvents.worldInitialize.subscribe(data => {
	const { itemComponentRegistry } = data;
	itemComponentRegistry.registerCustomComponent("blocklab_friend_exe:emoticon_use", {
		// TODO: changing texture dynamically depending on mood (decide on texture first)
		//		 utilize Emojis?
		onUse: event => {
			const { itemStack, source } = event;
			if (friend_exe) {
				const moodStage = MoodStage(Mood(friend_exe));
				const tickEvent = eventmanager.tickingEvent ? 1 : 0;
				source.runCommand(`scriptevent blocklab_friend_exe:playerdata t.use_emoticon = 1;t.emoticon_face = ${5 - moodStage};t.objective = ${tickEvent}`);
				// No event --> mood, else --> event + event status 
				mc.system.runTimeout(() => {//Scan delay
					source.runCommand(`scriptevent blocklab_friend_exe:playerdata t.use_emoticon = 0`);
					if (!eventmanager.tickingEvent) {
						let current_mood = MoodStageString(MoodStage(Mood(friend_exe)));
						const randomNumber = randInt(1, 3);
						const stage = Stage(friend_exe);
						const moodInfo = moodData[current_mood].text + (stage > 0 ? String(stage) + "_" : "") + String(randomNumber);
						source.sendMessage({
							rawtext: [
								{ translate: moodInfo }
							]
						});
					} else if (eventmanager.tickingEvent) {
						const curr_event = eventmanager.tickingEvent;
						const progress_info = curr_event.progress_info ?? "";
						const filler = curr_event.progress_info ? " " : "";
						const amount_item = curr_event.item_amount && curr_event.itemName ? [String(curr_event.maxItems), curr_event.itemName] : ["", ""];
						const { description = "", progress = "" } = eventmanager.tickingEvent;
						source.sendMessage({
							rawtext: [
								{
									translate: description, with: { rawtext: [{ translate: amount_item[0] }, { translate: amount_item[1] }] }
								},
								{ text: " §c(" },
								{ translate: progress_info },
								{ text: filler },
								{ text: progress },
								{ text: "§c)" }
							]
						});
					}
				}, 30);
			}
		}
	});
});

const manual = {};
function _initializeManualForms() {
	for (let i = 0; i < 3; i++) {
		const form = new ui.ActionFormData();

		switch (i) {
			case 0: {
				form.title("AI-Friend Manual")
					.body({
						rawtext: [
							{ translate: "text.blocklab_friend_exe:manual.intro" }
						]
					})
					.button({ translate: "text.blocklab_friend_exe:manual.button.how_to_play" }, "textures/items/compass_item")
					.button({ translate: "text.blocklab_friend_exe:manual.button.items" }, "textures/blocklab/friend_exe/items/wrench")
				break;
			}
			case 1: {
				form.title("text.blocklab_friend_exe:manual.button.how_to_play")
					.body({
						rawtext: [
							{ translate: "text.blocklab_friend_exe:manual.how_to_play" },
							{ text: "\n\n" },
							{ translate: "text.blocklab_friend_exe:manual.maintenance.mood" }

						]
					});
				break;
			}
			case 2: {
				form.title("text.blocklab_friend_exe:manual.button.items")
				form.body({
					rawtext: [
						{ text: "\n" },
						{ translate: "text.blocklab_friend_exe:manual.maintenance.wrench" },
						{ text: "\n" },
						{ translate: "text.blocklab_friend_exe:manual.maintenance.emoticon" },
						{ text: "\n" },
						{ translate: "text.blocklab_friend_exe:manual.maintenance.manual" },
						{ text: "\n\n" },
						{ translate: "text.blocklab_friend_exe:manual.maintenance.recipe" },
						{ text: "\n" }
					]
				});
				break;
			}

		}
		if (i == 2) {
			form.button({
				translate: "text.blocklab_friend_exe:manual.maintenance.wrench_recipe"
			}, 'textures/blocklab/friend_exe/forms/wrench_recipe');
			form.button({
				translate: "text.blocklab_friend_exe:manual.maintenance.emoticon_recipe"
			}, 'textures/blocklab/friend_exe/forms/emoticon_recipe');
			form.button({
				translate: "text.blocklab_friend_exe:manual.maintenance.manual_recipe"
			}, 'textures/blocklab/friend_exe/forms/manual_recipe');
		}
		//if (i > 0 && i <= 2) form.button({ translate: "text.blocklab_friend_exe:manual.button.next" }, 'textures/blocklab/friend_exe/forms/next');
		//if (i > 0) form.button({ translate: "text.blocklab_friend_exe:manual.button.back" }, 'textures/blocklab/friend_exe/forms/return');
		manual[i] = form;
	}
}

function openManualPage(player, page = 0) {
	if (!manual[page]) _initializeManualForms();
	if (page === 3) {
		const modalForm = new ui.ModalFormData().title({ translate: "text.blocklab_friend_exe:manual.button.settings" }).dropdown({ translate: "text.blocklab_friend_exe:manual.settings.slider.difficulty" }, ["EASY", "HARD"]).submitButton({ translate: "text.blocklab_friend_exe:manual.button.submit" }).show(player).then((e) => {
			if (e.canceled) return;
			const difficulty = mc.world.scoreboard.getObjective(SCOREBOARD_DIFFICULTY);
			difficulty.setScore(PARTICIPANT_NAME, e.formValues[0] + 1);
		});
	}
	else {
		manual[page].show(player).then(res => {
			if (res.canceled) return;
			if (page === 0) {
				openManualPage(player, res.selection + 1); // jump to Usage / Maintenance / Settings
			}
		}).catch(err => {
			console.error("Manual form error:", err);
		});
	}
}
mc.world.beforeEvents.worldInitialize.subscribe(data => {
	const { itemComponentRegistry } = data;
	itemComponentRegistry.registerCustomComponent("blocklab_friend_exe:manual_use", {
		// TODO: How to get images into form outside of buttons?
		// Structure: What has to be written in the menu?
		onUse: event => {
			openManualPage(event.source);
		}
	});
});

