Skip to content

Commit bfc4c7c

Browse files
committed
Implement archeologists
1 parent c97b5fb commit bfc4c7c

8 files changed

Lines changed: 316 additions & 6 deletions

File tree

CHANGELOG.txt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
Base mod:
2-
Significantly improve performance
3-
Grass types now harvest sweet berry bushes
4-
Grass types now harvest glow berries
5-
Honey bottles can be crafted with 1x glass bottle and 3x honeycombs
6-
Balance dive treasure loot
2+
Ground types harvest archeology related items
73

84
Integration:
9-
Grass types also harvest farmer's delight crops
5+
-

common/src/main/kotlin/accieo/cobbleworkers/config/CobbleworkersConfig.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ class CobbleworkersConfig : ConfigData {
7878
@ConfigEntry.Gui.CollapsibleObject
7979
var extinguisher = ExtinguisherGroup()
8080

81+
@ConfigEntry.Gui.CollapsibleObject
82+
var archeology = ArcheologyGroup()
83+
8184
class GeneralGroup {
8285
@ConfigEntry.BoundedDiscrete(min = 10, max = 30)
8386
var blocksScannedPerTick = 15
@@ -284,4 +287,18 @@ class CobbleworkersConfig : ConfigData {
284287
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.DROPDOWN)
285288
var typeExtinguishesFire: CobbleworkersConfigPokemonType = CobbleworkersConfigPokemonType.WATER
286289
}
290+
291+
class ArcheologyGroup {
292+
var archeologistsEnabled = true
293+
var archeologists: MutableList<String> = mutableListOf("pikachu")
294+
295+
var archeologyLootingCooldownSeconds: Long = 80
296+
297+
var lootTables: MutableList<String> = mutableListOf(
298+
"cobbleworkers:archeology_treasure",
299+
)
300+
301+
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.DROPDOWN)
302+
var typeDoesArcheology: CobbleworkersConfigPokemonType = CobbleworkersConfigPokemonType.GROUND
303+
}
287304
}

