From 5b198635adafc95c11b820966b8f403375c098c4 Mon Sep 17 00:00:00 2001 From: Arceveti <73617174+Arceveti@users.noreply.github.com> Date: Mon, 27 Sep 2021 22:33:00 -0700 Subject: [PATCH] Improve puppyprint font a bit more --- src/game/platform_displacement.c | 40 ++-- src/game/platform_displacement.h | 4 +- src/game/print.c | 5 +- src/game/puppycam2.c | 35 ++-- src/game/puppylights.c | 300 ++++++++++++--------------- src/game/puppyprint.c | 89 ++++---- src/game/puppyprint.h | 40 ++-- textures/segment2/custom_text.i4.png | Bin 16346 -> 16247 bytes 8 files changed, 236 insertions(+), 277 deletions(-) diff --git a/src/game/platform_displacement.c b/src/game/platform_displacement.c index 39599038..bbb4b5fa 100644 --- a/src/game/platform_displacement.c +++ b/src/game/platform_displacement.c @@ -21,9 +21,7 @@ struct Object *gMarioPlatform = NULL; */ void update_mario_platform(void) { struct Surface *floor; - f32 marioX; - f32 marioY; - f32 marioZ; + f32 marioX, marioY, marioZ; f32 floorHeight; u32 awayFromFloor; @@ -41,27 +39,19 @@ void update_mario_platform(void) { marioZ = gMarioObject->oPosZ; floorHeight = find_floor(marioX, marioY, marioZ, &floor); - if (absf(marioY - floorHeight) < 4.0f) { - awayFromFloor = 0; - } else { - awayFromFloor = 1; - } + awayFromFloor = (absf(marioY - floorHeight) >= 4.0f); - switch (awayFromFloor) { - case 1: + if (awayFromFloor) { + gMarioPlatform = NULL; + gMarioObject->platform = NULL; + } else { + if (floor != NULL && floor->object != NULL) { + gMarioPlatform = floor->object; + gMarioObject->platform = floor->object; + } else { gMarioPlatform = NULL; gMarioObject->platform = NULL; - break; - - case 0: - if (floor != NULL && floor->object != NULL) { - gMarioPlatform = floor->object; - gMarioObject->platform = floor->object; - } else { - gMarioPlatform = NULL; - gMarioObject->platform = NULL; - } - break; + } } } @@ -231,12 +221,8 @@ void apply_mario_platform_displacement(void) { * platform. If isMario is false, use gCurrentObject. */ void apply_platform_displacement(u32 isMario, struct Object *platform) { - f32 x; - f32 y; - f32 z; - f32 platformPosX; - f32 platformPosY; - f32 platformPosZ; + f32 x, y, z; + f32 platformPosX, platformPosY, platformPosZ; Vec3f currentObjectOffset; Vec3f relativeOffset; Vec3f newObjectOffset; diff --git a/src/game/platform_displacement.h b/src/game/platform_displacement.h index 5eef98d9..6029be9a 100644 --- a/src/game/platform_displacement.h +++ b/src/game/platform_displacement.h @@ -20,9 +20,9 @@ void update_mario_platform(void); void get_mario_pos(f32 *x, f32 *y, f32 *z); void set_mario_pos(f32 x, f32 y, f32 z); #ifdef PLATFORM_DISPLACEMENT_2 - void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform); +void apply_platform_displacement(struct PlatformDisplacementInfo *displaceInfo, Vec3f pos, s16 *yaw, struct Object *platform); #else - void apply_platform_displacement(u32 isMario, struct Object *platform); +void apply_platform_displacement(u32 isMario, struct Object *platform); #endif void apply_mario_platform_displacement(void); void clear_mario_platform(void); diff --git a/src/game/print.c b/src/game/print.c index de346548..6af48f13 100644 --- a/src/game/print.c +++ b/src/game/print.c @@ -52,7 +52,7 @@ void format_integer(s32 n, s32 base, char *dest, s32 *totalLength, u8 width, s8 s8 negative = FALSE; char pad; - if (zeroPad == TRUE) { + if (zeroPad) { pad = '0'; } else { pad = -1; @@ -207,8 +207,7 @@ void print_text_fmt_int(s32 x, s32 y, const char *str, s32 n) { srcIndex++; format_integer(n, base, sTextLabels[sTextLabelsCount]->buffer + len, &len, width, zeroPad); - } else // straight copy - { + } else { // straight copy sTextLabels[sTextLabelsCount]->buffer[len] = c; len++; srcIndex++; diff --git a/src/game/puppycam2.c b/src/game/puppycam2.c index 683ba402..ec6035bf 100644 --- a/src/game/puppycam2.c +++ b/src/game/puppycam2.c @@ -30,7 +30,7 @@ static inline float smooth(float x) { x = CLAMP(x, 0, 1); - return x * x * (3.f - 2.f * x); + return sqr(x) * (3.f - 2.f * x); } static inline float softClamp(float x, float a, float b) { @@ -43,12 +43,12 @@ static inline float softClamp(float x, float a, float b) { struct gPuppyStruct gPuppyCam; struct sPuppyVolume *sPuppyVolumeStack[MAX_PUPPYCAM_VOLUMES]; -s16 sFloorHeight = 0; -u8 gPCOptionOpen = 0; -s8 gPCOptionSelected = 0; +s16 sFloorHeight = 0; +u8 gPCOptionOpen = 0; +s8 gPCOptionSelected = 0; s32 gPCOptionTimer = 0; -u8 gPCOptionIndex = 0; -u8 gPCOptionScroll = 0; +u8 gPCOptionIndex = 0; +u8 gPCOptionScroll = 0; u16 gPuppyVolumeCount = 0; struct MemoryPool *gPuppyMemoryPool; s32 gPuppyError = 0; @@ -196,9 +196,9 @@ void puppycam_activate_cutscene(s32 (*scene)(), s32 lockinput) { gPuppyCam.sceneInput = lockinput; } -//If you've read camera.c this will look familiar. -//It takes the next 4 spline points and extrapolates a curvature based positioning of the camera vector that's passed through. -//It's a standard B spline +// If you've read camera.c this will look familiar. +// It takes the next 4 spline points and extrapolates a curvature based positioning of the camera vector that's passed through. +// It's a standard B spline static void puppycam_evaluate_spline(f32 progress, Vec3s cameraPos, Vec3f spline1, Vec3f spline2, Vec3f spline3, Vec3f spline4) { f32 tempP[4]; @@ -227,11 +227,11 @@ s32 puppycam_move_spline(struct sPuppySpline splinePos[], struct sPuppySpline sp gPuppyCam.splineIndex = index; } if (splinePos[gPuppyCam.splineIndex].index == -1 || splinePos[gPuppyCam.splineIndex + 1].index == -1 || splinePos[gPuppyCam.splineIndex + 2].index == -1) { - return 1; + return TRUE; } if (mode == PUPPYSPLINE_FOLLOW) { if (splineFocus[gPuppyCam.splineIndex].index == -1 || splineFocus[gPuppyCam.splineIndex + 1].index == -1 || splineFocus[gPuppyCam.splineIndex + 2].index == -1) { - return 1; + return TRUE; } } vec3f_set(prevPos, gPuppyCam.pos[0], gPuppyCam.pos[1], gPuppyCam.pos[2]); @@ -262,12 +262,12 @@ s32 puppycam_move_spline(struct sPuppySpline splinePos[], struct sPuppySpline sp if (splinePos[gPuppyCam.splineIndex + 3].index == -1) { gPuppyCam.splineIndex = 0; gPuppyCam.splineProgress = 0; - return 1; + return TRUE; } gPuppyCam.splineProgress -=1; } - return 0; + return FALSE; } static void puppycam_process_cutscene(void) { @@ -979,7 +979,6 @@ static s32 puppycam_check_volume_bounds(struct sPuppyVolume *volume, s32 index) void puppycam_wall_angle(void) { struct Surface *wall; struct WallCollisionData cData; - s16 wallYaw; if (!(gMarioState->action & ACT_WALL_KICK_AIR) || ((gMarioState->action & ACT_FLAG_AIR) && ABS(gMarioState->forwardVel) < 16.0f) || !(gMarioState->action & ACT_FLAG_AIR)) { return; @@ -995,7 +994,7 @@ void puppycam_wall_angle(void) { } else { return; } - wallYaw = atan2s(wall->normal.z, wall->normal.x) + 0x4000; + s16 wallYaw = atan2s(wall->normal.z, wall->normal.x) + 0x4000; wallYaw -= gPuppyCam.yawTarget; if (wallYaw % 0x4000) { @@ -1345,15 +1344,15 @@ void puppycam_loop(void) { puppycam_input_core(); puppycam_projection(); puppycam_script(); - if (gPuppyCam.flags & PUPPYCAM_BEHAVIOUR_COLLISION) + if (gPuppyCam.flags & PUPPYCAM_BEHAVIOUR_COLLISION) { puppycam_collision(); - else + } else { gPuppyCam.opacity = 255; + } } else if (gPuppyCam.cutscene) { gPuppyCam.opacity = 255; puppycam_process_cutscene(); } - puppycam_apply(); } diff --git a/src/game/puppylights.c b/src/game/puppylights.c index 64f86098..8cdacaf6 100644 --- a/src/game/puppylights.c +++ b/src/game/puppylights.c @@ -39,31 +39,31 @@ shaped and rotated correctly, for accurate representation of their properties. #ifdef PUPPYLIGHTS -Lights1 gLevelLight; //Existing ambient light in the area. Will be set by the level script, though can always be changed afterwards if desired. +Lights1 gLevelLight; // Existing ambient light in the area. Will be set by the level script, though can always be changed afterwards if desired. u8 levelAmbient = FALSE; -Lights1 *sLightBase; //The base value where lights are written to when worked with. -Lights1 sDefaultLights = gdSPDefLights1(0x7F, 0x7F, 0x7F, 0xFE, 0xFE, 0xFE, 0x28, 0x28, 0x28); //Default lights default lights -u16 gNumLights = 0; //How many lights are loaded. -u16 gDynLightStart = 0; //Where the dynamic lights will start. -struct PuppyLight *gPuppyLights[MAX_LIGHTS]; //This contains all the loaded data. -struct MemoryPool *gLightsPool; //The memory pool where the above is stored. +Lights1 *sLightBase; // The base value where lights are written to when worked with. +Lights1 sDefaultLights = gdSPDefLights1(0x7F, 0x7F, 0x7F, 0xFE, 0xFE, 0xFE, 0x28, 0x28, 0x28); // Default lights default lights +u16 gNumLights = 0; // How many lights are loaded. +u16 gDynLightStart = 0; // Where the dynamic lights will start. +struct PuppyLight *gPuppyLights[MAX_LIGHTS]; // This contains all the loaded data. +struct MemoryPool *gLightsPool; // The memory pool where the above is stored. -//Runs after an area load, allocates the dynamic light slots. -void puppylights_allocate(void) -{ +// Runs after an area load, allocates the dynamic light slots. +void puppylights_allocate(void) { s32 numAllocate = MIN(MAX_LIGHTS - gNumLights, MAX_LIGHTS_DYNAMIC); s32 i; gDynLightStart = gNumLights; - if (numAllocate <= 0) //If this happens you've allocated too many static lights and therefore cucked dynamic. + if (numAllocate <= 0) { // If this happens you've allocated too many static lights and therefore cucked dynamic. return; - //Now it has the number it wants, it will allocate this many extra lights, intended for dynamic lights. - for (i = 0; i < numAllocate; i++) - { + } + // Now it has the number it wants, it will allocate this many extra lights, intended for dynamic lights. + for (i = 0; i < numAllocate; i++) { gPuppyLights[gNumLights] = mem_pool_alloc(gLightsPool, sizeof(struct PuppyLight)); - if (gPuppyLights[gNumLights] == NULL) + if (gPuppyLights[gNumLights] == NULL) { return; + } gPuppyLights[gNumLights]->active = FALSE; gPuppyLights[gNumLights]->flags = 0; gNumLights++; @@ -72,9 +72,8 @@ void puppylights_allocate(void) extern Mat4 gMatStack[32]; -//Function that iterates through each light. -void puppylights_iterate(struct PuppyLight *light, Lights1 *src, struct Object *obj, s32 flags) -{ +// Function that iterates through each light. +void puppylights_iterate(struct PuppyLight *light, Lights1 *src, struct Object *obj, s32 flags) { Lights1 *tempLight; s32 lightPos[2]; Vec3i lightRelative; @@ -88,77 +87,74 @@ void puppylights_iterate(struct PuppyLight *light, Lights1 *src, struct Object * f64 scaleVal = 1.0f; Vec3f debugPos[2]; - //Relative positions of the object vs. the centre of the node. + // Relative positions of the object vs. the centre of the node. lightRelative[0] = light->pos[0][0] - obj->oPosX; lightRelative[1] = light->pos[0][1] - obj->oPosY; lightRelative[2] = light->pos[0][2] - obj->oPosZ; - //If the nodes X and Z values are equal, then a check is made if the angle is a derivative of 90. - //If so, then it will completely skip over the calculation that figures out position from rotation. - //If it's a cylinder, then it ignores that check, simply because an equal sided cylinder will have the - //same result no matter the yaw. If neither is true, then it simply checks if it's 180 degrees, since - //That will just be the same as 0. - if (light->pos[1][0] == light->pos[1][2]) - { - if (light->yaw % 0x4000 == 0 || light->flags & PUPPYLIGHT_SHAPE_CYLINDER) - { + // If the nodes X and Z values are equal, then a check is made if the angle is a derivative of 90. + // If so, then it will completely skip over the calculation that figures out position from rotation. + // If it's a cylinder, then it ignores that check, simply because an equal sided cylinder will have the + // same result no matter the yaw. If neither is true, then it simply checks if it's 180 degrees, since + // That will just be the same as 0. + if (light->pos[1][0] == light->pos[1][2]) { + if (light->yaw % 0x4000 == 0 || light->flags & PUPPYLIGHT_SHAPE_CYLINDER) { lightPos[0] = lightRelative[0]; lightPos[1] = lightRelative[2]; goto skippingTrig; } - } - else - if (light->yaw % 0x8000 == 0) - { + } else if (light->yaw % 0x8000 == 0) { lightPos[0] = lightRelative[0]; lightPos[1] = lightRelative[2]; goto skippingTrig; } - //Get the position based off the rotation of the box. + // Get the position based off the rotation of the box. lightPos[0] = lightRelative[2] * sins(-light->yaw) + lightRelative[0] * coss(-light->yaw); lightPos[1] = lightRelative[2] * coss(-light->yaw) - lightRelative[0] * sins(-light->yaw); skippingTrig: - #ifdef VISUAL_DEBUG +#ifdef VISUAL_DEBUG vec3f_set(debugPos[0], light->pos[0][0], light->pos[0][1], light->pos[0][2]); vec3f_set(debugPos[1], light->pos[1][0], light->pos[1][1], light->pos[1][2]); debug_box_color(0x08FF00FF); - if (light->flags & PUPPYLIGHT_SHAPE_CYLINDER) + if (light->flags & PUPPYLIGHT_SHAPE_CYLINDER) { debug_box_rot(debugPos[0], debugPos[1], light->yaw, DEBUG_SHAPE_CYLINDER | DEBUG_UCODE_DEFAULT); - else + } else { debug_box_rot(debugPos[0], debugPos[1], light->yaw, DEBUG_SHAPE_BOX | DEBUG_UCODE_DEFAULT); - #endif - //Check if the object is inside the box, after correcting it for rotation. + } +#endif + // Check if the object is inside the box, after correcting it for rotation. if (-light->pos[1][0] < lightPos[0] && lightPos[0] < light->pos[1][0] && -light->pos[1][1] < lightRelative[1] && lightRelative[1] < light->pos[1][1] && - -light->pos[1][2] < lightPos[1] && lightPos[1] < light->pos[1][2]) - { - //If so, then start making preparations to see how alongside they're in. - //This takes the largest side of the box and multiplies the other axis to match the numbers. - //This way, the colour value will scale correctly, no matter which side is entered. - //Because positions are a vector, and Y is up, it means tempID needs to be multiplied - //By 2 in order to reach the X and Z axis. Thanks SM64. - //It will skip scaling the opposite axis if there's no need to. + -light->pos[1][2] < lightPos[1] && lightPos[1] < light->pos[1][2]) { + // If so, then start making preparations to see how alongside they're in. + // This takes the largest side of the box and multiplies the other axis to match the numbers. + // This way, the colour value will scale correctly, no matter which side is entered. + // Because positions are a vector, and Y is up, it means tempID needs to be multiplied + // By 2 in order to reach the X and Z axis. Thanks SM64. + // It will skip scaling the opposite axis if there's no need to. - //Every axis needs to be the same as Z, so X and Y, if necessary, will be scaled to match it. - //This is done, so that when calculating scale, it's done spherically. - if (light->pos[1][0] != light->pos[1][2]) - lightPos[0] /= ((f32)light->pos[1][0]/light->pos[1][2]); - //Same for Y axis. - if (light->pos[1][1] != light->pos[1][2]) - lightRelative[1] /= ((f32)light->pos[1][1]/light->pos[1][2]); - - if (light->flags & PUPPYLIGHT_IGNORE_Y) - scaleOrig = (lightPos[0] * lightPos[0]) + (lightPos[1] * lightPos[1]); - else - scaleOrig = (lightPos[0] * lightPos[0]) + (lightRelative[1] * lightRelative[1]) + (lightPos[1] * lightPos[1]); + // Every axis needs to be the same as Z, so X and Y, if necessary, will be scaled to match it. + // This is done, so that when calculating scale, it's done spherically. + if (light->pos[1][0] != light->pos[1][2]) { + lightPos[0] /= ((f32)light->pos[1][0] / light->pos[1][2]); + } + // Same for Y axis. + if (light->pos[1][1] != light->pos[1][2]) { + lightRelative[1] /= ((f32)light->pos[1][1] / light->pos[1][2]); + } + if (light->flags & PUPPYLIGHT_IGNORE_Y) { + scaleOrig = sqr(lightPos[0]) + sqr(lightPos[1]); + } else { + scaleOrig = sqr(lightPos[0]) + sqr(lightRelative[1]) + sqr(lightPos[1]); + } scaleVal = (light->pos[1][2]*light->pos[1][2]); - //If it's a cylinder, then bin anything outside it. - if (light->flags & PUPPYLIGHT_SHAPE_CYLINDER) - { - if (scaleOrig > scaleVal) + // If it's a cylinder, then bin anything outside it. + if (light->flags & PUPPYLIGHT_SHAPE_CYLINDER) { + if (scaleOrig > scaleVal) { return; + } } } else @@ -167,79 +163,69 @@ void puppylights_iterate(struct PuppyLight *light, Lights1 *src, struct Object * f32 epc = (f32)(light->epicentre/100.0f); tempLight = segmented_to_virtual(src); //Now we have a scale value and a scale factor, we can start lighting things up. - //Convert to a percentage. + // Convert to a percentage. scale = CLAMP(scaleOrig/scaleVal, 0.0f, 1.0f); - //Reduce scale2 by the epicentre. - scale2 = CLAMP((scale - epc) * (1+epc), 0.0f, 1.0f); + // Reduce scale2 by the epicentre. + scale2 = CLAMP((scale - epc) * (1 + epc), 0.0f, 1.0f); - //Get the direction numbers we want by applying some maths to the relative positions. We use 64 because light directions range from -64 to 63. - //Note: can this be optimised further? Simply squaring lightRelative and then dividing it by preScale doesn't work. - if (light->flags & PUPPYLIGHT_DIRECTIONAL) - { + // Get the direction numbers we want by applying some maths to the relative positions. We use 64 because light directions range from -64 to 63. + // Note: can this be optimised further? Simply squaring lightRelative and then dividing it by preScale doesn't work. + if (light->flags & PUPPYLIGHT_DIRECTIONAL) { lightDir[0] = ((lightRelative[0]) * 64.0f) / light->pos[1][0]; lightDir[1] = ((lightRelative[1]) * 64.0f) / light->pos[1][1]; lightDir[2] = ((lightRelative[2]) * 64.0f) / light->pos[1][2]; } //Get direction if applicable. - for (i = 0; i < 3; i++) - { + for (i = 0; i < 3; i++) { //So it works by starting from the final colour, and then lerping to the original colour, by a factor of the epicentre corrected scale. Light opacity affects this further. - colour = approach_f32_asymptotic(light->rgba[i], tempLight->l[0].l.col[i], scale2*((f32)light->rgba[3]/255.0f)); - //If it's a directional light, then increase the current ambient by 50%, to give the effect better. - //Otherwise, just normalise the brightness to keep it in line with the current ambient. - //And now to apply the values. + colour = approach_f32_asymptotic(light->rgba[i], tempLight->l[0].l.col[i], scale2 * ((f32)light->rgba[3]/255.0f)); + // If it's a directional light, then increase the current ambient by 50%, to give the effect better. + // Otherwise, just normalise the brightness to keep it in line with the current ambient. + // And now to apply the values. tempLight->l[0].l.col[i] = colour; tempLight->l[0].l.colc[i] = colour; - //Ambient, too. - if (!(light->flags & PUPPYLIGHT_DIRECTIONAL)) - { - ambient = approach_f32_asymptotic(light->rgba[i]/2, tempLight->a.l.col[i], scale*((f32)light->rgba[3]/255.0f)); + // Ambient, too. + if (!(light->flags & PUPPYLIGHT_DIRECTIONAL)) { + ambient = approach_f32_asymptotic(light->rgba[i]/2, tempLight->a.l.col[i], scale*((f32)light->rgba[3] / 255.0f)); tempLight->a.l.col[i] = ambient; tempLight->a.l.colc[i] = ambient; } - //A slightly hacky way to offset the ambient lighting in order to prevent directional lighting from having a noticeable change in ambient brightness. - if (flags & LIGHTFLAG_DIRECTIONAL_OFFSET) - { - ambient = approach_f32_asymptotic(MIN(tempLight->a.l.col[i] * 2, 0xFF), tempLight->a.l.col[i], scale2*((f32)light->rgba[3]/255.0f)); + // A slightly hacky way to offset the ambient lighting in order to prevent directional lighting from having a noticeable change in ambient brightness. + if (flags & LIGHTFLAG_DIRECTIONAL_OFFSET) { + ambient = approach_f32_asymptotic(MIN(tempLight->a.l.col[i] * 2, 0xFF), tempLight->a.l.col[i], scale2*((f32)light->rgba[3] / 255.0f)); tempLight->a.l.col[i] = ambient; tempLight->a.l.colc[i] = ambient; } - //Apply direction. It takes the relative positions, and then multiplies them with the perspective matrix to get a correct direction. - //Index 1 of the first dimension of gMatStack is perspective. Note that if you ever decide to cheat your way into rendering things after the game does :^) - if (light->flags & PUPPYLIGHT_DIRECTIONAL) + // Apply direction. It takes the relative positions, and then multiplies them with the perspective matrix to get a correct direction. + // Index 1 of the first dimension of gMatStack is perspective. Note that if you ever decide to cheat your way into rendering things after the game does :^) + if (light->flags & PUPPYLIGHT_DIRECTIONAL) { tempLight->l->l.dir[i] = approach_f32_asymptotic((s8)(lightDir[0] * gMatStack[1][0][i] + lightDir[1] * gMatStack[1][1][i] + lightDir[2] * gMatStack[1][2][i]), tempLight->l->l.dir[i], scale); + } } } -//Main function. Run this in the object you wish to illuminate, and just give it its light, object pointer and any potential flags if you want to use them. -//If the object has multiple lights, then you run this for each light. -void puppylights_run(Lights1 *src, struct Object *obj, s32 flags, u32 baseColour) -{ +// Main function. Run this in the object you wish to illuminate, and just give it its light, object pointer and any potential flags if you want to use them. +// If the object has multiple lights, then you run this for each light. +void puppylights_run(Lights1 *src, struct Object *obj, s32 flags, u32 baseColour) { s32 i; s32 numlights = 0; s32 offsetPlaced = 0; s32 lightFlags = flags; - if (gCurrLevelNum < LEVEL_BBH) + if (gCurrLevelNum < LEVEL_BBH) { return; - //Checks if there's a hardset colour. Colours are only the first 3 bytes, so you can really put whatever you want in the last. - //If there isn't a colour, then it decides whether to apply the ambient lighting, or the default lighting as the baseline. - //Otherwise, it hardsets a colour to begin with. I don't recommend you use this, simply because it's intended to be used - //As a hacky quick-fix for models coloured by lights. Lightcoloured models don't blend nearly as nicely as ones coloured - //By other means. - if (baseColour < 0x100) - { - if (levelAmbient) - sLightBase = &gLevelLight; - else - sLightBase = &sDefaultLights; } - else - { + // Checks if there's a hardset colour. Colours are only the first 3 bytes, so you can really put whatever you want in the last. + // If there isn't a colour, then it decides whether to apply the ambient lighting, or the default lighting as the baseline. + // Otherwise, it hardsets a colour to begin with. I don't recommend you use this, simply because it's intended to be used + // As a hacky quick-fix for models coloured by lights. Lightcoloured models don't blend nearly as nicely as ones coloured + // By other means. + if (baseColour < 0x100) { + sLightBase = (levelAmbient ? &gLevelLight : &sDefaultLights); + } else { s32 colour; sLightBase = (levelAmbient) ? &gLevelLight : &sDefaultLights; - for (i = 0; i < 3; i++) - { + for (i = 0; i < 3; i++) { colour = (((baseColour >> (24-(i*8)))) & 0xFF); sLightBase->l[0].l.col[i] = colour; sLightBase->l[0].l.colc[i] = colour; @@ -250,10 +236,8 @@ void puppylights_run(Lights1 *src, struct Object *obj, s32 flags, u32 baseColour } memcpy(segmented_to_virtual(src), &sLightBase[0], sizeof(Lights1)); - for (i = 0; i < gNumLights; i++) - { - if (gPuppyLights[i]->rgba[3] > 0 && gPuppyLights[i]->active == TRUE && gPuppyLights[i]->area == gCurrAreaIndex && (gPuppyLights[i]->room == -1 || gPuppyLights[i]->room == gMarioCurrentRoom)) - { + for (i = 0; i < gNumLights; i++) { + if (gPuppyLights[i]->rgba[3] > 0 && gPuppyLights[i]->active == TRUE && gPuppyLights[i]->area == gCurrAreaIndex && (gPuppyLights[i]->room == -1 || gPuppyLights[i]->room == gMarioCurrentRoom)) { if (gPuppyLights[i]->flags & PUPPYLIGHT_DIRECTIONAL && !offsetPlaced) { lightFlags |= LIGHTFLAG_DIRECTIONAL_OFFSET; offsetPlaced = 1; @@ -266,35 +250,33 @@ void puppylights_run(Lights1 *src, struct Object *obj, s32 flags, u32 baseColour } } -//Sets and updates dynamic lights from objects. -//0xFFFF is essentially the null ID. If the display flag is met, it will find and set an ID, otherwise it frees up the spot. -void puppylights_object_emit(struct Object *obj) -{ +// Sets and updates dynamic lights from objects. +// 0xFFFF is essentially the null ID. If the display flag is met, it will find and set an ID, otherwise it frees up the spot. +void puppylights_object_emit(struct Object *obj) { s32 i; - if (gCurrLevelNum < LEVEL_BBH) + if (gCurrLevelNum < LEVEL_BBH) { return; - - if (obj->oFlags & OBJ_FLAG_EMIT_LIGHT) - { + } + if (obj->oFlags & OBJ_FLAG_EMIT_LIGHT) { f64 dist = ((obj->oPosX - gMarioState->pos[0]) * (obj->oPosX - gMarioState->pos[0])) + ((obj->oPosY - gMarioState->pos[1]) * (obj->oPosY - gMarioState->pos[1])) + ((obj->oPosZ - gMarioState->pos[2]) * (obj->oPosZ - gMarioState->pos[2])); f64 lightSize = ((obj->puppylight.pos[1][0]) * (obj->puppylight.pos[1][0])) + ((obj->puppylight.pos[1][1]) * (obj->puppylight.pos[1][1])) + ((obj->puppylight.pos[1][2]) * (obj->puppylight.pos[1][2])); - if (dist > lightSize) - goto deallocate; //That's right. I used a goto. Eat your heart out xkcd. - if (obj->oLightID == 0xFFFF) - { + if (dist > lightSize) { + goto deallocate; // That's right. I used a goto. Eat your heart out xkcd. + } + if (obj->oLightID == 0xFFFF) { s32 fadingExists = FALSE; - if (ABS(gNumLights - gDynLightStart) < MAX_LIGHTS_DYNAMIC) + if (ABS(gNumLights - gDynLightStart) < MAX_LIGHTS_DYNAMIC) { goto deallocate; - for (i = gDynLightStart; i < MIN(gDynLightStart+MAX_LIGHTS_DYNAMIC, MAX_LIGHTS); i++) - { - if (gPuppyLights[i]->active == TRUE) - { - if (gPuppyLights[i]->flags & PUPPYLIGHT_DELETE) + } + for (i = gDynLightStart; i < MIN(gDynLightStart+MAX_LIGHTS_DYNAMIC, MAX_LIGHTS); i++) { + if (gPuppyLights[i]->active == TRUE) { + if (gPuppyLights[i]->flags & PUPPYLIGHT_DELETE) { fadingExists = TRUE; + } continue; } memcpy(gPuppyLights[i], &obj->puppylight, sizeof(struct PuppyLight)); @@ -304,13 +286,12 @@ void puppylights_object_emit(struct Object *obj) obj->oLightID = i; goto updatepos; } - //Go through all the lights again, now this time, ignore the fading light flag and overwrite them. - if (fadingExists) - { - for (i = gDynLightStart; i < MIN(gDynLightStart+MAX_LIGHTS_DYNAMIC, MAX_LIGHTS); i++) - { - if (gPuppyLights[i]->active == TRUE && !(gPuppyLights[i]->flags & PUPPYLIGHT_DELETE)) + // Go through all the lights again, now this time, ignore the fading light flag and overwrite them. + if (fadingExists) { + for (i = gDynLightStart; i < MIN(gDynLightStart+MAX_LIGHTS_DYNAMIC, MAX_LIGHTS); i++) { + if (gPuppyLights[i]->active == TRUE && !(gPuppyLights[i]->flags & PUPPYLIGHT_DELETE)) { continue; + } memcpy(gPuppyLights[i], &obj->puppylight, sizeof(struct PuppyLight)); gPuppyLights[i]->active = TRUE; gPuppyLights[i]->area = gCurrAreaIndex; @@ -320,20 +301,15 @@ void puppylights_object_emit(struct Object *obj) goto updatepos; } } - } - else - { + } else { updatepos: gPuppyLights[obj->oLightID]->pos[0][0] = obj->oPosX; gPuppyLights[obj->oLightID]->pos[0][1] = obj->oPosY; gPuppyLights[obj->oLightID]->pos[0][2] = obj->oPosZ; } - } - else - { + } else { deallocate: - if (obj->oLightID != 0xFFFF) - { + if (obj->oLightID != 0xFFFF) { gPuppyLights[obj->oLightID]->active = FALSE; gPuppyLights[obj->oLightID]->flags = 0; } @@ -341,10 +317,9 @@ void puppylights_object_emit(struct Object *obj) } } -//A bit unorthodox, but anything to avoid having to set up data to pass through in the original function. -//Objects will completely ignore X, Y, Z and active though. -void set_light_properties(struct PuppyLight *light, s32 x, s32 y, s32 z, s32 offsetX, s32 offsetY, s32 offsetZ, s32 yaw, s32 epicentre, s32 colour, s32 flags, s32 room, s32 active) -{ +// A bit unorthodox, but anything to avoid having to set up data to pass through in the original function. +// Objects will completely ignore X, Y, Z and active though. +void set_light_properties(struct PuppyLight *light, s32 x, s32 y, s32 z, s32 offsetX, s32 offsetY, s32 offsetZ, s32 yaw, s32 epicentre, s32 colour, s32 flags, s32 room, s32 active) { light->active = active; light->pos[0][0] = x; light->pos[0][1] = y; @@ -354,7 +329,7 @@ void set_light_properties(struct PuppyLight *light, s32 x, s32 y, s32 z, s32 off light->pos[1][2] = MAX(offsetZ, 10); light->rgba[0] = (colour >> 24) & 0xFF; light->rgba[1] = (colour >> 16) & 0xFF; - light->rgba[2] = (colour >> 8) & 0xFF; + light->rgba[2] = (colour >> 8) & 0xFF; light->rgba[3] = colour & 0xFF; light->yaw = yaw; light->area = gCurrAreaIndex; @@ -365,45 +340,38 @@ void set_light_properties(struct PuppyLight *light, s32 x, s32 y, s32 z, s32 off light->flags |= flags | PUPPYLIGHT_DYNAMIC; } -//You can run these in objects to enable or disable their light properties. -void cur_obj_enable_light(void) -{ +// You can run these in objects to enable or disable their light properties. +void cur_obj_enable_light(void) { gCurrentObject->oFlags |= OBJ_FLAG_EMIT_LIGHT; } -void cur_obj_disable_light(void) -{ +void cur_obj_disable_light(void) { gCurrentObject->oFlags &= ~OBJ_FLAG_EMIT_LIGHT; if (gPuppyLights[gCurrentObject->oLightID] && gCurrentObject->oLightID != 0xFFFF) gPuppyLights[gCurrentObject->oLightID]->flags |= PUPPYLIGHT_DELETE; } -void obj_enable_light(struct Object *obj) -{ +void obj_enable_light(struct Object *obj) { obj->oFlags |= OBJ_FLAG_EMIT_LIGHT; } -void obj_disable_light(struct Object *obj) -{ +void obj_disable_light(struct Object *obj) { obj->oFlags &= ~OBJ_FLAG_EMIT_LIGHT; - if (gPuppyLights[obj->oLightID] && obj->oLightID != 0xFFFF) + if (gPuppyLights[obj->oLightID] && obj->oLightID != 0xFFFF) { gPuppyLights[obj->oLightID]->flags |= PUPPYLIGHT_DELETE; + } } -//This is ran during a standard area update -void delete_lights(void) -{ +// This is ran during a standard area update +void delete_lights(void) { s32 i; - for (i = 0; i < gNumLights; i++) - { - if (gPuppyLights[i]->active == TRUE && gPuppyLights[i]->flags & PUPPYLIGHT_DELETE) - { + for (i = 0; i < gNumLights; i++) { + if (gPuppyLights[i]->active == TRUE && gPuppyLights[i]->flags & PUPPYLIGHT_DELETE) { gPuppyLights[i]->pos[1][0] = approach_f32_asymptotic(gPuppyLights[i]->pos[1][0], 0, 0.15f); gPuppyLights[i]->pos[1][1] = approach_f32_asymptotic(gPuppyLights[i]->pos[1][1], 0, 0.15f); gPuppyLights[i]->pos[1][2] = approach_f32_asymptotic(gPuppyLights[i]->pos[1][2], 0, 0.15f); - if (gPuppyLights[i]->pos[1][0] < 1.0f && gPuppyLights[i]->pos[1][1] < 1.0f && gPuppyLights[i]->pos[1][2] < 1.0f) - { + if (gPuppyLights[i]->pos[1][0] < 1.0f && gPuppyLights[i]->pos[1][1] < 1.0f && gPuppyLights[i]->pos[1][2] < 1.0f) { gPuppyLights[i]->flags &= ~ PUPPYLIGHT_DELETE; gPuppyLights[i]->active = FALSE; } diff --git a/src/game/puppyprint.c b/src/game/puppyprint.c index 5c3d454b..8b88692d 100644 --- a/src/game/puppyprint.c +++ b/src/game/puppyprint.c @@ -47,18 +47,18 @@ a modern game engine's developer's console. #include "debug_box.h" ColorRGBA currEnv; -u8 fDebug = 0; +u8 fDebug = FALSE; #if PUPPYPRINT_DEBUG -s8 benchViewer = 0; +s8 benchViewer = FALSE; u8 benchOption = 0; -s8 logViewer = 0; +s8 logViewer = FALSE; // Profiler values -s8 perfIteration = 0; +s8 perfIteration = 0; s16 benchmarkLoop = 0; s32 benchmarkTimer = 0; s32 benchmarkProgramTimer = 0; -s8 benchmarkType = 0; +s8 benchmarkType = 0; // General OSTime cpuTime = 0; OSTime rspTime = 0; @@ -67,34 +67,41 @@ OSTime ramTime = 0; OSTime loadTime = 0; OSTime gLastOSTime = 0; OSTime rspDelta = 0; -s32 benchMark[NUM_BENCH_ITERATIONS+2]; +s32 benchMark[NUM_BENCH_ITERATIONS + 2]; // CPU -OSTime collisionTime[NUM_PERF_ITERATIONS+1]; -OSTime behaviourTime[NUM_PERF_ITERATIONS+1]; -OSTime scriptTime[NUM_PERF_ITERATIONS+1]; -OSTime graphTime[NUM_PERF_ITERATIONS+1]; -OSTime audioTime[NUM_PERF_ITERATIONS+1]; -OSTime dmaTime[NUM_PERF_ITERATIONS+1]; -OSTime dmaAudioTime[NUM_PERF_ITERATIONS+1]; -OSTime faultTime[NUM_PERF_ITERATIONS+1]; -OSTime taskTime[NUM_PERF_ITERATIONS+1]; -OSTime profilerTime[NUM_PERF_ITERATIONS+1]; -OSTime profilerTime2[NUM_PERF_ITERATIONS+1]; +OSTime collisionTime[NUM_PERF_ITERATIONS + 1]; +OSTime behaviourTime[NUM_PERF_ITERATIONS + 1]; +OSTime scriptTime[NUM_PERF_ITERATIONS + 1]; +OSTime graphTime[NUM_PERF_ITERATIONS + 1]; +OSTime audioTime[NUM_PERF_ITERATIONS + 1]; +OSTime dmaTime[NUM_PERF_ITERATIONS + 1]; +OSTime dmaAudioTime[NUM_PERF_ITERATIONS + 1]; +OSTime faultTime[NUM_PERF_ITERATIONS + 1]; +OSTime taskTime[NUM_PERF_ITERATIONS + 1]; +OSTime profilerTime[NUM_PERF_ITERATIONS + 1]; +OSTime profilerTime2[NUM_PERF_ITERATIONS + 1]; // RSP -OSTime audioTime[NUM_PERF_ITERATIONS+1]; -OSTime rspGenTime[NUM_PERF_ITERATIONS+1]; +OSTime audioTime[NUM_PERF_ITERATIONS + 1]; +OSTime rspGenTime[NUM_PERF_ITERATIONS + 1]; // RDP -OSTime bufferTime[NUM_PERF_ITERATIONS+1]; -OSTime tmemTime[NUM_PERF_ITERATIONS+1]; -OSTime busTime[NUM_PERF_ITERATIONS+1]; +OSTime bufferTime[NUM_PERF_ITERATIONS + 1]; +OSTime tmemTime[NUM_PERF_ITERATIONS + 1]; +OSTime busTime[NUM_PERF_ITERATIONS + 1]; // RAM -s8 ramViewer = 0; -s32 ramsizeSegment[33] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +s8 ramViewer = FALSE; +s32 ramsizeSegment[33] = { 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + 0, 0, 0 }; s32 audioPool[12]; s32 mempool; -// Collision -u8 collisionViewer = 0; -s32 numSurfaces = 0; extern u8 _mainSegmentStart[]; extern u8 _mainSegmentEnd[]; @@ -107,7 +114,7 @@ extern u8 _buffersSegmentBssEnd[]; extern u8 _goddardSegmentStart[]; extern u8 _goddardSegmentEnd[]; -//Here is stored the rom addresses of the global code segments. If you get rid of any, it's best to just write them as NULL. +// Here is stored the rom addresses of the global code segments. If you get rid of any, it's best to just write them as NULL. s32 ramP[5][2] = { {(u32)&_buffersSegmentBssStart, (u32)&_buffersSegmentBssEnd}, {(u32)&_mainSegmentStart, (u32)&_mainSegmentEnd}, @@ -143,7 +150,7 @@ void puppyprint_profiler_finished(void) { benchMark[NUM_BENCH_ITERATIONS] = 0; benchMark[NUM_BENCH_ITERATIONS+1] = 0; benchmarkTimer = 300; - benchViewer = 0; + benchViewer = FALSE; for (i = 0; i < NUM_BENCH_ITERATIONS - 2; i++) { benchMark[NUM_BENCH_ITERATIONS] += benchMark[i]; if (benchMark[i] > benchMark[NUM_BENCH_ITERATIONS + 1]) { @@ -521,17 +528,17 @@ void puppyprint_profiler_process(void) { } if (fDebug) { if (gPlayer1Controller->buttonPressed & D_JPAD) { - benchViewer ^= 1; - ramViewer = 0; - logViewer = 0; + benchViewer ^= TRUE; + ramViewer = FALSE; + logViewer = FALSE; } else if (gPlayer1Controller->buttonPressed & U_JPAD) { - ramViewer ^= 1; - benchViewer = 0; - logViewer = 0; + ramViewer ^= TRUE; + benchViewer = FALSE; + logViewer = FALSE; } else if (gPlayer1Controller->buttonPressed & L_JPAD) { - logViewer ^= 1; - ramViewer = 0; - benchViewer = 0; + logViewer ^= TRUE; + ramViewer = FALSE; + benchViewer = FALSE; } #ifdef VISUAL_DEBUG else if (!benchViewer && !ramViewer && !logViewer) { @@ -551,9 +558,9 @@ void puppyprint_profiler_process(void) { benchmark_custom(); } if (gPlayer1Controller->buttonDown & U_JPAD && gPlayer1Controller->buttonPressed & L_TRIG) { - ramViewer = 0; - benchViewer = 0; - fDebug ^= 1; + ramViewer = FALSE; + benchViewer = FALSE; + fDebug ^= TRUE; } diff --git a/src/game/puppyprint.h b/src/game/puppyprint.h index 19523668..0af3ce0b 100644 --- a/src/game/puppyprint.h +++ b/src/game/puppyprint.h @@ -35,25 +35,25 @@ extern OSTime rdpTime; extern OSTime ramTime; extern OSTime loadTime; extern OSTime rspDelta; -extern s32 benchMark[NUM_BENCH_ITERATIONS+2]; +extern s32 benchMark[NUM_BENCH_ITERATIONS + 2]; -//CPU -extern OSTime collisionTime[NUM_PERF_ITERATIONS+1]; -extern OSTime behaviourTime[NUM_PERF_ITERATIONS+1]; -extern OSTime scriptTime[NUM_PERF_ITERATIONS+1]; -extern OSTime graphTime[NUM_PERF_ITERATIONS+1]; -extern OSTime audioTime[NUM_PERF_ITERATIONS+1]; -extern OSTime dmaTime[NUM_PERF_ITERATIONS+1]; -extern OSTime dmaAudioTime[NUM_PERF_ITERATIONS+1]; -extern OSTime faultTime[NUM_PERF_ITERATIONS+1]; -extern OSTime taskTime[NUM_PERF_ITERATIONS+1]; -extern OSTime profilerTime[NUM_PERF_ITERATIONS+1]; -//RSP -extern OSTime rspGenTime[NUM_PERF_ITERATIONS+1]; -//RDP -extern OSTime bufferTime[NUM_PERF_ITERATIONS+1]; -extern OSTime tmemTime[NUM_PERF_ITERATIONS+1]; -extern OSTime busTime[NUM_PERF_ITERATIONS+1]; +// CPU +extern OSTime collisionTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime behaviourTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime scriptTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime graphTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime audioTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime dmaTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime dmaAudioTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime faultTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime taskTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime profilerTime[NUM_PERF_ITERATIONS + 1]; +// RSP +extern OSTime rspGenTime[NUM_PERF_ITERATIONS + 1]; +// RDP +extern OSTime bufferTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime tmemTime[NUM_PERF_ITERATIONS + 1]; +extern OSTime busTime[NUM_PERF_ITERATIONS + 1]; extern void profiler_update(OSTime *time, OSTime time2); extern void puppyprint_profiler_process(void); @@ -65,8 +65,8 @@ extern void finish_blank_box(void); extern void render_blank_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b, u8 a); extern void print_small_text(s32 x, s32 y, const char *str, s32 align, s32 amount); extern void render_multi_image(Texture *image, s32 x, s32 y, s32 width, s32 height, s32 scaleX, s32 scaleY, s32 mode); -extern s32 get_text_height(const char *str); -extern s32 get_text_width(const char *str); +extern s32 get_text_height(const char *str); +extern s32 get_text_width(const char *str); extern void prepare_blank_box(void); extern void finish_blank_box(void); extern void render_blank_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b, u8 a); diff --git a/textures/segment2/custom_text.i4.png b/textures/segment2/custom_text.i4.png index bb9be8eef3f6b7d694cf6f9b55598737ecf6bd0c..ccecd85f80c1c528ca00be449182fba0ae95a2e6 100644 GIT binary patch delta 12678 zcmcar|GjR4GLz=piK_DTk!3PFg;M@+?AqRPfJcY@vE|<8xVm`flpvAxRl8IshG-cp zJ!szflVM8a|L^~EKJWkk_x!GiZcfALA;G(T=wAtlecWDj_y5+G?J3ju?Z^B-UTr<~*lFAUzx<{)>*DXloVa_xe$PA) z`}4-M(nd-wkH&Lc(t4Sxxpy5Ilz-FwNGXHV4bDLx%t z|MBY2BKwBx^4T?y+fRi*-+b@H`eV_;@r&!@cp7U|mQVk$<-g_Yiid~I|1O<6eg3@+ zZTorm_FN4s=->Xs+{sV>zJ1Nz8$X`O|2h5lZvAWP_D?@f{{8z)EAPJl7q?%(Hh?8bsuH+9ONBqbzTv9Xm94oO zE}QLsYjwX})+VXov_;9&Ino&c`eG&FcO52QWBAVe9tcbo{gJ@;9Q_C9F-Ena}zm=A7pZn>Lf>>-X^X-uNq!Wy4hW zuj=>wFRv~pOqudjN%Y{i-$J&x>^&azFS5<=so7t9;n~j}3`&o#O>EyN$GgDv^B;*vL9b%|1>eG5rt&Ah?YZiD{B+y(dPZXt!H?^E@;+Ek z5p_CJSEO+xpxdrZBDu?QT12w9U8_j2mnEl2^f${HGR3DXr^gg)?`skA*4jBG$b0p= zDhK0hA;r2|uU*@b{`gd{zxlljQRR2PUs(Kd<@30zx48*+$~_jBPAu-Rx>a)7|3mV! zndvi2x21|N+j6K&d+nZ6t5(GS8RYaXb0rE8p-nQ*aU8ka$a`rO!~Z4Z`MzI?NaZ+*v(9le)sZn-2O#nWxD zJBW%ur2U2R4@9ynmrrl=ib=#E5|N5%rRQZT!ZrfIViaGsh`i*B> zuf=UvdoOTij@tDW<*ObNIW~o-7BAl*VDxeIHrYq_wtjTc{#FJi${v9dxk51Mt{3&$p%i(j|PP1gCzezr` zt2kMA+UY4qHHB3R;!eGqX#OcLY@*rxyV);hR()yEb&9jOaUrkH^d5$KvmI-|8qYekc?EX5pfH z7EyPv9(!x`Ep@qj{kN3~=YEIXd!upw(~YyUI=}6b*6i7u|FqJsO!r-%;@epUYAVJR z3UMEc<14@Jig~Nrey@7&k(E!|Z;IZi*1h_AYKU|pPoz+U&h_cKmRC|YKG?Ww@0U|k z&GbXFwf=3sa3w17(+Zu7?;8TWI`+L={b=so_sp3JEnO~?-%tBmy4`A9ebC<-i^H3L z%zdq`b);&}`aaHzpY0OXESD?w1okZH_~X8|_2GK9?LX|0J!m$)(93x4lv{cQOtU?cH-9_D-X~Mzo!yse>=(0X?#=tF_Go=?>GS!> zWhi!^CyeJx;@*8Z`yFN(#vZ7)`l9kqs_V71{*(B}ePZkLr*37));V;IMP~D+dCMH@ z>g`T?J0H5Wf#Z?J{QDEG_Iz^rXV~$wu)xFFZ-vZirtl5%6ZRyB$jNB3J!Q`kdiZf> zt5)NiP0x;O`{H)UZDaPchFJEtjLpR7dbS%WO%FaF>B(~ zIZuVZn=fmgxim=EqyM9LSn$FU1rDz0J2OkSq=kK~UsFCk?8}G4+qMc=ncdG~2;FdV zU)ZeTMVqtxuXX!KSY$tu-sv~(HS4@K`JW3z^voHfKD7OgI+@F56T5ClYj)*Cx8AnS zmENR~Y#Yw+&*%Tv$hLN!!iR`$)3zStV!hn4^SJApm4%;9 z{O00(p0x1J!r4*v3BqN<&p!y>TfemVhThh;`V+enA4NZV_jnGIoBN&C%|dLo^O)_Q zTh3*5`w%`aMw`$5x?n87&%0B5eJ{)uV0hql@rM=T;`fn}hO^2T*K63tvhWtaZLO$? zxz=@3FR{|Xac;&{j!NPA>Pa_6owjn;3NtN&#MbjD+)qBT zz~Hh-esx=WEy^4#M~7dAFrytYfFvgxA@hh5>E7%P)<{mr>? zC&QT><~tNxm&u8FHzw%o(EL;R%d#J1CVVZWFY zYm~S@XD8O^bZ0(DlU?&};~S@yIHj1JM(sUrKlJg+&_#3Cx_sVf}I1nqr2lcMQ6o3KlJR+O$(rr&~oW_o2{* zz$v;hzIx5t4IE5jbEcRuE!VZ&yopDfH9+ZOeOm%Y&*xI6*_(GJoX*&BzO8LxgXQ`e zotX^Ghg{j^9_m~!Z*Dq2Q%h}=!jhV~!Dm|5%$mUV_4#6k(zw-=GPm6R`yuqoEXCJ5 zV>ut?c69$KZ8T1h`X;Z$^l0Kv0Wp5xo5`H(E;JS05LPnmj_Qjlgv$i?ABmDxvpY|&MN0yM>9D)SQ73?pWk9AVPd3gF(ph?>QmUX z9nJ1%SeQCYtvi$R^}244B6uQolGb>J3G=bgMp z^TE>->)O?#9K5uOT8?ZwBrbE>RMcu)QuQsdl7@ohZy93u9QPQ%xYZf8);FkuQ9tCi z$9<2|hz*ytBXr~VyAB9^&^{!y>|lR|k%o60%dw{kt3n?#OkBQ{U*=5d*ZKs#NVPES zW=_T?1Ewile37s+XXV^5Tx2 zX5t~;jq9|RJ>KE9=t7jj*HvB$1zIJ`O->}PXU${2^XReitBN|;tB>Z)I(~ffBs(!zCiWjU+FFm)iv`^^n-=N8^l(9*j)vei$4{D5R;n;AIJ#vK z?@GbzI{Pk6X%t#$WYQXuCdznH?e#+oznX=jr>8dZONy*XeyR82P*HJfVnW6pwv}=1 z(xEAL*UkJCDDlD1D^-yxHR(d+!XQtrhRN+`G}0TVP4bpjm? zViqtq9-Yi`nAv&#h0-6EZgLl=N?!dQ6B@r}!U?hWGTP6Ub5YueZtJ$SoGfbeg?}fs~1WT3vly4%!59*k;_xtGGYh*d-G$CK> z;{5aNf@{8Q2~X92{?p*ogQGGVY#Ta6iiA8iJ)HdHV?tK=qKb)gZH4R(@m5%Hxcy;@ zP&~ieUp3P}v|cfw#?W!zv_1Fln`vzNyTNheZIP5_JC%x;huKnJx`_Pyx1r=}h1|5d zNaK({N3T4*x#`dw`KBqNO&c!=Mk;>0-6ylrX~!$k^~tRhq+)E7AK$a_bfCnqf*q@I$jpO_r>WkpSRqRjjC zhm3?)$2;G=?~zu=lKSx6sXp%0Tqjl8eib@*7Fg@$Y;aoQu45PWW6Hg^;o@9V^B5e3 z0y*FR2(@cn=4G<6RYSLK(wxA;%p<>4!fvppDJHQldRx^Y+Vh_4n}GII>kE954}I^P za6K8UYAEcIDROLK-ntu?C)GFYx@n=`I%!&bk7&o-^*y4YvdL?szwkZl-#Tf<+^oKM z->nY2MYAt!er{PeOGaXGgX@F99;JC}ON*j@+z57mCO`FypZl}J6_?+z-}|!Fc;@`- z%h%7P)V=eKx1Rj-^lT>)-Q!E8F0}Ja5%c*H+o1*KC+etwvy27|TTK>55Z6Xq39f zxSlcYJel`6#Qja=l@CsVfu-N&Kipc$!cu*D;*CCS|Hc2V#%a35Y}Z#kBv{>-6y=l9toTb|EpcbeV1Xfr$=$t z>wR~$eP^_5?K55Cf98dmQqGAzrb-KZSS#IyRz$u0viabCMbU{dR@a+UBZVB>nd1_+ z9uPaP`kr6us_l=8(5n)m$J>i0zBsEjEq^(K%-6c(6MACmzyD`;4*HxBV`IB3Q|r4` zX53YMk)UZ7p7VvP8@?%T@DQ*q*ybnic;@&^?}Bqzw5B_a2os@C?zr(eL38!yyFxEVim>nLp+MshS^9gG~ci{vFHqI28AFEE6 zFKF7)mAl+f^?Q|mP3aVmw({wRxeq3nE-%_KseY_Ls_>3h2EWwi3h$n$t4|fQ z%zG}&a!&YnS=7Y*%dDUMe;#6g*;IK_d`IHGd!-Wp-#L_ZWY0MN=2T;}VOn!L92THMYo=v;+%vg)zB) zx~F^P^x{{HLOqWsFxv>#s}!c?F1Xjy@OApS&g<#juNq$M$y1QCW`EP3bSj$LaQ_(# zdtZyQ^Lv6zr!PCUqHF2$rli1+KIe;UPhQlV_AFT_*mvvO(A6qi-lcxHll)H0exBH` z(^8M?SF3t$bKYQ~c>NWZS+4JgW{1!gJS{7&ZO{-wuyuXW+ z^W(O@k(aIJ4-PjCIVs5Bcq%*W_S!Jh_u*nY-Z=aITph&wsDJA-zsTfEE*Iu*nv}hw zVoA$Y)~gjUd0+kR$g66p9$O{GaJu}X#@mE>of~KQRv330rttm=%g#A<_z&NV+9M(? zzphpJBzFl~%`6b#U(Y01s(8p~{e$P`YjS&QO+@#tzb}$-Mqlu&=>jwE$!-&0bx-OR z;aK|V%-*KZ&GE56bQ4617aWf^Py8mVw<*NP#-zjJHq*Q2b&u^r zm}coc{&{4DovCKcTVH|w(ld-!>L>lEdH&zct!zKfT;c0irKh>{HMwl4ulf`C#Pom5 z>iWa0R;ewC+G``Yu$t{;TDqLi6zb_A8$M`s^6G{+!a+z^yDMe|{`A zIotl|%zphDb7ykco{0IY_(|8VrQ@V=pWB7C!KXHO@a&v!bx!GBK&jG)(^kD=zapO9 z4*eJTI9|KP|2K={vuvJU1-(!CIM!dU=eV+O9e0s^;cb2O1;2J^Z#Um=A*!Y)qwq${ zaN-P^B1VQTi`_+==1t%8q|bQ6Dc0$8UW(j!dc4cnV(X_#r&)?DpEi5>B)qWizLEcU zYp8zp{Mq0By!liW^kRY5&AU%U7dEc@2>U6cIk)q2*mmSMh@ z8TaHi3+INptao_3^kAFRv|lX&F79oXdl$tX%J4{)RczCl`8;2$QMmNN1G&QO>KA7% z*~ymPak;W*qM(^X!7;%FhL^R26(-qU*xj+Gy=Jlb&KGqH$~U~0a($5-^fTdL{n~Kd z?(N!bH$>9qQ#UUCSZ~6-pEE0*>$lv}jhC1%v|mZ=3Fxcdk^cH^XtP6Xs?ga7AM@s| zS};jwZDhLck&VqYiTf??14BWM&>8U)-ut%x3{3bZ z_2o?QJL6~d8ZP@+1|XDlvu;iV>1oz^$EtFGU1B&Fr%Z%f^lX-eRSxzSoL$2 zAKN@armBRhJy~q_V)3j$flHb6*N4i)zPnboHo?s6C9l8y5jnk;0tK9(?|J`Y{bV;` z&rk0!_AB1ntJXKIa(gso?&>2_{S9S%53F3fCE>o$rWKENm7Klv_N3#3aDDmg_PV?C zFV);^$+#=OdD06z`OoT$dR`x{JG9)Z-o##)e_!YV>$b`vmCeZgk{P*TyUQ@_k*#<+J8JN=6qXf7}u!MWqQ<{ zV|!Y?)~xepA&Gle^QpA#npg5e>;wPGq%Z> zid?V(J?%t?94t>ein<|NiG!-Lv7}Q`XL3`z7{>;rsm6wY3x4UzbljXXA9YIeA@@ z|LS#}3&kF&{@~U-uhm{9P@!A4Y7v88$F@5&w{H7$=gNx5w%;>^FBFwVa@a`o$;jT{ zD=w3x!DzEKy}tcu#ObSluC}P{;#;Y~cO~Xf;?=VsnDtuvSMsoZ*88!*qrND4< z9}Z1nvk$uL?7Yg^W*S4<#+=gY>ARQzsGoLV&$;-PH~ZIbJQ%&_@*BA|YPX*?Ej!!) zYxPX6CkqQJcEsFUdLn({%9ma{52mvBf4RG9i&pV0tN(UfAA^pW-tTux(fIbA%h3Mf ziM121r6!fHkN4FUefMtBtYap=pI&_}&#mXba%ED+sbrteKEgZJ8lKhNQ_+)|HH(3* z|FzAw*tQeQB_}k}oW3x)f0xW-UvBfK()7J)==Mu{m%El833mM=_3%dTodyo$qaEjJ z78TwRE0Q;vrxE+q_{qD!`OD4DY!_yf=VR${`|>AS_u1m3E8g1HTIs&vV%Lr3&3Lze zYpR%0{T{D*S#y@Hyesxm;nKL^e>rH!3 z@07B4?HkXods4BEg{g4*Gf(B&yDqMB-#0VpZ=T218k40_iE%Aja=Lut*Mssuy?HWe ziSVuKIx#VgCY$-^R9?}Rcv}=0EXtX?dTnJ^Wpmw%3FiIvGhV&)$^OjPB*iQr^h0jb zv!{OPQu295Coa!oTmDyK>BO0=5`hU-6e^Juk5R@82i zc;Y{WXRn5ScU0dtPmVpO*td#*e(Q2gc;`O0b$N9S(Hy_4FBtv5`1O3vwm%MM>WsmTa9yL%yWGE3 zcE3^CS6=!hz$15k)W2mDTsyPO+a1J~{}uVp#v}Uf|3crqkXDDa*VKc)R2Bd67TvHy z|G}jVUB7OvVBk0~v+^E;UA0eJYRhghGbIksO;FUtmlj0ZQlF|H3kpR`$kuTu4u@cv`%=lM>H z6rSqh`?=xo3C*+aaXY6x+rr9~VC!5z_l=UujRpG4dILXvUURB5_u@Rs+&4zoAIwkP zpx2%Mwz*a5m|eDGvbpFEXAADEw~DvEPcqGSsqW|cd*Jqc6Q;?I)9xQXA>Sfed|Y&1 zzsH5@L%M}Y6K?D@VeMKatisVH#E^TG>!jwPRc5Yd-nuTXU9n#B$9%O1zn<52T#IPj zHm}}{xBa`sBguZp(5)FCB-q}FZt~ywy(LcR+SY4|vuAZ3&zXq_7mrD@$KLAAIk+AzrI>&G`~|ZLTQIr ziBzX1OZ#j2wrh^T4(TWM?%oy2`&aNv*Rz$Ye&xrWtdA%QJTG>5rR_YPw)Q)>?k_p? zK5XVErs-Z~%Y)tYns)8I#OaZ;DfMWPzr0C>;QBeW{*nE1TX;ouPCLX*VNC9`V_&=L zdd6DDw;@bYf6hs1zo{=3tG^Y|!(6o{X>tcA|GjA+O5eIGC&ct_9l<`Wxsnue^+wCg#a-`lt98<*-UYt1udc2m~!HD3InJHK>q%k-_YuVZSos!PyNYP|-?r%N8#!VJ&8M%pv8?;U^|i5`9X#>U^Y34)sfp*PZxE<| zAU;dt={KwY%s-wiW?i7j?9j1juhh}bi<#fEV$=CPZaMw+*b0e-0T0&@6uI`zvwa2IG5Oblaq%{ra{=?xyv;k5A_6$DgZJOmc~FZ1=q}F*Ygx zhW!W8=}HmjBqncrCMdFY!u5@lj3unh~w?Fer9aL!g+1`xUkiOq1pn zo6HG%8^Xaf*MLPQUTy7bm84JFcjBH4JX!N;@vr`A zYm73Lic;P#S3Q4iTf*5+Va5yf9YXsSn)od6IOMg4`MPwc(MF!4PKMVO_uS@u{MTfu z!(1Q0a#hA@HP<`8*>_L6Y&*w#fTQm;cc^2WoaIWnN6$0@Z<=bUlwbc=Gq+J8I`?Ug z<%PH$-XP{DPwcoQqQ47sj zveE1j%dPxVR);b(4o&6cStW0JaYprx;)xuqCZ5#w$So;4X1iqiRQF@XSz-*59~^=U z>Q?c(_3ZV3D!N?nT&=M?I_Xh9$NI{|J*zWz z@AOljb^cBLoIv-t|HI2x7c-eyv71!vof4AyBE7ytY3(mdmjB+phks9yW@rB{WpX3h zZ0#|>V~Ph(T<*RZcPDoN_taMpikHS2gv&0yRCL9xfLESJqN9EW2d8!a>(y&lUt`l& zjL6F8x#KOVk{B?v`<%28M{eqb2^G1Uet9nadHT}K%Cwh1-0D-s_HW}VKEt$8(C*yk z=jlHf*k3LFUVEs$&rHHb)l1{K#J^EjFNpHyXa%+*#kAZ)-dT`*DPUq75tX<^|_ngDyxk@@NNoQHqWCra6#DC z*bKV_(f5`&7fhPkwCshrP1JF@)bj6hN-WrPuJ9RcnteULRO6tu<5~swqSmAJUWYGe z@uL@>NxZCD)+OWj(%=>Q?H*f_1T|t1?ziXQ)Z~t1(!WoK4X7*V0z${;I0DQ zX0N!f0c+H2R`Y${Yd7!Jj@vV4y+66!%4>(><*&Z($p`wE1;Boj!Z=i}SS`CS+UXaODR(md~jVUb5;~#oEtj=Y0vC{kkqC z=^1-=(81m_N{mugXZ@dZK03JhRmF_86KiwlTGR@@4V9e09CR*jkMo^VC(YPiqPE5FHU}<-u9{>cPcL-|W6T==o&w!Z`3v&hML66PU!p{bz1Ai#T!6p*Zt(_i1%7c=pBxB>ri4ZS{gbtZ`v<0BGDy~`GEoU=|j*!;rvQiW&p zZe_SN0c`re0mPonvC! z!_>(a81L_zrL?KciBB}oRQO2H`6bJKxjYGJNh+P`#@kS^zrFF3W%gMglU0?qw{vz) zt`BQ`d1cAk39~A41oc&K``x6Bh)v8_m3M91eeAMA zc(UpWpKWXU6r8PXCY)MOG%*w>poPGzyqUZc1u)A#hr*;?X{^{>mG z(@WuE+-?3s{OuN}Xy2b)L8=#vHqWY23*GvVRcxi~rVIAri3>Kzh6O+5y<*G3xgyqJ zTg8qyFK@5gc7EUbmKVLJVv08PFuuBQ_?_q#x8U;C+N&j2a89s2uuzUCGqfS8opa`) z$mwnMvm;s$?v+|{wwt+iS+~eE54WhR4}*k*rXH1fnsfW%Z5Erkb6GXza(S=Lt_a_x zmJqY3JW1rmW&gK=D;T(5-d7X}Ri3|Ue#Os|H=nO+VsiXZB6&6WvR(LL_rEhs`a0_8 z)$F&ROoroZnfu>D%rAc3ByvNy`J+wYS_VE#fL| z`u?Ozt$O;!6S3_1C+posR(S{(%FQ*n>bYv}uj77qKA-s;d&KPR!?IJmwGtFBzX))c zl(qQM`bla5vA++S&SRUb^nzDZV}j4Fo$evA-fXK?!hg=%w(Rk8tLbUeMpX1vm>TpgD5o`MMG)ZBBUuZ%=ZT(6+u@bG$ zUDk2Y&IV3PpGeIvIyJxbY2;3yt1@?Y*o3zPJ6rWsC{K?{Uod}bm2dp&j2SYSx5L`a zmiHQpvkC_ty)2$}>&%5+SudkHuX67Cy{>3k#rr3v?VUmk&+QZcskd)Ei~q$BF4Owc ztIk~in)*pk_3hGCuiwiFGFs`}>r;`aPSY1+T=hzc}Y{>gpL5^Sym-d$yOX?BD*BH%l&z4mdzCVm|NM` zmxcP)^I2=|n;je<931;xY~JiU>gIpSY>p||{7$X?xtZze#MDsT&IgO0NLXJz;G?#> z)Vo>ylck|>t;NTOwjqm-pKG}OI+)pX{`6_bYWOCq$#Smxtfbepr&@+{Ra5wtM!WBG z76cw zvkU%TNLc+uDO!ErqZ}=+_p1`8RJT3da-X%%>hzQa3~C*x9xVuKivPXu^}SoZ-~Tf# zl;jrt&;R}W<|?hb%qo_tsfmUb2Bx|uMiyzhCW(obx|YVNhPq~EsVPR5MkYonMi!H+ z_2gC33@y!#lG03d4NOxLbxn+t5_K)jl2dez4J-^&QjAT^jVzNU@6%IPF}5^KNlHsG z&`mT?HP$srHZsw*G`2|8H8e>wOg2q6FtM;So6M=7$Yo|^Xkcz;VPIe|xkUe(vay9} znt_?Qu0gVyfv!nnvZ-#8S&Ff)pajk%4jQYr%|{F}I9Rlw ztPY=SU};>>DqtiTqy3kGfi2Y2#WAGf*4x;(lU^%u_*(Bv`ZVFJ^S6IHX3Kj0wBXp5 z8J=F2Cfppza)f!~^wh|Ar*-o6U+?<&Z~yz<)%#bO{1b}W(pORXM{-H+Ys=Mde&=m_ zzxCxs_h0kp{<-~jTh8roA@z6SuZPwwH#Lv@um5`2z5eMJic0ERj9yfv-QK4DEaOXw zK-BLj>*L0A!m8T8{?oC&^uMFco6AK#K4{(IIAMm&ya_*de(HScb6sk}4CRZ;Ur$(T z*yg%t%{LBOy!w@WRNnU6!gH7J6%7k|^}2BV!rq+QY?(n@;x_5ss#bZOa5Z>xCs$7X^_jmpxA3pMe&pltOAkyA zI+(~@oWXU>5=0A5YDcznTr+#v={M(eioc&3^HD~@f@!RO}{qKJZ#Tyn?oVa(F z`VuhUj-RYC4G%sqkPFuys7JBugw2^uL!qUaXWc?Tu$31&W_x; zw&zR#=X|9`Va_?JN{yR&io@L5i>!6aQU{qpO!}p=sT~v)n)Sf zFr)U=S?`Ml*W6ic|9k7TdfiHqyOSy{4xdQ5Bp|Zp>f(CO`R`A3tv$fG=0Ml-iRm%l zPuDW&Nlfwly|3Hn!n^eANx4lbx=%}|#CaV&rPdnt=W~+w;wj5mIHzn$aaohlVqE&e zQY2~XzV0h6kArJFvip~L&wKCTFCH7N>g#yu?yS~NEc;LW{QARu)4x|U51dF*_;Nxh zX=bJ`v#$G^SC9R2XV(9I^65*gLkZKxJ^y{;_PNGS3~JqGf9RyM?~ZS=4%6x){~c#~ zXWvv1fBkFSl=%)9|5QJYFSE1xwan6G^PJ@=&krvuU)*>#qAb69Z^w-9c3bQ(1s#~a z-{<%8V~ne0=Verv$iB9nxpH4g5HdW^* zZ+o$J&fYVcDK+!ob24tyeqs21&aqv6AH<*fUY?*=7tt8@{$h3dXNC9nuT&h^Y?t=r z7j8W)aW8RYg^*^xGxrt6Ki^->e)2QAQDJTK#q93POCNQ_lJ|alX}mb$)f*$%-|Y)F zOw8`y=jwOfX|j!}TErKrJ6#pGCVt(mRiArj)}MKHL5kOZnJb;#wopnW^0cY1L(!vZ z_3cg#6Hjh65wW~@S*UMK=%!=KG$WiPU$`T1s zTawN3D}8R?(u>t!1HS~WesAe`dbiuJcD6-TKcsiM{d%rDKfZZt@R{mkhH^g_e|)Tc z>5%dB`tN^RCv~x&3ECdKq0GeAu(K=T)YniRtAG1SKUMY|_!Z`0Z5U%c<8aXl&HTOk zb2H`ptMz{yJemEWICu66M&D_|$G4xGCJ-UPwRCbzFnW4)f?+Fn9qMrU#MajOt?mo5t=hQKC#NeaE6m!) zdsQv|R8`z{QD>&=4bSBiQl=OmOT3n0uaw*?_Fmk{=Ik@C7k@?gT}`&F;w7QpZU5{o8#|`g)m%I*>Chi|I^{&h zvuS$D*YvX^<+eRbiL8IC%u}~?@yn}~k-;&74&utA+X9FB zrt2R&b>8CH#m|MkNyqQ^Or399XHaDKkMWksy{<14Gp!jI7#KWV{an^LB``Jt0Dl^M A@c;k- delta 12778 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