Added first version.

This commit is contained in:
Andrew Lalis 2021-07-18 22:35:27 +02:00
parent 693fb2c717
commit c00b2444bd
8 changed files with 208 additions and 48 deletions

View File

@ -1,9 +1,3 @@
# Fabric Example Mod # Speed Carts
## Setup This mod adds the ability to control minecarts with signs.
For setup instructions please see the [fabric wiki page](https://fabricmc.net/wiki/tutorial:setup) that relates to the IDE that you are using.
## License
This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects.

View File

@ -9,8 +9,8 @@ org.gradle.jvmargs=-Xmx1G
# Mod Properties # Mod Properties
mod_version = 1.0.0 mod_version = 1.0.0
maven_group = com.example maven_group = nl.andrewlalis
archives_base_name = fabric-example-mod archives_base_name = speed-carts
# Dependencies # Dependencies
fabric_version=0.37.0+1.17 fabric_version=0.37.0+1.17

View File

@ -1,14 +0,0 @@
package net.fabricmc.example;
import net.fabricmc.api.ModInitializer;
public class ExampleMod implements ModInitializer {
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
System.out.println("Hello Fabric world!");
}
}

View File

@ -1,15 +0,0 @@
package net.fabricmc.example.mixin;
import net.minecraft.client.gui.screen.TitleScreen;
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;
@Mixin(TitleScreen.class)
public class ExampleMixin {
@Inject(at = @At("HEAD"), method = "init()V")
private void init(CallbackInfo info) {
System.out.println("This line is printed by an example mod mixin!");
}
}

View File

@ -0,0 +1,10 @@
package nl.andrewlalis.speed_carts;
import net.fabricmc.api.ModInitializer;
public class SpeedCarts implements ModInitializer {
@Override
public void onInitialize() {
System.out.println("Speed Carts initialized.");
}
}

View File

