Skip to content

Commit

Permalink
blaster ADS zoom via entity attr components
Browse files Browse the repository at this point in the history
  • Loading branch information
parzivail committed Oct 21, 2024
1 parent c860a84 commit df29620
Show file tree
Hide file tree
Showing 14 changed files with 271 additions and 16 deletions.
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ maven_group=dev.pswg
archives_base_name=pswg

# https://fabricmc.net/develop/
minecraft_version=1.21.2-rc1
yarn_mappings=1.21.2-rc1+build.1
minecraft_version=1.21.2-rc2
yarn_mappings=1.21.2-rc2+build.1
loader_version=0.16.7
# Fabric API
fabric_version=0.106.0+1.21.2
fabric_version=0.106.1+1.21.2
39 changes: 27 additions & 12 deletions projects/pswg_blasters/src/main/java/dev/pswg/item/BlasterItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.mojang.serialization.Codec;
import dev.pswg.Blasters;
import dev.pswg.attributes.AttributeUtil;
import dev.pswg.attributes.GalaxiesEntityAttributes;
import dev.pswg.world.TickConstants;
import net.minecraft.block.BlockState;
import net.minecraft.component.ComponentType;
Expand Down Expand Up @@ -42,13 +44,13 @@ public class BlasterItem extends Item
);

