This function is called only 1 time ever, the first time the End arena is fully loaded. needsStateScanning
is this "first-run" variable.
-
Look for hasActiveExitPortal. This returns
true
if any End Portal block entities exist in a decent region around the origin. The variablepreviouslyKilled
is set to the result of this check.- If the check failed, an initial, closed End Portal will be created too
-
Look for Ender Dragon entities.
- If there are none,
dragonKilled
is set totrue
. - If there are some,
dragonKilled
is set tofalse
and the dragons are removed if an exit portal blockentity was not found in (2). (This would happen if you logged out of your 1.8 world mid-bossfight)
- If there are none,
-
If
!previouslyKilled
, setdragonKilled
tofalse
So in non-legacy worlds, this function
- Spawns a closed exit End portal at 0, 0
- (doesn't find any dragons)
- sets
previouslyKilled
anddragonKilled
tofalse
.
previouslyKilled
determines whether a new Ender Dragon kill will place a Dragon Egg block, and changes XP dropped by dragon
dragonKilled
is a more spicy one - if !dragonKilled
and there's no dragon (defined as dragonUUID == null || ticksSinceLastDragonSeen >= 1200)
, the game will spawn a new one.
fixing issue 21
Probably inject at the return of scanState instead
-
player wants closed end portal: No side effect
-
player wants open end portal: call
spawnExitPortal(true)
-
player wants egg portal: same thing, then add the egg
-
player wants a dragon: No side effect
-
player wants the "passive dragon" thing: i think existing mixins handle this
-
player wants no dragon: Set
dragonKilled = true
all fabrics using the latest fabric-api version on modrinth as of apr 19 2023
- 1.16.5 fabric
- mobs cfg seems to work (was worried about tag rules but it seems to be okay)
- general cfg: angryPiggies did not work
- boss: dragon disabling works (not sure about ai patterns), wither disabling works, elder guardian effect works
- 1.18.2 fabric
- mobs cfg seems to work
- general cfg seems to work
- boss: same
- 1.18.2 forge
- mobs cfg works
- general cfg works
hmm...decided to reload the world in the middle of spawn_gateway effect and it crashed (exitPos is null, set from a shadow of portalLocation)... should test on otther versions toofixed!- 1.19.2 fabric
- mobs cfg works
- tested most of the general cfg it seems to work
- boss stuff works including warden
- 1.19.2 forge
- mobs cfg works
- general cfg works
- boss disabling works
- 1.19.4 fabric:
- mobs cfg works
- general cfg works
- boss disabling works
- 1.19.4 forge:
- mobs cfg works
- general cfg works
- boss disabling works
forge seems to load its configs "too much" (based on the debug prints) but i think it's fine, most of that is not actually hitting the harddisk it's jsut setting values from an event
it's weird but not that complicated
- Make a
ForgeConfigSpec.Builder
, add properties to it, then call.build()
(or use theconfigure
convenience method) - Keep the config spec around. As soon as possible, call
ModLoadingContext.get().registerConfig()
, passing it as an argument - The
ForgeConfigSpec.Builder
will returnConfigValue
/IntValue
/etc objects. Hold on to them too, at runtime they are to be queried for the live config values, and will update without any user intervention. - If you want a notification that the config was refreshed, subscribe to
ModConfigEvent.Loading
andModConfigEvent.Reloading
on the fml java mod loading context mod event bus. The event fires with a reference to the config in question (and I think it's correct to check it's actually one of yours before taking action)
Builder stuff:
- Use
push
to define a new section andpop
to end one. Usecomment
before calling other methods to define a config comment. You can put comments on sections. - For properties, the general case is
ConfigValue<T>
, but you will sometimes get stuff likeBooleanValue
andDoubleValue
etc. They're not actually unboxed. it's weird
getting called from NearestAttackableTargetGoal
which seems to be used by lots and lots entities (slime, creeper, enderman, polarbear, vex, evoker, ghast; to name a few), even though only zombies seem to target players in creative 🤔
it affects zombies, drowned, husks, and zombie villagers, but not piglins/zombified piglins?
it does call Mob.setTarget
selecting the player, apathy's rules kick in and have no effect, so the default fallthroguh behavior of "allow" is invoked. but that's weird, why is it calling setTarget
at all, it's a creative-mode player, in vanilla (untested claim) i don't think it would call settarget on creative players
- zombie#tick 219, super() call
- mob#tick 325, super() call
- livingentity#tick 2086, this.aiStep
- zombie#aiStep 244
- unrelated helmet-sunlight-damage calculation
- super call
- monster#aiStep 50
- swing time, no action time, super call
- mob#aiStep 497
- super call (then item pickup)
- livingentity#aiStep 2299
- jump delay
- packet sync
- position/angle/velocity update
- serverAiStep
- mob#serverAiStep 691
- tick sensing
- tick target selector (goal selector)
- goalselector#tick 100
- buncha stuff but iterates through all goals and calls start() on each one that can be started?
- second one is a WrappedGoal of NearestAttackableTargetGoal
- im not really sure what wrappedgoal is but it seems to independently keep an
isRunning
flag so that the wrapee can't be started when it's already started or stopped when it's already stopped
- nearestattackabletargetgoal#start
- calls mob.setTarget with
this.target
- hey wait a minute why is
this.target
being set
- calls mob.setTarget with
new stacktrace, when does NearestAttackableTargetGoal#setTarget
get called
- blaming "Library source does not match the bytecode" for this but the stacktrace is going straight from
NearestAttackableTargetGoal#start
tosetTarget
different direction: where is the actual "don't target creative mode players" check implemented?
starting in TargetingConditions
:
- eventually it calls
source.canAttack(target)
- Apathy hooks the shit out of this but the super implementation of it calls
target.canBeSeenAsEnemy
- apathy's hooks only ever make it return
false
though...
- apathy's hooks only ever make it return
- that is
!isInvulnerable && canBeSeenByAnyone
, but the implementation inPlayer
adds an additional constraint on!getAbilities().invulnerable
(which is set in creative mode)
RIGHT but, apathy hooks Zombie#canAttack
and i probably should have checked thatSHIT!!! misbracketed ternary!! lmfao ok that was actually easy
setTarget system handles the entity the wither is physically chasing but heads do their own thing
- Periodically calls world(/entityview).getTargets to scan for targets, picks a random one, sets resultant entity ID on trackeddata
- Mitigated in EntityViewMixin by filtering getTargets (fortunately it takes a parameter of the entity doing the targeting)
- Evicts targets if LivingEntity#canTarget becomes false / entity disappears / distance check
- Mitigated in LivingEntityMixin by patching canTarget
Both of those are kinda wide-reaching mixins idk
Idk if the setTarget system is even used. Most attacking stuff happens directly in PhaseManager phases
- HoldingPattern: transitions to StrafePlayer or LandingApproach
- Mitigated in
HoldingPatternMixin
by not transitioning to StrafePlayer if the player can't be targeted
- Mitigated in
- StrafePlayer: This is the one where she shoots a fireball at you
- Mitigated in
StrafePlayerMixin
by transitioning away from this phase if the player can't be targeted
- Mitigated in
- SittingScanning: uses world/entityview.findClosestPlayer to transition to ChargingPlayer
- Mitigated in
SittingScanningMixin
by not transitioning to ChargingPlayer if the player can't be targeted
- Mitigated in
- ChargingPlayer: Vec3d of player position to charge at
- Mitigated by not transitioning to this phase from SittingScanning, the only one that goes to this phase
areas of improvement:
- HoldingPatternMixin could search for the nearest player allowed to be targeted, instead of only the nearest player and discarding them if they can't be targeted
note that SittingAttacking isn't an actual attack phase, she just roars (cute)