221 lines
5 KiB
JavaScript
221 lines
5 KiB
JavaScript
import { LitElement, html, css } from "lit";
|
|
import { narrativeManager } from "../managers/NarrativeManager.js";
|
|
|
|
export class DialogueOverlay extends LitElement {
|
|
static get styles() {
|
|
return css`
|
|
:host {
|
|
position: absolute;
|
|
bottom: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 80%;
|
|
max-width: 800px;
|
|
z-index: 100;
|
|
pointer-events: auto;
|
|
font-family: "Courier New", monospace;
|
|
}
|
|
|
|
.dialogue-box {
|
|
background: rgba(10, 10, 20, 0.95);
|
|
border: 2px solid #00ffff;
|
|
box-shadow: 0 0 20px rgba(0, 255, 255, 0.2);
|
|
padding: 20px;
|
|
display: flex;
|
|
gap: 20px;
|
|
animation: slideUp 0.3s ease-out;
|
|
}
|
|
|
|
.portrait {
|
|
width: 100px;
|
|
height: 100px;
|
|
background: #222;
|
|
border: 1px solid #555;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.portrait img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.content {
|
|
flex-grow: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.speaker {
|
|
color: #00ffff;
|
|
font-weight: bold;
|
|
font-size: 1.2rem;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.text {
|
|
color: white;
|
|
font-size: 1.1rem;
|
|
line-height: 1.5;
|
|
min-height: 3em;
|
|
}
|
|
|
|
.choices {
|
|
margin-top: 15px;
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
|
|
button {
|
|
background: #333;
|
|
color: white;
|
|
border: 1px solid #555;
|
|
padding: 8px 16px;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
button:hover {
|
|
background: #444;
|
|
border-color: #00ffff;
|
|
}
|
|
|
|
.next-indicator {
|
|
align-self: flex-end;
|
|
font-size: 0.8rem;
|
|
color: #888;
|
|
margin-top: 10px;
|
|
animation: blink 1s infinite;
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
transform: translateY(20px);
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
transform: translateY(0);
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
@keyframes blink {
|
|
50% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
/* Tutorial Style Override */
|
|
.type-tutorial {
|
|
border-color: #00ff00;
|
|
}
|
|
.type-tutorial .speaker {
|
|
color: #00ff00;
|
|
}
|
|
`;
|
|
}
|
|
|
|
static get properties() {
|
|
return {
|
|
activeNode: { type: Object },
|
|
isVisible: { type: Boolean },
|
|
};
|
|
}
|
|
|
|
constructor() {
|
|
super();
|
|
this.activeNode = null;
|
|
this.isVisible = false;
|
|
}
|
|
|
|
connectedCallback() {
|
|
super.connectedCallback();
|
|
// Subscribe to Manager Updates
|
|
narrativeManager.addEventListener(
|
|
"narrative-update",
|
|
this._onUpdate.bind(this)
|
|
);
|
|
narrativeManager.addEventListener("narrative-end", this._onEnd.bind(this));
|
|
|
|
// Allow clicking/spacebar to advance
|
|
window.addEventListener("keydown", this._handleInput.bind(this));
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
super.disconnectedCallback();
|
|
window.removeEventListener("keydown", this._handleInput.bind(this));
|
|
}
|
|
|
|
_onUpdate(e) {
|
|
this.activeNode = e.detail.node;
|
|
this.isVisible = e.detail.active;
|
|
}
|
|
|
|
_onEnd() {
|
|
this.isVisible = false;
|
|
this.activeNode = null;
|
|
}
|
|
|
|
_handleInput(e) {
|
|
if (!this.isVisible) return;
|
|
if (e.code === "Space" || e.code === "Enter") {
|
|
// Only advance if no choices
|
|
if (!this.activeNode.choices) {
|
|
narrativeManager.next();
|
|
}
|
|
}
|
|
}
|
|
|
|
render() {
|
|
if (!this.isVisible || !this.activeNode) return html``;
|
|
|
|
return html`
|
|
<div
|
|
class="dialogue-box ${this.activeNode.type === "TUTORIAL"
|
|
? "type-tutorial"
|
|
: ""}"
|
|
@click="${() => !this.activeNode.choices && narrativeManager.next()}"
|
|
>
|
|
${this.activeNode.portrait
|
|
? html`
|
|
<div class="portrait">
|
|
<img
|
|
src="${this.activeNode.portrait}"
|
|
alt="${this.activeNode.speaker}"
|
|
/>
|
|
</div>
|
|
`
|
|
: ""}
|
|
|
|
<div class="content">
|
|
<div class="speaker">${this.activeNode.speaker}</div>
|
|
<div class="text">${this.activeNode.text}</div>
|
|
|
|
${this.activeNode.choices
|
|
? html`
|
|
<div class="choices">
|
|
${this.activeNode.choices.map(
|
|
(choice, index) => html`
|
|
<button
|
|
@click="${(e) => {
|
|
e.stopPropagation();
|
|
narrativeManager.makeChoice(index);
|
|
}}"
|
|
>
|
|
${choice.text}
|
|
</button>
|
|
`
|
|
)}
|
|
</div>
|
|
`
|
|
: html`<div class="next-indicator">
|
|
Press SPACE to continue...
|
|
</div>`}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
customElements.define("dialogue-overlay", DialogueOverlay);
|