/**
* The attribute modifier that is applied to the {@link EntityAttributes#MOVEMENT_SPEED}
* attribute in players when they are not aiming-down-sights.
* The attribute modifier that is applied to the {@link GalaxiesEntityAttributes#FIELD_OF_VIEW_ZOOM}
* attribute in players when they are aiming-down-sights.
*/
protected static final EntityAttributeModifier ATTR_MODIFIER_AIMING_SPEED_PENALTY_DISABLED = new EntityAttributeModifier(
Blasters.id("aiming_speed_penalty"),
0,
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
protected static final EntityAttributeModifier ATTR_MODIFIER_AIMING_FOV_ENABLED = new EntityAttributeModifier(
Blasters.id("aiming_zoom"),
2,
EntityAttributeModifier.Operation.ADD_MULTIPLIED_BASE
);

/**
Expand Down Expand Up @@ -88,11 +90,19 @@ public static void setAiming(ItemStack stack, boolean aiming)
{
stack.set(IS_AIMING, aiming);
var attrs = stack.getOrDefault(DataComponentTypes.ATTRIBUTE_MODIFIERS, AttributeModifiersComponent.DEFAULT);
stack.set(DataComponentTypes.ATTRIBUTE_MODIFIERS, attrs.with(
EntityAttributes.MOVEMENT_SPEED,
aiming ? ATTR_MODIFIER_AIMING_SPEED_PENALTY_ENABLED : ATTR_MODIFIER_AIMING_SPEED_PENALTY_DISABLED,
AttributeModifierSlot.HAND)
);

if (aiming)
{
attrs = attrs.with(EntityAttributes.MOVEMENT_SPEED, ATTR_MODIFIER_AIMING_SPEED_PENALTY_ENABLED, AttributeModifierSlot.HAND);
attrs = attrs.with(GalaxiesEntityAttributes.FIELD_OF_VIEW_ZOOM, ATTR_MODIFIER_AIMING_FOV_ENABLED, AttributeModifierSlot.HAND);
}
else
{
attrs = AttributeUtil.without(attrs, EntityAttributes.MOVEMENT_SPEED, ATTR_MODIFIER_AIMING_SPEED_PENALTY_ENABLED);
attrs = AttributeUtil.without(attrs, GalaxiesEntityAttributes.FIELD_OF_VIEW_ZOOM, ATTR_MODIFIER_AIMING_FOV_ENABLED);
}

stack.set(DataComponentTypes.ATTRIBUTE_MODIFIERS, attrs);
}

@Override
Expand Down Expand Up @@ -130,10 +140,15 @@ public ActionResult use(World world, PlayerEntity user, Hand hand)
{
var stack = user.getStackInHand(hand);

// TODO: hold-ADS not working
if (!world.isClient)
{
setAiming(stack, !isAiming(stack));

// this is required to "start using" the item instead of
// immediately consuming it.
user.setCurrentHand(hand);

return ActionResult.CONSUME;
}

return ActionResult.FAIL;
Expand Down
4 changes: 4 additions & 0 deletions projects/pswg_core/project.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ loom {
}
}
}

fabricApi {
configureDataGeneration()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package dev.pswg.mixin.client;

import dev.pswg.attributes.GalaxiesEntityAttributes;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;

@Mixin(AbstractClientPlayerEntity.class)
public abstract class AbstractClientPlayerEntityMixin
{
/**
* Append our field of view changes to the client player's field of view calculations
*
* @param fieldOfView The client's current field of view
*
* @return The modified value of the client's field of view
*/
@ModifyArg(method = "Lnet/minecraft/client/network/AbstractClientPlayerEntity;getFovMultiplier(ZF)F", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerp(FFF)F"), index = 2)
public float getFovMultiplier(float fieldOfView)
{
var self = (PlayerEntity)(Object)this;
return fieldOfView / (float)self.getAttributeValue(GalaxiesEntityAttributes.FIELD_OF_VIEW_ZOOM);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.pswg.mixin.client;

import dev.pswg.attributes.GalaxiesEntityAttributes;
import net.minecraft.component.type.AttributeModifiersComponent;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.function.Consumer;

@Mixin(ItemStack.class)
public abstract class ItemStackMixin
{
@Inject(method = "appendAttributeModifierTooltip(Ljava/util/function/Consumer;Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/registry/entry/RegistryEntry;Lnet/minecraft/entity/attribute/EntityAttributeModifier;)V", at = @At("HEAD"), cancellable = true)
public void appendAttributeModifierTooltip(Consumer<Text> textConsumer, PlayerEntity player, RegistryEntry<EntityAttribute> attribute, EntityAttributeModifier modifier, CallbackInfo ci)
{
if (attribute.matchesId(GalaxiesEntityAttributes.FIELD_OF_VIEW_ZOOM_ID))
{
var d = modifier.value();

textConsumer.accept(Text.translatable(GalaxiesEntityAttributes.I18N_ATTR_MULTIPLIER, AttributeModifiersComponent.DECIMAL_FORMAT.format(d), Text.translatable(attribute.value().getTranslationKey())).formatted(attribute.value().getFormatting(true)));

ci.cancel();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"package": "dev.pswg.mixin.client",
"compatibilityLevel": "JAVA_21",
"client": [
"DrawContextAccessor"
"AbstractClientPlayerEntityMixin",
"DrawContextAccessor",
"ItemStackMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"pswg.attribute.modifier.multiplier": "%sx %s",
"pswg.attribute.name.field_of_view_zoom": "Zoom"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dev.pswg.attributes;

import com.google.common.collect.ImmutableList;
import net.minecraft.component.type.AttributeModifiersComponent;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.entity.attribute.EntityAttributeModifier;
import net.minecraft.registry.entry.RegistryEntry;

/**
* A collection of utilities related to entity attributes
*/
public final class AttributeUtil
{
/**
* Removes a given attribute modifier from the given attribute modifiers component
*
* @param base The component from which the modifier will be removed
* @param attribute The attribute from which the modifier will be removed
* @param modifier The modifier that will be removed
*
* @return The component without the given modifier
*/
public static AttributeModifiersComponent without(AttributeModifiersComponent base, RegistryEntry<EntityAttribute> attribute, EntityAttributeModifier modifier)
{
ImmutableList.Builder<AttributeModifiersComponent.Entry> builder = ImmutableList.builderWithExpectedSize(base.modifiers().size() + 1);

for (AttributeModifiersComponent.Entry entry : base.modifiers())
{
if (!entry.matches(attribute, modifier.id()))
{
builder.add(entry);
}
}

return new AttributeModifiersComponent(builder.build(), base.showInTooltip());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.pswg.attributes;

import dev.pswg.Galaxies;
import net.minecraft.entity.attribute.EntityAttribute;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;

import java.util.function.Function;

/**
* A collection of common entity attributes used in PSWG modules and addons
*/
public final class GalaxiesEntityAttributes
{
public static final String I18N_ATTR_MULTIPLIER = "pswg.attribute.modifier.multiplier";

/**
* The ID of the {@link GalaxiesEntityAttributes#FIELD_OF_VIEW_ZOOM} attribute modifier
*/
public static final Identifier FIELD_OF_VIEW_ZOOM_ID = Galaxies.id("field_of_view_zoom");

/**
* An entity attribute that can modify the entity's field of view. Units
* are zoom multipliers (e.g. 2x, 5x, 10x zoom) and not field of view angle
* multipliers.
*/
public static final RegistryEntry<EntityAttribute> FIELD_OF_VIEW_ZOOM = register(
FIELD_OF_VIEW_ZOOM_ID,
(translationKey) -> new UnclampedEntityAttribute(translationKey, 1).setTracked(true)
);

/**
* Registers a new entity attribute with a specific ID.
*
* @param id The ID of the entity attribute to register
* @param attributeConstructor A function to construct the entity attribute using a translation key derived from the ID
*
* @return The registered entity attribute entry
*/
public static RegistryEntry<EntityAttribute> register(Identifier id, Function<String, EntityAttribute> attributeConstructor)
{
return Registry.registerReference(Registries.ATTRIBUTE, id, attributeConstructor.apply("%s.attribute.name.%s".formatted(id.getNamespace(), id.getPath())));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.pswg.attributes;

import net.minecraft.entity.attribute.EntityAttribute;

/**
* Represents an attribute for an entity that is not clamped
* to upper and lower bounds
*/
public class UnclampedEntityAttribute extends EntityAttribute
{
protected UnclampedEntityAttribute(String translationKey, double fallback)
{
super(translationKey, fallback);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dev.pswg.datagen;

import dev.pswg.attributes.GalaxiesEntityAttributes;
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider;
import net.minecraft.registry.RegistryWrapper;

import java.util.concurrent.CompletableFuture;

/**
* The base data generator
*/
public class GalaxiesDataGenerator implements DataGeneratorEntrypoint
{
@Override
public void onInitializeDataGenerator(FabricDataGenerator generator)
{
var pack = generator.createPack();

pack.addProvider(LangGenerator::new);
}

/**
* The base language file generator. All language entries should be
* added through this generator.
*/
private static class LangGenerator extends FabricLanguageProvider
{
protected LangGenerator(FabricDataOutput dataOutput, CompletableFuture<RegistryWrapper.WrapperLookup> registryLookup)
{
super(dataOutput, "en_us", registryLookup);
}

@Override
public void generateTranslations(RegistryWrapper.WrapperLookup registryLookup, TranslationBuilder translationBuilder)
{
translationBuilder.add(GalaxiesEntityAttributes.I18N_ATTR_MULTIPLIER, "%sx %s");

translationBuilder.add(GalaxiesEntityAttributes.FIELD_OF_VIEW_ZOOM, "Zoom");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.pswg.mixin;

import dev.pswg.attributes.GalaxiesEntityAttributes;
import net.minecraft.entity.attribute.DefaultAttributeContainer;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

@Mixin(PlayerEntity.class)
public class PlayerEntityMixin
{
/**
* Appends our custom attributes to the player's attribute builder
*
* @param cir The builder container
*/
@Inject(method = "createPlayerAttributes", at = @At("RETURN"))
private static void modifyPlayerAttributes(CallbackInfoReturnable<DefaultAttributeContainer.Builder> cir)
{
DefaultAttributeContainer.Builder builder = cir.getReturnValue();
builder.add(GalaxiesEntityAttributes.FIELD_OF_VIEW_ZOOM);
}
}
3 changes: 3 additions & 0 deletions projects/pswg_core/src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
],
"client": [
"dev.pswg.GalaxiesClient"
],
"fabric-datagen": [
"dev.pswg.datagen.GalaxiesDataGenerator"
]
},
"mixins": [
Expand Down
1 change: 1 addition & 0 deletions projects/pswg_core/src/main/resources/pswg.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"package": "dev.pswg.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"PlayerEntityMixin"
],
"injectors": {
"defaultRequire": 1
Expand Down

0 comments on commit df29620

Please sign in to comment.