@ -0,0 +1,181 @@
package nl.andrewlalis.speed_carts.mixin;
import com.mojang.datafixers.util.Pair;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.block.enums.RailShape;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.vehicle.AbstractMinecartEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.state.property.Properties;
import net.minecraft.text.Text;
import net.minecraft.util.DyeColor;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Mixin which overrides the default minecart behavior so that we can define a
* changeable speed, and check for updates.
*/
@Mixin(AbstractMinecartEntity.class)
public abstract class AbstractMinecartMixin extends Entity {
private static final double DEFAULT_SPEED = 8.0;
private static final double MIN_SPEED = 1.0;
private static final double MAX_SPEED = 128.0;
private static final long SPEED_UPDATE_COOLDOWN = 20 * 3;
/**
* The currently-set maximum speed, in blocks per second.
*/
private double maxSpeedBps = DEFAULT_SPEED;
/**
* The last time (in game ticks) that the speed was updated.
*/
private long lastSpeedUpdate = 0;
/**
* The position of the block which was last responsible for updating the
* cart's speed.
*/
private BlockPos lastUpdatedFrom = null;
@Shadow
public abstract Vec3d snapPositionToRail(double x, double y, double z);
@Shadow
private static Pair<Vec3i, Vec3i> getAdjacentRailPositionsByShape(RailShape shape) {
return null;
}
@Shadow
protected abstract double getMaxOffRailSpeed();
@Inject(at = @At("HEAD"), method = "getMaxOffRailSpeed", cancellable = true)
public void getMaxOffRailSpeedOverwrite(CallbackInfoReturnable<Double> cir) {
cir.setReturnValue(this.maxSpeedBps / 20.0);
}
@Shadow
protected abstract void applySlowdown();
@Shadow
protected abstract boolean willHitBlockAt(BlockPos pos);
public AbstractMinecartMixin(EntityType<?> type, World world) {
super(type, world);
}
@Shadow
protected abstract void moveOnRail(BlockPos pos, BlockState state);
@Shadow public abstract Direction getMovementDirection();
@Inject(at = @At("HEAD"), method = "moveOnRail", cancellable = true)
public void moveOnRailOverwrite(BlockPos pos, BlockState state, CallbackInfo ci) {
this.updateForSpeedModifiers(pos);
}
/**
* Checks for any speed modifiers (signs, blocks, etc.) and attempts to
* apply their effects to the cart. It does this by iterating over a list of
* possible positions for blocks which are considered speed modifiers, and
* then if a valid speed modifier is found, the cart's speed is updated.
* @param pos The cart's current position.
*/
private void updateForSpeedModifiers(BlockPos pos) {
// Quit if the cart is not moving, and set its speed to default.
if (this.getVelocity().length() == 0) {
this.maxSpeedBps = DEFAULT_SPEED;
return;
}
for (var position : this.getPositionsToCheck(pos)) {
if (
this.world.getBlockEntity(position) instanceof SignBlockEntity sign &&
(!sign.getPos().equals(this.lastUpdatedFrom) || this.world.getTime() > this.lastSpeedUpdate + SPEED_UPDATE_COOLDOWN)
) {
var state = this.world.getBlockState(position);
var dir = (Direction) state.getEntries().get(Properties.HORIZONTAL_FACING);
// Only allow free-standing signs or those facing the cart.
if (dir == null || dir.equals(this.getMovementDirection().getOpposite())) {
if (this.updateSpeedForSign(sign)) return;
}
}
}
}
/**
* Attempts to update the cart's speed according to a sign.
* @param sign The sign that contains speed information.
* @return True if the cart's speed was updated, or false otherwise.
*/
private boolean updateSpeedForSign(SignBlockEntity sign) {
Text text = sign.getTextOnRow(0, false);
String s = text.asString();
try {
double speed = Double.parseDouble(s);
if (speed >= MIN_SPEED && speed <= MAX_SPEED) {
this.maxSpeedBps = speed;
this.lastSpeedUpdate = this.world.getTime();
this.lastUpdatedFrom = sign.getPos();
if (this.hasPlayerRider()) {
PlayerEntity player = (PlayerEntity) this.getFirstPassenger();
if (player != null) {
player.playSound(new SoundEvent(new Identifier("block.note_block.bell")), SoundCategory.PLAYERS, 1.0f, 1.0f);
}
}
return true;
} else {
sign.setTextOnRow(0, Text.of("Invalid speed!"));
sign.setTextOnRow(1, Text.of("Min: " + MIN_SPEED));
sign.setTextOnRow(2, Text.of("Max: " + MAX_SPEED));
sign.setGlowingText(true);
sign.setTextColor(DyeColor.RED);
}
} catch (NumberFormatException e) {
// Do nothing if no value could be parsed.
}
return false;
}
/**
* Gathers a list of all block positions to check for signs that may affect
* the cart's speed.
* @param pos The cart's block position.
* @return A collection of positions to check.
*/
private Collection<BlockPos> getPositionsToCheck(BlockPos pos) {
// Compute the number of blocks we have to check ahead.
// This accounts for speeds greater than 1 block per tick.
int blockRange = Math.max(1, (int) Math.ceil(this.maxSpeedBps / 20));
List<BlockPos> positionsToCheck = new ArrayList<>(6 * blockRange);
for (int i = 0; i < blockRange; i++) {
positionsToCheck.add(pos.north());
positionsToCheck.add(pos.south());
positionsToCheck.add(pos.east());
positionsToCheck.add(pos.west());
positionsToCheck.add(pos.up());
positionsToCheck.add(pos.down());
pos = pos.add(this.getMovementDirection().getVector());
}
return positionsToCheck;
}
}

View File

@ -1,16 +1,16 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "modid", "id": "speed-carts",
"version": "${version}", "version": "${version}",
"name": "Example Mod", "name": "Speed Carts",
"description": "This is an example description! Tell everyone what your mod is about!", "description": "Increases the speed of minecarts.",
"authors": [ "authors": [
"Me!" "Andrew Lalis"
], ],
"contact": { "contact": {
"homepage": "https://fabricmc.net/", "homepage": "https://andrewl.nl/",
"sources": "https://github.com/FabricMC/fabric-example-mod" "sources": "https://github.com/andrewlalis/SpeedCarts"
}, },
"license": "CC0-1.0", "license": "CC0-1.0",
@ -19,7 +19,7 @@
"environment": "*", "environment": "*",
"entrypoints": { "entrypoints": {
"main": [ "main": [
"net.fabricmc.example.ExampleMod" "nl.andrewlalis.speed_carts.SpeedCarts"
] ]
}, },
"mixins": [ "mixins": [

View File

@ -1,12 +1,16 @@
{ {
"required": true, "required": true,
"minVersion": "0.8", "minVersion": "0.8",
"package": "net.fabricmc.example.mixin", "package": "nl.andrewlalis.speed_carts.mixin",
"compatibilityLevel": "JAVA_16", "compatibilityLevel": "JAVA_16",
"mixins": [ "mixins": [
"AbstractMinecartMixin"
], ],
"client": [ "client": [
"ExampleMixin" "AbstractMinecartMixin"
],
"server": [
"AbstractMinecartMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1