common/src/main/kotlin/accieo/cobbleworkers/enums/JobType.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ package accieo.cobbleworkers.enums
1111
enum class JobType {
1212
AmethystHarvester,
1313
ApricornHarvester,
14+
Archeologist,
1415
BerryHarvester,
1516
BrewingStandFuelGenerator,
1617
CropHarvester,
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Copyright (C) 2025 Accieo
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
package accieo.cobbleworkers.jobs
10+
11+
import accieo.cobbleworkers.cache.CobbleworkersCacheManager
12+
import accieo.cobbleworkers.config.CobbleworkersConfigHolder
13+
import accieo.cobbleworkers.enums.JobType
14+
import accieo.cobbleworkers.interfaces.Worker
15+
import accieo.cobbleworkers.utilities.CobbleworkersInventoryUtils
16+
import accieo.cobbleworkers.utilities.CobbleworkersNavigationUtils
17+
import accieo.cobbleworkers.utilities.CobbleworkersTypeUtils
18+
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
19+
import net.minecraft.block.Block
20+
import net.minecraft.block.Blocks
21+
import net.minecraft.item.ItemStack
22+
import net.minecraft.loot.context.LootContextParameterSet
23+
import net.minecraft.loot.context.LootContextParameters
24+
import net.minecraft.loot.context.LootContextTypes
25+
import net.minecraft.registry.RegistryKey
26+
import net.minecraft.registry.RegistryKeys
27+
import net.minecraft.server.world.ServerWorld
28+
import net.minecraft.util.Identifier
29+
import net.minecraft.util.math.BlockPos
30+
import net.minecraft.world.World
31+
import java.util.UUID
32+
import kotlin.text.lowercase
33+
34+
object Archeologist : Worker {
35+
private val config = CobbleworkersConfigHolder.config.archeology
36+
private val cooldownTicks get() = config.archeologyLootingCooldownSeconds * 20L
37+
private val lastGenerationTime = mutableMapOf<UUID, Long>()
38+
private val heldItemsByPokemon = mutableMapOf<UUID, List<ItemStack>>()
39+
private val failedDepositLocations = mutableMapOf<UUID, MutableSet<BlockPos>>()
40+
private val validBlocks: Set<Block> = setOf(
41+
Blocks.DIRT,
42+
Blocks.GRAVEL,
43+
Blocks.MUD,
44+
Blocks.COARSE_DIRT,
45+
Blocks.ROOTED_DIRT,
46+
)
47+
48+
override val jobType: JobType = JobType.Archeologist
49+
override val blockValidator: ((World, BlockPos) -> Boolean) = { world: World, pos: BlockPos ->
50+
val state = world.getBlockState(pos)
51+
val aboveState = world.getBlockState(pos.up())
52+
state.block in validBlocks && aboveState.isAir
53+
}
54+
55+
/**
56+
* Determines if Pokémon is eligible to be an archeologist.
57+
* NOTE: This is used to prevent running the tick method unnecessarily.
58+
*/
59+
override fun shouldRun(pokemonEntity: PokemonEntity): Boolean {
60+
if (!config.archeologistsEnabled) return false
61+
62+
return CobbleworkersTypeUtils.isAllowedByType(config.typeDoesArcheology, pokemonEntity) || isDesignatedHarvester(pokemonEntity)
63+
}
64+
65+
/**
66+
* Main logic loop for the archeologist, executed each tick.
67+
* Delegates to state handlers handleHarvesting and handleDepositing
68+
* to manage the current task of the Pokémon.
69+
*
70+
* NOTE: Origin refers to the pasture's block position.
71+
*/
72+
override fun tick(world: World, origin: BlockPos, pokemonEntity: PokemonEntity) {
73+
val pokemonId = pokemonEntity.pokemon.uuid
74+
val heldItems = heldItemsByPokemon[pokemonId]
75+
76+
if (heldItems.isNullOrEmpty()) {
77+
failedDepositLocations.remove(pokemonId)
78+
handleHarvesting(world, origin, pokemonEntity)
79+
} else {
80+
CobbleworkersInventoryUtils.handleDepositing(world, origin, pokemonEntity, heldItems,
81+
failedDepositLocations,
82+
heldItemsByPokemon
83+
)
84+
}
85+
}
86+
87+
/**
88+
* Handles logic for archeologist when the Pokémon is not holding items.
89+
*/
90+
private fun handleHarvesting(world: World, origin: BlockPos, pokemonEntity: PokemonEntity) {
91+
val pokemonId = pokemonEntity.pokemon.uuid
92+
val closestBlock = findClosestArcheologySpot(world, origin) ?: return
93+
val currentTarget = CobbleworkersNavigationUtils.getTarget(pokemonId, world)
94+
95+
if (currentTarget == null) {
96+
if (!CobbleworkersNavigationUtils.isTargeted(closestBlock, world) && !CobbleworkersNavigationUtils.isRecentlyExpired(closestBlock, world)) {
97+
CobbleworkersNavigationUtils.claimTarget(pokemonId, closestBlock, world)
98+
}
99+
return
100+
}
101+
102+
if (currentTarget == closestBlock) {
103+
CobbleworkersNavigationUtils.navigateTo(pokemonEntity, closestBlock)
104+
}
105+
106+
if (CobbleworkersNavigationUtils.isPokemonAtPosition(pokemonEntity, currentTarget)) {
107+
handleGeneration(world, closestBlock, pokemonEntity)
108+
CobbleworkersNavigationUtils.releaseTarget(pokemonId, world)
109+
}
110+
}
111+
112+
/**
113+
* Scans the pasture's block surrounding area for the closest archeology spot.
114+
*/
115+
private fun findClosestArcheologySpot(world: World, origin: BlockPos): BlockPos? {
116+
val possibleTargets = CobbleworkersCacheManager.getTargets(origin, jobType)
117+
if (possibleTargets.isEmpty()) return null
118+
119+
return possibleTargets
120+
.filter { pos ->
121+
blockValidator(world, pos) && !CobbleworkersNavigationUtils.isRecentlyExpired(pos, world)
122+
}
123+
.minByOrNull { it.getSquaredDistance(origin) }
124+
}
125+
126+
/**
127+
* Handles logic for generating loot from cobblemon loot table.
128+
*/
129+
fun handleGeneration(world: World, origin: BlockPos, pokemonEntity: PokemonEntity) {
130+
val pokemonId = pokemonEntity.pokemon.uuid
131+
val now = world.time
132+
val lastTime = lastGenerationTime[pokemonId] ?: 0L
133+
134+
if (now - lastTime < cooldownTicks) {
135+
return
136+
}
137+
138+
val lootParams = LootContextParameterSet.Builder(world as ServerWorld)
139+
.add(LootContextParameters.ORIGIN, origin.toCenterPos())
140+
.add(LootContextParameters.THIS_ENTITY, pokemonEntity)
141+
.build(LootContextTypes.CHEST)
142+
143+
val lootTables = config.lootTables.mapNotNull { Identifier.tryParse(it) }
144+
145+
if (lootTables.isEmpty()) return
146+
147+
val selectedId = lootTables.random()
148+
149+
val lootTableKey = RegistryKey.of(RegistryKeys.LOOT_TABLE, selectedId)
150+
val lootTable = world.server.reloadableRegistries.getLootTable(lootTableKey)
151+
val drops = lootTable.generateLoot(lootParams)
152+
153+
if (drops.isNotEmpty()) {
154+
lastGenerationTime[pokemonId] = now
155+
heldItemsByPokemon[pokemonId] = drops
156+
}
157+
}
158+
159+
/**
160+
* Checks if the Pokémon qualifies as an archeologist because its species is
161+
* explicitly listed in the config.
162+
*/
163+
private fun isDesignatedHarvester(pokemonEntity: PokemonEntity): Boolean {
164+
val speciesName = pokemonEntity.pokemon.species.translatedName.string.lowercase()
165+
return config.archeologists.any { it.lowercase() == speciesName }
166+
}
167+
}

common/src/main/kotlin/accieo/cobbleworkers/jobs/WorkerDispatcher.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ object WorkerDispatcher {
2424
private val workers: List<Worker> = listOf(
2525
ApricornHarvester,
2626
AmethystHarvester,
27+
Archeologist,
2728
BerryHarvester,
2829
BrewingStandFuelGenerator,
2930
CropHarvester,

common/src/main/resources/assets/cobbleworkers/infographic/index.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@
169169
<td class="type-cell"><span class="type-icon" data-type="Water"></span>Water</td>
170170
<td>Pikachu</td>
171171
</tr>
172+
<tr>
173+
<td>Archeologists</td>
174+
<td class="type-cell"><span class="type-icon" data-type="Ground"></span>Ground</td>
175+
<td>Pikachu</td>
176+
</tr>
172177
</tbody>
173178
</table>
174179
<script>
11.4 KB
Loading
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
{
2+
"type": "minecraft:chest",
3+
"pools": [
4+
{
5+
"rolls": 1,
6+
"conditions": [
7+
{
8+
"condition": "minecraft:random_chance",
9+
"chance": 0.35
10+
}
11+
],
12+
"entries": [
13+
{
14+
"type": "minecraft:item",
15+
"name": "minecraft:raw_copper",
16+
"functions": [{ "function": "set_count", "count": { "min": 2, "max": 8 } }]
17+
},
18+
{
19+
"type": "minecraft:item",
20+
"name": "minecraft:raw_iron",
21+
"functions": [{ "function": "set_count", "count": { "min": 1, "max": 4 } }]
22+
},
23+
{
24+
"type": "minecraft:item",
25+
"name": "minecraft:raw_gold",
26+
"functions": [{ "function": "set_count", "count": { "min": 1, "max": 3 } }]
27+
},
28+
{
29+
"type": "minecraft:item",
30+
"name": "minecraft:coal",
31+
"functions": [{ "function": "set_count", "count": { "min": 3, "max": 6 } }]
32+
},
33+
{
34+
"type": "minecraft:item",
35+
"name": "minecraft:charcoal",
36+
"functions": [{ "function": "set_count", "count": { "min": 3, "max": 6 } }]
37+
}
38+
]
39+
},
40+
{
41+
"rolls": 1,
42+
"conditions": [
43+
{
44+
"condition": "minecraft:random_chance",
45+
"chance": 0.45
46+
}
47+
],
48+
"entries": [
49+
{
50+
"type": "minecraft:item",
51+
"name": "minecraft:bone",
52+
"functions": [{ "function": "set_count", "count": { "min": 2, "max": 3 } }]
53+
}
54+
]
55+
},
56+
{
57+
"rolls": 1,
58+
"conditions": [
59+
{
60+
"condition": "minecraft:random_chance",
61+
"chance": 0.05
62+
}
63+
],
64+
"entries": [
65+
{ "type": "minecraft:item", "name": "cobblemon:armor_fossil" },
66+
{ "type": "minecraft:item", "name": "cobblemon:claw_fossil" },
67+
{ "type": "minecraft:item", "name": "cobblemon:cover_fossil" },
68+
{ "type": "minecraft:item", "name": "cobblemon:dome_fossil" },
69+
{ "type": "minecraft:item", "name": "cobblemon:fossilized_bird" },
70+
{ "type": "minecraft:item", "name": "cobblemon:fossilized_dino" },
71+
{ "type": "minecraft:item", "name": "cobblemon:fossilized_drake" },
72+
{ "type": "minecraft:item", "name": "cobblemon:fossilized_fish" },
73+
{ "type": "minecraft:item", "name": "cobblemon:helix_fossil" },
74+
{ "type": "minecraft:item", "name": "cobblemon:jaw_fossil" },
75+
{ "type": "minecraft:item", "name": "cobblemon:old_amber_fossil" },
76+
{ "type": "minecraft:item", "name": "cobblemon:plume_fossil" },
77+
{ "type": "minecraft:item", "name": "cobblemon:root_fossil" },
78+
{ "type": "minecraft:item", "name": "cobblemon:sail_fossil" },
79+
{ "type": "minecraft:item", "name": "cobblemon:skull_fossil" }
80+
]
81+
},
82+
{
83+
"rolls": 1,
84+
"conditions": [
85+
{
86+
"condition": "minecraft:random_chance",
87+
"chance": 0.15
88+
}
89+
],
90+
"entries": [
91+
{ "type": "minecraft:item", "name": "cobblemon:absorb_bulb" },
92+
{ "type": "minecraft:item", "name": "cobblemon:big_root" },
93+
{ "type": "minecraft:item", "name": "cobblemon:black_augurite" },
94+
{ "type": "minecraft:item", "name": "cobblemon:charcoal_stick" },
95+
{ "type": "minecraft:item", "name": "cobblemon:damp_rock" },
96+
{ "type": "minecraft:item", "name": "cobblemon:deep_sea_scale" },
97+
{ "type": "minecraft:item", "name": "cobblemon:deep_sea_tooth" },
98+
{ "type": "minecraft:item", "name": "cobblemon:dragon_fang" },
99+
{ "type": "minecraft:item", "name": "cobblemon:energy_root" },
100+
{ "type": "minecraft:item", "name": "cobblemon:everstone" },
101+
{ "type": "minecraft:item", "name": "cobblemon:fairy_feather" },
102+
{ "type": "minecraft:item", "name": "cobblemon:heat_rock" },
103+
{ "type": "minecraft:item", "name": "cobblemon:ice_stone" },
104+
{ "type": "minecraft:item", "name": "cobblemon:icy_rock" },
105+
{ "type": "minecraft:item", "name": "cobblemon:kings_rock" },
106+
{ "type": "minecraft:item", "name": "cobblemon:leaf_stone" },
107+
{ "type": "minecraft:item", "name": "cobblemon:mental_herb" },
108+
{ "type": "minecraft:item", "name": "cobblemon:mirror_herb" },
109+
{ "type": "minecraft:item", "name": "cobblemon:never_melt_ice" },
110+
{ "type": "minecraft:item", "name": "cobblemon:peat_block" },
111+
{ "type": "minecraft:item", "name": "cobblemon:power_herb" },
112+
{ "type": "minecraft:item", "name": "cobblemon:prism_scale" },
113+
{ "type": "minecraft:item", "name": "cobblemon:razor_claw" },
114+
{ "type": "minecraft:item", "name": "cobblemon:razor_fang" },
115+
{ "type": "minecraft:item", "name": "cobblemon:smooth_rock" },
116+
{ "type": "minecraft:item", "name": "cobblemon:sun_stone" },
117+
{ "type": "minecraft:item", "name": "cobblemon:vivichoke_seeds" },
118+
{ "type": "minecraft:item", "name": "cobblemon:water_stone" },
119+
{ "type": "minecraft:item", "name": "cobblemon:white_herb" }
120+
]
121+
}
122+
]
123+
}

0 commit comments

Comments
 (0)