// Priority: 1000 // Spring Fish global.springOcean = [ { fish: "aquaculture:atlantic_herring", weight: 16 }, { fish: "aquaculture:atlantic_herring", weight: 16, night: true }, { fish: "minecraft:pufferfish", weight: 9, night: true, requiresClear: true }, { fish: "aquaculture:jellyfish", weight: 9, night: true }, { fish: "minecraft:tropical_fish", weight: 8 }, { fish: "minecraft:tropical_fish", weight: 8, night: true }, { fish: "unusualfishmod:raw_drooping_gourami", weight: 6 }, { fish: "aquaculture:atlantic_halibut", weight: 4, requiresRain: true }, { fish: "unusualfishmod:raw_spindlefish", weight: 3, night: true }, ]; global.springRiver = [ { fish: "aquaculture:minnow", weight: 11 }, { fish: "aquaculture:bluegill", weight: 11, night: true }, { fish: "aquaculture:smallmouth_bass", weight: 9 }, { fish: "aquaculture:smallmouth_bass", weight: 9, night: true }, { fish: "minecraft:cod", weight: 10 }, { fish: "minecraft:cod", weight: 10, night: true }, { fish: "aquaculture:pink_salmon", weight: 9, night: true }, { fish: "aquaculture:catfish", weight: 8, night: true, requiresRain: true }, { fish: "aquaculture:bayad", weight: 5, night: true, requiresRain: true }, { fish: "unusualfishmod:raw_blind_sailfin", weight: 5 }, ]; global.springFresh = [ { fish: "aquaculture:minnow", weight: 11 }, { fish: "aquaculture:bluegill", weight: 11, night: true }, { fish: "aquaculture:perch", weight: 9 }, { fish: "aquaculture:carp", weight: 9, requiresRain: true }, { fish: "unusualfishmod:raw_beaked_herring", weight: 8 }, { fish: "unusualfishmod:raw_hatchetfish", night: true, weight: 5 }, { fish: "unusualfishmod:raw_eyelash", weight: 3, night: true, requiresClear: true, }, ]; // Summer fish global.summerOcean = [ { fish: "unusualfishmod:raw_sneep_snorp", weight: 32 }, { fish: "minecraft:pufferfish", weight: 30, night: true, requiresClear: true, }, { fish: "aquaculture:jellyfish", weight: 24, night: true }, { fish: "minecraft:tropical_fish", weight: 22 }, { fish: "minecraft:tropical_fish", weight: 22, night: true }, { fish: "aquaculture:red_grouper", weight: 19, night: true }, { fish: "aquaculture:tuna", weight: 15 }, { fish: "aquaculture:capitaine", weight: 10 }, { fish: "aquaculture:goldfish", weight: 4, requiresClear: true }, ]; global.summerRiver = [ { fish: "aquaculture:minnow", weight: 28 }, { fish: "aquaculture:bluegill", weight: 28, night: true }, { fish: "minecraft:salmon", weight: 25 }, { fish: "aquaculture:leech", weight: 23, night: true }, { fish: "aquaculture:smallmouth_bass", weight: 22 }, { fish: "aquaculture:smallmouth_bass", weight: 22, night: true }, { fish: "aquaculture:synodontis", weight: 18, requiresClear: true }, { fish: "aquaculture:tambaqui", weight: 18, night: true }, { fish: "aquaculture:box_turtle", weight: 5, night: true, requiresRain: true, }, ]; global.summerFresh = [ { fish: "aquaculture:minnow", weight: 11 }, { fish: "aquaculture:bluegill", weight: 11, night: true }, { fish: "aquaculture:carp", weight: 9 }, { fish: "aquaculture:leech", weight: 9, night: true }, { fish: "aquaculture:piranha", weight: 9, night: true, requiresRain: true }, { fish: "aquaculture:boulti", weight: 5, night: true }, { fish: "crittersandcompanions:koi_fish", weight: 3, requiresClear: true }, { fish: "aquaculture:arapaima", weight: 3, requiresRain: true }, { fish: "unusualfishmod:raw_circus_fish", weight: 2 }, ]; // Autumn Fish global.autumnOcean = [ { fish: "aquaculture:blackfish", weight: 22, night: true }, { fish: "aquaculture:blackfish", weight: 22 }, { fish: "minecraft:pufferfish", weight: 22, night: true, requiresClear: true, }, { fish: "unusualfishmod:raw_picklefish", weight: 22, night: true, requiresRain: true, }, { fish: "unusualfishmod:raw_forkfish", weight: 20 }, { fish: "unusualfishmod:raw_forkfish", weight: 20, night: true }, { fish: "unusualfishmod:raw_sailor_barb", weight: 18 }, { fish: "unusualfishmod:raw_demon_herring", weight: 16, night: true }, { fish: "unusualfishmod:raw_copperflame_anthias", weight: 11, night: true, requiresClear: true, }, { fish: "aquaculture:red_shrooma", weight: 8, requiresRain: true }, ]; global.autumnRiver = [ { fish: "aquaculture:minnow", weight: 27 }, { fish: "aquaculture:bluegill", weight: 27, night: true }, { fish: "minecraft:salmon", weight: 26 }, { fish: "aquaculture:brown_trout", weight: 26 }, { fish: "aquaculture:smallmouth_bass", weight: 24 }, { fish: "aquaculture:muskellunge", weight: 18, night: true }, { fish: "aquaculture:arrau_turtle", weight: 8, night: true, requiresRain: true, }, ]; global.autumnFresh = [ { fish: "aquaculture:minnow", weight: 11 }, { fish: "aquaculture:bluegill", weight: 11, night: true }, { fish: "aquaculture:perch", weight: 9 }, { fish: "aquaculture:carp", weight: 10 }, { fish: "aquaculture:gar", weight: 5, requiresRain: true }, { fish: "unusualfishmod:raw_bark_angelfish", weight: 4 }, { fish: "unusualfishmod:raw_amber_goby", weight: 1, night: true, requiresClear: true, }, ]; // Winter Fish global.winterOcean = [ { fish: "aquaculture:atlantic_herring", weight: 27 }, { fish: "aquaculture:atlantic_herring", weight: 27, night: true }, { fish: "aquaculture:blackfish", night: true, weight: 26 }, { fish: "aquaculture:blackfish", weight: 26 }, { fish: "aquaculture:pollock", weight: 25, requiresRain: true }, { fish: "aquaculture:atlantic_cod", weight: 22 }, { fish: "unusualfishmod:raw_triple_twirl_pleco", night: true, weight: 20 }, { fish: "unusualfishmod:raw_copperflame_anthias", weight: 15, night: true, requiresClear: true, }, { fish: "aquaculture:pacific_halibut", weight: 10 }, { fish: "aquaculture:brown_shrooma", weight: 8, requiresRain: true }, ]; global.winterRiver = [ { fish: "aquaculture:minnow", weight: 28 }, { fish: "aquaculture:bluegill", weight: 28, night: true }, { fish: "minecraft:cod", weight: 27 }, { fish: "minecraft:cod", weight: 27, night: true }, { fish: "aquaculture:brown_trout", weight: 25 }, { fish: "aquaculture:pink_salmon", weight: 22 }, { fish: "unusualfishmod:raw_duality_damselfish", weight: 16, night: true, requiresClear: true, }, { fish: "aquaculture:rainbow_trout", weight: 10, requiresRain: true }, { fish: "aquaculture:starshell_turtle", weight: 8, night: true, requiresRain: true, }, ]; global.winterFresh = [ { fish: "aquaculture:minnow", weight: 11 }, { fish: "aquaculture:bluegill", weight: 11, night: true }, { fish: "aquaculture:perch", weight: 9, night: true }, { fish: "aquaculture:carp", weight: 8, night: true }, { fish: "unusualfishmod:raw_snowflake", weight: 8, requiresRain: true }, { fish: "unusualfishmod:raw_snowflake", weight: 8, night: true, requiresRain: true, }, { fish: "unusualfishmod:raw_aero_mono", weight: 4 }, { fish: "unusualfishmod:raw_hatchetfish", night: true, weight: 2 }, ]; global.getRoe = (fish) => { let fishId = fish.path; if (fishId.includes("raw_")) { if (fishId === "raw_snowflake") fishId = "frosty_fin"; else fishId = fishId.substring(4, fishId.length); } return `society:${fishId}_roe`; }; global.getFishFromRoe = (roe) => { let roeId = roe.split(":")[1].slice(0, -4); if (roeId.includes("frosty_fin")) roeId = "raw_snowflake"; return Array.from(global.fishPondDefinitions.keys()).find((fish) => fish.includes(roeId), ); }; global.getPondProperties = (block) => { const properties = block.getProperties(); return { facing: properties.get("facing"), valid: properties.get("valid"), mature: properties.get("mature").toLowerCase(), upgraded: properties.get("upgraded").toLowerCase(), quest: properties.get("quest").toLowerCase(), }; }; /** * @param {Internal.Player|null} player * @param {Internal.Stages|SocietyStages} stages stages player.stages or fish pond basket NBT stages */ global.handleFishHarvest = (block, player, server, basket, stages) => { const { facing, valid, upgraded, quest } = global.getPondProperties(block); let nbt = block.getEntityData(); const { quest_id, type, population, max_population } = nbt.data; const fish = global.fishPondDefinitions.get(type); let additionalMaxRoe = 0; let harvestOutputs = []; if (Math.random() < 0.01 && !stages.has("bullfish_jobs")) { harvestOutputs.push("society:bullfish_jobs"); } if (stages.has("caper_catcher")) additionalMaxRoe += 3; if (stages.has("caviar_catcher")) additionalMaxRoe += 2; const fishRoe = global.getRoe(type); const calculateRoe = rnd( Math.floor(population / 4), Math.floor(population / 2) + additionalMaxRoe, ); const roeCount = calculateRoe > 1 ? calculateRoe : 1; harvestOutputs.push(`${roeCount}x ${fishRoe}`); if (fish.additionalRewards) { let fishPondRoll = 0; fish.additionalRewards.forEach((reward) => { fishPondRoll = Math.random(); let rewardChance = reward.chance; if (upgraded == "true") rewardChance *= 2; if (stages.has("scum_collector")) rewardChance *= 2; if (population >= reward.minPopulation && fishPondRoll <= rewardChance) { // Rewards scale to amount of fish population relative to when reward starts spawning let calculateCount = Math.floor( reward.count * ((population - reward.minPopulation) / (10 - reward.minPopulation)), ); if (population == 10) calculateCount = reward.count; harvestOutputs.push( `${calculateCount > 1 ? calculateCount : 1}x ${reward.item}`, ); } }); } server.runCommandSilent( `playsound stardew_fishing:dwop block @a ${block.x} ${block.y} ${block.z}`, ); if (!basket) { global.giveExperience(server, player, "fishing", roeCount * 4); } if (Number(max_population) === 10 && quest === "true") { block.set(block.id, { facing: facing, valid: valid, mature: false, upgraded: upgraded, quest: false, }); } else { block.set(block.id, { facing: facing, valid: valid, mature: false, upgraded: upgraded, quest: quest, }); } if (basket) return harvestOutputs; harvestOutputs.forEach((item) => { block.popItemFromFace(item, facing); }); }; /** * @param {Internal.Stages|SocietyStages} stages */ global.handleFishExtraction = (block, player, server, stages) => { let nbt = block.getEntityData(); const { type, population, non_native_fish } = nbt.data; let naturalPopulation = population - non_native_fish; let result; let resultCount = stages.has("mitosis") && naturalPopulation > 0 ? 2 : 1; let quality = 0; if (stages.has("bullfish_jobs") && Number(naturalPopulation) > 3) { quality = Math.floor((Number(population) - 3) / 2); } if (stages.has("hot_hands") && naturalPopulation > 0) { server.runCommandSilent( `playsound minecraft:block.lava.extinguish block @a ${block.x} ${block.y} ${block.z}`, ); let smokedFishId = type.path; if (smokedFishId.includes("raw_")) { if (smokedFishId === "raw_snowflake") smokedFishId = "frosty_fin"; else smokedFishId = smokedFishId.substring(4, smokedFishId.length); } result = Item.of( `${resultCount}x ${rnd25() ? "minecraft:coal" : `society:smoked_${smokedFishId}` }`, quality > 0 ? `{quality_food:{quality:${quality}}}` : null, ); } else { if (["aquaculture:leech", "aquaculture:minnow"].includes(type)) { result = Item.of( `${resultCount}x ${type}`, quality > 0 ? `{Damage:0,quality_food:{quality:${quality},effects:[]}}` : `{Damage:0}`, ); } else { result = Item.of( `${resultCount}x ${type}`, quality > 0 ? `{quality_food:{quality:${quality},effects:[]}}` : null, ); } } if (result) { nbt.merge({ data: { population: decreaseStage(population), non_native_fish: naturalPopulation > 0 ? non_native_fish : non_native_fish > 0 ? decreaseStage(non_native_fish) : 0, }, }); global.setBlockEntityData(block, nbt) } return result; }; global.validatePond = (block, level, lavaFish) => { const { x, y, z } = block; const facing = block.properties.get("facing"); const pondWaterCheckMap = { north: { startX: 1, startZ: 4, endX: -1, endZ: 1 }, south: { startX: -1, startZ: -4, endX: 1, endZ: -1 }, east: { startX: -1, startZ: 1, endX: -4, endZ: -1 }, west: { startX: 1, startZ: -1, endX: 4, endZ: 1 }, }; const adjacentPondMap = { north: { xOffset: 1, zOffset: 0 }, south: { xOffset: 1, zOffset: 0 }, east: { xOffset: 0, zOffset: 1 }, west: { xOffset: 0, zOffset: 1 }, }; const { startX, startZ, endX, endZ } = pondWaterCheckMap[facing]; let adjacentPos1 = new BlockPos( x + adjacentPondMap[facing].xOffset, y, z + adjacentPondMap[facing].zOffset, ); let adjacentPos2 = new BlockPos( x - adjacentPondMap[facing].xOffset, y, z - adjacentPondMap[facing].zOffset, ); if (!level.isLoaded(adjacentPos1) || !level.isLoaded(adjacentPos2)) return false; const conflictingPonds = level.getBlock(adjacentPos1).id === "society:fish_pond" || level.getBlock(adjacentPos2).id === "society:fish_pond"; let waterAmount = 0; let scannedId = ""; let scannedBlockProperties; for (let pos of BlockPos.betweenClosed( new BlockPos(x + startX, y, z + startZ), [x + endX, y, z + endZ], )) { if (!level.isLoaded(pos)) return false; scannedBlockProperties = level.getBlock(pos).properties; scannedId = level.getBlock(pos).id; if (lavaFish && scannedId === "minecraft:lava") { waterAmount += 1; } else if ( !lavaFish && (scannedId === "minecraft:water" || scannedId === "minecraft:ice") ) { waterAmount += 1; } else if ( !lavaFish && scannedBlockProperties && scannedBlockProperties.get("waterlogged") == "true" ) { waterAmount += 1; } else if (level.getBlock(pos).hasTag("society:fish_pond_water")) { waterAmount += 1; } } if (waterAmount !== 12 || conflictingPonds) return false; return true; }; const fishPondTickRate = 200; const fishPondProgTime = 40; global.convertPondFromLegacy = (block) => { let nbt = block.getEntityData(); let newRecipe; if (!nbt.data.contains("type")) return; if (!String(nbt.data.type).includes(":")) { newRecipe = Array.from(global.fishPondDefinitions.keys())[ Number(nbt.data.type) - 1 ]; if (newRecipe) { nbt.merge({ data: { type: newRecipe } }); global.setBlockEntityData(block, nbt) } } }; global.handleFishPondTick = (tickEvent) => { const { block, level } = tickEvent; const { x, y, z } = block; let dayTime = level.dayTime(); let morningModulo = dayTime % 24000; let blockProperties = level.getBlock(block.pos).getProperties(); let nbt = block.getEntityData(); if ( blockProperties.get("mature") === "true" && String(nbt.data.type).includes(":") ) return; const { facing, valid, mature, upgraded, quest } = global.getPondProperties( level.getBlock(block.pos), ); const { type, population, max_population, non_native_fish } = nbt.data; if (type == 0) { let defaultNbt = block.getEntityData(); defaultNbt.merge({ data: { type: "" } }); global.setBlockEntityData(block, defaultNbt); return; } if (!String(type).includes(":")) { global.convertPondFromLegacy(block); return; } const fish = global.fishPondDefinitions.get(type); if (!type.equals("") && tickEvent.tick % 20 === 0) { block.set(block.id, { facing: facing, valid: global.validatePond(block, level, fish.lava), mature: mature, upgraded: upgraded, quest: quest, }); } if ( morningModulo >= fishPondProgTime && morningModulo < fishPondProgTime + fishPondTickRate ) { if (!type.equals("") && valid === "true") { if (Number(population) > 1) { level.spawnParticles( "supplementaries:suds", true, x + 0.5, y + 1, z + 0.5, 0.1 * rnd(1, 2), 0.1 * rnd(1, 2), 0.1 * rnd(1, 2), rnd(2, 6), 0.001, ); block.set(block.id, { facing: facing, valid: valid, mature: true, upgraded: upgraded, quest: quest, }); } if (Number(max_population) === 10 && quest === "true") { block.set(block.id, { facing: facing, valid: valid, mature: false, upgraded: upgraded, quest: false, }); } if ( Number(max_population) !== 10 && quest !== "true" && rnd75() && population == max_population ) { block.set(block.id, { facing: facing, valid: valid, mature: false, upgraded: upgraded, quest: true, }); let questCount = getRequestedItems(type, max_population).length; nbt.merge({ data: { quest_id: `${Math.min(Math.floor(rnd(0, questCount)), questCount - 1)}`, }, }); global.setBlockEntityData(block, nbt) } else if (population < max_population) { successParticles(level, block); level.server.runCommandSilent( `playsound supplementaries:item.bubble_blower block @a ${block.x} ${block.y} ${block.z}`, ); nbt.merge({ data: { population: increaseStage(population) } }); global.setBlockEntityData(block, nbt) } else if (population > max_population) { nbt.merge({ data: { population: max_population } }); global.setBlockEntityData(block, nbt) } if (population === max_population && non_native_fish > 0) { nbt.merge({ data: { non_native_fish: decreaseStage(non_native_fish) }, }); global.setBlockEntityData(block, nbt) } } } }; global.hasBobber = (rod, bobberId) => { let stringNbt = rod.nbt.toString(); if ( rod.id == "minecraft:fishing_rod" || rod.id == "netherdepthsupgrade:lava_fishing_rod" ) { return stringNbt.includes(`{bobber:{Count:1b,id:"${bobberId}"}}`); } else { return stringNbt.includes(`{Count:1b,Slot:3,id:"${bobberId}"}`); } };