From 4a1f011dc123a2b3703c5a6b4ea2589c7344c81d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 2 Dec 2018 00:34:28 +0100 Subject: [PATCH] - Alt HUD scriptification, part 1. --- src/g_shared/shared_hud.cpp | 273 +++----------- wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/base.txt | 2 +- .../zscript/shared/player_inventory.txt | 2 +- wadsrc/static/zscript/statusbar/alt_hud.txt | 349 ++++++++++++++++++ 5 files changed, 399 insertions(+), 228 deletions(-) create mode 100644 wadsrc/static/zscript/statusbar/alt_hud.txt diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index 9684693d32..60c52b4d5e 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -121,6 +121,8 @@ static FTextureID tnt1a0; // We need this to check for empty sprites. static int hudwidth, hudheight; // current width/height for HUD display static int statspace; +DObject *althud; // scripted parts. This is here to make a gradual transition + DVector2 AM_GetPosition(); //--------------------------------------------------------------------------- @@ -131,30 +133,10 @@ DVector2 AM_GetPosition(); //--------------------------------------------------------------------------- static void DrawImageToBox(FTexture * tex, int x, int y, int w, int h, double trans = 0.75) { - double scale1, scale2; - - if (tex) + IFVM(AltHud, DrawImageToBox) { - double texwidth=tex->GetScaledWidthDouble(); - double texheight=tex->GetScaledHeightDouble(); - - if (w>1; - y+=h; - - w=(int)(texwidth*scale1); - h=(int)(texheight*scale1); - - screen->DrawTexture(tex, x, y, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, - DTA_DestWidth, w, DTA_DestHeight, h, DTA_CenterBottomOffset, 1, TAG_DONE); - + VMValue params[] = { althud, tex->id.GetIndex(), x, y, w, h, trans }; + VMCall(func, params, countof(params), nullptr, 0); } } @@ -164,26 +146,14 @@ static void DrawImageToBox(FTexture * tex, int x, int y, int w, int h, double tr // Draws a text but uses a fixed width for all characters // //--------------------------------------------------------------------------- -double GetBottomAlignOffset(FFont *font, int c); static void DrawHudText(FFont *font, int color, char * text, int x, int y, double trans = 0.75) { - int zerowidth; - FTexture *tex_zero = font->GetChar('0', &zerowidth); - - x+=zerowidth/2; - for(int i=0;text[i];i++) + IFVM(AltHud, DrawHudText) { - int width = font->GetCharWidth(text[i]); - double offset = GetBottomAlignOffset(font, text[i]); - - screen->DrawChar(font, color, x, y, text[i], - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, - DTA_LeftOffset, width/2, DTA_TopOffsetF, offset, - /*DTA_CenterBottomOffset, 1,*/ TAG_DONE); - - x += zerowidth; + FString string = text; + VMValue params[] = { althud, font, color, &string, x, y, trans }; + VMCall(func, params, countof(params), nullptr, 0); } } @@ -196,10 +166,11 @@ static void DrawHudText(FFont *font, int color, char * text, int x, int y, doubl static void DrawHudNumber(FFont *font, int color, int num, int x, int y, double trans = 0.75) { - char text[15]; - - mysnprintf(text, countof(text), "%d", num); - DrawHudText(font, color, text, x, y, trans); + IFVM(AltHud, DrawHudNumber) + { + VMValue params[] = { althud, font, color, num, x, y, trans }; + VMCall(func, params, countof(params), nullptr, 0); + } } @@ -209,57 +180,12 @@ static void DrawHudNumber(FFont *font, int color, int num, int x, int y, double // //=========================================================================== -static void DrawStatLine(int x, int &y, const char *prefix, const char *string) -{ - y -= SmallFont->GetHeight()-1; - screen->DrawText(SmallFont, hudcolor_statnames, x, y, prefix, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.75, TAG_DONE); - - screen->DrawText(SmallFont, hudcolor_stats, x+statspace, y, string, - DTA_KeepRatio, true, - DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.75, TAG_DONE); -} - static void DrawStatus(player_t * CPlayer, int x, int y) { - char tempstr[50]; - - if (hud_showscore) + IFVM(AltHud, DrawStatus) { - mysnprintf(tempstr, countof(tempstr), "%i ", CPlayer->mo->Score); - DrawStatLine(x, y, "Sc:", tempstr); - } - - if (hud_showstats) - { - mysnprintf(tempstr, countof(tempstr), "%i ", CPlayer->mo->accuracy); - DrawStatLine(x, y, "Ac:", tempstr); - mysnprintf(tempstr, countof(tempstr), "%i ", CPlayer->mo->stamina); - DrawStatLine(x, y, "St:", tempstr); - } - - if (!deathmatch) - { - // FIXME: ZDoom doesn't preserve the player's stat counters across hubs so this doesn't - // work in cooperative hub games - if (hud_showsecrets) - { - mysnprintf(tempstr, countof(tempstr), "%i/%i ", multiplayer? CPlayer->secretcount : level.found_secrets, level.total_secrets); - DrawStatLine(x, y, "S:", tempstr); - } - - if (hud_showitems) - { - mysnprintf(tempstr, countof(tempstr), "%i/%i ", multiplayer? CPlayer->itemcount : level.found_items, level.total_items); - DrawStatLine(x, y, "I:", tempstr); - } - - if (hud_showmonsters) - { - mysnprintf(tempstr, countof(tempstr), "%i/%i ", multiplayer? CPlayer->killcount : level.killed_monsters, level.total_monsters); - DrawStatLine(x, y, "K:", tempstr); - } + VMValue params[] = { althud, CPlayer, x, y }; + VMCall(func, params, countof(params), nullptr, 0); } } @@ -272,21 +198,11 @@ static void DrawStatus(player_t * CPlayer, int x, int y) static void DrawHealth(player_t *CPlayer, int x, int y) { - int health = CPlayer->health; - - // decide on the color first - int fontcolor = - health < hud_health_red ? CR_RED : - health < hud_health_yellow ? CR_GOLD : - health <= hud_health_green ? CR_GREEN : - CR_BLUE; - - const bool haveBerserk = hud_berserk_health - && nullptr != berserkpic - && nullptr != CPlayer->mo->FindInventory(NAME_PowerStrength); - - DrawImageToBox(haveBerserk ? berserkpic : healthpic, x, y, 31, 17); - DrawHudNumber(HudFont, fontcolor, health, x + 33, y + 17); + IFVM(AltHud, DrawHealth) + { + VMValue params[] = { althud, CPlayer, x, y }; + VMCall(func, params, countof(params), nullptr, 0); + } } //=========================================================================== @@ -298,61 +214,10 @@ static void DrawHealth(player_t *CPlayer, int x, int y) static void DrawArmor(AInventory * barmor, AInventory * harmor, int x, int y) { - int ap = 0; - int bestslot = 4; - - if (harmor) + IFVM(AltHud, DrawArmor) { - double *Slots = (double*)harmor->ScriptVar(NAME_Slots, nullptr); - auto ac = (Slots[0] + Slots[1] + Slots[2] + Slots[3] + Slots[4]); - ap += int(ac); - - if (ac) - { - // Find the part of armor that protects the most - bestslot = 0; - for (int i = 1; i < 4; ++i) - { - if (Slots[i] > Slots[bestslot]) - { - bestslot = i; - } - } - } - } - - if (barmor) - { - ap += barmor->Amount; - } - - if (ap) - { - // decide on color - int fontcolor = - ap < hud_armor_red ? CR_RED : - ap < hud_armor_yellow ? CR_GOLD : - ap <= hud_armor_green ? CR_GREEN : - CR_BLUE; - - - // Use the sprite of one of the predefined Hexen armor bonuses. - // This is not a very generic approach, but it is not possible - // to truly create new types of Hexen armor bonus items anyway. - if (harmor && bestslot < 4) - { - char icon[] = "AR_1A0"; - switch (bestslot) - { - case 1: icon[3] = '2'; break; - case 2: icon[3] = '3'; break; - case 3: icon[3] = '4'; break; - default: break; - } - DrawImageToBox(TexMan.FindTexture(icon, ETextureType::Sprite), x, y, 31, 17); - } - else if (barmor) DrawImageToBox(TexMan[barmor->Icon], x, y, 31, 17); - DrawHudNumber(HudFont, fontcolor, ap, x + 33, y + 17); + VMValue params[] = { althud, barmor, harmor, x, y }; + VMCall(func, params, countof(params), nullptr, 0); } } @@ -362,55 +227,6 @@ static void DrawArmor(AInventory * barmor, AInventory * harmor, int x, int y) // //=========================================================================== -//--------------------------------------------------------------------------- -// -// Draw one key -// -// Regarding key icons, Doom's are too small, Heretic doesn't have any, -// for Hexen the in-game sprites look better and for Strife it doesn't matter -// so always use the spawn state's sprite instead of the icon here unless an -// override is specified in ALTHUDCF. -// -//--------------------------------------------------------------------------- - -static void DrawOneKey(int xo, int & x, int & y, int & c, AInventory * inv) -{ - FTextureID icon = FNullTextureID(); - FTextureID AltIcon = inv->AltHUDIcon; - - if (!AltIcon.Exists()) return; - - if (AltIcon.isValid()) - { - icon = AltIcon; - } - else if (inv->SpawnState && inv->SpawnState->sprite!=0) - { - FState * state = inv->SpawnState; - if (state && (unsigned)state->sprite < (unsigned)sprites.Size ()) - { - spritedef_t * sprdef = &sprites[state->sprite]; - spriteframe_t * sprframe = &SpriteFrames[sprdef->spriteframes + state->GetFrame()]; - if (sprframe->Texture[0] != tnt1a0) - icon = sprframe->Texture[0]; - } - } - if (icon.isNull()) icon = inv->Icon; - - if (icon.isValid()) - { - x -= 9; - DrawImageToBox(TexMan[icon], x, y, 8, 10); - c++; - if (c>=10) - { - x=xo; - y-=11; - c=0; - } - } -} - //--------------------------------------------------------------------------- // // Draw all keys @@ -419,27 +235,17 @@ static void DrawOneKey(int xo, int & x, int & y, int & c, AInventory * inv) static int DrawKeys(player_t * CPlayer, int x, int y) { - int yo=y; - int xo=x; - int c=0; - AInventory *inv; - - if (!deathmatch) + IFVM(AltHud, DrawKeys) { - int i = P_GetKeyTypeCount(); - for(i--; i >= 0; i--) - { - if ((inv = CPlayer->mo->FindInventory(P_GetKeyType(i)))) - { - DrawOneKey(xo, x, y, c, inv); - } - } + VMValue params[] = { althud, CPlayer, x, y }; + int retv; + VMReturn ret(&retv); + VMCall(func, params, countof(params), &ret, 1); + return retv; } - if (x == xo && y != yo) y+=11; - return y-11; + return 0; } - //--------------------------------------------------------------------------- // // Drawing Ammo @@ -1029,6 +835,18 @@ void DrawHUD() hudwidth = SCREENWIDTH / scale; hudheight = hud_aspectscale ? int(SCREENHEIGHT / (scale*1.2)) : SCREENHEIGHT / scale; + // Until the script export is complete we need to do some manual setup here + auto cls = PClass::FindClass("AltHud"); + if (!cls) return; + + althud = cls->CreateNew(); + althud->IntVar("hudwidth") = hudwidth; + althud->IntVar("hudheight") = hudheight; + althud->IntVar("statspace") = statspace; + althud->IntVar("healthpic") = healthpic? healthpic->id.GetIndex() : -1; + althud->IntVar("berserkpic") = berserkpic? berserkpic->id.GetIndex() : -1; + althud->PointerVar("HUDFont") = HudFont; + if (!automapactive) { int i; @@ -1094,6 +912,9 @@ void DrawHUD() DrawCoordinates(CPlayer); } + + if (althud) althud->Destroy(); + althud = nullptr; } ///////////////////////////////////////////////////////////////////////// diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 20f050cd9a..e8823609b1 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -44,6 +44,7 @@ version "3.7" #include "zscript/statusbar/strife_sbar.txt" #include "zscript/statusbar/harm_sbar.txt" #include "zscript/statusbar/sbarinfowrapper.txt" +#include "zscript/statusbar/alt_hud.txt" #include "zscript/inventory/inventory.txt" #include "zscript/inventory/inv_misc.txt" diff --git a/wadsrc/static/zscript/base.txt b/wadsrc/static/zscript/base.txt index 047a15fff1..a3e907af34 100644 --- a/wadsrc/static/zscript/base.txt +++ b/wadsrc/static/zscript/base.txt @@ -288,7 +288,7 @@ struct Font native native String GetCursor(); native static int FindFontColor(Name color); - native static double GetBottomAlignOffset(int code); + native double GetBottomAlignOffset(int code); native static Font FindFont(Name fontname); native static Font GetFont(Name fontname); native BrokenLines BreakLines(String text, int maxlen); diff --git a/wadsrc/static/zscript/shared/player_inventory.txt b/wadsrc/static/zscript/shared/player_inventory.txt index 1f21e8314f..af3e152676 100644 --- a/wadsrc/static/zscript/shared/player_inventory.txt +++ b/wadsrc/static/zscript/shared/player_inventory.txt @@ -34,7 +34,7 @@ extend class PlayerPawn //=========================================================================== // - // APlayerPawn :: AddInventory + // APlayerPawn :: InvPrev // //=========================================================================== diff --git a/wadsrc/static/zscript/statusbar/alt_hud.txt b/wadsrc/static/zscript/statusbar/alt_hud.txt new file mode 100644 index 0000000000..3807bef0e0 --- /dev/null +++ b/wadsrc/static/zscript/statusbar/alt_hud.txt @@ -0,0 +1,349 @@ +/* +** Enhanced heads up 'overlay' for fullscreen +** +**--------------------------------------------------------------------------- +** Copyright 2003-2008 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +/* + +// Icons +static FTexture * fragpic; // Frags icon +static FTexture * invgems[2]; // Inventory arrows + +DVector2 AM_GetPosition(); +*/ + +class AltHud +{ + TextureID healthPic, berserkPic; + int hudwidth, hudheight; + int statspace; + Font HudFont; // The font for the health and armor display + Font IndexFont; // The font for the inventory indices + + //--------------------------------------------------------------------------- + // + // Draws an image into a box with its bottom center at the bottom + // center of the box. The image is scaled down if it doesn't fit + // + //--------------------------------------------------------------------------- + + void DrawImageToBox(TextureID tex, int x, int y, int w, int h, double trans = 0.75) + { + double scale1, scale2; + + if (tex) + { + let texsize = TexMan.GetScaledSize(tex); + + if (w < texsize.X) scale1 = w / texsize.X; + else scale1 = 1.0; + if (h < texsize.Y) scale2 = h / texsize.Y; + else scale2 = 1.0; + scale1 = min(scale1, scale2); + if (scale2 < scale1) scale1=scale2; + + x += w >> 1; + y += h; + + w = (int)(texsize.X * scale1); + h = (int)(texsize.Y * scale1); + + screen.DrawTexture(tex, false, x, y, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, + DTA_DestWidth, w, DTA_DestHeight, h, DTA_CenterBottomOffset, 1); + + } + } + + + //--------------------------------------------------------------------------- + // + // Draws a text but uses a fixed width for all characters + // + //--------------------------------------------------------------------------- + + void DrawHudText(Font fnt, int color, String text, int x, int y, double trans = 0.75) + { + int zerowidth = fnt.GetCharWidth("0"); + + x += zerowidth / 2; + for(int i=0; i < text.length(); i++) + { + int c = text.CharCodeAt(i); + int width = fnt.GetCharWidth(c); + double offset = fnt.GetBottomAlignOffset(c); + + screen.DrawChar(fnt, color, x, y, c, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, + DTA_LeftOffset, width/2, DTA_TopOffsetF, offset); + x += zerowidth; + } + } + + + //--------------------------------------------------------------------------- + // + // Draws a number with a fixed width for all digits + // + //--------------------------------------------------------------------------- + + void DrawHudNumber(Font fnt, int color, int num, int x, int y, double trans = 0.75) + { + DrawHudText(fnt, color, String.Format("%d", num), x, y, trans); + } + + + //=========================================================================== + // + // draw the status (number of kills etc) + // + //=========================================================================== + + void DrawStatLine(int x, in out int y, String prefix, String text) + { + y -= SmallFont.GetHeight()-1; + screen.DrawText(SmallFont, hudcolor_statnames, x, y, prefix, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.75); + + screen.DrawText(SmallFont, hudcolor_stats, x+statspace, y, text, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.75); + } + + void DrawStatus(PlayerInfo CPlayer, int x, int y) + { + let mo = CPlayer.mo; + if (hud_showscore) + { + DrawStatLine(x, y, "Sc:", String.Format("%d ", mo.Score)); + } + + if (hud_showstats) + { + DrawStatLine(x, y, "Ac:", String.Format("%i ", mo.accuracy)); + DrawStatLine(x, y, "St:", String.Format("%i ", mo.stamina)); + } + + if (!deathmatch) + { + // FIXME: ZDoom doesn't preserve the player's stat counters across hubs so this doesn't + // work in cooperative hub games + if (hud_showsecrets) + { + DrawStatLine(x, y, "S:", String.Format("%i/%i ", multiplayer? CPlayer.secretcount : level.found_secrets, level.total_secrets)); + } + + if (hud_showitems) + { + DrawStatLine(x, y, "I:", String.Format("%i/%i ", multiplayer? CPlayer.itemcount : level.found_items, level.total_items)); + } + + if (hud_showmonsters) + { + DrawStatLine(x, y, "K:", String.Format("%i/%i ", multiplayer? CPlayer.killcount : level.killed_monsters, level.total_monsters)); + } + } + } + + //=========================================================================== + // + // draw health + // + //=========================================================================== + + void DrawHealth(PlayerInfo CPlayer, int x, int y) + { + int health = CPlayer.health; + + // decide on the color first + int fontcolor = + health < hud_health_red ? Font.CR_RED : + health < hud_health_yellow ? Font.CR_GOLD : + health <= hud_health_green ? Font.CR_GREEN : + Font.CR_BLUE; + + bool haveBerserk = hud_berserk_health + && !berserkpic.IsNull() + && CPlayer.mo.FindInventory('PowerStrength'); + + DrawImageToBox(haveBerserk ? berserkpic : healthpic, x, y, 31, 17); + DrawHudNumber(HudFont, fontcolor, health, x + 33, y + 17); + } + + //=========================================================================== + // + // Draw Armor. + // very similar to drawhealth, but adapted to handle Hexen armor too + // + //=========================================================================== + + void DrawArmor(BasicArmor barmor, HexenArmor harmor, int x, int y) + { + int ap = 0; + int bestslot = 4; + + if (harmor) + { + let ac = (harmor.Slots[0] + harmor.Slots[1] + harmor.Slots[2] + harmor.Slots[3] + harmor.Slots[4]); + ap += int(ac); + + if (ac) + { + // Find the part of armor that protects the most + bestslot = 0; + for (int i = 1; i < 4; ++i) + { + if (harmor.Slots[i] > harmor.Slots[bestslot]) + { + bestslot = i; + } + } + } + } + + if (barmor) + { + ap += barmor.Amount; + } + + if (ap) + { + // decide on color + int fontcolor = + ap < hud_armor_red ? Font.CR_RED : + ap < hud_armor_yellow ? Font.CR_GOLD : + ap <= hud_armor_green ? Font.CR_GREEN : + Font.CR_BLUE; + + + // Use the sprite of one of the predefined Hexen armor bonuses. + // This is not a very generic approach, but it is not possible + // to truly create new types of Hexen armor bonus items anyway. + if (harmor && bestslot < 4) + { + static const String harmorIcons[] = { "AR_1A0", "AR_2A0", "AR_3A0", "AR_4A0" }; + DrawImageToBox(TexMan.CheckForTexture(harmorIcons[bestslot], TexMan.Type_Sprite), x, y, 31, 17); + } + else if (barmor) DrawImageToBox(barmor.Icon, x, y, 31, 17); + DrawHudNumber(HudFont, fontcolor, ap, x + 33, y + 17); + } + } + + //=========================================================================== + // + // KEYS + // + //=========================================================================== + + //--------------------------------------------------------------------------- + // + // Draw one key + // + // Regarding key icons, Doom's are too small, Heretic doesn't have any, + // for Hexen the in-game sprites look better and for Strife it doesn't matter + // so always use the spawn state's sprite instead of the icon here unless an + // override is specified in ALTHUDCF. + // + //--------------------------------------------------------------------------- + + bool DrawOneKey(int xo, int x, int y, in out int c, Key inv) + { + TextureID icon; + + if (!inv) return false; + + TextureID AltIcon = inv.AltHUDIcon; + if (!AltIcon.Exists()) return false; // Setting a non-existent AltIcon hides this key. + + if (AltIcon.isValid()) + { + icon = AltIcon; + } + else if (inv.SpawnState && inv.SpawnState.sprite!=0) + { + let state = inv.SpawnState; + if (state != null) icon = state.GetSpriteTexture(0); + else icon.SetNull(); + } + if (icon.isNull()) icon = inv.Icon; + + if (icon.isValid()) + { + DrawImageToBox(icon, x, y, 8, 10); + return true; + } + return false; + } + + //--------------------------------------------------------------------------- + // + // Draw all keys + // + //--------------------------------------------------------------------------- + + int DrawKeys(PlayerInfo CPlayer, int x, int y) + { + int yo = y; + int xo = x; + int i; + int c = 0; + Key inv; + + if (!deathmatch) + { + int count = Key.GetKeyTypeCount(); + + // Go through the key in reverse order of definition, because we start at the right. + for(int i = count-1; i >= 0; i--) + { + if ((inv = Key(CPlayer.mo.FindInventory(Key.GetKeyType(i))))) + { + if (DrawOneKey(xo, x - 9, y, c, inv)) + { + x -= 9; + if (++c >= 10) + { + x = xo; + y -= 11; + c = 0; + } + } + } + } + } + if (x == xo && y != yo) y += 11; // undo the last wrap if the current line is empty. + return y - 11; + } + +} \ No newline at end of file