openclonk/engine/src/C4GraphicsResource.cpp

503 lines
16 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2001, 2003, 2006 Matthes Bender
* Copyright (c) 2002, 2004-2009 Sven Eberhardt
* Copyright (c) 2005 Peter Wortmann
* Copyright (c) 2005-2007 Günther Brammer
* Copyright (c) 2008 Armin Burgmeier
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* Loads all standard graphics from Graphics.c4g */
#include <C4Include.h>
#include <C4GraphicsResource.h>
#ifndef BIG_C4INCLUDE
#include <C4Gui.h>
#include <C4Log.h>
#include <C4Game.h>
#include <C4GraphicsSystem.h>
#endif
#include <StdGL.h>
C4GraphicsResource::C4GraphicsResource()
{
Default();
}
C4GraphicsResource::~C4GraphicsResource()
{
Clear();
}
void C4GraphicsResource::Default()
{
fInitialized = false;
sfcControl.Default();
idSfcControl = 0;
idPalGrp = 0;
fctPlayer.Default();
fctFlag.Default();
fctCrew.Default();
fctScore.Default();
fctWealth.Default();
fctRank.Default();
fctFire.Default();
fctBackground.Default();
sfcLiquidAnimation.Default(); idSfcLiquidAnimation = 0;
fctCaptain.Default();
fctMouseCursor.Default();
fctSelectMark.Default();
fctMenu.Default();
fctUpperBoard.Default();
fctLogo.Default();
fctConstruction.Default();
fctEnergy.Default();
fctMagic.Default();
fctArrow.Default();
fctExit.Default();
fctHand.Default();
fctGamepad.Default();
fctBuild.Default();
fctEnergyBars.Default();
ZeroMem(GamePalette,3*256);
ZeroMem(AlphaPalette,256);
fctCrewClr.Default();
fctFlagClr.Default();
fctPlayerClr.Default();
fctCursor.Default();
fctDropTarget.Default();
fctInsideSymbol.Default();
fctKeyboard.Default();
fctGamepad.Default();
fctCommand.Default();
fctKey.Default();
fctOKCancel.Default();
fctMouse.Default();
iNumRanks=1;
idRegisteredMainGroupSetFiles=-1;
fOldStyleCursor = false;
}
void C4GraphicsResource::Clear()
{
fInitialized = false;
// GUI data
C4GUI::Resource::Unload();
sfcControl.Clear();
idSfcControl = 0;
idPalGrp = 0;
fctCrewClr.Clear();
fctFlagClr.Clear();
fctPlayerClr.Clear();
fctPlayerGray.Clear();
fctPlayer.Clear();
fctFlag.Clear();
fctCrew.Clear();
fctScore.Clear();
fctWealth.Clear();
fctRank.Clear();
fctFire.Clear();
fctBackground.Clear();
sfcLiquidAnimation.Clear();
fctCaptain.Clear();
fctMouseCursor.Clear();
fctSelectMark.Clear();
fctMenu.Clear();
fctUpperBoard.Clear();
fctLogo.Clear();
fctConstruction.Clear();
fctEnergy.Clear();
fctMagic.Clear();
fctArrow.Clear();
fctExit.Clear();
fctHand.Clear();
fctGamepad.Clear();
fctBuild.Clear();
fctEnergyBars.Clear();
// unhook deflist from font
FontRegular.SetCustomImages(NULL);
// closing the group set will also close the graphics.c4g
// this is just for games that failed to init
// normally, this is done after successful init anyway
CloseFiles();
}
bool C4GraphicsResource::InitFonts()
{
// update group set
if (!RegisterMainGroups())
{
LogFatal(LoadResStr("IDS_ERR_GFX_REGISTERMAIN"));
return false;
}
// reinit main font
// this regards scenario-specific fonts or overloads in Extra.c4g
const char *szFont;
if (*Game.C4S.Head.Font) szFont = Game.C4S.Head.Font; else szFont = Config.General.RXFontName;
#ifndef USE_CONSOLE
if (!Game.FontLoader.InitFont(FontRegular, szFont, C4FontLoader::C4FT_Main, Config.General.RXFontSize, &Files))
return false;
// assign def list as custom image source
FontRegular.SetCustomImages(&::Definitions);
// load additional fonts
if (!Game.FontLoader.InitFont(FontTitle, szFont, C4FontLoader::C4FT_Title, Config.General.RXFontSize, &::GraphicsResource.Files)) return false;
if (!Game.FontLoader.InitFont(FontCaption, szFont, C4FontLoader::C4FT_Caption, Config.General.RXFontSize, &::GraphicsResource.Files)) return false;
if (!Game.FontLoader.InitFont(FontTiny, szFont, C4FontLoader::C4FT_Log, Config.General.RXFontSize, &::GraphicsResource.Files)) return false;
if (!Game.FontLoader.InitFont(FontTooltip, szFont, C4FontLoader::C4FT_Main, Config.General.RXFontSize, &::GraphicsResource.Files, false)) return false;
#endif
// done, success
return true;
}
BOOL C4GraphicsResource::Init(bool fInitGUI)
{
// Init fonts (double init will never if groups didnt change)
if (!InitFonts())
return false;
// Game palette - could perhaps be eliminated...
int32_t idNewPalGrp;
C4Group *pPalGrp=Files.FindEntry("C4.pal", NULL, &idNewPalGrp);
if (!pPalGrp) { LogF("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), "C4.pal"); return FALSE; }
if (idPalGrp != idNewPalGrp)
{
if (!pPalGrp->AccessEntry("C4.pal")) { LogFatal("Pal error!"); return FALSE; }
if (!pPalGrp->Read(GamePalette,256*3)) { LogFatal("Pal error!"); return FALSE; }
for (int32_t cnt=0; cnt<256*3; cnt++) GamePalette[cnt]<<=2;
ZeroMemory(&AlphaPalette, 256);
// Set default force field color
GamePalette[191*3+0]=0;
GamePalette[191*3+1]=0;
GamePalette[191*3+2]=255;
// color 0 is transparent
GamePalette[0]=GamePalette[1]=GamePalette[2]=0;
AlphaPalette[0]=255;
AlphaPalette[191]=127;
// update game pal
if (!::GraphicsSystem.SetPalette()) { LogFatal("Pal error (2)!"); return FALSE; }
idPalGrp = idNewPalGrp;
}
// Control
if (!LoadFile(sfcControl, "Control", Files, idSfcControl)) return FALSE;
fctKeyboard.Set(&sfcControl,0,0,80,36);
fctCommand.Set(&sfcControl,0,36,32,32);
fctKey.Set(&sfcControl,0,100,64,64);
fctOKCancel.Set(&sfcControl,128,100,32,32);
fctMouse.Set(&sfcControl,198,100,32,32);
// Facet bitmap resources
if (!LoadFile(fctFire, "Fire", Files, C4FCT_Height)) return FALSE;
if (!LoadFile(fctBackground, "Background", Files)) return FALSE;
if (!LoadFile(fctFlag, "Flag", Files)) return FALSE; // (new format)
if (!LoadFile(fctCrew, "Crew", Files)) return FALSE; // (new format)
if (!LoadFile(fctScore, "Score", Files)) return FALSE; // (new)
if (!LoadFile(fctWealth, "Wealth", Files)) return FALSE; // (new)
if (!LoadFile(fctPlayer, "Player", Files)) return FALSE; // (new format)
if (!LoadFile(fctRank, "Rank", Files, C4FCT_Height)) return FALSE;
if (!LoadFile(fctCaptain, "Captain", Files)) return FALSE;
if (!LoadCursorGfx()) return FALSE;
if (!LoadFile(fctSelectMark, "SelectMark", Files, C4FCT_Height)) return FALSE;
if (!LoadFile(fctMenu, "Menu", Files, 35,35)) return FALSE;
if (!LoadFile(fctLogo, "Logo", Files)) return FALSE;
if (!LoadFile(fctConstruction,"Construction", Files)) return FALSE; // (new)
if (!LoadFile(fctEnergy, "Energy", Files)) return FALSE; // (new)
if (!LoadFile(fctMagic, "Magic", Files)) return FALSE; // (new)
if (!LoadFile(fctOptions, "Options", Files, C4FCT_Height)) return FALSE;
if (!LoadFile(fctUpperBoard, "UpperBoard", Files)) return FALSE;
if (!LoadFile(fctArrow, "Arrow", Files, C4FCT_Height)) return FALSE;
if (!LoadFile(fctExit, "Exit", Files)) return FALSE;
if (!LoadFile(fctHand, "Hand", Files, C4FCT_Height)) return FALSE;
if (!LoadFile(fctGamepad, "Gamepad", Files, 80)) return FALSE;
if (!LoadFile(fctBuild, "Build", Files)) return FALSE;
if (!LoadFile(fctEnergyBars, "EnergyBars", Files)) return FALSE;
if (!LoadFile(sfcLiquidAnimation, "Liquid", Files, idSfcLiquidAnimation)) return FALSE;
// life bar facets
if (fctEnergyBars.Surface)
{
int32_t bar_wdt = fctEnergyBars.Surface->Wdt/6;
int32_t bar_hgt = fctEnergyBars.Surface->Hgt/3;
if (!bar_wdt || !bar_hgt) { LogFatal("EnergyBars.png invalid or too small!"); return FALSE; }
fctEnergyBars.Set(fctEnergyBars.Surface, 0,0, bar_wdt, bar_hgt);
}
// create ColorByOwner overlay surfaces
if (fctCrew.idSourceGroup != fctCrewClr.idSourceGroup)
{
if (!fctCrewClr.CreateClrByOwner(fctCrew.Surface)) { LogFatal("ClrByOwner error! (1)"); return FALSE; }
fctCrewClr.Wdt=fctCrew.Wdt;
fctCrewClr.Hgt=fctCrew.Hgt;
fctCrewClr.idSourceGroup = fctCrew.idSourceGroup;
}
if (fctFlag.idSourceGroup != fctFlagClr.idSourceGroup)
{
if (!fctFlagClr.CreateClrByOwner(fctFlag.Surface)) { LogFatal("ClrByOwner error! (1)"); return FALSE; }
fctFlagClr.Wdt=fctFlag.Wdt;
fctFlagClr.Hgt=fctFlag.Hgt;
fctFlagClr.idSourceGroup = fctFlag.idSourceGroup;
}
if (fctPlayer.idSourceGroup != fctPlayerGray.idSourceGroup)
{
fctPlayerGray.Create(fctPlayer.Wdt, fctPlayer.Hgt);
fctPlayer.Draw(fctPlayerGray);
fctPlayerGray.Grayscale(30);
fctPlayerGray.idSourceGroup = fctPlayer.idSourceGroup;
}
if (fctPlayer.idSourceGroup != fctPlayerClr.idSourceGroup)
{
if (!fctPlayerClr.CreateClrByOwner(fctPlayer.Surface)) { LogFatal("ClrByOwner error! (1)"); return FALSE; }
fctPlayerClr.Wdt=fctPlayer.Wdt;
fctPlayerClr.Hgt=fctPlayer.Hgt;
fctPlayerClr.idSourceGroup = fctPlayer.idSourceGroup;
}
// get number of ranks
int32_t Q; fctRank.GetPhaseNum(iNumRanks, Q);
if (!iNumRanks) iNumRanks=1;
// load GUI files, if desired
if (fInitGUI)
{
C4GUI::Resource *pRes = C4GUI::GetRes();
if (!pRes) pRes = new C4GUI::Resource(FontCaption, FontTitle, FontRegular, FontTiny, FontTooltip);
if (!pRes->Load(Files)) { delete pRes; return FALSE; }
}
// CloseFiles() must not be called now:
// The sky still needs to be loaded from the global graphics
// group in C4Game::InitGame -> C4Sky::Init so we need to keep the group(s) open.
// In activated NETWORK2, the files mustn't be closed either, because there will be
// multiple calls to this function to allow overloadings
// mark initialized
fInitialized = true;
return TRUE;
}
bool C4GraphicsResource::LoadCursorGfx()
{
// old-style cursor file overloads new-stye, because old scenarios might want to have their own cursors
if (!LoadFile(fctMouseCursor, "Cursor", Files, C4FCT_Height, C4FCT_Full, true))
{
// no old-style overload present: Determine appropriate GFX file by screen resolution
const char *szCursorFilename;
if (C4GUI::GetScreenWdt() >= 1280)
szCursorFilename = "CursorLarge";
else if (C4GUI::GetScreenWdt() >= 800)
szCursorFilename = "CursorMedium";
else
szCursorFilename = "CursorSmall";
// always fallback to regular cursor file
if (!LoadFile(fctMouseCursor, szCursorFilename, Files, C4FCT_Height))
return false;
}
// adjust dependant faces
int32_t iCursorSize = fctMouseCursor.Hgt;
if (iCursorSize == 13)
{
fctCursor.Set(fctMouseCursor.Surface, 455, 0, 13, 13);
fOldStyleCursor = true;
}
else
{
fctCursor.Set(fctMouseCursor.Surface, 35*iCursorSize, 0, iCursorSize, iCursorSize);
fOldStyleCursor = false;
}
if (iCursorSize == 13)
{
fctInsideSymbol.Set(fctMouseCursor.Surface, 468, 0, 13, 13);
fctDropTarget.Set(fctMouseCursor.Surface, 494, 0, 13, 13);
}
else
{
fctInsideSymbol.Set(fctMouseCursor.Surface, 36*iCursorSize, 0, iCursorSize, iCursorSize);
fctDropTarget.Set(fctMouseCursor.Surface, 38*iCursorSize, 0, iCursorSize, iCursorSize);
}
// done
return true;
}
bool C4GraphicsResource::RegisterGlobalGraphics()
{
// Create main gfx group - register with fixed ID 1, to prevent unnecessary font reloading.
// FontLoader-initializations always check whether the font has already been initialized
// with the same parameters. If the game is simply reloaded in console-mode, this means
// that non-bitmap-fonts are not reinitialized. This will also apply for InGame-scenario
// switches yet to be implemented.
// Bitmap fonts from other groups are always reloaded, because the group indices of the gfx
// group set are not reset, and will then differ for subsequent group registrations.
// Resetting the group index of the gfx group set at game reset would cause problems if a
// scenario with its own font face is being closed, and then another scenario with another,
// overloaded font face is opened. The group indices could match and the old font would
// then be kept.
// The cleanest alternative would be to reinit all the fonts whenever a scenario is reloaded
C4Group *pMainGfxGrp = new C4Group();
if (!pMainGfxGrp->Open(C4CFN_Graphics) || !Files.RegisterGroup(*pMainGfxGrp, true, C4GSPrio_Base, C4GSCnt_Graphics, 1))
{
// error
LogFatal(FormatString(LoadResStr("IDS_PRC_NOGFXFILE"),C4CFN_Graphics,pMainGfxGrp->GetError()).getData());
delete pMainGfxGrp;
return false;
}
return true;
}
bool C4GraphicsResource::RegisterMainGroups()
{
// register main groups
Files.RegisterGroups(Game.GroupSet, C4GSCnt_Graphics, C4CFN_Graphics, idRegisteredMainGroupSetFiles);
idRegisteredMainGroupSetFiles = Game.GroupSet.GetLastID();
return true;
}
void C4GraphicsResource::CloseFiles()
{
// closes main gfx group; releases dependencies into game group set
Files.Clear();
idRegisteredMainGroupSetFiles=-1;
}
C4Group *FindSuitableFile(const char *szName, C4GroupSet &rGfxSet, char *szFileName, int32_t &rGroupID)
{
const char * const extensions[] = { "bmp", "jpeg", "jpg", "png" };
C4Group *pGrp = 0;
C4Group *pGrp2;
int iPrio = -1;
int iPrio2;
int GroupID;
char FileName[_MAX_FNAME];
SCopy(szName, FileName);
for (int i = 0; i < 4; ++i)
{
EnforceExtension(FileName, extensions[i]);
pGrp2=rGfxSet.FindEntry(FileName, reinterpret_cast<int32_t *>(&iPrio2), reinterpret_cast<int32_t *>(&GroupID));
if ((!pGrp || iPrio2 >= iPrio) && pGrp2)
{
rGroupID = GroupID;
pGrp = pGrp2;
SCopy(FileName, szFileName);
}
}
// return found group, if any
return pGrp;
}
bool C4GraphicsResource::LoadFile(C4FacetID &fct, const char *szName, C4GroupSet &rGfxSet, int32_t iWdt, int32_t iHgt, bool fNoWarnIfNotFound)
{
char FileName[_MAX_FNAME]; int32_t ID;
C4Group *pGrp = FindSuitableFile(szName, rGfxSet, FileName, ID);
if (!pGrp)
{
// FIXME: Use LogFatal here
if (!fNoWarnIfNotFound)
{
LogF(LoadResStr("IDS_PRC_NOGFXFILE"), szName, LoadResStr("IDS_PRC_FILENOTFOUND"));
}
return false;
}
// check group
if (fct.idSourceGroup == ID)
// already up-to-date
return true;
// load
if (!fct.Load(*pGrp, FileName, iWdt, iHgt))
{
LogF(LoadResStr("IDS_PRC_NOGFXFILE"), FileName, LoadResStr("IDS_ERR_NOFILE"));
return false;
}
fct.idSourceGroup = ID;
return true;
}
bool C4GraphicsResource::LoadFile(C4Surface& sfc, const char *szName, C4GroupSet &rGfxSet, int32_t &ridCurrSfc)
{
// find
char FileName[_MAX_FNAME]; int32_t ID;
C4Group *pGrp = FindSuitableFile(szName, rGfxSet, FileName, ID);
if (!pGrp)
{
LogF(LoadResStr("IDS_PRC_NOGFXFILE"), szName, LoadResStr("IDS_PRC_FILENOTFOUND"));
return false;
}
// check group
if (ID == ridCurrSfc)
// already up-to-date
return true;
// load
if (!sfc.Load(*pGrp, FileName))
{
LogF(LoadResStr("IDS_PRC_NOGFXFILE"), FileName, LoadResStr("IDS_ERR_NOFILE"));
return false;
}
ridCurrSfc = ID;
return true;
}
int32_t C4GraphicsResource::GetColorIndex(int32_t iColor, bool fLast)
{
// Returns first or last (hardcoded) index into the clonk color palette.
// Not a valid index
if (!Inside<int32_t>(iColor,0,C4MaxColor-1)) return 32;
// Last index for this color
if (fLast)
// Colors with 8 shades
if (iColor<10) return GetColorIndex(iColor,false)+7;
// Colors with 4 shades
else return GetColorIndex(iColor,false)+3;
// First index for this color
switch (iColor)
{
// Blue, red, green, yellow, light brown, dark brown, red brown, orange
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
return 32+8*iColor;
// Black, white
case 8: case 9:
return 16+8*(iColor-8);
// Cyan, purple
case 10: case 11:
return 96+4*(iColor-10);
}
// Unreachable code
return 0;
}
bool C4GraphicsResource::ReloadResolutionDependantFiles()
{
// reload any files that depend on the current resolution
// reloads the cursor
fctMouseCursor.idSourceGroup = 0;
return LoadCursorGfx();
}
C4GraphicsResource GraphicsResource;