mgs_reversing/source/menu/radiofacedraw.c

474 lines
12 KiB
C

#include "radio.h"
#include <stdlib.h>
#include "common.h"
#include "mts/mts.h"
#include "game/game.h"
#include "game/jimctrl.h"
#include "libdg/libdg.h"
#include "menu/menuman.h"
#include "mts/taskid.h"
#include "sd/g_sound.h"
STATIC RECT rect_800AB6D8 = {1008, 432, 5, 20};
STATIC int dword_800AB6E0 = 0;
menu_chara_struct *dword_800ABB38;
menu_chara_struct *SECTION(".sbss") dword_800ABB38; // force gp
int SECTION(".sbss") dword_800ABB3C;
extern menu_0x14 stru_800BDA48[ 2 ];
void sub_80048124()
{
PANEL_TEXTURE pPanelTex;
RECT rect;
menu_init_rpk_item_8003DDCC(&pPanelTex, 43, 42);
LoadImage(&rect_800AB6D8, pPanelTex.field_0_pixels);
rect = rect_800AB6D8;
rect.w = 16;
rect.h = 1;
rect.y += rect_800AB6D8.h;
LoadImage(&rect, pPanelTex.field_4_word_ptr_pixels);
dword_800ABB3C = (rect.y << 6) | (rect.x >> 4 & 0x3f);
}
void radio_draw_face_frame(MenuPrim *pGlue, int x, int y, int w, int h)
{
POLY_FT4 *polys[8];
int xn, yn;
int xend, yend;
int size;
int i;
POLY_FT4 * pPoly;
int px, py;
POLY_FT4 **ppPoly;
POLY_FT4 *pPoly2;
int x2, y2;
x += 3;
y += 3;
w -= 7;
h -= 7;
if (w < 0)
{
return;
}
// No idea why it copies x and y
xn = x;
yn = y;
xend = xn + w;
yend = yn + h;
size = 9;
for (i = 0; i < 8; i++)
{
_NEW_PRIM(pPoly, pGlue);
LSTORE(0x80808080, &pPoly->r0);
setPolyFT4(pPoly);
setSemiTrans(pPoly, 1);
pPoly->clut = dword_800ABB3C;
pPoly->tpage = getTPage(0, 1, 960, 256);
if (i < 4)
{
px = (i % 2) * 10 - 64;
py = (i / 2) * 10 - 80;
setUVWH(pPoly, px, py, 9, 9);
}
addPrim(pGlue->mPrimBuf.mOt, pPoly);
polys[i] = pPoly;
}
polys[0]->x0 = polys[2]->x0 = xn - size;
polys[1]->x0 = polys[3]->x0 = xend;
polys[0]->y0 = polys[1]->y0 = yn - size;
polys[2]->y0 = polys[3]->y0 = yend;
for (i = 0, ppPoly = polys; i < 4; ppPoly++, i++)
{
pPoly2 = *ppPoly;
x2 = pPoly2->x0;
y2 = pPoly2->y0;
pPoly2->x1 = x2 + size;
pPoly2->y1 = y2;
pPoly2->x2 = x2;
pPoly2->y2 = y2 + size;
pPoly2->x3 = x2 + size;
pPoly2->y3 = y2 + size;
}
setUVWH(polys[4], 201, 176, 1, 9);
setXYWH(polys[4], xn, yn - size, w, 9);
setUVWH(polys[5], 201, 186, 1, 9);
setXYWH(polys[5], xn, yend, w, 9);
setUVWH(polys[6], 192, 185, 9, 1);
setXYWH(polys[6], xn - size, yn, 9, h);
setUVWH(polys[7], 202, 185, 9, 1);
setXYWH(polys[7], xend, yn, 9, h);
}
/**
* @brief Helper function to update the face animation frame for a character.
*
* This function retrieves the next face animation frame for a character and updates
* the character's eye animation frame and left codec portrait frame based on the
* retrieved values. The higher part of the retrieved value indicates whether the
* character should blink, and the lower part indicates the mouth animation frame.
*
* @param a1 Pointer to the character data.
* @param idx unused
*/
void menu_radio_draw_face_helper6_800486A0( menu_chara_struct_sub *a1, int idx )
{
int v1, should_blink, result;
// get the next face animation frame
// the higer part is the blinking flag, the lower part is the mouth
result = jimctrl_helper_80037F68( a1->field_2_chara );
should_blink = result >> 8;
v1 = a1->field_E_eyesAnimFrame == 0;
if ( v1 && should_blink )
{
a1->field_E_eyesAnimFrame = 1;
}
v1 = (char)result;
// mouth animation frame
a1->field_4C_mouthAnimFrame = v1;
}
/**
* @brief Draw the simple animation frame for a character in the codec screen by
* its index.
*
* The simple animation is composed of an eyes image and a mouth image that are
* drawn on top of the character's codec portrait.
* The eyes images to simulate blinking are composed of 6 frames, the mouth images
* are composed of 3 frames.
*
* @param pSub Pointer to the Character Codec struct
* @param idx 0 left character, 1 right character
*/
void menu_radio_draw_face_helper2_800486F4(menu_chara_struct_sub *pSub, int idx)
{
face_anim_image *mouth_image;
face_anim_image *eyes_image;
mouth_image = eyes_image = NULL;
if (pSub->field_0_animState == 1)
{
// if the eyes animation is not idle
if (pSub->field_E_eyesAnimFrame > 0)
{
eyes_image = pSub->field_14_face_anim.simple_anim->field_8_eyes[(pSub->field_E_eyesAnimFrame + 1) / 2 - 1];
pSub->field_E_eyesAnimFrame++;
// Reset the eyes frame to 0 after 6 blinking frames
if (pSub->field_E_eyesAnimFrame > 6)
{
pSub->field_E_eyesAnimFrame = 0;
}
}
// if the mouth animation is not idle
if (pSub->field_4C_mouthAnimFrame > 0)
{
mouth_image = pSub->field_14_face_anim.simple_anim->field_14_mouth[pSub->field_4C_mouthAnimFrame - 1];
}
// Draw the base codec portrait
LoadFaceAnimImage_80046B10(pSub->field_14_face_anim.simple_anim->field_4_face, idx);
if (mouth_image)
{
LoadFaceAnimImage_80046B10(mouth_image, idx);
}
if (eyes_image)
{
LoadFaceAnimImage_80046B10(eyes_image, idx);
}
}
}
void menu_radio_draw_face_helper3_800487DC(menu_chara_struct *pStru, int idx)
{
RECT rect;
menu_radio_load_palette_80046B74(pStru->field_24_pImgData256, idx);
rect.x = idx * 32 + 960;
rect.y = 336;
rect.w = 26;
rect.h = 89;
// TODO: the following code loads
// an image from a random area of the memory.
// It looks like this is a trick to quickly get
// a pseudorandom noise. But, maybe this match is not correct
// and 0x80010000 should not be hardcoded.
LoadImage(&rect, (u_long *)(0x80010000 + (rand() % 32) * 0x2000 + GV_Clock * 0x1000));
}
void menu_radio_draw_face_helper4_80048868(MenuPrim *prim, menu_chara_struct_sub *a2, int idx)
{
LINE_F4 *line_f4;
short coord;
if (a2->field_4 < 5)
{
coord = a2->field_4 * 14;
}
else
{
coord = 54;
}
_NEW_PRIM(line_f4, prim);
LSTORE(0x1a1f13, &line_f4->r0);
if (idx == 0)
{
line_f4->x0 = 83 - coord;
line_f4->x3 = 83 - coord;
line_f4->y0 = 30;
line_f4->y1 = 30;
coord = 78 - coord;
line_f4->x1 = coord;
line_f4->x2 = coord;
line_f4->y2 = 118;
line_f4->y3 = 118;
}
else
{
line_f4->x0 = coord + 236;
line_f4->x3 = coord + 236;
line_f4->y0 = 30;
line_f4->y1 = 30;
coord = coord + 241;
line_f4->x1 = coord;
line_f4->x2 = coord;
line_f4->y2 = 118;
line_f4->y3 = 118;
}
setLineF4(line_f4);
addPrim(prim->mPrimBuf.mOt, line_f4);
}
void menu_radio_draw_face_helper5_8004896C( MenuPrim *pPrim, menu_chara_struct_sub *pChara, int idx )
{
int x, y, w, h;
int color;
DR_TPAGE *pTpage;
POLY_FT4 *pPoly;
POLY_FT4 *pPoly2;
LINE_F2 *pLine;
int shade;
x = (idx == 0) ? 31 : 237;
y = 30;
w = 52;
h = 89;
if ( pChara->field_6 != 0 )
{
if ( ( idx == 0 ) && ( pChara->field_4 == 0 ) && ( pChara->field_6 == 1 ) )
{
GM_SeSet2( 0, 63, SE_RADIO_CONNECT );
}
pChara->field_4 += pChara->field_6;
if ( pChara->field_4 <= 0 )
{
pChara->field_4 = 0;
pChara->field_6 = 0;
}
if ( pChara->field_4 < 5 )
{
return;
}
h = (pChara->field_4 - 4) * 15;
if ( h > 89 )
{
h = 89;
pChara->field_6 = 0;
}
else
{
y += ( 89 - h ) / 2;
}
}
radio_draw_face_frame( pPrim, x, y, w, h );
_NEW_PRIM( pTpage, pPrim );
setDrawTPage( pTpage, 0, 1, getTPage( 0, 0, 960, 256 ) );
addPrim( pPrim->mPrimBuf.mOt, pTpage );
if ( dword_800AB6E0 >= 0 && dword_800AB6E0 < h )
{
_NEW_PRIM( pLine, pPrim );
setXY2(pLine, x, dword_800AB6E0 + y, x + w, dword_800AB6E0 + y);
LSTORE( ( GV_Clock != 0 ) ? 0x80808 : 0x202020, &pLine->r0 );
setLineF2( pLine );
setSemiTrans( pLine, 1 );
addPrim( pPrim->mPrimBuf.mOt, pLine );
}
color = COLOR_GRAY;
if ( pChara->field_A > 0 )
{
_NEW_PRIM( pPoly, pPrim );
shade = ( pChara->field_A << 7 ) / pChara->field_C;
setUVWH( pPoly, idx * 64, 80, 52, 89 );
setXYWH( pPoly, x, y, w, h );
setClut( pPoly, 768, idx + 258 );
setTPage( pPoly, 1, 1, 896, 256 );
LSTORE( shade | ( ( shade << 16 ) | ( shade << 8 ) ), &pPoly->r0 );
setPolyFT4( pPoly );
setSemiTrans( pPoly, 1 );
addPrim( pPrim->mPrimBuf.mOt, pPoly );
shade = 128 - shade;
color = ( shade << 16 ) | ( shade << 8 ) | shade;
}
_NEW_PRIM( pPoly2, pPrim );
setUVWH( pPoly2, idx * 64, 80, 52, 89 );
setXYWH( pPoly2, x, y, w, h );
setClut( pPoly2, 768, idx + 256 );
setTPage( pPoly2, 1, 1, 960, 256 );
LSTORE( color, &pPoly2->r0 );
setPolyFT4( pPoly2 );
setSemiTrans( pPoly2, 1 );
addPrim( pPrim->mPrimBuf.mOt, pPoly2 );
}
static inline void draw_face_anim(menu_chara_struct_sub *a1, int i, menu_chara_struct *chara_struct, MenuPrim *prim)
{
switch ( a1->field_0_animState )
{
case 0:
return;
case 1:
if ( a1->field_A > 0 )
{
a1->field_A--;
}
else
{
if ( stru_800BDA48[ i ].field_2_bTaskWup )
{
mts_wup_tsk( MTSID_CD_READ );
stru_800BDA48[ i ].field_2_bTaskWup = 0;
}
menu_radio_draw_face_helper6_800486A0( a1, i );
menu_radio_draw_face_helper2_800486F4( a1, i );
}
break;
case 2:
if ( a1->field_A > 0 )
{
a1->field_A--;
}
else if ( a1->field_E_eyesAnimFrame < 1 )
{
if ( !sub_80046C90( a1, i, a1->field_14_face_anim.full_anim, a1->field_8_animFrameNum + 1 ) &&
( a1->field_E_eyesAnimFrame = 0x7000, stru_800BDA48[ i ].field_2_bTaskWup != 0 ) )
{
mts_wup_tsk( MTSID_CD_READ );
stru_800BDA48[ i ].field_2_bTaskWup = 0;
}
}
else
{
a1->field_E_eyesAnimFrame--;
}
break;
case 3: // Static noise effect for radio face, used when deepthroat is talking
menu_radio_draw_face_helper3_800487DC( chara_struct, i );
case 4:
default:
break;
}
menu_radio_draw_face_helper5_8004896C( prim, a1, i );
}
void menu_radio_draw_face( MenuWork *work, menu_chara_struct *chara_struct )
{
menu_chara_struct_sub *chara_struct_sub;
MenuPrim *prim;
int i;
if ( chara_struct == NULL )
{
return;
}
if ( !chara_struct->field_1C_radioDatFragment )
{
chara_struct->field_3C[ 0 ].field_0_animState = 0;
chara_struct->field_3C[ 1 ].field_0_animState = 0;
}
prim = work->field_20_otBuf;
dword_800AB6E0 = dword_800AB6E0 + 1;
if ( dword_800AB6E0 > 0x6d )
{
dword_800AB6E0 = 0;
}
for ( i = 0; i < 2; i++ )
{
chara_struct_sub = &chara_struct->field_3C[ i ];
menu_radio_draw_face_helper_800470F4( i );
if ( chara_struct->field_38 != 0 )
{
draw_face_anim(chara_struct_sub, i, chara_struct, prim);
}
menu_radio_draw_face_helper4_80048868( prim, chara_struct_sub, i );
}
}
int menu_radio_end_check(void)
{
int idx;
for (idx = 0; idx < 2; idx++)
{
if ( dword_800ABB38->field_3C[idx].field_4 )
{
return 0;
}
dword_800ABB38->field_3C[idx].field_0_animState = 0;
}
return 1;
}