mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 07:21:10 +02:00
Applied formatting to 1487+ files using pnpm format:write - TypeScript/JavaScript files - Svelte components - Astro pages - JSON configs - Markdown docs 13 files still need manual review (Astro JSX comments)
1210 lines
34 KiB
JavaScript
1210 lines
34 KiB
JavaScript
class RPGScene extends Phaser.Scene {
|
|
constructor() {
|
|
super({ key: 'RPGScene' });
|
|
}
|
|
|
|
preload() {
|
|
// Wir verwenden die in BootScene erstellten Texturen
|
|
}
|
|
|
|
create() {
|
|
console.log('RPGScene create wird aufgerufen');
|
|
|
|
// Initialisiere NPC-Status
|
|
this.initNPCState();
|
|
|
|
// Spielwelt erstellen
|
|
this.createWorld();
|
|
|
|
// Spieler erstellen
|
|
this.createPlayer();
|
|
|
|
// NPCs erstellen
|
|
this.createNPCs();
|
|
|
|
// Kamera einrichten
|
|
this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
|
|
this.cameras.main.startFollow(this.player, true);
|
|
|
|
// Steuerung einrichten
|
|
this.cursors = this.input.keyboard.createCursorKeys();
|
|
|
|
// UI erstellen
|
|
this.createUI();
|
|
|
|
console.log('RPGScene create abgeschlossen');
|
|
}
|
|
|
|
createWorld() {
|
|
// Einfache Welt mit Kacheln erstellen
|
|
this.map = {
|
|
widthInPixels: 440, // Angepasste Spielfeldgröße
|
|
heightInPixels: 440,
|
|
tileWidth: 40,
|
|
tileHeight: 40,
|
|
};
|
|
|
|
// Hintergrund
|
|
this.add
|
|
.tileSprite(0, 0, this.map.widthInPixels, this.map.heightInPixels, 'background')
|
|
.setOrigin(0, 0)
|
|
.setScale(1.0); // Doppelte Skalierung für größeres Bild
|
|
|
|
// Erstelle eine Gruppe für Hindernisse
|
|
this.obstacles = this.physics.add.staticGroup();
|
|
|
|
// Definiere die Tile-Typen
|
|
const tileTypes = [
|
|
{ key: 'tile_grass', isObstacle: false },
|
|
{ key: 'tile_grass_flower', isObstacle: false },
|
|
{ key: 'tile_dirt', isObstacle: false },
|
|
{ key: 'tile_dirt_stone', isObstacle: false },
|
|
{ key: 'tile_stone_wall', isObstacle: true },
|
|
{ key: 'tile_stone_wall_flower', isObstacle: true },
|
|
];
|
|
|
|
// Größe des Spielfelds (angepasst für das 440x440 Spielfeld)
|
|
const gridSize = 11; // 11x11 Raster für 440x440 Pixel (40 Pixel pro Kachel)
|
|
|
|
// Erstelle das Spielfeld
|
|
for (let y = 0; y < gridSize; y++) {
|
|
for (let x = 0; x < gridSize; x++) {
|
|
let tileType;
|
|
|
|
// Erstelle Muster für die Welt
|
|
if (x === 0 || y === 0 || x === gridSize - 1 || y === gridSize - 1) {
|
|
// Tür in der oberen Mauer für NPCs
|
|
if (y === 0 && x === Math.floor(gridSize / 2)) {
|
|
// Tür (kein Hindernis)
|
|
tileType = tileTypes[2]; // Erde als Tür
|
|
} else {
|
|
// Steinwände am Rand
|
|
tileType = Math.random() < 0.3 ? tileTypes[5] : tileTypes[4]; // Steinwand oder Steinwand mit Blumen
|
|
}
|
|
} else {
|
|
// Innerer Bereich - Mischung aus Gras und Erde
|
|
const rand = Math.random();
|
|
if (rand < 0.4) {
|
|
tileType = tileTypes[0]; // Gras
|
|
} else if (rand < 0.7) {
|
|
tileType = tileTypes[1]; // Gras mit Blumen
|
|
} else if (rand < 0.9) {
|
|
tileType = tileTypes[2]; // Erde
|
|
} else {
|
|
tileType = tileTypes[3]; // Erde mit Steinen
|
|
}
|
|
}
|
|
|
|
// Erstelle das Tile mit doppelter Größe
|
|
const tile = this.add.image(x * 40 + 20, y * 40 + 20, tileType.key);
|
|
tile.setScale(2.0); // Doppelte Skalierung
|
|
|
|
// Wenn es ein Hindernis ist, füge es zur Hindernisgruppe hinzu
|
|
if (tileType.isObstacle) {
|
|
// Erstelle ein unsichtbares Rechteck für die Kollision
|
|
const obstacle = this.add.rectangle(
|
|
x * 40 + 20,
|
|
y * 40 + 20,
|
|
40 * 2,
|
|
40 * 2 // Doppelte Größe für Kollision
|
|
);
|
|
|
|
// Füge das Hindernis zur Gruppe hinzu
|
|
this.obstacles.add(obstacle);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
createPlayer() {
|
|
// Spieler in der Mitte der Karte platzieren
|
|
this.player = this.physics.add.sprite(
|
|
this.map.widthInPixels / 2, // Mitte der Karte
|
|
this.map.heightInPixels / 2,
|
|
'player_down' // Verwende die neue Textur
|
|
);
|
|
|
|
// Spieler-Größe anpassen - doppelte Skalierung
|
|
this.player.setScale(2.4); // Doppelte Skalierung (1.2 * 2)
|
|
|
|
// Kollisionen mit Hindernissen
|
|
this.physics.add.collider(this.player, this.obstacles);
|
|
|
|
// Spieler-Grenzen setzen
|
|
this.player.setCollideWorldBounds(true);
|
|
}
|
|
|
|
createUI() {
|
|
// Zurück-Button
|
|
const backButton = this.add
|
|
.text(10, 10, 'Zurück zum Menü', {
|
|
fontSize: '18px',
|
|
fill: '#fff',
|
|
backgroundColor: '#4a4a4a',
|
|
padding: { x: 10, y: 5 },
|
|
})
|
|
.setScrollFactor(0)
|
|
.setInteractive();
|
|
|
|
backButton.on('pointerover', () => {
|
|
backButton.setStyle({ fill: '#ff0' });
|
|
});
|
|
|
|
backButton.on('pointerout', () => {
|
|
backButton.setStyle({ fill: '#fff' });
|
|
});
|
|
|
|
backButton.on('pointerdown', () => {
|
|
this.scene.start('MainMenuScene');
|
|
});
|
|
|
|
// Spielanleitung
|
|
this.add
|
|
.text(10, 50, 'Pfeiltasten zum Bewegen', {
|
|
fontSize: '16px',
|
|
fill: '#fff',
|
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
padding: { x: 5, y: 2 },
|
|
})
|
|
.setScrollFactor(0);
|
|
}
|
|
|
|
createNPCs() {
|
|
console.log('createNPCs wird aufgerufen');
|
|
|
|
// Importiere die NPC-Charaktere aus der npc_characters.js-Datei
|
|
try {
|
|
// Versuche, die NPC-Charaktere aus der externen Datei zu laden
|
|
this.npcCharacters = window.npcCharacters || [];
|
|
|
|
if (!this.npcCharacters || this.npcCharacters.length === 0) {
|
|
console.error(
|
|
'Keine NPC-Charaktere gefunden! Stelle sicher, dass npc_characters.js geladen wurde.'
|
|
);
|
|
// Fallback zu einigen Standard-Charakteren
|
|
this.npcCharacters = [
|
|
{
|
|
id: 1,
|
|
name: 'Leonardo da Vinci',
|
|
personality: 'Ein vielseitiger Universalgelehrter der Renaissance.',
|
|
hint: 'Meine Skizzenbücher enthalten Flugmaschinen und anatomische Studien.',
|
|
},
|
|
{
|
|
id: 2,
|
|
name: 'Nikola Tesla',
|
|
personality: 'Ein exzentrischer Elektroingenieur mit visionären Ideen.',
|
|
hint: 'Meine Arbeiten mit Wechselstrom revolutionierten die Energienutzung.',
|
|
},
|
|
];
|
|
}
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der NPC-Charaktere:', error);
|
|
// Fallback zu einigen Standard-Charakteren
|
|
this.npcCharacters = [
|
|
{
|
|
id: 1,
|
|
name: 'Leonardo da Vinci',
|
|
personality: 'Ein vielseitiger Universalgelehrter der Renaissance.',
|
|
hint: 'Meine Skizzenbücher enthalten Flugmaschinen und anatomische Studien.',
|
|
},
|
|
{
|
|
id: 2,
|
|
name: 'Nikola Tesla',
|
|
personality: 'Ein exzentrischer Elektroingenieur mit visionären Ideen.',
|
|
hint: 'Meine Arbeiten mit Wechselstrom revolutionierten die Energienutzung.',
|
|
},
|
|
];
|
|
}
|
|
|
|
console.log('NPC-Charaktere geladen:', this.npcCharacters.length);
|
|
|
|
// Array für alle NPCs im Spiel
|
|
this.npcs = [];
|
|
|
|
// Aktiver NPC (der, mit dem der Spieler gerade interagiert)
|
|
this.npc = null;
|
|
|
|
// Speichere den Status der NPCs
|
|
this.npcState = {
|
|
isInConversation: false,
|
|
isWaitingForResponse: false,
|
|
identityRevealed: false,
|
|
discoveredNPCs: [],
|
|
currentNpcIndex: -1,
|
|
};
|
|
|
|
// Dialog-Box für den NPC
|
|
this.npcDialog = this.add.text(0, 0, 'Hallo! Ich bin ein NPC.\nDrücke E zum Sprechen.', {
|
|
fontSize: '12px',
|
|
fill: '#fff',
|
|
backgroundColor: '#000',
|
|
padding: { x: 5, y: 5 },
|
|
wordWrap: { width: 200 },
|
|
});
|
|
this.npcDialog.setVisible(false);
|
|
|
|
// Interaktions-Prompt
|
|
this.interactionPrompt = this.add.text(0, 0, 'Drücke E zum Sprechen', {
|
|
fontSize: '10px',
|
|
fill: '#fff',
|
|
backgroundColor: '#000',
|
|
padding: { x: 3, y: 3 },
|
|
});
|
|
this.interactionPrompt.setVisible(false);
|
|
|
|
// Erstelle den ersten NPC
|
|
this.createNewNPC();
|
|
}
|
|
|
|
// Methode zum Erstellen eines neuen NPCs
|
|
createNewNPC() {
|
|
console.log('createNewNPC wird aufgerufen');
|
|
|
|
// Initialisiere npcState, wenn es noch nicht existiert
|
|
if (!this.npcState) {
|
|
this.initNPCState();
|
|
}
|
|
|
|
// Debug-Ausgabe der bereits entdeckten NPCs
|
|
console.log('Bereits entdeckte NPCs:', this.npcState.discoveredNPCs);
|
|
|
|
// Wähle einen zufälligen NPC-Charakter, der noch nicht entdeckt wurde
|
|
// UND der nicht der aktuelle NPC ist (falls vorhanden)
|
|
let availableCharacters = this.npcCharacters.filter(
|
|
(char) => !this.npcState.discoveredNPCs.includes(char.id)
|
|
);
|
|
|
|
// Wenn es einen aktuellen NPC gibt, stelle sicher, dass wir nicht denselben Charakter erneut auswählen
|
|
if (this.npc && this.npc.characterId) {
|
|
availableCharacters = availableCharacters.filter((char) => char.id !== this.npc.characterId);
|
|
console.log(
|
|
`Aktueller NPC ${this.npc.characterName} (ID: ${this.npc.characterId}) wird aus der Auswahl ausgeschlossen.`
|
|
);
|
|
}
|
|
|
|
console.log('Verfügbare Charaktere:', availableCharacters.length);
|
|
availableCharacters.forEach((char) => {
|
|
console.log(`- ${char.name} (ID: ${char.id})`);
|
|
});
|
|
|
|
// Wenn keine Charaktere mehr verfügbar sind, verwende alle Charaktere außer dem aktuellen
|
|
if (availableCharacters.length === 0) {
|
|
console.log('Keine neuen Charaktere verfügbar, verwende alle außer dem aktuellen.');
|
|
availableCharacters = this.npcCharacters.filter((char) => {
|
|
if (this.npc && this.npc.characterId) {
|
|
return char.id !== this.npc.characterId;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
if (availableCharacters.length === 0) {
|
|
console.log('Keine Charaktere verfügbar!');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const randomIndex = Math.floor(Math.random() * availableCharacters.length);
|
|
const selectedCharacter = availableCharacters[randomIndex];
|
|
|
|
console.log(
|
|
'Ausgewählter Charakter:',
|
|
selectedCharacter.name,
|
|
'(ID:',
|
|
selectedCharacter.id,
|
|
')'
|
|
);
|
|
console.log('Persönlichkeit:', selectedCharacter.personality);
|
|
|
|
// Feste Position in der Mitte des Bildschirms
|
|
// Positioniere den NPC an der Tür in der oberen Mauer
|
|
const doorX = Math.floor(this.map.widthInPixels / 2);
|
|
const doorY = 40; // Knapp unterhalb der oberen Mauer (angepasst für 440x440 Spielfeld)
|
|
|
|
// Verwende die Türposition
|
|
const x = doorX;
|
|
const y = doorY;
|
|
|
|
console.log(`NPC wird an der Tür (${x}, ${y}) erstellt`);
|
|
|
|
// Erstelle den NPC
|
|
const newNpc = this.physics.add.sprite(x, y, 'npc_down');
|
|
newNpc.setScale(2.4); // Doppelte Skalierung (1.2 * 2)
|
|
|
|
// NPC ist im Anonymitätsmodus (komplett schwarz)
|
|
newNpc.setTint(0x000000);
|
|
|
|
// Speichere die Charakter-ID im NPC-Objekt
|
|
newNpc.characterId = selectedCharacter.id;
|
|
newNpc.characterName = selectedCharacter.name;
|
|
newNpc.characterPersonality = selectedCharacter.personality;
|
|
|
|
// Animation: NPC läuft durch die Tür herein
|
|
// Starte an der Türposition
|
|
newNpc.y = doorY;
|
|
|
|
// Animiere den NPC, damit er durch die Tür hereinläuft
|
|
this.tweens.add({
|
|
targets: newNpc,
|
|
y: this.map.heightInPixels / 2, // Zielposition in der Mitte des Spielfelds
|
|
duration: 2000,
|
|
ease: 'Linear', // Gleichmäßige Bewegung für einen Laufeffekt
|
|
onUpdate: () => {
|
|
// Aktualisiere die Position des Debug-Textes während der Animation
|
|
if (newNpc.debugText) {
|
|
newNpc.debugText.x = newNpc.x;
|
|
newNpc.debugText.y = newNpc.y + 20;
|
|
}
|
|
|
|
// Wechsle zwischen verschiedenen Frames, um eine Laufanimation zu simulieren
|
|
if (Math.floor(Date.now() / 150) % 2 === 0) {
|
|
newNpc.setTexture('npc_down');
|
|
} else {
|
|
// Wenn es eine alternative Textur gibt, verwende diese
|
|
// Ansonsten bleibt es bei 'npc_down'
|
|
if (this.textures.exists('npc_down_walk')) {
|
|
newNpc.setTexture('npc_down_walk');
|
|
}
|
|
}
|
|
},
|
|
});
|
|
|
|
// Füge einen Debug-Text unter dem NPC hinzu
|
|
// Bei anonymen NPCs nur "Anonym" anzeigen
|
|
const debugText = this.add.text(x, y + 20, 'Anonym', {
|
|
fontSize: '10px',
|
|
fontFamily: 'Arial',
|
|
fill: '#ffffff',
|
|
stroke: '#000000',
|
|
strokeThickness: 2,
|
|
align: 'center',
|
|
});
|
|
debugText.setOrigin(0.5, 0);
|
|
newNpc.debugText = debugText; // Speichere die Referenz im NPC-Objekt
|
|
|
|
// Kollisionen mit Hindernissen
|
|
if (this.obstacles) {
|
|
this.physics.add.collider(newNpc, this.obstacles);
|
|
}
|
|
|
|
// Kollision mit dem Spieler
|
|
if (this.player) {
|
|
this.physics.add.collider(newNpc, this.player, this.showInteractionPrompt, null, this);
|
|
}
|
|
|
|
// Füge den NPC zum Array hinzu
|
|
this.npcs.push(newNpc);
|
|
|
|
// Setze den aktuellen NPC
|
|
this.npc = newNpc;
|
|
this.npcState.currentNpcIndex = this.npcs.length - 1;
|
|
|
|
console.log(`Neuer NPC erstellt: ${selectedCharacter.name} (ID: ${selectedCharacter.id})`);
|
|
|
|
return newNpc;
|
|
}
|
|
|
|
// Methode zum Erstellen eines Test-NPCs an einer festen Position
|
|
createTestNPC() {
|
|
console.log('createTestNPC wird aufgerufen');
|
|
|
|
// Feste Position in der Nähe des Spielers
|
|
const x = 400;
|
|
const y = 400;
|
|
|
|
// Erstelle den NPC
|
|
const testNpc = this.physics.add.sprite(x, y, 'npc_down');
|
|
testNpc.setScale(1.2);
|
|
|
|
// NPC ist im Anonymitätsmodus (komplett schwarz)
|
|
testNpc.setTint(0x000000);
|
|
|
|
// Speichere die Charakter-ID im NPC-Objekt
|
|
testNpc.characterId = 1;
|
|
testNpc.characterName = 'Test NPC';
|
|
testNpc.characterPersonality = 'Ein Test-NPC zum Testen der Anzeige';
|
|
|
|
// Kollisionen mit Hindernissen
|
|
if (this.obstacles) {
|
|
this.physics.add.collider(testNpc, this.obstacles);
|
|
}
|
|
|
|
// Kollision mit dem Spieler
|
|
if (this.player) {
|
|
this.physics.add.collider(testNpc, this.player, this.showInteractionPrompt, null, this);
|
|
}
|
|
|
|
// Füge den NPC zum Array hinzu
|
|
if (!this.npcs) {
|
|
this.npcs = [];
|
|
}
|
|
this.npcs.push(testNpc);
|
|
|
|
// Setze den aktuellen NPC
|
|
this.npc = testNpc;
|
|
if (this.npcState) {
|
|
this.npcState.currentNpcIndex = this.npcs.length - 1;
|
|
}
|
|
|
|
console.log(`Test-NPC erstellt an Position ${x}, ${y}`);
|
|
|
|
return testNpc;
|
|
}
|
|
|
|
// Methode zum Initialisieren der UI-Elemente
|
|
createUI() {
|
|
// Interaktions-Prompt
|
|
this.interactionPrompt.setVisible(false);
|
|
|
|
// Chat-Eingabe erstellen
|
|
this.createChatInput();
|
|
|
|
// Interaktions-Taste (E) für Dialog
|
|
this.interactKey = this.input.keyboard.addKey('E');
|
|
|
|
// NPC-Bewegung
|
|
this.time.addEvent({
|
|
delay: 3000, // Alle 3 Sekunden
|
|
callback: this.moveNPC,
|
|
callbackScope: this,
|
|
loop: true,
|
|
});
|
|
}
|
|
|
|
// Methode zum Initialisieren des NPC-Status
|
|
initNPCState() {
|
|
console.log('initNPCState wird aufgerufen');
|
|
// NPC-Status initialisieren
|
|
this.npcState = {
|
|
isInConversation: false,
|
|
lastMessage: '',
|
|
isWaitingForResponse: false,
|
|
identityRevealed: false,
|
|
discoveredNPCs: [], // Liste der bereits entdeckten NPCs (IDs)
|
|
currentNpcIndex: -1,
|
|
};
|
|
|
|
// Debug-Ausgabe der NPC-Charaktere
|
|
if (this.npcCharacters) {
|
|
console.log('Verfügbare NPC-Charaktere:');
|
|
this.npcCharacters.forEach((char) => {
|
|
console.log(`- ${char.name} (ID: ${char.id})`);
|
|
});
|
|
}
|
|
|
|
console.log('NPC-Status initialisiert');
|
|
}
|
|
|
|
moveNPC() {
|
|
// Zufällige Bewegung des NPCs
|
|
if (!this.npc) return;
|
|
|
|
// Stoppe vorherige Bewegung
|
|
this.npc.setVelocity(0);
|
|
|
|
// 30% Chance, dass der NPC sich bewegt
|
|
if (Math.random() < 0.3) {
|
|
const speed = 50;
|
|
const direction = Math.floor(Math.random() * 4); // 0: up, 1: right, 2: down, 3: left
|
|
|
|
switch (direction) {
|
|
case 0: // up
|
|
this.npc.setVelocityY(-speed);
|
|
this.npc.setTexture('npc_up');
|
|
break;
|
|
case 1: // right
|
|
this.npc.setVelocityX(speed);
|
|
this.npc.setTexture('npc_down'); // Wir haben nur up/down Texturen
|
|
break;
|
|
case 2: // down
|
|
this.npc.setVelocityY(speed);
|
|
this.npc.setTexture('npc_down');
|
|
break;
|
|
case 3: // left
|
|
this.npc.setVelocityX(-speed);
|
|
this.npc.setTexture('npc_up'); // Wir haben nur up/down Texturen
|
|
break;
|
|
}
|
|
|
|
// Stoppe die Bewegung nach 1-2 Sekunden
|
|
this.time.delayedCall(
|
|
1000 + Math.random() * 1000,
|
|
() => {
|
|
if (this.npc) this.npc.setVelocity(0);
|
|
},
|
|
[],
|
|
this
|
|
);
|
|
}
|
|
}
|
|
|
|
createChatInput() {
|
|
const width = this.cameras.main.width;
|
|
const height = this.cameras.main.height;
|
|
const padding = 20;
|
|
const chatWidth = width - padding * 2;
|
|
const chatHeight = 250; // Erhöhte Höhe für mehr Platz
|
|
|
|
// Chat-Hintergrund mit abgerundeten Ecken und Schatten
|
|
this.chatBackground = this.add.graphics();
|
|
this.chatBackground.fillStyle(0x1a1a2a, 0.9); // Dunkleres Blau mit höherer Opazität
|
|
this.chatBackground.fillRoundedRect(
|
|
padding,
|
|
height - chatHeight - padding,
|
|
chatWidth,
|
|
chatHeight,
|
|
10 // Abgerundete Ecken
|
|
);
|
|
|
|
// Füge einen Rahmen hinzu
|
|
this.chatBackground.lineStyle(2, 0x4a6fa5, 1); // Blauer Rahmen
|
|
this.chatBackground.strokeRoundedRect(
|
|
padding,
|
|
height - chatHeight - padding,
|
|
chatWidth,
|
|
chatHeight,
|
|
10
|
|
);
|
|
|
|
this.chatBackground.setScrollFactor(0);
|
|
this.chatBackground.setVisible(false);
|
|
|
|
// Titel für den Chat
|
|
this.chatTitle = this.add.text(
|
|
width / 2,
|
|
height - chatHeight - padding + 20,
|
|
'Gespräch mit NPC',
|
|
{
|
|
fontSize: '18px',
|
|
fontFamily: 'Arial',
|
|
fontStyle: 'bold',
|
|
fill: '#ffffff',
|
|
align: 'center',
|
|
}
|
|
);
|
|
this.chatTitle.setOrigin(0.5, 0.5);
|
|
this.chatTitle.setScrollFactor(0);
|
|
this.chatTitle.setVisible(false);
|
|
|
|
// NPC-Antwortbereich mit besserem Styling
|
|
this.npcResponse = this.add.text(padding + 15, height - chatHeight - padding + 50, '', {
|
|
fontSize: '16px',
|
|
fontFamily: 'Arial',
|
|
fill: '#e0e0ff', // Helleres Blau für bessere Lesbarkeit
|
|
padding: { x: 10, y: 10 },
|
|
wordWrap: { width: chatWidth - 50 },
|
|
lineSpacing: 6,
|
|
});
|
|
this.npcResponse.setScrollFactor(0);
|
|
this.npcResponse.setVisible(false);
|
|
|
|
// Trennlinie zwischen Antwort und Eingabe
|
|
this.chatDivider = this.add.graphics();
|
|
this.chatDivider.lineStyle(1, 0x4a6fa5, 0.8); // Blauer Trennstrich
|
|
this.chatDivider.lineBetween(padding + 15, height - 90, width - padding - 15, height - 90);
|
|
this.chatDivider.setScrollFactor(0);
|
|
this.chatDivider.setVisible(false);
|
|
|
|
// Chat-Eingabefeld mit besserem Styling
|
|
const inputBg = this.add.graphics();
|
|
inputBg.fillStyle(0x2a2a3a, 1); // Dunklerer Hintergrund für Eingabefeld
|
|
inputBg.fillRoundedRect(
|
|
padding + 15,
|
|
height - 70,
|
|
chatWidth - 230, // Platz für Buttons lassen
|
|
40,
|
|
5
|
|
);
|
|
inputBg.setScrollFactor(0);
|
|
inputBg.setVisible(false);
|
|
this.inputBackground = inputBg;
|
|
|
|
this.chatInput = this.add.text(padding + 25, height - 65, 'Tippe deine Nachricht hier ein...', {
|
|
fontSize: '16px',
|
|
fontFamily: 'Arial',
|
|
fill: '#bbbbbb', // Hellgrau für Platzhaltertext
|
|
padding: { x: 5, y: 5 },
|
|
});
|
|
this.chatInput.setScrollFactor(0);
|
|
this.chatInput.setVisible(false);
|
|
|
|
// Chat-Senden-Button mit besserem Styling
|
|
const sendBg = this.add.graphics();
|
|
sendBg.fillStyle(0x4a6fa5, 1); // Blauer Button
|
|
sendBg.fillRoundedRect(width - padding - 100, height - 70, 85, 40, 5);
|
|
sendBg.setScrollFactor(0);
|
|
sendBg.setVisible(false);
|
|
this.sendBackground = sendBg;
|
|
|
|
this.chatSendButton = this.add.text(width - padding - 57.5, height - 50, 'Senden', {
|
|
fontSize: '16px',
|
|
fontFamily: 'Arial',
|
|
fontStyle: 'bold',
|
|
fill: '#ffffff',
|
|
});
|
|
this.chatSendButton.setOrigin(0.5, 0.5);
|
|
this.chatSendButton.setScrollFactor(0);
|
|
this.chatSendButton.setVisible(false);
|
|
this.chatSendButton.setInteractive({ useHandCursor: true });
|
|
this.chatSendButton.on('pointerdown', () => this.sendChatMessage());
|
|
|
|
// Hover-Effekt für den Senden-Button
|
|
this.chatSendButton.on('pointerover', () => {
|
|
sendBg.clear();
|
|
sendBg.fillStyle(0x5a7fb5, 1); // Hellerer Blau bei Hover
|
|
sendBg.fillRoundedRect(width - padding - 100, height - 70, 85, 40, 5);
|
|
});
|
|
|
|
this.chatSendButton.on('pointerout', () => {
|
|
sendBg.clear();
|
|
sendBg.fillStyle(0x4a6fa5, 1); // Normales Blau
|
|
sendBg.fillRoundedRect(width - padding - 100, height - 70, 85, 40, 5);
|
|
});
|
|
|
|
// X-Icon zum Schließen in der oberen rechten Ecke
|
|
const closeIconSize = 24;
|
|
const closeIconPadding = 10;
|
|
|
|
// Runder Hintergrund für das X-Icon
|
|
const closeIconBg = this.add.graphics();
|
|
closeIconBg.fillStyle(0x8a4a4a, 0.7); // Halbtransparentes Rot
|
|
closeIconBg.fillCircle(
|
|
width - padding - closeIconPadding,
|
|
height - chatHeight - padding + closeIconPadding + closeIconSize / 2,
|
|
closeIconSize / 2
|
|
);
|
|
closeIconBg.setScrollFactor(0);
|
|
closeIconBg.setVisible(false);
|
|
this.cancelBackground = closeIconBg;
|
|
|
|
// X-Icon erstellen mit Linien
|
|
const closeIcon = this.add.graphics();
|
|
// Zeichne das X
|
|
closeIcon.lineStyle(3, 0xffffff, 1);
|
|
// Erste Linie des X (von oben links nach unten rechts)
|
|
closeIcon.lineBetween(
|
|
width - padding - closeIconPadding - closeIconSize / 3,
|
|
height - chatHeight - padding + closeIconPadding + closeIconSize / 3,
|
|
width - padding - closeIconPadding + closeIconSize / 3,
|
|
height - chatHeight - padding + closeIconPadding + (closeIconSize * 2) / 3
|
|
);
|
|
// Zweite Linie des X (von oben rechts nach unten links)
|
|
closeIcon.lineBetween(
|
|
width - padding - closeIconPadding + closeIconSize / 3,
|
|
height - chatHeight - padding + closeIconPadding + closeIconSize / 3,
|
|
width - padding - closeIconPadding - closeIconSize / 3,
|
|
height - chatHeight - padding + closeIconPadding + (closeIconSize * 2) / 3
|
|
);
|
|
closeIcon.setScrollFactor(0);
|
|
closeIcon.setVisible(false);
|
|
this.chatCancelButton = closeIcon;
|
|
|
|
// Interaktiver Bereich für das X-Icon
|
|
const closeHitArea = this.add.rectangle(
|
|
width - padding - closeIconPadding,
|
|
height - chatHeight - padding + closeIconPadding + closeIconSize / 2,
|
|
closeIconSize * 1.5,
|
|
closeIconSize * 1.5
|
|
);
|
|
closeHitArea.setScrollFactor(0);
|
|
closeHitArea.setVisible(false); // Unsichtbarer Klickbereich
|
|
closeHitArea.setInteractive({ useHandCursor: true });
|
|
closeHitArea.on('pointerdown', () => this.closeChatInput());
|
|
this.closeHitArea = closeHitArea;
|
|
|
|
// Hover-Effekt für das X-Icon
|
|
closeHitArea.on('pointerover', () => {
|
|
closeIconBg.clear();
|
|
closeIconBg.fillStyle(0x9a5a5a, 0.9); // Helleres, weniger transparentes Rot bei Hover
|
|
closeIconBg.fillCircle(
|
|
width - padding - closeIconPadding,
|
|
height - chatHeight - padding + closeIconPadding + closeIconSize / 2,
|
|
(closeIconSize / 2) * 1.1 // Leicht größer bei Hover
|
|
);
|
|
});
|
|
|
|
closeHitArea.on('pointerout', () => {
|
|
closeIconBg.clear();
|
|
closeIconBg.fillStyle(0x8a4a4a, 0.7); // Normales Rot
|
|
closeIconBg.fillCircle(
|
|
width - padding - closeIconPadding,
|
|
height - chatHeight - padding + closeIconPadding + closeIconSize / 2,
|
|
closeIconSize / 2
|
|
);
|
|
});
|
|
|
|
// Aktiviere Tastatureingabe
|
|
this.userInput = '';
|
|
this.input.keyboard.on('keydown', this.handleKeyInput, this);
|
|
|
|
// Letzte NPC-Antwort
|
|
this.lastNpcResponse = '';
|
|
|
|
// Konversationsverlauf für das LLM
|
|
this.conversationHistory = [];
|
|
}
|
|
|
|
showInteractionPrompt() {
|
|
// Wenn der Spieler mit dem NPC kollidiert
|
|
if (!this.npc || !this.player) return;
|
|
|
|
// Zeige Interaktions-Prompt über dem NPC
|
|
this.interactionPrompt.setPosition(
|
|
this.npc.x - this.interactionPrompt.width / 2,
|
|
this.npc.y - 40
|
|
);
|
|
|
|
this.interactionPrompt.setVisible(true);
|
|
|
|
// Verstecke den Prompt nach 2 Sekunden
|
|
this.time.delayedCall(
|
|
2000,
|
|
() => {
|
|
this.interactionPrompt.setVisible(false);
|
|
},
|
|
[],
|
|
this
|
|
);
|
|
}
|
|
|
|
talkToNPC() {
|
|
// Wenn der Spieler mit dem NPC spricht
|
|
if (!this.npc || !this.player || this.npcState.isInConversation) return;
|
|
|
|
// Stoppe die Bewegung des Spielers und des NPCs
|
|
this.player.setVelocity(0);
|
|
this.npc.setVelocity(0);
|
|
|
|
// Drehe den NPC zum Spieler
|
|
if (this.player.x < this.npc.x) {
|
|
this.npc.setTexture('npc_up'); // Links (wir haben nur up/down)
|
|
} else {
|
|
this.npc.setTexture('npc_down'); // Rechts
|
|
}
|
|
|
|
// Starte die Konversation
|
|
this.npcState.isInConversation = true;
|
|
|
|
// Starte direkt den Chat ohne Sprechblase über dem NPC
|
|
this.lastNpcResponse = 'Verhüllt von Zeit,\nwer könnt es sein?';
|
|
|
|
// Zeige Chat-Eingabe direkt
|
|
this.openChatInput();
|
|
}
|
|
|
|
openChatInput() {
|
|
// Aktiviere Chat-Eingabe
|
|
this.chatBackground.setVisible(true);
|
|
this.chatTitle.setVisible(true);
|
|
this.inputBackground.setVisible(true);
|
|
this.chatInput.setVisible(true);
|
|
this.sendBackground.setVisible(true);
|
|
this.chatSendButton.setVisible(true);
|
|
this.cancelBackground.setVisible(true);
|
|
this.chatCancelButton.setVisible(true);
|
|
this.closeHitArea.setVisible(true); // Klickbereich für X-Icon
|
|
this.npcResponse.setVisible(true);
|
|
this.chatDivider.setVisible(true);
|
|
|
|
// Setze Eingabe zurück
|
|
this.userInput = '';
|
|
this.chatInput.setText('Tippe deine Nachricht hier ein...');
|
|
this.chatInput.setStyle({ fill: '#bbbbbb' }); // Platzhaltertext in Grau
|
|
|
|
// Zeige letzte NPC-Antwort an
|
|
this.npcResponse.setText(this.lastNpcResponse || 'Sprich mit dem NPC...');
|
|
|
|
// Aktualisiere den Titel mit dem NPC-Namen
|
|
this.chatTitle.setText('Gespräch mit Unbekanntem');
|
|
}
|
|
|
|
closeChatInput() {
|
|
// Deaktiviere Chat-Eingabe
|
|
this.chatBackground.setVisible(false);
|
|
this.chatTitle.setVisible(false);
|
|
this.inputBackground.setVisible(false);
|
|
this.chatInput.setVisible(false);
|
|
this.sendBackground.setVisible(false);
|
|
this.chatSendButton.setVisible(false);
|
|
this.cancelBackground.setVisible(false);
|
|
this.chatCancelButton.setVisible(false);
|
|
this.closeHitArea.setVisible(false); // Klickbereich für X-Icon
|
|
this.npcResponse.setVisible(false);
|
|
this.chatDivider.setVisible(false);
|
|
|
|
// Beende die Konversation
|
|
this.npcState.isInConversation = false;
|
|
this.npcDialog.setVisible(false);
|
|
}
|
|
|
|
handleKeyInput(event) {
|
|
// Wenn keine Chat-Eingabe aktiv ist, ignoriere Tastatureingabe
|
|
if (!this.chatInput.visible) return;
|
|
|
|
// Enter-Taste zum Senden
|
|
if (event.keyCode === 13) {
|
|
// Enter
|
|
this.sendChatMessage();
|
|
return;
|
|
}
|
|
|
|
// Escape-Taste zum Abbrechen
|
|
if (event.keyCode === 27) {
|
|
// Escape
|
|
this.closeChatInput();
|
|
return;
|
|
}
|
|
|
|
// Backspace zum Löschen
|
|
if (event.keyCode === 8 && this.userInput.length > 0) {
|
|
// Backspace
|
|
this.userInput = this.userInput.slice(0, -1);
|
|
}
|
|
// Normale Tasteneingabe
|
|
else if (event.keyCode >= 32 && event.keyCode <= 126) {
|
|
// Druckbare Zeichen
|
|
this.userInput += event.key;
|
|
}
|
|
|
|
// Aktualisiere Anzeige
|
|
if (this.userInput.length === 0) {
|
|
this.chatInput.setText('Tippe deine Nachricht hier ein...');
|
|
this.chatInput.setStyle({ fill: '#bbbbbb' }); // Platzhaltertext in Grau
|
|
} else {
|
|
this.chatInput.setText(this.userInput);
|
|
this.chatInput.setStyle({ fill: '#ffffff' }); // Aktiver Text in Weiß
|
|
|
|
// Visuelles Feedback beim Tippen
|
|
this.tweens.add({
|
|
targets: this.inputBackground,
|
|
alpha: 0.7,
|
|
duration: 50,
|
|
yoyo: true,
|
|
ease: 'Power1',
|
|
});
|
|
}
|
|
}
|
|
|
|
updateNpcResponse(response) {
|
|
// Aktualisiere die letzte NPC-Antwort
|
|
this.lastNpcResponse = response;
|
|
|
|
// Aktualisiere die Anzeige, wenn sichtbar
|
|
if (this.npcResponse.visible) {
|
|
this.npcResponse.setText(response);
|
|
}
|
|
}
|
|
|
|
// Methode, die aufgerufen wird, wenn der Spieler die Identität des NPCs erraten hat
|
|
revealIdentity() {
|
|
// Markiere, dass die Identität aufgedeckt wurde
|
|
this.npcState.identityRevealed = true;
|
|
|
|
// Füge den aktuellen NPC zur Liste der entdeckten NPCs hinzu
|
|
if (this.npc && this.npc.characterId) {
|
|
// Prüfe, ob der NPC bereits in der Liste ist, um Duplikate zu vermeiden
|
|
if (!this.npcState.discoveredNPCs.includes(this.npc.characterId)) {
|
|
this.npcState.discoveredNPCs.push(this.npc.characterId);
|
|
console.log(`NPC ${this.npc.characterName} (ID: ${this.npc.characterId}) wurde entdeckt!`);
|
|
console.log('Aktualisierte Liste der entdeckten NPCs:', this.npcState.discoveredNPCs);
|
|
} else {
|
|
console.log(
|
|
`NPC ${this.npc.characterName} (ID: ${this.npc.characterId}) wurde bereits zuvor entdeckt.`
|
|
);
|
|
}
|
|
}
|
|
|
|
// Spiele einen Soundeffekt ab (falls vorhanden)
|
|
// this.sound.play('reveal');
|
|
|
|
// Entferne die schwarze Einfärbung, um den NPC normal anzuzeigen
|
|
this.npc.clearTint();
|
|
|
|
// Aktualisiere den Debug-Text mit dem richtigen Namen ohne ID
|
|
if (this.npc.debugText) {
|
|
this.npc.debugText.setText(this.npc.characterName);
|
|
this.npc.debugText.setStyle({
|
|
fontSize: '12px',
|
|
fontFamily: 'Arial',
|
|
fontStyle: 'bold',
|
|
fill: '#ffff00', // Gelb für aufgedeckte NPCs
|
|
stroke: '#000000',
|
|
strokeThickness: 3,
|
|
align: 'center',
|
|
});
|
|
}
|
|
|
|
// Kurzer gelber Blitz-Effekt
|
|
this.npc.setTint(0xffff00);
|
|
this.time.delayedCall(300, () => {
|
|
if (this.npcState.identityRevealed) {
|
|
this.npc.clearTint(); // Zur normalen Farbe zurückkehren
|
|
}
|
|
});
|
|
|
|
// Erstelle einen Partikeleffekt um den NPC
|
|
const particles = this.add.particles('particle'); // Du musst ein Partikel-Sprite laden
|
|
|
|
// Erstelle einen Emitter für den Partikeleffekt
|
|
if (particles.createEmitter) {
|
|
const emitter = particles.createEmitter({
|
|
x: this.npc.x,
|
|
y: this.npc.y,
|
|
speed: { min: 50, max: 100 },
|
|
angle: { min: 0, max: 360 },
|
|
scale: { start: 0.5, end: 0 },
|
|
blendMode: 'ADD',
|
|
lifespan: 1000,
|
|
gravityY: 0,
|
|
});
|
|
|
|
// Stoppe den Emitter nach 2 Sekunden
|
|
this.time.delayedCall(2000, () => {
|
|
emitter.stop();
|
|
});
|
|
} else {
|
|
console.warn('Particles not available or not properly loaded');
|
|
}
|
|
|
|
// Aktualisiere den Chat-Titel, um den NPC-Namen anzuzeigen
|
|
if (this.chatTitle && this.chatTitle.visible) {
|
|
this.chatTitle.setText(`Gespräch mit ${this.npc.characterName}`);
|
|
}
|
|
|
|
// Zeige eine spezielle Nachricht an
|
|
const revealText = this.add.text(
|
|
this.cameras.main.width / 2,
|
|
this.cameras.main.height / 3,
|
|
`Du hast ${this.npc.characterName} entlarvt!`,
|
|
{
|
|
fontSize: '24px',
|
|
fontFamily: 'Arial',
|
|
fontStyle: 'bold',
|
|
fill: '#ffff00',
|
|
stroke: '#000000',
|
|
strokeThickness: 4,
|
|
align: 'center',
|
|
}
|
|
);
|
|
revealText.setOrigin(0.5);
|
|
revealText.setScrollFactor(0);
|
|
|
|
// Blende die Nachricht nach einigen Sekunden aus
|
|
this.tweens.add({
|
|
targets: revealText,
|
|
alpha: 0,
|
|
duration: 2000,
|
|
delay: 3000,
|
|
onComplete: () => {
|
|
revealText.destroy();
|
|
|
|
// Erstelle nach kurzer Verzögerung einen neuen NPC
|
|
this.time.delayedCall(1000, () => {
|
|
// Erstelle einen neuen NPC nur, wenn noch nicht alle entdeckt wurden
|
|
const newNpc = this.createNewNPC();
|
|
if (newNpc) {
|
|
// Zeige eine Benachrichtigung an
|
|
const newNpcText = this.add.text(
|
|
this.cameras.main.width / 2,
|
|
this.cameras.main.height / 3,
|
|
'Ein neuer geheimnisvoller NPC ist erschienen!',
|
|
{
|
|
fontSize: '20px',
|
|
fontFamily: 'Arial',
|
|
fontStyle: 'bold',
|
|
fill: '#ffffff',
|
|
stroke: '#000000',
|
|
strokeThickness: 3,
|
|
align: 'center',
|
|
}
|
|
);
|
|
newNpcText.setOrigin(0.5);
|
|
newNpcText.setScrollFactor(0);
|
|
|
|
// Blende die Nachricht nach einigen Sekunden aus
|
|
this.tweens.add({
|
|
targets: newNpcText,
|
|
alpha: 0,
|
|
duration: 1500,
|
|
delay: 2500,
|
|
onComplete: () => newNpcText.destroy(),
|
|
});
|
|
}
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
async sendChatMessage() {
|
|
// Wenn keine Nachricht eingegeben wurde
|
|
if (this.userInput.length === 0 || this.npcState.isWaitingForResponse) return;
|
|
|
|
const message = this.userInput;
|
|
this.userInput = '';
|
|
|
|
// Füge die Nachricht des Spielers zum Konversationsverlauf hinzu (für das LLM)
|
|
this.conversationHistory.push({
|
|
type: 'user',
|
|
message: message,
|
|
});
|
|
|
|
// Zeige "Nachricht wird gesendet"-Status
|
|
this.chatInput.setText('Nachricht wird gesendet...');
|
|
this.npcState.isWaitingForResponse = true;
|
|
|
|
try {
|
|
// Sende Anfrage an den Server mit der Konversationshistorie
|
|
const response = await fetch('http://localhost:3000/api/chat', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
message,
|
|
conversationHistory: this.conversationHistory,
|
|
characterName: this.npc ? this.npc.characterName : null,
|
|
characterPersonality: this.npc ? this.npc.characterPersonality : null,
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
// Verarbeite die Antwort
|
|
let npcResponse = 'Entschuldigung, ich habe dich nicht verstanden.';
|
|
|
|
if (data.response) {
|
|
npcResponse = data.response;
|
|
|
|
// Füge die NPC-Antwort zum Konversationsverlauf hinzu (für das LLM)
|
|
this.conversationHistory.push({
|
|
type: 'npc',
|
|
message: npcResponse,
|
|
});
|
|
|
|
// Prüfe, ob die Identität aufgedeckt wurde
|
|
if (data.identityRevealed) {
|
|
console.log('Identität aufgedeckt!');
|
|
|
|
// Spiele eine Animation ab oder führe eine spezielle Aktion aus
|
|
this.revealIdentity();
|
|
}
|
|
}
|
|
|
|
// Aktualisiere die NPC-Antwort
|
|
this.updateNpcResponse(npcResponse);
|
|
|
|
// Füge die Antwort des NPCs zum Konversationsverlauf hinzu (für das LLM)
|
|
this.conversationHistory.push({
|
|
type: 'npc',
|
|
message: npcResponse,
|
|
});
|
|
} catch (error) {
|
|
console.error('Fehler beim Senden der Nachricht:', error);
|
|
|
|
// Aktualisiere die NPC-Antwort mit Fehlermeldung
|
|
this.updateNpcResponse('Entschuldigung, ich kann gerade nicht antworten.');
|
|
|
|
// Füge die Fehlermeldung zum Konversationsverlauf hinzu (für das LLM)
|
|
this.conversationHistory.push({
|
|
type: 'npc',
|
|
message: 'Entschuldigung, ich kann gerade nicht antworten.',
|
|
});
|
|
|
|
// Fehlermeldung wird nur im Chat-Fenster angezeigt
|
|
}
|
|
|
|
// Setze Status zurück
|
|
this.npcState.isWaitingForResponse = false;
|
|
this.chatInput.setText('Tippe deine Nachricht hier ein...');
|
|
}
|
|
|
|
update() {
|
|
// Spielerbewegung
|
|
this.handlePlayerMovement();
|
|
|
|
// Aktualisiere die Position des Dialogs, wenn er sichtbar ist
|
|
if (this.npcDialog && this.npcDialog.visible && this.npc) {
|
|
this.npcDialog.setPosition(this.npc.x - 100, this.npc.y - 50);
|
|
}
|
|
|
|
// Aktualisiere die Position des Interaktions-Prompts
|
|
if (this.interactionPrompt && this.interactionPrompt.visible && this.npc) {
|
|
this.interactionPrompt.setPosition(this.npc.x - 50, this.npc.y - 30);
|
|
}
|
|
|
|
// Aktualisiere die Position der Debug-Texte für alle NPCs
|
|
if (this.npcs) {
|
|
this.npcs.forEach((npc) => {
|
|
if (npc.debugText) {
|
|
npc.debugText.setPosition(npc.x, npc.y + 20);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
handlePlayerMovement() {
|
|
if (!this.player) return;
|
|
|
|
const speed = 160;
|
|
|
|
// Horizontal
|
|
if (this.cursors.left.isDown) {
|
|
this.player.setVelocityX(-speed);
|
|
this.player.setTexture('player_left');
|
|
} else if (this.cursors.right.isDown) {
|
|
this.player.setVelocityX(speed);
|
|
this.player.setTexture('player_right');
|
|
} else {
|
|
this.player.setVelocityX(0);
|
|
}
|
|
|
|
// Vertikal
|
|
if (this.cursors.up.isDown) {
|
|
this.player.setVelocityY(-speed);
|
|
if (!this.cursors.left.isDown && !this.cursors.right.isDown) {
|
|
this.player.setTexture('player_up');
|
|
}
|
|
} else if (this.cursors.down.isDown) {
|
|
this.player.setVelocityY(speed);
|
|
if (!this.cursors.left.isDown && !this.cursors.right.isDown) {
|
|
this.player.setTexture('player_down');
|
|
}
|
|
} else {
|
|
this.player.setVelocityY(0);
|
|
}
|
|
|
|
// Interaktion mit NPCs prüfen, wenn E gedrückt wird
|
|
if (
|
|
this.interactKey &&
|
|
Phaser.Input.Keyboard.JustDown(this.interactKey) &&
|
|
this.npcs &&
|
|
this.npcs.length > 0
|
|
) {
|
|
// Prüfe die Distanz zu allen NPCs
|
|
let closestNPC = null;
|
|
let closestDistance = 100; // Maximale Interaktionsdistanz
|
|
|
|
for (let i = 0; i < this.npcs.length; i++) {
|
|
const npc = this.npcs[i];
|
|
const distance = Phaser.Math.Distance.Between(this.player.x, this.player.y, npc.x, npc.y);
|
|
|
|
console.log(`Abstand zu NPC ${i}: ${distance}`);
|
|
|
|
// Wenn dieser NPC näher ist als der bisher nächste und in Reichweite
|
|
if (distance < closestDistance) {
|
|
closestDistance = distance;
|
|
closestNPC = npc;
|
|
this.npcState.currentNpcIndex = i;
|
|
}
|
|
}
|
|
|
|
// Wenn ein NPC in Reichweite ist
|
|
if (closestNPC) {
|
|
console.log(
|
|
`Interaktion mit NPC an Position ${closestNPC.x}, ${closestNPC.y}, Abstand: ${closestDistance}`
|
|
);
|
|
this.npc = closestNPC; // Setze den aktuellen NPC
|
|
this.talkToNPC();
|
|
}
|
|
}
|
|
}
|
|
}
|