Commit 9dbe4702 by Jonathan Thomas

- Render other player messages above their heads (skips own player)

- Added player face to player chat bubbles
- Added placeholder player text-top UI graphic (no friendship)
- Reduced ticks visible per page of player messages
- Changed PlayerMessage to extend EntityChatData, for simplicity
parent 53000c90
Pipeline #11993 passed with stage
in 20 seconds
......@@ -42,7 +42,7 @@ public class BubbleRenderer {
public static int animationFrame = 0;
public static long lastTick = 0;
public static void drawTextBubbleBackground(MatrixStack matrices, float x, float y, float width, float height, int friendship) {
public static void drawTextBubbleBackground(String base_name, MatrixStack matrices, float x, float y, float width, float height, int friendship) {
RenderSystem.enableDepthTest();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
......@@ -54,11 +54,11 @@ public class BubbleRenderer {
// Draw UI text background (based on friendship)
if (friendship == -3) {
RenderSystem.setShaderTexture(0, textures.GetUI("text-top-enemy"));
RenderSystem.setShaderTexture(0, textures.GetUI(base_name + "-enemy"));
} else if (friendship == 3) {
RenderSystem.setShaderTexture(0, textures.GetUI("text-top-friend"));
RenderSystem.setShaderTexture(0, textures.GetUI(base_name + "-friend"));
} else {
RenderSystem.setShaderTexture(0, textures.GetUI("text-top"));
RenderSystem.setShaderTexture(0, textures.GetUI(base_name));
}
drawTexturePart(matrices, buffer, x - 50, y, z, 228, 40);
......@@ -164,6 +164,40 @@ public class BubbleRenderer {
RenderSystem.disableDepthTest();
}
private static void drawPlayerIcon(MatrixStack matrices, Entity entity, float x, float y, float width, float height) {
// Get player skin texture
EntityRenderer renderer = EntityRendererAccessor.getEntityRenderer(entity);
Identifier playerTexture = renderer.getTexture(entity);
RenderSystem.setShaderTexture(0, playerTexture);
RenderSystem.enableDepthTest();
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionTexProgram);
Tessellator tessellator = Tessellator.getInstance();
BufferBuilder bufferBuilder = tessellator.getBuffer();
bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE);
// Texture coordinates for the face region (8, 8) to (16, 16) in a 64x64 texture
float textureWidth = 64.0F;
float textureHeight = 64.0F;
float u1 = 8.0F / textureWidth;
float v1 = 8.0F / textureHeight;
float u2 = 16.0F / textureWidth;
float v2 = 16.0F / textureHeight;
float z = -0.01F;
bufferBuilder.vertex(matrices.peek().getPositionMatrix(), x, y + height, z).texture(u1, v2).next(); // bottom left
bufferBuilder.vertex(matrices.peek().getPositionMatrix(), x + width, y + height, z).texture(u2, v2).next(); // bottom right
bufferBuilder.vertex(matrices.peek().getPositionMatrix(), x + width, y, z).texture(u2, v1).next(); // top right
bufferBuilder.vertex(matrices.peek().getPositionMatrix(), x, y, z).texture(u1, v1).next(); // top left
tessellator.draw();
RenderSystem.disableBlend();
RenderSystem.disableDepthTest();
}
private static void drawMessageText(Matrix4f matrix, List<String> lines, int starting_line, int ending_line,
VertexConsumerProvider immediate, float lineSpacing, int fullBright, float yOffset) {
TextRenderer fontRenderer = MinecraftClient.getInstance().textRenderer;
......@@ -239,13 +273,21 @@ public class BubbleRenderer {
.collect(Collectors.toList());
for (Entity entity : relevantEntities) {
if (entity.hasPassengers()) {
// Look-up greeting (if any)
ChatDataManager.EntityChatData chatData = null;
if (entity instanceof MobEntity) {
chatData = ChatDataManager.getClientInstance().getOrCreateChatData(entity.getUuidAsString());
} else if (entity instanceof PlayerEntity) {
chatData = PlayerMessageManager.getMessage(entity.getUuid());
}
// Bail if no chatData found, or if they have passengers
if (chatData == null || entity.hasPassengers()) {
// Skip
continue;
}
// Look-up greeting (if any)
ChatDataManager.EntityChatData chatData = ChatDataManager.getClientInstance().getOrCreateChatData(entity.getUuidAsString());
List<String> lines = chatData.getWrappedLines();
// Set the range of lines to display
......@@ -375,7 +417,7 @@ public class BubbleRenderer {
} else if (chatData.sender == ChatDataManager.ChatSender.ASSISTANT && chatData.status != ChatDataManager.ChatStatus.HIDDEN) {
// Draw text background (no smaller than 50F tall)
drawTextBubbleBackground(matrices, -64, 0, 128, scaledTextHeight, chatData.friendship);
drawTextBubbleBackground("text-top", matrices, -64, 0, 128, scaledTextHeight, chatData.friendship);
// Draw face icon of entity
drawEntityIcon(matrices, entity, -82, 7, 32, 32);
......@@ -399,6 +441,16 @@ public class BubbleRenderer {
} else if (chatData.sender == ChatDataManager.ChatSender.ASSISTANT && chatData.status == ChatDataManager.ChatStatus.HIDDEN) {
// Draw 'resume chat' button
drawIcon("button-chat", matrices, -16, textHeaderHeight, 32, 17);
} else if (chatData.sender == ChatDataManager.ChatSender.USER && chatData.status == ChatDataManager.ChatStatus.DISPLAY) {
// Draw text background
drawTextBubbleBackground("text-top-player", matrices, -64, 0, 128, scaledTextHeight, chatData.friendship);
// Draw face icon of player
drawPlayerIcon(matrices, entity, -75, 14, 18, 18);
// Render each line of the player's text
drawMessageText(matrix, lines, starting_line, ending_line, immediate, lineSpacing, fullBright, 40.0F + DISPLAY_PADDING);
}
// Pop the matrix to return to the original state.
......
......@@ -57,7 +57,7 @@ public class ClickHandler {
ClientPlayNetworking.registerGlobalReceiver(ModInit.PACKET_S2C_MESSAGE, (client, handler, buffer, responseSender) -> {
// Read the data from the server packet
UUID entityId = UUID.fromString(buffer.readString());
UUID playerId = UUID.fromString(buffer.readString());
String playerId = buffer.readString();
String message = buffer.readString(32767);
int line = buffer.readInt();
String status_name = buffer.readString(32767);
......@@ -70,7 +70,7 @@ public class ClickHandler {
if (entity != null) {
ChatDataManager chatDataManager = ChatDataManager.getClientInstance();
ChatDataManager.EntityChatData chatData = chatDataManager.getOrCreateChatData(entity.getUuidAsString());
chatData.playerId = playerId.toString();
chatData.playerId = playerId;
if (!message.isEmpty()) {
chatData.currentMessage = message;
}
......@@ -79,7 +79,7 @@ public class ClickHandler {
chatData.sender = ChatDataManager.ChatSender.valueOf(sender_name);
chatData.friendship = friendship;
if (chatData.sender == ChatDataManager.ChatSender.USER) {
if (chatData.sender == ChatDataManager.ChatSender.USER && !playerId.isEmpty()) {
// Add player message to queue for rendering
PlayerMessageManager.addMessage(UUID.fromString(chatData.playerId), chatData.currentMessage, ChatDataManager.TICKS_TO_DISPLAY_USER_MESSAGE);
}
......
package com.owlmaddie.ui;
import com.owlmaddie.chat.ChatDataManager;
import com.owlmaddie.chat.LineWrapper;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
......@@ -11,24 +8,14 @@ import java.util.concurrent.atomic.AtomicInteger;
* many ticks to remain visible, and the message to display. Similar to an EntityChatData, but
* much simpler.
*/
public class PlayerMessage {
public String currentMessage;
public int currentLineNumber;
public class PlayerMessage extends ChatDataManager.EntityChatData {
public AtomicInteger tickCountdown;
public PlayerMessage(String messageText, int ticks) {
public PlayerMessage(String playerId, String messageText, int ticks) {
super("", playerId);
this.currentMessage = messageText;
this.currentLineNumber = 0;
this.tickCountdown = new AtomicInteger(ticks);
}
public List<String> getWrappedLines() {
return LineWrapper.wrapLines(this.currentMessage, ChatDataManager.MAX_CHAR_PER_LINE);
}
public boolean isEndOfMessage() {
int totalLines = this.getWrappedLines().size();
// Check if the current line number plus DISPLAY_NUM_LINES covers or exceeds the total number of lines
return currentLineNumber + ChatDataManager.DISPLAY_NUM_LINES >= totalLines;
this.status = ChatDataManager.ChatStatus.DISPLAY;
}
}
\ No newline at end of file
......@@ -12,7 +12,11 @@ public class PlayerMessageManager {
private static final ConcurrentHashMap<UUID, PlayerMessage> messages = new ConcurrentHashMap<>();
public static void addMessage(UUID playerUUID, String messageText, int ticks) {
messages.put(playerUUID, new PlayerMessage(messageText, ticks));
messages.put(playerUUID, new PlayerMessage(playerUUID.toString(), messageText, ticks));
}
public static PlayerMessage getMessage(UUID playerId) {
return messages.get(playerId);
}
public static void tickUpdate() {
......
......@@ -42,7 +42,7 @@ public class ChatDataManager {
public static int MAX_CHAR_PER_LINE = 20;
public static int DISPLAY_NUM_LINES = 3;
public static int MAX_CHAR_IN_USER_MESSAGE = 512;
public static int TICKS_TO_DISPLAY_USER_MESSAGE = 90;
public static int TICKS_TO_DISPLAY_USER_MESSAGE = 60;
public QuestJson quest = null;
private static final Gson GSON = new Gson();
......@@ -83,9 +83,9 @@ public class ChatDataManager {
public ChatSender sender;
public int friendship; // -3 to 3 (0 = neutral)
public EntityChatData(String entityId) {
public EntityChatData(String entityId, String playerId) {
this.entityId = entityId;
this.playerId = null;
this.playerId = playerId;
this.currentMessage = "";
this.currentLineNumber = 0;
this.previousMessages = new ArrayList<>();
......@@ -200,7 +200,12 @@ public class ChatDataManager {
public void generateMessage(ServerPlayerEntity player, String systemPrompt, String userMessage) {
this.status = ChatStatus.PENDING;
// Add USER Message
if (systemPrompt == "system-character") {
// Add message without playerId (so it does not display)
this.addMessage(userMessage, ChatSender.USER, "");
} else if (systemPrompt == "system-chat") {
this.addMessage(userMessage, ChatSender.USER, player.getUuidAsString());
}
// Add PLAYER context information
Map<String, String> contextData = getPlayerContext(player);
......@@ -383,7 +388,7 @@ public class ChatDataManager {
// Retrieve chat data for a specific entity, or create it if it doesn't exist
public EntityChatData getOrCreateChatData(String entityId) {
return entityChatDataMap.computeIfAbsent(entityId, k -> new EntityChatData(entityId));
return entityChatDataMap.computeIfAbsent(entityId, k -> new EntityChatData(entityId, ""));
}
// Generate quest data for this server session
......
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