#include "koopatlas/hud.h" dTexMapColouriser_c::dTexMapColouriser_c() { texmap = 0; original = 0; mine = 0; } void *EGG__Heap__alloc(unsigned long size, int unk, void *heap); void EGG__Heap__free(void *ptr, void *heap); dTexMapColouriser_c::~dTexMapColouriser_c() { resetAndClear(); } void dTexMapColouriser_c::resetAndClear() { texmap = 0; if (mine) { EGG__Heap__free(mine, 0); mine = 0; } } void dTexMapColouriser_c::setTexMap(nw4r::lyt::TexMap *tm) { OSReport("Colourising TexMap: %p (w:%d h:%d)\n", tm, tm->width, tm->height); if (texmap) resetAndClear(); if (tm->mBits.textureFormat != GX_TF_IA8) { OSReport("Warning: Trying to colourise image whose format is %d not GX_TF_IA8\n", tm->mBits.textureFormat); } texmap = tm; original = (u16*)tm->image; mine = (u16*)EGG__Heap__alloc(tm->width * tm->height * 4, 0x20, mHeap::gameHeaps[2]); tm->image = mine; tm->mBits.textureFormat = GX_TF_RGBA8; } void dTexMapColouriser_c::applyAlso(nw4r::lyt::TexMap *tm) { if (!texmap) { setTexMap(tm); } else { tm->image = mine; tm->mBits.textureFormat = GX_TF_RGBA8; } } inline static float hslValue(float n1, float n2, float hue) { if (hue > 6.0f) hue -= 6.0f; else if (hue < 0.0f) hue += 6.0f; if (hue < 1.0f) return n1 + (n2 - n1) * hue; else if (hue < 3.0f) return n2; else if (hue < 4.0f) return n1 + (n2 - n1) * (4.0f - hue); else return n1; } void dTexMapColouriser_c::colourise(int h, int s, int l) { if (!mine) return; int width = texmap->width, height = texmap->height; int texelW = width / 4, texelH = height / 4; u16 *source = original, *dest = mine; float hueParam = h / 360.0f; float satParam = s / 100.0f; float lumParam = l / 100.0f; for (int texelY = 0; texelY < texelH; texelY++) { for (int texelX = 0; texelX < texelW; texelX++) { for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { u8 intensity = *source & 0xFF; u8 alpha = *source >> 8; u8 r, g, b; // This is a hack if (alpha < 250) { r = g = b = intensity; } else { // converting from GIMP's colourise code... // h and s are always the same // l is the only thing we need to touch: // we get the luminance from the source pixel // (which, conveniently, is the intensity) // manipulate it using the passed l and then // convert the whole thing to RGB float lum = intensity / 255.0f; // manipulate it if (l > 0) { lum = lum * (1.0f - lumParam); lum += (1.0f - (1.0f - lumParam)); } else if (l < 0) { lum = lum * (lumParam + 1.0f); } // make it RGB if (s == 0) { r = g = b = lum*255.0f; } else { float m1, m2; if (lum <= 0.5f) m2 = lum * (1.0f + satParam); else m2 = lum + satParam - lum * satParam; m1 = 2.0f * lum - m2; r = hslValue(m1, m2, hueParam * 6.0f + 2.0) * 255.0f; g = hslValue(m1, m2, hueParam * 6.0f) * 255.0f; b = hslValue(m1, m2, hueParam * 6.0f - 2.0) * 255.0f; } } // now write it dest[0] = (alpha<<8)|r; dest[16] = (g<<8)|b; source++; dest++; } } dest += 16; } } } dWMHud_c *dWMHud_c::instance = 0; dWMHud_c *dWMHud_c::build() { void *buffer = AllocFromGameHeap1(sizeof(dWMHud_c)); dWMHud_c *c = new(buffer) dWMHud_c; instance = c; return c; } dWMHud_c::dWMHud_c() { layoutLoaded = false; displayedControllerType = -1; isFooterVisible = false; } enum WMHudAnimation { SHOW_LIVES = 0, SHOW_HEADER, SHOW_FOOTER }; int dWMHud_c::onCreate() { if (!layoutLoaded) { bool gotFile = layout.loadArc("MapHUD.arc", false); if (!gotFile) return false; bool output = layout.build("maphud.brlyt"); if (!IsWideScreen()) { layout.clippingEnabled = true; layout.clipX = 0; layout.clipY = 52; layout.clipWidth = 640; layout.clipHeight = 352; layout.layout.rootPane->scale.x = 0.7711f; layout.layout.rootPane->scale.y = 0.7711f; } static const char *brlanNames[2] = {"MapHUD_ShowMain.brlan", "MapHUD_ShowHeader.brlan"}; static const char *groupNames[3] = {"G_Lives", "G_Header", "G_Footer"}; layout.loadAnimations(brlanNames, 2); layout.loadGroups(groupNames, (int[3]){0, 1, 0}, 3); layout.disableAllAnimations(); layout.enableNonLoopAnim(SHOW_LIVES); layout.resetAnim(SHOW_FOOTER); layout.resetAnim(SHOW_HEADER); static const char *tbNames[2] = {"MenuButtonInfo", "ItemsButtonInfo"}; layout.setLangStrings(tbNames, (int[2]){12, 15}, 4, 2); static const char *paneNames[] = { "N_IconPos1P_00", "N_IconPos2P_00", "N_IconPos3P_00", "N_IconPos4P_00" }; layout.getPanes(paneNames, &N_IconPosXP_00[0], 4); static const char *pictureNames[] = { "Header_Centre", "Header_Right", "Footer", "NormalExitFlag", "SecretExitFlag", "StarCoinOff0", "StarCoinOff1", "StarCoinOff2", "StarCoinOn0", "StarCoinOn1", "StarCoinOn2", "P_marioFace_00", "P_luigiFace_00", "P_BkinoFace_00", "P_YkinoFace_00", "Star0", "Star1", "Star2" }; layout.getPictures(pictureNames, &Header_Centre, 18); static const char *textBoxNames[] = { "LevelName", "LevelNameS", "LevelNumber", "LevelNumberS", "WorldName", "WorldNameS", "StarCoinCounter", "T_lifeNumber_00", "T_lifeNumber_01", "T_lifeNumber_02", "T_lifeNumber_03" }; layout.getTextBoxes(textBoxNames, &LevelName, 11); headerCol.setTexMap(Header_Right->material->texMaps); headerCol.applyAlso(Header_Centre->material->texMaps); footerCol.setTexMap(Footer->material->texMaps); layoutLoaded = true; layout.drawOrder = 0; willShowHeader = false; willShowFooter = false; loadFooterInfo(); SaveBlock *save = GetSaveFile()->GetBlock(-1); willShowFooter = (save->newerWorldName[0] != 0); if (!dScKoopatlas_c::instance->pathManager.isMoving) enteredNode(); setupLives(); } return true; } int dWMHud_c::onDelete() { dWMHud_c::instance = 0; if (!layoutLoaded) return true; return layout.free(); } int dWMHud_c::onExecute() { if (!layoutLoaded) return true; if (willShowHeader && (!(layout.isAnimOn(SHOW_HEADER)))) { willShowHeader = false; loadHeaderInfo(); playShowAnim(SHOW_HEADER); } if (willShowFooter && (!(layout.isAnimOn(SHOW_FOOTER)))) { willShowFooter = false; isFooterVisible = true; loadFooterInfo(); playShowAnim(SHOW_FOOTER); } setupLives(); // FUCK IT updatePressableButtonThingies(); int scCount = getStarCoinCount(); WriteNumberToTextBox(&scCount, StarCoinCounter, false); layout.execAnimations(); layout.update(); return true; } int dWMHud_c::onDraw() { if (!layoutLoaded) return true; layout.scheduleForDrawing(); return true; } void dWMHud_c::playShowAnim(int id) { if (!this || !this->layoutLoaded) return; layout.enableNonLoopAnim(id); } void dWMHud_c::playHideAnim(int id) { if (!this || !this->layoutLoaded) return; if (!layout.isAnimOn(id)) { layout.enableNonLoopAnim(id, true); } layout.grpHandlers[id].frameCtrl.flags = 3; // NO_LOOP | REVERSE if (id == SHOW_FOOTER) isFooterVisible = false; } void dWMHud_c::loadHeaderInfo() { dLevelInfo_c *levelInfo = &dScKoopatlas_c::instance->levelInfo; dLevelInfo_c::entry_s *infEntry = levelInfo->searchBySlot( nodeForHeader->levelNumber[0]-1, nodeForHeader->levelNumber[1]-1); if (infEntry == 0) { LevelName->SetString(L"Unknown Level Name!"); LevelNameS->SetString(L"Unknown Level Name!"); return; } // LEVEL NAME wchar_t convertedLevelName[100]; const char *sourceLevelName = levelInfo->getNameForLevel(infEntry); int charCount = 0; while (*sourceLevelName != 0 && charCount < 99) { convertedLevelName[charCount] = *sourceLevelName; sourceLevelName++; charCount++; } convertedLevelName[charCount] = 0; LevelName->SetString(convertedLevelName); LevelNameS->SetString(convertedLevelName); // a hack because I don't feel like editing the rlyt LevelName->size.x = LevelNameS->size.x = 400.0f; // LEVEL NUMBER static const wchar_t *numberKinds[] = { // 0-19 are handled by code // To insert a picturefont character: // \x0B\x01YY\xZZZZ // YY is the character code, ZZZZ is ignored L"A", // 20, alternate L"\x0B\x012F\xBEEF", // 21, tower L"\x0B\x012F\xBEEF" L"2", // 22, tower 2 L"\x0B\x012E\xBEEF", // 23, castle L"\x0B\x012F\xBEEF", // 24, fortress L"\x0B\x013D\xBEEF", // 25, final castle L"Train", // 26, train L"\x0B\x0132\xBEEF", // 27, airship L"Palace", // 28, switch palace L"House", // 29, yoshi's house L"Key1", // 30, key 1 L"Key2", // 31, key 2 L"Key3", // 32, key 3 L"Key4", // 33, key 4 L"Key5", // 34, key 5 L"Key6", // 35, key 6 }; int origWN = infEntry->displayWorld; int origWL = infEntry->displayLevel; wchar_t levelNumber[16]; levelNumber[0] = (origWN >= 10) ? (origWN-10+'A') : (origWN+'0'); levelNumber[1] = '-'; if (origWL > 20) { wcscpy(&levelNumber[2], numberKinds[origWL-20]); } else if (origWL >= 10) { levelNumber[2] = '1'; levelNumber[3] = ('0' - 10) + origWL; levelNumber[4] = 0; } else { levelNumber[2] = '0' + origWL; levelNumber[3] = 0; } LevelNumber->SetString(levelNumber); // make the picture shadowy int sidx = 0; while (levelNumber[sidx]) { if (levelNumber[sidx] == 11) { levelNumber[sidx+1] = 0x200 | (levelNumber[sidx+1]&0xFF); sidx += 2; } sidx++; } LevelNumberS->SetString(levelNumber); nw4r::ut::TextWriter tw2; tw2.font = LevelNumber->font; tw2.SetFontSize(LevelNumber->fontSizeX, LevelNumber->fontSizeY); tw2.lineSpace = LevelNumber->lineSpace; tw2.charSpace = LevelNumber->charSpace; if (LevelNumber->tagProc != 0) tw2.tagProcessor = LevelNumber->tagProc; float currentPos = tw2.CalcStringWidth(levelNumber, wcslen(levelNumber)); currentPos += LevelNumber->trans.x + 12.0f; // INFO int w = nodeForHeader->levelNumber[0] - 1; int l = nodeForHeader->levelNumber[1] - 1; u32 conds = GetSaveFile()->GetBlock(-1)->GetLevelCondition(w, l); NormalExitFlag->trans.x = currentPos; NormalExitFlag->SetVisible(conds & COND_NORMAL); if (conds & COND_NORMAL) currentPos += NormalExitFlag->size.x; SecretExitFlag->trans.x = currentPos; SecretExitFlag->SetVisible(conds & COND_SECRET); if (conds & COND_SECRET) currentPos += SecretExitFlag->size.x; // are star coins enabled or not? bool haveSC = (infEntry->flags & 2); for (int i = 0; i < 3; i++) { bool flag = (conds & (COND_COIN1 << i)); StarCoinOn[i]->SetVisible(flag); StarCoinOff[i]->SetVisible(haveSC); if (haveSC) { StarCoinOff[i]->trans.x = currentPos; currentPos += StarCoinOff[i]->size.x + 4.0f; } } // SIZE THING nw4r::ut::TextWriter tw; tw.font = LevelName->font; tw.SetFontSize(LevelName->fontSizeX, LevelName->fontSizeY); tw.lineSpace = LevelName->lineSpace; tw.charSpace = LevelName->charSpace; if (LevelName->tagProc != 0) tw.tagProcessor = LevelName->tagProc; float width = tw.CalcStringWidth(convertedLevelName, charCount); float totalWidth = width + LevelName->trans.x - 20.0f; if (totalWidth < currentPos) totalWidth = currentPos; Header_Centre->size.x = totalWidth; Header_Right->trans.x = totalWidth; SaveBlock *save = GetSaveFile()->GetBlock(-1); headerCol.colourise(save->hudHintH, save->hudHintS, save->hudHintL); } void dWMHud_c::loadFooterInfo() { SaveBlock *save = GetSaveFile()->GetBlock(-1); wchar_t convertedWorldName[36]; int i; for (i = 0; i < 36; i++) { convertedWorldName[i] = save->newerWorldName[i]; if (convertedWorldName[i] == 0) break; } convertedWorldName[35] = 0; WorldName->SetString(convertedWorldName); WorldNameS->SetString(convertedWorldName); WorldName->colour1 = save->hudTextColours[0]; WorldName->colour2 = save->hudTextColours[1]; footerCol.colourise(save->hudHintH, save->hudHintS, save->hudHintL); // figure out if stars are needed // Star 0: world is complete // Star 1: all exits complete // Star 2: all star coins obtained int lastLevelID = 27; // airship if (save->newerWorldID >= 10) lastLevelID = 24; // fortress else if (save->newerWorldID == 8) lastLevelID = 25; // final castle dLevelInfo_c *linfo = &dScKoopatlas_c::instance->levelInfo; dLevelInfo_c::entry_s *lastLevel = linfo->searchByDisplayNum(save->newerWorldID, lastLevelID); bool lastComplete = false; if (lastLevel) { lastComplete = (save->GetLevelCondition(lastLevel->worldSlot,lastLevel->levelSlot) & COND_NORMAL); } // now calculate the other two bool haveExits = true, haveCoins = true; dLevelInfo_c::section_s *sect = linfo->getSectionByIndex(save->newerWorldID); for (int i = 0; i < sect->levelCount; i++) { dLevelInfo_c::entry_s *entry = §->levels[i]; u32 conds = save->GetLevelCondition(entry->worldSlot, entry->levelSlot); if (((entry->flags & 0x10) && !(conds & COND_NORMAL)) || ((entry->flags & 0x20) && !(conds & COND_SECRET))) haveExits = false; if (entry->flags & 2) { if ((conds & COND_COIN_ALL) != COND_COIN_ALL) haveCoins = false; } } Star[0]->SetVisible(lastComplete); Star[1]->SetVisible(haveExits); Star[2]->SetVisible(haveCoins); } void dWMHud_c::enteredNode(dKPNode_s *node) { if (node == 0) node = dScKoopatlas_c::instance->pathManager.currentNode; if (node->type == dKPNode_s::LEVEL) { willShowHeader = true; nodeForHeader = node; } } void dWMHud_c::leftNode() { if (layout.grpHandlers[SHOW_HEADER].frameCtrl.currentFrame > 0.1f) { // not hidden if ((layout.isAnimOn(SHOW_HEADER) && !(layout.grpHandlers[SHOW_HEADER].frameCtrl.flags & 2)) || (!layout.isAnimOn(SHOW_HEADER))) { // currently being shown, OR fully shown already playHideAnim(SHOW_HEADER); } } } void dWMHud_c::hideFooter() { if (isFooterVisible) playHideAnim(SHOW_FOOTER); } void dWMHud_c::showFooter() { willShowFooter = true; if (isFooterVisible) playHideAnim(SHOW_FOOTER); } void dWMHud_c::setupLives() { static const int LogicalPlayerIDs[] = {0,1,3,2}; P_marioFace_00->SetVisible(false); P_luigiFace_00->SetVisible(false); P_BkinoFace_00->SetVisible(false); P_YkinoFace_00->SetVisible(false); int playerCount = 0; for (int i = 0; i < 4; i++) { // The part in setupLives() int playerID = LogicalPlayerIDs[i]; int slotID = SearchForIndexOfPlayerID(playerID); int lives = Player_Lives[slotID]; int length = 2; WriteNumberToTextBox(&lives, &length, T_lifeNumber[slotID], true); // The part in setupIconThings() if (QueryPlayerAvailability(slotID)) { playerCount++; nw4r::lyt::Pane *facePane = (&P_marioFace_00)[playerID]; facePane->trans = N_IconPosXP_00[i]->trans; facePane->SetVisible(true); } } for (int i = 0; i < 4; i++) N_IconPosXP_00[i]->SetVisible(false); N_IconPosXP_00[playerCount - 1]->SetVisible(true); } void dWMHud_c::updatePressableButtonThingies() { int cntType = RemoconMng->controllers[0]->controllerType; if (cntType != displayedControllerType) { displayedControllerType = cntType; int beef = (cntType == 0) ? 0 : 1; GameMgrP->currentControllerType = beef; WriteBMGToTextBox( layout.findTextBoxByName("ItemsButtonInfo"), GetBMG(), 4, 15, 0); } }