feat(ui): Update font sizes for dialogue and speaker name for improved readability

This commit is contained in:
Z. Cliffe Schreuders
2025-11-06 00:54:08 +00:00
parent f055d6c536
commit 13f71baa82
4 changed files with 4 additions and 344 deletions

View File

@@ -103,7 +103,7 @@
/* Speaker name */
.person-chat-speaker-name {
font-size: 14px;
font-size: 20px;
font-weight: bold;
padding-bottom: 8px;
border-bottom: 2px solid #333;
@@ -132,7 +132,7 @@
}
.person-chat-dialogue-text {
font-size: 16px;
font-size: 20px;
line-height: 1.5;
color: #fff;
margin: 0;
@@ -164,7 +164,7 @@
color: #fff;
border: 2px solid #555;
padding: 10px 15px;
font-size: 13px;
font-size: 18px;
cursor: pointer;
text-align: left;
transition: all 0.1s ease;
@@ -194,7 +194,7 @@
color: #4eff4a;
border: 2px solid #555;
padding: 12px 15px;
font-size: 13px;
font-size: 18px;
font-weight: bold;
cursor: pointer;
text-align: center;

View File

@@ -1,338 +0,0 @@
/**
* PersonChatUI - UI Component for Person-Chat Minigame
*
* Handles rendering of conversation interface with:
* - Zoomed portrait displays (NPC left, player right)
* - Dialogue text box
* - Choice buttons
* - Pixel-art styling
*
* @module person-chat-ui
*/
import PersonChatPortraits from './person-chat-portraits.js';
export default class PersonChatUI {
/**
* Create UI component
* @param {HTMLElement} container - Container for UI
* @param {Object} params - Configuration (game, npc, playerSprite)
* @param {NPCManager} npcManager - NPC manager for sprite access
*/
constructor(container, params, npcManager) {
this.container = container;
this.params = params;
this.npcManager = npcManager;
this.game = params.game;
this.npc = params.npc;
this.playerSprite = params.playerSprite;
// UI elements
this.elements = {
root: null,
portraitsContainer: null,
npcPortraitContainer: null,
playerPortraitContainer: null,
dialogueBox: null,
dialogueText: null,
choicesContainer: null,
speakerName: null
};
// Portrait renderers
this.npcPortrait = null;
this.playerPortrait = null;
// State
this.currentSpeaker = null; // 'npc' or 'player'
console.log('📱 PersonChatUI created');
}
/**
* Render the complete UI structure
*/
render() {
try {
this.container.innerHTML = '';
// Create root container
this.elements.root = document.createElement('div');
this.elements.root.className = 'person-chat-root';
// Create portraits and dialogue sections (integrated)
this.createConversationLayout();
// Create choices section (right side)
this.createChoicesSection();
// Add to container
this.container.appendChild(this.elements.root);
// Initialize portrait renderers
this.initializePortraits();
console.log('✅ PersonChatUI rendered');
} catch (error) {
console.error('❌ Error rendering UI:', error);
}
}
/**
* Create conversation layout with portraits and dialogue areas
*/
createConversationLayout() {
const portraitsContainer = document.createElement('div');
portraitsContainer.className = 'person-chat-portraits-container';
// NPC section (left) - portrait + dialogue
const npcSection = this.createCharacterSection('npc', this.npc?.displayName || 'NPC');
this.elements.npcPortraitSection = npcSection.section;
this.elements.npcPortraitContainer = npcSection.portraitContainer;
this.elements.npcDialogueSection = npcSection.dialogueSection;
// Player section (right) - portrait + dialogue
const playerSection = this.createCharacterSection('player', 'You');
this.elements.playerPortraitSection = playerSection.section;
this.elements.playerPortraitContainer = playerSection.portraitContainer;
this.elements.playerDialogueSection = playerSection.dialogueSection;
// Add both sections
portraitsContainer.appendChild(npcSection.section);
portraitsContainer.appendChild(playerSection.section);
this.elements.root.appendChild(portraitsContainer);
this.elements.portraitsContainer = portraitsContainer;
}
/**
* Create a character section (portrait + dialogue)
* @param {string} type - 'npc' or 'player'
* @param {string} displayName - Character's display name
* @returns {Object} Elements created
*/
createCharacterSection(type, displayName) {
const section = document.createElement('div');
section.className = `person-chat-portrait-section ${type}-portrait-section`;
// Label
const label = document.createElement('div');
label.className = `person-chat-portrait-label ${type}-label`;
label.textContent = displayName;
// Portrait container
const portraitContainer = document.createElement('div');
portraitContainer.className = 'person-chat-portrait-canvas-container';
portraitContainer.id = `${type}-portrait-container`;
// Dialogue section (below portrait)
const dialogueSection = document.createElement('div');
dialogueSection.className = 'person-chat-dialogue-section';
dialogueSection.style.display = 'none'; // Hidden by default
const speakerName = document.createElement('div');
speakerName.className = 'person-chat-speaker-name';
speakerName.textContent = displayName;
const dialogueBox = document.createElement('div');
dialogueBox.className = 'person-chat-dialogue-box';
const dialogueText = document.createElement('p');
dialogueText.className = 'person-chat-dialogue-text';
dialogueText.id = `${type}-dialogue-text`;
dialogueBox.appendChild(dialogueText);
dialogueSection.appendChild(speakerName);
dialogueSection.appendChild(dialogueBox);
// Assemble section
section.appendChild(label);
section.appendChild(portraitContainer);
section.appendChild(dialogueSection);
return {
section,
portraitContainer,
dialogueSection,
dialogueText
};
}
/**
* Create choices section (right side)
*/
createChoicesSection() {
const choicesContainer = document.createElement('div');
choicesContainer.className = 'person-chat-choices-container';
choicesContainer.id = 'choices-container';
choicesContainer.style.display = 'none'; // Hidden until choices available
this.elements.root.appendChild(choicesContainer);
this.elements.choicesContainer = choicesContainer;
}
/**
* Initialize portrait renderers
*/
initializePortraits() {
try {
if (!this.game || !this.npc) {
console.warn('⚠️ Missing game or NPC, skipping portrait initialization');
return;
}
// Initialize NPC portrait
if (this.npc._sprite) {
this.npcPortrait = new PersonChatPortraits(
this.game,
this.npc,
this.elements.npcPortraitContainer
);
this.npcPortrait.init();
} else {
console.warn(`⚠️ NPC ${this.npc.id} has no sprite reference`);
}
// Initialize player portrait (if player sprite exists)
if (this.playerSprite) {
// Create a pseudo-NPC object for player portrait
const playerNPC = {
id: 'player',
displayName: 'You',
_sprite: this.playerSprite
};
this.playerPortrait = new PersonChatPortraits(
this.game,
playerNPC,
this.elements.playerPortraitContainer
);
this.playerPortrait.init();
}
console.log('✅ Portraits initialized');
} catch (error) {
console.error('❌ Error initializing portraits:', error);
}
}
/**
* Display dialogue text
* @param {string} text - Dialogue text
* @param {string} speaker - Speaker name ('npc' or 'player')
*/
showDialogue(text, speaker = 'npc') {
this.currentSpeaker = speaker;
// Hide both dialogue sections first
if (this.elements.npcDialogueSection) {
this.elements.npcDialogueSection.style.display = 'none';
}
if (this.elements.playerDialogueSection) {
this.elements.playerDialogueSection.style.display = 'none';
}
// Remove active speaker class from both
if (this.elements.npcPortraitSection) {
this.elements.npcPortraitSection.classList.remove('active-speaker');
}
if (this.elements.playerPortraitSection) {
this.elements.playerPortraitSection.classList.remove('active-speaker');
}
// Show dialogue in the correct section
if (speaker === 'npc' && this.elements.npcDialogueSection) {
const dialogueText = this.elements.npcDialogueSection.querySelector('.person-chat-dialogue-text');
if (dialogueText) {
dialogueText.textContent = text;
}
this.elements.npcDialogueSection.style.display = 'flex';
this.elements.npcPortraitSection.classList.add('active-speaker');
} else if (speaker === 'player' && this.elements.playerDialogueSection) {
const dialogueText = this.elements.playerDialogueSection.querySelector('.person-chat-dialogue-text');
if (dialogueText) {
dialogueText.textContent = text;
}
this.elements.playerDialogueSection.style.display = 'flex';
this.elements.playerPortraitSection.classList.add('active-speaker');
}
}
/**
* Display choice buttons
* @param {Array} choices - Array of choice objects {text, index}
*/
showChoices(choices) {
if (!this.elements.choicesContainer) {
return;
}
// Clear existing choices
this.elements.choicesContainer.innerHTML = '';
if (!choices || choices.length === 0) {
this.elements.choicesContainer.style.display = 'none';
return;
}
// Show choices container
this.elements.choicesContainer.style.display = 'flex';
// Create button for each choice
choices.forEach((choice, idx) => {
const choiceButton = document.createElement('button');
choiceButton.className = 'person-chat-choice-button';
choiceButton.dataset.index = idx;
choiceButton.textContent = choice.text;
this.elements.choicesContainer.appendChild(choiceButton);
});
console.log(`✅ Displayed ${choices.length} choices`);
}
/**
* Hide choices
*/
hideChoices() {
if (this.elements.choicesContainer) {
this.elements.choicesContainer.innerHTML = '';
}
}
/**
* Clear dialogue
*/
clearDialogue() {
if (this.elements.dialogueText) {
this.elements.dialogueText.textContent = '';
}
if (this.elements.speakerName) {
this.elements.speakerName.textContent = '';
}
}
/**
* Cleanup UI and resources
*/
destroy() {
try {
// Stop portrait updates
if (this.npcPortrait) {
this.npcPortrait.destroy();
}
if (this.playerPortrait) {
this.playerPortrait.destroy();
}
// Clear container
if (this.container) {
this.container.innerHTML = '';
}
console.log('✅ PersonChatUI destroyed');
} catch (error) {
console.error('❌ Error destroying UI:', error);
}
}
}

View File

@@ -1 +0,0 @@
{"inkVersion":21,"root":[[["done",{"#n":"g-0"}],null],"done",{"start":["ev",{"VAR?":"conversation_count"},1,"+",{"VAR=":"conversation_count","re":true},"/ev","ev",{"VAR?":"npc_name"},"out","/ev","^: Hey there! This is conversation ","#","ev",{"VAR?":"conversation_count"},"out","/ev","^.","/#","\n","ev",{"VAR?":"npc_name"},"out","/ev","^: What can I help you with?","\n",{"->":"hub"},null],"hub":[["ev",{"VAR?":"asked_question"},"!","/ev",[{"->":".^.b","c":true},{"b":["\n",["ev",{"^->":"hub.0.4.b.1.$r1"},{"temp=":"$r"},"str",{"->":".^.s"},[{"#n":"$r1"}],"/str","str","^Introduce yourself","/str","/ev",{"*":".^.^.c-0","flg":22},{"s":["^once ",{"->":"$r","var":true},null]}],{"->":"hub.0.5"},{"c-0":["ev",{"^->":"hub.0.4.b.c-0.$r2"},"/ev",{"temp=":"$r"},{"->":".^.^.1.s"},[{"#n":"$r2"}],"\n","ev","str","^Nice to meet you!","/str","/ev",{"VAR=":"npc_name","re":true},{"->":"introduction"},{"#f":5}]}]}],"nop","\n","ev",{"VAR?":"asked_question"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Remind me about that question","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.12"},{"c-0":["\n",{"->":"question_reminder"},null]}]}],[{"->":".^.b"},{"b":["\n","ev","str","^Ask a question","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.12"},{"c-0":["\n",{"->":"question"},null]}]}],"nop","\n","ev",{"VAR?":"asked_about_passwords"},"/ev",[{"->":".^.b","c":true},{"b":["\n","ev","str","^Tell me more about passwords","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.19"},{"c-0":["\n",{"->":"passwords_advanced"},null]}]}],[{"->":".^.b"},{"b":["\n","ev","str","^Ask about password security","/str","/ev",{"*":".^.c-0","flg":4},{"->":"hub.0.19"},{"c-0":["\n",{"->":"ask_passwords"},null]}]}],"nop","\n","ev","str","^Say hello","/str","/ev",{"*":".^.c-0","flg":4},"ev","str","^Leave","/str","/ev",{"*":".^.c-1","flg":4},{"c-0":["\n",{"->":"greeting"},null],"c-1":["^ ","#","^exit_conversation","/#","\n","ev",{"VAR?":"npc_name"},"out","/ev","^: See you later!","\n",{"->":"hub"},null]}],null],"introduction":["ev",{"VAR?":"npc_name"},"out","/ev","^: Nice to meet you too! I'm ","ev",{"VAR?":"npc_name"},"out","/ev","^.","\n","ev",{"VAR?":"npc_name"},"out","/ev","^: Feel free to ask me anything.","\n",{"->":"hub"},null],"ask_passwords":["ev",true,"/ev",{"VAR=":"asked_about_passwords","re":true},"ev",{"VAR?":"npc_name"},"out","/ev","^: Passwords should be long and complex...","\n","ev",{"VAR?":"npc_name"},"out","/ev","^: Use at least 12 characters with mixed case and numbers.","\n",{"->":"hub"},null],"question_reminder":["ev",{"VAR?":"npc_name"},"out","/ev","^: As I said before, passwords should be strong and unique.","\n","ev",{"VAR?":"npc_name"},"out","/ev","^: Anything else?","\n",{"->":"hub"},null],"passwords_advanced":["ev",{"VAR?":"npc_name"},"out","/ev","^: For advanced security, use a password manager to generate unique passwords for each site.","\n","ev",{"VAR?":"npc_name"},"out","/ev","^: Never reuse passwords across different services.","\n",{"->":"hub"},null],"question":["ev",{"VAR?":"npc_name"},"out","/ev","^: That's a good question. Let me think about it...","\n","ev",{"VAR?":"npc_name"},"out","/ev","^: I'm not sure I have all the answers right now.","\n",{"->":"hub"},null],"greeting":["ev",{"VAR?":"npc_name"},"out","/ev","^: Hello to you too! Nice to chat with you.","\n",{"->":"hub"},null],"global decl":["ev","str","^NPC","/str",{"VAR=":"npc_name"},0,{"VAR=":"conversation_count"},false,{"VAR=":"asked_question"},false,{"VAR=":"asked_about_passwords"},"/ev","end",null]}],"listDefs":{}}

File diff suppressed because one or more lines are too long