Von Händlern, Kisten und Münzen (Arduino Esplora, Part 8)
Ok, so richtiger Handel wird hier nicht stattfinden. Dafür reicht der Speicher nicht. Oder? In erster Linie sollen nur Grund Funktionen Umgesetzt werden. Ziel wird sein, wenn die Figur vor dem Händler oder einer Kiste steht, dann sollte sich der Inhalt Zeigen. Anschließend kann ein Objekt Ausgewählt und in die eigene Tasche übertragen werden. Leider passt das nicht alles in einen Blogpost, so dass der Inhalt mit der Waren Anzeige in einem späteren Post kommt.
Anforderung
Beim Händler können Gegenstände erworben werden und diese in Kisten abgelegt werden. Das erfordert einige mehr Programmcodezeilen und daher muss an der Stelle wieder eine neue Seite mit dem Namen 'TraderComponent' angelegt werden.
Am Anfang werden die Werte für Händler und Kisten hinterlegt, die später über den Flashspeicher abgerufen werden. Die Münzen werden hier ebenfalls abgelegt als Funktionsvariable, wird aber erst in einen späteren Post weiter behandelt. (im Folgender Programmcode sind Kommentare und Bilddaten gekürzt, ggf. schaut ihr am besten in die Github Sourcen)
// # Coins, im Besitz
int16_t coins = 25;
int16_t lastStateCoins = 0;
// # Common Text
// Begruessungstext (Sollte immer verschieden sein.)
const PROGMEM char traderStartText[] = "Hallo, was darf ich ihnen verkaufen?";
// Wenn zu wenig Muenzen zum Kaufen da sind
const PROGMEM char traderNotEnough[] = "Du hast nicht genug Muenzen.";
// Frage zum Kauf
const PROGMEM char traderYouWantToBuy[] = "Kaufen?";
// # Common Sprite
// Bild vom Handler / Die Farbe des Shirts, kann veraendert werden.
const PROGMEM byte traderSpriteFrontMen[160] = { … };
const PROGMEM byte traderSpriteFrontWomen[160] = { … };
const PROGMEM byte boxSpriteFront[100] = { … };
const PROGMEM byte coinSpiteIcon[49] = { … };
// # TRADER
// temp Variablen zum zwischen laden.
char traderName[1];
char traderdescription[1];
byte traderItems[4];
// '0' bedeutet immer nicht belegt.
// #######################################
// ID 1
// Name des Handlers
const PROGMEM char trader01Name[5] = "Surie";
// Kurze Beschreibung
const PROGMEM char trader01Description[11] = "Verkaeferin";
// Dinge zum verkauf
const PROGMEM byte trader01Items[4] = { 2, 0, 0, 0 }; // 2 = Kamera
// 0 = Taschenplaetze werden wie angegeben befullt.
// Stellen werden Stellenweise in Bit herausgenommen
byte trader01ItemsClear = 0;
// # Box
// '0' bedeutet immer nicht belegt.
// #######################################
// ID 1
// Name des Handlers
const PROGMEM char box01Name[11] = "Meine Kiste";
// Kurze Beschreibung
const PROGMEM char box01Description[25] = "Dinge die man so braucht.";
// Dinge zum verkauf
const PROGMEM byte box01Items[4] = { 3, 0, 0, 0 }; // 3 = Foto
void memCopyItems(byte arrayContent[], byte traderItemsClear) {
if(traderItemsClear == 128) {
traderItemsClear-= 128;
traderItems[0] = 0;
}
else { traderItems[0] = pgm_read_byte_near(arrayContent + 0); }
if(traderItemsClear >= 64) {
traderItemsClear-= 64;
traderItems[1] = 0;
}
else { traderItems[1] = pgm_read_byte_near(arrayContent + 1); }
if(traderItemsClear >= 32) {
traderItemsClear-= 32;
traderItems[2] = 0;
}
else { traderItems[2] = pgm_read_byte_near(arrayContent + 2); }
if(traderItemsClear >= 16) {
traderItemsClear-= 16;
traderItems[3] = 0;
}
else { traderItems[3] = pgm_read_byte_near(arrayContent + 3); }
}
void drawTrader(int16_t traderId, int16_t positionX, int16_t positionY) {
if(!mapFigureRerender) {
return;
}
mapFigureRerender = false;
switch(traderId) {
case(1): { // Farbe des Haenderls/in
spriteHairColor1 = 0xEEEC; spriteHairColor2 = 0xE662; // hell Braun 1, hell braun 2
spriteShirtColor1 = 0xD69A; spriteShirtColor2 = 0xB596; // hell grau, grau
spritePantsColor1 = 0x0418; spritePantsColor2 = 0x0312; // Blau 1, blau
memCopy(traderSpriteFrontWomen); // sprite einer Weiblichen figur
memCopyItems(trader01Items, trader01ItemsClear); // Taschen Inhalt
break;
}
case(2): { // Farbe des Haenderls/in
spriteHairColor1 = 0xD615; spriteHairColor2 = 0xBD30; // hell Braun 1, hell braun 2
spriteShirtColor1 = 0xD69A; spriteShirtColor2 = 0xB596; // hell grau, grau
spritePantsColor1 = 0x0418; spritePantsColor2 = 0x0312; // Blau 1, blau
memCopy(traderSpriteFrontMen);
break;
}
default: { break; }
}
drawTile(positionX, positionY, 10, 16, tempArray, false);
}
void drawBox(int16_t boxId, int16_t positionX, int16_t positionY) {
switch(boxId) {
case(1): {
boxColor = 0xDCFE;
break;
}
default: { break; }
}
memCopy(boxSpriteFront);
drawTile(positionX, positionY, 10, 10, tempArray, false);
}
void drawCoinsStatus(bool redraw) {
if(coins != lastStateCoins || redraw) {
EsploraTFT.fillRect(2, 2, 30, 9, mapNumberToColor(1));
memCopy(coinSpiteIcon);
drawTile(3, 3, 7, 7, tempArray, false);
writeValue(12, 3, coins, false);
lastStateCoins = coins;
}
}
Der Händler oder Händlerin sollten für die Kollisionsabfrage den selben Raum einnehmen, wie die eigene Spielfigur. Damit dies funktioniert und der Händler nicht wie ein Karten Block (Kachelgröße) registriert wird, ist eine kleine Erweiterung an der Methode "CanEnterArea" mit "checkCollideOther" notwendig. Etwas abwegig ist die Abfrage der Position, weil diese wiederum über das Byte Array der Karte weiterhin abgefragt wird. Dafür habe ich eine relativ simple Lösung (ggf. in den Github Source schauen)
boolean checkCollideOther(boolean resultColide, int positionX, int positionY) {
// anderes bewegbares objekt
if(resultColide) {
int overlap = 4;
resultColide = checkCollide(positionX, positionY, mapFigurePositionX + (overlap / 2), mapFigurePositionY + (overlap), 10 - overlap, 16 - (overlap * 2));
// zum testen Fenster oeffnen
showWindow = !resultColide;
menueNavigation = showWindow;
}
return resultColide;
}
Message Box
Der Text bekommt sein Platz in einem eigenen Fenster Bereich. Für diese Funktion wird ebenfalls eine weiter Seite angelegt mit dem Namen "WindowComponent". Das Fenster (MessageBox) wird angezeigt, sobald man mit seiner gesteuerten Figur in den Kollisionsradius des Händlers kommt.
Solange der Dialog offen ist, sollte die Figur nicht mehr bewegbar sein und mit dem Joystick kann nur noch in den Taschenplätzen Navigiert werden. Nachdem der Spieler die Schließen-Option Auswählt, verschwindet das Fenster und die Figur sollte sich wieder frei bewegen können.
Was im folgenden Code nicht zu sehen ist, ist die Ausführung des Schließen der MessageBox über den Button 2 bzw. Switch 2.
// Legt ein Fenster in den Vordergrund
bool lastStateShowWindow = false;
bool windowHasRendered = false;
void drawWindow(bool rightSide) {
if(lastStateShowWindow != showWindow && !showWindow) {
lastStateShowWindow = showWindow;
drawStack(true);
}
lastStateShowWindow = showWindow;
if(!showWindow) {
windowHasRendered = false;
return;
}
if(windowHasRendered) {
return;
}
// Mitte des Bildschirm schreiben
int sizeX = 100; int sizeY = 40;
int winPosX = (EsploraTFT.width() / 2) - (sizeX / 2);
int winPosY = (EsploraTFT.height() / 2) - (sizeY / 2);
EsploraTFT.fillRect(winPosX, winPosY, sizeX, sizeY, mapNumberToColor(0));
EsploraTFT.drawRect(winPosX, winPosY, sizeX, sizeY, mapNumberToColor(18));
EsploraTFT.drawRect(winPosX + 2, winPosY + 2, sizeX - 4, sizeY - 4, mapNumberToColor(18));
// Text schreiben
writeText(winPosX + 5, winPosY + 5, "Hallo!");
writeText(winPosX + 5, winPosY + 28, "Schliessen [2]");
windowHasRendered = true;
}
Das Stehenbleiben der Figur muss wiederum auf der Hauptseite festgelegt werden. Dazu muss die Funktion für das Laufen erweitert werden, damit die Figur sich erst nach der Option "Schließen" bewegen kann. Zudem müssen alle Inhalte nach dem Schließen neu gerendert werden mit der Methode 'drawStack'.
void loop() {
…
// Wenn sich X oder Y Position unterscheiden, dann den zu bewegenden Punkt neu zeichnen.
if(!menueNavigation && lastPosX != lastPosXtemp || lastPosY != lastPosYtemp) {
drawStack(false);
}
else if(menueNavigation) {
menueNavigateWithDelay();
}
drawWindow(lastPosX > EsploraTFT.width() / 2);
drawCoinsStatus(false);
}
Der Dialog ist noch nicht ganz fertig. Die Taschenplätze sollten mit dem Joystick erreichbar sein. Das fehlt derzeitig auch für den Rucksack. Dies würde jedoch den Rahmen des Posts sprengen und kommt daher im übernächsten. Für den nächsten Part wird der Programmcode dringend aufgeräumt, auf dass ich näher eingehen will.
Kommentare