Skip to content

Commit

Permalink
Update to newer OfflinePlayerCache & UI Updates (along with mixin cha…
Browse files Browse the repository at this point in the history
…nges) (#19)

* Implement new OPC

* Rewrote `AbstractArrowMixin` & `ExperienceOrbMixin`

* Adjust math in `AbstractArrowMixin`

* Nitpick mixins, remove `ServerLevelMixin`

* Nitpick `ServerPlayerMixin`

* Add CL, tweak `AbstractArrowMixin`

* Redo menu logic

* Step version -> alpha.8

* Add ordering to UI to have certain elements display first

* Added in proper type for screen

* Forgot a `ResourceLocation`

* Make API funcs @JvmStatic

* Step up OPC, JvmFields/JvmStatics

* Change workflow
  • Loading branch information
bibi-reden authored Aug 2, 2024
1 parent 5914ffe commit 5f5617e
Show file tree
Hide file tree
Showing 32 changed files with 204 additions and 176 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
if: github.ref == 'refs/heads/${{ env.BRANCH_PREFIX }}'
id: minecraft_version
run: |
minecraft_version_val=$(grep "mod_version" gradle.properties | cut -d'+' -f2 | tr -d '\n')
minecraft_version_val=$(grep "mod_version" gradle.properties | cut -d'=' -f2 | cut -d'-' -f1 | tr -d '\n')
echo "minecraft_version=$minecraft_version_val" >> $GITHUB_OUTPUT
- name: deploy with mc-publish
if: github.ref == 'refs/heads/${{ env.BRANCH_PREFIX }}'
Expand Down
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
## Fixes & Changes ⚒️
- Fixed issues with `alpha.5` having an outdated variant of the previous.
- Migrated to mojmappings.
## Changes ⚒️
- Updated to a newer build of `OfflinePlayerCache`.
- Changed some more internals.
- Reimplemented original calculation of projectile critical chance.
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ parchment_version=1.20.1:2023.09.03
quilt_mappings_version=23

# Mod Properties
mod_version=4.0.0-alpha.7+1.20.1
mod_version=4.0.0+1.20.1-alpha.8
maven_group=com.bibireden.playerex
archives_base_name=playerex-directors-cut

Expand All @@ -30,7 +30,7 @@ placeholder_api_version=2.1.1+1.20
ranged_weapon_api_version=1.1.2+1.20.1

# in-house
opc_version=1.0.0+1.20.1-fabric
opc_version=2.0.0+1.20.1-beta.1-fabric
data_attributes_version=2.0.0-beta.7+1.20.1-fabric

# owo
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,67 @@
package com.bibireden.playerex.registry;

import com.bibireden.playerex.ui.components.MenuComponent;
import com.bibireden.playerex.ui.PlayerEXScreen;
import kotlin.Pair;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
* Used to register {@link MenuComponent}'s to the {@link PlayerEXScreen}.
* This allows you to build a UI layer with PlayerEX
* and access component data for the {@link LocalPlayer}.
*/
public final class PlayerEXMenuRegistry {
@NotNull
private static final List<Class<? extends MenuComponent>> ENTRIES = new ArrayList<>();
private static final List<Pair<ResourceLocation, Class<? extends MenuComponent>>> ENTRIES = new ArrayList<>();

@NotNull
private static final HashMap<String, Integer> PRIORITY_ORDER = new HashMap<>();

/**
* Registers a {@link MenuComponent} to the registry,
* which will be applied to the {@link com.bibireden.playerex.ui.PlayerEXScreen} as a page.
* which will be applied to the {@link PlayerEXScreen} as a page.
*/
public static void register(@NotNull Class<? extends MenuComponent> menu) { ENTRIES.add(menu); }
public static void register(ResourceLocation id, @NotNull Class<? extends MenuComponent> menu) {
Integer position = PRIORITY_ORDER.get(id.getNamespace());
if (position != null) {
for (int i = 0; i < ENTRIES.size(); i++) {
ResourceLocation entryId = ENTRIES.get(i).getFirst();
if (PRIORITY_ORDER.get(entryId.getNamespace()) >= position) {
position = i + 1;
}
}
ENTRIES.add(position, new Pair<>(id, menu));
}
else {
ENTRIES.add(new Pair<>(id, menu));
}
}

@NotNull
public static List<Class<? extends MenuComponent>> get() {
public static List<Pair<ResourceLocation, Class<? extends MenuComponent>>> get() {
return ENTRIES;
}

@NotNull
public static List<ResourceLocation> getIds() {
return ENTRIES.stream().map(Pair::component1).toList();
}

@NotNull
public static List<Class<? extends MenuComponent>> getDefs() {
return ENTRIES.stream().map(Pair::component2).collect(Collectors.toUnmodifiableList());
}

static {
PRIORITY_ORDER.put("playerex", 0);
PRIORITY_ORDER.put("relicex", 1);
PRIORITY_ORDER.put("wizardex", 2);
}
}
7 changes: 3 additions & 4 deletions src/client/kotlin/com/bibireden/playerex/PlayerEXClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,13 @@ object PlayerEXClient : ClientModInitializer {
}
}

PlayerEXMenuRegistry.register(PlayerEXAttributesMenu::class.java)
PlayerEXMenuRegistry.register(PlayerEX.id("attributes"), PlayerEXAttributesMenu::class.java)

ClientTickEvents.END_CLIENT_TICK.register { client ->
if (PlayerEX.CONFIG.disableUI) return@register

while (KEYBINDING_MAIN_SCREEN.consumeClick()) {
if (client.screen == null) {
client.setScreen(PlayerEXScreen())
}
if (client.screen == null) client.setScreen(PlayerEXScreen())
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/client/kotlin/com/bibireden/playerex/ui/PlayerEXScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@ class PlayerEXScreen : BaseUIModelScreen<FlowLayout>(FlowLayout::class.java, Dat
val nextPage = rootComponent.childById(ButtonComponent::class, "next")!!
val exit = rootComponent.childById(ButtonComponent::class, "exit")!!

PlayerEXMenuRegistry.get().forEach {
val instance = it.getDeclaredConstructor().newInstance()
instance.build(player, this.uiAdapter, player.data)
PlayerEXMenuRegistry.get().forEach { (_, clazz) ->
val instance = clazz.getDeclaredConstructor().newInstance()
instance.init(minecraft!!, this, player.data)
instance.build(content)
pages.add(instance)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ package com.bibireden.playerex.ui.components
import com.bibireden.playerex.components.player.IPlayerDataComponent
import com.bibireden.playerex.ui.PlayerEXScreen
import io.wispforest.owo.ui.container.FlowLayout
import io.wispforest.owo.ui.core.OwoUIAdapter
import io.wispforest.owo.ui.core.Sizing
import io.wispforest.owo.util.EventSource
import io.wispforest.owo.util.EventStream
import net.minecraft.client.player.LocalPlayer
import net.minecraft.client.Minecraft
import net.minecraft.world.entity.ai.attributes.Attribute
import org.jetbrains.annotations.ApiStatus

Expand All @@ -19,20 +18,31 @@ import org.jetbrains.annotations.ApiStatus
*/
@ApiStatus.OverrideOnly
abstract class MenuComponent(horizontalSizing: Sizing = Sizing.fill(100), verticalSizing: Sizing = Sizing.fill(100), algorithm: Algorithm) : FlowLayout(horizontalSizing, verticalSizing, algorithm) {
protected var client: Minecraft? = null
protected var screen: PlayerEXScreen? = null
protected var playerComponent: IPlayerDataComponent? = null

val onLevelUpdatedEvents = OnLevelUpdated.stream
val onAttributeUpdatedEvents = OnAttributeUpdated.stream

val onLevelUpdated: EventSource<OnLevelUpdated> = onLevelUpdatedEvents.source()
val onAttributeUpdated: EventSource<OnAttributeUpdated> = onAttributeUpdatedEvents.source()

/** When the [PlayerEXScreen] is ready to be constructed, this function (if the component is registered) will be called.*/
abstract fun build(player: LocalPlayer, adapter: OwoUIAdapter<FlowLayout>, component: IPlayerDataComponent)
/** Initializes this menu. Normally is called around the time it is mounted onto the [PlayerEXScreen]. */
fun init(minecraft: Minecraft, screen: PlayerEXScreen, component: IPlayerDataComponent) {
this.client = minecraft
this.screen = screen
this.playerComponent = component
}

/** Where ui-based logic should occur at, built off of the root of the screen's provided content area. */
abstract fun build(rootComponent: FlowLayout)

fun interface OnLevelUpdated {
fun onLevelUpdated(level: Int)

companion object {
val stream: EventStream<OnLevelUpdated> get() = EventStream { subscribers ->
val stream: EventStream<OnLevelUpdated> = EventStream { subscribers ->
OnLevelUpdated { level -> subscribers.forEach { it.onLevelUpdated(level) } }
}
}
Expand Down
14 changes: 0 additions & 14 deletions src/client/kotlin/com/bibireden/playerex/ui/menus/ConceptMenu.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,12 @@ import io.wispforest.owo.ui.container.Containers
import io.wispforest.owo.ui.container.FlowLayout
import io.wispforest.owo.ui.core.*
import net.fabric_extras.ranged_weapon.api.EntityAttributes_RangedWeapon
import net.minecraft.client.player.LocalPlayer
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.entity.player.Player
import net.minecraft.network.chat.Component
import org.jetbrains.annotations.ApiStatus

// todo: cache buttons/certain UI elements

@ApiStatus.Internal
class PlayerEXAttributesMenu : MenuComponent(algorithm = Algorithm.HORIZONTAL) {
private val MELEE_COMBAT_STATS: List<Pair<EntityAttributeSupplier, FormattingPredicate>> = listOf(
Expand All @@ -41,7 +38,7 @@ class PlayerEXAttributesMenu : MenuComponent(algorithm = Algorithm.HORIZONTAL) {
private val RANGED_COMBAT_STATS: List<Pair<EntityAttributeSupplier, FormattingPredicate>> = listOf(
EntityAttributeSupplier(PlayerEXAttributes.RANGED_CRITICAL_DAMAGE.id) to FormattingPredicates.PERCENTAGE_MULTIPLY,
EntityAttributeSupplier(PlayerEXAttributes.RANGED_CRITICAL_CHANCE.id) to FormattingPredicates.PERCENTAGE_MULTIPLY,
EntityAttributeSupplier(EntityAttributes_RangedWeapon.HASTE.id) to FormattingPredicates.fromBaseValue(EntityAttributes_RangedWeapon.HASTE.attribute),
EntityAttributeSupplier(EntityAttributes_RangedWeapon.HASTE.id) to FormattingPredicates.fromBaseValue(EntityAttributes_RangedWeapon.HASTE.attribute, true),
EntityAttributeSupplier(EntityAttributes_RangedWeapon.DAMAGE.id) to FormattingPredicates.NORMAL,
)

Expand Down Expand Up @@ -73,18 +70,11 @@ class PlayerEXAttributesMenu : MenuComponent(algorithm = Algorithm.HORIZONTAL) {
private fun onAttributeUpdate() {
// refresh all attribute labels
this.forEachDescendant { component ->
// todo: use derived interface to check instance
if (component is AttributeComponent) {
component.refresh()
return@forEachDescendant
}
if (component is AttributeLabelComponent) {
component.refresh()
return@forEachDescendant
}
if (component is AttributeListEntryComponent) {
component.refresh()
return@forEachDescendant
// a bit more tolerable
when (component) {
is AttributeComponent -> component.refresh()
is AttributeLabelComponent -> component.refresh()
is AttributeListEntryComponent -> component.refresh()
}
}
}
Expand All @@ -106,7 +96,10 @@ class PlayerEXAttributesMenu : MenuComponent(algorithm = Algorithm.HORIZONTAL) {

}

override fun build(player: LocalPlayer, adapter: OwoUIAdapter<FlowLayout>, component: IPlayerDataComponent) {
override fun build(rootComponent: FlowLayout) {
val player = client?.player ?: return
val component = playerComponent ?: return

child(Containers.verticalScroll(
Sizing.fill(45),
Sizing.fill(100),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ import net.minecraft.world.entity.ai.attributes.Attribute
import kotlin.math.round

object FormattingPredicates {
@JvmField
val NORMAL: FormattingPredicate = { "%.2f".format(it) }
val PERCENT: FormattingPredicate = { "${it.toInt()}%" }
@JvmField
val PERCENTAGE_MULTIPLY: FormattingPredicate = { "${(it * 100.0).toInt()}%" }
@JvmField
val PERCENTAGE_DIVIDE: FormattingPredicate = { "${(it / 100.0).toInt()}%" }

fun fromBaseValue(attribute: Attribute): FormattingPredicate {
/**
* Bases calculation and positive/negative evaluation using the [Attribute]'s base as a metric.
*
* For example, if an attribute's base value was 100,
* and the current value of a player's attribute was 150, it is a `+50` increase.
* */
@JvmStatic
fun fromBaseValue(attribute: Attribute, percentage: Boolean): FormattingPredicate {
return {
val result = round(it - attribute.defaultValue).toInt()
var text = "$result"
if (result > 0) text = "+$result"
text + "%"
if (percentage) text += "%"
text
}
}
}
65 changes: 25 additions & 40 deletions src/main/java/com/bibireden/playerex/mixin/AbstractArrowMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,58 +22,43 @@
public abstract class AbstractArrowMixin extends Projectile {
@Shadow public abstract void setCritArrow(boolean critical);

// Constructor for the mixin class
@Shadow public abstract boolean isCritArrow();

private AbstractArrowMixin(EntityType<? extends Projectile> entityType, Level level) {
super(entityType, level);
}

@SuppressWarnings("UnreachableCode")
@Inject(method = "onHitEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/projectile/AbstractArrow;isCritArrow()Z"))
private void playerex_onEntityHit(EntityHitResult entityHitResult, CallbackInfo info) {
AbstractArrow projectileEntity = (AbstractArrow) (Object) this;
Entity entity = projectileEntity.getOwner();

if (entity instanceof LivingEntity) {

Optional<Double> rangedCritChanceOptional = DataAttributesAPI.getValue(PlayerEXAttributes.RANGED_CRITICAL_CHANCE, (LivingEntity) entity);

if (rangedCritChanceOptional.isPresent()) {
projectileEntity.setCritArrow(false);
}
if (this.getOwner() instanceof LivingEntity owner) {
DataAttributesAPI.getValue(PlayerEXAttributes.RANGED_CRITICAL_CHANCE, owner).ifPresent((chance) -> this.setCritArrow(false));
}
private void playerex$onEntityHit(EntityHitResult entityHitResult, CallbackInfo info) {
if (this.getOwner() instanceof LivingEntity entity) {
DataAttributesAPI.getValue(PlayerEXAttributes.RANGED_CRITICAL_CHANCE, entity).ifPresent((chance) ->
this.setCritArrow(false)
);
}
}

@SuppressWarnings("UnreachableCode")
@ModifyArg(method = "onHitEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;hurt(Lnet/minecraft/world/damagesource/DamageSource;F)Z"))
private float playerex_onEntityHit(float i) {
AbstractArrow projectileEntity = (AbstractArrow)(Object) this;
Entity owner = projectileEntity.getOwner();
double damage = i;
private float playerex$onEntityHit(float original) {
if (this.getOwner() instanceof LivingEntity entity) {
final float damage = original;

if(owner instanceof LivingEntity livingEntity) {
final double amount = damage;

Optional<Double> rangedCritOptional = DataAttributesAPI.getValue(PlayerEXAttributes.RANGED_CRITICAL_CHANCE, livingEntity);

if (rangedCritOptional.isPresent())
{
boolean cache = livingEntity.getRandom().nextFloat() < rangedCritOptional.get();
projectileEntity.setCritArrow(cache);

if (cache)
{
Optional<Double> rangedCritDamageOptional = DataAttributesAPI.getValue(PlayerEXAttributes.RANGED_CRITICAL_DAMAGE, livingEntity);
if (rangedCritOptional.isPresent())
{
damage = amount * (1.0 + (10.0 * rangedCritDamageOptional.get()));
}
boolean isCritical = DataAttributesAPI.getValue(PlayerEXAttributes.RANGED_CRITICAL_CHANCE, entity)
.map((chance) -> {
boolean shouldCritical = entity.getRandom().nextFloat() < chance;
this.setCritArrow(shouldCritical);
return shouldCritical;
}
).orElse(this.isCritArrow());

if (isCritical) {
return DataAttributesAPI.getValue(PlayerEXAttributes.RANGED_CRITICAL_DAMAGE, entity)
.map((v) -> (float) (damage * (1.0 + (10.0 * v))))
.orElseGet(() -> {
final long offset = this.random.nextInt(Math.round(original) / 2 + 2);
return Math.min(offset + original, Integer.MAX_VALUE);
});
}
}

return (float) damage;
return original;
}
}
Loading

0 comments on commit 5f5617e

Please sign in to comment.