Commit 6cacdad0 by Jonathan Thomas

Initial custom particles, and a custom particle type and factor for friendship…

Initial custom particles, and a custom particle type and factor for friendship particles (heart and fire)
parent 69c681ad
Pipeline #12737 passed with stages
in 1 minute 51 seconds
...@@ -6,6 +6,12 @@ All notable changes to **CreatureChat** are documented in this file. The format ...@@ -6,6 +6,12 @@ All notable changes to **CreatureChat** are documented in this file. The format
## [Unreleased] ## [Unreleased]
### Added
- New friendship particles (hearts + fire) to indicate when friendship changes
### Changed
- Entity chat data now separates messages and friendship by player
### Fixed ### Fixed
- Fixed a regression caused by adding a "-forge" suffix to one of our builds - Fixed a regression caused by adding a "-forge" suffix to one of our builds
......
...@@ -2,14 +2,18 @@ package com.owlmaddie; ...@@ -2,14 +2,18 @@ package com.owlmaddie;
import com.owlmaddie.chat.ChatDataManager; import com.owlmaddie.chat.ChatDataManager;
import com.owlmaddie.network.ClientPackets; import com.owlmaddie.network.ClientPackets;
import com.owlmaddie.particle.CreatureParticleFactory;
import com.owlmaddie.ui.BubbleRenderer; import com.owlmaddie.ui.BubbleRenderer;
import com.owlmaddie.ui.ClickHandler; import com.owlmaddie.ui.ClickHandler;
import com.owlmaddie.ui.PlayerMessageManager; import com.owlmaddie.ui.PlayerMessageManager;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import static com.owlmaddie.network.ServerPackets.*;
/** /**
* The {@code ClientInit} class initializes this mod in the client and defines all hooks into the * The {@code ClientInit} class initializes this mod in the client and defines all hooks into the
* render pipeline to draw chat bubbles, text, and entity icons. * render pipeline to draw chat bubbles, text, and entity icons.
...@@ -19,6 +23,12 @@ public class ClientInit implements ClientModInitializer { ...@@ -19,6 +23,12 @@ public class ClientInit implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
// Register particle factories
ParticleFactoryRegistry.getInstance().register(HEART_SMALL_PARTICLE, CreatureParticleFactory::new);
ParticleFactoryRegistry.getInstance().register(HEART_BIG_PARTICLE, CreatureParticleFactory::new);
ParticleFactoryRegistry.getInstance().register(FIRE_SMALL_PARTICLE, CreatureParticleFactory::new);
ParticleFactoryRegistry.getInstance().register(FIRE_BIG_PARTICLE, CreatureParticleFactory::new);
ClientTickEvents.END_CLIENT_TICK.register(client -> { ClientTickEvents.END_CLIENT_TICK.register(client -> {
tickCounter++; tickCounter++;
PlayerMessageManager.tickUpdate(); PlayerMessageManager.tickUpdate();
......
package com.owlmaddie.particle;
import net.minecraft.client.particle.ParticleTextureSheet;
import net.minecraft.client.particle.SpriteBillboardParticle;
import net.minecraft.client.world.ClientWorld;
public class CreatureParticle extends SpriteBillboardParticle {
protected CreatureParticle(ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
this.scale(2f);
this.setMaxAge(35);
// Start with an initial upward velocity
this.velocityY = 0.1;
this.velocityX *= 0.1;
this.velocityZ *= 0.1;
}
@Override
public ParticleTextureSheet getType() {
return ParticleTextureSheet.PARTICLE_SHEET_OPAQUE;
}
@Override
public int getBrightness(float tint) {
return 0xF000F0;
}
@Override
public void tick() {
super.tick();
// Gradually decrease the upward velocity over time
if (this.velocityY > 0) {
this.velocityY -= 0.002;
}
// Ensure the particle doesn't start moving downwards
if (this.velocityY < 0) {
this.velocityY = 0;
}
}
}
package com.owlmaddie.particle;
import net.minecraft.client.particle.ParticleFactory;
import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.particle.DefaultParticleType;
public class CreatureParticleFactory implements ParticleFactory<DefaultParticleType> {
private final SpriteProvider spriteProvider;
public CreatureParticleFactory(SpriteProvider spriteProvider) {
this.spriteProvider = spriteProvider;
}
@Override
public CreatureParticle createParticle(DefaultParticleType type, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
CreatureParticle particle = new CreatureParticle(world, x, y, z, velocityX, velocityY, velocityZ);
particle.setSprite(this.spriteProvider);
return particle;
}
}
...@@ -96,43 +96,6 @@ public class ChatDataManager { ...@@ -96,43 +96,6 @@ public class ChatDataManager {
} }
} }
// Generate quest data for this server session
public void generateQuest() {
// Get items needed for Quest prompt
List<String> commonItems = RarityItemCollector.getItemsByRarity(Rarity.COMMON, 5);
List<String> uncommonItems = RarityItemCollector.getItemsByRarity(Rarity.UNCOMMON, 5);
List<String> rareItems = RarityItemCollector.getItemsByRarity(Rarity.RARE, 5);
// Get entities needed for Quest prompt
List<String> commonEntities = RarityItemCollector.getEntitiesByRarity(Rarity.COMMON, 5);
List<String> uncommonEntities = RarityItemCollector.getEntitiesByRarity(Rarity.UNCOMMON, 5);
List<String> rareEntities = RarityItemCollector.getEntitiesByRarity(Rarity.RARE, 5);
// Add context information for prompt
Map<String, String> contextData = new HashMap<>();
contextData.put("items_common", String.join("\n", commonItems));
contextData.put("items_uncommon", String.join("\n", uncommonItems));
contextData.put("items_rare", String.join("\n", rareItems));
contextData.put("entities_common", String.join("\n", commonEntities));
contextData.put("entities_uncommon", String.join("\n", uncommonEntities));
contextData.put("entities_rare", String.join("\n", rareEntities));
// Add message
List<ChatMessage> messages = new ArrayList<>();
messages.add(new ChatMessage("Generate me a new fantasy story with ONLY the 1st character in the story", ChatSender.USER));
// Get config (api key, url, settings)
ConfigurationHandler.Config config = new ConfigurationHandler(ServerPackets.serverInstance).loadConfig();
String questPrompt = ChatPrompt.loadPromptFromResource(ServerPackets.serverInstance.getResourceManager(), "system-quest");
// Generate Quest: fetch HTTP response from ChatGPT
ChatGPTRequest.fetchMessageFromChatGPT(config, questPrompt, contextData, messages, true).thenAccept(output_message -> {
// New Quest
Gson gson = new Gson();
quest = gson.fromJson(output_message, QuestJson.class);
});
}
// Save chat data to file // Save chat data to file
public String GetLightChatData(UUID playerId) { public String GetLightChatData(UUID playerId) {
try { try {
......
...@@ -9,6 +9,7 @@ import com.owlmaddie.message.Behavior; ...@@ -9,6 +9,7 @@ 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.particle.ParticleEmitter;
import com.owlmaddie.utils.Randomizer; import com.owlmaddie.utils.Randomizer;
import com.owlmaddie.utils.ServerEntityFinder; import com.owlmaddie.utils.ServerEntityFinder;
import com.owlmaddie.utils.VillagerEntityAccessor; import com.owlmaddie.utils.VillagerEntityAccessor;
...@@ -18,6 +19,7 @@ import net.minecraft.entity.passive.TameableEntity; ...@@ -18,6 +19,7 @@ import net.minecraft.entity.passive.TameableEntity;
import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.entity.passive.VillagerEntity;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.village.VillageGossipType; import net.minecraft.village.VillageGossipType;
import org.slf4j.Logger; import org.slf4j.Logger;
...@@ -31,6 +33,8 @@ import java.util.regex.Matcher; ...@@ -31,6 +33,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.owlmaddie.network.ServerPackets.*;
/** /**
* The {@code EntityChatData} class represents a conversation between an * The {@code EntityChatData} class represents a conversation between an
* entity and one or more players, including friendship, character sheets, * entity and one or more players, including friendship, character sheets,
...@@ -393,6 +397,16 @@ public class EntityChatData { ...@@ -393,6 +397,16 @@ public class EntityChatData {
} }
} }
// Show particles
if (playerData.friendship != new_friendship) {
int friendDiff = new_friendship - playerData.friendship;
if (friendDiff > 0) {
ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, HEART_SMALL_PARTICLE);
} else if (friendDiff < 0) {
ParticleEmitter.emitCreatureParticle((ServerWorld) entity.getWorld(), entity, FIRE_SMALL_PARTICLE);
}
}
playerData.friendship = new_friendship; playerData.friendship = new_friendship;
} }
} }
......
package com.owlmaddie.network; package com.owlmaddie.network;
import com.owlmaddie.chat.ChatDataManager; import com.owlmaddie.chat.ChatDataManager;
import com.owlmaddie.chat.EntityChatData;
import com.owlmaddie.chat.ChatDataSaverScheduler; import com.owlmaddie.chat.ChatDataSaverScheduler;
import com.owlmaddie.chat.EntityChatData;
import com.owlmaddie.chat.PlayerData; import com.owlmaddie.chat.PlayerData;
import com.owlmaddie.commands.ConfigurationHandler; import com.owlmaddie.commands.ConfigurationHandler;
import com.owlmaddie.goals.EntityBehaviorManager; import com.owlmaddie.goals.EntityBehaviorManager;
...@@ -16,10 +16,14 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents; ...@@ -16,10 +16,14 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.particle.DefaultParticleType;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
...@@ -54,8 +58,18 @@ public class ServerPackets { ...@@ -54,8 +58,18 @@ public class ServerPackets {
public static final Identifier PACKET_S2C_LOGIN = new Identifier("creaturechat", "packet_s2c_login"); public static final Identifier PACKET_S2C_LOGIN = new Identifier("creaturechat", "packet_s2c_login");
public static final Identifier PACKET_S2C_WHITELIST = new Identifier("creaturechat", "packet_s2c_whitelist"); public static final Identifier PACKET_S2C_WHITELIST = new Identifier("creaturechat", "packet_s2c_whitelist");
public static final Identifier PACKET_S2C_PLAYER_STATUS = new Identifier("creaturechat", "packet_s2c_player_status"); public static final Identifier PACKET_S2C_PLAYER_STATUS = new Identifier("creaturechat", "packet_s2c_player_status");
public static final DefaultParticleType HEART_SMALL_PARTICLE = FabricParticleTypes.simple();
public static final DefaultParticleType HEART_BIG_PARTICLE = FabricParticleTypes.simple();
public static final DefaultParticleType FIRE_SMALL_PARTICLE = FabricParticleTypes.simple();
public static final DefaultParticleType FIRE_BIG_PARTICLE = FabricParticleTypes.simple();
public static void register() { public static void register() {
// Register custom particles
Registry.register(Registries.PARTICLE_TYPE, new Identifier("creaturechat", "heart_small"), HEART_SMALL_PARTICLE);
Registry.register(Registries.PARTICLE_TYPE, new Identifier("creaturechat", "heart_big"), HEART_BIG_PARTICLE);
Registry.register(Registries.PARTICLE_TYPE, new Identifier("creaturechat", "fire_small"), FIRE_SMALL_PARTICLE);
Registry.register(Registries.PARTICLE_TYPE, new Identifier("creaturechat", "fire_big"), FIRE_BIG_PARTICLE);
// Handle packet for Greeting // Handle packet for Greeting
ServerPlayNetworking.registerGlobalReceiver(PACKET_C2S_GREETING, (server, player, handler, buf, responseSender) -> { ServerPlayNetworking.registerGlobalReceiver(PACKET_C2S_GREETING, (server, player, handler, buf, responseSender) -> {
UUID entityId = UUID.fromString(buf.readString()); UUID entityId = UUID.fromString(buf.readString());
......
package com.owlmaddie.particle;
import net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.MathHelper;
import net.minecraft.particle.DefaultParticleType;
public class ParticleEmitter {
public static void emitCreatureParticle(ServerWorld world, Entity entity, DefaultParticleType particleType) {
// Calculate the offset for the particle to appear above and in front of the entity
float yaw = entity.getHeadYaw();
double offsetX = -MathHelper.sin(yaw * ((float) Math.PI / 180F)) * 0.9;
double offsetY = entity.getHeight() + 0.5;
double offsetZ = MathHelper.cos(yaw * ((float) Math.PI / 180F)) * 0.9;
// Final position
double x = entity.getX() + offsetX;
double y = entity.getY() + offsetY;
double z = entity.getZ() + offsetZ;
// Emit the custom particle on the server
world.spawnParticles(particleType, x, y, z, 1, 0, 0, 0, 0);
}
}
\ No newline at end of file
{
"textures": [
"creaturechat:fire_big"
]
}
\ No newline at end of file
{
"textures": [
"creaturechat:fire_small"
]
}
\ No newline at end of file
{
"textures": [
"creaturechat:heart_big"
]
}
\ No newline at end of file
{
"textures": [
"creaturechat:heart_small"
]
}
\ 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