Skip to content

Commit

Permalink
Merge pull request #1 from N3ROO/MC_1.16.1_dev
Browse files Browse the repository at this point in the history
Version 1.0.0 for Minecraft 1.16.1
  • Loading branch information
lilgallon authored Jul 31, 2020
2 parents 8240313 + a5a1b36 commit f190915
Show file tree
Hide file tree
Showing 15 changed files with 756 additions and 19 deletions.
Binary file added .github/images/aim_blocks.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/images/aim_mobs.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## Version 1.0.0

**Core:**
- Aim assistance for mobs (while fighting)
- Aim assistance for blocks (while mining)

**Settings:**
- Aim force customizable (0.1 -> 10.0)
- Assistance on mobs (on/off)
- Assistance on blocks (on/off)
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,20 @@
![release](https://img.shields.io/github/release/n3roo/aimassistancemod.svg)
![build](https://img.shields.io/github/workflow/status/N3ROO/aimassistancemod/Build%20MC1.16.1?label=build%201.16.1)

A minecraft mod that helps you aiming (mods, blocks, but not players). Made for newbies or disabled players with difficulties to aim in general.
A minecraft mod that helps aiming (at mods, blocks, but not players). Made for newbies or disabled players with difficulties to aim in general.

![screenshot](.github/images/screenshot.png)
![aim mobs demo](.github/images/aim_mobs.gif)

![aim blocks demo](.github/images/aim_blocks.gif)

## Disclamer

**The mod has been designed to simplify the gameplay of people with disabilities (or newbies)**.

If you play online, make sure that the server allows the use of this mod.
It will only works on mobs anyway. Also, the server owner can easily blacklist
this mod by blacklisting its id ("aimassistancemod"). I am not responsible for any
misuse of this mod. You have been warned.

## Roadmap
Check [github projects](https://github.com/N3ROO/AimAssistanceMod/projects)
If you play online, make sure that the server allows the use of this mod. It won't work on
other players anyway, so it won't give you a PvP advantage. Also, the server owner can easily
blacklist this mod by blacklisting its id ("aimassistancemod"). I am not responsible for any
misuse of this mod.

## Contributing
Don't hesitate to contribute. Even if it's your first time, it's never too late to learn!
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'eclipse'
apply plugin: 'maven-publish'

version = '1.0.0'
version = '1.0.0-MC1.16.1'
group = 'dev.nero.aimassistance' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'aimassistance'

Expand Down Expand Up @@ -43,5 +43,5 @@ minecraft {
}

dependencies {
minecraft 'net.minecraftforge:forge:1.16.1-32.0.75'
minecraft 'net.minecraftforge:forge:1.16.1-32.0.98'
}
44 changes: 38 additions & 6 deletions src/main/java/dev/nero/aimassistance/AimAssistanceMod.java
Original file line number Diff line number Diff line change
@@ -1,39 +1,71 @@
package dev.nero.aimassistance;

import net.minecraft.client.Minecraft;
import dev.nero.aimassistance.config.Config;
import dev.nero.aimassistance.module.AimAssistance;
import dev.nero.aimassistance.utils.Wrapper;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

// The value here should match an entry in the META-INF/mods.toml file
@Mod("aimassistancemod")
@Mod(AimAssistanceMod.MODID)
public class AimAssistanceMod
{
// Directly reference a log4j logger.
private static final Logger LOGGER = LogManager.getLogger();

public static final String MODID = "aimassistancemod";
private AimAssistance aimAssistance;

public AimAssistanceMod() {
// Register the setup method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);

// Register ourselves for server and other game events we are interested in
MinecraftForge.EVENT_BUS.register(this);

// Register config
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.CLIENT_SPEC);
}

private void setup(final FMLCommonSetupEvent event)
{
private void setup(final FMLCommonSetupEvent event) {
LOGGER.info("Init");
aimAssistance = new AimAssistance();
Config.bakeConfig(); // init config values
}

@SubscribeEvent
public void onModConfigEvent(final ModConfig.ModConfigEvent configEvent) {
if (configEvent.getConfig().getSpec() == Config.CLIENT_SPEC) {
Config.bakeConfig(); // update the values
}
}

@SubscribeEvent
public void onPlayerTick(TickEvent.PlayerTickEvent playerTickEvent) {
if (Wrapper.playerPlaying()) {
aimAssistance.analyseBehaviour();
}
}

@SubscribeEvent
public void onClientTick(TickEvent.ClientTickEvent clientTickEvent) {
if (Wrapper.playerPlaying()) {
aimAssistance.analyseEnvironment();
}
}

@SubscribeEvent
public void onRender(TickEvent.RenderTickEvent renderTickEvent) {
if (Minecraft.getInstance().player != null) {
// The user is playing
if (Wrapper.playerPlaying()) {
aimAssistance.assistIfPossible();
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/dev/nero/aimassistance/config/ClientConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.nero.aimassistance.config;

import dev.nero.aimassistance.AimAssistanceMod;
import net.minecraftforge.common.ForgeConfigSpec;

public class ClientConfig {

public final ForgeConfigSpec.DoubleValue aimForce;
public final ForgeConfigSpec.BooleanValue aimMobs;
public final ForgeConfigSpec.BooleanValue aimBlocks;

public ClientConfig(ForgeConfigSpec.Builder builder) {

builder.push("Aim assistance"); // Category

aimForce = builder
.comment("What should be the force of the aim assistance?")
.translation(AimAssistanceMod.MODID + ".config." + "aimForce")
.defineInRange("aimForce", 5.0, 0.1, 10.0);

aimMobs = builder
.comment("Should the aim assistance be activated for mobs?")
.translation(AimAssistanceMod.MODID + ".config." + "aimMobs")
.define("aimMobs", true);

aimBlocks = builder
.comment("Should the aim assistance be activated for blocks?")
.translation(AimAssistanceMod.MODID + ".config." + "aimBlocks")
.define("aimBlocks", true);

builder.pop();
}
}
37 changes: 37 additions & 0 deletions src/main/java/dev/nero/aimassistance/config/Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package dev.nero.aimassistance.config;

import net.minecraftforge.common.ForgeConfigSpec;
import org.apache.commons.lang3.tuple.Pair;

public class Config {

public static final ClientConfig CLIENT;
public static final ForgeConfigSpec CLIENT_SPEC;
static {
final Pair<ClientConfig, ForgeConfigSpec> specPair = new ForgeConfigSpec.Builder().configure(ClientConfig::new);
CLIENT_SPEC = specPair.getRight();
CLIENT = specPair.getLeft();
}

private static double aimForce;
private static boolean aimMobs;
private static boolean aimBlocks;

public static void bakeConfig() {
aimForce = CLIENT.aimForce.get();
aimMobs = CLIENT.aimMobs.get();
aimBlocks = CLIENT.aimBlocks.get();
}

public static double getAimForce() {
return aimForce;
}

public static boolean isAimMobs() {
return aimMobs;
}

public static boolean isAimBlocks() {
return aimBlocks;
}
}
184 changes: 184 additions & 0 deletions src/main/java/dev/nero/aimassistance/module/AimAssistance.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package dev.nero.aimassistance.module;

import dev.nero.aimassistance.config.Config;
import dev.nero.aimassistance.utils.TimeHelper;
import dev.nero.aimassistance.utils.Wrapper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.MobEntity;
import net.minecraft.util.math.BlockRayTraceResult;

import java.util.List;

/**
* These functions should be called periodically. Check their description for details.
* - analyseBehaviour()
* - analyseEnvironment()
* - assistIfPossible()
*/
public class AimAssistance {

// Main attributes
private Target target; // it defines what the assistance will target
private TargetType interactingWith; // defines what the player is interacting with (can be none)

// Used to keep track of the player's behaviour
private TimeHelper interactionTimer = new TimeHelper(); // used to toggle the interaction for a given amount of time
private TimeHelper miningTimer = new TimeHelper();
private TimeHelper attackTimer = new TimeHelper(); // used to calculate the attack speed of the player
private int attackCount = 0; // used to calculate the attack speed of the player
private boolean attackKeyAlreadyPressed = false; // used to handle key press

// Behaviour settings
private final float INTERACTION_ATTACK_SPEED = 1f / 1000f; // (attacks per ms) user faster means user attacking
private final int INTERACTION_ATTACK_DURATION = 3000; // (ms) duration after which we give up
private final int INTERACTION_MINING_DURATION = 400; // (ms) duration the player needs to be mining to assist
private final int INTERACTION_DURATION = 500; // (ms) duration during which the assistance will assist (i'm a poet)
private final float RANGE_TO_SCAN = 5; // (blocks) range to scan from the player to find entities
private final Class ENTITY_TYPE_TO_SCAN = MobEntity.class; // defines the type of entity to scan
private final float BLOCK_REACH = 7; // (blocks) reach to find blocks (lower than default -> ignored)

// Assistance settings
// (IS IN CONFIG CLASS) private final float FORCE = 5; // force of the assistance
private final float FOV = 60; // field of view

/**
* Inits attributes
*/
public AimAssistance() {
this.target = Target.NULL_TARGET;
this.interactingWith = TargetType.NONE;
}

/**
* This function analyses the player's environment to know what they're aiming at.
*/
public void analyseEnvironment() {
if (!Wrapper.playerPlaying()) return;

// idea: Optimization: only perform the analysis when the player is interacting, and do it once, then start a
// timer to update the target if needed: (if the player is fighting a lot of mobs, they will still be fighting,
// but the target won't be the same anymore)

switch (this.interactingWith) {
case ENTITY:
// Get all entities around the player
List<Entity> entities = Wrapper.getEntitiesAroundPlayer(this.RANGE_TO_SCAN, this.ENTITY_TYPE_TO_SCAN);

// Get the closest one to the crosshair
Entity closest = Wrapper.getClosestEntityToCrosshair(entities);

if (closest != null) {
this.target = new Target(closest);
}

break;

case BLOCK:
// Check what block the player is aiming at
BlockRayTraceResult target = Wrapper.getPointedBlock(this.BLOCK_REACH);

if (target != null) {
this.target = new Target(target);
}

break;
}
}

/**
* This function analyzes the player's behaviour to know if the aim assistance should be turned on or not. It should
* be called (at least) at every game tick because it uses input events (attack key information).
*/
public void analyseBehaviour() {
if (!Wrapper.playerPlaying()) return;

// MINING SECTION (Block)

// If the player wasn't doing anything, and is pressing the attack key (same as mining), then start the timer
if (this.miningTimer.isStopped() && Wrapper.attackKeyPressed() && Config.isAimBlocks()) {
this.miningTimer.start();
}
// Else (means that the player is mining) if the player stopped mining during the timer, then stop it
else if (!this.miningTimer.isDelayComplete(this.INTERACTION_MINING_DURATION) && !Wrapper.attackKeyPressed()) {
this.miningTimer.stop();
}
// Else (means that the player is mining) if the player has been mining for the given duration, then they're
// interacting
else if (this.miningTimer.isDelayComplete(this.INTERACTION_MINING_DURATION) && Wrapper.attackKeyPressed()) {
this.miningTimer.stop();
this.interactionTimer.start(); // it will reset if already started, so we're all good
this.interactingWith = TargetType.BLOCK;
}

// ATTACK SECTION (Entity)

// Event handling (convert "keyDown" to "isPressed". Minecraft has one built-in but using it may break some
// code in the backend)
boolean playerAttacks = false;
if (this.attackKeyAlreadyPressed && !Wrapper.attackKeyPressed()) {
this.attackKeyAlreadyPressed = false;
} else if (!this.attackKeyAlreadyPressed && Wrapper.attackKeyPressed()) {
playerAttacks = true;
this.attackKeyAlreadyPressed = true;
}

// First time that the player attacks
if (this.attackCount == 0 && playerAttacks && Config.isAimMobs()) {
this.attackCount += 1;
this.attackTimer.start();
}
// If it's not the first time that the player attacked
else if (this.attackCount > 0 && playerAttacks) {
this.attackCount += 1;

// Calculate the number of attacks per miliseconds
float speed = (float) this.attackCount / (float) this.attackTimer.getTimeElapsed();

// If player's attack speed is greater than the speed given to toggle the assistance, then we can tell to
// the instance that the player is interacting
if (speed > this.INTERACTION_ATTACK_SPEED) {
// We need to reset the variables that are used to define if the player is interacting because we know
// that the user is interacting right now
this.attackCount = 0;
this.attackTimer.stop();

this.interactionTimer.start(); // it will reset if already started, so we're all good
this.interactingWith = TargetType.ENTITY;
}
}
// If the player did not attack for that period of time, we give up and reset everything
else if (this.attackTimer.isDelayComplete(INTERACTION_ATTACK_DURATION)) {
this.attackTimer.stop();
this.attackCount = 0;
}

// COMMON SECTION

// Stop the interaction once that the delay is reached
if (this.interactingWith != TargetType.NONE
&& this.interactionTimer.isDelayComplete(this.INTERACTION_DURATION)) {
this.target = Target.NULL_TARGET;
this.interactingWith = TargetType.NONE;
this.interactionTimer.stop();
}
}

/**
* This function will move the player's crosshair. The faster this function is called, the smoother the aim
* assistance is.
*/
public void assistIfPossible() {
if (!Wrapper.playerPlaying()) return;

// Assist the player by taking into account this.target, only if this.isInteracting is true
if (this.interactingWith != TargetType.NONE && this.target.getType() != TargetType.NONE) {
final float[] rotations = Wrapper.getRotationsNeeded(
target,
FOV, FOV,
(float) Config.getAimForce(), (float) Config.getAimForce() // forceX, forceY
);

Wrapper.setRotations(rotations[0], rotations[1]);
}
}
}
Loading

0 comments on commit f190915

Please sign in to comment.