Commit b53ce384 by Jonathan Thomas

Best friends are now rideable! Right click with an empty hand. Large refactor of…

Best friends are now rideable! Right click with an empty hand. Large refactor of how MobEntity avoids targeting players when friendship > 0.
parent 07e7e336
Pipeline #12714 passed with stages
in 2 minutes 18 seconds
...@@ -8,8 +8,10 @@ All notable changes to **CreatureChat** are documented in this file. The format ...@@ -8,8 +8,10 @@ All notable changes to **CreatureChat** are documented in this file. The format
### Added ### Added
- New LEAD behavior, to guide a player to a random location (and show message when destination is reached) - New LEAD behavior, to guide a player to a random location (and show message when destination is reached)
- Best friends are now rideable! Right click with an empty hand.
### Changed ### Changed
- Large refactor of how MobEntity avoids targeting players when friendship > 0
- Updated LookControls to support PhantomEntity and made it more generalized (look in any direction) - Updated LookControls to support PhantomEntity and made it more generalized (look in any direction)
- Updated FLEE behavior Y movement speed - Updated FLEE behavior Y movement speed
- Updated unit tests to add new LEAD tests - Updated unit tests to add new LEAD tests
......
...@@ -11,7 +11,6 @@ import com.owlmaddie.message.Behavior; ...@@ -11,7 +11,6 @@ import com.owlmaddie.message.Behavior;
import com.owlmaddie.message.MessageParser; import com.owlmaddie.message.MessageParser;
import com.owlmaddie.message.ParsedMessage; import com.owlmaddie.message.ParsedMessage;
import com.owlmaddie.network.ServerPackets; import com.owlmaddie.network.ServerPackets;
import com.owlmaddie.utils.LivingEntityInterface;
import com.owlmaddie.utils.Randomizer; import com.owlmaddie.utils.Randomizer;
import com.owlmaddie.utils.ServerEntityFinder; import com.owlmaddie.utils.ServerEntityFinder;
import net.minecraft.entity.boss.dragon.EnderDragonEntity; import net.minecraft.entity.boss.dragon.EnderDragonEntity;
...@@ -324,13 +323,7 @@ public class ChatDataManager { ...@@ -324,13 +323,7 @@ public class ChatDataManager {
} else if (behavior.getName().equals("FRIENDSHIP")) { } else if (behavior.getName().equals("FRIENDSHIP")) {
int new_friendship = Math.max(-3, Math.min(3, behavior.getArgument())); int new_friendship = Math.max(-3, Math.min(3, behavior.getArgument()));
if (new_friendship > 0) {
// positive friendship (apply friend goal)
((LivingEntityInterface) entity).setCanTargetPlayers(false);
} else if (new_friendship <= 0) {
// negative friendship (remove friend goal)
((LivingEntityInterface) entity).setCanTargetPlayers(true);
}
// Does friendship improve? // Does friendship improve?
if (new_friendship > this.friendship) { if (new_friendship > this.friendship) {
// Stop any attack/flee if friendship improves // Stop any attack/flee if friendship improves
......
package com.owlmaddie.mixin; package com.owlmaddie.mixin;
import com.owlmaddie.chat.ChatDataManager; import com.owlmaddie.chat.ChatDataManager;
import com.owlmaddie.commands.ConfigurationHandler;
import com.owlmaddie.network.ServerPackets; import com.owlmaddie.network.ServerPackets;
import com.owlmaddie.utils.LivingEntityInterface;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSource;
...@@ -11,10 +9,8 @@ import net.minecraft.entity.mob.MobEntity; ...@@ -11,10 +9,8 @@ import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.passive.TameableEntity; import net.minecraft.entity.passive.TameableEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
...@@ -22,19 +18,25 @@ import org.spongepowered.asm.mixin.injection.Inject; ...@@ -22,19 +18,25 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
@Mixin(LivingEntity.class) @Mixin(LivingEntity.class)
public class MixinLivingEntity implements LivingEntityInterface { public class MixinLivingEntity {
private boolean canTargetPlayers = true; // Default to true to maintain original behavior
private ChatDataManager.EntityChatData getChatData(LivingEntity entity) {
ChatDataManager chatDataManager = ChatDataManager.getServerInstance();
return chatDataManager.getOrCreateChatData(entity.getUuidAsString());
}
@Inject(method = "canTarget(Lnet/minecraft/entity/LivingEntity;)Z", at = @At("HEAD"), cancellable = true) @Inject(method = "canTarget(Lnet/minecraft/entity/LivingEntity;)Z", at = @At("HEAD"), cancellable = true)
private void modifyCanTarget(LivingEntity target, CallbackInfoReturnable<Boolean> cir) { private void modifyCanTarget(LivingEntity target, CallbackInfoReturnable<Boolean> cir) {
if (!this.canTargetPlayers && target instanceof PlayerEntity) { if (target instanceof PlayerEntity) {
LivingEntity thisEntity = (LivingEntity) (Object) this;
ChatDataManager.EntityChatData chatData = getChatData(thisEntity);
if (chatData.friendship > 0) {
// Friendly creatures can't target a player
cir.setReturnValue(false); cir.setReturnValue(false);
} }
} }
}
@Inject(method = "damage", at = @At(value = "RETURN")) @Inject(method = "damage", at = @At(value = "RETURN"))
private void onDamage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) { private void onDamage(DamageSource source, float amount, CallbackInfoReturnable<Boolean> cir) {
...@@ -51,12 +53,11 @@ public class MixinLivingEntity implements LivingEntityInterface { ...@@ -51,12 +53,11 @@ public class MixinLivingEntity implements LivingEntityInterface {
if (attacker instanceof PlayerEntity && thisEntity instanceof MobEntity && !thisEntity.isDead()) { if (attacker instanceof PlayerEntity && thisEntity instanceof MobEntity && !thisEntity.isDead()) {
// Generate attacked message (only if the previous user message was not an attacked message) // Generate attacked message (only if the previous user message was not an attacked message)
// We don't want to constantly generate messages during a prolonged, multi-damage event // We don't want to constantly generate messages during a prolonged, multi-damage event
ChatDataManager chatDataManager = ChatDataManager.getServerInstance(); ChatDataManager.EntityChatData chatData = getChatData(thisEntity);
ChatDataManager.EntityChatData chatData = chatDataManager.getOrCreateChatData(thisEntity.getUuidAsString()); if (!chatData.characterSheet.isEmpty() && chatData.auto_generated < ChatDataManager.MAX_AUTOGENERATE_RESPONSES) {
if (!chatData.characterSheet.isEmpty() && chatData.auto_generated < chatDataManager.MAX_AUTOGENERATE_RESPONSES) {
// Only auto-generate a response to being attacked if chat data already exists // Only auto-generate a response to being attacked if chat data already exists
// and this is the first attack event. // and this is the first attack event.
ServerPlayerEntity player = (ServerPlayerEntity)attacker; ServerPlayerEntity player = (ServerPlayerEntity) attacker;
ItemStack weapon = player.getMainHandStack(); ItemStack weapon = player.getMainHandStack();
String weaponName = weapon.isEmpty() ? "with fists" : "with " + weapon.getItem().toString(); String weaponName = weapon.isEmpty() ? "with fists" : "with " + weapon.getItem().toString();
...@@ -65,7 +66,7 @@ public class MixinLivingEntity implements LivingEntityInterface { ...@@ -65,7 +66,7 @@ public class MixinLivingEntity implements LivingEntityInterface {
String directness = isIndirect ? "indirectly" : "directly"; String directness = isIndirect ? "indirectly" : "directly";
String attackedMessage = "<" + player.getName().getString() + " attacked you " + directness + " with " + weaponName + ">"; String attackedMessage = "<" + player.getName().getString() + " attacked you " + directness + " with " + weaponName + ">";
ServerPackets.generate_chat("N/A", chatData, player, (MobEntity)thisEntity, attackedMessage, true); ServerPackets.generate_chat("N/A", chatData, player, (MobEntity) thisEntity, attackedMessage, true);
} }
} }
} }
...@@ -91,9 +92,4 @@ public class MixinLivingEntity implements LivingEntityInterface { ...@@ -91,9 +92,4 @@ public class MixinLivingEntity implements LivingEntityInterface {
ServerPackets.BroadcastMessage(deathMessage); ServerPackets.BroadcastMessage(deathMessage);
} }
} }
@Override
public void setCanTargetPlayers(boolean canTarget) {
this.canTargetPlayers = canTarget;
}
} }
...@@ -48,8 +48,14 @@ public class MixinMobEntity { ...@@ -48,8 +48,14 @@ public class MixinMobEntity {
return; return;
} }
// Get chat data for entity
ChatDataManager chatDataManager = ChatDataManager.getServerInstance();
ChatDataManager.EntityChatData chatData = chatDataManager.getOrCreateChatData(thisEntity.getUuidAsString());
// Check if the player successfully interacts with an item // Check if the player successfully interacts with an item
if (!itemStack.isEmpty() && player instanceof ServerPlayerEntity) { if (player instanceof ServerPlayerEntity) {
// Player has item in hand
if (!itemStack.isEmpty()) {
ServerPlayerEntity serverPlayer = (ServerPlayerEntity) player; ServerPlayerEntity serverPlayer = (ServerPlayerEntity) player;
String itemName = itemStack.getItem().getName().getString(); String itemName = itemStack.getItem().getName().getString();
int itemCount = itemStack.getCount(); int itemCount = itemStack.getCount();
...@@ -64,11 +70,17 @@ public class MixinMobEntity { ...@@ -64,11 +70,17 @@ public class MixinMobEntity {
String giveItemMessage = "<" + serverPlayer.getName().getString() + String giveItemMessage = "<" + serverPlayer.getName().getString() +
action_verb + "you " + itemCount + " " + itemName + ">"; action_verb + "you " + itemCount + " " + itemName + ">";
ChatDataManager chatDataManager = ChatDataManager.getServerInstance();
ChatDataManager.EntityChatData chatData = chatDataManager.getOrCreateChatData(thisEntity.getUuidAsString());
if (!chatData.characterSheet.isEmpty() && chatData.auto_generated < chatDataManager.MAX_AUTOGENERATE_RESPONSES) { if (!chatData.characterSheet.isEmpty() && chatData.auto_generated < chatDataManager.MAX_AUTOGENERATE_RESPONSES) {
ServerPackets.generate_chat("N/A", chatData, serverPlayer, thisEntity, giveItemMessage, true); ServerPackets.generate_chat("N/A", chatData, serverPlayer, thisEntity, giveItemMessage, true);
} }
} else if (itemStack.isEmpty()) {
// Player's hand is empty
if (chatData.friendship == 3) {
// Ride your best friend!
player.startRiding(thisEntity, true);
}
}
} }
} }
} }
\ No newline at end of file
...@@ -7,7 +7,6 @@ import com.owlmaddie.goals.EntityBehaviorManager; ...@@ -7,7 +7,6 @@ import com.owlmaddie.goals.EntityBehaviorManager;
import com.owlmaddie.goals.GoalPriority; import com.owlmaddie.goals.GoalPriority;
import com.owlmaddie.goals.TalkPlayerGoal; import com.owlmaddie.goals.TalkPlayerGoal;
import com.owlmaddie.utils.Compression; import com.owlmaddie.utils.Compression;
import com.owlmaddie.utils.LivingEntityInterface;
import com.owlmaddie.utils.Randomizer; import com.owlmaddie.utils.Randomizer;
import com.owlmaddie.utils.ServerEntityFinder; import com.owlmaddie.utils.ServerEntityFinder;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
...@@ -217,17 +216,6 @@ public class ServerPackets { ...@@ -217,17 +216,6 @@ public class ServerPackets {
scheduler.stopAutoSaveTask(); scheduler.stopAutoSaveTask();
} }
}); });
ServerEntityEvents.ENTITY_LOAD.register((entity, world) -> {
String entityUUID = entity.getUuidAsString();
if (ChatDataManager.getServerInstance().entityChatDataMap.containsKey(entityUUID)) {
int friendship = ChatDataManager.getServerInstance().entityChatDataMap.get(entityUUID).friendship;
if (friendship > 0) {
LOGGER.info("Entity loaded (" + entityUUID + "), setting friendship to " + friendship);
((LivingEntityInterface)entity).setCanTargetPlayers(false);
}
}
});
ServerEntityEvents.ENTITY_UNLOAD.register((entity, world) -> { ServerEntityEvents.ENTITY_UNLOAD.register((entity, world) -> {
String entityUUID = entity.getUuidAsString(); String entityUUID = entity.getUuidAsString();
if (entity.getRemovalReason() == Entity.RemovalReason.KILLED && ChatDataManager.getServerInstance().entityChatDataMap.containsKey(entityUUID)) { if (entity.getRemovalReason() == Entity.RemovalReason.KILLED && ChatDataManager.getServerInstance().entityChatDataMap.containsKey(entityUUID)) {
......
package com.owlmaddie.utils;
public interface LivingEntityInterface {
void setCanTargetPlayers(boolean canTarget);
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment