mirror of
https://github.com/pret/pokefirered.git
synced 2026-09-30 04:48:36 -05:00
dns
This commit is contained in:
@@ -1764,6 +1764,15 @@
|
||||
.byte 0xd6
|
||||
.endm
|
||||
|
||||
@ Equivalent to fadescreen but copies gPlttBufferUnfaded to gDecompressionBuffer on the fade out
|
||||
@ and the reverse on the fade in, in effect saving gPlttBufferUnfaded to restore it.
|
||||
@ If nowait set, does not wait for the fade to complete
|
||||
.macro fadescreenswapbuffers mode:req, nowait=0
|
||||
.byte 0xdc
|
||||
.byte \mode
|
||||
.byte \nowait
|
||||
.endm
|
||||
|
||||
.macro _dynmultichoice left:req, top:req, ignoreBPress:req, maxBeforeScroll:req, shouldSort:req, initialSelected:req, callbacks:req argv:vararg
|
||||
.byte 0xe3
|
||||
.2byte \left
|
||||
|
||||
@@ -1087,12 +1087,12 @@ EventScript_GymBadgeFanfare::
|
||||
return
|
||||
|
||||
EventScript_OutOfCenterPartyHeal::
|
||||
fadescreen FADE_TO_BLACK
|
||||
fadescreenswapbuffers FADE_TO_BLACK
|
||||
playfanfare MUS_HEAL
|
||||
waitfanfare
|
||||
special HealPlayerParty
|
||||
callnative UpdateFollowingPokemon
|
||||
fadescreen FADE_FROM_BLACK
|
||||
fadescreenswapbuffers FADE_FROM_BLACK
|
||||
return
|
||||
|
||||
EventScript_WallTownMap::
|
||||
|
||||
@@ -51,10 +51,10 @@ MtEmber_Exterior_EventScript_BattleGrunt1::
|
||||
MtEmber_Exterior_EventScript_DefeatedBothGrunts::
|
||||
msgbox MtEmber_Exterior_Text_WellRegroupDontStepInsideThere
|
||||
closemessage
|
||||
fadescreen FADE_TO_BLACK
|
||||
fadescreenswapbuffers FADE_TO_BLACK
|
||||
removeobject LOCALID_GRUNT1
|
||||
removeobject LOCALID_GRUNT2
|
||||
fadescreen FADE_FROM_BLACK
|
||||
fadescreenswapbuffers FADE_FROM_BLACK
|
||||
release
|
||||
end
|
||||
|
||||
|
||||
@@ -216,6 +216,12 @@ gScriptCmdTable::
|
||||
.4byte ScrCmd_bufferitemnameplural @ 0xd4
|
||||
.4byte ScrCmd_selectapproachingtrainer @ 0xd5
|
||||
.4byte ScrCmd_lockfortrainer @ 0xd6
|
||||
.4byte ScrCmd_nop1 @ 0xd7
|
||||
.4byte ScrCmd_nop1 @ 0xd8
|
||||
.4byte ScrCmd_nop1 @ 0xd9
|
||||
.4byte ScrCmd_nop1 @ 0xda
|
||||
.4byte ScrCmd_nop1 @ 0xdb
|
||||
.4byte ScrCmd_fadescreenswapbuffers @ 0xdc
|
||||
|
||||
gScriptCmdTableEnd::
|
||||
.4byte ScrCmd_nop
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
EventScript_RemoveStaticMon::
|
||||
fadescreen FADE_TO_BLACK
|
||||
fadescreenswapbuffers FADE_TO_BLACK
|
||||
removeobject VAR_LAST_TALKED
|
||||
fadescreen FADE_FROM_BLACK
|
||||
fadescreenswapbuffers FADE_FROM_BLACK
|
||||
release
|
||||
end
|
||||
|
||||
EventScript_MonFlewAway::
|
||||
fadescreen FADE_TO_BLACK
|
||||
fadescreenswapbuffers FADE_TO_BLACK
|
||||
removeobject VAR_LAST_TALKED
|
||||
fadescreen FADE_FROM_BLACK
|
||||
fadescreenswapbuffers FADE_FROM_BLACK
|
||||
bufferspeciesname STR_VAR_1, VAR_0x8004
|
||||
msgbox Text_MonFlewAway
|
||||
release
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
#define GEN_8_PLA GEN_LATEST + 2
|
||||
|
||||
//Time
|
||||
#define OW_TIMES_OF_DAY GEN_LATEST // Different generations have the times of day change at different times.
|
||||
#define OW_TIMES_OF_DAY GEN_5 // Different generations have the times of day change at different times.
|
||||
#define OW_USE_FAKE_RTC FALSE // When TRUE, seconds on the in-game clock will only advance once every 60 playTimeVBlanks (every 60 frames).
|
||||
#define OW_ALTERED_TIME_RATIO GEN_LATEST // In GEN_8_PLA, the time in game moves forward 60 seconds for every second in the RTC. In GEN_9, it is 20 seconds. This has no effect if OW_USE_FAKE_RTC is FALSE.
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ void SetWeatherScreenFadeOut(void);
|
||||
void WeatherProcessingIdle(void);
|
||||
u8 GetCurrentWeather(void);
|
||||
void delay(u8, u8, u32);
|
||||
void UpdateSpritePaletteWithWeather(u8 palIdx);
|
||||
void UpdateSpritePaletteWithWeather(u8 palIdx, bool32 allowFog);
|
||||
void ResetPreservedPalettesInWeather(void);
|
||||
void PreservePaletteInWeather(u8 palIdx);
|
||||
bool32 IsWeatherAlphaBlend(void);
|
||||
|
||||
@@ -79,7 +79,8 @@ typedef void (*TilesetCB)(void);
|
||||
|
||||
struct Tileset
|
||||
{
|
||||
/*0x00*/ bool8 isCompressed;
|
||||
/*0x00*/ bool8 isCompressed:1;
|
||||
/*0x00*/ u8 swapPalettes:7; // bitmask determining whether palette has an alternate, night-time palette
|
||||
/*0x01*/ bool8 isSecondary;
|
||||
/*0x04*/ const u32 *tiles;
|
||||
/*0x08*/ const u16 (*palettes)[16];
|
||||
|
||||
@@ -27,6 +27,11 @@
|
||||
#define MOVEMENT_MODE_FROZEN 1
|
||||
#define MOVEMENT_MODE_SCRIPTED 2
|
||||
|
||||
#define TIME_OF_DAY_NIGHT 0
|
||||
#define TIME_OF_DAY_TWILIGHT 1
|
||||
#define TIME_OF_DAY_DAY 2
|
||||
#define TIME_OF_DAY_MAX TIME_OF_DAY_DAY
|
||||
|
||||
struct LinkPlayerObjectEvent
|
||||
{
|
||||
u8 active;
|
||||
@@ -49,6 +54,14 @@ enum {
|
||||
MUSIC_DISABLE_KEEP,
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) TimeBlendSettings {
|
||||
u16 weight:9;
|
||||
u16 time1:3;
|
||||
u16 time0:3;
|
||||
u16 unused:1;
|
||||
u16 altWeight;
|
||||
};
|
||||
|
||||
|
||||
extern struct WarpData gLastUsedWarp;
|
||||
extern struct LinkPlayerObjectEvent gLinkPlayerObjectEvents[4];
|
||||
@@ -65,6 +78,10 @@ extern u8 gExitStairsMovementDisabled;
|
||||
|
||||
extern u8 gDisableMapMusicChangeOnMapLoad;
|
||||
extern u8 gGlobalFieldTintMode;
|
||||
extern u8 gTimeOfDay;
|
||||
extern u16 gTimeUpdateCounter;
|
||||
|
||||
extern struct TimeBlendSettings currentTimeBlend;
|
||||
|
||||
extern const struct Coords32 gDirectionToVectors[];
|
||||
|
||||
@@ -178,10 +195,15 @@ u16 SetLinkWaitingForScript(void);
|
||||
void SetMainCallback1(MainCallback cb);
|
||||
void CB1_Overworld(void);
|
||||
void CB2_ReturnToFieldContinueScript(void);
|
||||
u8 UpdateTimeOfDay(void);
|
||||
bool8 MapHasNaturalLight(u8 mapType);
|
||||
void UpdateAltBgPalettes(u16 palettes);
|
||||
void UpdatePalettesWithTime(u32);
|
||||
u8 GetLastUsedWarpMapSectionId(void);
|
||||
void StoreInitialPlayerAvatarState(void);
|
||||
void UpdateEscapeWarp(s16 x, s16 y);
|
||||
bool8 SetDiveWarpEmerge(u16 x, u16 y);
|
||||
bool8 SetDiveWarpDive(u16 x, u16 y);
|
||||
u8 UpdateSpritePaletteWithTime(u8 paletteNum);
|
||||
|
||||
#endif //GUARD_OVERWORLD_H
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#define PALETTE_FADE_STATUS_LOADING 0xFF
|
||||
|
||||
#define PALETTES_BG 0x0000FFFF
|
||||
// like PALETTES_BG but excludes UI pals [13, 15]
|
||||
#define PALETTES_MAP 0x00001FFF
|
||||
#define PALETTES_OBJECTS 0xFFFF0000
|
||||
#define PALETTES_ALL (PALETTES_BG | PALETTES_OBJECTS)
|
||||
|
||||
@@ -26,6 +28,11 @@
|
||||
#define OBJ_PLTT_ID(n) (OBJ_PLTT_OFFSET + PLTT_ID(n))
|
||||
#define OBJ_PLTT_ID2(n) (PLTT_ID((n) + 16))
|
||||
|
||||
// Used to determine whether a sprite palette tag
|
||||
// should be excluded from time (and weather) blending
|
||||
#define BLEND_IMMUNE_FLAG (1 << 15)
|
||||
#define IS_BLEND_IMMUNE_TAG(tag) ((tag) & BLEND_IMMUNE_FLAG)
|
||||
|
||||
enum
|
||||
{
|
||||
FAST_FADE_IN_FROM_WHITE,
|
||||
@@ -34,9 +41,19 @@ enum
|
||||
FAST_FADE_OUT_TO_BLACK,
|
||||
};
|
||||
|
||||
struct BlendSettings {
|
||||
u32 blendColor:24;
|
||||
u32 isTint:1;
|
||||
u32 coeff:5;
|
||||
};
|
||||
|
||||
struct PaletteFadeControl
|
||||
{
|
||||
u32 multipurpose1;
|
||||
// These three are only used for TOD blending
|
||||
struct BlendSettings *bld0;
|
||||
struct BlendSettings *bld1;
|
||||
u16 weight:9; // [0, 256], so must be 9 bits
|
||||
u8 delayCounter:6;
|
||||
u16 y:5; // blend coefficient
|
||||
u16 targetY:5; // target blend coefficient
|
||||
@@ -55,6 +72,8 @@ struct PaletteFadeControl
|
||||
u32 unused;
|
||||
};
|
||||
|
||||
extern const struct BlendSettings gTimeOfDayBlend[];
|
||||
|
||||
extern struct PaletteFadeControl gPaletteFade;
|
||||
extern u32 gPlttBufferTransferPending;
|
||||
extern u16 gPlttBufferUnfaded[PLTT_BUFFER_SIZE];
|
||||
@@ -67,11 +86,13 @@ void TransferPlttBuffer(void);
|
||||
u32 UpdatePaletteFade(void);
|
||||
void ResetPaletteFade(void);
|
||||
bool32 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, u32 blendColor);
|
||||
bool32 BeginTimeOfDayPaletteFade(u32, s8, u8, u8, struct BlendSettings *, struct BlendSettings *, u32, u32);
|
||||
void ResetPaletteFadeControl(void);
|
||||
void InvertPlttBuffer(u32 selectedPalettes);
|
||||
void BeginFastPaletteFade(u32 submode);
|
||||
void BeginHardwarePaletteFade(u32 blendCnt, u32 delay, u32 y, u32 targetY, u32 shouldResetBlendRegisters);
|
||||
void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color);
|
||||
void BlendPalettesFine(u32 palettes, u16 *src, u16 *dst, u32 coeff, u32 color);
|
||||
void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u32 color);
|
||||
void TintPalette_GrayScale(u16 *palette, u32 count);
|
||||
void TintPalette_GrayScale2(u16 *palette, u32 count);
|
||||
@@ -81,6 +102,9 @@ void CopyPaletteInvertedTint(const u16 *src, u16 *dst, u32 count, u8 tone);
|
||||
void BlendPalettesGradually(u32 selectedPalettes, s8 delay, u8 coeff, u8 coeffTarget, u16 color, u8 priority, u8 id);
|
||||
bool32 IsBlendPalettesGraduallyTaskActive(u8 var);
|
||||
void DestroyBlendPalettesGraduallyTask(void);
|
||||
void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor);
|
||||
void TimeMixPalettes(u32, u16 *, u16 *, struct BlendSettings *, struct BlendSettings *, u16);
|
||||
void AvgPaletteWeighted(u16 *src0, u16 *src1, u16 *dst, u16 weight0);
|
||||
|
||||
static inline void SetBackdropFromColor(u32 color)
|
||||
{
|
||||
|
||||
@@ -398,12 +398,18 @@ static u8 GetBattleTerrainByMapScene(u8 mapBattleScene)
|
||||
|
||||
static void LoadBattleTerrainGfx(u16 terrain)
|
||||
{
|
||||
u32 palettes = 0;
|
||||
if (terrain >= NELEMS(gBattleTerrainInfo))
|
||||
terrain = BATTLE_TERRAIN_PLAIN;
|
||||
// Copy to bg3
|
||||
LZDecompressVram(gBattleTerrainInfo[terrain].background.tileset, (void *)BG_CHAR_ADDR(2));
|
||||
LZDecompressVram(gBattleTerrainInfo[terrain].background.tilemap, (void *)BG_SCREEN_ADDR(26));
|
||||
LoadCompressedPalette(gBattleTerrainInfo[terrain].background.palette, BG_PLTT_ID(2), 3 * PLTT_SIZE_4BPP);
|
||||
|
||||
palettes ^= (1 << 2);
|
||||
palettes ^= (1 << 3);
|
||||
palettes ^= (1 << 4);
|
||||
UpdatePalettesWithTime(palettes);
|
||||
}
|
||||
|
||||
static void LoadBattleTerrainEntryGfx(u16 terrain)
|
||||
|
||||
@@ -2093,7 +2093,7 @@ static u8 LoadDynamicFollowerPalette(u16 species, u8 form, bool32 shiny)
|
||||
}
|
||||
|
||||
if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog
|
||||
UpdateSpritePaletteWithWeather(paletteNum);
|
||||
UpdateSpritePaletteWithWeather(paletteNum, FALSE);
|
||||
return paletteNum;
|
||||
}
|
||||
|
||||
@@ -2116,7 +2116,7 @@ static void FollowerSetGraphics(struct ObjectEvent *objEvent, u16 species, u8 fo
|
||||
}
|
||||
else if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog
|
||||
{
|
||||
UpdateSpritePaletteWithWeather(gSprites[objEvent->spriteId].oam.paletteNum);
|
||||
UpdateSpritePaletteWithWeather(gSprites[objEvent->spriteId].oam.paletteNum, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2162,7 +2162,7 @@ static void RefreshFollowerGraphics(struct ObjectEvent *objEvent)
|
||||
{
|
||||
UpdateSpritePalette(&sObjectEventSpritePalettes[i], sprite);
|
||||
if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL) // don't want to weather blend in fog
|
||||
UpdateSpritePaletteWithWeather(sprite->oam.paletteNum);
|
||||
UpdateSpritePaletteWithWeather(sprite->oam.paletteNum, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2795,7 +2795,17 @@ static u8 UpdateSpritePalette(const struct SpritePalette *spritePalette, struct
|
||||
sprite->inUse = FALSE;
|
||||
FieldEffectFreePaletteIfUnused(sprite->oam.paletteNum);
|
||||
sprite->inUse = TRUE;
|
||||
if (IndexOfSpritePaletteTag(spritePalette->tag) == 0xFF)
|
||||
{
|
||||
sprite->oam.paletteNum = LoadSpritePalette(spritePalette);
|
||||
UpdateSpritePaletteWithWeather(sprite->oam.paletteNum, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite->oam.paletteNum = LoadSpritePalette(spritePalette);
|
||||
}
|
||||
sprite->oam.paletteNum = LoadSpritePalette(spritePalette);
|
||||
|
||||
return sprite->oam.paletteNum;
|
||||
}
|
||||
|
||||
@@ -3011,6 +3021,8 @@ static u8 LoadSpritePaletteIfTagExists(const struct SpritePalette *spritePalette
|
||||
if (paletteNum != 0xFF) // don't load twice; return
|
||||
return paletteNum;
|
||||
paletteNum = LoadSpritePalette(spritePalette);
|
||||
if (paletteNum != 0xFF)
|
||||
UpdateSpritePaletteWithWeather(paletteNum, FALSE);
|
||||
ApplyGlobalFieldPaletteTint(paletteNum);
|
||||
return paletteNum;
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ static void FieldEffectScript_LoadFadedPal(const u8 **script)
|
||||
LoadSpritePalette(spritePalette);
|
||||
if (idx == 0xFF)
|
||||
ApplyGlobalFieldPaletteTint(IndexOfSpritePaletteTag(spritePalette->tag));
|
||||
UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(spritePalette->tag));
|
||||
UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(spritePalette->tag), TRUE);
|
||||
*script += sizeof(u32);
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ static void LoadObjectRegularReflectionPalette(struct ObjectEvent *objectEvent,
|
||||
ApplyIceFilter(mainSprite->oam.paletteNum, filteredData);
|
||||
paletteNum = LoadSpritePalette(&filteredPal);
|
||||
ApplyGlobalFieldPaletteTint(paletteNum);
|
||||
UpdateSpritePaletteWithWeather(paletteNum);
|
||||
UpdateSpritePaletteWithWeather(paletteNum, TRUE);
|
||||
}
|
||||
sprite->oam.paletteNum = paletteNum;
|
||||
sprite->oam.objMode = ST_OAM_OBJ_BLEND;
|
||||
@@ -172,7 +172,7 @@ static void LoadObjectHighBridgeReflectionPalette(struct ObjectEvent *objectEven
|
||||
CpuFill16(0x55C9, blueData, PLTT_SIZE_4BPP);
|
||||
sprite->oam.paletteNum = LoadSpritePalette(&bluePalette);
|
||||
ApplyGlobalFieldPaletteTint(sprite->oam.paletteNum);
|
||||
UpdateSpritePaletteWithWeather(sprite->oam.paletteNum);
|
||||
UpdateSpritePaletteWithWeather(sprite->oam.paletteNum, TRUE);
|
||||
}
|
||||
|
||||
static void UpdateObjectReflectionSprite(struct Sprite *reflectionSprite)
|
||||
@@ -209,7 +209,7 @@ static void UpdateObjectReflectionSprite(struct Sprite *reflectionSprite)
|
||||
ApplyIceFilter(mainSprite->oam.paletteNum, filteredData);
|
||||
paletteNum = LoadSpritePalette(&filteredPal);
|
||||
ApplyGlobalFieldPaletteTint(paletteNum);
|
||||
UpdateSpritePaletteWithWeather(paletteNum);
|
||||
UpdateSpritePaletteWithWeather(paletteNum, TRUE);
|
||||
}
|
||||
reflectionSprite->oam.paletteNum = paletteNum;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "battle_tower.h"
|
||||
#include "field_camera.h"
|
||||
#include "field_effect.h"
|
||||
#include "field_weather.h"
|
||||
#include "event_object_movement.h"
|
||||
#include "item.h"
|
||||
#include "menu_indicators.h"
|
||||
@@ -2476,6 +2477,7 @@ static void MoveDeoxysObject(u8 num)
|
||||
{
|
||||
u8 mapObjId;
|
||||
LoadPalette(sDeoxysObjectPals[num], OBJ_PLTT_ID(10), PLTT_SIZEOF(4));
|
||||
UpdateSpritePaletteWithWeather(10, FALSE);
|
||||
ApplyGlobalFieldPaletteTint(10);
|
||||
TryGetObjectEventIdByLocalIdAndMap(1, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &mapObjId);
|
||||
if (num == 0)
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
#include "field_weather.h"
|
||||
#include "field_weather_util.h"
|
||||
#include "field_weather_effects.h"
|
||||
#include "overworld.h"
|
||||
#include "task.h"
|
||||
#include "trig.h"
|
||||
#include "constants/field_weather.h"
|
||||
#include "constants/weather.h"
|
||||
#include "constants/songs.h"
|
||||
|
||||
#include "quest_log.h"
|
||||
#include "constants/quest_log.h"
|
||||
|
||||
#define DROUGHT_COLOR_INDEX(color) ((((color) >> 1) & 0xF) | (((color) >> 2) & 0xF0) | (((color) >> 3) & 0xF00))
|
||||
|
||||
enum
|
||||
@@ -449,15 +453,30 @@ static void ApplyGammaShift(u8 startPalIndex, u8 numPalettes, s8 gammaIndex)
|
||||
|
||||
if (gammaIndex > 0)
|
||||
{
|
||||
// Create the palette mask
|
||||
u32 palettes = PALETTES_ALL;
|
||||
numPalettes += startPalIndex;
|
||||
palettes = (palettes >> startPalIndex) << startPalIndex;
|
||||
palettes = (palettes << (32-numPalettes)) >> (32-numPalettes);
|
||||
numPalettes -= startPalIndex;
|
||||
|
||||
gammaIndex--;
|
||||
palOffset = PLTT_ID(startPalIndex);
|
||||
UpdateAltBgPalettes(palettes & PALETTES_BG);
|
||||
// Thunder gamma-shift looks bad on night-blended palettes, so ignore time blending in some situations
|
||||
if (!(gammaIndex > 3) && MapHasNaturalLight(gMapHeader.mapType))
|
||||
UpdatePalettesWithTime(palettes);
|
||||
else
|
||||
CpuFastCopy(gPlttBufferUnfaded + palOffset, gPlttBufferFaded + palOffset, PLTT_SIZE_4BPP * numPalettes);
|
||||
|
||||
numPalettes += startPalIndex;
|
||||
curPalIndex = startPalIndex;
|
||||
|
||||
// Loop through the speficied palette range and apply necessary gamma shifts to the colors.
|
||||
while (curPalIndex < numPalettes)
|
||||
{
|
||||
if (sPaletteGammaTypes[curPalIndex] == GAMMA_NONE)
|
||||
if (sPaletteGammaTypes[curPalIndex] == GAMMA_NONE ||
|
||||
(curPalIndex >= 16 && IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(curPalIndex - 16))))
|
||||
{
|
||||
// No palette change.
|
||||
CpuFastCopy(&gPlttBufferUnfaded[palOffset], &gPlttBufferFaded[palOffset], PLTT_SIZE_4BPP);
|
||||
@@ -493,8 +512,18 @@ static void ApplyGammaShift(u8 startPalIndex, u8 numPalettes, s8 gammaIndex)
|
||||
}
|
||||
else
|
||||
{
|
||||
// No palette blending.
|
||||
CpuFastCopy(&gPlttBufferUnfaded[PLTT_ID(startPalIndex)], &gPlttBufferFaded[PLTT_ID(startPalIndex)], numPalettes * PLTT_SIZE_4BPP);
|
||||
if (MapHasNaturalLight(gMapHeader.mapType))
|
||||
{
|
||||
// Time-blend
|
||||
u32 palettes = ((1 << numPalettes) - 1) << startPalIndex;
|
||||
UpdateAltBgPalettes(palettes & PALETTES_BG);
|
||||
UpdatePalettesWithTime(palettes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No palette blending.
|
||||
CpuFastCopy(&gPlttBufferUnfaded[PLTT_ID(startPalIndex)], &gPlttBufferFaded[PLTT_ID(startPalIndex)], numPalettes * PLTT_SIZE_4BPP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -515,6 +544,9 @@ static void ApplyGammaShiftWithBlend(u8 startPalIndex, u8 numPalettes, s8 gammaI
|
||||
|
||||
while (curPalIndex < numPalettes)
|
||||
{
|
||||
UpdateAltBgPalettes((1 << (palOffset >> 4)) & PALETTES_BG);
|
||||
CpuFastCopy(gPlttBufferUnfaded + palOffset, gPlttBufferFaded + palOffset, 16 * sizeof(u16));
|
||||
UpdatePalettesWithTime(1 << (palOffset >> 4)); // Apply TOD blend
|
||||
if (sPaletteGammaTypes[curPalIndex] == GAMMA_NONE)
|
||||
{
|
||||
// No gamma shift. Simply blend the colors.
|
||||
@@ -597,49 +629,24 @@ static void ApplyDroughtGammaShiftWithBlend(s8 gammaIndex, u8 blendCoeff, u32 bl
|
||||
|
||||
static void ApplyFogBlend(u8 blendCoeff, u32 blendColor)
|
||||
{
|
||||
struct RGBColor color;
|
||||
u8 rBlend;
|
||||
u8 gBlend;
|
||||
u8 bBlend;
|
||||
u16 curPalIndex;
|
||||
u32 curPalIndex;
|
||||
u16 fogCoeff = min((gTimeOfDay + 1) * 4, 12);
|
||||
|
||||
BlendPalette(0, 256, blendCoeff, blendColor);
|
||||
color = *(struct RGBColor *)&blendColor;
|
||||
rBlend = color.r;
|
||||
gBlend = color.g;
|
||||
bBlend = color.b;
|
||||
// First blend all palettes with time
|
||||
UpdateAltBgPalettes(PALETTES_BG);
|
||||
CpuFastCopy(gPlttBufferUnfaded, gPlttBufferFaded, PLTT_BUFFER_SIZE * 2);
|
||||
UpdatePalettesWithTime(PALETTES_ALL);
|
||||
// Then blend tile palettes [0, 12] faded->faded with fadeIn color
|
||||
BlendPalettesFine(PALETTES_MAP, gPlttBufferFaded, gPlttBufferFaded, blendCoeff, blendColor);
|
||||
|
||||
// Do fog blending on marked sprite palettes
|
||||
for (curPalIndex = 16; curPalIndex < 32; curPalIndex++)
|
||||
{
|
||||
if (LightenSpritePaletteInFog(curPalIndex))
|
||||
{
|
||||
u16 palEnd = PLTT_ID(curPalIndex + 1);
|
||||
u16 palOffset = PLTT_ID(curPalIndex);
|
||||
|
||||
while (palOffset < palEnd)
|
||||
{
|
||||
struct RGBColor color = *(struct RGBColor *)&gPlttBufferUnfaded[palOffset];
|
||||
u8 r = color.r;
|
||||
u8 g = color.g;
|
||||
u8 b = color.b;
|
||||
|
||||
r += ((28 - r) * 3) >> 2;
|
||||
g += ((31 - g) * 3) >> 2;
|
||||
b += ((28 - b) * 3) >> 2;
|
||||
|
||||
r += ((rBlend - r) * blendCoeff) >> 4;
|
||||
g += ((gBlend - g) * blendCoeff) >> 4;
|
||||
b += ((bBlend - b) * blendCoeff) >> 4;
|
||||
|
||||
gPlttBufferFaded[palOffset] = (b << 10) | (g << 5) | r;
|
||||
palOffset++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BlendPalette(PLTT_ID(curPalIndex), 16, blendCoeff, blendColor);
|
||||
}
|
||||
BlendPalettesFine(1, gPlttBufferFaded + PLTT_ID(curPalIndex), gPlttBufferFaded + PLTT_ID(curPalIndex), fogCoeff, RGB(28, 31, 28));
|
||||
}
|
||||
// Finally blend all sprite palettes faded->faded with fadeIn color
|
||||
BlendPalettesFine(PALETTES_OBJECTS, gPlttBufferFaded, gPlttBufferFaded, blendCoeff, blendColor);
|
||||
}
|
||||
|
||||
static void MarkFogSpritePalToLighten(u8 paletteIndex)
|
||||
@@ -732,8 +739,10 @@ void FadeScreen(u8 mode, s8 delay)
|
||||
|
||||
if (fadeOut)
|
||||
{
|
||||
if (useWeatherPal)
|
||||
CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, PLTT_SIZE);
|
||||
// Note: Copying faded -> unfaded like this works fine, except if the screen is faded back in
|
||||
// without transitioning to a different screen
|
||||
// For cases like that, use fadescreenswapbuffers
|
||||
CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, PLTT_SIZE);
|
||||
|
||||
BeginNormalPaletteFade(PALETTES_ALL, delay, 0, 16, fadeColor);
|
||||
gWeatherPtr->palProcessingState = WEATHER_PAL_STATE_SCREEN_FADING_OUT;
|
||||
@@ -741,8 +750,17 @@ void FadeScreen(u8 mode, s8 delay)
|
||||
else
|
||||
{
|
||||
gWeatherPtr->fadeDestColor = fadeColor;
|
||||
UpdateTimeOfDay();
|
||||
if (useWeatherPal)
|
||||
gWeatherPtr->fadeScreenCounter = 0;
|
||||
else if (!QL_IS_PLAYBACK_STATE && MapHasNaturalLight(gMapHeader.mapType))
|
||||
{
|
||||
UpdateAltBgPalettes(PALETTES_BG);
|
||||
BeginTimeOfDayPaletteFade(PALETTES_ALL, delay, 16, 0,
|
||||
(struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time0],
|
||||
(struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time1],
|
||||
currentTimeBlend.weight, fadeColor);
|
||||
}
|
||||
else
|
||||
BeginNormalPaletteFade(PALETTES_ALL, delay, 16, 0, fadeColor);
|
||||
|
||||
@@ -828,7 +846,7 @@ bool8 IsWeatherNotFadingIn(void)
|
||||
return (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_SCREEN_FADING_IN);
|
||||
}
|
||||
|
||||
void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex)
|
||||
void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex, bool32 allowFog)
|
||||
{
|
||||
u16 paletteIndex = 16 + spritePaletteIndex;
|
||||
u16 i;
|
||||
@@ -855,12 +873,29 @@ void UpdateSpritePaletteWithWeather(u8 spritePaletteIndex)
|
||||
default:
|
||||
if (gWeatherPtr->currWeather != WEATHER_FOG_HORIZONTAL)
|
||||
{
|
||||
ApplyGammaShift(paletteIndex, 1, gWeatherPtr->gammaIndex);
|
||||
if (gWeatherPtr->gammaIndex)
|
||||
ApplyGammaShift(paletteIndex, 1, gWeatherPtr->gammaIndex);
|
||||
else
|
||||
UpdateSpritePaletteWithTime(spritePaletteIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
paletteIndex = PLTT_ID(paletteIndex);
|
||||
BlendPalette(paletteIndex, 16, 12, RGB(28, 31, 28));
|
||||
// In horizontal fog, only specific palettes should be fog-blended
|
||||
if (allowFog)
|
||||
{
|
||||
i = min((gTimeOfDay + 1) * 4, 12); // fog coeff, highest in day and lowest at night
|
||||
paletteIndex = PLTT_ID(paletteIndex);
|
||||
// First blend with time
|
||||
CpuFastCopy(gPlttBufferUnfaded + paletteIndex, gPlttBufferFaded + paletteIndex, PLTT_SIZE_4BPP);
|
||||
UpdateSpritePaletteWithTime(spritePaletteIndex);
|
||||
// Then blend faded->faded with fog coeff
|
||||
BlendPalettesFine(1, gPlttBufferFaded + paletteIndex, gPlttBufferFaded + paletteIndex, i, RGB(28, 31, 28));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, just time-blend the palette
|
||||
UpdateSpritePaletteWithTime(spritePaletteIndex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -874,7 +909,7 @@ void ApplyWeatherGammaShiftToPal(u8 paletteIndex)
|
||||
void LoadCustomWeatherSpritePalette(const u16 *palette)
|
||||
{
|
||||
LoadPalette(palette, OBJ_PLTT_ID(gWeatherPtr->weatherPicSpritePalIndex), PLTT_SIZE_4BPP);
|
||||
UpdateSpritePaletteWithWeather(gWeatherPtr->weatherPicSpritePalIndex);
|
||||
UpdateSpritePaletteWithWeather(gWeatherPtr->weatherPicSpritePalIndex, TRUE);
|
||||
}
|
||||
|
||||
static void LoadDroughtWeatherPalette(u8 *gammaIndexPtr, u8 *a1)
|
||||
|
||||
186
src/overworld.c
186
src/overworld.c
@@ -7,6 +7,7 @@
|
||||
#include "event_data.h"
|
||||
#include "event_object_movement.h"
|
||||
#include "event_scripts.h"
|
||||
#include "fake_rtc.h"
|
||||
#include "field_camera.h"
|
||||
#include "field_control_avatar.h"
|
||||
#include "field_effect.h"
|
||||
@@ -38,6 +39,7 @@
|
||||
#include "random.h"
|
||||
#include "renewable_hidden_items.h"
|
||||
#include "roamer.h"
|
||||
#include "rtc.h"
|
||||
#include "safari_zone.h"
|
||||
#include "save_location.h"
|
||||
#include "scanline_effect.h"
|
||||
@@ -121,6 +123,10 @@ static KeyInterCB sPlayerKeyInterceptCallback;
|
||||
static bool8 sReceivingFromLink;
|
||||
static u8 sRfuKeepAliveTimer;
|
||||
|
||||
u8 gTimeOfDay;
|
||||
struct TimeBlendSettings currentTimeBlend;
|
||||
u16 gTimeUpdateCounter; // playTimeVBlanks will eventually overflow, so this is used to update TOD
|
||||
|
||||
static u8 CountBadgesForOverworldWhiteOutLossCalculation(void);
|
||||
static void Overworld_ResetStateAfterWhitingOut(void);
|
||||
static void Overworld_SetWhiteoutRespawnPoint(void);
|
||||
@@ -1460,6 +1466,168 @@ void CB1_Overworld(void)
|
||||
}
|
||||
}
|
||||
|
||||
#define TINT_NIGHT Q_8_8(0.456) | Q_8_8(0.456) << 8 | Q_8_8(0.615) << 16
|
||||
|
||||
const struct BlendSettings gTimeOfDayBlend[] =
|
||||
{
|
||||
[TIME_OF_DAY_NIGHT] = {.coeff = 10, .blendColor = TINT_NIGHT, .isTint = TRUE},
|
||||
[TIME_OF_DAY_TWILIGHT] = {.coeff = 4, .blendColor = 0xA8B0E0, .isTint = TRUE},
|
||||
[TIME_OF_DAY_DAY] = {.coeff = 0, .blendColor = 0},
|
||||
};
|
||||
|
||||
#define MORNING_START_MINUTES (MORNING_HOUR_BEGIN * MINUTES_PER_HOUR)
|
||||
#define MORNING_MID_POINT (MORNING_HOUR_BEGIN * MINUTES_PER_HOUR + (MORNING_HOUR_END * MINUTES_PER_HOUR - MORNING_HOUR_BEGIN * MINUTES_PER_HOUR) / 2)
|
||||
#define MORNING_END_MINUTES (MORNING_HOUR_END * MINUTES_PER_HOUR)
|
||||
#define EVENING_START_MINUTES (EVENING_HOUR_BEGIN * MINUTES_PER_HOUR)
|
||||
#define EVENING_MID_POINT (EVENING_HOUR_BEGIN * MINUTES_PER_HOUR + (EVENING_HOUR_END * MINUTES_PER_HOUR - EVENING_HOUR_BEGIN * MINUTES_PER_HOUR) / 2)
|
||||
#define EVENING_END_MINUTES (EVENING_HOUR_END * MINUTES_PER_HOUR)
|
||||
|
||||
u8 UpdateTimeOfDay(void)
|
||||
{
|
||||
s32 hours, minutes;
|
||||
s32 minutesOfDay;
|
||||
RtcCalcLocalTime();
|
||||
hours = gLocalTime.hours;
|
||||
minutes = gLocalTime.minutes;
|
||||
minutesOfDay = hours * MINUTES_PER_HOUR + minutes;
|
||||
if (minutesOfDay < MORNING_START_MINUTES) // night
|
||||
{
|
||||
currentTimeBlend.weight = 256;
|
||||
currentTimeBlend.altWeight = 0;
|
||||
gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_NIGHT;
|
||||
}
|
||||
else if (minutesOfDay < MORNING_MID_POINT) // night->twilight
|
||||
{
|
||||
currentTimeBlend.time0 = TIME_OF_DAY_NIGHT;
|
||||
currentTimeBlend.time1 = TIME_OF_DAY_TWILIGHT;
|
||||
currentTimeBlend.weight = 256 - 256 * (hours * 60 - MORNING_START_MINUTES + minutes) / (MORNING_MID_POINT - MORNING_START_MINUTES);
|
||||
currentTimeBlend.altWeight = (256 - currentTimeBlend.weight) / 2;
|
||||
gTimeOfDay = TIME_OF_DAY_DAY;
|
||||
}
|
||||
else if (minutesOfDay < MORNING_END_MINUTES) // twilight->day
|
||||
{
|
||||
currentTimeBlend.time0 = TIME_OF_DAY_TWILIGHT;
|
||||
currentTimeBlend.time1 = TIME_OF_DAY_DAY;
|
||||
currentTimeBlend.weight = 256 - 256 * (hours * 60 - MORNING_MID_POINT + minutes) / (MORNING_END_MINUTES - MORNING_MID_POINT);
|
||||
currentTimeBlend.altWeight = (256 - currentTimeBlend.weight) / 2 + 128;
|
||||
gTimeOfDay = TIME_OF_DAY_DAY;
|
||||
}
|
||||
else if (minutesOfDay < EVENING_START_MINUTES) // day
|
||||
{
|
||||
currentTimeBlend.weight = currentTimeBlend.altWeight = 256;
|
||||
gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_DAY;
|
||||
}
|
||||
else if (minutesOfDay < EVENING_MID_POINT)
|
||||
{
|
||||
currentTimeBlend.time0 = TIME_OF_DAY_DAY;
|
||||
currentTimeBlend.time1 = TIME_OF_DAY_TWILIGHT;
|
||||
currentTimeBlend.weight = 256 - 256 * (hours * 60 - EVENING_START_MINUTES + minutes) / (EVENING_MID_POINT - EVENING_START_MINUTES);
|
||||
currentTimeBlend.altWeight = currentTimeBlend.weight / 2 + 128;
|
||||
gTimeOfDay = TIME_OF_DAY_TWILIGHT;
|
||||
}
|
||||
else if (minutesOfDay < EVENING_END_MINUTES) // twilight->night
|
||||
{
|
||||
currentTimeBlend.time0 = TIME_OF_DAY_TWILIGHT;
|
||||
currentTimeBlend.time1 = TIME_OF_DAY_NIGHT;
|
||||
currentTimeBlend.weight = 256 - 256 * (hours * 60 - EVENING_MID_POINT + minutes) / (EVENING_END_MINUTES - EVENING_MID_POINT);
|
||||
currentTimeBlend.altWeight = currentTimeBlend.weight / 2;
|
||||
gTimeOfDay = TIME_OF_DAY_NIGHT;
|
||||
}
|
||||
else // 22-24, night
|
||||
{
|
||||
currentTimeBlend.weight = 256;
|
||||
currentTimeBlend.altWeight = 0;
|
||||
gTimeOfDay = currentTimeBlend.time0 = currentTimeBlend.time1 = TIME_OF_DAY_NIGHT;
|
||||
}
|
||||
return gTimeOfDay;
|
||||
}
|
||||
|
||||
// Whether a map type is naturally lit/outside
|
||||
bool8 MapHasNaturalLight(u8 mapType)
|
||||
{
|
||||
return (mapType == MAP_TYPE_TOWN
|
||||
|| mapType == MAP_TYPE_CITY
|
||||
|| mapType == MAP_TYPE_ROUTE
|
||||
|| mapType == MAP_TYPE_OCEAN_ROUTE
|
||||
);
|
||||
}
|
||||
|
||||
// Update & mix day / night bg palettes (into unfaded)
|
||||
void UpdateAltBgPalettes(u16 palettes)
|
||||
{
|
||||
const struct Tileset *primary = gMapHeader.mapLayout->primaryTileset;
|
||||
const struct Tileset *secondary = gMapHeader.mapLayout->secondaryTileset;
|
||||
u32 i = 1;
|
||||
if (QL_IS_PLAYBACK_STATE)
|
||||
return;
|
||||
if (!MapHasNaturalLight(gMapHeader.mapType))
|
||||
return;
|
||||
palettes &= ~((1 << NUM_PALS_IN_PRIMARY) - 1) | primary->swapPalettes;
|
||||
palettes &= ((1 << NUM_PALS_IN_PRIMARY) - 1) | (secondary->swapPalettes << NUM_PALS_IN_PRIMARY);
|
||||
palettes &= PALETTES_MAP ^ (1 << 0); // don't blend palette 0, [13,15]
|
||||
palettes >>= 1; // start at palette 1
|
||||
if (!palettes)
|
||||
return;
|
||||
while (palettes)
|
||||
{
|
||||
if (palettes & 1)
|
||||
{
|
||||
if (i < NUM_PALS_IN_PRIMARY)
|
||||
AvgPaletteWeighted(&((u16*)primary->palettes)[i*16], &((u16*)primary->palettes)[((i+9)%16)*16], gPlttBufferUnfaded + i * 16, currentTimeBlend.altWeight);
|
||||
else
|
||||
AvgPaletteWeighted(&((u16*)secondary->palettes)[i*16], &((u16*)secondary->palettes)[((i+9)%16)*16], gPlttBufferUnfaded + i * 16, currentTimeBlend.altWeight);
|
||||
}
|
||||
i++;
|
||||
palettes >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePalettesWithTime(u32 palettes)
|
||||
{
|
||||
if (QL_IS_PLAYBACK_STATE)
|
||||
return;
|
||||
if (MapHasNaturalLight(gMapHeader.mapType))
|
||||
{
|
||||
u32 i;
|
||||
u32 mask = 1 << 16;
|
||||
if (palettes >= (1 << 16))
|
||||
for (i = 0; i < 16; i++, mask <<= 1)
|
||||
if (IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(i)))
|
||||
palettes &= ~(mask);
|
||||
|
||||
palettes &= PALETTES_MAP | PALETTES_OBJECTS; // Don't blend UI pals
|
||||
if (!palettes)
|
||||
return;
|
||||
TimeMixPalettes(
|
||||
palettes,
|
||||
gPlttBufferUnfaded,
|
||||
gPlttBufferFaded,
|
||||
(struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time0],
|
||||
(struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time1],
|
||||
currentTimeBlend.weight
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
u8 UpdateSpritePaletteWithTime(u8 paletteNum)
|
||||
{
|
||||
if (QL_IS_PLAYBACK_STATE)
|
||||
return paletteNum;
|
||||
if (MapHasNaturalLight(gMapHeader.mapType))
|
||||
{
|
||||
if (GetSpritePaletteTagByPaletteNum(paletteNum) != 0xFFFF && IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(paletteNum)))
|
||||
return paletteNum;
|
||||
TimeMixPalettes(1,
|
||||
&gPlttBufferUnfaded[OBJ_PLTT_ID(paletteNum)],
|
||||
&gPlttBufferFaded[OBJ_PLTT_ID(paletteNum)],
|
||||
(struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time0],
|
||||
(struct BlendSettings *)&gTimeOfDayBlend[currentTimeBlend.time1],
|
||||
currentTimeBlend.weight
|
||||
);
|
||||
}
|
||||
return paletteNum;
|
||||
}
|
||||
|
||||
static void OverworldBasic(void)
|
||||
{
|
||||
ScriptContext_RunScript();
|
||||
@@ -1472,6 +1640,24 @@ static void OverworldBasic(void)
|
||||
UpdatePaletteFade();
|
||||
UpdateTilesetAnimations();
|
||||
DoScheduledBgTilemapCopiesToVram();
|
||||
// Every minute if no palette fade is active, update TOD blending as needed
|
||||
if (!gPaletteFade.active && ++gTimeUpdateCounter >= (SECONDS_PER_MINUTE * 1 / FakeRtc_GetSecondsRatio()))
|
||||
{
|
||||
struct TimeBlendSettings cachedBlend = {
|
||||
.time0 = currentTimeBlend.time0,
|
||||
.time1 = currentTimeBlend.time1,
|
||||
.weight = currentTimeBlend.weight,
|
||||
};
|
||||
gTimeUpdateCounter = 0;
|
||||
UpdateTimeOfDay();
|
||||
if (cachedBlend.time0 != currentTimeBlend.time0
|
||||
|| cachedBlend.time1 != currentTimeBlend.time1
|
||||
|| cachedBlend.weight != currentTimeBlend.weight)
|
||||
{
|
||||
UpdateAltBgPalettes(PALETTES_BG);
|
||||
UpdatePalettesWithTime(PALETTES_ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This CB2 is used when starting
|
||||
|
||||
444
src/palette.c
444
src/palette.c
@@ -9,11 +9,13 @@ enum
|
||||
NORMAL_FADE,
|
||||
FAST_FADE,
|
||||
HARDWARE_FADE,
|
||||
TIME_OF_DAY_FADE,
|
||||
};
|
||||
|
||||
static u32 UpdateNormalPaletteFade(void);
|
||||
static void BeginFastPaletteFadeInternal(u32);
|
||||
static u32 UpdateFastPaletteFade(void);
|
||||
static u8 UpdateTimeOfDayPaletteFade(void);
|
||||
static u32 UpdateHardwarePaletteFade(void);
|
||||
static void UpdateBlendRegisters(void);
|
||||
static bool32 IsSoftwarePaletteFadeFinishing(void);
|
||||
@@ -77,6 +79,8 @@ u32 UpdatePaletteFade(void)
|
||||
result = UpdateNormalPaletteFade();
|
||||
else if (gPaletteFade.mode == FAST_FADE)
|
||||
result = UpdateFastPaletteFade();
|
||||
else if (gPaletteFade.mode == TIME_OF_DAY_FADE)
|
||||
result = UpdateTimeOfDayPaletteFade();
|
||||
else
|
||||
result = UpdateHardwarePaletteFade();
|
||||
sPlttBufferTransferPending = gPaletteFade.multipurpose1;
|
||||
@@ -128,6 +132,56 @@ bool32 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targ
|
||||
}
|
||||
}
|
||||
|
||||
// Like normal palette fade but respects sprite/tile palettes immune to time of day fading
|
||||
bool32 BeginTimeOfDayPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, struct BlendSettings *bld0, struct BlendSettings *bld1, u32 weight, u32 color)
|
||||
{
|
||||
u8 temp;
|
||||
|
||||
if (gPaletteFade.active)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
gPaletteFade.deltaY = 2;
|
||||
|
||||
if (delay < 0)
|
||||
{
|
||||
gPaletteFade.deltaY += (delay * -1);
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
gPaletteFade_selectedPalettes = selectedPalettes;
|
||||
gPaletteFade.delayCounter = delay;
|
||||
gPaletteFade_delay = delay;
|
||||
gPaletteFade.y = startY;
|
||||
gPaletteFade.targetY = targetY;
|
||||
gPaletteFade.active = 1;
|
||||
gPaletteFade.mode = TIME_OF_DAY_FADE;
|
||||
|
||||
gPaletteFade.blendColor = color;
|
||||
gPaletteFade.bld0 = bld0;
|
||||
gPaletteFade.bld1 = bld1;
|
||||
gPaletteFade.weight = weight;
|
||||
|
||||
if (startY < targetY)
|
||||
gPaletteFade.yDec = 0;
|
||||
else
|
||||
gPaletteFade.yDec = 1;
|
||||
|
||||
UpdatePaletteFade();
|
||||
|
||||
temp = gPaletteFade.bufferTransferDisabled;
|
||||
gPaletteFade.bufferTransferDisabled = 0;
|
||||
CpuCopy32(gPlttBufferFaded, (void *)PLTT, PLTT_SIZE);
|
||||
sPlttBufferTransferPending = 0;
|
||||
if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
|
||||
UpdateBlendRegisters();
|
||||
gPaletteFade.bufferTransferDisabled = temp;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetPaletteFadeControl(void)
|
||||
{
|
||||
gPaletteFade.multipurpose1 = 0;
|
||||
@@ -148,6 +202,119 @@ void ResetPaletteFadeControl(void)
|
||||
gPaletteFade.deltaY = 2;
|
||||
}
|
||||
|
||||
// Like normal palette fade, but respects sprite/tile palettes immune to time of day fading
|
||||
static u8 UpdateTimeOfDayPaletteFade(void)
|
||||
{
|
||||
u16 paletteOffset;
|
||||
u16 selectedPalettes;
|
||||
u16 timePalettes = 0; // palettes passed to the time-blender
|
||||
u16 copyPalettes;
|
||||
u16 * src;
|
||||
u16 * dst;
|
||||
|
||||
if (!gPaletteFade.active)
|
||||
return PALETTE_FADE_STATUS_DONE;
|
||||
|
||||
if (IsSoftwarePaletteFadeFinishing())
|
||||
return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
|
||||
|
||||
if (!gPaletteFade.objPaletteToggle)
|
||||
{
|
||||
if (gPaletteFade.delayCounter < gPaletteFade_delay)
|
||||
{
|
||||
gPaletteFade.delayCounter++;
|
||||
return 2;
|
||||
}
|
||||
gPaletteFade.delayCounter = 0;
|
||||
}
|
||||
|
||||
paletteOffset = 0;
|
||||
|
||||
if (!gPaletteFade.objPaletteToggle)
|
||||
{
|
||||
selectedPalettes = gPaletteFade_selectedPalettes;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedPalettes = gPaletteFade_selectedPalettes >> 16;
|
||||
paletteOffset = 256;
|
||||
}
|
||||
|
||||
src = gPlttBufferUnfaded + paletteOffset;
|
||||
dst = gPlttBufferFaded + paletteOffset;
|
||||
|
||||
// First pply TOD blend to relevant subset of palettes
|
||||
if (gPaletteFade.objPaletteToggle) // Sprite palettes, don't blend those with tags
|
||||
{
|
||||
u32 i;
|
||||
u32 j = 1;
|
||||
for (i = 0; i < 16; i++, j <<= 1) // Mask out palettes that should not be light blended
|
||||
{
|
||||
if ((selectedPalettes & j) && !IS_BLEND_IMMUNE_TAG(GetSpritePaletteTagByPaletteNum(i)))
|
||||
timePalettes |= j;
|
||||
}
|
||||
}
|
||||
else // tile palettes, don't blend [13, 15]
|
||||
{
|
||||
timePalettes = selectedPalettes & PALETTES_MAP;
|
||||
}
|
||||
TimeMixPalettes(timePalettes, src, dst, gPaletteFade.bld0, gPaletteFade.bld1, gPaletteFade.weight);
|
||||
|
||||
// palettes that were not blended above must be copied through
|
||||
if ((copyPalettes = ~timePalettes))
|
||||
{
|
||||
u16 * src1 = src;
|
||||
u16 * dst1 = dst;
|
||||
while (copyPalettes)
|
||||
{
|
||||
if (copyPalettes & 1)
|
||||
CpuFastCopy(src1, dst1, 32);
|
||||
copyPalettes >>= 1;
|
||||
src1 += 16;
|
||||
dst1 += 16;
|
||||
}
|
||||
}
|
||||
|
||||
// Then, blend from faded->faded with native BlendPalettes
|
||||
BlendPalettesFine(selectedPalettes, dst, dst, gPaletteFade.y, gPaletteFade.blendColor);
|
||||
|
||||
gPaletteFade.objPaletteToggle ^= 1;
|
||||
|
||||
if (!gPaletteFade.objPaletteToggle)
|
||||
{
|
||||
if ((gPaletteFade.yDec && gPaletteFade.y == 0) || (!gPaletteFade.yDec && gPaletteFade.y == gPaletteFade.targetY))
|
||||
{
|
||||
gPaletteFade_selectedPalettes = 0;
|
||||
gPaletteFade.softwareFadeFinishing = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s8 val;
|
||||
|
||||
if (!gPaletteFade.yDec)
|
||||
{
|
||||
val = gPaletteFade.y;
|
||||
val += gPaletteFade.deltaY;
|
||||
if (val > gPaletteFade.targetY)
|
||||
val = gPaletteFade.targetY;
|
||||
gPaletteFade.y = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
val = gPaletteFade.y;
|
||||
val -= gPaletteFade.deltaY;
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
gPaletteFade.y = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gPaletteFade.active cannot change since the last time it was checked. So this
|
||||
// is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
|
||||
return PALETTE_FADE_STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
static u32 UpdateNormalPaletteFade(void)
|
||||
{
|
||||
u16 paletteOffset;
|
||||
@@ -492,6 +659,46 @@ static bool32 IsSoftwarePaletteFadeFinishing(void)
|
||||
}
|
||||
}
|
||||
|
||||
// optimized based on lucktyphlosion's BlendPalettesFine
|
||||
void BlendPalettesFine(u32 palettes, u16 *src, u16 *dst, u32 coeff, u32 color)
|
||||
{
|
||||
s32 newR, newG, newB;
|
||||
|
||||
if (!palettes)
|
||||
return;
|
||||
|
||||
coeff *= 2;
|
||||
newR = (color << 27) >> 27;
|
||||
newG = (color << 22) >> 27;
|
||||
newB = (color << 17) >> 27;
|
||||
|
||||
do
|
||||
{
|
||||
if (palettes & 1)
|
||||
{
|
||||
u16 *srcEnd = src + 16;
|
||||
while (src != srcEnd) // Transparency is blended (for backdrop reasons)
|
||||
{
|
||||
u32 srcColor = *src;
|
||||
s32 r = (srcColor << 27) >> 27;
|
||||
s32 g = (srcColor << 22) >> 27;
|
||||
s32 b = (srcColor << 16) >> 26;
|
||||
|
||||
*dst++ = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0)
|
||||
| ((g + (((newG - g) * (s32)coeff) >> 5)) << 5)
|
||||
| ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10);
|
||||
src++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src += 16;
|
||||
dst += 16;
|
||||
}
|
||||
palettes >>= 1;
|
||||
} while (palettes);
|
||||
}
|
||||
|
||||
void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color)
|
||||
{
|
||||
u16 paletteOffset;
|
||||
@@ -504,6 +711,243 @@ void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color)
|
||||
}
|
||||
}
|
||||
|
||||
#define DEFAULT_LIGHT_COLOR 0x3f9f
|
||||
|
||||
// Like BlendPalette, but ignores blendColor if the transparency high bit is set
|
||||
// Optimization help by lucktyphlosion
|
||||
void TimeBlendPalette(u16 palOffset, u32 coeff, u32 blendColor)
|
||||
{
|
||||
s32 newR, newG, newB, defR, defG, defB;
|
||||
u16 * src = gPlttBufferUnfaded + palOffset;
|
||||
u16 * dst = gPlttBufferFaded + palOffset;
|
||||
u32 defaultBlendColor = DEFAULT_LIGHT_COLOR;
|
||||
u16 *srcEnd = src + 16;
|
||||
u32 altBlendColor = *dst++ = *src++; // color 0 is copied through unchanged
|
||||
|
||||
coeff *= 2;
|
||||
newR = (blendColor << 27) >> 27;
|
||||
newG = (blendColor << 22) >> 27;
|
||||
newB = (blendColor << 17) >> 27;
|
||||
|
||||
if (altBlendColor >> 15) // Transparency high bit set; alt blend color
|
||||
{
|
||||
defR = (altBlendColor << 27) >> 27;
|
||||
defG = (altBlendColor << 22) >> 27;
|
||||
defB = (altBlendColor << 17) >> 27;
|
||||
}
|
||||
else
|
||||
{
|
||||
defR = (defaultBlendColor << 27) >> 27;
|
||||
defG = (defaultBlendColor << 22) >> 27;
|
||||
defB = (defaultBlendColor << 17) >> 27;
|
||||
altBlendColor = 0;
|
||||
}
|
||||
while (src != srcEnd)
|
||||
{
|
||||
u32 srcColor = *src;
|
||||
s32 r = (srcColor << 27) >> 27;
|
||||
s32 g = (srcColor << 22) >> 27;
|
||||
s32 b = (srcColor << 16) >> 26;
|
||||
|
||||
if (srcColor >> 15)
|
||||
{
|
||||
*dst = ((r + (((defR - r) * (s32)coeff) >> 5)) << 0)
|
||||
| ((g + (((defG - g) * (s32)coeff) >> 5)) << 5)
|
||||
| ((b + (((defB - (b & 31)) * (s32)coeff) >> 5)) << 10);
|
||||
}
|
||||
else // Use provided blend color
|
||||
{
|
||||
*dst = ((r + (((newR - r) * (s32)coeff) >> 5)) << 0)
|
||||
| ((g + (((newG - g) * (s32)coeff) >> 5)) << 5)
|
||||
| ((b + (((newB - (b & 31)) * (s32)coeff) >> 5)) << 10);
|
||||
}
|
||||
src++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
// Blends a weighted average of two blend parameters
|
||||
// Parameters can be either blended (as in BlendPalettes) or tinted (as in TintPaletteRGB_Copy)
|
||||
void TimeMixPalettes(u32 palettes, u16 *src, u16 *dst, struct BlendSettings *blend0, struct BlendSettings *blend1, u16 weight0)
|
||||
{
|
||||
s32 r0, g0, b0, r1, g1, b1, defR, defG, defB, altR, altG, altB;
|
||||
u32 color0, coeff0, color1, coeff1;
|
||||
bool8 tint0, tint1;
|
||||
u32 defaultColor = DEFAULT_LIGHT_COLOR;
|
||||
|
||||
if (!palettes)
|
||||
return;
|
||||
|
||||
color0 = blend0->blendColor;
|
||||
tint0 = blend0->isTint;
|
||||
coeff0 = tint0 ? 8*2 : blend0->coeff*2;
|
||||
color1 = blend1->blendColor;
|
||||
tint1 = blend1->isTint;
|
||||
coeff1 = tint1 ? 8*2 : blend1->coeff*2;
|
||||
|
||||
if (tint0)
|
||||
{
|
||||
r0 = (color0 << 24) >> 24;
|
||||
g0 = (color0 << 16) >> 24;
|
||||
b0 = (color0 << 8) >> 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
r0 = (color0 << 27) >> 27;
|
||||
g0 = (color0 << 22) >> 27;
|
||||
b0 = (color0 << 17) >> 27;
|
||||
}
|
||||
if (tint1)
|
||||
{
|
||||
r1 = (color1 << 24) >> 24;
|
||||
g1 = (color1 << 16) >> 24;
|
||||
b1 = (color1 << 8) >> 24;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1 = (color1 << 27) >> 27;
|
||||
g1 = (color1 << 22) >> 27;
|
||||
b1 = (color1 << 17) >> 27;
|
||||
}
|
||||
defR = (defaultColor << 27) >> 27;
|
||||
defG = (defaultColor << 22) >> 27;
|
||||
defB = (defaultColor << 17) >> 27;
|
||||
|
||||
do
|
||||
{
|
||||
if (palettes & 1)
|
||||
{
|
||||
u16 *srcEnd = src + 16;
|
||||
u32 altBlendColor = *dst++ = *src++; // color 0 is copied through
|
||||
if (altBlendColor >> 15) // Transparency high bit set; alt blend color
|
||||
{
|
||||
altR = (altBlendColor << 27) >> 27;
|
||||
altG = (altBlendColor << 22) >> 27;
|
||||
altB = (altBlendColor << 17) >> 27;
|
||||
}
|
||||
else
|
||||
{
|
||||
altBlendColor = 0;
|
||||
}
|
||||
while (src != srcEnd)
|
||||
{
|
||||
u32 srcColor = *src;
|
||||
s32 r = (srcColor << 27) >> 27;
|
||||
s32 g = (srcColor << 22) >> 27;
|
||||
s32 b = (srcColor << 17) >> 27;
|
||||
s32 r2, g2, b2;
|
||||
|
||||
if (srcColor >> 15)
|
||||
{
|
||||
if (altBlendColor)
|
||||
{
|
||||
// Use alternate blend color
|
||||
r2 = r + (((altR - r) * (s32)coeff1) >> 5);
|
||||
g2 = g + (((altG - g) * (s32)coeff1) >> 5);
|
||||
b2 = b + (((altB - b) * (s32)coeff1) >> 5);
|
||||
r = r + (((altR - r) * (s32)coeff0) >> 5);
|
||||
g = g + (((altG - g) * (s32)coeff0) >> 5);
|
||||
b = b + (((altB - b) * (s32)coeff0) >> 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use default blend color
|
||||
r2 = r + (((defR - r) * (s32)coeff1) >> 5);
|
||||
g2 = g + (((defG - g) * (s32)coeff1) >> 5);
|
||||
b2 = b + (((defB - b) * (s32)coeff1) >> 5);
|
||||
r = r + (((defR - r) * (s32)coeff0) >> 5);
|
||||
g = g + (((defG - g) * (s32)coeff0) >> 5);
|
||||
b = b + (((defB - b) * (s32)coeff0) >> 5);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use provided blend colors
|
||||
if (!tint1)
|
||||
{
|
||||
// blend-based
|
||||
r2 = (r + (((r1 - r) * (s32)coeff1) >> 5));
|
||||
g2 = (g + (((g1 - g) * (s32)coeff1) >> 5));
|
||||
b2 = (b + (((b1 - b) * (s32)coeff1) >> 5));
|
||||
}
|
||||
else
|
||||
{
|
||||
// tint-based
|
||||
r2 = (u16)((r1 * r)) >> 8;
|
||||
g2 = (u16)((g1 * g)) >> 8;
|
||||
b2 = (u16)((b1 * b)) >> 8;
|
||||
if (r2 > 31)
|
||||
r2 = 31;
|
||||
if (g2 > 31)
|
||||
g2 = 31;
|
||||
if (b2 > 31)
|
||||
b2 = 31;
|
||||
}
|
||||
if (!tint0)
|
||||
{
|
||||
// blend-based
|
||||
r = (r + (((r0 - r) * (s32)coeff0) >> 5));
|
||||
g = (g + (((g0 - g) * (s32)coeff0) >> 5));
|
||||
b = (b + (((b0 - b) * (s32)coeff0) >> 5));
|
||||
}
|
||||
else
|
||||
{
|
||||
// tint-based
|
||||
r = (u16)((r0 * r)) >> 8;
|
||||
g = (u16)((g0 * g)) >> 8;
|
||||
b = (u16)((b0 * b)) >> 8;
|
||||
if (r > 31)
|
||||
r = 31;
|
||||
if (g > 31)
|
||||
g = 31;
|
||||
if (b > 31)
|
||||
b = 31;
|
||||
}
|
||||
}
|
||||
r = r2 + (((r - r2) * (s32)weight0) >> 8);
|
||||
g = g2 + (((g - g2) * (s32)weight0) >> 8);
|
||||
b = b2 + (((b - b2) * (s32)weight0) >> 8);
|
||||
*dst++ = RGB2(r, g, b);
|
||||
// *dst++ = RGB2(r, g, b) | (srcColor >> 15) << 15;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src += 16;
|
||||
dst += 16;
|
||||
}
|
||||
palettes >>= 1;
|
||||
} while (palettes);
|
||||
}
|
||||
|
||||
// Apply weighted average to palettes, preserving high bits of dst throughout
|
||||
void AvgPaletteWeighted(u16 *src0, u16 *src1, u16 *dst, u16 weight0)
|
||||
{
|
||||
u16 *srcEnd = src0 + 16;
|
||||
src0++;
|
||||
src1++;
|
||||
dst++; // leave dst transparency unchanged
|
||||
while (src0 != srcEnd)
|
||||
{
|
||||
u32 src0Color = *src0++;
|
||||
s32 r0 = (src0Color << 27) >> 27;
|
||||
s32 g0 = (src0Color << 22) >> 27;
|
||||
s32 b0 = (src0Color << 17) >> 27;
|
||||
u32 src1Color = *src1++;
|
||||
s32 r1 = (src1Color << 27) >> 27;
|
||||
s32 g1 = (src1Color << 22) >> 27;
|
||||
s32 b1 = (src1Color << 17) >> 27;
|
||||
|
||||
// Average and bitwise-OR
|
||||
r0 = r1 + (((r0 - r1) * weight0) >> 8);
|
||||
g0 = g1 + (((g0 - g1) * weight0) >> 8);
|
||||
b0 = b1 + (((b0 - b1) * weight0) >> 8);
|
||||
*dst = (*dst & RGB_ALPHA) | RGB2(r0, g0, b0); // preserve high bit of dst
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u32 color)
|
||||
{
|
||||
// This copy is done via DMA in both RUBY and EMERALD
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "gflib.h"
|
||||
#include "overworld.h"
|
||||
#include "palette_util.h"
|
||||
#include "util.h"
|
||||
|
||||
@@ -374,6 +375,8 @@ void UpdatePulseBlend(struct PulseBlend *pulseBlend)
|
||||
if (--pulseBlendPalette->delayCounter == 0xFF)
|
||||
{
|
||||
pulseBlendPalette->delayCounter = pulseBlendPalette->pulseBlendSettings.delay;
|
||||
CpuFastCopy(gPlttBufferUnfaded + pulseBlendPalette->pulseBlendSettings.paletteOffset, gPlttBufferFaded + pulseBlendPalette->pulseBlendSettings.paletteOffset, PLTT_SIZE_4BPP);
|
||||
UpdatePalettesWithTime(1 << (pulseBlendPalette->pulseBlendSettings.paletteOffset >> 4));
|
||||
BlendPalette(pulseBlendPalette->pulseBlendSettings.paletteOffset, pulseBlendPalette->pulseBlendSettings.numColors, pulseBlendPalette->blendCoeff, pulseBlendPalette->pulseBlendSettings.blendColor);
|
||||
switch (pulseBlendPalette->pulseBlendSettings.fadeType)
|
||||
{
|
||||
|
||||
@@ -1246,6 +1246,15 @@ static bool8 RestoreScreenAfterPlayback(u8 taskId)
|
||||
|
||||
CopyPaletteInvertedTint(&gPlttBufferUnfaded[BG_PLTT_ID(0) + 1], &gPlttBufferFaded[BG_PLTT_ID(0) + 1], 0xDF, 15 - tTimer);
|
||||
CopyPaletteInvertedTint(&gPlttBufferUnfaded[OBJ_PLTT_ID(0)], &gPlttBufferFaded[OBJ_PLTT_ID(0)], 0x100, 15 - tTimer);
|
||||
|
||||
gTimeUpdateCounter = 0;
|
||||
UpdateTimeOfDay();
|
||||
|
||||
if (MapHasNaturalLight(gMapHeader.mapType))
|
||||
{
|
||||
UpdateAltBgPalettes(PALETTES_BG);
|
||||
UpdatePalettesWithTime(PALETTES_ALL);
|
||||
}
|
||||
FillWindowPixelRect(sWindowIds[WIN_TOP_BAR],
|
||||
0x00, 0,
|
||||
sWindowTemplates[WIN_TOP_BAR].height * 8 - 1 - tTimer,
|
||||
|
||||
27
src/scrcmd.c
27
src/scrcmd.c
@@ -3,6 +3,7 @@
|
||||
#include "clock.h"
|
||||
#include "rtc.h"
|
||||
#include "script.h"
|
||||
#include "decompress.h"
|
||||
#include "mystery_event_script.h"
|
||||
#include "event_data.h"
|
||||
#include "event_scripts.h"
|
||||
@@ -646,6 +647,32 @@ bool8 ScrCmd_fadescreenspeed(struct ScriptContext * ctx)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool8 ScrCmd_fadescreenswapbuffers(struct ScriptContext *ctx)
|
||||
{
|
||||
u8 mode = ScriptReadByte(ctx);
|
||||
u8 nowait = ScriptReadByte(ctx);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case FADE_TO_BLACK:
|
||||
case FADE_TO_WHITE:
|
||||
default:
|
||||
CpuCopy32(gPlttBufferUnfaded, gDecompressionBuffer, PLTT_SIZE);
|
||||
FadeScreen(mode, 0);
|
||||
break;
|
||||
case FADE_FROM_BLACK:
|
||||
case FADE_FROM_WHITE:
|
||||
CpuCopy32(gDecompressionBuffer, gPlttBufferUnfaded, PLTT_SIZE);
|
||||
FadeScreen(mode, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (nowait)
|
||||
return FALSE;
|
||||
SetupNativeScript(ctx, IsPaletteNotActive);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool8 RunPauseTimer(void)
|
||||
{
|
||||
if (--sPauseCounter == 0)
|
||||
|
||||
Reference in New Issue
Block a user