// Created by Lofi Girl. This code is like lofi beats: made to chill, not to steal ;)
import{system,world,ItemStack,BlockPermutation}from"@minecraft/server";import{ActionFormData}from"@minecraft/server-ui";import{Vector}from'./vector.js';export class StructurePlacerComponent{constructor(){this.onPlayerInteract=this.onPlayerInteract.bind(this);this.beforeOnPlayerPlace=this.beforeOnPlayerPlace.bind(this);this.onPlayerBreak=this.onPlayerBreak.bind(this);this.blockId="lfg_ff:structure_placer_block"
this.entityId="lfg_ff:structure_placer"
this.houseStructures=[{id:"modern_house_1",name:"Modern House 1",height:16},{id:"modern_house_2",name:"Modern House 2",height:16},{id:"contemporary_house_1",name:"Contemporary House 1",height:16},{id:"contemporary_house_2",name:"Contemporary House 2",height:20},{id:"japanese_house_1",name:"Japanese House 1",height:35},{id:"japanese_house_2",name:"Japanese House 2",height:22},{id:"medieval_house_1",name:"Medieval House 1",height:21},{id:"medieval_house_2",name:"Medieval House 2",height:31},{id:"farmhouse_1",name:"Farmhouse 1",height:16},{id:"farmhouse_2",name:"Farmhouse 2",height:20}];}
onPlayerInteract(e){const block=e.block;const direction=block.permutation.getState('minecraft:cardinal_direction');const center=this.getBlockCenter(block)
const blueprintActive=block.permutation.getState('lfg_ff:blueprint_active');const player=e.player;let selectedSlot=player.selectedSlotIndex;let handItem=player.getComponent("inventory").container.getItem(selectedSlot);const idList=this.houseStructures.map(h=>h.id)
const nameList=this.houseStructures.map(h=>h.name)
if(!blueprintActive){const placerUI=new ActionFormData().title("Structure Placer").body("\nSelect the house blueprint you want to place:\n\n")
idList.forEach((id,index)=>{placerUI.button(nameList[index],`textures/lfg_ff/lfg_ff/ui/${id}`)})
player.playSound("random.click",player.location)
placerUI.show(player).then(r=>{if(r.canceled)return;const houseIndex=r.selection
this.placeBluePrint(houseIndex,block,player)
block.setPermutation(block.permutation.withState("lfg_ff:blueprint_active",true))})
return;}else{const entity=this.getEntity(block)
if(!entity)return;const houseIndex=entity.getProperty("lfg_ff:id_index")
const placerUI=new ActionFormData().title("Structure Placer").body(`§9${nameList[houseIndex]}:§r\n\nFirst, add all craft resources to the drawer.\n\n`).button("Remove Blueprint","textures/lfg_ff/lfg_ff/ui/no").button("Rotate Blueprint","textures/lfg_ff/lfg_ff/ui/rotate").button("Craft Structure","textures/lfg_ff/lfg_ff/ui/yes")
player.playSound("random.click",player.location)
placerUI.show(player).then(r=>{if(r.canceled)return;if(r.selection==0)this.removeBlueprint(block)
if(r.selection==1)this.rotateStructure(block)
if(r.selection==2)this.craftStructure(block,player)})
return;}}
getStructurePlacementLoc(block,houseIndex){const direction=this.getDirection(block)
const structureCenter=this.getBlockInDirection(block.below(),direction,17)
const height=this.houseStructures.map(h=>h.height)[houseIndex]
const selec={corner1:Vector.add(structureCenter,new Vector(-16,1,-16)),corner2:Vector.add(structureCenter,new Vector(15,height,15))}
return{selec,center:structureCenter};}
placeBluePrint(houseIndex,block,player){const entity=this.getEntity(block)
if(!entity)return;const idList=this.houseStructures.map(h=>h.id)
const nameList=this.houseStructures.map(h=>h.name)
entity.setProperty("lfg_ff:id_index",houseIndex)
entity.setProperty("lfg_ff:blueprint_active",true)
entity.setProperty("lfg_ff:can_craft",false)
const{selec,center}=this.getStructurePlacementLoc(block,houseIndex)
const height=this.houseStructures.map(h=>h.height)[houseIndex]
const pRot=this.getBlueprintEntityRotations(this.getDirection(block))
const bluePrintEntity=block.dimension.spawnEntity("lfg_ff:structure_blueprint",center)
bluePrintEntity.setProperty("lfg_ff:placement_rotation",pRot)
entity.setProperty("lfg_ff:placement_rotation",pRot)
bluePrintEntity.setProperty("lfg_ff:height",height)
bluePrintEntity.setProperty("lfg_ff:id_index",houseIndex)}
craftStructure(block,player){const entity=this.getEntity(block)
if(!entity)return;if(!entity.getProperty("lfg_ff:blueprint_active"))return;if(!entity.getProperty("lfg_ff:can_craft")&&player.getGameMode()!=="Creative"){player.sendMessage("§cMissing resources. Please add all craft resources to the drawer.")
player.playSound("note.bass",player.location)
return;}
const idList=this.houseStructures.map(h=>h.id)
const nameList=this.houseStructures.map(h=>h.name)
const houseIndex=entity.getProperty("lfg_ff:id_index")
const houseId=idList[houseIndex]
const{selec,center}=this.getStructurePlacementLoc(block,houseIndex)
const pLoc=selec.corner1
const height=this.houseStructures.map(h=>h.height)[houseIndex]
const currentRot=entity.getProperty("lfg_ff:placement_rotation")
const animTime=height/2
entity.dimension.runCommand(`structure load lfg_ff:${houseId} ${pLoc.x} ${houseId == "modern_house_2" ? (pLoc.y - 3) : (pLoc.y - 2)} ${pLoc.z} ${currentRot}_degrees none layer_by_layer ${animTime} false true true`)
this.clearRequiredResources(entity)
for(let i=0;i<height;i++){system.runTimeout(()=>{block.dimension.playSound("break.calcite",block.location,{volume:20})
block.dimension.playSound("break.wood",block.location,{volume:20})
block.dimension.playSound("dig.stone",block.location,{volume:20})},10*i)}
system.runTimeout(()=>{block.dimension.playSound("lfg_ff:structure_placer_construct",block.location,{volume:20})
this.removeBlueprint(block)},20*animTime)
block.setPermutation(block.permutation.withState("lfg_ff:blueprint_active",false))
entity.setProperty("lfg_ff:blueprint_active",false)}
rotateStructure(block){const entity=this.getEntity(block)
if(!entity)return;if(!entity.getProperty("lfg_ff:blueprint_active"))return;const currentRot=entity.getProperty("lfg_ff:placement_rotation")
let newRot=currentRot==270?0:currentRot+90
entity.setProperty("lfg_ff:placement_rotation",newRot)
const houseIndex=entity.getProperty("lfg_ff:id_index")
const{selec,center}=this.getStructurePlacementLoc(block,houseIndex)
const bluePrintEntity=block.dimension.getEntities({location:center,maxDistance:1,type:"lfg_ff:structure_blueprint"})[0]
if(!bluePrintEntity)return;block.dimension.playSound("vr.stutterturn",block.location)
bluePrintEntity.setProperty("lfg_ff:placement_rotation",newRot)}
removeBlueprint(block){const entity=this.getEntity(block)
if(!entity)return;const houseIndex=entity.getProperty("lfg_ff:id_index")
const{selec,center}=this.getStructurePlacementLoc(block,houseIndex)
const bluePrintEntity=block.dimension.getEntities({location:center,maxDistance:1,type:"lfg_ff:structure_blueprint"})[0]
if(!bluePrintEntity)return;bluePrintEntity.remove()
block.setPermutation(block.permutation.withState("lfg_ff:blueprint_active",false))
entity.setProperty("lfg_ff:blueprint_active",false)}
clearRequiredResources(sourceEntity){const houseIndex=sourceEntity.getProperty("lfg_ff:id_index");const costTab=HouseCost[houseIndex];const inv=sourceEntity.getComponent('inventory').container;if(!inv)return;const requiredResources=new Map();for(const{item,amount}of costTab){requiredResources.set(item,amount);}
for(let i=0;i<inv.size;i++){const item=inv.getItem(i);if(!item)continue;const requiredAmount=requiredResources.get(item.typeId);if(!requiredAmount)continue;const removeAmount=Math.min(item.amount,requiredAmount);if(item.amount>removeAmount){item.amount-=removeAmount;inv.setItem(i,item);}else{inv.setItem(i,null);}
requiredResources.set(item.typeId,requiredAmount-removeAmount);if(requiredResources.get(item.typeId)<=0){requiredResources.delete(item.typeId);}
if(requiredResources.size===0)break;}}
onPlayerBreak(e){const block=e.block;const brokenBlockPermutation=e.brokenBlockPermutation
const direction=brokenBlockPermutation.getState('minecraft:cardinal_direction');const center=this.getBlockCenter(block)
const placerEntity=this.getEntity(block)
if(placerEntity){const structureCenter=this.getBlockInDirection(block.below(),direction,17)
const bluePrintEntity=block.dimension.getEntities({location:structureCenter,maxDistance:1,type:"lfg_ff:structure_blueprint"})[0]
if(bluePrintEntity)bluePrintEntity.remove()
const inv=placerEntity.getComponent('inventory').container;for(let i=0;i<inv.size;i++){const item=inv.getItem(i);if(item)
placerEntity.dimension.spawnItem(item,Vector.add(placerEntity.location,new Vector(0,0.5,0)))}
placerEntity.remove()}}
beforeOnPlayerPlace(e){const block=e.block;const permutationToPlace=e.permutationToPlace;const direction=permutationToPlace.getState('minecraft:cardinal_direction');const placementPerm=permutationToPlace.withState("lfg_ff:is_placed",true)
system.run(()=>{block.setPermutation(placementPerm)
const loc=block.location
const placerRotationY=this.getPlacerEntityRotations(direction)
const spawnedplacerEntity=block.dimension.spawnEntity(this.entityId,Vector.add(loc,new Vector(0.5,0,0.5)))
spawnedplacerEntity.setRotation({x:0,y:placerRotationY})
spawnedplacerEntity.setProperty("lfg_ff:rotation_y",placerRotationY);})}
getBlueprintEntityRotations(dir){let rotationY=0;switch(dir){case'north':rotationY=90;break;case'south':rotationY=270;break;case'east':rotationY=180;break;case'west':rotationY=0;break;}
return rotationY;}
getPlacerEntityRotations(dir){let rotationY=0;switch(dir){case'north':rotationY=0;break;case'south':rotationY=180;break;case'east':rotationY=90;break;case'west':rotationY=-90;break;}
return rotationY;}
getDirection(block){return block.permutation.getState("minecraft:cardinal_direction")}
getBlockCenter(b){return{x:b.location.x+0.5,y:b.location.y+0.5,z:b.location.z+0.5}}
getBlock(entity){const block=entity.dimension.getBlock(Vector.add(entity.location,new Vector(0,0.2,0)))
if(block&&block.typeId==this.blockId)return block;return null;}
getEntity(block){const entity=block.dimension.getEntities({location:Vector.add(block.location,new Vector(0.5,0,0.5)),maxDistance:0.1,type:this.entityId})[0]
if(entity)return entity;return null;}
getBlockInDirection(block,direction,distance){switch(direction){case'north':return block.north(distance);case'south':return block.south(distance);case'east':return block.east(distance);case'west':return block.west(distance);}}
getOppositeDirection(direction){switch(direction){case'north':return'south';case'south':return'north';case'east':return'west';case'west':return'east';case'up':return'down';case'down':return'up';default:return null;}}}
const HouseCost=[[{item:"minecraft:light_gray_concrete",amount:832},{item:"lfg_ff:window_frame",amount:256},{item:"lfg_ff:fence",amount:32},{item:"lfg_ff:beam",amount:32},{item:"lfg_ff:floor",amount:448},{item:"lfg_ff:bush",amount:32},],[{item:"minecraft:white_concrete",amount:832},{item:"lfg_ff:window_frame",amount:256},{item:"lfg_ff:fence",amount:32},{item:"lfg_ff:beam",amount:16},{item:"lfg_ff:floor",amount:448},{item:"lfg_ff:bush",amount:32},],[{item:"lfg_ff:wall",amount:512},{item:"lfg_ff:window_frame",amount:64},{item:"lfg_ff:roof",amount:128},{item:"lfg_ff:fence",amount:16},{item:"lfg_ff:floor",amount:256},{item:"lfg_ff:bush",amount:32},],[{item:"lfg_ff:wall",amount:832},{item:"lfg_ff:window_frame",amount:64},{item:"lfg_ff:roof",amount:128},{item:"lfg_ff:beam",amount:16},{item:"lfg_ff:floor",amount:448},{item:"lfg_ff:bush",amount:64},],[{item:"lfg_ff:wall",amount:512},{item:"lfg_ff:window_frame",amount:32},{item:"lfg_ff:roof",amount:256},{item:"lfg_ff:beam",amount:32},{item:"lfg_ff:floor",amount:64},{item:"lfg_ff:bush",amount:16},],[{item:"lfg_ff:wall",amount:256},{item:"lfg_ff:window_frame",amount:16},{item:"lfg_ff:roof",amount:64},{item:"lfg_ff:beam",amount:64},{item:"lfg_ff:floor",amount:64},{item:"lfg_ff:bush",amount:128},],[{item:"lfg_ff:wall",amount:256},{item:"lfg_ff:window_frame",amount:64},{item:"lfg_ff:roof",amount:128},{item:"lfg_ff:beam",amount:64},{item:"lfg_ff:floor",amount:32},{item:"lfg_ff:bush",amount:32},],[{item:"lfg_ff:wall",amount:512},{item:"lfg_ff:window_frame",amount:64},{item:"lfg_ff:roof",amount:128},{item:"lfg_ff:beam",amount:64},{item:"lfg_ff:floor",amount:32},{item:"lfg_ff:bush",amount:32},],[{item:"lfg_ff:wall",amount:512},{item:"lfg_ff:window_frame",amount:64},{item:"lfg_ff:roof",amount:128},{item:"lfg_ff:fence",amount:64},{item:"lfg_ff:floor",amount:256},{item:"lfg_ff:bush",amount:64},],[{item:"lfg_ff:wall",amount:256},{item:"lfg_ff:window_frame",amount:32},{item:"lfg_ff:roof",amount:128},{item:"lfg_ff:beam",amount:32},{item:"lfg_ff:floor",amount:128},{item:"lfg_ff:bush",amount:64},]]
system.afterEvents.scriptEventReceive.subscribe((data)=>{const{id,sourceEntity}=data;if(id==="lfg_ff:structure_placer_inventory_check"){if(!sourceEntity)return;if(!sourceEntity.getProperty("lfg_ff:blueprint_active")){sourceEntity.nameTag="§r"
return;};const houseIndex=sourceEntity.getProperty("lfg_ff:id_index");const costTab=HouseCost[houseIndex];const containerItems=new Map();const inv=sourceEntity.getComponent('inventory').container;for(let i=0;i<inv.size;i++){const item=inv.getItem(i);if(item){containerItems.set(item.typeId,(containerItems.get(item.typeId)||0)+item.amount);}}
let allResourcesAvailable=true;let nametagDisplay=`§l§9Required Resources:\n`;for(const{item,amount}of costTab){const hasAmount=containerItems.get(item)||0;const color=hasAmount>=amount?"§a":"§c";if(hasAmount<amount)allResourcesAvailable=false;const ressourceName=item.replace("minecraft:","").replace("lfg_ff:","").replace(/_/g," ")
nametagDisplay+=`${color}- ${amount}x ${ressourceName == "wall" ? "interior wall" : ressourceName == "floor" ? "flooring" : ressourceName} (${hasAmount}/${amount})\n`;}
sourceEntity.nameTag=nametagDisplay;sourceEntity.setProperty("lfg_ff:can_craft",allResourcesAvailable);}});