mirror of
https://github.com/ACreTeam/ac-decomp
synced 2026-05-23 06:34:18 -04:00
7537 lines
200 KiB
C
7537 lines
200 KiB
C
#include "m_npc.h"
|
|
|
|
#include "m_font.h"
|
|
#include "m_name_table.h"
|
|
#include "m_common_data.h"
|
|
#include "m_mail_check.h"
|
|
#include "m_mail_check_ovl.h"
|
|
#include "m_handbill.h"
|
|
#include "m_string.h"
|
|
#include "m_shop.h"
|
|
#include "m_house.h"
|
|
#include "m_event_map_npc.h"
|
|
#include "ac_my_room.h"
|
|
#include "m_mail_password_check.h"
|
|
#include "libultra/libultra.h"
|
|
#include "libjsys/jsyswrapper.h"
|
|
|
|
extern mNpc_Default_Data_c npc_def_list[];
|
|
extern s8 npc_grow_list[];
|
|
extern mNpc_NpcHouseData_c npc_house_list[];
|
|
|
|
typedef struct sp_actor_name_data_s {
|
|
mActor_name_t sp_npc_id;
|
|
u16 sex;
|
|
int name_str_no;
|
|
int sound_id;
|
|
} mNpc_Sp_Npc_Name_c;
|
|
|
|
static mNpc_Sp_Npc_Name_c l_sp_actor_name[] = {
|
|
// Tom Nook
|
|
{ SP_NPC_SHOP_MASTER, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_SHOP_MASTERSP, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_SHOP_MASTERSP_2, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_SHOP_MASTERSP_3, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_SHOP_MASTERSP_4, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_CONV_MASTER, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_SUPER_MASTER, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_DEPART_MASTER, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE2, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE_1, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE2_1, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE_2, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE2_2, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE_3, mPr_SEX_MALE, 0x205, 2 },
|
|
{ SP_NPC_RCN_GUIDE2_3, mPr_SEX_MALE, 0x205, 2 },
|
|
// Redd
|
|
{ SP_NPC_BROKER, mPr_SEX_MALE, 0x206, 2 },
|
|
{ SP_NPC_EV_BROKER2, mPr_SEX_MALE, 0x206, 2 },
|
|
{ SP_NPC_EV_YOMISE, mPr_SEX_MALE, 0x206, 2 },
|
|
{ SP_NPC_EV_YOMISE2, mPr_SEX_MALE, 0x206, 2 },
|
|
// Katrina
|
|
{ SP_NPC_GYPSY, mPr_SEX_FEMALE, 0x207, 2 },
|
|
{ SP_NPC_EV_MIKO, mPr_SEX_FEMALE, 0x207, 2 },
|
|
// Saharah
|
|
{ SP_NPC_CARPETPEDDLER, mPr_SEX_MALE, 0x208, 2 },
|
|
// Wendell
|
|
{ SP_NPC_ARTIST, mPr_SEX_MALE, 0x209, 3 },
|
|
// Jingle
|
|
{ SP_NPC_SANTA, mPr_SEX_MALE, 0x20A, 2 },
|
|
// Gracie
|
|
{ SP_NPC_DESIGNER, mPr_SEX_MALE, 0x20B, 2 },
|
|
// Joan
|
|
{ SP_NPC_KABUPEDDLER, mPr_SEX_FEMALE, 0x20C, 8 },
|
|
// Pelly
|
|
{ SP_NPC_POST_GIRL, mPr_SEX_FEMALE, 0x20D, 4 },
|
|
// Phyllis
|
|
{ SP_NPC_POST_GIRL2, mPr_SEX_FEMALE, 0x20E, 4 },
|
|
// Pete
|
|
{ SP_NPC_POST_MAN, mPr_SEX_MALE, 0x20F, 3 },
|
|
// Copper
|
|
{ SP_NPC_POLICE, mPr_SEX_MALE, 0x210, 2 },
|
|
// Booker
|
|
{ SP_NPC_POLICE2, mPr_SEX_MALE, 0x6D9, 3 },
|
|
// Copper
|
|
{ SP_NPC_EV_TAISOU_0, mPr_SEX_MALE, 0x210, 2 },
|
|
// Porter
|
|
{ SP_NPC_STATION_MASTER, mPr_SEX_MALE, 0x211, 2 },
|
|
// Jack
|
|
{ SP_NPC_HALLOWEEN, mPr_SEX_MALE, 0x212, 3 },
|
|
// Gyroid
|
|
{ ACTOR_PROP_HANIWA0, mPr_SEX_OTHER, 0x213, 2 },
|
|
{ ACTOR_PROP_HANIWA1, mPr_SEX_OTHER, 0x213, 2 },
|
|
{ ACTOR_PROP_HANIWA2, mPr_SEX_OTHER, 0x213, 2 },
|
|
{ ACTOR_PROP_HANIWA3, mPr_SEX_OTHER, 0x213, 2 },
|
|
// K.K. Slider
|
|
{ SP_NPC_P_SEL, mPr_SEX_OTHER, 0x214, 2 },
|
|
{ SP_NPC_TOTAKEKE, mPr_SEX_OTHER, 0x214, 2 },
|
|
{ SP_NPC_RTC, mPr_SEX_OTHER, 0x214, 2 },
|
|
// Rover
|
|
{ SP_NPC_GUIDE, mPr_SEX_MALE, 0x215, 2 },
|
|
{ SP_NPC_GUIDE2, mPr_SEX_MALE, 0x215, 2 },
|
|
// Chip
|
|
{ SP_NPC_ANGLER, mPr_SEX_MALE, 0x216, 2 },
|
|
// Timmy
|
|
{ SP_NPC_MAMEDANUKI0, mPr_SEX_MALE, 0x217, 2 },
|
|
// Tommy
|
|
{ SP_NPC_MAMEDANUKI1, mPr_SEX_MALE, 0x218, 2 },
|
|
// Resetti
|
|
{ SP_NPC_MAJIN, mPr_SEX_MALE, 0x4E0, 2 },
|
|
{ SP_NPC_MAJIN2, mPr_SEX_MALE, 0x4E0, 2 },
|
|
{ SP_NPC_MAJIN3, mPr_SEX_MALE, 0x4E0, 2 },
|
|
{ SP_NPC_MAJIN4, mPr_SEX_MALE, 0x4E0, 2 },
|
|
{ SP_NPC_MAJIN5, mPr_SEX_MALE, 0x4E0, 2 },
|
|
{ SP_NPC_MAJIN_D07C, mPr_SEX_MALE, 0x4E0, 2 },
|
|
{ SP_NPC_MAJIN_D07D, mPr_SEX_MALE, 0x4E0, 2 },
|
|
// Don
|
|
{ SP_NPC_MAJIN_BROTHER, mPr_SEX_MALE, 0x6D8, 2 },
|
|
// Resetti
|
|
{ SP_NPC_MAJIN_D080, mPr_SEX_MALE, 0x4E0, 2 },
|
|
{ SP_NPC_EV_MAJIN, mPr_SEX_MALE, 0x4E0, 2 },
|
|
// Gulliver
|
|
{ SP_NPC_EV_DOZAEMON, mPr_SEX_MALE, 0x4E1, 2 },
|
|
// Snowman
|
|
{ ETC_SNOWMAN_BALL_A, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ ETC_SNOWMAN_BALL_B, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN0, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN1, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN2, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN3, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN4, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN5, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN6, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN7, mPr_SEX_MALE, 0x4E2, 2 },
|
|
{ SNOWMAN8, mPr_SEX_MALE, 0x4E2, 2 },
|
|
// Tortimer
|
|
{ SP_NPC_EV_SONCHO, mPr_SEX_MALE, 0x4E3, 2 },
|
|
{ SP_NPC_EV_SONCHO2, mPr_SEX_MALE, 0x4E3, 2 },
|
|
{ SP_NPC_SONCHO_D078, mPr_SEX_MALE, 0x4E3, 2 },
|
|
{ SP_NPC_SONCHO_D079, mPr_SEX_MALE, 0x4E3, 2 },
|
|
{ SP_NPC_SONCHO, mPr_SEX_MALE, 0x4E3, 2 },
|
|
{ SP_NPC_EV_SPEECH_SONCHO, mPr_SEX_MALE, 0x4E3, 2 },
|
|
// Blathers
|
|
{ SP_NPC_CURATOR, mPr_SEX_MALE, 0x4E4, 2 },
|
|
// Mabel
|
|
{ SP_NPC_NEEDLEWORK0, mPr_SEX_FEMALE, 0x4E5, 4 },
|
|
// Sabel
|
|
{ SP_NPC_NEEDLEWORK1, mPr_SEX_FEMALE, 0x4E6, 4 },
|
|
// Kapp'n
|
|
{ SP_NPC_SENDO, mPr_SEX_MALE, 0x4E7, 2 },
|
|
// Wisp
|
|
{ SP_NPC_EV_GHOST, mPr_SEX_MALE, 0x4E8, 2 },
|
|
// Blanca
|
|
{ SP_NPC_MASK_CAT, mPr_SEX_MALE, 0x4E9, 4 },
|
|
{ SP_NPC_MASK_CAT2, mPr_SEX_MALE, 0x4E9, 4 },
|
|
// Franklin
|
|
{ SP_NPC_TURKEY, mPr_SEX_MALE, 0x6DA, 2 },
|
|
// Farley
|
|
{ SP_NPC_HEM, mPr_SEX_MALE, 0x6DB, 9 }
|
|
};
|
|
|
|
static u8 l_no_name_npc_name[ANIMAL_NAME_LEN] = { 0xD4, 0x8E, 0xA6, 0x90, 0x85, 0x42 }; // never translated
|
|
static u8 l_no_ending_npc_ending[ANIMAL_CATCHPHRASE_LEN] = { 0xD3, 0xAF, 0x9D, 0x20 }; // never translated
|
|
|
|
static void mNpc_MakeRandTable(int* table, int count, int swap_num) {
|
|
int a;
|
|
int b;
|
|
int i;
|
|
f32 c = count;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
table[i] = i;
|
|
}
|
|
|
|
while (swap_num--) {
|
|
int temp;
|
|
|
|
a = RANDOM_F(c);
|
|
b = RANDOM_F(c);
|
|
temp = table[a];
|
|
|
|
table[a] = table[b];
|
|
table[b] = temp;
|
|
}
|
|
}
|
|
|
|
static void mNpc_ClearBufSpace1(u8* buf, int size) {
|
|
mem_clear(buf, size, CHAR_SPACE);
|
|
}
|
|
|
|
extern void mNpc_AddNowNpcMax(u8* npc_max) {
|
|
if (npc_max[0] < ANIMAL_NUM_MAX) {
|
|
npc_max[0]++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SubNowNpcMax(u8* npc_max) {
|
|
if (npc_max[0] > ANIMAL_NUM_MIN) {
|
|
npc_max[0]--;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_ClearAnimalPersonalID(AnmPersonalID_c* pid) {
|
|
pid->npc_id = EMPTY_NO;
|
|
pid->name_id = 0xFF;
|
|
pid->land_id = 0xFFFF;
|
|
pid->looks = mNpc_LOOKS_UNSET;
|
|
mNPS_reset_schedule_area(pid);
|
|
mLd_ClearLandName(pid->land_name);
|
|
}
|
|
|
|
extern int mNpc_CheckFreeAnimalPersonalID(AnmPersonalID_c* pid) {
|
|
int res = FALSE;
|
|
|
|
if ((pid->npc_id == EMPTY_NO && pid->name_id == 0xFF) || (ITEM_NAME_GET_TYPE(pid->npc_id) != NAME_TYPE_NPC)) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_CopyAnimalPersonalID(AnmPersonalID_c* dst, AnmPersonalID_c* src) {
|
|
if (src != NULL && ITEM_NAME_GET_TYPE(src->npc_id) == NAME_TYPE_NPC) {
|
|
dst->npc_id = src->npc_id;
|
|
dst->name_id = src->name_id;
|
|
dst->land_id = src->land_id;
|
|
dst->looks = src->looks;
|
|
mLd_CopyLandName(dst->land_name, src->land_name);
|
|
}
|
|
}
|
|
|
|
extern int mNpc_CheckCmpAnimalPersonalID(AnmPersonalID_c* pid0, AnmPersonalID_c* pid1) {
|
|
int res = FALSE;
|
|
|
|
if (
|
|
(pid0 != NULL && pid1 != NULL) &&
|
|
(pid0->npc_id == pid1->npc_id) &&
|
|
(pid0->name_id == pid1->name_id) &&
|
|
(pid0->land_id == pid1->land_id) &&
|
|
mLd_CheckCmpLandName(pid0->land_name, pid1->land_name) == TRUE
|
|
) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetAnimalNum() {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int num = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) {
|
|
num++;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
extern int mNpc_CheckRemoveExp(Animal_c* animal) {
|
|
return (animal->remove_info >> 13) & 0b111;
|
|
}
|
|
|
|
extern int mNpc_GetRemoveTime(Animal_c* animal) {
|
|
return animal->remove_info & 0x1FFF;
|
|
}
|
|
|
|
/* @unused */
|
|
extern void mNpc_AddRemoveTime(Animal_c* animal) {
|
|
if ((animal->remove_info & 0x1FFF) < 0x1FFF) {
|
|
animal->remove_info++;
|
|
}
|
|
}
|
|
|
|
/* @unused */
|
|
#ifdef MUST_MATCH
|
|
extern void mNpc_SetRemoveExp(Animal_c* animal, u16 remove_exp) {
|
|
animal->remove_info = ((((animal->remove_info >> 13) & 0b111) >> 13) | remove_exp) << 13; // what the... "official" impl from DnM
|
|
}
|
|
#else
|
|
extern void mNpc_SetRemoveExp(Animal_c* animal, u16 remove_exp) {
|
|
animal->remove_info |= (remove_exp & 0b111) << 13;
|
|
}
|
|
#endif
|
|
|
|
extern void mNpc_SetParentName(Animal_c* animal, PersonalID_c* parent_id) {
|
|
if (parent_id != NULL && mPr_NullCheckPersonalID(parent_id) == FALSE && mPr_NullCheckPlayerName(animal->parent_name) == TRUE) {
|
|
mPr_CopyPlayerName(animal->parent_name, parent_id->player_name);
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetParentNameAllAnimal() {
|
|
Private_c* priv = Common_Get(now_private);
|
|
|
|
if (priv != NULL) {
|
|
PersonalID_c* parent_id = &priv->player_ID;
|
|
Animal_c* animal = Save_Get(animals);
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
mNpc_SetParentName(animal, parent_id);
|
|
animal++;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_ClearAnimalMail(Anmplmail_c* mail) {
|
|
mail->font = 0xFF;
|
|
mail->paper_type = 0;
|
|
mail->present = EMPTY_NO;
|
|
mem_clear(mail->body, sizeof(mail->body), CHAR_SPACE);
|
|
mail->date.year = mTM_rtcTime_ymd_clear_code.year;
|
|
mail->date.month = mTM_rtcTime_ymd_clear_code.month;
|
|
mail->date.day = mTM_rtcTime_ymd_clear_code.day;
|
|
}
|
|
|
|
/* @unused */
|
|
/*
|
|
extern void mNpc_CopyAnimalMail(Anmplmail_c* dst, Anmplmail_c* src) {
|
|
mem_copy((u8*)dst, (u8*)src, sizeof(Anmplmail_c));
|
|
}
|
|
*/
|
|
|
|
extern void mNpc_ClearAnimalMemory(Anmmem_c* memory, int num) {
|
|
for (num; num != 0; num--) {
|
|
if (memory != NULL) {
|
|
mPr_ClearPersonalID(&memory->memory_player_id);
|
|
lbRTC_TimeCopy(&memory->last_speak_time, &mTM_rtcTime_clear_code);
|
|
mLd_ClearLandName(memory->memuni.land.name);
|
|
memory->memuni.land.id = 0;
|
|
memory->saved_town_tune = 0;
|
|
memory->friendship = 0;
|
|
memory->letter_info.exists = FALSE;
|
|
memory->letter_info.cond = mNpc_LETTER_RANK_BAD;
|
|
memory->letter_info.send_reply = FALSE;
|
|
memory->letter_info.has_present_cloth = FALSE;
|
|
mNpc_ClearAnimalMail(&memory->letter);
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
}
|
|
|
|
/* TODO: this probably implies the union is on the memory itself and not a struct inside it */
|
|
extern void mNpc_ClearIslandAnimalMemory(Anmmem_c* memory, int num) {
|
|
for (num; num != 0; num--) {
|
|
if (memory != NULL) {
|
|
mPr_ClearPersonalID(&memory->memory_player_id);
|
|
lbRTC_TimeCopy(&memory->last_speak_time, &mTM_rtcTime_clear_code);
|
|
bzero(&memory->memuni.island, sizeof(memuni_u));
|
|
memory->saved_town_tune = 0;
|
|
memory->friendship = 0;
|
|
memory->letter_info.exists = FALSE;
|
|
memory->letter_info.cond = mNpc_LETTER_RANK_BAD;
|
|
memory->letter_info.send_reply = FALSE;
|
|
memory->letter_info.has_present_cloth = FALSE;
|
|
mNpc_ClearAnimalMail(&memory->letter);
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
}
|
|
|
|
/* @unused */
|
|
/*
|
|
extern void mNpc_CopyAnimalMemory(Anmmem_c* dst, Anmmem_c* src) {
|
|
mem_copy((u8*)dst, (u8*)src, sizeof(Anmmem_c));
|
|
}
|
|
*/
|
|
|
|
extern void mNpc_AddFriendship(Anmmem_c* memory, int amount) {
|
|
/* @BUG - devs checked for memory being NULL *after* deferencing it */
|
|
#ifdef BUGFIXES
|
|
if (memory == NULL) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
int friendship = memory->friendship + amount;
|
|
|
|
#ifndef BUGFIXES
|
|
if (memory == NULL) {
|
|
return; //???
|
|
}
|
|
#endif
|
|
|
|
if (friendship > 127) {
|
|
memory->friendship = 127;
|
|
}
|
|
else if (friendship < 0) {
|
|
memory->friendship = 0;
|
|
}
|
|
else {
|
|
memory->friendship = friendship;
|
|
}
|
|
}
|
|
|
|
extern int mNpc_CheckFreeAnimalMemory(Anmmem_c* memory) {
|
|
int res = FALSE;
|
|
|
|
if (mPr_NullCheckPersonalID(&memory->memory_player_id) == TRUE) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_RenewalAnimalMemory() {
|
|
Private_c* priv;
|
|
Animal_c* animal = Save_Get(animals);
|
|
Anmmem_c* memory;
|
|
int i;
|
|
int j;
|
|
int k;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
memory = animal->memories;
|
|
|
|
for (j = 0; j < ANIMAL_MEMORY_NUM; j++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE && mLd_CheckThisLand(memory->memory_player_id.land_name, memory->memory_player_id.land_id) == TRUE) {
|
|
priv = Save_Get(private);
|
|
|
|
for (k = 0; k < PLAYER_NUM; k++) {
|
|
if (mPr_NullCheckPersonalID(&priv->player_ID) == FALSE && mPr_CheckCmpPersonalID(&priv->player_ID, &memory->memory_player_id) == TRUE) {
|
|
break;
|
|
}
|
|
|
|
priv++;
|
|
}
|
|
|
|
if (k == PLAYER_NUM) {
|
|
mNpc_ClearAnimalMemory(memory, 1); // player was deleted so free the memory
|
|
}
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
/* @unused */
|
|
/*
|
|
extern int mNpc_GetOldAnimalMemoryIdx(Anmmem_c* memory, int num) {
|
|
lbRTC_time_c oldest;
|
|
int res;
|
|
int i;
|
|
|
|
lbRTC_TimeCopy(&oldest, &mTM_rtcTime_clear_code);
|
|
res = 0;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (lbRTC_IsOverTime(&oldest, &memory->last_speak_time) == lbRTC_LESS) {
|
|
res = i;
|
|
lbRTC_TimeCopy(&oldest, &memory->last_speak_time);
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
*/
|
|
|
|
extern int mNpc_GetFreeAnimalMemoryIdx(Anmmem_c* memory, int num) {
|
|
int res = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == TRUE) {
|
|
res = i;
|
|
break;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetOldPlayerAnimalMemoryIdx(Anmmem_c* memory, int num) {
|
|
Private_c* priv;
|
|
int res = -1;
|
|
int i;
|
|
int j;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE && mLd_CheckThisLand(memory->memory_player_id.land_name, memory->memory_player_id.land_id) == TRUE) {
|
|
priv = Save_Get(private);
|
|
|
|
for (j = 0; j < PLAYER_NUM; j++) {
|
|
if (mPr_NullCheckPersonalID(&priv->player_ID) == FALSE && mPr_CheckCmpPersonalID(&priv->player_ID, &memory->memory_player_id) == TRUE) {
|
|
break;
|
|
}
|
|
|
|
priv++;
|
|
}
|
|
|
|
/* memory belongs to a player from this town which was deleted */
|
|
if (j == PLAYER_NUM) {
|
|
res = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_ForceGetFreeAnimalMemoryIdx(Animal_c* animal, Anmmem_c* memory, int num) {
|
|
Anmmem_c* mem_p = memory;
|
|
Anmmem_c* chosen_mem = NULL;
|
|
int free_idx = mNpc_GetFreeAnimalMemoryIdx(memory, num);
|
|
int i;
|
|
|
|
if (free_idx == -1) {
|
|
for (i = 0; i < num; i++) {
|
|
if (mLd_CheckThisLand(memory->memory_player_id.land_name, memory->memory_player_id.land_id) == FALSE) {
|
|
if (chosen_mem != NULL) {
|
|
if (chosen_mem->letter_info.exists == memory->letter_info.exists) {
|
|
if (chosen_mem->friendship > memory->friendship) {
|
|
chosen_mem = memory;
|
|
free_idx = i;
|
|
}
|
|
else if (chosen_mem->friendship == memory->friendship) {
|
|
if (lbRTC_IsOverTime(&chosen_mem->last_speak_time, &memory->last_speak_time) == lbRTC_LESS) {
|
|
chosen_mem = memory;
|
|
free_idx = i;
|
|
}
|
|
}
|
|
}
|
|
else if (memory->letter_info.exists == FALSE) {
|
|
chosen_mem = memory;
|
|
free_idx = i;
|
|
}
|
|
}
|
|
else {
|
|
chosen_mem = memory;
|
|
free_idx = i;
|
|
}
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
}
|
|
else {
|
|
chosen_mem = memory + free_idx;
|
|
}
|
|
|
|
if (free_idx == -1 || chosen_mem == NULL) {
|
|
free_idx = mNpc_GetOldPlayerAnimalMemoryIdx(mem_p, num);
|
|
|
|
if (free_idx == -1) {
|
|
free_idx = 0; // all else failed, so pick the first memory
|
|
}
|
|
|
|
chosen_mem = mem_p + free_idx;
|
|
}
|
|
|
|
if (chosen_mem != NULL) {
|
|
if (mNpc_CheckIslandAnimalID(&animal->id) == FALSE) {
|
|
mNpc_ClearAnimalMemory(chosen_mem, 1);
|
|
}
|
|
else {
|
|
mNpc_ClearIslandAnimalMemory(chosen_mem, 1);
|
|
}
|
|
}
|
|
|
|
return free_idx;
|
|
}
|
|
|
|
static void mNpc_ResetAnimalRelation(int idx) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
u8* this_relations = animal[idx].animal_relations;
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
this_relations[0] = 128;
|
|
this_relations++;
|
|
}
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (i != idx) {
|
|
animal->animal_relations[idx] = 128;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
static void mNpc_SetAnimalMemory_NotSetDay(PersonalID_c* pid, AnmPersonalID_c* anm_id, Anmmem_c* memory) {
|
|
if (memory != NULL) {
|
|
if (mNpc_CheckIslandAnimalID(anm_id) == FALSE) {
|
|
mNpc_ClearAnimalMemory(memory, 1);
|
|
}
|
|
else {
|
|
mNpc_ClearIslandAnimalMemory(memory, 1);
|
|
}
|
|
|
|
mPr_CopyPersonalID(&memory->memory_player_id, pid);
|
|
memory->friendship = 1;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetAnimalMemory(PersonalID_c* pid, AnmPersonalID_c* anm_id, Anmmem_c* memory) {
|
|
lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time);
|
|
|
|
if (memory != NULL) {
|
|
mNpc_SetAnimalMemory_NotSetDay(pid, anm_id, memory);
|
|
lbRTC_TimeCopy(&memory->last_speak_time, rtc_time);
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetAnimalMemoryIdx(PersonalID_c* pid, Anmmem_c* memory, int num) {
|
|
int res = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (mPr_CheckCmpPersonalID(&memory->memory_player_id, pid) == TRUE) {
|
|
res = i;
|
|
break;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_SetAnimalLastTalk(Animal_c* animal) {
|
|
Private_c* priv = Common_Get(now_private);
|
|
Anmmem_c* memory = NULL;
|
|
|
|
if (priv != NULL && animal != NULL) {
|
|
int memory_idx = mNpc_GetAnimalMemoryIdx(&priv->player_ID, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (memory_idx != -1) {
|
|
memory = animal->memories + memory_idx;
|
|
}
|
|
else {
|
|
memory_idx = mNpc_ForceGetFreeAnimalMemoryIdx(animal, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (memory_idx != -1) {
|
|
memory = animal->memories + memory_idx;
|
|
mNpc_SetAnimalMemory(&priv->player_ID, &animal->id, memory);
|
|
}
|
|
}
|
|
|
|
if (memory != NULL) {
|
|
lbRTC_TimeCopy(&memory->last_speak_time, Common_GetPointer(time.rtc_time));
|
|
mLd_CopyLandName(memory->memuni.land.name, Save_Get(land_info).name);
|
|
memory->memuni.land.id = Save_Get(land_info).id;
|
|
memory->saved_town_tune = Save_Get(melody);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetAnimalPersonalID2Memory(AnmPersonalID_c* anm_id) {
|
|
Private_c* priv = Common_Get(now_private);
|
|
|
|
if (priv != NULL) {
|
|
int anm_idx = mNpc_SearchAnimalPersonalID(anm_id);
|
|
|
|
if (anm_idx != -1) {
|
|
Animal_c* animal = Save_Get(animals) + anm_idx;
|
|
int mem_idx = mNpc_GetAnimalMemoryIdx(&priv->player_ID, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (mem_idx == -1) {
|
|
int free_idx = mNpc_ForceGetFreeAnimalMemoryIdx(animal, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (free_idx != -1) {
|
|
mNpc_SetAnimalMemory_NotSetDay(&priv->player_ID, &animal->id, animal->memories + free_idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetHighestFriendshipIdx(Anmmem_c* memory, int num) {
|
|
s8 max = 0;
|
|
int res = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE) {
|
|
if (max <= memory->friendship) {
|
|
max = memory->friendship;
|
|
res = i;
|
|
}
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SelectBestFriend(Anmmem_c** best_friend_mem, Anmmem_c* memory, s8* best_friend_friendship) {
|
|
int res = FALSE;
|
|
Anmmem_c* best_friend;
|
|
|
|
if (memory->friendship > best_friend_friendship[0]) {
|
|
best_friend_friendship[0] = memory->friendship;
|
|
best_friend_mem[0] = memory;
|
|
res = TRUE;
|
|
}
|
|
else if (memory->friendship == best_friend_friendship[0]) {
|
|
best_friend = best_friend_mem[0];
|
|
|
|
if (best_friend != NULL) {
|
|
if (best_friend->letter_info.exists == memory->letter_info.exists) {
|
|
if (lbRTC_IsOverTime(&best_friend->last_speak_time, &memory->last_speak_time) == lbRTC_LESS) {
|
|
best_friend_mem[0] = memory;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
else if (memory->letter_info.exists == TRUE) {
|
|
best_friend_mem[0] = memory;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
best_friend_mem[0] = memory;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetAnimalMemoryBestFriend(Anmmem_c* memory, int num) {
|
|
Anmmem_c* memory_p = memory;
|
|
Anmmem_c* best_friend = NULL;
|
|
s8 best_friendship = 0;
|
|
int res = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE && mNpc_SelectBestFriend(&best_friend, memory, &best_friendship) == TRUE) {
|
|
res = i;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
memory = memory_p;
|
|
if (res != -1) {
|
|
memory = &memory[res];
|
|
|
|
if (memory->friendship < 80) {
|
|
res = -1; // minimum best friend friendship value failed
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_GetAnimalMemoryFriend_Land_Sex(Anmmem_c* memory, int num, int sex) {
|
|
Anmmem_c* best_friend = NULL;
|
|
s8 best_friendship = 0;
|
|
int priv_idx;
|
|
int res = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE) {
|
|
priv_idx = mPr_GetPrivateIdx(&memory->memory_player_id);
|
|
|
|
if (priv_idx != -1 && Save_Get(private[priv_idx]).gender == sex && mNpc_SelectBestFriend(&best_friend, memory, &best_friendship) == TRUE) {
|
|
res = i;
|
|
}
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetAnimalMemoryNum(Anmmem_c* memory, int count) {
|
|
int num = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE) {
|
|
num++;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
extern int mNpc_GetAnimalMemoryLetterNum(Anmmem_c* memory, int count) {
|
|
int num = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE && memory->letter_info.exists == TRUE) {
|
|
num++;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
extern int mNpc_GetAnimalMemoryLandKindNum(Anmmem_c* memory, int count) {
|
|
Anmmem_c* memory2;
|
|
Anmlnd_c* land;
|
|
Anmlnd_c* land2;
|
|
u8 bitfield = 0b11111111;
|
|
int num = 0;
|
|
int i;
|
|
int j;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
land = &memory->memuni.land;
|
|
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE) {
|
|
if (((bitfield >> i) & 1) == 1) {
|
|
memory2 = memory + 1;
|
|
land2 = &memory2->memuni.land;
|
|
|
|
for (j = i + 1; j < count; j++) {
|
|
if (((bitfield >> j) & 1) == 1) {
|
|
if (mLd_CheckCmpLand(land->name, land->id, land2->name, land2->id) == TRUE) {
|
|
bitfield &= ~(1 << j); // duplicate town here, so remove this memory from being checked
|
|
}
|
|
}
|
|
|
|
memory2++;
|
|
}
|
|
}
|
|
|
|
num++;
|
|
}
|
|
else {
|
|
bitfield &= ~(1 << i);
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
extern void mNpc_ClearAnimalInfo(Animal_c* animal) {
|
|
bzero(animal, sizeof(Animal_c));
|
|
mNpc_ClearAnimalPersonalID(&animal->id);
|
|
mNpc_ClearAnimalMemory(animal->memories, ANIMAL_MEMORY_NUM);
|
|
animal->cloth = EMPTY_NO;
|
|
animal->home_info.type_unused = 0;
|
|
animal->home_info.block_x = 0xFF;
|
|
animal->home_info.block_z = 0xFF;
|
|
animal->home_info.ut_x = 0xFF;
|
|
animal->home_info.ut_z = 0xFF;
|
|
mNpc_ClearBufSpace1(animal->catchphrase, ANIMAL_CATCHPHRASE_LEN);
|
|
mQst_ClearContest(&animal->contest_quest);
|
|
mLd_ClearLandName(animal->anmuni.previous_land_name);
|
|
animal->previous_land_id = 0;
|
|
animal->remove_info = 0;
|
|
animal->is_home = TRUE;
|
|
mPr_ClearPlayerName(animal->parent_name);
|
|
}
|
|
|
|
extern void mNpc_ClearIslandAnimalInfo(Animal_c* animal) {
|
|
bzero(animal, sizeof(Animal_c));
|
|
mNpc_ClearAnimalPersonalID(&animal->id);
|
|
mNpc_ClearIslandAnimalMemory(animal->memories, ANIMAL_MEMORY_NUM);
|
|
animal->cloth = EMPTY_NO;
|
|
animal->home_info.type_unused = 0;
|
|
animal->home_info.block_x = 0xFF;
|
|
animal->home_info.block_z = 0xFF;
|
|
animal->home_info.ut_x = 0xFF;
|
|
animal->home_info.ut_z = 0xFF;
|
|
mNpc_ClearBufSpace1(animal->catchphrase, ANIMAL_CATCHPHRASE_LEN);
|
|
mQst_ClearContest(&animal->contest_quest);
|
|
animal->remove_info = 0;
|
|
animal->is_home = TRUE;
|
|
mPr_ClearPlayerName(animal->parent_name);
|
|
}
|
|
|
|
extern void mNpc_ClearAnyAnimalInfo(Animal_c* animal, int count) {
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
mNpc_ClearAnimalInfo(animal);
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
extern int mNpc_CheckFreeAnimalInfo(Animal_c* animal) {
|
|
int res = FALSE;
|
|
|
|
if ((animal->home_info.block_x == 0xFF && animal->home_info.block_z == 0xFF) && ITEM_NAME_GET_TYPE(animal->id.npc_id) != NAME_TYPE_NPC) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetFreeAnimalInfo(Animal_c* animal, int count) {
|
|
int res = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == TRUE) {
|
|
res = i;
|
|
break;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_UseFreeAnimalInfo(Animal_c* animal, int count) {
|
|
int free_idx = mNpc_GetFreeAnimalInfo(animal, count);
|
|
|
|
if (free_idx != -1) {
|
|
mNpc_ClearAnimalInfo(animal + free_idx);
|
|
mNpc_ResetAnimalRelation(free_idx);
|
|
}
|
|
|
|
return free_idx;
|
|
}
|
|
|
|
extern void mNpc_CopyAnimalInfo(Animal_c* dst, Animal_c* src) {
|
|
mem_copy((u8*)dst, (u8*)src, sizeof(Animal_c));
|
|
}
|
|
|
|
extern int mNpc_SearchAnimalinfo(Animal_c* animal, mActor_name_t npc_id, int count) {
|
|
int i;
|
|
|
|
animal = animal + (count - 1);
|
|
for (i = count - 1; i >= 0; i--) {
|
|
if (animal->id.npc_id == npc_id) {
|
|
return i;
|
|
}
|
|
|
|
animal--;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
extern Animal_c* mNpc_GetAnimalInfoP(mActor_name_t npc_id) {
|
|
Animal_c* animal = NULL;
|
|
int idx = mNpc_SearchAnimalinfo(Save_Get(animals), npc_id, ANIMAL_NUM_MAX);
|
|
|
|
if (idx != -1) {
|
|
animal = Save_Get(animals) + idx;
|
|
}
|
|
|
|
return animal;
|
|
}
|
|
|
|
extern int mNpc_SearchAnimalPersonalID(AnmPersonalID_c* anm_pid) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int res = -1;
|
|
int i;
|
|
|
|
if (anm_pid != NULL && !mNpc_CheckFreeAnimalPersonalID(anm_pid)) {
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckCmpAnimalPersonalID(anm_pid, &animal->id) == TRUE) {
|
|
res = i;
|
|
break;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern AnmPersonalID_c* mNpc_GetOtherAnimalPersonalIDOtherBlock(AnmPersonalID_c* pids, int count, int bx, int bz, int check_flag) {
|
|
Animal_c* animal;
|
|
AnmPersonalID_c* other_id;
|
|
int npc_max;
|
|
int homes_in_block;
|
|
int ids = count;
|
|
u16 bitfield;
|
|
int j;
|
|
int i;
|
|
|
|
other_id = NULL;
|
|
npc_max = Save_Get(now_npc_max);
|
|
animal = Save_Get(animals);
|
|
homes_in_block = 0;
|
|
ids = count;
|
|
bitfield = 0xFFFF;
|
|
|
|
if (bx == 0xFF || bz == 0xFF) {
|
|
check_flag = FALSE;
|
|
}
|
|
|
|
if (pids == NULL && count != 0) {
|
|
count = 0;
|
|
}
|
|
|
|
/* clear any uninitialized personal ids */
|
|
for (i = 0; i < count; i++) {
|
|
if (mNpc_CheckFreeAnimalPersonalID(pids + i) == TRUE) {
|
|
ids--;
|
|
bitfield &= ~(1 << i);
|
|
}
|
|
}
|
|
|
|
/* clear any npcs who are in the list and live in the acre */
|
|
if (check_flag == TRUE) {
|
|
for (j = 0; j < ANIMAL_NUM_MAX; j++) {
|
|
if (animal->home_info.block_x == bx && animal->home_info.block_z == bz) {
|
|
homes_in_block++;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (((bitfield >> i) & 1) == 1 && mNpc_CheckCmpAnimalPersonalID(&animal->id, &pids[i]) == TRUE) {
|
|
ids--;
|
|
bitfield &= ~(1 << i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
if (npc_max > (ids + homes_in_block) && count < ANIMAL_NUM_MAX) {
|
|
int valid;
|
|
int t;
|
|
|
|
animal = Save_Get(animals);
|
|
homes_in_block = RANDOM((npc_max - ids) - homes_in_block);
|
|
|
|
for (j = 0; j < ANIMAL_NUM_MAX; j++) {
|
|
i = 0;
|
|
valid = TRUE;
|
|
|
|
if (!mNpc_CheckFreeAnimalPersonalID(&animal->id)) {
|
|
for (npc_max = 0; npc_max < count; npc_max++) {
|
|
if (((bitfield >> npc_max) & 1) == 1 && !mNpc_CheckCmpAnimalPersonalID(&animal->id, &pids[npc_max])) {
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (i != ids) {
|
|
valid = FALSE;
|
|
}
|
|
else if (check_flag == TRUE && animal->home_info.block_x == bx && animal->home_info.block_z == bz) {
|
|
if (homes_in_block > 0) {
|
|
homes_in_block--;
|
|
}
|
|
|
|
valid = FALSE;
|
|
}
|
|
|
|
if (valid == TRUE) {
|
|
if (homes_in_block == 0) {
|
|
other_id = &animal->id;
|
|
break;
|
|
}
|
|
else {
|
|
homes_in_block--;
|
|
}
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
return other_id;
|
|
}
|
|
|
|
extern AnmPersonalID_c* mNpc_GetOtherAnimalPersonalID(AnmPersonalID_c* pids, int count) {
|
|
return mNpc_GetOtherAnimalPersonalIDOtherBlock(pids, count, 0, 0, FALSE);
|
|
}
|
|
|
|
extern void mNpc_SetAnimalThisLand(Animal_c* animal, int count) {
|
|
mLd_land_info_c* land_info = Save_GetPointer(land_info);
|
|
|
|
for (count; count != 0; count--) {
|
|
if (!mNpc_CheckFreeAnimalPersonalID(&animal->id)) {
|
|
animal->id.land_id = land_info->id;
|
|
mLd_CopyLandName(animal->id.land_name, land_info->name);
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetSameLooksNum(u8 looks) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int i;
|
|
int num = 0;
|
|
|
|
if (looks < mNpc_LOOKS_UNSET) {
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (looks == animal->id.looks) {
|
|
num++;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
extern int mNpc_CheckNpcExistBlock(int idx, int check_bx, int check_bz) {
|
|
int bx;
|
|
int bz;
|
|
int res = FALSE;
|
|
|
|
if (idx >= 0 && idx < (ANIMAL_NUM_MAX + 1)) {
|
|
mNpc_NpcList_c* list = Common_GetPointer(npclist[idx]);
|
|
|
|
if (mFI_Wpos2BlockNum(&bx, &bz, list->position) == TRUE && bx == check_bx && bz == check_bz) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_Mail2AnimalMail(Anmplmail_c* animal_mail, Mail_c* mail) {
|
|
mem_copy(animal_mail->body, mail->content.body, MAIL_BODY_LEN);
|
|
mem_copy(animal_mail->header, mail->content.header, MAIL_HEADER_LEN);
|
|
mem_copy(animal_mail->footer, mail->content.footer, MAIL_FOOTER_LEN);
|
|
|
|
animal_mail->header_back_start = mail->content.header_back_start;
|
|
animal_mail->font = mail->content.font;
|
|
animal_mail->present = mail->present;
|
|
animal_mail->paper_type = mail->content.paper_type;
|
|
}
|
|
|
|
extern void mNpc_AnimalMail2Mail(Mail_c* mail, Anmplmail_c* animal_mail, PersonalID_c* pid, AnmPersonalID_c* anm_id) {
|
|
mem_copy(mail->content.body, animal_mail->body, MAIL_BODY_LEN);
|
|
mem_copy(mail->content.header, animal_mail->header, MAIL_HEADER_LEN);
|
|
mem_copy(mail->content.footer, animal_mail->footer, MAIL_FOOTER_LEN);
|
|
|
|
mail->content.header_back_start = animal_mail->header_back_start;
|
|
mail->content.font = animal_mail->font;
|
|
mail->present = animal_mail->present;
|
|
mail->content.paper_type = animal_mail->paper_type;
|
|
|
|
if (pid != NULL) {
|
|
mPr_CopyPersonalID(&mail->header.sender.personalID, pid);
|
|
mail->header.sender.type = mMl_NAME_TYPE_PLAYER;
|
|
}
|
|
|
|
if (anm_id != NULL) {
|
|
mMl_set_mail_name_npcinfo(&mail->header.recipient, anm_id);
|
|
}
|
|
}
|
|
|
|
static int mNpc_CheckMailChar(u8 c) {
|
|
if (
|
|
(c == CHAR_EXCLAMATION) ||
|
|
(c == CHAR_QUOTATION) ||
|
|
(c == CHAR_UNDERSCORE) ||
|
|
(c == CHAR_HYPHEN) ||
|
|
(c == CHAR_SYMBOL_ANNOYED) ||
|
|
((c >= CHAR_PERCENT) && (c <= CHAR_AT_SIGN)) ||
|
|
((c >= CHAR_CONTROL_CODE) && (c <= CHAR_INTERPUNCT))
|
|
) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
extern int mNpc_CheckNormalMail_sub(int* char_num, u8* body) {
|
|
u8 last_char = CHAR_SPACE;
|
|
int run_len = 1;
|
|
int consecutive_chars = FALSE;
|
|
int t = 0;
|
|
int i;
|
|
|
|
char_num[0] = 0;
|
|
|
|
for (i = 0; i < MAIL_BODY_LEN; i++) {
|
|
if (body[0] != CHAR_SPACE) {
|
|
|
|
if (last_char == body[0]) {
|
|
run_len++;
|
|
|
|
if (run_len >= 3) {
|
|
if (mNpc_CheckMailChar(body[0]) == TRUE) {
|
|
if (run_len >= 8) {
|
|
consecutive_chars = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
consecutive_chars = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
run_len = 0;
|
|
last_char = body[0];
|
|
}
|
|
|
|
char_num[0]++;
|
|
}
|
|
|
|
body++;
|
|
t++;
|
|
}
|
|
|
|
for (i = t; i < MAIL_BODY_LEN; i++) {
|
|
if (body[0] != CHAR_SPACE) {
|
|
char_num[0]++;
|
|
}
|
|
|
|
body++;
|
|
}
|
|
|
|
return consecutive_chars;
|
|
}
|
|
|
|
extern u8 mNpc_CheckNormalMail_length(int* len, u8* body) {
|
|
int hit_chars;
|
|
u8 rank = mNpc_LETTER_RANK_NUM;
|
|
int hit_rate = mMC_get_mail_hit_rate(&hit_chars, body, NULL);
|
|
int run_on = mNpc_CheckNormalMail_sub(len, body);
|
|
|
|
if (hit_chars < 3) {
|
|
if (len[0] < 5) {
|
|
rank = mNpc_LETTER_RANK_BAD;
|
|
}
|
|
else if (run_on == TRUE) {
|
|
rank = mNpc_LETTER_RANK_BAD;
|
|
}
|
|
}
|
|
else if (hit_rate >= 30) {
|
|
rank = mNpc_LETTER_RANK_OK;
|
|
}
|
|
else if (run_on == TRUE) {
|
|
rank = mNpc_LETTER_RANK_BAD;
|
|
}
|
|
|
|
return rank;
|
|
}
|
|
|
|
extern u8 mNpc_CheckNormalMail_nes(u8* body) {
|
|
u8 rank = mNpc_LETTER_RANK_NUM;
|
|
int key_hit = mMck_check_key_hit_nes(body);
|
|
|
|
if (key_hit >= 100) {
|
|
rank = mNpc_LETTER_RANK_OK;
|
|
}
|
|
else if (key_hit < 50) {
|
|
rank = mNpc_LETTER_RANK_BAD;
|
|
}
|
|
|
|
return rank;
|
|
}
|
|
|
|
static u8 mNpc_CheckNormalMail(u8* body) {
|
|
return mNpc_CheckNormalMail_nes(body);
|
|
}
|
|
|
|
static int mNpc_SetMailCondThisLand(Anmmem_c* memory, u8* body) {
|
|
u8 letter_rank;
|
|
|
|
memory->letter.date.year = Common_Get(time.rtc_time).year;
|
|
memory->letter.date.month = Common_Get(time.rtc_time).month;
|
|
memory->letter.date.day = Common_Get(time.rtc_time).day;
|
|
|
|
letter_rank = mNpc_CheckNormalMail(body);
|
|
|
|
if (letter_rank < mNpc_LETTER_RANK_NUM) {
|
|
memory->letter_info.cond = letter_rank;
|
|
memory->letter_info.send_reply = TRUE;
|
|
}
|
|
|
|
return letter_rank;
|
|
}
|
|
|
|
static int mNpc_SetMailCondOtherLand(Animal_c* animal, u8* body) {
|
|
u8 letter_rank = mNpc_CheckNormalMail(body);
|
|
|
|
if (letter_rank < mNpc_LETTER_RANK_NUM) {
|
|
Private_c* priv = Common_Get(now_private);
|
|
Anmremail_c* remail = &priv->remail;
|
|
|
|
remail->date.year = Common_Get(time.rtc_time).year;
|
|
remail->date.month = Common_Get(time.rtc_time).month;
|
|
remail->date.day = Common_Get(time.rtc_time).day;
|
|
remail->flags.cond = letter_rank;
|
|
remail->flags.looks = animal->id.looks;
|
|
|
|
mNpc_GetNpcWorldNameAnm(remail->name, &animal->id);
|
|
mLd_CopyLandName(remail->land_name, mLd_GetLandName());
|
|
}
|
|
|
|
return letter_rank;
|
|
}
|
|
|
|
static int mNpc_SetRemailCond(Animal_c* animal, Anmmem_c* memory, u8* body) {
|
|
if (mLd_PlayerManKindCheck() == FALSE) {
|
|
return mNpc_SetMailCondThisLand(memory, body);
|
|
}
|
|
else {
|
|
return mNpc_SetMailCondOtherLand(animal, body);
|
|
}
|
|
}
|
|
|
|
static int mNpc_SetPresentCloth(Animal_c* animal, PersonalID_c* player_id, mActor_name_t cloth) {
|
|
Anmmem_c* mem;
|
|
int res = FALSE;
|
|
|
|
if (
|
|
animal != NULL &&
|
|
player_id != NULL &&
|
|
ITEM_NAME_GET_TYPE(cloth) == NAME_TYPE_ITEM1 &&
|
|
ITEM_NAME_GET_CAT(cloth) == ITEM1_CAT_CLOTH &&
|
|
!mNpc_CheckFreeAnimalPersonalID(&animal->id) &&
|
|
!mPr_NullCheckPersonalID(player_id)
|
|
) {
|
|
int memory_idx = mNpc_GetAnimalMemoryIdx(player_id, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (memory_idx != -1) {
|
|
Anmmem_c* m = &animal->memories[memory_idx];
|
|
|
|
if (m->friendship > 30) {
|
|
Anmmem_c* memory = animal->memories;
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_MEMORY_NUM; i++) {
|
|
if (i == memory_idx) {
|
|
memory->letter_info.has_present_cloth = TRUE;
|
|
}
|
|
else {
|
|
memory->letter_info.has_present_cloth = FALSE;
|
|
}
|
|
|
|
memory->letter_info.wearing_present_cloth = FALSE;
|
|
memory++;
|
|
}
|
|
|
|
animal->present_cloth = cloth;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_SendMailtoNpc(Mail_c* mail) {
|
|
int res = FALSE;
|
|
Animal_c* animal;
|
|
AnmPersonalID_c anm_id;
|
|
int anm_idx;
|
|
int memory_idx;
|
|
Anmmem_c* memory;
|
|
Anmplmail_c* plmail;
|
|
int letter_rank;
|
|
Animal_c* anm;
|
|
|
|
if (mMl_get_npcinfo_from_mail_name(&anm_id, &mail->header.recipient) == TRUE) {
|
|
anm = Save_Get(animals);
|
|
anm_idx = mNpc_SearchAnimalPersonalID(&anm_id);
|
|
|
|
if (anm_idx != -1) {
|
|
animal = anm + anm_idx;
|
|
memory_idx = mNpc_GetAnimalMemoryIdx(&mail->header.sender.personalID, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (memory_idx == -1) {
|
|
memory_idx = mNpc_ForceGetFreeAnimalMemoryIdx(anm, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (memory_idx >= 0) {
|
|
mPr_CopyPersonalID(&animal->memories[memory_idx].memory_player_id, &mail->header.sender.personalID);
|
|
}
|
|
}
|
|
else {
|
|
mNpc_SetPresentCloth(animal, &mail->header.sender.personalID, mail->present);
|
|
}
|
|
|
|
memory = animal->memories + memory_idx;
|
|
plmail = &memory->letter;
|
|
memory->letter_info.exists = TRUE;
|
|
mNpc_ClearAnimalMail(plmail);
|
|
mNpc_Mail2AnimalMail(plmail, mail);
|
|
letter_rank = mNpc_SetRemailCond(animal, memory, mail->content.body);
|
|
|
|
if (mEv_CheckFirstJob() == TRUE) {
|
|
mQst_errand_c* first_job = mQst_GetFirstJobData();
|
|
|
|
if (
|
|
(first_job->base.quest_kind == mQst_ERRAND_FIRSTJOB_SEND_LETTER || first_job->base.quest_kind == mQst_ERRAND_FIRSTJOB_SEND_LETTER2) &&
|
|
first_job->base.progress != 0
|
|
) {
|
|
first_job->base.progress = 3;
|
|
memory->letter_info.send_reply = FALSE;
|
|
}
|
|
}
|
|
else {
|
|
int friendship;
|
|
|
|
if (mLd_PlayerManKindCheck() == FALSE) {
|
|
int occur_idx = mQst_GetOccuredContestIdx(mQst_CONTEST_KIND_LETTER);
|
|
if (occur_idx == anm_idx) {
|
|
mQst_SetReceiveLetter(&animal->contest_quest, &mail->header.sender.personalID, mail->content.body, mail->present);
|
|
memory->letter_info.send_reply = FALSE;
|
|
}
|
|
}
|
|
|
|
friendship = 3;
|
|
|
|
if (letter_rank == mNpc_LETTER_RANK_BAD) {
|
|
friendship = -2;
|
|
}
|
|
|
|
if (mail->present != EMPTY_NO) {
|
|
friendship += 3;
|
|
}
|
|
|
|
mNpc_AddFriendship(memory, friendship);
|
|
}
|
|
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_ClearRemail(Anmremail_c* remail) {
|
|
remail->date = mTM_rtcTime_ymd_clear_code;
|
|
mPr_ClearPlayerName(remail->name);
|
|
mLd_ClearLandName(remail->land_name);
|
|
remail->flags.cond = mNpc_LETTER_RANK_BAD;
|
|
remail->flags.looks = 0x7F;
|
|
}
|
|
|
|
static void mNpc_GetRemailPresent(mActor_name_t* present) {
|
|
static int category_table[2] = { mSP_KIND_FURNITURE, mSP_KIND_CLOTH };
|
|
|
|
mSP_SelectRandomItem_New(NULL, present, 1, NULL, 0, category_table[RANDOM(4) & 1], mSP_LISTTYPE_ABC, FALSE);
|
|
}
|
|
|
|
static int mNpc_GetHandbillz(Mail_c* mail, int super_num, int mail_a_num, int mail_b_num, int mail_c_num, int ps_num) {
|
|
static u8 tmp_super[MAIL_HEADER2_LEN];
|
|
static u8 tmp_ps[MAIL_FOOTER2_LEN];
|
|
|
|
mHandbillz_Info_c handbill_info;
|
|
int res;
|
|
|
|
handbill_info.super_buf_p = tmp_super;
|
|
handbill_info.super_buf_size = MAIL_HEADER2_LEN;
|
|
handbill_info.mail_buf_p = mail->content.body;
|
|
handbill_info.mail_buf_size = MAIL_BODY_LEN;
|
|
handbill_info.ps_buf_p = tmp_ps;
|
|
handbill_info.ps_buf_size = MAIL_FOOTER2_LEN;
|
|
handbill_info.super_no = super_num;
|
|
handbill_info.maila_no = mail_a_num;
|
|
handbill_info.mailb_no = mail_b_num;
|
|
handbill_info.mailc_no = mail_c_num;
|
|
handbill_info.ps_no = ps_num;
|
|
|
|
res = mHandbillz_load(&handbill_info);
|
|
if (res == TRUE) {
|
|
mail->content.header_back_start = handbill_info.header_back_start;
|
|
mem_copy(mail->content.header, tmp_super, MAIL_HEADER_LEN);
|
|
mem_copy(mail->content.footer, tmp_ps, MAIL_FOOTER_LEN);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void mNpc_SetRemailFreeString(PersonalID_c* pid, AnmPersonalID_c* anm_id, Anmremail_c* remail) {
|
|
static int base_str_no[11] = {
|
|
0x314, // food
|
|
0x334, // sports
|
|
0x2F4, // hobby games
|
|
0x6A1, // fish
|
|
0x679, // insects
|
|
0x354, // food tastes (sweet, spicy, ...)
|
|
0x374, // feelings
|
|
0x394, // music genres
|
|
0x3D4, // food satisfaction feelings (delicious, stuffed, satisfying, ...)
|
|
0x3F4, // "good" descriptors (brilliant, splendid, ...)
|
|
0x3B4 // "bad" descriptors (unimpressive, awful, ...)
|
|
};
|
|
|
|
/* number of string entries per type */
|
|
static f32 rand_max_table[11] = {
|
|
32.0f,
|
|
32.0f,
|
|
32.0f,
|
|
40.0f,
|
|
40.0f,
|
|
32.0f,
|
|
32.0f,
|
|
32.0f,
|
|
32.0f,
|
|
32.0f,
|
|
32.0f
|
|
};
|
|
|
|
u8 free_str[16];
|
|
AnmPersonalID_c* other_id;
|
|
int i;
|
|
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
|
|
if (remail == NULL) {
|
|
mNpc_GetNpcWorldNameAnm(free_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, free_str, ANIMAL_NAME_LEN);
|
|
other_id = mNpc_GetOtherAnimalPersonalID(anm_id, 1);
|
|
|
|
if (other_id != NULL) {
|
|
mNpc_GetNpcWorldNameAnm(free_str, other_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR2, free_str, ANIMAL_NAME_LEN);
|
|
}
|
|
}
|
|
else {
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, remail->name, ANIMAL_NAME_LEN); // foreign npc name
|
|
other_id = mNpc_GetOtherAnimalPersonalID(NULL, 0);
|
|
mNpc_GetNpcWorldNameAnm(free_str, other_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR2, free_str, ANIMAL_NAME_LEN); // random player town npc name
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR14, remail->land_name, LAND_NAME_SIZE); // foreign town name
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR15, mLd_GetLandName(), LAND_NAME_SIZE); // player town name
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_COUNT(base_str_no); i++) {
|
|
int rand = RANDOM_F(rand_max_table[i]);
|
|
|
|
mString_Load_StringFromRom(free_str, ARRAY_COUNT(free_str), base_str_no[i] + rand);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR3 + i, free_str, ARRAY_COUNT(free_str));
|
|
}
|
|
}
|
|
|
|
static void mNpc_GetRemailGoodData(Mail_c* mail, PersonalID_c* pid, AnmPersonalID_c* anm_id, Anmremail_c* remail, u8 foreign) {
|
|
static int start_no[mNpc_LOOKS_NUM] = {
|
|
0x020,
|
|
0x040,
|
|
0x000,
|
|
0x060,
|
|
0x080,
|
|
0x0A0
|
|
};
|
|
|
|
static int ohter_start_no[mNpc_LOOKS_NUM] = {
|
|
0x0E0,
|
|
0x100,
|
|
0x0C0,
|
|
0x120,
|
|
0x140,
|
|
0x160
|
|
};
|
|
|
|
static int* start_no_table[2] = {
|
|
start_no,
|
|
ohter_start_no
|
|
};
|
|
|
|
mActor_name_t present = EMPTY_NO;
|
|
int msg_no;
|
|
int* msg_tbl;
|
|
int looks;
|
|
int give_present;
|
|
|
|
if (remail == NULL) {
|
|
looks = anm_id->looks;
|
|
}
|
|
else {
|
|
looks = remail->flags.looks;
|
|
}
|
|
|
|
msg_tbl = start_no_table[foreign];
|
|
|
|
give_present = RANDOM(4) & 1;
|
|
if (give_present == 0) {
|
|
mNpc_GetRemailPresent(&present);
|
|
}
|
|
|
|
mNpc_SetRemailFreeString(pid, anm_id, remail);
|
|
msg_no = msg_tbl[looks];
|
|
mNpc_GetHandbillz(mail, msg_no + RANDOM(32), msg_no + RANDOM(32), msg_no + RANDOM(16) + give_present * 16, msg_no + RANDOM(32), msg_no + RANDOM(32));
|
|
mail->present = present;
|
|
}
|
|
|
|
static void mNpc_GetRemailWrongData(Mail_c* mail, PersonalID_c* pid, AnmPersonalID_c* anm_id, Anmremail_c* remail, u8 foreign) {
|
|
static int mail_no[2] = { 0xC5, 0xD8 };
|
|
static u8 tmp_super[MAIL_HEADER2_LEN];
|
|
static u8 tmp_ps[MAIL_FOOTER2_LEN];
|
|
|
|
int looks;
|
|
int header_back_start;
|
|
int msg_no;
|
|
|
|
if (remail == NULL) {
|
|
looks = anm_id->looks;
|
|
}
|
|
else {
|
|
looks = remail->flags.looks;
|
|
}
|
|
|
|
mNpc_SetRemailFreeString(pid, anm_id, remail);
|
|
msg_no = mail_no[foreign] + looks * 3;
|
|
msg_no += RANDOM(3);
|
|
mHandbill_Load_HandbillFromRom(tmp_super, &header_back_start, tmp_ps, mail->content.body, msg_no);
|
|
mail->content.header_back_start = header_back_start;
|
|
mem_copy(mail->content.header, tmp_super, MAIL_HEADER_LEN);
|
|
mem_copy(mail->content.footer, tmp_ps, MAIL_FOOTER_LEN);
|
|
mail->present = EMPTY_NO;
|
|
}
|
|
|
|
typedef void (*mNPC_GET_REMAIL_PROC)(Mail_c*, PersonalID_c*, AnmPersonalID_c*, Anmremail_c*, u8);
|
|
|
|
static void mNpc_GetRemailData(Mail_c* mail, PersonalID_c* pid, AnmPersonalID_c* anm_id, Anmremail_c* remail, int cond, u8 foreign) {
|
|
static mNPC_GET_REMAIL_PROC get_remail[mNpc_LETTER_RANK_NUM] = {
|
|
&mNpc_GetRemailWrongData,
|
|
&mNpc_GetRemailGoodData
|
|
};
|
|
|
|
mActor_name_t paper;
|
|
int paper_no;
|
|
|
|
(*get_remail[cond])(mail, pid, anm_id, remail, foreign);
|
|
mail->content.font = mMl_FONT_0;
|
|
mail->content.mail_type = mMl_TYPE_MAIL;
|
|
mPr_CopyPersonalID(&mail->header.recipient.personalID, pid);
|
|
mail->header.recipient.type = mMl_NAME_TYPE_PLAYER;
|
|
|
|
if (remail == NULL) {
|
|
mMl_set_mail_name_npcinfo(&mail->header.sender, anm_id);
|
|
}
|
|
else {
|
|
mPr_CopyPlayerName(mail->header.sender.personalID.player_name, remail->name);
|
|
mLd_CopyLandName(mail->header.sender.personalID.land_name, remail->land_name);
|
|
mail->header.sender.type = mMl_NAME_TYPE_NPC;
|
|
}
|
|
|
|
mSP_SelectRandomItem_New(NULL, &paper, 1, NULL, 0, mSP_KIND_PAPER, mSP_LISTTYPE_ABC, FALSE);
|
|
mail->content.paper_type = (paper - ITM_PAPER_START) % PAPER_UNIQUE_NUM;
|
|
}
|
|
|
|
static Mail_c l_npc_mail;
|
|
|
|
static int mNpc_SendRemailPostOffice(PersonalID_c* pid, AnmPersonalID_c* anm_id, Anmremail_c* remail, int cond, u8 foreign) {
|
|
Mail_c* mail = &l_npc_mail;
|
|
int res = FALSE;
|
|
|
|
if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mMl_clear_mail(mail);
|
|
mNpc_GetRemailData(mail, pid, anm_id, remail, cond, foreign);
|
|
res = mPO_receipt_proc(mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_CheckLetterTime(lbRTC_ymd_c* letter_date, lbRTC_time_c* rtc_time) {
|
|
int res = FALSE;
|
|
|
|
if (letter_date->day != 0xFF && (letter_date->year != rtc_time->year || letter_date->month != rtc_time->month || letter_date->day != rtc_time->day)) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// 25%
|
|
|
|
extern void mNpc_Remail() {
|
|
Animal_c* animal = Save_Get(animals);
|
|
Anmremail_c* remail;
|
|
Private_c* priv = Common_Get(now_private);
|
|
Anmmem_c* memory;
|
|
lbRTC_time_c* rtc_time;
|
|
|
|
rtc_time = Common_GetPointer(time.rtc_time);
|
|
|
|
if (mPr_NullCheckPersonalID(&priv->player_ID) == FALSE && mLd_PlayerManKindCheck() == FALSE) {
|
|
int i;
|
|
|
|
/* process replies from town villagers first */
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) {
|
|
int mem_idx = mNpc_GetAnimalMemoryIdx(&priv->player_ID, animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (mem_idx != -1) {
|
|
memory = animal->memories + mem_idx;
|
|
|
|
if (memory->letter_info.send_reply && mNpc_CheckLetterTime(&memory->letter.date, rtc_time) == TRUE) {
|
|
if (mNpc_SendRemailPostOffice(&priv->player_ID, &animal->id, NULL, memory->letter_info.cond, FALSE) != TRUE) {
|
|
break;
|
|
}
|
|
|
|
memory->letter_info.send_reply = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
|
|
/* process info from the last foreign villager (if any) */
|
|
remail = &priv->remail;
|
|
if (remail->flags.looks != 0x7F && mNpc_SendRemailPostOffice(&priv->player_ID, &animal->id, remail, remail->flags.cond, TRUE) == TRUE) {
|
|
mNpc_ClearRemail(remail);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern u8 mNpc_GetPaperType() {
|
|
mActor_name_t paper;
|
|
|
|
mSP_SelectRandomItem_New(NULL, &paper, 1, NULL, 0, mSP_KIND_PAPER, mSP_LISTTYPE_ABC, FALSE);
|
|
return (paper - ITM_PAPER_START) % PAPER_UNIQUE_NUM;
|
|
}
|
|
|
|
static void mNpc_LoadMailDataCommon2(Mail_c* mail, PersonalID_c* pid, AnmPersonalID_c* anm_id, mActor_name_t present, u8 paper_type, int mail_no) {
|
|
u8 super[MAIL_HEADER2_LEN];
|
|
u8 ps[MAIL_FOOTER2_LEN];
|
|
int header_back_start;
|
|
|
|
mMl_clear_mail(mail);
|
|
mHandbill_Load_HandbillFromRom2(super, MAIL_HEADER2_LEN, &header_back_start, ps, MAIL_FOOTER2_LEN, mail->content.body, mail_no);
|
|
mem_copy(mail->content.header, super, MAIL_HEADER_LEN);
|
|
mem_copy(mail->content.footer, ps, MAIL_FOOTER_LEN);
|
|
mail->content.header_back_start = header_back_start;
|
|
mail->content.font = mMl_FONT_0;
|
|
mail->content.mail_type = mMl_TYPE_MAIL;
|
|
mPr_CopyPersonalID(&mail->header.recipient.personalID, pid);
|
|
mail->header.recipient.type = mMl_NAME_TYPE_PLAYER;
|
|
mMl_set_mail_name_npcinfo(&mail->header.sender, anm_id);
|
|
mail->present = present;
|
|
mail->content.paper_type = paper_type;
|
|
}
|
|
|
|
static void mNpc_GetEventPresent(mActor_name_t* present, int type) {
|
|
static int priority_table[mNpc_EVENT_MAIL_FRIEND_NUM] = {
|
|
mSP_LISTTYPE_RARE,
|
|
mSP_LISTTYPE_UNCOMMON,
|
|
mSP_LISTTYPE_COMMON
|
|
};
|
|
|
|
static int category_table[mNpc_EVENT_MAIL_FRIEND_NUM] = {
|
|
mSP_KIND_FURNITURE,
|
|
mSP_KIND_FURNITURE,
|
|
mSP_KIND_CLOTH
|
|
};
|
|
|
|
mSP_SelectRandomItem_New(NULL, present, 1, NULL, 0, category_table[type], priority_table[type], FALSE);
|
|
}
|
|
|
|
static void mNpc_GetEventMail(Mail_c* mail, PersonalID_c* pid, AnmPersonalID_c* anm_id, int type, int looks) {
|
|
static u8 animal_name[ANIMAL_NAME_LEN];
|
|
|
|
mActor_name_t present;
|
|
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(animal_name, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR6, animal_name, ANIMAL_NAME_LEN);
|
|
mNpc_GetEventPresent(&present, type);
|
|
mNpc_LoadMailDataCommon2(mail, pid, anm_id, present, mNpc_GetPaperType(), 0x60 + looks * mNpc_EVENT_MAIL_FRIEND_NUM + type);
|
|
}
|
|
|
|
static int mNpc_SendEventPresentMail(PersonalID_c* pid, int player_no, AnmPersonalID_c* anm_id, int type) {
|
|
Mail_c* mail = &l_npc_mail;
|
|
mHm_hs_c* home;
|
|
int looks = anm_id->looks;
|
|
int res = FALSE;
|
|
|
|
home = Save_GetPointer(homes[mHS_get_arrange_idx(player_no)]);
|
|
|
|
if (mPr_CheckCmpPersonalID(pid, &home->ownerID) == TRUE) {
|
|
int free_idx;
|
|
|
|
mMl_clear_mail(mail);
|
|
free_idx = mMl_chk_mail_free_space(home->mailbox, HOME_MAILBOX_SIZE);
|
|
|
|
if (free_idx != -1) {
|
|
mNpc_GetEventMail(mail, pid, anm_id, type, looks);
|
|
mMl_copy_mail(home->mailbox + free_idx, mail);
|
|
res = TRUE;
|
|
}
|
|
else if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mNpc_GetEventMail(mail, pid, anm_id, type, looks);
|
|
res = mPO_receipt_proc(mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void mNpc_SendEventPresentMailSex(int* selected, u8* type, Animal_c* animal, int animal_sex) {
|
|
int best_friend_idx = mNpc_GetAnimalMemoryBestFriend(animal->memories, ANIMAL_MEMORY_NUM);
|
|
int other_sex_best_friend_idx = mNpc_GetAnimalMemoryFriend_Land_Sex(animal->memories, ANIMAL_MEMORY_NUM, (~animal_sex) & 1);
|
|
|
|
if (best_friend_idx != -1 && other_sex_best_friend_idx == best_friend_idx) {
|
|
selected[0] = other_sex_best_friend_idx;
|
|
type[0] = mNpc_EVENT_MAIL_BEST_FRIEND;
|
|
}
|
|
else if (other_sex_best_friend_idx != -1) {
|
|
selected[0] = other_sex_best_friend_idx;
|
|
|
|
if (animal->memories[other_sex_best_friend_idx].friendship >= 80) {
|
|
type[0] = mNpc_EVENT_MAIL_OK_FRIEND;
|
|
}
|
|
else {
|
|
type[0] = mNpc_EVENT_MAIL_NOT_FRIEND;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* this used to be mNpc_SendEventPresentMail_common in DnM and DnM+ (handled both valentine's day & white day) */
|
|
|
|
extern int mNpc_SendVtdayMail() {
|
|
u8 types[ANIMAL_NUM_MAX];
|
|
int other_sex_best_friends[ANIMAL_NUM_MAX];
|
|
Animal_c* animal_p;
|
|
int sent;
|
|
u8 player_bitfield;
|
|
int mem_idx;
|
|
PersonalID_c* pid;
|
|
int player_no;
|
|
int i;
|
|
int j;
|
|
|
|
animal_p = Save_Get(animals);
|
|
sent = 0;
|
|
player_bitfield = 0b1111;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
other_sex_best_friends[i] = -1;
|
|
|
|
if (mNpc_CheckFreeAnimalPersonalID(&animal_p->id) == FALSE) {
|
|
mNpc_SendEventPresentMailSex(&other_sex_best_friends[i], &types[i], animal_p, mNpc_GetAnimalSex(animal_p));
|
|
}
|
|
|
|
animal_p++;
|
|
}
|
|
|
|
for (i = 0; i < mNpc_EVENT_MAIL_FRIEND_NUM; i++) {
|
|
animal_p = Save_Get(animals);
|
|
|
|
for (j = 0; j < ANIMAL_NUM_MAX; j++) {
|
|
if (other_sex_best_friends[j] >= 0 && types[j] == i) {
|
|
pid = &animal_p->memories[other_sex_best_friends[j]].memory_player_id;
|
|
player_no = mPr_GetPrivateIdx(pid);
|
|
|
|
if (((player_bitfield >> player_no) & 1) == 1) {
|
|
if (mNpc_SendEventPresentMail(pid, player_no, &animal_p->id, i) == TRUE) {
|
|
other_sex_best_friends[j] = -1;
|
|
sent++;
|
|
}
|
|
else {
|
|
player_bitfield &= ~(1 << player_no); // remove player from bitfield because they can't receive letters
|
|
}
|
|
}
|
|
}
|
|
|
|
if (player_bitfield == 0) {
|
|
return sent; // all players cannot receive any further mail
|
|
}
|
|
|
|
animal_p++;
|
|
}
|
|
}
|
|
|
|
return sent;
|
|
}
|
|
|
|
static void mNpc_GetBirthdayPresent(mActor_name_t* present) {
|
|
static u8 category_table[5] = { mSP_KIND_FURNITURE, mSP_KIND_FURNITURE, mSP_KIND_CLOTH, mSP_KIND_CLOTH, 0xFF };
|
|
|
|
int selected = category_table[RANDOM(ARRAY_COUNT(category_table))];
|
|
|
|
if (selected == 0xFF) {
|
|
mSP_RandomUmbSelect(present, 1);
|
|
}
|
|
else {
|
|
mSP_SelectRandomItem_New(NULL, present, 1, NULL, 0, selected, mSP_LISTTYPE_RARE, FALSE);
|
|
}
|
|
}
|
|
|
|
static void mNpc_GetBirthdayCard(Mail_c* mail, PersonalID_c* pid, AnmPersonalID_c* anm_id) {
|
|
static u8 animal_name[ANIMAL_NAME_LEN];
|
|
|
|
u8 item_name[mIN_ITEM_NAME_LEN];
|
|
int mail_no = 0xEA + anm_id->looks * 3 + RANDOM(3);
|
|
mActor_name_t present;
|
|
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(animal_name, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, animal_name, ANIMAL_NAME_LEN);
|
|
mNpc_GetBirthdayPresent(&present);
|
|
mIN_copy_name_str(item_name, present);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR2, item_name, mIN_ITEM_NAME_LEN);
|
|
mNpc_LoadMailDataCommon2(mail, pid, anm_id, present, mNpc_GetPaperType(), mail_no);
|
|
}
|
|
|
|
static int mNpc_SendBirthdayCard(PersonalID_c* pid, int player_no, AnmPersonalID_c* anm_id) {
|
|
Mail_c* mail = &l_npc_mail;
|
|
mHm_hs_c* home;
|
|
int free_idx;
|
|
int res = FALSE;
|
|
|
|
home = Save_GetPointer(homes[mHS_get_arrange_idx(player_no)]);
|
|
|
|
if (mPr_NullCheckPersonalID(pid) == FALSE && mPr_CheckCmpPersonalID(pid, &home->ownerID) == TRUE) {
|
|
mMl_clear_mail(mail);
|
|
free_idx = mMl_chk_mail_free_space(home->mailbox, HOME_MAILBOX_SIZE);
|
|
|
|
if (free_idx != -1) {
|
|
mNpc_GetBirthdayCard(mail, pid, anm_id);
|
|
mMl_copy_mail(home->mailbox + free_idx, mail);
|
|
res = TRUE;
|
|
}
|
|
else if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mNpc_GetBirthdayCard(mail, pid, anm_id);
|
|
res = mPO_receipt_proc(mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckFriendship(PersonalID_c* pid, Animal_c* animal) {
|
|
int best_idx;
|
|
int idx = -1;
|
|
|
|
if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) {
|
|
best_idx = mNpc_GetHighestFriendshipIdx(animal->memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (
|
|
best_idx != -1 &&
|
|
mPr_CheckCmpPersonalID(pid, &animal->memories[best_idx].memory_player_id) &&
|
|
Common_Get(now_private)->birthday_present_npc != animal->id.npc_id
|
|
) {
|
|
idx = best_idx;
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
/* @unused */
|
|
/*
|
|
extern int mNpc_SendEventBirthdayCard(PersonalID_c* pid) {
|
|
return FALSE; // seemingly stubbed
|
|
}
|
|
*/
|
|
|
|
extern int mNpc_SendEventBirthdayCard2(PersonalID_c* pid, int player_no) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int res = FALSE;
|
|
int i;
|
|
|
|
if (pid != NULL && mLd_CheckThisLand(pid->land_name, pid->land_id) == TRUE) {
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFriendship(pid, animal) != -1 && mNpc_SendBirthdayCard(pid, player_no, &animal->id) == TRUE) {
|
|
res = TRUE;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
|
|
if (Common_Get(now_private)->celebrated_birthday_year != Common_Get(time.rtc_time).year) {
|
|
Common_Get(now_private)->birthday_present_npc = EMPTY_NO;
|
|
Common_Get(now_private)->celebrated_birthday_year = Common_Get(time.rtc_time).year;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void mNpc_GetXmasCardData(Mail_c* mail, PersonalID_c* pid) {
|
|
int header_back_start;
|
|
|
|
mHandbill_Load_HandbillFromRom(mail->content.header, &header_back_start, mail->content.footer, mail->content.body, 0xD7);
|
|
mail->content.header_back_start = header_back_start;
|
|
mail->content.font = mMl_FONT_0;
|
|
mail->content.mail_type = mMl_TYPE_XMAS;
|
|
mPr_CopyPersonalID(&mail->header.recipient.personalID, pid);
|
|
mail->header.recipient.type = mMl_NAME_TYPE_PLAYER;
|
|
mail->present = FTR_FAMICOM_BALLOON_FIGHT;
|
|
mail->content.paper_type = 22; // festive paper
|
|
}
|
|
|
|
extern int mNpc_SendEventXmasCard(PersonalID_c* pid, int player_no) {
|
|
Mail_c* mail = &l_npc_mail;
|
|
mHm_hs_c* home;
|
|
int res = FALSE;
|
|
|
|
if (mPr_NullCheckPersonalID(pid) == FALSE) {
|
|
home = Save_GetPointer(homes[mHS_get_arrange_idx(player_no)]);
|
|
|
|
if (mPr_CheckCmpPersonalID(pid, &home->ownerID) == TRUE) {
|
|
int free_space = mMl_chk_mail_free_space(home->mailbox, HOME_MAILBOX_SIZE);
|
|
|
|
if (free_space != -1) {
|
|
mMl_clear_mail(mail);
|
|
mNpc_GetXmasCardData(mail, pid);
|
|
mMl_copy_mail(home->mailbox + free_space, mail);
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetPresentClothMemoryIdx(Anmmem_c* memory) {
|
|
int i;
|
|
int res = -1;
|
|
|
|
if (memory != NULL) {
|
|
for (i = 0; i < ANIMAL_MEMORY_NUM; i++) {
|
|
if (memory->letter_info.wearing_present_cloth == TRUE) {
|
|
res = i;
|
|
break;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetPresentClothMemoryIdx_rnd(Anmmem_c* memory) {
|
|
Anmmem_c* mem_p = memory;
|
|
int selected;
|
|
int cloth_mem = 0;
|
|
int res = -1;
|
|
int i;
|
|
|
|
if (memory != NULL) {
|
|
for (i = 0; i < ANIMAL_MEMORY_NUM; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE && memory->letter_info.wearing_present_cloth == TRUE) {
|
|
cloth_mem++;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
if (cloth_mem > 0) {
|
|
selected = RANDOM(cloth_mem);
|
|
memory = mem_p;
|
|
for (i = 0; i < ANIMAL_MEMORY_NUM; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE && memory->letter_info.wearing_present_cloth == TRUE) {
|
|
if (selected <= 0) {
|
|
res = i;
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckTalkPresentCloth(Animal_c* animal) {
|
|
int res = FALSE;
|
|
|
|
if (animal != NULL && mNpc_GetPresentClothMemoryIdx(animal->memories) != -1) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_ChangePresentCloth() {
|
|
Animal_c* animal = Save_Get(animals);
|
|
Anmmem_c* memory;
|
|
mActor_name_t present_cloth;
|
|
int i;
|
|
int j;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
present_cloth = animal->present_cloth;
|
|
|
|
if (
|
|
mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE &&
|
|
present_cloth != EMPTY_NO &&
|
|
ITEM_NAME_GET_TYPE(present_cloth) == NAME_TYPE_ITEM1 &&
|
|
ITEM_NAME_GET_CAT(present_cloth) == ITEM1_CAT_CLOTH
|
|
) {
|
|
memory = animal->memories;
|
|
|
|
for (j = 0; j < ANIMAL_MEMORY_NUM; j++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE && memory->letter_info.has_present_cloth == TRUE) {
|
|
memory->letter_info.wearing_present_cloth = TRUE;
|
|
memory->letter_info.has_present_cloth = FALSE;
|
|
animal->cloth = present_cloth;
|
|
break;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
extern u8* mNpc_GetWordEnding(ACTOR* actor) {
|
|
NPC_ACTOR* npc;
|
|
u8* word_ending;
|
|
|
|
/* @BUG - devs used & instead of && */
|
|
#ifndef BUGFIXES
|
|
if (actor != NULL & actor->part == ACTOR_PART_NPC) {
|
|
#else
|
|
if (actor != NULL && actor->part == ACTOR_PART_NPC) {
|
|
#endif
|
|
npc = (NPC_ACTOR*)actor;
|
|
|
|
if (npc->npc_info.animal != NULL) {
|
|
word_ending = npc->npc_info.animal->catchphrase;
|
|
}
|
|
else {
|
|
word_ending = l_no_ending_npc_ending;
|
|
}
|
|
}
|
|
else {
|
|
word_ending = l_no_ending_npc_ending;
|
|
}
|
|
|
|
return word_ending;
|
|
}
|
|
|
|
extern void mNpc_ResetWordEnding(ACTOR* actor) {
|
|
if (actor != NULL && actor->part == ACTOR_PART_NPC) {
|
|
NPC_ACTOR* npc = (NPC_ACTOR*)actor;
|
|
Animal_c* animal = npc->npc_info.animal;
|
|
|
|
if (animal != NULL) {
|
|
mString_Load_StringFromRom(animal->catchphrase, ANIMAL_CATCHPHRASE_LEN, npc_def_list[animal->id.npc_id & 0xFFF].catchphrase_str_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetFreeEventNpcIdx() {
|
|
mNpc_EventNpc_c* event_npc = Common_Get(event_npc);
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_EVENT_NPC_NUM; i++) {
|
|
if (event_npc->in_use == FALSE) {
|
|
return i;
|
|
}
|
|
|
|
event_npc++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
extern int mNpc_RegistEventNpc(mActor_name_t event_id, mActor_name_t texture_id, mActor_name_t npc_id, mActor_name_t cloth_id) {
|
|
int free_idx = mNpc_GetFreeEventNpcIdx();
|
|
int res = FALSE;
|
|
|
|
if (free_idx != -1) {
|
|
mNpc_EventNpc_c* event_npc = Common_GetPointer(event_npc[free_idx]);
|
|
|
|
if (cloth_id != EMPTY_NO) {
|
|
int valid = (cloth_id == RSV_CLOTH) || (cloth_id >= ITM_CLOTH_START && cloth_id < ITM_CLOTH_END);
|
|
|
|
if (valid == FALSE) {
|
|
cloth_id = ITM_CLOTH000;
|
|
}
|
|
}
|
|
|
|
event_npc->event_id = event_id;
|
|
event_npc->texture_id = texture_id;
|
|
event_npc->npc_id = npc_id;
|
|
event_npc->cloth_id = cloth_id;
|
|
event_npc->exists = FALSE;
|
|
event_npc->in_use = TRUE;
|
|
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_UnRegistEventNpc(mNpc_EventNpc_c* npc) {
|
|
mNpc_EventNpc_c* event_npc = Common_Get(event_npc);
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_EVENT_NPC_NUM; i++) {
|
|
if (event_npc == npc) {
|
|
bzero(npc, sizeof(mNpc_EventNpc_c));
|
|
break;
|
|
}
|
|
|
|
event_npc++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_ClearEventNpc() {
|
|
bzero(Common_Get(event_npc), mNpc_EVENT_NPC_NUM * sizeof(mNpc_EventNpc_c));
|
|
}
|
|
|
|
extern mNpc_EventNpc_c* mNpc_GetSameEventNpc(mActor_name_t event_id) {
|
|
mNpc_EventNpc_c* event_npc = Common_Get(event_npc);
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_EVENT_NPC_NUM; i++) {
|
|
if (event_npc->event_id == event_id) {
|
|
return event_npc;
|
|
}
|
|
|
|
event_npc++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
extern int mNpc_GetFreeMaskNpcIdx() {
|
|
mNpc_MaskNpc_c* mask_npc = Common_Get(mask_npc);
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_MASK_NPC_NUM; i++) {
|
|
if (mask_npc->in_use == FALSE) {
|
|
return i;
|
|
}
|
|
|
|
mask_npc++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
extern int mNpc_RegistMaskNpc(mActor_name_t mask_id, mActor_name_t npc_id, mActor_name_t cloth_id) {
|
|
int free_idx = mNpc_GetFreeMaskNpcIdx();
|
|
int res = FALSE;
|
|
|
|
if (free_idx != -1) {
|
|
mNpc_MaskNpc_c* mask_npc = Common_GetPointer(mask_npc[free_idx]);
|
|
|
|
if (cloth_id != EMPTY_NO) {
|
|
int valid = (cloth_id == RSV_CLOTH) || (cloth_id >= ITM_CLOTH_START && cloth_id < ITM_CLOTH_END);
|
|
|
|
if (valid == FALSE) {
|
|
cloth_id = ITM_CLOTH000;
|
|
}
|
|
}
|
|
|
|
mask_npc->mask_id = mask_id;
|
|
mask_npc->npc_id = npc_id;
|
|
mask_npc->exists = FALSE;
|
|
mask_npc->in_use = TRUE;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
int animal_idx = mNpc_SearchAnimalinfo(Save_Get(animals), npc_id, ANIMAL_NUM_MAX);
|
|
|
|
if (animal_idx != -1) {
|
|
mNpc_CopyAnimalInfo(&mask_npc->animal_data, Save_GetPointer(animals[animal_idx]));
|
|
}
|
|
else {
|
|
mNpc_ClearAnimalInfo(&mask_npc->animal_data);
|
|
mNpc_SetDefAnimal(&mask_npc->animal_data, npc_id, npc_def_list);
|
|
}
|
|
|
|
if (cloth_id == EMPTY_NO) {
|
|
mask_npc->cloth_id = mask_npc->animal_data.cloth;
|
|
}
|
|
else {
|
|
mask_npc->cloth_id = cloth_id;
|
|
}
|
|
}
|
|
else {
|
|
mask_npc->cloth_id = cloth_id;
|
|
}
|
|
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_UnRegistMaskNpc(mNpc_MaskNpc_c* npc) {
|
|
mNpc_MaskNpc_c* mask_npc = Common_Get(mask_npc);
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_MASK_NPC_NUM; i++) {
|
|
if (mask_npc == npc) {
|
|
bzero(npc, sizeof(mNpc_MaskNpc_c));
|
|
break;
|
|
}
|
|
|
|
mask_npc++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_ClearMaskNpc() {
|
|
bzero(Common_Get(mask_npc), mNpc_MASK_NPC_NUM * sizeof(mNpc_MaskNpc_c));
|
|
}
|
|
|
|
extern mNpc_MaskNpc_c* mNpc_GetSameMaskNpc(mActor_name_t mask_id) {
|
|
mNpc_MaskNpc_c* mask_npc = Common_Get(mask_npc);
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_MASK_NPC_NUM; i++) {
|
|
if (mask_npc->mask_id == mask_id) {
|
|
return mask_npc;
|
|
}
|
|
|
|
mask_npc++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
extern u8 mNpc_GetLooks(mActor_name_t npc_id) {
|
|
u8 looks = mNpc_LOOKS_GIRL;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
looks = npc_looks_table[npc_id & 0xFFF];
|
|
}
|
|
|
|
return looks;
|
|
}
|
|
|
|
static void mNpc_SetDefAnimalInfo(Animal_c* animal, mActor_name_t npc_id, u8 looks, mNpc_Default_Data_c* def_data) {
|
|
mLd_land_info_c* land_info = Save_GetPointer(land_info);
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
animal->id.npc_id = npc_id;
|
|
animal->id.looks = looks;
|
|
animal->cloth = def_data->cloth;
|
|
animal->umbrella_id = def_data->umbrella;
|
|
mString_Load_StringFromRom(animal->catchphrase, ANIMAL_CATCHPHRASE_LEN, def_data->catchphrase_str_idx);
|
|
animal->id.land_id = land_info->id;
|
|
mLd_CopyLandName(animal->id.land_name, land_info->name);
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetDefAnimalCloth(Animal_c* animal) {
|
|
mActor_name_t npc_id = animal->id.npc_id;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) != NAME_TYPE_NPC) {
|
|
return;
|
|
}
|
|
|
|
if ((npc_id & 0xFFF) < 0) {
|
|
return;
|
|
}
|
|
|
|
if ((npc_id & 0xFFF) >= NPC_NUM) {
|
|
return;
|
|
}
|
|
|
|
animal->cloth = npc_def_list[npc_id & 0xFFF].cloth;
|
|
animal->cloth_original_id = 0xFF;
|
|
}
|
|
|
|
extern void mNpc_SetDefAnimalUmbrella(Animal_c* animal) {
|
|
mActor_name_t npc_id = animal->id.npc_id;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) != NAME_TYPE_NPC) {
|
|
return;
|
|
}
|
|
|
|
if ((npc_id & 0xFFF) < 0) {
|
|
return;
|
|
}
|
|
|
|
if ((npc_id & 0xFFF) >= NPC_NUM) {
|
|
return;
|
|
}
|
|
|
|
animal->umbrella_id = npc_def_list[npc_id & 0xFFF].umbrella;
|
|
}
|
|
|
|
extern void mNpc_SetDefAnimal(Animal_c* animal, mActor_name_t npc_id, mNpc_Default_Data_c* def_data) {
|
|
int idx = npc_id & 0xFFF;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
mNpc_SetDefAnimalInfo(animal, npc_id, npc_looks_table[idx], &def_data[idx]);
|
|
}
|
|
}
|
|
|
|
static void mNpc_SetHaveAppeared(mActor_name_t npc_id) {
|
|
u8 v;
|
|
int idx;
|
|
int bit;
|
|
u8* used_tbl = Save_Get(npc_used_tbl);
|
|
int i;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
idx = npc_id & 0xFFF;
|
|
i = idx / 8;
|
|
|
|
if (i < ARRAY_COUNT(Save_Get(npc_used_tbl))) {
|
|
/* what in the fuck */
|
|
v = used_tbl[i];
|
|
bit = idx & 7;
|
|
bit = 1 << bit;
|
|
v |= bit;
|
|
used_tbl[i] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mNpc_GetHaveAppeared_idx(int idx) {
|
|
u8* used_tbl = Save_Get(npc_used_tbl);
|
|
int i = idx / 8;
|
|
int res = TRUE;
|
|
|
|
if (i < ARRAY_COUNT(Save_Get(npc_used_tbl))) {
|
|
res = (used_tbl[i] >> (idx & 7)) & 1;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_GetDefGrowPermission(int idx, s8* grow_list, int count) {
|
|
int res = mNpc_GROW_MOVE_IN;
|
|
|
|
if (idx < count) {
|
|
res = grow_list[idx];
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_GetLooks2NotHaveAppearedNum(u8 looks) {
|
|
u8* looks_tbl;
|
|
s8* grow_list = npc_grow_list;
|
|
int num = 0;
|
|
int i;
|
|
|
|
if (looks < mNpc_LOOKS_NUM) {
|
|
looks_tbl = npc_looks_table;
|
|
|
|
for (i = 0; i < NPC_NUM; i++) {
|
|
if (looks == looks_tbl[0]) {
|
|
int grow_type = mNpc_GetDefGrowPermission(i, grow_list, NPC_NUM);
|
|
|
|
if (mNpc_GetHaveAppeared_idx(i) == FALSE && (grow_type == mNpc_GROW_STARTER || grow_type == mNpc_GROW_MOVE_IN)) {
|
|
num++;
|
|
}
|
|
}
|
|
|
|
looks_tbl++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
static void mNpc_ResetHaveAppeared_common(u8* npc_used_tbl, Animal_c* animal) {
|
|
int i;
|
|
|
|
bzero(npc_used_tbl, sizeof(Save_Get(npc_used_tbl)));
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE) {
|
|
mNpc_SetHaveAppeared(animal->id.npc_id);
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
static void mNpc_ResetHaveAppeared() {
|
|
s8* grow_list = npc_grow_list;
|
|
int i;
|
|
|
|
for (i = 0; i < NPC_NUM; i++) {
|
|
u32 grow_perm = mNpc_GetDefGrowPermission(i, grow_list, NPC_NUM);
|
|
|
|
if (mNpc_GetHaveAppeared_idx(i) == FALSE && grow_perm <= mNpc_GROW_MOVE_IN) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* All animals have appeared in town (islanders excluded) */
|
|
if (i == NPC_NUM) {
|
|
mNpc_ResetHaveAppeared_common(Save_Get(npc_used_tbl), Save_Get(animals));
|
|
}
|
|
}
|
|
|
|
static void mNpc_DecideLivingNpcMax(Animal_c* animal, u8 count, int malloc_flag) {
|
|
static int fakeTable[NPC_NUM];
|
|
|
|
mNpc_Default_Data_c* def_list;
|
|
s8* grow_list;
|
|
u8 looks_bitfield = 0;
|
|
int i = 0;
|
|
int animal_idx = 0;
|
|
|
|
bzero(fakeTable, sizeof(fakeTable));
|
|
mNpc_MakeRandTable(fakeTable, NPC_NUM, NPC_NUM);
|
|
def_list = npc_def_list;
|
|
grow_list = npc_grow_list;
|
|
|
|
while (count != 0) {
|
|
if (animal == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (animal->id.npc_id == EMPTY_NO) {
|
|
int idx = fakeTable[i];
|
|
mActor_name_t npc_id = NPC_START | idx;
|
|
int grow_perm = mNpc_GetDefGrowPermission(idx, grow_list, NPC_NUM);
|
|
|
|
if (grow_perm == mNpc_GROW_STARTER) {
|
|
int looks = npc_looks_table[idx];
|
|
|
|
if (((looks_bitfield >> looks) & 1) == 0) {
|
|
mNpc_SetDefAnimal(animal, npc_id, def_list);
|
|
looks_bitfield |= (1 << looks);
|
|
mNpc_SetHaveAppeared(npc_id);
|
|
mNpc_ResetAnimalRelation(animal_idx);
|
|
animal++;
|
|
animal_idx++;
|
|
count--;
|
|
}
|
|
}
|
|
else if (grow_perm == -1) {
|
|
mNpc_SetHaveAppeared(npc_id);
|
|
}
|
|
}
|
|
else {
|
|
animal++;;
|
|
animal_idx++;
|
|
count--;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetAnimalTitleDemo(mNpc_demo_npc_c* demo_npc, Animal_c* animal, GAME* game) {
|
|
mNpc_Default_Data_c* def_list = npc_def_list;
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
mNpc_SetDefAnimal(animal, demo_npc->npc_name, def_list);
|
|
animal->home_info.type_unused = 0;
|
|
animal->home_info.block_x = demo_npc->block_x;
|
|
animal->home_info.block_z = demo_npc->block_z;
|
|
animal->home_info.ut_x = demo_npc->ut_x;
|
|
animal->home_info.ut_z = demo_npc->ut_z;
|
|
|
|
animal++;
|
|
demo_npc++;
|
|
}
|
|
}
|
|
|
|
/* @unused */
|
|
extern int mNpc_GetReservedUtNum(int* ut_x, int* ut_z, mActor_name_t* item) {
|
|
f32 reserve_num = 0.0f;
|
|
int rand;
|
|
int res = FALSE;
|
|
int i;
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if ((*item >= SIGN00 && *item < WISHING_WELL) || *item == DUMMY_RESERVE) {
|
|
reserve_num++;
|
|
}
|
|
|
|
item++;
|
|
}
|
|
|
|
if (reserve_num > 0.0f) {
|
|
item -= UT_TOTAL_NUM;
|
|
rand = RANDOM(reserve_num);
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if ((*item >= SIGN00 && *item < WISHING_WELL) || *item == DUMMY_RESERVE) {
|
|
if (rand == 0) {
|
|
*ut_x = i % UT_Z_NUM;
|
|
*ut_z = i / UT_X_NUM;
|
|
res = TRUE;
|
|
|
|
break;
|
|
}
|
|
else {
|
|
rand--;
|
|
}
|
|
}
|
|
|
|
item++;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* @unused */
|
|
extern int mNpc_BlockNum2ReservedUtNum(int* ut_x, int* ut_z, int bx, int bz) {
|
|
mActor_name_t* items = mFI_BkNumtoUtFGTop(bx, bz);
|
|
int res = FALSE;
|
|
|
|
if (items != NULL) {
|
|
res = mNpc_GetReservedUtNum(ut_x, ut_z, items);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_MakeReservedListBeforeFieldct(Anmhome_c* reserved, int reserved_num, u8* reserved_count) {
|
|
mFM_fg_c* fg = Save_GetPointer(fg[0][0]);
|
|
Anmhome_c* reserved_p = reserved;
|
|
mActor_name_t* item;
|
|
u8 num = 0;
|
|
u8 i;
|
|
u8 j;
|
|
u8 k;
|
|
u8 l;
|
|
|
|
for (i = 0; i < reserved_num; i++) {
|
|
reserved->block_x = -1;
|
|
reserved->block_z = -1;
|
|
reserved->ut_x = -1;
|
|
reserved->ut_z = -1;
|
|
reserved++;
|
|
}
|
|
|
|
reserved = reserved_p;
|
|
for (i = 0; i < FG_BLOCK_Z_NUM; i++) {
|
|
for (j = 0; j < FG_BLOCK_X_NUM; j++) {
|
|
item = &fg->items[0][0];
|
|
|
|
for (k = 0; k < UT_Z_NUM; k++) {
|
|
for (l = 0; l < UT_X_NUM; l++) {
|
|
if (mNT_IS_RESERVE(*item)) {
|
|
num++;
|
|
reserved->block_x = j + 1;
|
|
reserved->block_z = i + 1;
|
|
reserved->ut_x = l;
|
|
reserved->ut_z = k;
|
|
reserved++;
|
|
if (num >= reserved_num) {
|
|
*reserved_count = num;
|
|
return;
|
|
}
|
|
}
|
|
|
|
item++;
|
|
}
|
|
}
|
|
|
|
fg++;
|
|
}
|
|
}
|
|
|
|
*reserved_count = num;
|
|
}
|
|
|
|
extern void mNpc_MakeReservedListAfterFieldct(Anmhome_c* reserved, int reserved_num, u8* reserved_count, u8 bx_max, u8 bz_max) {
|
|
mFM_block_info_c* block_info = mFI_GetBlockTopP();
|
|
Anmhome_c* reserved_p = reserved;
|
|
mActor_name_t* item;
|
|
u8 num = 0;
|
|
u8 i;
|
|
u8 j;
|
|
u8 k;
|
|
u8 l;
|
|
|
|
for (i = 0; i < reserved_num; i++) {
|
|
reserved->block_x = -1;
|
|
reserved->block_z = -1;
|
|
reserved->ut_x = -1;
|
|
reserved->ut_z = -1;
|
|
reserved++;
|
|
}
|
|
|
|
reserved = reserved_p;
|
|
for (i = 0; i < bz_max; i++) {
|
|
for (j = 0; j < bx_max; j++) {
|
|
item = block_info->fg_info.items_p;
|
|
|
|
for (k = 0; k < UT_Z_NUM; k++) {
|
|
for (l = 0; l < UT_X_NUM; l++) {
|
|
if (mNT_IS_RESERVE(*item)) {
|
|
num++;
|
|
reserved->block_x = j;
|
|
reserved->block_z = i;
|
|
reserved->ut_x = l;
|
|
reserved->ut_z = k;
|
|
reserved++;
|
|
if (num >= reserved_num) {
|
|
*reserved_count = num;
|
|
return;
|
|
}
|
|
}
|
|
|
|
item++;
|
|
}
|
|
}
|
|
|
|
block_info++;
|
|
}
|
|
}
|
|
|
|
*reserved_count = num;
|
|
}
|
|
|
|
static void mNpc_BuildHouseBeforeFieldct(mActor_name_t npc_id, int bx, int bz, int ut_x, int ut_z) {
|
|
static int ut_d[9][2] = {
|
|
{ 0, 0 },
|
|
{ -1, 1 },
|
|
{ 0, 1 },
|
|
{ 1, 1 },
|
|
{ -1, 0 },
|
|
{ 1, 0 },
|
|
{ -1, -1 },
|
|
{ 0, -1 },
|
|
{ 1, -1 }
|
|
};
|
|
|
|
static mActor_name_t set_fg[9] = {
|
|
EMPTY_NO, // npc house id
|
|
ACTOR_PROP_VILLAGER_SIGNBOARD,
|
|
RSV_NO,
|
|
RSV_NO,
|
|
RSV_NO,
|
|
RSV_NO,
|
|
RSV_NO,
|
|
RSV_NO,
|
|
RSV_NO
|
|
};
|
|
|
|
u16* deposit;
|
|
mFM_fg_c* fg;
|
|
int x;
|
|
int z;
|
|
int i;
|
|
mActor_name_t house = npc_id - 0x10000 + 0x7000; // this shifts it to the house range 0x50XX
|
|
|
|
if (
|
|
bx >= 0 && bx < FG_BLOCK_X_NUM &&
|
|
bz >= 0 && bz < FG_BLOCK_Z_NUM &&
|
|
ut_x > 0 && ut_x < (UT_X_NUM - 1) &&
|
|
ut_z > 0 && ut_z < (UT_Z_NUM - 1)
|
|
) {
|
|
set_fg[0] = house;
|
|
fg = Save_GetPointer(fg[bz][bx]);
|
|
|
|
deposit = mFI_GetDepositP(bx + 1, bz + 1);
|
|
|
|
for (i = 0; i < ARRAY_COUNT(set_fg); i++) {
|
|
x = ut_d[i][0] + ut_x;
|
|
z = ut_d[i][1] + ut_z;
|
|
|
|
mPB_keep_item(fg->items[z][x]);
|
|
if (deposit != NULL) {
|
|
mFI_BlockDepositOFF(deposit, x, z);
|
|
}
|
|
|
|
fg->items[z][x] = set_fg[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mNpc_DestroyHouse(Anmhome_c* home) {
|
|
u8 bx = home->block_x - 1;
|
|
u8 bz = home->block_z - 1;
|
|
u8 ut_x = home->ut_x;
|
|
u8 ut_z = home->ut_z - 1;
|
|
|
|
if (bx < FG_BLOCK_X_NUM && bz < FG_BLOCK_Z_NUM && ut_x > 0 && ut_x < (UT_X_NUM - 1) && ut_z > 0 && ut_z < (UT_Z_NUM - 1)) {
|
|
mActor_name_t reserve_no = mFM_GetReseveName(home->block_x, home->block_z);
|
|
|
|
Save_Get(fg[bz][bx]).items[ut_z][ut_x] = reserve_no;
|
|
Save_Get(fg[bz][bx]).items[ut_z + 1][ut_x - 1] = EMPTY_NO;
|
|
Save_Get(fg[bz][bx]).items[ut_z + 1][ut_x] = EMPTY_NO;
|
|
Save_Get(fg[bz][bx]).items[ut_z + 1][ut_x + 1] = EMPTY_NO;
|
|
Save_Get(fg[bz][bx]).items[ut_z][ut_x - 1] = EMPTY_NO;
|
|
Save_Get(fg[bz][bx]).items[ut_z][ut_x + 1] = EMPTY_NO;
|
|
Save_Get(fg[bz][bx]).items[ut_z - 1][ut_x - 1] = EMPTY_NO;
|
|
Save_Get(fg[bz][bx]).items[ut_z - 1][ut_x] = EMPTY_NO;
|
|
Save_Get(fg[bz][bx]).items[ut_z - 1][ut_x + 1] = EMPTY_NO;
|
|
}
|
|
}
|
|
|
|
static void mNpc_SetNpcHome(Animal_c* animal, Anmhome_c* reserved, u8 reserved_num) {
|
|
static int fake_table[60];
|
|
|
|
Anmhome_c* home;
|
|
int* fake_table_p = fake_table;
|
|
int n = 0;
|
|
int i;
|
|
int idx;
|
|
|
|
if (reserved_num > ARRAY_COUNT(fake_table)) {
|
|
reserved_num = ARRAY_COUNT(fake_table);
|
|
}
|
|
|
|
|
|
if (reserved_num > 0) {
|
|
bzero(fake_table_p, sizeof(fake_table));
|
|
mNpc_MakeRandTable(fake_table_p, reserved_num, ARRAY_COUNT(fake_table) / 2);
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (ITEM_NAME_GET_TYPE(animal->id.npc_id) == NAME_TYPE_NPC && animal->home_info.block_x == 0xFF && animal->home_info.block_z == 0xFF) {
|
|
idx = fake_table_p[n];
|
|
|
|
if (idx >= reserved_num) {
|
|
/* I don't like this */
|
|
while ((idx = fake_table_p[n], idx >= reserved_num) && n < reserved_num) {
|
|
n++;
|
|
}
|
|
|
|
if (n >= reserved_num) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (idx < reserved_num) {
|
|
home = &reserved[idx];
|
|
|
|
animal->home_info.block_x = home->block_x;
|
|
animal->home_info.block_z = home->block_z;
|
|
animal->home_info.ut_x = home->ut_x;
|
|
animal->home_info.ut_z = home->ut_z + 1;
|
|
mNpc_BuildHouseBeforeFieldct(animal->id.npc_id, animal->home_info.block_x - 1, animal->home_info.block_z - 1, animal->home_info.ut_x, home->ut_z);
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
if (n >= reserved_num) {
|
|
break;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern mNpc_InitNpcData() {
|
|
static Anmhome_c reserved[60];
|
|
|
|
u8 reserved_num = 0;
|
|
|
|
mNpc_MakeReservedListBeforeFieldct(reserved, ARRAY_COUNT(reserved), &reserved_num);
|
|
mNpc_SetNpcHome(Save_Get(animals), reserved, reserved_num);
|
|
}
|
|
|
|
extern void mNpc_InitNpcList(mNpc_NpcList_c* npclist, int count) {
|
|
int i;
|
|
|
|
if (npclist != NULL) {
|
|
bzero(npclist, count * sizeof(mNpc_NpcList_c));
|
|
|
|
for (i = 0; i < count; i++) {
|
|
npclist->name = EMPTY_NO;
|
|
npclist->field_name = RSV_NO;
|
|
mQst_ClearQuestInfo(&npclist->quest_info);
|
|
npclist->house_data.type = 0xFF;
|
|
npclist->house_data.palette = 0xFF;
|
|
npclist->house_data.wall_id = 0xFF;
|
|
npclist->house_data.floor_id = 0xFF;
|
|
npclist->house_data.main_layer_id = 203;
|
|
|
|
npclist++;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetNpcList(mNpc_NpcList_c* npclist, Animal_c* animal, int count, int malloc_flag) {
|
|
xyz_t pos;
|
|
mActor_name_t npc_id;
|
|
int i;
|
|
mNpc_NpcHouseData_c* house_list = npc_house_list;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
npc_id = animal->id.npc_id;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC && animal->home_info.block_x != 0xFF) {
|
|
npclist->name = npc_id;
|
|
|
|
npclist->house_position.x = animal->home_info.block_x * mFI_BK_WORLDSIZE_X_F;
|
|
npclist->house_position.z = animal->home_info.block_z * mFI_BK_WORLDSIZE_Z_F;
|
|
mFI_UtNum2PosXZInBk(&pos.x, &pos.z, animal->home_info.ut_x, animal->home_info.ut_z);
|
|
npclist->house_position.x += pos.x;
|
|
npclist->house_position.z += pos.z;
|
|
npclist->house_position.y = 0.0f;
|
|
|
|
npclist->position.x = npclist->house_position.x;
|
|
npclist->position.y = npclist->house_position.y;
|
|
npclist->position.z = npclist->house_position.z;
|
|
|
|
npclist->appear_flag = TRUE;
|
|
npclist->reward_furniture = EMPTY_NO;
|
|
|
|
npclist->house_data.type = house_list[npc_id & 0xFFF].type;
|
|
npclist->house_data.palette = house_list[npc_id & 0xFFF].palette;
|
|
npclist->house_data.wall_id = house_list[npc_id & 0xFFF].wall_id;
|
|
npclist->house_data.floor_id = house_list[npc_id & 0xFFF].floor_id;
|
|
npclist->house_data.main_layer_id = house_list[npc_id & 0xFFF].main_layer_id;
|
|
npclist->house_data.secondary_layer_id = house_list[npc_id & 0xFFF].secondary_layer_id;
|
|
}
|
|
|
|
npclist++;
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetNpcinfo(ACTOR* actor, s8 npc_info_idx) {
|
|
if (actor->part == ACTOR_PART_NPC) {
|
|
NPC_ACTOR* npc = (NPC_ACTOR*)actor;
|
|
mActor_name_t npc_id = actor->npc_id;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_SPNPC) {
|
|
mNpc_EventNpc_c* event_npc = mNpc_GetSameEventNpc(npc_id);
|
|
|
|
if (event_npc != NULL) {
|
|
npc_info_idx = mNpc_SearchAnimalinfo(Save_Get(animals), event_npc->npc_id, ANIMAL_NUM_MAX);
|
|
}
|
|
}
|
|
|
|
if (npc_info_idx >= 0 && npc_info_idx < ANIMAL_NUM_MAX) {
|
|
npc->npc_info.animal = Save_GetPointer(animals[npc_info_idx]);
|
|
npc->npc_info.list = Common_GetPointer(npclist[npc_info_idx]);
|
|
}
|
|
else if (npc_info_idx == -ANIMAL_NUM_MAX) {
|
|
npc->npc_info.animal = &Save_Get(island).animal;
|
|
npc->npc_info.list = Common_GetPointer(island_npclist[0]);
|
|
}
|
|
else {
|
|
mNpc_MaskNpc_c* mask_npc = mNpc_GetSameMaskNpc(npc_id);
|
|
|
|
if (mask_npc != NULL) {
|
|
if (ITEM_NAME_GET_TYPE(mask_npc->npc_id) == NAME_TYPE_NPC) {
|
|
npc->npc_info.animal = &mask_npc->animal_data;
|
|
npc->npc_info.list = NULL;
|
|
}
|
|
else {
|
|
npc->npc_info.animal = NULL;
|
|
npc->npc_info.list = NULL;
|
|
}
|
|
}
|
|
else {
|
|
npc->npc_info.animal = NULL;
|
|
npc->npc_info.list = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mNpc_AddNpc_inNpcRoom(mFM_move_actor_c* move_actor, int move_actor_idx, u8 bx, u8 bz) {
|
|
u32 owner_id = Common_Get(house_owner_name);
|
|
mActor_name_t id = owner_id;
|
|
|
|
if (id != RSV_NO) {
|
|
if (id != EMPTY_NO && mEvNM_CheckJointEvent(owner_id) != TRUE) {
|
|
int idx = mNpc_SearchAnimalinfo(Save_Get(animals), id, ANIMAL_NUM_MAX);
|
|
|
|
if (idx != -1) {
|
|
move_actor->name_id = id;
|
|
move_actor->ut_x = 4;
|
|
move_actor->ut_z = 7;
|
|
move_actor->npc_info_idx = idx;
|
|
move_actor->arg = -1;
|
|
mFI_SetMoveActorBitData_ON(move_actor_idx, bx, bz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mNpc_AddNpc_inNpcRoomIsland(mFM_move_actor_c* move_actor, int move_actor_idx, u8 bx, u8 bz) {
|
|
move_actor->name_id = Save_Get(island).animal.id.npc_id;
|
|
move_actor->ut_x = 4;
|
|
move_actor->ut_z = 7;
|
|
move_actor->npc_info_idx = -ANIMAL_NUM_MAX;
|
|
move_actor->arg = -1;
|
|
mFI_SetMoveActorBitData_ON(move_actor_idx, bx, bz);
|
|
}
|
|
|
|
static void mNpc_AddNpc_inKamakura(mFM_move_actor_c* move_actor, int move_actor_idx, u8 bx, u8 bz) {
|
|
u8* kamakura_event = (u8*)mEv_get_save_area(mEv_EVENT_KAMAKURA, 15);
|
|
|
|
if (kamakura_event != NULL) {
|
|
int idx = *kamakura_event;
|
|
mActor_name_t npc_id = Save_Get(animals[idx]).id.npc_id;
|
|
|
|
mEvMN_GetEventNpcName(&npc_id, mEv_EVENT_KAMAKURA, idx, 0);
|
|
move_actor->name_id = npc_id;
|
|
move_actor->ut_x = 4;
|
|
move_actor->ut_z = 7;
|
|
move_actor->npc_info_idx = idx;
|
|
move_actor->arg = -1;
|
|
mFI_SetMoveActorBitData_ON(move_actor_idx, bx, bz);
|
|
}
|
|
}
|
|
|
|
extern void mNpc_AddNpc_inBlock(mFM_move_actor_c* move_actor_list, u8 bx, u8 bz) {
|
|
int scene = Save_Get(scene_no);
|
|
|
|
if ((scene == SCENE_NPC_HOUSE || scene == SCENE_KAMAKURA || scene == SCENE_COTTAGE_NPC) && move_actor_list != NULL) {
|
|
mActor_name_t field_id = mFI_GetFieldId(); // unused
|
|
int move_actor_idx = mFI_GetMoveActorListIdx(move_actor_list, mFM_MOVE_ACTOR_NUM, EMPTY_NO);
|
|
|
|
if (move_actor_idx != -1 && move_actor_idx < ANIMAL_NUM_MAX) {
|
|
move_actor_list += move_actor_idx;
|
|
|
|
switch (scene) {
|
|
case SCENE_NPC_HOUSE:
|
|
mNpc_AddNpc_inNpcRoom(move_actor_list, move_actor_idx, bx, bz);
|
|
break;
|
|
case SCENE_COTTAGE_NPC:
|
|
mNpc_AddNpc_inNpcRoomIsland(move_actor_list, move_actor_idx, bx, bz);
|
|
break;
|
|
case SCENE_KAMAKURA:
|
|
mNpc_AddNpc_inKamakura(move_actor_list, move_actor_idx, bx, bz);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_RenewalNpcRoom(s16* wall_floor) {
|
|
mActor_name_t field_id = mFI_GetFieldId();
|
|
mActor_name_t owner_id = Common_Get(house_owner_name);
|
|
int island_wall = 0;
|
|
int island_floor = 0;
|
|
u8 wall = 0;
|
|
u8 floor = 0;
|
|
int island_room_ftr_num;
|
|
|
|
if (Save_Get(scene_no) == SCENE_COTTAGE_NPC) {
|
|
wall = 8; // exotic wall
|
|
floor = 32; // bamboo flooring
|
|
island_room_ftr_num = mNpc_GetIslandRoomFtrNum();
|
|
|
|
if (island_room_ftr_num >= 2) {
|
|
mNpc_GetIslandWallFloorIdx(&island_wall, &island_floor, Save_Get(island).animal.id.npc_id);
|
|
wall = island_wall;
|
|
|
|
if (island_room_ftr_num >= 5) {
|
|
floor = island_floor;
|
|
}
|
|
}
|
|
}
|
|
else if (mFI_GET_TYPE(field_id) == mFI_FIELD_NPCROOM0 && owner_id != EMPTY_NO && owner_id != RSV_NO) {
|
|
int idx = mNpc_SearchAnimalinfo(Save_Get(animals), owner_id, ANIMAL_NUM_MAX);
|
|
mNpc_NpcList_c* npclist;
|
|
|
|
if (idx == -1) {
|
|
idx = 0;
|
|
}
|
|
|
|
npclist = Common_GetPointer(npclist[idx]);
|
|
|
|
wall = npclist->house_data.wall_id;
|
|
floor = npclist->house_data.floor_id;
|
|
}
|
|
|
|
wall_floor[0] = (wall << 8) | floor;
|
|
}
|
|
|
|
extern void mNpc_RenewalSetNpc(ACTOR* actor) {
|
|
NPC_ACTOR* npc = (NPC_ACTOR*)actor;
|
|
Animal_c* animal;
|
|
|
|
if (npc->npc_info.list != NULL) {
|
|
mActor_name_t field_id = mFI_GetFieldId();
|
|
mNpc_NpcList_c* npclist = npc->npc_info.list;
|
|
xyz_t* npc_pos = &npclist->position;
|
|
|
|
if (mFI_GET_TYPE(field_id) == mFI_FIELD_FG) {
|
|
xyz_t_move(npc_pos, &actor->world.position);
|
|
npclist->appear_flag = TRUE;
|
|
animal = &Save_Get(island).animal;
|
|
|
|
if (actor->npc_id == animal->id.npc_id) {
|
|
int bnum = mFI_GetBlockNum(actor->block_x, actor->block_z);
|
|
mFM_move_actor_c* move_actor_list = g_fdinfo->block_info[bnum].fg_info.move_actors;
|
|
|
|
if (move_actor_list != NULL) {
|
|
int move_actor_idx = mFI_GetMoveActorListIdx(move_actor_list, mFM_MOVE_ACTOR_NUM, actor->npc_id);
|
|
|
|
if (move_actor_idx != -1) {
|
|
int bx;
|
|
int bz;
|
|
int ut_x;
|
|
int ut_z;
|
|
|
|
if (mFI_Wpos2BkandUtNuminBlock(&bx, &bz, &ut_x, &ut_z, npclist->position) == FALSE) {
|
|
ut_x = animal->home_info.ut_x;
|
|
ut_z = animal->home_info.ut_z;
|
|
}
|
|
else if (mNpc_CheckNpcSet(bx, bz, ut_x, ut_z) == FALSE && mNpc_GetMakeUtNuminBlock_area(&ut_x, &ut_z, bx, bz, 0) == FALSE) {
|
|
ut_x = animal->home_info.ut_x;
|
|
ut_z = animal->home_info.ut_z;
|
|
}
|
|
|
|
move_actor_list[move_actor_idx].ut_x = ut_x;
|
|
move_actor_list[move_actor_idx].ut_z = ut_z;
|
|
}
|
|
}
|
|
|
|
mFI_MyMoveActorBitData_ON(actor);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
mFI_MyMoveActorBitData_ON(actor);
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetFriendAnimalNum(PersonalID_c* pid) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int num = 0;
|
|
int i;
|
|
|
|
if (pid != NULL) {
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE && mNpc_GetAnimalMemoryIdx(pid, animal->memories, ANIMAL_MEMORY_NUM) != -1) {
|
|
num++;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
extern int mNpc_CheckFriendAllAnimal(PersonalID_c* pid) {
|
|
int now_npc_max = Save_Get(now_npc_max);
|
|
int friend_num = mNpc_GetFriendAnimalNum(pid);
|
|
|
|
if (now_npc_max <= friend_num) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static int mNpc_CheckSelectFurniture(mActor_name_t item) {
|
|
int res = FALSE;
|
|
|
|
if (
|
|
!(item >= FTR_CLOTH_START && item <= FTR_CLOTH_END ) && // Clothing (furniture)
|
|
!(item >= FTR_UMBRELLA_START && item <= FTR_UMBRELLA_END) && // Umbrellas (furniture)
|
|
!(item >= FTR_INSECT_START && item <= FTR_INSECT_END ) && // Insects (furniture)
|
|
!(item >= FTR_FISH_START && item <= FTR_FISH_END ) && // Fish (furniture)
|
|
!(item >= HANIWA_START && item <= HANIWA_END ) && // Gyroids
|
|
!(item >= FTR_DINO_START && item <= FTR_DINO_END ) && // Idenitfied fossils
|
|
!(item >= FTR_FAMICOM_START && item <= FTR_FAMICOM_END ) // NES/Famicom games
|
|
) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static mActor_name_t mNpc_DecideNpcFurniture(mFM_fg_data_c** fg_data_table, mNpc_NpcList_c* npclist, int fg_base_id) {
|
|
int data_idx = npclist->house_data.main_layer_id - fg_base_id;
|
|
mActor_name_t* fg_items;
|
|
u8 num = 0;
|
|
int ut_z;
|
|
int ut_x;
|
|
|
|
if (data_idx < 0) {
|
|
data_idx = 0;
|
|
}
|
|
|
|
fg_items = fg_data_table[data_idx]->items[0];
|
|
|
|
for (ut_z = 0; ut_z < 10; ut_z++) {
|
|
for (ut_x = 0; ut_x < 10; ut_x++) {
|
|
if (ITEM_IS_FTR(*fg_items) && mNpc_CheckSelectFurniture(*fg_items) == TRUE) {
|
|
num++;
|
|
}
|
|
|
|
fg_items++;
|
|
}
|
|
|
|
fg_items += UT_X_NUM - 10;
|
|
}
|
|
|
|
if (num != 0) {
|
|
num = RANDOM(num);
|
|
|
|
fg_items = fg_data_table[data_idx]->items[0];
|
|
for (ut_z = 0; ut_z < 10; ut_z++) {
|
|
for (ut_x = 0; ut_x < 10; ut_x++) {
|
|
if (ITEM_IS_FTR(*fg_items) && mNpc_CheckSelectFurniture(*fg_items) == TRUE) {
|
|
if (num <= 0) {
|
|
return *fg_items;
|
|
}
|
|
else {
|
|
num--;
|
|
}
|
|
}
|
|
|
|
fg_items++;
|
|
}
|
|
|
|
fg_items += UT_X_NUM - 10;
|
|
}
|
|
}
|
|
|
|
return EMPTY_NO;
|
|
}
|
|
|
|
extern void mNpc_SetNpcFurnitureRandom(mFM_fg_data_c** fg_data_table, int fg_base_id) {
|
|
mNpc_NpcList_c* npclist = Common_Get(npclist);
|
|
Animal_c* animal = Save_Get(animals);
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) {
|
|
npclist->reward_furniture = mNpc_DecideNpcFurniture(fg_data_table, npclist, fg_base_id);
|
|
}
|
|
|
|
npclist++;
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
extern mActor_name_t mNpc_GetNpcFurniture(AnmPersonalID_c* anm_id) {
|
|
mActor_name_t furniture = EMPTY_NO;
|
|
int idx = mNpc_SearchAnimalPersonalID(anm_id);
|
|
|
|
if (idx != -1) {
|
|
furniture = Common_Get(npclist[idx]).reward_furniture;
|
|
}
|
|
|
|
return furniture;
|
|
}
|
|
|
|
static Animal_c l_mnpc_remove_in_animal;
|
|
|
|
extern void mNpc_ClearInAnimal() {
|
|
mNpc_ClearAnimalInfo(&l_mnpc_remove_in_animal);
|
|
}
|
|
|
|
extern Animal_c* mNpc_GetInAnimalP() {
|
|
return &l_mnpc_remove_in_animal;
|
|
}
|
|
|
|
static void mNpc_RenewRemoveHistory() {
|
|
mTM_set_renew_time(Save_GetPointer(force_remove_date), Common_GetPointer(time.rtc_time));
|
|
}
|
|
|
|
static int mNpc_DecideRemoveAnimalNo_Friend(Animal_c* animal, int ignored_idx, int met_all_flag) {
|
|
int res;
|
|
Private_c* priv_p;
|
|
int n_players;
|
|
int num_possible;
|
|
u16 remove_bitfield;
|
|
int met;
|
|
int selected;
|
|
int i;
|
|
int j;
|
|
|
|
res = -1;
|
|
n_players = 0;
|
|
num_possible = 0;
|
|
remove_bitfield = 0;
|
|
priv_p = Save_Get(private);
|
|
|
|
for (i = 0; i < PLAYER_NUM; i++) {
|
|
if (mPr_NullCheckPersonalID(&priv_p->player_ID) == FALSE) {
|
|
n_players++;
|
|
}
|
|
|
|
priv_p++;
|
|
}
|
|
|
|
if (n_players > 0) {
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE && i != ignored_idx) {
|
|
met = 0;
|
|
priv_p = Save_Get(private);
|
|
|
|
for (j = 0; j < PLAYER_NUM; j++) {
|
|
if (mPr_NullCheckPersonalID(&priv_p->player_ID) == FALSE) {
|
|
if (mNpc_GetAnimalMemoryIdx(&priv_p->player_ID, animal->memories, ANIMAL_MEMORY_NUM) == -1) {
|
|
break;
|
|
}
|
|
|
|
met++;
|
|
}
|
|
|
|
priv_p++;
|
|
}
|
|
|
|
if (met_all_flag == TRUE) {
|
|
if (met == n_players) {
|
|
remove_bitfield |= (1 << i);
|
|
num_possible++;
|
|
}
|
|
}
|
|
else if (met > 0) {
|
|
remove_bitfield |= (1 << i);
|
|
num_possible++;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
if (num_possible > 0) {
|
|
selected = RANDOM(num_possible);
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (((remove_bitfield >> i) & 1) == 1) {
|
|
if (selected <= 0) {
|
|
res = i;
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_DecideRemoveAnimalNo(Animal_c* animal, int ignored_idx, int now_npc_max) {
|
|
int i;
|
|
int selected;
|
|
int res = -1;
|
|
|
|
if (ignored_idx >= 0 || ignored_idx < ANIMAL_NUM_MAX) {
|
|
now_npc_max--;
|
|
}
|
|
|
|
if (now_npc_max > 0 && now_npc_max <= ANIMAL_NUM_MAX) {
|
|
selected = RANDOM(now_npc_max);
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE) {
|
|
if (selected <= 0 && ignored_idx != i) {
|
|
res = i;
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
/* 50% */
|
|
|
|
extern void mNpc_SetRemoveAnimalNo(u8* remove_animal_no, Animal_c* animal, int ignored_idx) {
|
|
u8 now_npc_max = Save_Get(now_npc_max);
|
|
|
|
if (now_npc_max > ANIMAL_NUM_MIN && remove_animal_no[0] == 0xFF) {
|
|
/* First, try to pick an animal which all players have met before */
|
|
int remove_no = mNpc_DecideRemoveAnimalNo_Friend(animal, ignored_idx, TRUE);
|
|
|
|
if (remove_no == -1) {
|
|
/* Next, try to pick an animal which at least one player has met before */
|
|
remove_no = mNpc_DecideRemoveAnimalNo_Friend(animal, ignored_idx, FALSE);
|
|
|
|
if (remove_no == -1) {
|
|
/* Finally, forcefully find an animal to remove */
|
|
remove_no = mNpc_DecideRemoveAnimalNo(animal, ignored_idx, now_npc_max);
|
|
}
|
|
}
|
|
|
|
if (remove_no != -1) {
|
|
remove_animal_no[0] = remove_no;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mNpc_CheckGoodbyAnimalMemoryNum(Animal_c* a0, Animal_c* a1) {
|
|
Anmmem_c* memories0 = a0->memories;
|
|
Anmmem_c* memories1 = a1->memories;
|
|
int res = 0;
|
|
int res0;
|
|
int res1;
|
|
|
|
res0 = mNpc_GetAnimalMemoryNum(memories0, ANIMAL_MEMORY_NUM);
|
|
res1 = mNpc_GetAnimalMemoryNum(memories1, ANIMAL_MEMORY_NUM);
|
|
|
|
/* Check the total number of memories first */
|
|
if (res1 > res0) {
|
|
res = 1;
|
|
}
|
|
else if (res1 == res0) {
|
|
/* Next, check the number of memories which have a letter associated with them */
|
|
res0 = mNpc_GetAnimalMemoryLetterNum(memories0, ANIMAL_MEMORY_NUM);
|
|
res1 = mNpc_GetAnimalMemoryLetterNum(memories1, ANIMAL_MEMORY_NUM);
|
|
|
|
if (res1 > res0) {
|
|
res = 1;
|
|
}
|
|
else if (res1 == res0) {
|
|
/* Next, check the number of unique towns each animal's memories contains */
|
|
res0 = mNpc_GetAnimalMemoryLandKindNum(memories0, ANIMAL_MEMORY_NUM);
|
|
res1 = mNpc_GetAnimalMemoryLandKindNum(memories1, ANIMAL_MEMORY_NUM);
|
|
|
|
if (res1 > res0) {
|
|
res = 1;
|
|
}
|
|
else if (res1 == res0) {
|
|
/* Finally, check the total number of animals in town with the same personality and pick the one with more */
|
|
res0 = mNpc_GetSameLooksNum(a0->id.looks);
|
|
res1 = mNpc_GetSameLooksNum(a1->id.looks);
|
|
|
|
if (res1 < res0) {
|
|
res = 1;
|
|
}
|
|
else {
|
|
res = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetGoodbyAnimalIdx(int ignored_idx) {
|
|
Animal_c* animal;
|
|
Animal_c* other_animal = NULL;
|
|
int res0;
|
|
int res1;
|
|
int selected;
|
|
u16 remove_bitfield;
|
|
int candidate_num;
|
|
int goodby_animal_idx;
|
|
int i;
|
|
|
|
animal = Save_Get(animals);
|
|
remove_bitfield = 0;
|
|
candidate_num = 0;
|
|
goodby_animal_idx = -1;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (
|
|
i != Save_Get(remove_animal_idx) &&
|
|
((ignored_idx >= 0 && ignored_idx < ANIMAL_NUM_MAX && i != ignored_idx) || ignored_idx == -1) &&
|
|
mNpc_CheckFreeAnimalInfo(animal) == FALSE
|
|
) {
|
|
if (other_animal != NULL) {
|
|
/* Compare remove experience first (unused feature) */
|
|
if (mNpc_CheckRemoveExp(animal) == 0) {
|
|
if (mNpc_CheckRemoveExp(other_animal) != 0) {
|
|
candidate_num = 1;
|
|
remove_bitfield = (1 << i);
|
|
other_animal = animal;
|
|
goodby_animal_idx = i;
|
|
}
|
|
else {
|
|
/* Compare total memories in each animal */
|
|
res1 = mNpc_CheckGoodbyAnimalMemoryNum(animal, other_animal);
|
|
switch (res1) {
|
|
case -1:
|
|
candidate_num++;
|
|
remove_bitfield |= (1 << i);
|
|
break;
|
|
case 1:
|
|
candidate_num = 1;
|
|
remove_bitfield = (1 << i);
|
|
other_animal = animal;
|
|
goodby_animal_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (mNpc_CheckRemoveExp(other_animal) != 0) {
|
|
/* NOTE: this path can never be taken due to remove experiences never being set */
|
|
/* Compare times removed (unused feature) */
|
|
res0 = mNpc_GetRemoveTime(animal);
|
|
res1 = mNpc_GetRemoveTime(other_animal);
|
|
if (res1 > res0) {
|
|
candidate_num = 1;
|
|
remove_bitfield = (1 << i);
|
|
other_animal = animal;
|
|
goodby_animal_idx = i;
|
|
}
|
|
else if (res1 == res0) {
|
|
/* Compare total memories in each animal */
|
|
res1 = mNpc_CheckGoodbyAnimalMemoryNum(animal, other_animal);
|
|
switch (res1) {
|
|
case -1:
|
|
candidate_num++;
|
|
remove_bitfield |= (1 << i);
|
|
break;
|
|
case 1:
|
|
candidate_num = 1;
|
|
remove_bitfield = (1 << i);
|
|
other_animal = animal;
|
|
goodby_animal_idx = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
candidate_num = 1;
|
|
remove_bitfield = (1 << i);
|
|
other_animal = animal;
|
|
goodby_animal_idx = i;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
|
|
animal = Save_Get(animals);
|
|
if (candidate_num > 1) {
|
|
selected = RANDOM(candidate_num);
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (((remove_bitfield >> i) & 1) == 1) {
|
|
if (selected == 0) {
|
|
goodby_animal_idx = i;
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
return goodby_animal_idx;
|
|
}
|
|
|
|
typedef struct animal_goodbye_mail_s {
|
|
AnmPersonalID_c id;
|
|
u8 deliver_to_bitfield;
|
|
} Anm_GoodbyMail_c;
|
|
|
|
static Anm_GoodbyMail_c l_mnpc_goodby_mail;
|
|
|
|
static void mNpc_ClearGoodbyMail(Anm_GoodbyMail_c* goodby_mail) {
|
|
mNpc_ClearAnimalPersonalID(&goodby_mail->id);
|
|
goodby_mail->deliver_to_bitfield = 0;
|
|
}
|
|
|
|
extern void mNpc_FirstClearGoodbyMail() {
|
|
mNpc_ClearGoodbyMail(&l_mnpc_goodby_mail);
|
|
}
|
|
|
|
static void mNpc_SetGoodbyAnimalMail(Anm_GoodbyMail_c* goodby_mail, AnmPersonalID_c* anm_id) {
|
|
Private_c* priv = Save_Get(private);
|
|
int i;
|
|
|
|
if (anm_id != NULL && mNpc_CheckFreeAnimalPersonalID(anm_id) == FALSE) {
|
|
mNpc_ClearGoodbyMail(goodby_mail);
|
|
mNpc_CopyAnimalPersonalID(&goodby_mail->id, anm_id);
|
|
|
|
for (i = 0; i < PLAYER_NUM; i++) {
|
|
if (mPr_NullCheckPersonalID(&priv->player_ID) == FALSE) {
|
|
goodby_mail->deliver_to_bitfield |= (1 << i);
|
|
}
|
|
|
|
priv++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mNpc_SetGoodbyMailData(Mail_c* mail, PersonalID_c* pid, AnmPersonalID_c* anm_id) {
|
|
static u8 animal_name[ANIMAL_NAME_LEN];
|
|
|
|
u8 looks = anm_id->looks;
|
|
int mail_no;
|
|
int res = FALSE;
|
|
|
|
if (looks < mNpc_LOOKS_NUM) {
|
|
mail_no = 0x20E + looks * 3;
|
|
mail_no += mQst_GetRandom(3);
|
|
mNpc_GetNpcWorldNameAnm(animal_name, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, animal_name, ANIMAL_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR3, Save_Get(land_info).name, LAND_NAME_SIZE);
|
|
mNpc_LoadMailDataCommon2(mail, pid, anm_id, EMPTY_NO, mNpc_GetPaperType(), mail_no);
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SendGoodbyAnimalMailOne(Mail_c* mail, PersonalID_c* pid, int player_no, Anm_GoodbyMail_c* goodby_mail) {
|
|
int free_space;
|
|
mHm_hs_c* home;
|
|
int res = FALSE;
|
|
|
|
home = Save_GetPointer(homes[mHS_get_arrange_idx(player_no)]);
|
|
|
|
if (mPr_NullCheckPersonalID(pid) == FALSE && mPr_CheckCmpPersonalID(pid, &home->ownerID) == TRUE) {
|
|
free_space = mMl_chk_mail_free_space(home->mailbox, HOME_MAILBOX_SIZE);
|
|
|
|
if (free_space != -1) {
|
|
if (mNpc_SetGoodbyMailData(mail, pid, &goodby_mail->id) == TRUE) {
|
|
mMl_copy_mail(home->mailbox + free_space, mail);
|
|
res = TRUE;
|
|
}
|
|
}
|
|
else if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE && mNpc_SetGoodbyMailData(mail, pid, &goodby_mail->id) == TRUE) {
|
|
res = mPO_receipt_proc(mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void mNpc_SendGoodbyAnimalMail(Anm_GoodbyMail_c* goodby_mail) {
|
|
Private_c* priv = Save_Get(private);
|
|
int i;
|
|
|
|
if (mNpc_CheckFreeAnimalPersonalID(&goodby_mail->id) == FALSE) {
|
|
for (i = 0; i < PLAYER_NUM; i++) {
|
|
if (((goodby_mail->deliver_to_bitfield >> i) & 1) == 1) {
|
|
mMl_clear_mail(&l_npc_mail);
|
|
|
|
if (mNpc_SendGoodbyAnimalMailOne(&l_npc_mail, &priv->player_ID, i, goodby_mail) == TRUE) {
|
|
goodby_mail->deliver_to_bitfield &= ~(1 << i); // player received goodbye letter
|
|
}
|
|
}
|
|
|
|
priv++;
|
|
}
|
|
|
|
if (goodby_mail->deliver_to_bitfield == 0) {
|
|
mNpc_ClearGoodbyMail(goodby_mail); // all players received goodbye letter
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SendRegisteredGoodbyMail() {
|
|
mNpc_SendGoodbyAnimalMail(&l_mnpc_goodby_mail);
|
|
}
|
|
|
|
extern void mNpc_GetRemoveAnimal(Animal_c* transferring_animal, int moving_out) {
|
|
Private_c* priv = Common_Get(now_private);
|
|
u8* now_npc_max = Save_GetPointer(now_npc_max);
|
|
int new_animal;
|
|
Animal_c* animal2;
|
|
Animal_c* animal = Save_Get(animals);
|
|
int free_animal_idx;
|
|
u8* remove_animal_idx = Save_GetPointer(remove_animal_idx);
|
|
int bruh;
|
|
int i;
|
|
|
|
new_animal = TRUE;
|
|
|
|
if (mLd_PlayerManKindCheck() == FALSE) {
|
|
if (moving_out == TRUE) {
|
|
if (*remove_animal_idx != 0xFF) {
|
|
animal2 = Save_GetPointer(animals[*remove_animal_idx]);
|
|
animal2->removing = FALSE;
|
|
mNpc_CopyAnimalInfo(transferring_animal, animal2);
|
|
mNpc_DestroyHouse(&animal2->home_info);
|
|
mNpc_ClearAnimalInfo(animal2);
|
|
now_npc_max[0]--;
|
|
*remove_animal_idx = 0xFF;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (mNpc_CheckFreeAnimalInfo(transferring_animal) == FALSE && transferring_animal->removing == FALSE) {
|
|
mActor_name_t* summercamper_id;
|
|
|
|
/* The incoming animal cannot be present in the town already */
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (transferring_animal->id.npc_id == animal->id.npc_id) {
|
|
new_animal = FALSE;
|
|
break;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
|
|
/* The last animal who left the town cannot be the next animal to transfer in */
|
|
if (transferring_animal->id.npc_id == Save_Get(last_removed_animal_id).npc_id) {
|
|
new_animal = FALSE;
|
|
}
|
|
|
|
/* Ensure that the animal moving in is not the same one that's currently visiting as a summer camper */
|
|
summercamper_id = (mActor_name_t*)mEv_get_save_area(mEv_EVENT_SUMMER_CAMPER, 0);
|
|
if (summercamper_id != NULL) {
|
|
if (ITEM_NAME_GET_TYPE(summercamper_id[0]) == NAME_TYPE_NPC) {
|
|
mActor_name_t id = transferring_animal->id.npc_id;
|
|
|
|
if (id == summercamper_id[0]) {
|
|
new_animal = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* The new animal was unintialized or was scheduled to move out */
|
|
new_animal = FALSE;
|
|
}
|
|
|
|
if (new_animal == TRUE) {
|
|
animal = Save_Get(animals);
|
|
|
|
/* Try to find an unused slot in the animal data */
|
|
free_animal_idx = mNpc_GetFreeAnimalInfo(Save_Get(animals), ANIMAL_NUM_MAX);
|
|
|
|
if (free_animal_idx == -1) {
|
|
/* No slot, so we're going to replace a villager from in town */
|
|
free_animal_idx = mNpc_GetGoodbyAnimalIdx(-1);
|
|
|
|
if (free_animal_idx == -1) {
|
|
/* Final fallback, delete the first villager */
|
|
free_animal_idx = 0;
|
|
}
|
|
|
|
animal += free_animal_idx;
|
|
mNpc_DestroyHouse(&animal->home_info);
|
|
mNpc_SetGoodbyAnimalMail(&l_mnpc_goodby_mail, &animal->id);
|
|
mNpc_SendRegisteredGoodbyMail();
|
|
}
|
|
else {
|
|
animal += free_animal_idx;
|
|
mNpc_AddNowNpcMax(now_npc_max);
|
|
}
|
|
|
|
mNpc_ClearAnimalInfo(animal);
|
|
mNpc_CopyAnimalInfo(animal, transferring_animal);
|
|
animal->home_info.block_x = 0xFF;
|
|
animal->home_info.block_z = 0xFF;
|
|
animal->home_info.ut_x = 0xFF;
|
|
animal->home_info.ut_z = 0xFF;
|
|
mLd_CopyLandName(animal->anmuni.previous_land_name, Common_Get(now_private)->player_ID.land_name);
|
|
animal->previous_land_id = Common_Get(now_private)->player_ID.land_id;
|
|
mQst_ClearContest(&animal->contest_quest);
|
|
mNpc_ClearHPMail(animal->hp_mail, ANIMAL_HP_MAIL_NUM);
|
|
transferring_animal->removing = TRUE;
|
|
priv->animal_memory.npc_id = animal->id.npc_id;
|
|
mLd_CopyLandName(priv->animal_memory.land_name, mLd_GetLandName());
|
|
mNpc_SetHaveAppeared(animal->id.npc_id);
|
|
mNpc_ResetAnimalRelation(free_animal_idx);
|
|
mNpc_RenewRemoveHistory();
|
|
}
|
|
}
|
|
}
|
|
|
|
static int mNpc_CheckBuildHouse(int bx, int bz, u8 ut_x, u8 ut_z) {
|
|
int res = FALSE;
|
|
|
|
if (bx >= 0 && bx < FG_BLOCK_X_NUM && bz >= 0 && bz < FG_BLOCK_Z_NUM && ut_x < UT_X_NUM && ut_z < UT_Z_NUM) {
|
|
mActor_name_t item = Save_Get(fg[bz][bx]).items[ut_z][ut_x];
|
|
|
|
if (mNT_IS_RESERVE(item)) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_SetReturnAnimal(Animal_c* return_animal) {
|
|
Anmhome_c* home_info;
|
|
Animal_c* animals = Save_Get(animals);
|
|
Animal_c* found_animal = NULL;
|
|
u8* now_npc_max = Save_GetPointer(now_npc_max);
|
|
Anmret_c* return_animal_p = Save_GetPointer(return_animal);
|
|
int found_animal_idx;
|
|
int ignored_idx = -1;
|
|
|
|
if (mLd_PlayerManKindCheck() == FALSE && mNpc_CheckFreeAnimalInfo(return_animal) == FALSE) {
|
|
if (return_animal->removing == FALSE) {
|
|
found_animal_idx = mNpc_SearchAnimalinfo(animals, return_animal->id.npc_id, ANIMAL_NUM_MAX);
|
|
|
|
if (found_animal_idx != -1) {
|
|
found_animal = &animals[found_animal_idx];
|
|
mNpc_DestroyHouse(&found_animal->home_info);
|
|
(*now_npc_max)--;
|
|
|
|
if (Save_Get(remove_animal_idx) == found_animal_idx) {
|
|
Save_Set(remove_animal_idx, 0xFF);
|
|
}
|
|
}
|
|
else if ((*now_npc_max) >= ANIMAL_NUM_MAX) {
|
|
if (Save_Get(scene_no) == SCENE_PLAYERSELECT_2) {
|
|
/* Don't remove the animal we just saw on the player select screen */
|
|
ignored_idx = Common_Get(player_select_animal_no);
|
|
}
|
|
|
|
found_animal_idx = mNpc_GetGoodbyAnimalIdx(ignored_idx);
|
|
|
|
if (found_animal_idx == -1) {
|
|
found_animal_idx = 0; // fallback
|
|
}
|
|
|
|
found_animal = &animals[found_animal_idx];
|
|
mNpc_SetGoodbyAnimalMail(&l_mnpc_goodby_mail, &found_animal->id);
|
|
mNpc_DestroyHouse(&found_animal->home_info);
|
|
(*now_npc_max)--;
|
|
|
|
if (Save_Get(remove_animal_idx) == found_animal_idx) {
|
|
Save_Set(remove_animal_idx, 0xFF);
|
|
}
|
|
}
|
|
else {
|
|
found_animal_idx = mNpc_GetFreeAnimalInfo(animals, ANIMAL_NUM_MAX);
|
|
|
|
if (found_animal_idx != -1) {
|
|
found_animal = &animals[found_animal_idx];
|
|
}
|
|
}
|
|
|
|
if (found_animal != NULL) {
|
|
mNpc_ClearAnimalInfo(found_animal);
|
|
mNpc_CopyAnimalInfo(found_animal, return_animal);
|
|
mNpc_ResetAnimalRelation(found_animal_idx);
|
|
home_info = &found_animal->home_info;
|
|
|
|
if (
|
|
mNpc_CheckBuildHouse(
|
|
home_info->block_x - 1, home_info->block_z - 1,
|
|
home_info->ut_x, home_info->ut_z - 1
|
|
) == TRUE) {
|
|
mNpc_BuildHouseBeforeFieldct(
|
|
found_animal->id.npc_id,
|
|
home_info->block_x - 1, home_info->block_z - 1,
|
|
home_info->ut_x, home_info->ut_z - 1
|
|
);
|
|
}
|
|
else {
|
|
home_info->block_x = 0xFF;
|
|
home_info->block_z = 0xFF;
|
|
home_info->ut_x = 0xFF;
|
|
home_info->ut_z = 0xFF;
|
|
}
|
|
|
|
(*now_npc_max)++;
|
|
|
|
if ((*now_npc_max) > ANIMAL_NUM_MAX) {
|
|
(*now_npc_max) = ANIMAL_NUM_MAX;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
return_animal_p->npc_id = return_animal->id.npc_id;
|
|
return_animal_p->talk_bit = FALSE;
|
|
return_animal_p->exist = FALSE;
|
|
lbRTC_TimeCopy(&return_animal_p->renew_time, Common_GetPointer(time.rtc_time));
|
|
mNpc_CopyAnimalPersonalID(Save_GetPointer(last_removed_animal_id), &return_animal->id);
|
|
}
|
|
|
|
mNpc_ClearAnimalInfo(return_animal);
|
|
}
|
|
}
|
|
|
|
extern void mNpc_AddActor_inBlock(mFM_move_actor_c* move_actor_list, u8 bx, u8 bz) {
|
|
mNpc_AddNpc_inBlock(move_actor_list, bx, bz);
|
|
}
|
|
|
|
static void mNpc_SetNpcNameID(Animal_c* animal, int num) {
|
|
int i;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (animal != NULL && ITEM_NAME_GET_TYPE(animal->id.npc_id) == NAME_TYPE_NPC) {
|
|
animal->id.name_id = (u8)animal->id.npc_id;
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_LoadNpcNameString(u8* dst, u8 name_id) {
|
|
static u8 dma_area[64] ATTRIBUTE_ALIGN(32);
|
|
u32 addr;
|
|
u32 ofs;
|
|
u32 base_addr;
|
|
|
|
mem_clear(dma_area, sizeof(dma_area), CHAR_SPACE);
|
|
addr = JW_GetAramAddress(RESOURCE_NPC_NAME_STR_TABLE);
|
|
|
|
if (name_id < 0xFF && dst != NULL && addr != 0) {
|
|
ofs = (name_id) * ANIMAL_NAME_LEN; /* offset into dma table */
|
|
base_addr = ALIGN_PREV(ofs, 32); /* align it to the nearest 32 bytes for DMA */
|
|
|
|
_JW_GetResourceAram(base_addr + addr, dma_area, sizeof(dma_area)); // DMA transfer from ARAM
|
|
bcopy(dma_area + (ofs - base_addr), dst, ANIMAL_NAME_LEN); // perform final copy
|
|
}
|
|
}
|
|
|
|
static u8* mNpc_GetNpcWorldNamePTableNo(mActor_name_t npc_id) {
|
|
static u8 npc_name[ANIMAL_NAME_LEN];
|
|
|
|
u8* world_name_p = NULL;
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC && (u8)(npc_id & 0xFF) < 0xFF) {
|
|
mNpc_LoadNpcNameString(npc_name, npc_id & 0xFF);
|
|
world_name_p = npc_name;
|
|
}
|
|
|
|
return world_name_p;
|
|
}
|
|
|
|
extern void mNpc_GetNpcWorldNameTableNo(u8* dst, mActor_name_t npc_id) {
|
|
u8* world_name_p = mNpc_GetNpcWorldNamePTableNo(npc_id);
|
|
|
|
if (world_name_p == NULL) {
|
|
world_name_p = l_no_name_npc_name;
|
|
}
|
|
|
|
mPr_CopyPlayerName(dst, world_name_p);
|
|
}
|
|
|
|
typedef struct npc_name_cache_s {
|
|
mActor_name_t npc_id;
|
|
u8 name[ANIMAL_NAME_LEN];
|
|
u8 npc_type; // 0? = Special NPC, 1 = Regular NPC
|
|
} mNpc_NameCache_c;
|
|
|
|
static mNpc_NameCache_c l_npc_name_cache;
|
|
|
|
extern void mNpc_ClearCacheName() {
|
|
mPr_ClearPlayerName(l_npc_name_cache.name);
|
|
l_npc_name_cache.npc_id = RSV_NO;
|
|
l_npc_name_cache.npc_type = 0;
|
|
}
|
|
|
|
static int mNpc_GetCacheName(u8* dst, mActor_name_t npc_id, u8 npc_type) {
|
|
int res = FALSE;
|
|
|
|
if (npc_id == l_npc_name_cache.npc_id && dst != NULL && npc_type == l_npc_name_cache.npc_type) {
|
|
mPr_CopyPlayerName(dst, l_npc_name_cache.name);
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void mNpc_SetCacheName(u8* name, mActor_name_t npc_id, u8 npc_type) {
|
|
mNpc_NameCache_c* name_cache = &l_npc_name_cache;
|
|
|
|
if (name != NULL) {
|
|
mPr_CopyPlayerName(name_cache->name, name);
|
|
name_cache->npc_id = npc_id;
|
|
name_cache->npc_type = npc_type;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_GetNpcWorldNameAnm(u8* dst, AnmPersonalID_c* anm_id) {
|
|
u8 animal_name[ANIMAL_NAME_LEN];
|
|
u8* name = l_no_name_npc_name;
|
|
|
|
if (anm_id != NULL) {
|
|
if (anm_id->npc_id != RSV_NO && mNpc_GetCacheName(animal_name, anm_id->npc_id, mNpc_NAME_TYPE_NPC) != FALSE) {
|
|
name = animal_name;
|
|
}
|
|
else if (ITEM_NAME_GET_TYPE(anm_id->npc_id) == NAME_TYPE_NPC) {
|
|
mNpc_GetNpcWorldNameTableNo(animal_name, anm_id->npc_id);
|
|
name = animal_name;
|
|
mNpc_SetCacheName(name, anm_id->npc_id, mNpc_NAME_TYPE_NPC);
|
|
}
|
|
}
|
|
|
|
mPr_CopyPlayerName(dst, name);
|
|
}
|
|
|
|
static u8* mNpc_GetActorWorldNameP(mActor_name_t npc_id) {
|
|
static u8 load_name[ANIMAL_NAME_LEN];
|
|
|
|
mNpc_Sp_Npc_Name_c* sp_npc_name = l_sp_actor_name;
|
|
u8* name_p = NULL;
|
|
int i;
|
|
|
|
if (npc_id != RSV_NO && mNpc_GetCacheName(load_name, npc_id, mNpc_NAME_TYPE_NPC) != FALSE) {
|
|
name_p = load_name;
|
|
}
|
|
else if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
mNpc_GetNpcWorldNameTableNo(load_name, npc_id);
|
|
name_p = load_name;
|
|
mNpc_SetCacheName(load_name, npc_id, mNpc_NAME_TYPE_NPC);
|
|
}
|
|
else {
|
|
for (i = 0; i < ARRAY_COUNT(l_sp_actor_name); i++) {
|
|
if (npc_id == sp_npc_name->sp_npc_id) {
|
|
mString_Load_StringFromRom(load_name, ANIMAL_NAME_LEN, sp_npc_name->name_str_no);
|
|
name_p = load_name;
|
|
mNpc_SetCacheName(load_name, npc_id, mNpc_NAME_TYPE_NPC);
|
|
|
|
break;
|
|
}
|
|
|
|
sp_npc_name++;
|
|
}
|
|
}
|
|
|
|
return name_p;
|
|
}
|
|
|
|
extern void mNpc_GetActorWorldName(u8* dst, mActor_name_t npc_id) {
|
|
u8* name_p = mNpc_GetActorWorldNameP(npc_id);
|
|
|
|
if (name_p == NULL) {
|
|
name_p = l_no_name_npc_name;
|
|
}
|
|
|
|
mPr_CopyPlayerName(dst, name_p);
|
|
}
|
|
|
|
extern u8* mNpc_GetNpcWorldNameP(mActor_name_t npc_id) {
|
|
u8* name_p = mNpc_GetNpcWorldNamePTableNo(npc_id);
|
|
|
|
if (name_p == NULL) {
|
|
name_p = mNpc_GetActorWorldNameP(npc_id);
|
|
|
|
if (name_p == NULL) {
|
|
name_p = l_no_name_npc_name;
|
|
}
|
|
}
|
|
|
|
return name_p;
|
|
}
|
|
|
|
extern void mNpc_GetNpcWorldName(u8* dst, ACTOR* actor) {
|
|
u8 npc_name[ANIMAL_NAME_LEN];
|
|
u8* name_p = l_no_name_npc_name;
|
|
|
|
if (dst != NULL && actor != NULL) {
|
|
if (actor->part == ACTOR_PART_NPC) {
|
|
NPC_ACTOR* npc = (NPC_ACTOR*)actor;
|
|
Animal_c* animal = npc->npc_info.animal;
|
|
|
|
if (animal != NULL) {
|
|
mNpc_GetNpcWorldNameAnm(npc_name, &animal->id);
|
|
}
|
|
else {
|
|
mActor_name_t npc_id;
|
|
mNpc_EventNpc_c* event_npc;
|
|
mNpc_MaskNpc_c* mask_npc;
|
|
|
|
event_npc = mNpc_GetSameEventNpc(actor->npc_id);
|
|
|
|
if (event_npc != NULL) {
|
|
npc_id = event_npc->npc_id;
|
|
}
|
|
else {
|
|
mask_npc = mNpc_GetSameMaskNpc(actor->npc_id);
|
|
|
|
if (mask_npc != NULL) {
|
|
npc_id = mask_npc->npc_id;
|
|
}
|
|
else {
|
|
npc_id = actor->npc_id;
|
|
}
|
|
}
|
|
|
|
if (npc_id == SP_NPC_EV_GHOST) {
|
|
u8 arg = actor->actor_specific;
|
|
|
|
if (mNpc_GetCacheName(npc_name, npc_id, arg) == FALSE) {
|
|
if (arg != 1) {
|
|
/* Load the ?????? name */
|
|
mString_Load_StringFromRom(npc_name, ANIMAL_NAME_LEN, 0x6D7);
|
|
mNpc_SetCacheName(npc_name, npc_id, arg);
|
|
}
|
|
else {
|
|
mNpc_GetActorWorldName(npc_name, npc_id);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
mNpc_GetActorWorldName(npc_name, npc_id);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
mNpc_GetActorWorldName(npc_name, actor->npc_id);
|
|
}
|
|
|
|
name_p = npc_name;
|
|
}
|
|
|
|
mPr_CopyPlayerName(dst, name_p);
|
|
}
|
|
|
|
extern void mNpc_GetRandomAnimalName(u8* dst) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
Animal_c* animal_p = animal;
|
|
u16 candidate_bitfield = 0;
|
|
int candidate_num = 0;
|
|
int i;
|
|
|
|
if (dst != NULL) {
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal_p) == FALSE) {
|
|
candidate_bitfield |= (1 << i);
|
|
candidate_num++;
|
|
}
|
|
|
|
animal_p++;
|
|
}
|
|
|
|
if (candidate_num > 0) {
|
|
int selected = RANDOM(candidate_num);
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (((candidate_bitfield >> i) & 1) == 1) {
|
|
if (selected == 0) {
|
|
mNpc_GetNpcWorldNameAnm(dst, &animal->id);
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_GetAnimalPlateName(u8* dst, xyz_t wpos) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int bx;
|
|
int bz;
|
|
int ut_x;
|
|
int ut_z;
|
|
|
|
if (mFI_Wpos2BkandUtNuminBlock(&bx, &bz, &ut_x, &ut_z, wpos) == TRUE) {
|
|
if (mFI_CheckBlockKind_OR(bx, bz, mRF_BLOCKKIND_OCEAN) == TRUE) {
|
|
mNpc_GetNpcWorldNameAnm(dst, &Save_Get(island).animal.id);
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
ut_x++;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE) {
|
|
Anmhome_c* home = &animal->home_info;
|
|
|
|
if (
|
|
home->block_x == bx &&
|
|
home->block_z == bz &&
|
|
home->ut_x == ut_x &&
|
|
home->ut_z == ut_z
|
|
) {
|
|
mNpc_GetNpcWorldNameAnm(dst, &animal->id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetNpcLooks(ACTOR* actor) {
|
|
int looks = mNpc_LOOKS_BOY;
|
|
|
|
if (actor != NULL && actor->part == ACTOR_PART_NPC) {
|
|
NPC_ACTOR* npc = (NPC_ACTOR*)actor;
|
|
|
|
if (npc->npc_info.animal != NULL) {
|
|
looks = npc->npc_info.animal->id.looks;
|
|
}
|
|
}
|
|
|
|
return looks;
|
|
}
|
|
|
|
static int mNpc_GetActorSex(mActor_name_t npc_id) {
|
|
mNpc_Sp_Npc_Name_c* sp_name_info = l_sp_actor_name;
|
|
int sex = mPr_SEX_MALE;
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(l_sp_actor_name); i++) {
|
|
if (npc_id == sp_name_info->sp_npc_id) {
|
|
sex = sp_name_info->sex;
|
|
break;
|
|
}
|
|
|
|
sp_name_info++;
|
|
}
|
|
|
|
return sex;
|
|
}
|
|
|
|
extern int mNpc_GetLooks2Sex(int looks) {
|
|
int sex = mPr_SEX_OTHER;
|
|
|
|
if (looks == mNpc_LOOKS_GIRL || looks == mNpc_LOOKS_KO_GIRL || looks == mNpc_LOOKS_NANIWA_LADY) {
|
|
sex = mPr_SEX_FEMALE;
|
|
}
|
|
else if (looks == mNpc_LOOKS_BOY || looks == mNpc_LOOKS_SPORT_MAN || looks == mNpc_LOOKS_GRIM_MAN) {
|
|
sex = mPr_SEX_MALE;
|
|
}
|
|
|
|
return sex;
|
|
}
|
|
|
|
extern int mNpc_GetAnimalSex(Animal_c* animal) {
|
|
int sex = mPr_SEX_OTHER;
|
|
|
|
if (animal != NULL) {
|
|
sex = mNpc_GetLooks2Sex(animal->id.looks);
|
|
}
|
|
|
|
return sex;
|
|
}
|
|
|
|
extern int mNpc_GetNpcSex(ACTOR* actor) {
|
|
if (actor != NULL && actor->part == ACTOR_PART_NPC) {
|
|
NPC_ACTOR* npc = (NPC_ACTOR*)actor;
|
|
|
|
return mNpc_GetAnimalSex(npc->npc_info.animal);
|
|
}
|
|
|
|
|
|
return mNpc_GetActorSex(actor->npc_id);
|
|
}
|
|
|
|
static int mNpc_GetNpcSoundSpecNotAnimal(mActor_name_t npc_id) {
|
|
mNpc_Sp_Npc_Name_c* sp_name_info = l_sp_actor_name;
|
|
int sound_spec = 2;
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(l_sp_actor_name); i++) {
|
|
if (npc_id == sp_name_info->sp_npc_id) {
|
|
sound_spec = sp_name_info->sound_id;
|
|
break;
|
|
}
|
|
|
|
sp_name_info++;
|
|
}
|
|
|
|
return sound_spec;
|
|
}
|
|
|
|
extern int mNpc_GetNpcSoundSpec(ACTOR* actor) {
|
|
static int spec_table[mNpc_LOOKS_NUM] = {
|
|
4,
|
|
4,
|
|
2,
|
|
2,
|
|
3,
|
|
4
|
|
};
|
|
|
|
int sound_spec = 2;
|
|
|
|
if (actor != NULL) {
|
|
if (actor->part == ACTOR_PART_NPC) {
|
|
NPC_ACTOR* npc = (NPC_ACTOR*)actor;
|
|
|
|
if (npc->npc_info.animal != NULL) {
|
|
int looks = npc->npc_info.animal->id.looks;
|
|
|
|
if (looks >= mNpc_LOOKS_NUM) {
|
|
looks = mNpc_LOOKS_BOY;
|
|
}
|
|
|
|
sound_spec = spec_table[looks];
|
|
}
|
|
else {
|
|
sound_spec = mNpc_GetNpcSoundSpecNotAnimal(actor->npc_id);
|
|
}
|
|
}
|
|
else {
|
|
sound_spec = mNpc_GetNpcSoundSpecNotAnimal(actor->npc_id);
|
|
}
|
|
}
|
|
|
|
return sound_spec;
|
|
}
|
|
|
|
static void mNpc_InitRemoveHistory() {
|
|
lbRTC_ymd_c* ymd = Save_GetPointer(force_remove_date);
|
|
|
|
ymd->year = 0xFFFF;
|
|
ymd->month = 0xFF;
|
|
ymd->day = 0xFF;
|
|
}
|
|
|
|
extern void mNpc_InitNpcAllInfo(int malloc_flag) {
|
|
Animal_c* animals = Save_Get(animals);
|
|
|
|
Save_Set(now_npc_max, mNpc_LOOKS_NUM);
|
|
Save_Set(remove_animal_idx, 0xFF);
|
|
mNpc_InitRemoveHistory();
|
|
mNpc_ClearAnimalPersonalID(Save_GetPointer(last_removed_animal_id));
|
|
mNpc_ClearAnyAnimalInfo(animals, ANIMAL_NUM_MAX);
|
|
mNpc_DecideLivingNpcMax(animals, mNpc_LOOKS_NUM, malloc_flag);
|
|
mNpc_SetNpcNameID(animals, ANIMAL_NUM_MAX);
|
|
}
|
|
|
|
static int mNpc_CheckGrowFieldRank() {
|
|
static int npc_grow_prob[mFAs_FIELDRANK_NUM] = {
|
|
40,
|
|
50,
|
|
60,
|
|
70,
|
|
80,
|
|
90,
|
|
100
|
|
};
|
|
|
|
int rank = mFAs_GetFieldRank();
|
|
int res = FALSE;
|
|
|
|
if (rank >= 0 && rank < mFAs_FIELDRANK_NUM) {
|
|
if (RANDOM(100) < npc_grow_prob[rank]) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_CheckGrow() {
|
|
lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time);
|
|
lbRTC_time_c* last_grow_time = Save_GetPointer(last_grow_time);
|
|
int res = FALSE;
|
|
|
|
if (Save_Get(now_npc_max) < ANIMAL_NUM_MAX) {
|
|
if (lbRTC_IsEqualTime(&mTM_rtcTime_clear_code, last_grow_time, lbRTC_CHECK_ALL) == TRUE) {
|
|
lbRTC_TimeCopy(last_grow_time, rtc_time); // first initialization
|
|
}
|
|
else if (mNpc_CheckGrowFieldRank() == TRUE) {
|
|
u32 interval;
|
|
|
|
if (lbRTC_IsOverTime(last_grow_time, rtc_time) == lbRTC_OVER) {
|
|
interval = lbRTC_IntervalTime(rtc_time, last_grow_time);
|
|
}
|
|
else {
|
|
interval = lbRTC_IntervalTime(last_grow_time, rtc_time);
|
|
}
|
|
|
|
if (
|
|
interval >= (lbRTC_HOURS_PER_DAY * lbRTC_MINUTES_PER_HOUR) && /* Only one new villager per day */
|
|
mLd_PlayerManKindCheck() == FALSE && /* Player must be from the town being loaded */
|
|
mNpc_CheckFriendAllAnimal(&Common_Get(now_private)->player_ID) == TRUE /* The player loading must have talked to all current villagers */
|
|
) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static u8 mNpc_GetMinLooks(u8* min_looks_bitfield, int* min_looks_num) {
|
|
int min_looks_animals;
|
|
u8 min_looks;
|
|
u8 i;
|
|
|
|
min_looks_bitfield[0] = 0;
|
|
min_looks_num[0] = 0;
|
|
min_looks_animals = ANIMAL_NUM_MAX;
|
|
min_looks = mNpc_LOOKS_UNSET;
|
|
|
|
for (i = 0; i < mNpc_LOOKS_NUM; i++) {
|
|
int not_appear_num = mNpc_GetLooks2NotHaveAppearedNum(i);
|
|
|
|
if (not_appear_num > 0) {
|
|
int same_looks_num = mNpc_GetSameLooksNum(i);
|
|
|
|
if (min_looks_animals > same_looks_num) {
|
|
min_looks_animals = same_looks_num;
|
|
min_looks = i;
|
|
min_looks_bitfield[0] = 0;
|
|
min_looks_bitfield[0] |= (1 << i);
|
|
min_looks_num[0] = 1;
|
|
}
|
|
else if (min_looks_animals == same_looks_num) {
|
|
min_looks_bitfield[0] |= (1 << i);
|
|
min_looks_num[0]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (min_looks_bitfield[0] == 0 && min_looks_num[0] == 0) {
|
|
/* All animals have appeared before, so reset the list */
|
|
mNpc_ResetHaveAppeared_common(Save_Get(npc_used_tbl), Save_Get(animals));
|
|
|
|
for (i = 0; i < mNpc_LOOKS_NUM; i++) {
|
|
min_looks_bitfield[0] |= (1 << i);
|
|
min_looks_num[0]++;
|
|
}
|
|
}
|
|
|
|
if (min_looks_num[0] > 1) {
|
|
min_looks = mNpc_LOOKS_UNSET;
|
|
}
|
|
|
|
return min_looks;
|
|
}
|
|
|
|
static int mNpc_GetMinSex() {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int males = 0;
|
|
int females = 0;
|
|
int res;
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE) {
|
|
switch (mNpc_GetAnimalSex(animal)) {
|
|
case mPr_SEX_MALE:
|
|
males++;
|
|
break;
|
|
case mPr_SEX_FEMALE:
|
|
females++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
|
|
/* TODO: is this bugged? Shouldn't this check be inverted? */
|
|
if (females < males) {
|
|
res = mPr_SEX_MALE;
|
|
}
|
|
else {
|
|
res = mPr_SEX_FEMALE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_GrowLooksNpcIdx(u8 looks) {
|
|
static u8 candidate_table[ALIGN_NEXT(NPC_NUM, 8) / 8];
|
|
|
|
u8* looks_table = npc_looks_table;
|
|
s8* grow_list = npc_grow_list;
|
|
int npc_idx;
|
|
int candidates;
|
|
int res;
|
|
int i;
|
|
int j;
|
|
int selected;
|
|
|
|
npc_idx = 0;
|
|
candidates = 0;
|
|
res = -1;
|
|
|
|
if (looks < mNpc_LOOKS_UNSET) {
|
|
bzero(candidate_table, sizeof(candidate_table));
|
|
|
|
for (i = 0; i < ARRAY_COUNT(candidate_table); i++) {
|
|
for (j = 0; j < 8; j++) {
|
|
if (looks == *looks_table) {
|
|
int grow_perm = mNpc_GetDefGrowPermission(npc_idx, grow_list, NPC_NUM);
|
|
|
|
if (
|
|
(mNpc_SearchAnimalinfo(Save_Get(animals), npc_idx | NPC_START, ANIMAL_NUM_MAX) == -1) &&
|
|
(mNpc_GetHaveAppeared_idx(npc_idx) == FALSE) &&
|
|
(grow_perm == mNpc_GROW_STARTER || grow_perm == mNpc_GROW_MOVE_IN)
|
|
) {
|
|
candidates++;
|
|
candidate_table[i] |= 1 << j;
|
|
}
|
|
}
|
|
|
|
npc_idx++;
|
|
looks_table++;
|
|
|
|
if (npc_idx >= NPC_NUM) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (npc_idx >= NPC_NUM) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (candidates > 0) {
|
|
selected = RANDOM(candidates);
|
|
npc_idx = 0;
|
|
|
|
for (i = 0; i < ARRAY_COUNT(candidate_table); i++) {
|
|
if (npc_idx >= NPC_NUM) {
|
|
break;
|
|
}
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
if (npc_idx >= NPC_NUM) {
|
|
break;
|
|
}
|
|
|
|
if (((candidate_table[i] >> j) & 1) == 1) {
|
|
if (selected <= 0) {
|
|
res = npc_idx;
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
npc_idx++;
|
|
}
|
|
|
|
if (res != -1) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void mNpc_SetAnimalInfoNpcIdx(Animal_c* animal, int idx) {
|
|
if (idx >= 0 && idx < NPC_NUM) {
|
|
mNpc_SetDefAnimalInfo(animal, NPC_START | idx, npc_looks_table[idx], &npc_def_list[idx]);
|
|
}
|
|
}
|
|
|
|
static int mNpc_SetGrowNpc(u8 looks) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int idx = mNpc_UseFreeAnimalInfo(animal, ANIMAL_NUM_MAX);
|
|
int npc_idx;
|
|
|
|
if (idx != -1) {
|
|
animal += idx;
|
|
npc_idx = mNpc_GrowLooksNpcIdx(looks);
|
|
|
|
if (npc_idx != -1) {
|
|
mNpc_SetAnimalInfoNpcIdx(animal, npc_idx);
|
|
mNpc_SetHaveAppeared(animal->id.npc_id);
|
|
animal->moved_in = TRUE;
|
|
}
|
|
else {
|
|
idx = -1;
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
extern void mNpc_Grow() {
|
|
int selected;
|
|
int grow_idx;
|
|
|
|
if (mNpc_CheckGrow() == TRUE) {
|
|
lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time);
|
|
lbRTC_time_c* last_grow_time = Save_GetPointer(last_grow_time);
|
|
u8 min_looks_bitfield;
|
|
int min_looks_num;
|
|
u8 min_looks;
|
|
u8 selected_looks;
|
|
|
|
mNpc_ResetHaveAppeared();
|
|
lbRTC_TimeCopy(last_grow_time, rtc_time);
|
|
|
|
min_looks = mNpc_GetMinLooks(&min_looks_bitfield, &min_looks_num);
|
|
selected_looks = min_looks;
|
|
|
|
/* Multiple personalities are tied for minimum */
|
|
if (min_looks == mNpc_LOOKS_UNSET) {
|
|
int min_sex = mNpc_GetMinSex();
|
|
u8 min_looks_bitfield_save;
|
|
int min_looks_num_save;
|
|
u8 looks;
|
|
|
|
if (min_sex != mPr_SEX_OTHER) {
|
|
min_looks_bitfield_save = min_looks_bitfield;
|
|
min_looks_num_save = min_looks_num;
|
|
|
|
/* Clear any tied minimum personalities which are of the greater sex */
|
|
for (looks = 0; looks < mNpc_LOOKS_NUM; looks++) {
|
|
if (((min_looks_bitfield >> looks) & 1) == 1 && mNpc_GetLooks2Sex(looks) != min_sex) {
|
|
min_looks_bitfield &= ~(1 << looks);
|
|
min_looks_num--;
|
|
}
|
|
}
|
|
|
|
if (min_looks_num == 0 || min_looks_num == min_looks_num_save) {
|
|
/* Either all personalities were cleared or none were, so restore the prior state */
|
|
min_looks_bitfield = min_looks_bitfield_save;
|
|
min_looks_num = min_looks_num_save;
|
|
}
|
|
}
|
|
|
|
selected = RANDOM(min_looks_num);
|
|
|
|
for (looks = 0; looks < mNpc_LOOKS_NUM; looks++) {
|
|
if (((min_looks_bitfield >> looks) & 1) == 1) {
|
|
if (selected <= 0) {
|
|
selected_looks = looks;
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
grow_idx = mNpc_SetGrowNpc(selected_looks);
|
|
|
|
if (grow_idx >= 0 && grow_idx < ANIMAL_NUM_MAX) {
|
|
mNpc_SetNpcNameID(Save_GetPointer(animals[grow_idx]), 1);
|
|
mNpc_AddNowNpcMax(Save_GetPointer(now_npc_max));
|
|
mNpc_RenewRemoveHistory();
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_ForceRemove() {
|
|
lbRTC_time_c force_remove_time;
|
|
lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time);
|
|
lbRTC_ymd_c* force_remove_date = Save_GetPointer(force_remove_date);
|
|
Animal_c* animal = Save_Get(animals);
|
|
int interval;
|
|
int animal_num = mNpc_GetAnimalNum();
|
|
int ignored_idx = -1;
|
|
int idx;
|
|
|
|
if (
|
|
animal_num == ANIMAL_NUM_MAX &&
|
|
force_remove_date->year != 0xFFFF &&
|
|
force_remove_date->month != 0xFF &&
|
|
force_remove_date->day != 0xFF
|
|
) {
|
|
mTM_ymd_2_time(&force_remove_time, force_remove_date);
|
|
|
|
if (lbRTC_IsOverTime(&force_remove_time, rtc_time) == lbRTC_OVER) {
|
|
interval = lbRTC_GetIntervalDays(&force_remove_time, rtc_time);
|
|
}
|
|
else {
|
|
interval = lbRTC_GetIntervalDays(rtc_time, &force_remove_time);
|
|
}
|
|
|
|
|
|
if (interval >= mNpc_MINIMUM_DAYS_BEFORE_FORCE_REMOVAL) {
|
|
if (Save_Get(scene_no) == SCENE_PLAYERSELECT_2) {
|
|
ignored_idx = Common_Get(player_select_animal_no);
|
|
}
|
|
|
|
idx = mNpc_GetGoodbyAnimalIdx(ignored_idx);
|
|
|
|
if (idx != -1) {
|
|
animal += idx;
|
|
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE) {
|
|
mNpc_DestroyHouse(&animal->home_info);
|
|
mNpc_SetGoodbyAnimalMail(&l_mnpc_goodby_mail, &animal->id);
|
|
mNpc_SendRegisteredGoodbyMail();
|
|
mNpc_ClearAnimalInfo(animal);
|
|
mNpc_SubNowNpcMax(Save_GetPointer(now_npc_max));
|
|
mNpc_RenewRemoveHistory();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_DecideMaskNpc_summercamp(mActor_name_t* npc_id) {
|
|
static int looks_table[mNpc_LOOKS_NUM];
|
|
|
|
u8 looks;
|
|
int i;
|
|
int res = FALSE;
|
|
|
|
if (npc_id != NULL) {
|
|
npc_id[0] = EMPTY_NO;
|
|
mNpc_ResetHaveAppeared();
|
|
mNpc_MakeRandTable(looks_table, mNpc_LOOKS_NUM, NPC_NUM); // why are we swapping this 'NPC_NUM' times???
|
|
|
|
for (i = 0; i < mNpc_LOOKS_NUM; i++) {
|
|
looks = looks_table[i];
|
|
|
|
if (mNpc_GetLooks2NotHaveAppearedNum(looks) > 0) {
|
|
int idx = mNpc_GrowLooksNpcIdx(looks);
|
|
|
|
if (idx != -1) {
|
|
npc_id[0] = NPC_START | idx;
|
|
/* This makes summer campers impossible to appear by 'natural growth' in your town
|
|
unless you've seen every non-islander in your town as a villager or summer camper... */
|
|
mNpc_SetHaveAppeared(npc_id[0]);
|
|
res = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_RegistMaskNpc_summercamp(mActor_name_t mask_id, mActor_name_t npc_id, mActor_name_t cloth_id) {
|
|
int free_mask_idx = mNpc_GetFreeMaskNpcIdx();
|
|
int res = FALSE;
|
|
|
|
if (free_mask_idx != -1) {
|
|
mNpc_MaskNpc_c* mask_npc = Common_GetPointer(mask_npc[free_mask_idx]);
|
|
|
|
if (cloth_id != EMPTY_NO) {
|
|
int valid_cloth = (cloth_id == RSV_CLOTH) || (cloth_id >= ITM_CLOTH_START && cloth_id < ITM_CLOTH_END);
|
|
|
|
if (valid_cloth == FALSE) {
|
|
cloth_id = ITM_CLOTH_START;
|
|
}
|
|
}
|
|
|
|
if (ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
mask_npc->mask_id = mask_id;
|
|
mask_npc->npc_id = npc_id;
|
|
mask_npc->exists = FALSE;
|
|
mask_npc->in_use = TRUE;
|
|
mNpc_ClearAnimalInfo(&mask_npc->animal_data);
|
|
mNpc_SetDefAnimal(&mask_npc->animal_data, npc_id, npc_def_list);
|
|
|
|
if (cloth_id == EMPTY_NO) {
|
|
mask_npc->cloth_id = mask_npc->animal_data.cloth;
|
|
}
|
|
else {
|
|
mask_npc->cloth_id = cloth_id;
|
|
}
|
|
|
|
if (Common_Get(npc_is_summercamper) == TRUE) {
|
|
mNpc_ClearAnimalMemory(mask_npc->animal_data.memories, 1);
|
|
mNpc_SetAnimalMemory(&Common_Get(now_private)->player_ID, &mask_npc->animal_data.id, &mask_npc->animal_data.memories[0]);
|
|
}
|
|
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckNpcSet_fgcol(mActor_name_t fg_item, u32 attribute) {
|
|
int res = FALSE;
|
|
|
|
if (mFI_CheckFGNpcOn(fg_item)) {
|
|
res = mCoBG_Attr2CheckPlaceNpc(attribute);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_CheckNpcSet_fgcol_hard(mActor_name_t fg_item, u32 attribute) {
|
|
int res = FALSE;
|
|
|
|
if (mFI_CheckFGNpcOn(fg_item) && (attribute <= mCoBG_ATTRIBUTE_GRASS2 || (attribute >= mCoBG_ATTRIBUTE_SOIL0 && attribute <= mCoBG_ATTRIBUTE_SOIL2) || attribute == mCoBG_ATTRIBUTE_STONE)) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckNpcSet(int bx, int bz, int ut_x, int ut_z) {
|
|
mCoBG_Collision_u* collision = mFI_GetBkNum2ColTop(bx, bz);
|
|
mActor_name_t* items = mFI_BkNumtoUtFGTop(bx, bz);
|
|
int res = FALSE;
|
|
|
|
if (collision != NULL && items != NULL) {
|
|
res = mNpc_CheckNpcSet_fgcol(items[ut_z * UT_X_NUM + ut_x], collision[ut_z * UT_X_NUM + ut_x].data.unit_attribute);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetMakeUtNuminBlock_hard_area(int* ut_x, int* ut_z, int bx, int bz, int restrict_area) {
|
|
int now_ut_x;
|
|
int now_ut_z;
|
|
mCoBG_Collision_u* col;
|
|
mCoBG_Collision_u* col_p;
|
|
mActor_name_t* items;
|
|
int min_ut_x;
|
|
int min_ut_z;
|
|
int res;
|
|
u32 center;
|
|
int i;
|
|
int j;
|
|
|
|
col = mFI_GetBkNum2ColTop(bx, bz);
|
|
items = mFI_BkNumtoUtFGTop(bx, bz);
|
|
min_ut_x = UT_X_NUM;
|
|
min_ut_z = UT_Z_NUM;
|
|
res = FALSE;
|
|
|
|
if (restrict_area < 0 || restrict_area > 9) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (items != NULL && col != NULL) {
|
|
for (i = restrict_area; i < UT_Z_NUM - restrict_area; i++) {
|
|
for (j = restrict_area; j < UT_X_NUM - restrict_area; j++) {
|
|
col_p = &col[i * UT_X_NUM + j];
|
|
|
|
if (mNpc_CheckNpcSet_fgcol_hard(items[i * UT_X_NUM + j], col_p->data.unit_attribute) == TRUE) {
|
|
center = col_p->data.center;
|
|
|
|
/* Check that the height of each point on the unit is equal*/
|
|
if (
|
|
(int)center == col_p->data.top_left &&
|
|
center == col_p->data.bot_left &&
|
|
center == col_p->data.top_right &&
|
|
(int)center == col_p->data.bot_right
|
|
) {
|
|
|
|
now_ut_x = 8 - j;
|
|
now_ut_z = 8 - i;
|
|
|
|
now_ut_x = ABS(now_ut_x);
|
|
now_ut_z = ABS(now_ut_z);
|
|
|
|
if (min_ut_x > now_ut_x && min_ut_z > now_ut_z) {
|
|
*ut_x = j;
|
|
*ut_z = i;
|
|
min_ut_x = now_ut_x;
|
|
min_ut_z = now_ut_z;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetMakeUtNuminBlock_area(int* ut_x, int* ut_z, int bx, int bz, int restrict_area) {
|
|
static u16 fg_flag[UT_Z_NUM];
|
|
|
|
mCoBG_Collision_u* col_top;
|
|
mActor_name_t* fg_top;
|
|
u16* fg_flag_p = fg_flag;
|
|
int candidates;
|
|
int res;
|
|
int i;
|
|
int j;
|
|
int rand;
|
|
|
|
col_top = mFI_GetBkNum2ColTop(bx, bz);
|
|
fg_top = mFI_BkNumtoUtFGTop(bx, bz);
|
|
candidates = 0;
|
|
res = FALSE;
|
|
|
|
if (restrict_area < 0 || restrict_area > 9) {
|
|
return FALSE;
|
|
}
|
|
|
|
bzero(fg_flag, sizeof(fg_flag));
|
|
|
|
if (fg_top != NULL && col_top != NULL) {
|
|
for (i = restrict_area; i < UT_Z_NUM - restrict_area; i++) {
|
|
for (j = restrict_area; j < UT_X_NUM - restrict_area; j++) {
|
|
if (mNpc_CheckNpcSet_fgcol(fg_top[i * UT_X_NUM + j], col_top[i * UT_X_NUM + j].data.unit_attribute) == TRUE) {
|
|
*fg_flag_p |= 1 << j;
|
|
candidates++;
|
|
}
|
|
}
|
|
|
|
fg_flag_p++;
|
|
}
|
|
}
|
|
|
|
fg_flag_p = fg_flag;
|
|
if (candidates > 0) {
|
|
rand = RANDOM(candidates);
|
|
|
|
for (i = restrict_area; i < UT_Z_NUM - restrict_area; i++) {
|
|
for (j = restrict_area; j < UT_X_NUM - restrict_area; j++) {
|
|
if (((*fg_flag_p >> j) & 1) == 1) {
|
|
if (rand == 0) {
|
|
*ut_x = j;
|
|
*ut_z = i;
|
|
res = TRUE;
|
|
|
|
break;
|
|
}
|
|
else {
|
|
rand--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (res == TRUE) {
|
|
break;
|
|
}
|
|
|
|
fg_flag_p++;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetMakeUtNuminBlock(int* ut_x, int* ut_z, int bx, int bz) {
|
|
return mNpc_GetMakeUtNuminBlock_area(ut_x, ut_z, bx, bz, 1); // can't spawn in the outer-most square of units
|
|
}
|
|
|
|
static int mNpc_CheckNpcSet_height(int bx, int bz, int ut_x, int ut_z, mActor_name_t item, mCoBG_Collision_u* col) {
|
|
int res = FALSE;
|
|
|
|
if (col != NULL && mNpc_CheckNpcSet_fgcol(item, col->data.unit_attribute)) {
|
|
xyz_t wpos;
|
|
|
|
mFI_BkandUtNum2Wpos(&wpos, bx, bz, ut_x, ut_z);
|
|
|
|
if (mCoBG_ExistHeightGap_KeepAndNow(wpos) == FALSE) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetMakeUtNuminBlock33(int* make_ut_x, int* make_ut_z, int ut_x, int ut_z, int bx, int bz) {
|
|
static int ut_xnum_table[9] = { 0, 1, -1, 0, 1, -1, 0, 1, -1 };
|
|
static int ut_znum_table[9] = { 0, 0, 0, 1, 1, 1, -1, -1, -1 };
|
|
|
|
mCoBG_Collision_u* col_top;
|
|
mActor_name_t* fg_top;
|
|
int res;
|
|
int x;
|
|
int z;
|
|
int idx;
|
|
int i;
|
|
|
|
col_top = mFI_GetBkNum2ColTop(bx, bz);
|
|
fg_top = mFI_BkNumtoUtFGTop(bx, bz);
|
|
res = FALSE;
|
|
|
|
if (col_top != NULL && fg_top != NULL) {
|
|
for (i = 0; i < 9; i++) {
|
|
x = ut_x + ut_xnum_table[i];
|
|
z = ut_z + ut_znum_table[i];
|
|
|
|
if (x >= 1 && x < (UT_X_NUM - 1) && z >= 1 && z < (UT_Z_NUM - 1)) {
|
|
idx = z * UT_X_NUM + x;
|
|
|
|
if (mNpc_CheckNpcSet_height(bx, bz, x, z, fg_top[idx], &col_top[idx]) == TRUE) {
|
|
make_ut_x[0] = x;
|
|
make_ut_z[0] = z;
|
|
res = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetMakeUtNuminBlock_hide_hard_area(int* ut_x, int* ut_z, int bx, int bz, int restrict_area) {
|
|
static u16 hide_ut_bit[UT_Z_NUM];
|
|
|
|
int now_ut_z;
|
|
int now_ut_x;
|
|
mCoBG_Collision_u* col_top;
|
|
mCoBG_Collision_u* col_p;
|
|
mActor_name_t* fg_top;
|
|
mActor_name_t* item_p;
|
|
u32 center;
|
|
int min_ut_x;
|
|
int min_ut_z;
|
|
int res;
|
|
int i;
|
|
int j;
|
|
|
|
col_top = mFI_GetBkNum2ColTop(bx, bz);
|
|
fg_top = mFI_BkNumtoUtFGTop(bx, bz);
|
|
min_ut_x = UT_X_NUM;
|
|
min_ut_z = UT_Z_NUM;
|
|
res = FALSE;
|
|
|
|
if (restrict_area < 0 || restrict_area > 9) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (fg_top != NULL && col_top != NULL) {
|
|
bzero(hide_ut_bit, sizeof(hide_ut_bit));
|
|
mAGrw_SetHideUtInfo(hide_ut_bit, fg_top);
|
|
|
|
for (i = restrict_area; i < UT_Z_NUM - restrict_area; i++) {
|
|
for (j = restrict_area; j < UT_X_NUM - restrict_area; j++) {
|
|
item_p = &fg_top[i * UT_X_NUM + j];
|
|
col_p = &col_top[i * UT_X_NUM + j];
|
|
|
|
if (mNpc_CheckNpcSet_fgcol_hard(item_p[0], col_p->data.unit_attribute) == TRUE) {
|
|
center = col_p->data.center;
|
|
|
|
/* Check that the height of each point on the unit is equal*/
|
|
if (
|
|
(int)center == col_p->data.top_left &&
|
|
center == col_p->data.bot_left &&
|
|
center == col_p->data.top_right &&
|
|
(int)center == col_p->data.bot_right &&
|
|
((hide_ut_bit[i] >> j) & 1) == 1
|
|
) {
|
|
|
|
now_ut_x = 8 - j;
|
|
now_ut_z = 8 - i;
|
|
|
|
now_ut_x = ABS(now_ut_x);
|
|
now_ut_z = ABS(now_ut_z);
|
|
|
|
if (min_ut_x > now_ut_x && min_ut_z > now_ut_z) {
|
|
*ut_x = j;
|
|
*ut_z = i;
|
|
min_ut_x = now_ut_x;
|
|
min_ut_z = now_ut_z;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
typedef struct npc_temper_s {
|
|
u16 unlock_timer;
|
|
u8 over_impatient_num;
|
|
u8 talk_num_max;
|
|
} mNpc_Temper_c;
|
|
|
|
typedef struct npc_talk_info_s {
|
|
u16 timer;
|
|
u8 talk_num;
|
|
u8 quest_request;
|
|
u16 unlock_timer;
|
|
u16 reset_timer;
|
|
} mNpc_Talk_Info_c;
|
|
|
|
static mNpc_Talk_Info_c l_npc_talk_info[ANIMAL_NUM_MAX + mISL_ISLANDER_NUM];
|
|
static mNpc_Temper_c l_npc_temper[mNpc_FEEL_NUM] = {
|
|
{ 4000, 12, 15 }, // mNpc_FEEL_NORMAL
|
|
{ 3000, 10, 13 }, // mNpc_FEEL_HAPPY
|
|
{ 4000, 12, 15 }, // mNpc_FEEL_ANGRY
|
|
{ 4000, 10, 13 }, // mNpc_FEEL_SAD
|
|
{ 5000, 9, 12 }, // mNpc_FEEL_SLEEPY
|
|
{ 5000, 9, 12 } // mNpc_FEEL_PITFALL
|
|
};
|
|
|
|
extern void mNpc_ClearTalkInfo() {
|
|
mNpc_Talk_Info_c* talk_info_p = l_npc_talk_info;
|
|
int i;
|
|
|
|
bzero(talk_info_p, sizeof(l_npc_talk_info));
|
|
|
|
for (i = 0; i < ARRAY_COUNT(l_npc_talk_info); i++) {
|
|
talk_info_p->quest_request = TRUE;
|
|
talk_info_p++;
|
|
}
|
|
}
|
|
|
|
static void mNpc_TimerCountDown(mNpc_Talk_Info_c* talk_info) {
|
|
if (talk_info->timer > 0) {
|
|
talk_info->timer--;
|
|
}
|
|
}
|
|
|
|
static void mNpc_SetUnlockTimer(u16* unlock_timer, u16* reset_timer, int feel) {
|
|
unlock_timer[0] = l_npc_temper[feel].unlock_timer;
|
|
reset_timer[0] = unlock_timer[0];
|
|
}
|
|
|
|
static int mNpc_CountTalkNum(int animal_idx, int feel) {
|
|
int res = FALSE;
|
|
|
|
if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info)) {
|
|
mNpc_Talk_Info_c* talk_info = &l_npc_talk_info[animal_idx];
|
|
|
|
if (talk_info->talk_num < l_npc_temper[feel].talk_num_max && talk_info->timer > 0) {
|
|
talk_info->talk_num++;
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckOverImpatient(int animal_idx, int feel) {
|
|
int res = FALSE;
|
|
|
|
if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info)) {
|
|
mNpc_Talk_Info_c* talk_info = &l_npc_talk_info[animal_idx];
|
|
|
|
if (talk_info->talk_num >= l_npc_temper[feel].over_impatient_num) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_GetOverImpatient(int animal_idx, int feel) {
|
|
int patience = mNpc_PATIENCE_NORMAL;
|
|
|
|
if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info)) {
|
|
mNpc_Talk_Info_c* talk_info = &l_npc_talk_info[animal_idx];
|
|
|
|
if (talk_info->talk_num >= l_npc_temper[feel].over_impatient_num) {
|
|
if (talk_info->talk_num >= l_npc_temper[feel].talk_num_max) {
|
|
patience = mNpc_PATIENCE_ANNOYED; // refuse to talk
|
|
}
|
|
else {
|
|
patience = mNpc_PATIENCE_MILDLY_ANNOYED; // dialog shows mild annoyance
|
|
}
|
|
}
|
|
}
|
|
|
|
return patience;
|
|
}
|
|
|
|
extern int mNpc_CheckQuestRequest(int animal_idx) {
|
|
int res = FALSE;
|
|
|
|
if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info)) {
|
|
mNpc_Talk_Info_c* talk_info = &l_npc_talk_info[animal_idx];
|
|
|
|
if (talk_info->quest_request == TRUE) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_SetQuestRequestOFF(int animal_idx, int feel) {
|
|
mNpc_Talk_Info_c* talk_info = &l_npc_talk_info[animal_idx];
|
|
|
|
if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info)) {
|
|
if (talk_info->quest_request == TRUE) {
|
|
mNpc_SetUnlockTimer(&talk_info->unlock_timer, &talk_info->reset_timer, feel);
|
|
}
|
|
|
|
talk_info->quest_request = FALSE;
|
|
}
|
|
}
|
|
|
|
static void mNpc_UnlockTimerCountDown(mNpc_Talk_Info_c* talk_info) {
|
|
if (talk_info != NULL && talk_info->unlock_timer > 0 && talk_info->unlock_timer > (talk_info->reset_timer - 1000)) {
|
|
talk_info->unlock_timer--;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_TalkInfoMove() {
|
|
mNpc_Talk_Info_c* talk_info_p = l_npc_talk_info;
|
|
int i;
|
|
|
|
if (mFI_CheckPlayerWade(mFI_WADE_START) == TRUE) {
|
|
for (i = 0; i < ARRAY_COUNT(l_npc_talk_info); i++) {
|
|
talk_info_p->reset_timer = talk_info_p->unlock_timer;
|
|
talk_info_p++;
|
|
}
|
|
}
|
|
|
|
talk_info_p = l_npc_talk_info;
|
|
for (i = 0; i < ARRAY_COUNT(l_npc_talk_info); i++) {
|
|
mNpc_TimerCountDown(talk_info_p);
|
|
mNpc_UnlockTimerCountDown(talk_info_p);
|
|
|
|
if (talk_info_p->unlock_timer == 0 && talk_info_p->reset_timer > 0) {
|
|
talk_info_p->talk_num = 0;
|
|
talk_info_p->quest_request = TRUE;
|
|
talk_info_p->reset_timer = 0;
|
|
}
|
|
|
|
talk_info_p++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_TalkEndMove(int animal_idx, int feel) {
|
|
if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info) && feel >= 0 && feel < mNpc_FEEL_NUM) {
|
|
mNpc_Talk_Info_c* talk_info = &l_npc_talk_info[animal_idx];
|
|
talk_info->timer = 1000;
|
|
|
|
if (mNpc_CountTalkNum(animal_idx, feel) == TRUE && mNpc_CheckOverImpatient(animal_idx, feel) == TRUE) {
|
|
mNpc_SetUnlockTimer(&talk_info->unlock_timer, &talk_info->reset_timer, feel);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetNpcFloorNo() {
|
|
mActor_name_t field_id = mFI_GetFieldId();
|
|
mActor_name_t owner_name = Common_Get(house_owner_name);
|
|
int floor_no = -1;
|
|
|
|
if (mFI_GET_TYPE(field_id) == mFI_FIELD_NPCROOM0) {
|
|
|
|
if (owner_name != EMPTY_NO && owner_name != RSV_NO) {
|
|
int idx = mNpc_SearchAnimalinfo(Save_Get(animals), owner_name, ANIMAL_NUM_MAX);
|
|
|
|
floor_no = Common_Get(npclist[idx]).house_data.floor_id;
|
|
}
|
|
}
|
|
|
|
return floor_no;
|
|
}
|
|
|
|
extern int mNpc_GetNpcWallNo() {
|
|
mActor_name_t field_id = mFI_GetFieldId();
|
|
mActor_name_t owner_name = Common_Get(house_owner_name);
|
|
int wall_no = -1;
|
|
|
|
if (mFI_GET_TYPE(field_id) == mFI_FIELD_NPCROOM0) {
|
|
|
|
if (owner_name != EMPTY_NO && owner_name != RSV_NO) {
|
|
int idx = mNpc_SearchAnimalinfo(Save_Get(animals), owner_name, ANIMAL_NUM_MAX);
|
|
|
|
wall_no = Common_Get(npclist[idx]).house_data.wall_id;
|
|
}
|
|
}
|
|
|
|
return wall_no;
|
|
}
|
|
|
|
/* 75% */
|
|
|
|
extern void mNpc_SetTalkBee() {
|
|
if (Common_Get(player_bee_swell_flag) != TRUE) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
mNpc_NpcList_c* npclist = Common_Get(npclist);
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE) {
|
|
npclist->conversation_flags.beesting = TRUE;
|
|
}
|
|
|
|
animal++;
|
|
npclist++;
|
|
}
|
|
|
|
if (mNpc_CheckFreeAnimalInfo(&Save_Get(island).animal) == FALSE) {
|
|
Common_Get(island_npclist[0]).conversation_flags.beesting = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern u8 mNpc_GetFishCompleteTalk(mNpc_NpcList_c* npclist) {
|
|
u8 res = TRUE;
|
|
|
|
if (npclist != NULL) {
|
|
res = npclist->conversation_flags.fish_complete;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern u8 mNpc_GetInsectCompleteTalk(mNpc_NpcList_c* npclist) {
|
|
u8 res = TRUE;
|
|
|
|
if (npclist != NULL) {
|
|
res = npclist->conversation_flags.insect_complete;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_SetFishCompleteTalk(mNpc_NpcList_c* npclist) {
|
|
if (npclist != NULL) {
|
|
npclist->conversation_flags.fish_complete = TRUE;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetInsectCompleteTalk(mNpc_NpcList_c* npclist) {
|
|
if (npclist != NULL) {
|
|
npclist->conversation_flags.insect_complete = TRUE;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetNpcHomeYpos() {
|
|
Animal_c* animal = Save_Get(animals);
|
|
mNpc_NpcList_c* npclist = Common_Get(npclist);
|
|
int i;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE) {
|
|
npclist->house_position.y = mCoBG_GetBgY_OnlyCenter_FromWpos2(npclist->house_position, 0.0f);
|
|
}
|
|
|
|
animal++;
|
|
npclist++;
|
|
}
|
|
}
|
|
|
|
static void mNpc_SetIslandAnimalHouse(Anmhome_c* home) {
|
|
int island_x_blocks[mISL_FG_BLOCK_X_NUM];
|
|
int set = FALSE;
|
|
int bx;
|
|
int ut;
|
|
|
|
mFI_GetIslandBlockNumX(island_x_blocks);
|
|
|
|
for (bx = 0; bx < mISL_FG_BLOCK_X_NUM; bx++) {
|
|
mActor_name_t* items = &Save_Get(island).fgblock[0][bx].items[0][0];
|
|
|
|
if (items != NULL) {
|
|
for (ut = 0; ut < UT_TOTAL_NUM; ut++) {
|
|
if (*items == COTTAGE_NPC) {
|
|
home->block_x = island_x_blocks[bx];
|
|
home->block_z = mISL_BLOCK_Z;
|
|
home->ut_x = ut & 15;
|
|
home->ut_z = (ut >> 4) + 1;
|
|
set = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
items++;
|
|
}
|
|
|
|
if (set == TRUE) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_DecideIslandNpc(Animal_c* animal) {
|
|
static int cand_table[NPC_ISLANDER_NUM];
|
|
|
|
int* cand_p = cand_table;
|
|
int candidates = 0;
|
|
mNpc_Default_Data_c* def_list;
|
|
mActor_name_t npc_id;
|
|
s8* grow_list;
|
|
int i;
|
|
|
|
bzero(cand_p, sizeof(cand_table));
|
|
mNpc_ClearIslandAnimalInfo(animal);
|
|
def_list = npc_def_list;
|
|
grow_list = npc_grow_list;
|
|
|
|
for (i = 0; i < NPC_NUM; i++) {
|
|
if (*grow_list == mNpc_GROW_ISLANDER) {
|
|
cand_p[0] = i;
|
|
cand_p++;
|
|
candidates++;
|
|
}
|
|
|
|
if (candidates >= NPC_ISLANDER_NUM) {
|
|
break;
|
|
}
|
|
|
|
grow_list++;
|
|
}
|
|
|
|
if (candidates <= 0) {
|
|
candidates = 1;
|
|
}
|
|
|
|
npc_id = NPC_START | cand_table[RANDOM(candidates)];
|
|
mNpc_SetDefAnimal(animal, npc_id, def_list);
|
|
mNpc_SetHaveAppeared(npc_id);
|
|
mNpc_SetIslandAnimalHouse(&animal->home_info);
|
|
}
|
|
|
|
static mActor_name_t l_mnpc_get_ftr = EMPTY_NO;
|
|
static int l_mnpc_get_letter = FALSE;
|
|
static int l_mnpc_chk_ftr_msg = FALSE;
|
|
|
|
typedef struct island_ftr_s {
|
|
u16 set_ftr_bitfield;
|
|
mActor_name_t trade_list[mNpc_ISLAND_FTR_SAVE_NUM];
|
|
mActor_name_t item_list[mNpc_ISLAND_FTR_NUM];
|
|
} mNpc_Island_Ftr_c;
|
|
|
|
static mNpc_Island_Ftr_c l_mnpc_island_ftr;
|
|
|
|
static void mNpc_ClearIslandItemList(mActor_name_t* item_list, int count) {
|
|
bzero(item_list, count * sizeof(mActor_name_t));
|
|
}
|
|
|
|
static int mNpc_GetFreeIslandItemListIdx(mActor_name_t* item_list, int count) {
|
|
int idx = -1;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (*item_list == EMPTY_NO) {
|
|
idx = i;
|
|
break;
|
|
}
|
|
|
|
item_list++;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
static int mNpc_SetIslandItemList(mActor_name_t* item_list, int count, mActor_name_t item) {
|
|
int res = FALSE;
|
|
int free_idx = mNpc_GetFreeIslandItemListIdx(item_list, count);
|
|
|
|
if (free_idx != -1) {
|
|
item_list[free_idx] = item;
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_SetIslandRoomFtr(Animal_c* animal) {
|
|
mNpc_Island_Ftr_c* island_ftr_p = &l_mnpc_island_ftr;
|
|
|
|
if (animal != NULL) {
|
|
Anmmem_c* memory = animal->memories;
|
|
int i;
|
|
|
|
island_ftr_p->set_ftr_bitfield = 0;
|
|
for (i = 0; i < ANIMAL_MEMORY_NUM; i++) {
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE) {
|
|
island_ftr_p->set_ftr_bitfield |= memory->memuni.island.have_bitfield;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
|
|
bcopy(&animal->anmuni.island_ftr, island_ftr_p->trade_list, mNpc_ISLAND_FTR_SAVE_NUM * sizeof(mActor_name_t));
|
|
mNpc_ClearIslandItemList(island_ftr_p->item_list, mNpc_ISLAND_FTR_NUM);
|
|
}
|
|
}
|
|
|
|
static int mNpc_GetIslandFtrIdx(mActor_name_t item) {
|
|
mActor_name_t* island_room = mNpc_GetIslandRoomP(Save_Get(island).animal.id.npc_id);
|
|
int n = 0;
|
|
int idx = -1;
|
|
int i;
|
|
|
|
if (island_room != NULL && ITEM_IS_FTR(item)) {
|
|
if (mNpc_CheckFtrIsIslandBestFtr(item) == TRUE) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (aMR_CorrespondFurniture(item, *island_room) == TRUE) {
|
|
idx = n;
|
|
break;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
n++;
|
|
}
|
|
|
|
if (n == mNpc_ISLAND_FTR_NUM) {
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
else {
|
|
int ftr_unit = aMR_GetFurnitureUnit(item);
|
|
u32 ftr_variant0;
|
|
u32 ftr_variant1;
|
|
int j;
|
|
|
|
if (ftr_unit == mRmTp_FTRSIZE_1x1) {
|
|
ftr_variant0 = RSV_ISLAND_FTR0;
|
|
ftr_variant1 = RSV_ISLAND_FTR4;
|
|
}
|
|
else {
|
|
ftr_variant0 = (mActor_name_t)(RSV_ISLAND_FTR0 + (ftr_unit + 1) * mRmTp_DIRECT_NUM);
|
|
ftr_variant1 = EMPTY_NO;
|
|
}
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
if (*island_room == (mActor_name_t)(ftr_variant0 + j) || *island_room == (mActor_name_t)(ftr_variant1 + j)) {
|
|
idx = n;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (idx != -1) {
|
|
break;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
if (n == mNpc_ISLAND_FTR_NUM) {
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
extern void mNpc_SetIslandGetFtr(mActor_name_t ftr) {
|
|
int n;
|
|
mNpc_Island_Ftr_c* island_ftr_p = &l_mnpc_island_ftr;
|
|
mActor_name_t* island_room = mNpc_GetIslandRoomP(Save_Get(island).animal.id.npc_id);
|
|
int set;
|
|
int variant;
|
|
int slot;
|
|
int i;
|
|
|
|
n = 0;
|
|
set = FALSE;
|
|
|
|
if (ftr != EMPTY_NO && ITEM_IS_FTR(ftr) && island_room != NULL) {
|
|
if (mNpc_CheckFtrIsIslandBestFtr(ftr) == TRUE) {
|
|
int idx = mNpc_GetIslandFtrIdx(ftr);
|
|
|
|
if (idx >= 0 && idx < mNpc_ISLAND_FTR_NUM && ((island_ftr_p->set_ftr_bitfield >> idx) & 1) == 0) {
|
|
island_ftr_p->set_ftr_bitfield |= (1 << idx);
|
|
set = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
int ftr_unit = aMR_GetFurnitureUnit(ftr);
|
|
mActor_name_t ftr_variant0;
|
|
mActor_name_t ftr_variant1;
|
|
int j;
|
|
|
|
if (ftr_unit == mRmTp_FTRSIZE_1x1) {
|
|
ftr_variant0 = RSV_ISLAND_FTR0;
|
|
ftr_variant1 = RSV_ISLAND_FTR4;
|
|
}
|
|
else {
|
|
ftr_variant0 = RSV_ISLAND_FTR0 + (ftr_unit + 1) * 4;
|
|
ftr_variant1 = EMPTY_NO;
|
|
}
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
variant = -1;
|
|
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
if (*island_room == (mActor_name_t)(ftr_variant0 + j)) {
|
|
variant = 0;
|
|
break;
|
|
}
|
|
else if (*island_room == (mActor_name_t)(ftr_variant1 + j)) {
|
|
variant = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (variant != -1 && n >= 0 && n < mNpc_ISLAND_FTR_NUM && ((island_ftr_p->set_ftr_bitfield >> n) & 1) == 0) {
|
|
island_ftr_p->set_ftr_bitfield |= (1 << n);
|
|
|
|
if (variant == 0) {
|
|
slot = (ftr_variant0 - RSV_ISLAND_FTR0) / mNpc_ISLAND_FTR_SAVE_NUM;
|
|
}
|
|
else {
|
|
slot = (ftr_variant1 - RSV_ISLAND_FTR0) / mNpc_ISLAND_FTR_SAVE_NUM;
|
|
}
|
|
|
|
if (slot < mNpc_ISLAND_FTR_SAVE_NUM) {
|
|
ftr = aMR_FurnitureFg_to_FurnitureFgWithDirect(ftr, j);
|
|
island_ftr_p->trade_list[slot] = ftr;
|
|
set = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
if (n == mNpc_ISLAND_FTR_NUM) {
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
|
|
if (set == TRUE) {
|
|
mNpc_SetIslandItemList(island_ftr_p->item_list, mNpc_ISLAND_FTR_NUM, ftr);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetIslandGetFtrtoRoom() {
|
|
mActor_name_t* item_list = l_mnpc_island_ftr.item_list;
|
|
Animal_c* animal = &Save_Get(island).animal;
|
|
|
|
if (Common_Get(now_private) != NULL) {
|
|
Private_c* priv = Common_Get(now_private);
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_ISLAND_FTR_NUM; i++) {
|
|
if (*item_list != EMPTY_NO && ITEM_IS_FTR(*item_list)) {
|
|
mNpc_SetIslandFtr(&priv->player_ID, *item_list);
|
|
}
|
|
|
|
item_list++;
|
|
}
|
|
|
|
mNpc_RestoreIslandPresentFtr();
|
|
mNpc_ClearIslandPresentFtrInfo();
|
|
mNpc_SetIslandRoomFtr(animal);
|
|
}
|
|
}
|
|
|
|
extern void mNpc_SetIslandGetLetter(int get) {
|
|
l_mnpc_get_letter = get;
|
|
}
|
|
|
|
extern int mNpc_GetIslandGetLetter() {
|
|
return l_mnpc_get_letter;
|
|
}
|
|
|
|
extern void mNpc_SetIslandCheckFtrMsg(int set) {
|
|
l_mnpc_chk_ftr_msg = set;
|
|
}
|
|
|
|
extern int mNpc_GetIslandCheckFtrMsg() {
|
|
return l_mnpc_chk_ftr_msg;
|
|
}
|
|
|
|
typedef struct npc_island_room_data_s {
|
|
mActor_name_t items[UT_Z_NUM][UT_X_NUM];
|
|
int wall_no;
|
|
int floor_no;
|
|
mActor_name_t npc_id;
|
|
} mNpc_Island_Npc_Room_Data_c;
|
|
|
|
static mNpc_Island_Npc_Room_Data_c l_island_npc_room_data[NPC_ISLANDER_NUM];
|
|
|
|
static mActor_name_t l_island_npc_best_fg_id[NPC_ISLANDER_NUM][2] = {
|
|
{ 0x0250, 0x0251 },
|
|
{ 0x0346, 0x0347 },
|
|
{ 0x0372, 0x0373 },
|
|
{ 0x027C, 0x027D },
|
|
{ 0x02EA, 0x02EB },
|
|
{ 0x0326, 0x0327 },
|
|
{ 0x0270, 0x0271 },
|
|
{ 0x01C0, 0x01C1 },
|
|
{ 0x039A, 0x039B },
|
|
{ 0x02F0, 0x02F1 },
|
|
{ 0x02C4, 0x02C5 },
|
|
{ 0x0224, 0x0225 },
|
|
{ 0x02D4, 0x02D5 },
|
|
{ 0x02A4, 0x02A5 },
|
|
{ 0x038C, 0x038D },
|
|
{ 0x01FE, 0x01FF },
|
|
{ 0x032E, 0x032F },
|
|
{ 0x01E6, 0x01E7 }
|
|
};
|
|
|
|
extern void mNpc_ClearIslandNpcRoomData() {
|
|
bzero(l_island_npc_room_data, sizeof(l_island_npc_room_data));
|
|
}
|
|
|
|
extern void mNpc_IslandNpcRoomDataSet(mFM_fg_data_c** data_table, int base_idx) {
|
|
mNpc_Island_Npc_Room_Data_c* dst = l_island_npc_room_data;
|
|
s8* grow_list = npc_grow_list;
|
|
mNpc_NpcHouseData_c* house_list = npc_house_list;
|
|
int islander = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < NPC_NUM; i++) {
|
|
if (*grow_list == mNpc_GROW_ISLANDER) {
|
|
int idx = l_island_npc_best_fg_id[islander][0] - base_idx;
|
|
mFM_fg_data_c* best_id_p = data_table[idx];
|
|
|
|
if (best_id_p != NULL) {
|
|
dst->wall_no = house_list->wall_id;
|
|
dst->floor_no = house_list->floor_id;
|
|
bcopy(data_table[idx]->items, dst->items, sizeof(best_id_p->items));
|
|
dst->npc_id = NPC_START | i;
|
|
|
|
islander++;
|
|
dst++;
|
|
}
|
|
}
|
|
|
|
if (islander >= NPC_ISLANDER_NUM) {
|
|
break;
|
|
}
|
|
|
|
grow_list++;
|
|
house_list++;
|
|
}
|
|
}
|
|
|
|
static mNpc_Island_Npc_Room_Data_c* mNpc_GetIslandRoomInfoP(mActor_name_t npc_id) {
|
|
mNpc_Island_Npc_Room_Data_c* room_p = l_island_npc_room_data;
|
|
mNpc_Island_Npc_Room_Data_c* res = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < NPC_ISLANDER_NUM; i++) {
|
|
/* @BUG - devs used & instead of && */
|
|
#ifndef BUGFIXES
|
|
if (npc_id != EMPTY_NO & room_p->npc_id == npc_id) {
|
|
#else
|
|
if (npc_id != EMPTY_NO && room_p->npc_id == npc_id) {
|
|
#endif
|
|
res = room_p;
|
|
break;
|
|
}
|
|
|
|
room_p++;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern mActor_name_t* mNpc_GetIslandRoomP(mActor_name_t npc_id) {
|
|
mActor_name_t* res = NULL;
|
|
mNpc_Island_Npc_Room_Data_c* room_info = mNpc_GetIslandRoomInfoP(npc_id);
|
|
|
|
if (room_info != NULL) {
|
|
res = room_info->items[0];
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern void mNpc_GetIslandWallFloorIdx(int* wall, int* floor, mActor_name_t npc_id) {
|
|
mNpc_Island_Npc_Room_Data_c* room_info;
|
|
|
|
if (wall != NULL && floor != NULL) {
|
|
wall[0] = 8;
|
|
floor[0] = 32;
|
|
|
|
room_info = mNpc_GetIslandRoomInfoP(npc_id);
|
|
|
|
if (room_info != NULL) {
|
|
wall[0] = room_info->wall_no;
|
|
floor[0] = room_info->floor_no;
|
|
}
|
|
}
|
|
}
|
|
|
|
static mActor_name_t mNpc_getNormalFtr(mActor_name_t rsv_name, Animal_c* animal) {
|
|
mActor_name_t ftr = EMPTY_NO;
|
|
int idx;
|
|
|
|
if (rsv_name >= RSV_ISLAND_FTR0 && rsv_name <= RSV_ISLAND_FTR15) {
|
|
int rsv = rsv_name - RSV_ISLAND_FTR0;
|
|
|
|
idx = (rsv - (rsv % mNpc_ISLAND_FTR_SAVE_NUM)) / mNpc_ISLAND_FTR_SAVE_NUM;
|
|
if (idx >= mNpc_ISLAND_FTR_SAVE_NUM) {
|
|
idx = 0;
|
|
}
|
|
|
|
ftr = animal->anmuni.island_ftr[idx];
|
|
}
|
|
|
|
return ftr;
|
|
}
|
|
|
|
static int mNpc_CheckIslandNpcRoomFtrIdx(int ftr_idx) {
|
|
Animal_c* animal = &Save_Get(island).animal;
|
|
Anmmem_c* memory;
|
|
int i;
|
|
int res = -1;
|
|
|
|
if (mNpc_CheckFreeAnimalInfo(animal) == FALSE && ftr_idx >= 0 && ftr_idx < mNpc_ISLAND_FTR_NUM) {
|
|
memory = animal->memories;
|
|
|
|
for (i = 0; i < ANIMAL_MEMORY_NUM; i++) {
|
|
if (
|
|
mPr_NullCheckPersonalID(&memory->memory_player_id) == FALSE &&
|
|
((memory->memuni.island.have_bitfield >> ftr_idx) & 1) == 1
|
|
) {
|
|
res = i;
|
|
break;
|
|
}
|
|
|
|
memory++;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckIslandNpcRoomFtrItemNo_keep(mActor_name_t ftr) {
|
|
mActor_name_t* island_room;
|
|
int keep;
|
|
mActor_name_t* trade_list;
|
|
mNpc_Island_Ftr_c* island_ftr_p;
|
|
mActor_name_t rsv_item;
|
|
int n;
|
|
int found;
|
|
int i;
|
|
int j;
|
|
|
|
island_ftr_p = &l_mnpc_island_ftr;
|
|
trade_list = l_mnpc_island_ftr.trade_list;
|
|
rsv_item = EMPTY_NO;
|
|
found = FALSE;
|
|
n = 0;
|
|
keep = FALSE;
|
|
island_room = mNpc_GetIslandRoomP(Save_Get(island).animal.id.npc_id);
|
|
|
|
if (ITEM_IS_FTR(ftr) && island_room != NULL) {
|
|
if (mNpc_CheckFtrIsIslandBestFtr(ftr) == TRUE) {
|
|
int idx = mNpc_GetIslandFtrIdx(ftr);
|
|
|
|
/* @BUG & should've been && */
|
|
#ifndef BUGFIXES
|
|
if (((idx >= 0) & (idx < mNpc_ISLAND_FTR_NUM)) && ((island_ftr_p->set_ftr_bitfield >> idx) & 1) == 0) {
|
|
#else
|
|
if (((idx >= 0) && (idx < mNpc_ISLAND_FTR_NUM)) && ((island_ftr_p->set_ftr_bitfield >> idx) & 1) == 0) {
|
|
#endif
|
|
keep = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < mNpc_ISLAND_FTR_SAVE_NUM; i++) {
|
|
/* Why loop through each direction instead of just masking off the rotation bits */
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
mActor_name_t dir_item = aMR_FurnitureFg_to_FurnitureFgWithDirect(ftr, j);
|
|
|
|
if (dir_item == trade_list[i]) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found == TRUE) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found == FALSE) {
|
|
int ftr_unit = aMR_GetFurnitureUnit(ftr);
|
|
|
|
if (ftr_unit == mRmTp_FTRSIZE_1x1) {
|
|
if (trade_list[0] == EMPTY_NO) {
|
|
rsv_item = RSV_ISLAND_FTR0;
|
|
}
|
|
else if (trade_list[1] == EMPTY_NO) {
|
|
rsv_item = RSV_ISLAND_FTR4;
|
|
}
|
|
}
|
|
else if (ftr_unit != -1) {
|
|
ftr_unit++;
|
|
|
|
if (trade_list[ftr_unit] == EMPTY_NO) {
|
|
rsv_item = RSV_ISLAND_FTR0 + ftr_unit * mRmTp_DIRECT_NUM;
|
|
}
|
|
}
|
|
|
|
if (rsv_item != EMPTY_NO) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
if (*island_room == (mActor_name_t)(rsv_item + j)) {
|
|
keep = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (keep == TRUE) {
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return keep;
|
|
}
|
|
|
|
extern void mNpc_ChangeIslandRoom(mActor_name_t* items) {
|
|
Animal_c* animal = &Save_Get(island).animal;
|
|
int n = 0;
|
|
int i;
|
|
|
|
if (items != NULL) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*items)) {
|
|
if (mNpc_CheckIslandNpcRoomFtrIdx(n) == -1) {
|
|
items[0] = RSV_NO; // cleared because the item hasn't been "obtained" by the islander yet
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*items >= RSV_ISLAND_FTR0 && *items <= RSV_ISLAND_FTR15) {
|
|
if (mNpc_CheckIslandNpcRoomFtrIdx(n) == -1) {
|
|
items[0] = RSV_NO; // cleared because the item hasn't been "obtained" by the islander yet
|
|
}
|
|
else {
|
|
items[0] = mNpc_getNormalFtr(*items, animal); // update with "not best" furniture
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
if (n >= mNpc_ISLAND_FTR_NUM) {
|
|
break;
|
|
}
|
|
|
|
items++;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_CheckFtrIsIslandBestFtr(mActor_name_t ftr) {
|
|
mActor_name_t* island_room;
|
|
int i;
|
|
int res = FALSE;
|
|
|
|
island_room = mNpc_GetIslandRoomP(Save_Get(island).animal.id.npc_id);
|
|
if (island_room != NULL && ITEM_IS_FTR(ftr)) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (aMR_CorrespondFurniture(ftr, *island_room) == TRUE) {
|
|
res = TRUE;
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckFtrIsIslandNormalFtr(mActor_name_t ftr) {
|
|
int res = FALSE;
|
|
|
|
if (ITEM_IS_FTR(ftr) && mNpc_CheckFtrIsIslandBestFtr(ftr) == FALSE) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_SetIslandFtr(PersonalID_c* pid, mActor_name_t ftr) {
|
|
int n;
|
|
Anmmem_c* memory;
|
|
mActor_name_t* island_room;
|
|
int slot;
|
|
Animal_c* animal;
|
|
mActor_name_t rsv_item;
|
|
int i;
|
|
int j;
|
|
int variant;
|
|
int set;
|
|
int direct;
|
|
|
|
memory = Save_Get(island).animal.memories;
|
|
animal = &Save_Get(island).animal;
|
|
island_room = mNpc_GetIslandRoomP(animal->id.npc_id);
|
|
n = 0;
|
|
rsv_item = EMPTY_NO;
|
|
set = FALSE;
|
|
slot = 0;
|
|
direct = 0;
|
|
|
|
if (ITEM_IS_FTR(ftr) && island_room != NULL && pid != NULL && mPr_NullCheckPersonalID(pid) == FALSE) {
|
|
int mem_idx = mNpc_GetAnimalMemoryIdx(pid, memory, ANIMAL_MEMORY_NUM);
|
|
|
|
if (mem_idx == -1) {
|
|
memory = &memory[mNpc_ForceGetFreeAnimalMemoryIdx(animal, memory, ANIMAL_MEMORY_NUM)];
|
|
mNpc_SetAnimalMemory(pid, &animal->id, memory);
|
|
}
|
|
else {
|
|
memory = &memory[mem_idx];
|
|
}
|
|
|
|
/* Unnecessary NULL check, memory is guaranteed to exist */
|
|
if (memory != NULL) {
|
|
if (mNpc_CheckFtrIsIslandBestFtr(ftr) == TRUE) {
|
|
int idx = mNpc_GetIslandFtrIdx(ftr);
|
|
|
|
if (idx != -1 && mNpc_CheckIslandNpcRoomFtrIdx(idx) == -1) {
|
|
memory->memuni.island.have_bitfield |= (1 << idx);
|
|
set = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
mActor_name_t ftr_variant0;
|
|
mActor_name_t ftr_variant1;
|
|
int ftr_unit = aMR_GetFurnitureUnit(ftr);
|
|
|
|
if (ftr_unit == mRmTp_FTRSIZE_1x1) {
|
|
ftr_variant0 = RSV_ISLAND_FTR0;
|
|
ftr_variant1 = RSV_ISLAND_FTR4;
|
|
}
|
|
else {
|
|
ftr_variant0 = RSV_ISLAND_FTR0 + (ftr_unit + 1) * 4;
|
|
ftr_variant1 = EMPTY_NO;
|
|
}
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
variant = -1;
|
|
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
if (*island_room == (mActor_name_t)(ftr_variant0 + j)) {
|
|
direct = j;
|
|
variant = 0;
|
|
break;
|
|
}
|
|
else if (*island_room == (mActor_name_t)(ftr_variant1 + j)) {
|
|
direct = j;
|
|
variant = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (variant != -1 && mNpc_CheckIslandNpcRoomFtrIdx(n) == -1) {
|
|
memory->memuni.island.have_bitfield |= (1 << n);
|
|
|
|
if (variant == 0) {
|
|
slot = (ftr_variant0 - RSV_ISLAND_FTR0) / mNpc_ISLAND_FTR_SAVE_NUM;
|
|
}
|
|
else {
|
|
slot = (ftr_variant1 - RSV_ISLAND_FTR0) / mNpc_ISLAND_FTR_SAVE_NUM;
|
|
}
|
|
|
|
if (slot < mNpc_ISLAND_FTR_SAVE_NUM) {
|
|
Save_Get(island).animal.anmuni.island_ftr[slot] = aMR_FurnitureFg_to_FurnitureFgWithDirect(ftr, direct);
|
|
set = TRUE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
if (n == mNpc_ISLAND_FTR_NUM) {
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return set;
|
|
}
|
|
|
|
extern int mNpc_EraseIslandFtr(mActor_name_t ftr) {
|
|
int n;
|
|
Animal_c* animal;
|
|
mActor_name_t* island_room;
|
|
mActor_name_t* island_ftr;
|
|
mActor_name_t rsv_no;
|
|
int i;
|
|
int j;
|
|
int variant;
|
|
int set;
|
|
|
|
animal = &Save_Get(island).animal;
|
|
island_room = mNpc_GetIslandRoomP(animal->id.npc_id);
|
|
island_ftr = Save_Get(island).animal.anmuni.island_ftr;
|
|
rsv_no = EMPTY_NO;
|
|
n = 0;
|
|
set = FALSE;
|
|
|
|
if (ITEM_IS_FTR(ftr) && island_room != NULL) {
|
|
if (mNpc_CheckFtrIsIslandBestFtr(ftr) == TRUE) {
|
|
int idx = mNpc_GetIslandFtrIdx(ftr);
|
|
int mem_idx = mNpc_CheckIslandNpcRoomFtrIdx(idx);
|
|
|
|
if (idx != -1 && mem_idx != -1) {
|
|
Anmmem_c* memory = &Save_Get(island).animal.memories[mem_idx];
|
|
u16* have_bitfield_p = &memory->memuni.island.have_bitfield;
|
|
u16 have_bitfield = have_bitfield_p[0] & ~(1 << idx);
|
|
have_bitfield_p[0] = have_bitfield;
|
|
set = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < mNpc_ISLAND_FTR_SAVE_NUM; i++) {
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
mActor_name_t ftr_rot = aMR_FurnitureFg_to_FurnitureFgWithDirect(ftr, j);
|
|
|
|
if (ftr_rot == island_ftr[i]) {
|
|
rsv_no = RSV_ISLAND_FTR0 + i * mRmTp_DIRECT_NUM;
|
|
island_ftr[i] = EMPTY_NO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rsv_no != EMPTY_NO) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rsv_no != EMPTY_NO) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
variant = -1;
|
|
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
if (*island_room == (mActor_name_t)(rsv_no + j)) {
|
|
variant = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (variant != -1) {
|
|
int mem_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
if (mem_idx != -1) {
|
|
Anmmem_c* memory = &Save_Get(island).animal.memories[mem_idx];
|
|
u16* have_bitfield_p = &memory->memuni.island.have_bitfield;
|
|
u16 have_bitfield = have_bitfield_p[0] & ~(1 << n);
|
|
have_bitfield_p[0] = have_bitfield;
|
|
set = TRUE;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
if (n == mNpc_ISLAND_FTR_NUM) {
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return set;
|
|
}
|
|
|
|
extern int mNpc_EraseIslandFtr_keep(mActor_name_t ftr) {
|
|
int n;
|
|
Animal_c* animal;
|
|
mNpc_Island_Ftr_c* island_ftr_p;
|
|
mActor_name_t* island_room;
|
|
mActor_name_t* trade_list;
|
|
int set;
|
|
mActor_name_t rsv_no;
|
|
int i;
|
|
int j;
|
|
|
|
animal = &Save_Get(island).animal;
|
|
island_ftr_p = &l_mnpc_island_ftr;
|
|
island_room = mNpc_GetIslandRoomP(animal->id.npc_id);
|
|
trade_list = l_mnpc_island_ftr.trade_list;
|
|
rsv_no = EMPTY_NO;
|
|
n = 0;
|
|
set = FALSE;
|
|
|
|
if (ITEM_IS_FTR(ftr) && island_room != NULL) {
|
|
if (mNpc_CheckFtrIsIslandBestFtr(ftr) == TRUE) {
|
|
int idx = mNpc_GetIslandFtrIdx(ftr);
|
|
|
|
if (idx >= 0 && idx < mNpc_ISLAND_FTR_NUM && ((island_ftr_p->set_ftr_bitfield >> idx) & 1) == 1) {
|
|
u16* set_ftr_bitfield_p = &island_ftr_p->set_ftr_bitfield;
|
|
u16 set_bitfield = set_ftr_bitfield_p[0] & ~(1 << idx);
|
|
set_ftr_bitfield_p[0] = set_bitfield;
|
|
set = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0; i < mNpc_ISLAND_FTR_SAVE_NUM; i++) {
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
mActor_name_t ftr_rot = aMR_FurnitureFg_to_FurnitureFgWithDirect(ftr, j);
|
|
|
|
if (ftr_rot == trade_list[i]) {
|
|
rsv_no = RSV_ISLAND_FTR0 + i * mRmTp_DIRECT_NUM;
|
|
trade_list[i] = EMPTY_NO;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rsv_no != EMPTY_NO) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rsv_no != EMPTY_NO) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
int variant = -1;
|
|
|
|
for (j = 0; j < mRmTp_DIRECT_NUM; j++) {
|
|
if (*island_room == (mActor_name_t)(rsv_no + j)) {
|
|
variant = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (variant != -1 && n >= 0 && n < mNpc_ISLAND_FTR_NUM && ((island_ftr_p->set_ftr_bitfield >> n) & 1) == 1) {
|
|
u16* set_ftr_bitfield_p = &island_ftr_p->set_ftr_bitfield;
|
|
u16 set_bitfield = set_ftr_bitfield_p[0] & ~(1 << n);
|
|
set_ftr_bitfield_p[0] = set_bitfield;
|
|
set = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
if (n == mNpc_ISLAND_FTR_NUM) {
|
|
break;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return set;
|
|
}
|
|
|
|
typedef struct island_present_ftr_s {
|
|
PersonalID_c pid;
|
|
mActor_name_t present;
|
|
} mNpc_IslandPresentFtr_c;
|
|
|
|
static mNpc_IslandPresentFtr_c l_mnpc_island_present_ftr;
|
|
|
|
static void mNpc_ClearIslandPresentFtrInfo_common(mNpc_IslandPresentFtr_c* info) {
|
|
mPr_ClearPersonalID(&info->pid);
|
|
info->present = EMPTY_NO;
|
|
}
|
|
|
|
extern void mNpc_ClearIslandPresentFtrInfo() {
|
|
mNpc_ClearIslandPresentFtrInfo_common(&l_mnpc_island_present_ftr);
|
|
}
|
|
|
|
extern void mNpc_SetIslandPresentFtr() {
|
|
Animal_c* animal;
|
|
AnmPersonalID_c* anm_id;
|
|
mNpc_IslandPresentFtr_c* present_ftr_p;
|
|
Anmmem_c* memory;
|
|
int ftr_num;
|
|
mActor_name_t* island_room;
|
|
int selected;
|
|
int n;
|
|
int i;
|
|
|
|
ftr_num = 0;
|
|
n = 0;
|
|
animal = &Save_Get(island).animal;
|
|
anm_id = &Save_Get(island).animal.id;
|
|
memory = Save_Get(island).animal.memories;
|
|
present_ftr_p = &l_mnpc_island_present_ftr;
|
|
|
|
mNpc_ClearIslandPresentFtrInfo_common(present_ftr_p);
|
|
|
|
for (i = 0; i < mNpc_ISLAND_FTR_NUM; i++) {
|
|
if (mNpc_CheckIslandNpcRoomFtrIdx(i) != -1) {
|
|
ftr_num++;
|
|
}
|
|
}
|
|
|
|
if (ftr_num > 1) {
|
|
selected = RANDOM(ftr_num);
|
|
island_room = mNpc_GetIslandRoomP(anm_id->npc_id);
|
|
|
|
if (island_room != NULL) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (mNpc_CheckIslandNpcRoomFtrIdx(n) != -1) {
|
|
if (selected == 0) {
|
|
int mem_idx;
|
|
|
|
present_ftr_p->present = aMR_FurnitureFg_to_FurnitureFgWithDirect(*island_room, mRmTp_DIRECT_SOUTH);
|
|
mem_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
if (mem_idx != -1) {
|
|
memory = &memory[mem_idx];
|
|
mPr_CopyPersonalID(&present_ftr_p->pid, &memory->memory_player_id);
|
|
}
|
|
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
if (mNpc_CheckIslandNpcRoomFtrIdx(n) != -1) {
|
|
if (selected == 0) {
|
|
int mem_idx;
|
|
|
|
present_ftr_p->present = mNpc_getNormalFtr(*island_room, animal);
|
|
mem_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
if (mem_idx != -1) {
|
|
memory = &memory[mem_idx];
|
|
mPr_CopyPersonalID(&present_ftr_p->pid, &memory->memory_player_id);
|
|
}
|
|
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mNpc_CheckIslandPresentFtrIs() == TRUE) {
|
|
mNpc_EraseIslandFtr(mNpc_GetIslandPresentFtr());
|
|
}
|
|
}
|
|
|
|
extern void mNpc_RestoreIslandPresentFtr() {
|
|
PersonalID_c* pid;
|
|
mActor_name_t present;
|
|
|
|
if (mNpc_CheckIslandPresentFtrIs() == TRUE) {
|
|
present = mNpc_GetIslandPresentFtr();
|
|
pid = mNpc_GetIslandPresentFtrPersonalID();
|
|
|
|
if (present != EMPTY_NO && pid != NULL && mPr_NullCheckPersonalID(pid) == FALSE) {
|
|
mNpc_SetIslandFtr(pid, present);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_GetIslandRoomFtrNum() {
|
|
int num = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < mNpc_ISLAND_FTR_NUM; i++) {
|
|
if (mNpc_CheckIslandNpcRoomFtrIdx(i) != -1) {
|
|
num++;
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
extern int mNpc_CheckIslandPresentFtrIs() {
|
|
mNpc_IslandPresentFtr_c* present_ftr_p = &l_mnpc_island_present_ftr;
|
|
int res = FALSE;
|
|
|
|
if (present_ftr_p->present != EMPTY_NO && mPr_NullCheckPersonalID(&present_ftr_p->pid) == FALSE) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern mActor_name_t mNpc_GetIslandPresentFtr() {
|
|
return l_mnpc_island_present_ftr.present;
|
|
}
|
|
|
|
extern PersonalID_c* mNpc_GetIslandPresentFtrPersonalID() {
|
|
PersonalID_c* pid = NULL;
|
|
|
|
if (mNpc_CheckIslandPresentFtrIs() == TRUE) {
|
|
pid = &l_mnpc_island_present_ftr.pid;
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
extern mActor_name_t mNpc_GetRandomBestFtr() {
|
|
Animal_c* animal = &Save_Get(island).animal;
|
|
mActor_name_t* island_room = mNpc_GetIslandRoomP(animal->id.npc_id);
|
|
mNpc_Island_Ftr_c* island_ftr_p = &l_mnpc_island_ftr;
|
|
mActor_name_t* island_room_p = island_room;
|
|
int other_ftr_num = 0;
|
|
int n = 0;
|
|
mActor_name_t best_ftr = EMPTY_NO;
|
|
mActor_name_t present = mNpc_GetIslandPresentFtr();
|
|
int selected;
|
|
int i;
|
|
|
|
if (island_room != NULL) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (n >= 0 && n < mNpc_ISLAND_FTR_NUM &&
|
|
((island_ftr_p->set_ftr_bitfield >> n) & 1) == 0 &&
|
|
aMR_CorrespondFurniture(present, *island_room) == FALSE
|
|
) {
|
|
other_ftr_num++;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
|
|
island_room = island_room_p;
|
|
n = 0;
|
|
|
|
if (island_room != NULL && other_ftr_num > 0) {
|
|
selected = RANDOM(other_ftr_num);
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (n >= 0 && n < mNpc_ISLAND_FTR_NUM &&
|
|
((island_ftr_p->set_ftr_bitfield >> n) & 1) == 0 &&
|
|
aMR_CorrespondFurniture(present, *island_room) == FALSE
|
|
) {
|
|
if (selected == 0) {
|
|
best_ftr = aMR_FurnitureFg_to_FurnitureFgWithDirect(*island_room, mRmTp_DIRECT_SOUTH);
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return best_ftr;
|
|
}
|
|
|
|
extern Anmmem_c* mNpc_GetOtherBestFtr(PersonalID_c* pid, mActor_name_t* other_best_ftr, mActor_name_t exist_ftr) {
|
|
Animal_c* animal;
|
|
mActor_name_t* island_room;
|
|
Anmmem_c* memory;
|
|
Anmmem_c* memories;
|
|
mActor_name_t* island_room_p;
|
|
int other_ftr_num;
|
|
int n;
|
|
int mem_idx;
|
|
int i;
|
|
|
|
animal = &Save_Get(island).animal;
|
|
memories = Save_Get(island).animal.memories;
|
|
memory = NULL;
|
|
island_room = mNpc_GetIslandRoomP(animal->id.npc_id);
|
|
island_room_p = island_room;
|
|
other_ftr_num = 0;
|
|
n = 0;
|
|
|
|
if (island_room != NULL && other_best_ftr != NULL) {
|
|
other_best_ftr[0] = EMPTY_NO;
|
|
mem_idx = mNpc_GetAnimalMemoryIdx(pid, memories, ANIMAL_MEMORY_NUM);
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (aMR_CorrespondFurniture(*island_room, exist_ftr) == FALSE) {
|
|
int ftr_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
/* The item must not have been gifted by the current player */
|
|
if (ftr_idx != -1 && ftr_idx != mem_idx) {
|
|
other_ftr_num++;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
|
|
island_room = island_room_p;
|
|
n = 0;
|
|
|
|
if (island_room != NULL && other_ftr_num > 0) {
|
|
int selected = RANDOM(other_ftr_num);
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (aMR_CorrespondFurniture(*island_room, exist_ftr) == FALSE) {
|
|
int ftr_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
/* The item must not have been gifted by the current player */
|
|
if (ftr_idx != -1 && ftr_idx != mem_idx) {
|
|
if (selected == 0) {
|
|
other_best_ftr[0] = aMR_FurnitureFg_to_FurnitureFgWithDirect(*island_room, mRmTp_DIRECT_SOUTH);
|
|
memory = &memories[ftr_idx]; /* Memory of the other player who gave it */
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return memory;
|
|
}
|
|
|
|
extern mActor_name_t mNpc_GetPlayerBestFtr(PersonalID_c* pid, mActor_name_t exist_ftr) {
|
|
AnmPersonalID_c* anm_id;
|
|
Anmmem_c* memories;
|
|
mActor_name_t* island_room;
|
|
mActor_name_t* island_room_p;
|
|
int mem_idx;
|
|
int other_ftr_num;
|
|
int n;
|
|
int selected;
|
|
int i;
|
|
mActor_name_t best_ftr;
|
|
|
|
anm_id = &Save_Get(island).animal.id;
|
|
memories = Save_Get(island).animal.memories;
|
|
island_room = mNpc_GetIslandRoomP(anm_id->npc_id);
|
|
other_ftr_num = 0;
|
|
n = 0;
|
|
best_ftr = EMPTY_NO;
|
|
island_room_p = island_room;
|
|
|
|
if (island_room != NULL && pid != NULL) {
|
|
mem_idx = mNpc_GetAnimalMemoryIdx(pid, memories, ANIMAL_MEMORY_NUM);
|
|
|
|
if (mem_idx != -1) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (aMR_CorrespondFurniture(*island_room, exist_ftr) == FALSE) {
|
|
int ftr_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
/* The item must have been gifted by the current player */
|
|
if (ftr_idx == mem_idx) {
|
|
other_ftr_num++;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
|
|
n = 0;
|
|
|
|
if (island_room_p != NULL && other_ftr_num > 0) {
|
|
selected = RANDOM(other_ftr_num);
|
|
island_room = island_room_p;
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room)) {
|
|
if (aMR_CorrespondFurniture(*island_room, exist_ftr) == FALSE) {
|
|
int ftr_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
/* The item must have been gifted by the current player */
|
|
if (ftr_idx == mem_idx) {
|
|
if (selected == 0) {
|
|
best_ftr = aMR_FurnitureFg_to_FurnitureFgWithDirect(*island_room, mRmTp_DIRECT_SOUTH);
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
else if (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15) {
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return best_ftr;
|
|
}
|
|
|
|
extern mActor_name_t mNpc_GetPlayerFtr(PersonalID_c* pid) {
|
|
Animal_c* animal = &Save_Get(island).animal;
|
|
mActor_name_t* island_room = mNpc_GetIslandRoomP(Save_Get(island).animal.id.npc_id);
|
|
mActor_name_t best_ftr = EMPTY_NO;
|
|
mActor_name_t* island_room_p = island_room;
|
|
int other_ftr_num = 0;
|
|
int n = 0;
|
|
int mem_idx = mNpc_GetAnimalMemoryIdx(pid, animal->memories, ANIMAL_MEMORY_NUM);
|
|
int selected;
|
|
int i;
|
|
|
|
if (island_room != NULL && mem_idx != -1) {
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room) || (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15)) {
|
|
int ftr_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
/* The item must have been gifted by the current player */
|
|
if (ftr_idx == mem_idx) {
|
|
other_ftr_num++;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
|
|
island_room = island_room_p;
|
|
n = 0;
|
|
|
|
if (island_room != NULL && other_ftr_num > 0) {
|
|
selected = RANDOM(other_ftr_num);
|
|
|
|
for (i = 0; i < UT_TOTAL_NUM; i++) {
|
|
if (ITEM_IS_FTR(*island_room) || (*island_room >= RSV_ISLAND_FTR0 && *island_room <= RSV_ISLAND_FTR15)) {
|
|
int ftr_idx = mNpc_CheckIslandNpcRoomFtrIdx(n);
|
|
|
|
/* The item must have been gifted by the current player */
|
|
if (ftr_idx == mem_idx) {
|
|
if (selected == 0) {
|
|
mActor_name_t ftr = *island_room;
|
|
|
|
if (ftr >= RSV_ISLAND_FTR0 && ftr <= RSV_ISLAND_FTR15) {
|
|
ftr = mNpc_getNormalFtr(ftr, animal);
|
|
}
|
|
|
|
best_ftr = aMR_FurnitureFg_to_FurnitureFgWithDirect(ftr, mRmTp_DIRECT_SOUTH);
|
|
break;
|
|
}
|
|
else {
|
|
selected--;
|
|
}
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
island_room++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return best_ftr;
|
|
}
|
|
|
|
static int mNpc_CheckIslandAnimalTableNo(mActor_name_t npc_id) {
|
|
int res = FALSE;
|
|
|
|
if (
|
|
ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC &&
|
|
(npc_id & 0xFFF) < NPC_NUM &&
|
|
npc_grow_list[npc_id & 0xFFF] == mNpc_GROW_ISLANDER
|
|
) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_CheckIslandAnimalID(AnmPersonalID_c* anm_id) {
|
|
int res = FALSE;
|
|
|
|
if (anm_id != NULL && mNpc_CheckFreeAnimalPersonalID(anm_id) == FALSE) {
|
|
res = mNpc_CheckIslandAnimalTableNo(anm_id->npc_id);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
extern int mNpc_CheckIslandAnimal(Animal_c* animal) {
|
|
int res = FALSE;
|
|
|
|
if (animal != NULL) {
|
|
res = mNpc_CheckIslandAnimalID(&animal->id);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static mActor_name_t l_mnpc_island_md_table[NPC_ISLANDER_NUM] = {
|
|
ITM_MINIDISK16, // K.K. Tango
|
|
ITM_MINIDISK08, // K.K. Aria
|
|
ITM_MINIDISK06, // K.K. Etude
|
|
ITM_MINIDISK33, // K.K. Soul
|
|
ITM_MINIDISK26, // K.K. Rock
|
|
ITM_MINIDISK15, // K.K. Ska
|
|
ITM_MINIDISK07, // K.K. Lullaby
|
|
ITM_MINIDISK05, // K.K. Fusion
|
|
ITM_MINIDISK43, // Comrade K.K.
|
|
ITM_MINIDISK09, // K.K. Samba
|
|
ITM_MINIDISK49, // Cafe K.K.
|
|
ITM_MINIDISK13, // K.K. Mambo
|
|
ITM_MINIDISK01, // K.K. March
|
|
ITM_MINIDISK44, // K.K. Lament
|
|
ITM_MINIDISK40, // K.K. Country
|
|
ITM_MINIDISK10, // K.K. Bossa
|
|
ITM_MINIDISK18, // Aloha K.K.
|
|
ITM_MINIDISK46 // K.K. Dirge
|
|
};
|
|
|
|
extern u32 mNpc_GetMDIdx(mActor_name_t npc_id) {
|
|
mNpc_Island_Npc_Room_Data_c* room_data = l_island_npc_room_data;
|
|
u32 idx = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < NPC_ISLANDER_NUM; i++) {
|
|
if (room_data->npc_id == npc_id) {
|
|
idx = (u16)(l_mnpc_island_md_table[i] - ITM_MINIDISK_START);
|
|
// TODO: Bug? missing break here
|
|
}
|
|
|
|
room_data++;
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
|
|
extern u32 mNpc_GetIslandMDIdx() {
|
|
return mNpc_GetMDIdx(Save_Get(island).animal.id.npc_id);
|
|
}
|
|
|
|
static Mail_c l_npc_hp_mail;
|
|
|
|
extern void mNpc_ClearHPMail(AnmHPMail_c* hp_mail, int count) {
|
|
bzero(hp_mail, count * sizeof(AnmHPMail_c));
|
|
}
|
|
|
|
extern void mNpc_AllClearHPMailPlayerIdx(int player_no) {
|
|
Animal_c* animal = Save_Get(animals);
|
|
int i;
|
|
|
|
if (player_no >= 0 && player_no < PLAYER_NUM) {
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
mNpc_ClearHPMail(&animal->hp_mail[player_no], 1);
|
|
animal++;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int mNpc_ReceiveHPMail(Mail_c* mail) {
|
|
static u8 password[mMpswd_PASSWORD_DATA_LEN];
|
|
|
|
Animal_c* animal = Save_Get(animals);
|
|
AnmPersonalID_c anm_id;
|
|
int res = FALSE;
|
|
int private_idx = mPr_GetPrivateIdx(&mail->header.sender.personalID);
|
|
|
|
if (mail->header.sender.type == mMl_NAME_TYPE_PLAYER && private_idx >= 0 && private_idx < PLAYER_NUM) {
|
|
int is_password = mMpswd_restore_code(mail->content.body, password);
|
|
|
|
if (is_password == TRUE && mMl_get_npcinfo_from_mail_name(&anm_id, &mail->header.recipient) == TRUE) {
|
|
int npc_idx = mNpc_SearchAnimalinfo(animal, anm_id.npc_id, ANIMAL_NUM_MAX);
|
|
|
|
if (npc_idx != -1) {
|
|
animal = &animal[npc_idx];
|
|
|
|
if (animal != NULL) {
|
|
/* @BUG - the final byte of the password should not be stored. */
|
|
#ifndef BUGFIXES
|
|
bcopy(password, animal->hp_mail[private_idx].password, mMpswd_PASSWORD_DATA_LEN);
|
|
#else
|
|
bcopy(password, animal->hp_mail[private_idx].password, sizeof(animal->hp_mail[private_idx].password));
|
|
#endif
|
|
lbRTC_TimeCopy(&animal->hp_mail[private_idx].receive_time, Common_GetPointer(time.rtc_time));
|
|
res = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_famicom(PersonalID_c* pid, AnmPersonalID_c* anm_id, mMpswd_password_c* password) {
|
|
static u8 name_str[mIN_ITEM_NAME_LEN];
|
|
|
|
Mail_c* hp_mail = &l_npc_hp_mail;
|
|
int mail_no;
|
|
mActor_name_t present = EMPTY_NO;
|
|
int res = FALSE;
|
|
|
|
if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mMl_clear_mail(hp_mail);
|
|
|
|
if (
|
|
password->hit_rate_index == 1 && // hit rate index must be 1
|
|
mMpswd_password_zuru_check(password) == FALSE && // checksum is valid
|
|
mMpswd_check_present(password) == TRUE && // present is valid
|
|
mMpswd_check_name(password) == TRUE // player & town name match
|
|
) {
|
|
mail_no = 0x24A + anm_id->looks; // valid password
|
|
present = password->item;
|
|
}
|
|
else {
|
|
mail_no = 0x250 + anm_id->looks; // invalid password
|
|
}
|
|
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, name_str, ANIMAL_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR2, password->str1, PLAYER_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR3, password->str0, PLAYER_NAME_LEN);
|
|
|
|
if (present != EMPTY_NO) {
|
|
mNpc_ClearBufSpace1(name_str, mIN_ITEM_NAME_LEN);
|
|
mIN_copy_name_str(name_str, present);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR4, name_str, mIN_ITEM_NAME_LEN);
|
|
}
|
|
|
|
/* Why is free string 6 the same as free string 1? */
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR6, name_str, ANIMAL_NAME_LEN);
|
|
|
|
mNpc_LoadMailDataCommon2(hp_mail, pid, anm_id, present, mNpc_GetPaperType(), mail_no);
|
|
res = mPO_receipt_proc(hp_mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_popular(PersonalID_c* pid, AnmPersonalID_c* anm_id, mMpswd_password_c* password) {
|
|
static u8 name_str[mIN_ITEM_NAME_LEN];
|
|
static u8 submenu_name_str[mIN_ITEM_NAME_LEN];
|
|
|
|
Mail_c* hp_mail = &l_npc_hp_mail;
|
|
mActor_name_t npc_id;
|
|
int mail_no;
|
|
mActor_name_t present;
|
|
u8 valid;
|
|
u8 special;
|
|
mActor_name_t target_npc_id;
|
|
int npc_idx;
|
|
int looks;
|
|
int res;
|
|
|
|
present = EMPTY_NO;
|
|
res = FALSE;
|
|
valid = FALSE;
|
|
special = FALSE;
|
|
|
|
if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mMl_clear_mail(hp_mail);
|
|
|
|
if (
|
|
mMpswd_password_zuru_check(password) == FALSE && // checksum is valid
|
|
mMpswd_check_npc_code(password) == TRUE && // valid npc code
|
|
mMpswd_check_present(password) == TRUE && // present is valid
|
|
mMpswd_check_name(password) == TRUE // player & town name match
|
|
) {
|
|
/* Valid password */
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_ClearBufSpace1(submenu_name_str, ANIMAL_NAME_LEN);
|
|
|
|
if (password->npc_type == mMpswd_NPCTYPE_NORMAL) {
|
|
mActor_name_t npc_id = anm_id->npc_id;
|
|
|
|
npc_idx = npc_id & 0xFFF;
|
|
target_npc_id = NPC_START | password->npc_code;
|
|
looks = mNpc_GetLooks(target_npc_id);
|
|
|
|
if (npc_idx == password->npc_code) {
|
|
mail_no = 0x256 + looks;
|
|
}
|
|
else if (mNpc_CheckIslandAnimalTableNo(target_npc_id) == TRUE) {
|
|
mail_no = 0x262 + looks;
|
|
}
|
|
else {
|
|
looks = mNpc_GetLooks(target_npc_id); // re-calling the function?
|
|
mail_no = 0x25C + looks;
|
|
}
|
|
|
|
mNpc_GetNpcWorldNameTableNo(name_str, NPC_START | password->npc_code);
|
|
mNpc_GetNpcWorldNameTableNo(submenu_name_str, NPC_START | password->npc_code);
|
|
valid = TRUE;
|
|
}
|
|
else {
|
|
/* Special NPC */
|
|
mail_no = 0x268 + password->npc_code;
|
|
npc_id = mMpswd_get_sp_npc_num(password->npc_code);
|
|
mNpc_GetActorWorldName(name_str, npc_id);
|
|
mNpc_GetActorWorldName(submenu_name_str, npc_id);
|
|
|
|
/* Leftover from JP, replace ちん (-san) honorific with spaces */
|
|
if (submenu_name_str[4] == 0x0A && submenu_name_str[5] == 0xC3) {
|
|
submenu_name_str[4] = CHAR_SPACE;
|
|
submenu_name_str[5] = CHAR_SPACE;
|
|
}
|
|
|
|
valid = TRUE;
|
|
special = TRUE;
|
|
}
|
|
|
|
|
|
if (password->item != RSV_NO) {
|
|
present = password->item;
|
|
}
|
|
}
|
|
else {
|
|
/* Invalid password */
|
|
mail_no = 0x288 + anm_id->looks;
|
|
}
|
|
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, name_str, ANIMAL_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR2, password->str1, PLAYER_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR3, password->str0, PLAYER_NAME_LEN);
|
|
|
|
if (present != EMPTY_NO) {
|
|
mNpc_ClearBufSpace1(name_str, mIN_ITEM_NAME_LEN);
|
|
mIN_copy_name_str(name_str, present);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR4, name_str, mIN_ITEM_NAME_LEN);
|
|
}
|
|
|
|
/* Why is free string 6 the same as free string 1? */
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR6, name_str, ANIMAL_NAME_LEN);
|
|
|
|
mNpc_LoadMailDataCommon2(hp_mail, pid, anm_id, present, mNpc_GetPaperType(), mail_no);
|
|
|
|
if (valid == TRUE) {
|
|
// Overwrite the sender name with the name of the password's embedded villager name
|
|
bcopy(submenu_name_str, hp_mail->header.sender.personalID.player_name, PLAYER_NAME_LEN);
|
|
bcopy(pid->player_name, hp_mail->header.recipient.personalID.player_name, PLAYER_NAME_LEN);
|
|
|
|
if (special == TRUE) {
|
|
hp_mail->content.mail_type = mMl_TYPE_SPNPC_PASSWORD;
|
|
}
|
|
}
|
|
|
|
res = mPO_receipt_proc(hp_mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_CheckHit_Rate(u8 hitrate, int n) {
|
|
u8 perfect_bit = 0xFF;
|
|
int res = FALSE;
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
perfect_bit &= ~(1 << i);
|
|
}
|
|
|
|
if ((hitrate & perfect_bit) == 0) {
|
|
res = TRUE;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_GetHit(u8 hitrate) {
|
|
static u8 prob_table[] = { 80, 60, 30, 0, 100 };
|
|
|
|
int res = FALSE;
|
|
|
|
if (hitrate < ARRAY_COUNT(prob_table)) {
|
|
u8 hit = RANDOM(100);
|
|
|
|
if (hit < prob_table[hitrate]) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static void mNPC_get_gobi_str_from_name(u8* str, mActor_name_t npc_id) {
|
|
if (str != NULL && ITEM_NAME_GET_TYPE(npc_id) == NAME_TYPE_NPC) {
|
|
int idx = npc_id & 0xFFF;
|
|
|
|
/* @BUG - devs used && instead of || */
|
|
#ifndef BUGFIXES
|
|
if (idx < 0 && idx >= NPC_NUM) {
|
|
#else
|
|
if (idx < 0 || idx >= NPC_NUM) {
|
|
#endif
|
|
idx = 0;
|
|
}
|
|
|
|
mString_Load_StringFromRom(str, ANIMAL_CATCHPHRASE_LEN, npc_def_list[idx].catchphrase_str_idx);
|
|
}
|
|
}
|
|
|
|
static int mNpc_GetHit_cardE(u8 hitrate) {
|
|
static int prob_table[] = { 80, 60, 40, 20 };
|
|
|
|
int res = FALSE;
|
|
|
|
if (hitrate < ARRAY_COUNT(prob_table)) {
|
|
u8 hit = RANDOM(100);
|
|
|
|
if (hit < prob_table[hitrate]) {
|
|
res = TRUE;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_cardE(PersonalID_c* pid, AnmPersonalID_c* anm_id, mMpswd_password_c* password) {
|
|
static u8 submenu_name_str[mIN_ITEM_NAME_LEN];
|
|
static u8 name_str[mIN_ITEM_NAME_LEN];
|
|
static u8 gobi_str[ANIMAL_CATCHPHRASE_LEN];
|
|
|
|
Mail_c* hp_mail = &l_npc_hp_mail;
|
|
int mail_no;
|
|
mActor_name_t present = EMPTY_NO;
|
|
int res = FALSE;
|
|
u8 valid = FALSE;
|
|
u8 special = FALSE;
|
|
int famicom_hit;
|
|
|
|
if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mMl_clear_mail(hp_mail);
|
|
|
|
if (
|
|
mMpswd_password_zuru_check(password) == FALSE && // checksum is valid
|
|
mMpswd_check_present(password) == TRUE && // present is valid
|
|
mMpswd_check_npc_code(password) == TRUE // valid npc code
|
|
) {
|
|
/* Valid password */
|
|
famicom_hit = FALSE;
|
|
mNpc_ClearBufSpace1(submenu_name_str, ANIMAL_NAME_LEN);
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_ClearBufSpace1(gobi_str, ANIMAL_CATCHPHRASE_LEN);
|
|
|
|
if (password->npc_type == mMpswd_NPCTYPE_NORMAL) {
|
|
mActor_name_t target_npc_id = NPC_START | password->npc_code;
|
|
int villager_idx = mNpc_SearchAnimalinfo(Save_Get(animals), target_npc_id, ANIMAL_NUM_MAX);
|
|
int islander_idx = mNpc_SearchAnimalinfo(&Save_Get(island).animal, target_npc_id, 1);
|
|
|
|
if (
|
|
(villager_idx != -1 || islander_idx != -1) &&
|
|
(
|
|
mNpc_GetAnimalMemoryIdx(&Common_Get(now_private)->player_ID, Save_Get(animals[villager_idx]).memories, ANIMAL_MEMORY_NUM) != -1 ||
|
|
mNpc_GetAnimalMemoryIdx(&Common_Get(now_private)->player_ID, Save_Get(island).animal.memories, ANIMAL_MEMORY_NUM) != -1
|
|
)
|
|
) {
|
|
int looks = mNpc_GetLooks(target_npc_id);
|
|
|
|
if (mNpc_GetHit_cardE(password->hit_rate_index)) {
|
|
famicom_hit = TRUE;
|
|
mail_no = mNpc_CheckIslandAnimalTableNo(target_npc_id) == TRUE ? 0x3CA + looks : 0x3C4 + looks;
|
|
mNpc_GetNpcWorldNameTableNo(name_str, NPC_START | password->npc_code);
|
|
mNpc_GetNpcWorldNameTableNo(submenu_name_str, NPC_START | password->npc_code);
|
|
valid = TRUE;
|
|
}
|
|
}
|
|
|
|
if (famicom_hit == FALSE) {
|
|
mail_no = 0x2B2 + password->npc_code;
|
|
mNpc_GetNpcWorldNameTableNo(name_str, NPC_START | password->npc_code);
|
|
mNpc_GetNpcWorldNameTableNo(submenu_name_str, NPC_START | password->npc_code);
|
|
valid = TRUE;
|
|
}
|
|
|
|
mNPC_get_gobi_str_from_name(gobi_str, target_npc_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR5, gobi_str, ANIMAL_CATCHPHRASE_LEN);
|
|
}
|
|
else {
|
|
/* Special NPC */
|
|
mActor_name_t npc_id;
|
|
|
|
mail_no = 0x39E + password->npc_code;
|
|
npc_id = mMpswd_get_sp_npc_num(password->npc_code);
|
|
mNpc_GetActorWorldName(name_str, npc_id);
|
|
mNpc_GetActorWorldName(submenu_name_str, npc_id);
|
|
|
|
/* Leftover from JP, replace ちん (-san) honorific with spaces */
|
|
if (submenu_name_str[4] == 0x0A && submenu_name_str[5] == 0xC3) {
|
|
submenu_name_str[4] = CHAR_SPACE;
|
|
submenu_name_str[5] = CHAR_SPACE;
|
|
}
|
|
|
|
valid = TRUE;
|
|
special = TRUE;
|
|
}
|
|
|
|
if (famicom_hit == FALSE && mMpswd_check_present(password) == TRUE) {
|
|
mActor_name_t item = password->item;
|
|
|
|
if (item == RSV_NO) {
|
|
present = EMPTY_NO;
|
|
}
|
|
else {
|
|
present = item;
|
|
}
|
|
}
|
|
else if (famicom_hit == TRUE) {
|
|
int famicom_idx = RANDOM_F(8);
|
|
|
|
present = mRmTp_FtrIdx2FtrItemNo(pswd_famicom_list[famicom_idx], mRmTp_DIRECT_SOUTH);
|
|
}
|
|
}
|
|
else {
|
|
/* Invalid password */
|
|
mail_no = 0x3BE + anm_id->looks;
|
|
}
|
|
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, name_str, ANIMAL_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR2, password->str1, PLAYER_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR3, password->str0, PLAYER_NAME_LEN);
|
|
|
|
if (present != EMPTY_NO) {
|
|
mNpc_ClearBufSpace1(name_str, mIN_ITEM_NAME_LEN);
|
|
mIN_copy_name_str(name_str, present);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR4, name_str, mIN_ITEM_NAME_LEN);
|
|
}
|
|
|
|
/* Why is free string 6 the same as free string 1? */
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR6, name_str, ANIMAL_NAME_LEN);
|
|
|
|
mNpc_LoadMailDataCommon2(hp_mail, pid, anm_id, present, mNpc_GetPaperType(), mail_no);
|
|
|
|
if (valid == TRUE) {
|
|
// Overwrite the sender name with the name of the password's embedded villager name
|
|
bcopy(submenu_name_str, hp_mail->header.sender.personalID.player_name, PLAYER_NAME_LEN);
|
|
bcopy(pid->player_name, hp_mail->header.recipient.personalID.player_name, PLAYER_NAME_LEN);
|
|
|
|
if (special == TRUE) {
|
|
hp_mail->content.mail_type = mMl_TYPE_SPNPC_PASSWORD;
|
|
}
|
|
}
|
|
|
|
res = mPO_receipt_proc(hp_mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_NG(PersonalID_c* pid, AnmPersonalID_c* anm_id) {
|
|
static u8 name_str[mIN_ITEM_NAME_LEN];
|
|
|
|
Mail_c* hp_mail = &l_npc_hp_mail;
|
|
int mail_no;
|
|
int res = FALSE;
|
|
|
|
if (anm_id != NULL && mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mMl_clear_mail(hp_mail);
|
|
mail_no = 0x3BE + anm_id->looks;
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR6, name_str, ANIMAL_NAME_LEN);
|
|
mNpc_LoadMailDataCommon2(hp_mail, pid, anm_id, EMPTY_NO, mNpc_GetPaperType(), mail_no);
|
|
res = mPO_receipt_proc(hp_mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_cardE_mini_user_password_common(PersonalID_c* pid, AnmPersonalID_c* anm_id) {
|
|
static u8 name_str[ANIMAL_NAME_LEN];
|
|
|
|
Mail_c* hp_mail = &l_npc_hp_mail;
|
|
int looks = anm_id->looks;
|
|
int res = FALSE;
|
|
|
|
if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE && looks < mNpc_LOOKS_NUM) {
|
|
mMl_clear_mail(hp_mail);
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR6, name_str, ANIMAL_NAME_LEN);
|
|
mNpc_LoadMailDataCommon2(hp_mail, pid, anm_id, EMPTY_NO, mNpc_GetPaperType(), 0x3D0 + looks);
|
|
res = mPO_receipt_proc(hp_mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_user_password(PersonalID_c* pid, AnmPersonalID_c* anm_id, mMpswd_password_c* password) {
|
|
if (
|
|
mMpswd_password_zuru_check(password) == FALSE &&
|
|
mMpswd_check_present(password) == TRUE &&
|
|
mMpswd_check_name(password) == TRUE
|
|
) {
|
|
return mNpc_SendHPMailNum_cardE_mini_user_password_common(pid, anm_id);
|
|
}
|
|
|
|
return mNpc_SendHPMailNum_NG(pid, anm_id);
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_cardE_mini(PersonalID_c* pid, AnmPersonalID_c* anm_id, mMpswd_password_c* password) {
|
|
if (mMpswd_password_zuru_check(password) == FALSE) {
|
|
return mNpc_SendHPMailNum_cardE_mini_user_password_common(pid, anm_id);
|
|
}
|
|
|
|
return mNpc_SendHPMailNum_NG(pid, anm_id);
|
|
}
|
|
|
|
static int mNpc_SendHPMailNum_magazine(PersonalID_c* pid, AnmPersonalID_c* anm_id, mMpswd_password_c* password) {
|
|
static u8 name_str[mIN_ITEM_NAME_LEN];
|
|
|
|
Mail_c* hp_mail = &l_npc_hp_mail;
|
|
int mail_no;
|
|
mActor_name_t present = EMPTY_NO;
|
|
int res = FALSE;
|
|
|
|
if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) {
|
|
mMl_clear_mail(hp_mail);
|
|
|
|
if (
|
|
mNpc_CheckHit_Rate(password->hit_rate_index, 3) == TRUE && // hitrate index is valid
|
|
mMpswd_password_zuru_check(password) == FALSE && // checksum is valid
|
|
mMpswd_check_present(password) == TRUE // present is valid
|
|
) {
|
|
/* Valid password */
|
|
if (mNpc_GetHit(password->hit_rate_index) == TRUE) {
|
|
/* Player "won" */
|
|
mail_no = 0x2A0 + anm_id->looks;
|
|
|
|
if (password->item != RSV_NO) {
|
|
present = password->item;
|
|
}
|
|
}
|
|
else {
|
|
/* Player did not "win" */
|
|
mail_no = 0x2A6 + anm_id->looks;
|
|
}
|
|
}
|
|
else {
|
|
/* Invalid password */
|
|
mail_no = 0x2AC + anm_id->looks;
|
|
}
|
|
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR0, pid->player_name, PLAYER_NAME_LEN);
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR1, name_str, ANIMAL_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR2, password->str1, PLAYER_NAME_LEN);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR3, password->str0, PLAYER_NAME_LEN);
|
|
|
|
if (present != EMPTY_NO) {
|
|
mNpc_ClearBufSpace1(name_str, mIN_ITEM_NAME_LEN);
|
|
mIN_copy_name_str(name_str, present);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR4, name_str, mIN_ITEM_NAME_LEN);
|
|
}
|
|
|
|
/* Why is free string 6 the same as free string 1? */
|
|
mNpc_ClearBufSpace1(name_str, ANIMAL_NAME_LEN);
|
|
mNpc_GetNpcWorldNameAnm(name_str, anm_id);
|
|
mHandbill_Set_free_str(mHandbill_FREE_STR6, name_str, ANIMAL_NAME_LEN);
|
|
|
|
mNpc_LoadMailDataCommon2(hp_mail, pid, anm_id, present, mNpc_GetPaperType(), mail_no);
|
|
res = mPO_receipt_proc(hp_mail, mPO_SENDTYPE_MAIL);
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
typedef int (*mNpc_HPMAIL_SEND_PROC)(PersonalID_c*, AnmPersonalID_c*, mMpswd_password_c*);
|
|
|
|
static int mNpc_SendHPMail_analysis(PersonalID_c* pid, AnmPersonalID_c* anm_id, AnmHPMail_c* hp_mail) {
|
|
static mNpc_HPMAIL_SEND_PROC send_proc[mMpswd_CODETYPE_NUM] = {
|
|
&mNpc_SendHPMailNum_famicom,
|
|
&mNpc_SendHPMailNum_popular,
|
|
&mNpc_SendHPMailNum_cardE,
|
|
&mNpc_SendHPMailNum_magazine,
|
|
&mNpc_SendHPMailNum_user_password,
|
|
&mNpc_SendHPMailNum_cardE_mini
|
|
};
|
|
|
|
mMpswd_password_c password;
|
|
|
|
mMpswd_password(hp_mail->password, &password);
|
|
|
|
if (password.type < mMpswd_CODETYPE_NUM) {
|
|
return (*send_proc[password.type])(pid, anm_id, &password);
|
|
}
|
|
|
|
return mNpc_SendHPMailNum_NG(pid, anm_id);
|
|
}
|
|
|
|
extern void mNpc_SendHPMail() {
|
|
Private_c* priv = Save_Get(private);
|
|
Private_c* priv_p;
|
|
Animal_c* animal = Save_Get(animals);
|
|
AnmHPMail_c* hp_mail;
|
|
lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time);
|
|
int i;
|
|
int j;
|
|
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) {
|
|
hp_mail = animal->hp_mail;
|
|
priv_p = priv;
|
|
|
|
for (j = 0; j < ANIMAL_HP_MAIL_NUM; j++) {
|
|
if (hp_mail->receive_time.year != 0 && hp_mail->receive_time.day != 0 && mPr_NullCheckPersonalID(&priv_p->player_ID) == FALSE) {
|
|
int days;
|
|
|
|
if (lbRTC_IsOverTime(&hp_mail->receive_time, rtc_time) == lbRTC_OVER) {
|
|
days = lbRTC_GetIntervalDays(&hp_mail->receive_time, rtc_time);
|
|
}
|
|
else {
|
|
days = lbRTC_GetIntervalDays(rtc_time, &hp_mail->receive_time);
|
|
}
|
|
|
|
if (days >= 1 && mNpc_SendHPMail_analysis(&priv_p->player_ID, &animal->id, hp_mail) == TRUE) {
|
|
mNpc_ClearHPMail(hp_mail, 1);
|
|
}
|
|
}
|
|
|
|
priv_p++;
|
|
hp_mail++;
|
|
}
|
|
}
|
|
|
|
animal++;
|
|
}
|
|
}
|
|
|
|
extern void mNpc_PrintRemoveInfo(gfxprint_t* gfxprint) {
|
|
Animal_c* remove_anm = NULL;
|
|
Animal_c* in_anm = mNpc_GetInAnimalP();
|
|
u8 remove_idx = Save_Get(remove_animal_idx);
|
|
|
|
gfxprint_color(gfxprint, 200, 150, 50, 255);
|
|
gfxprint_locate8x8(gfxprint, 3, 10);
|
|
|
|
if (remove_idx < ANIMAL_NUM_MAX) {
|
|
remove_anm = Save_GetPointer(animals[remove_idx]);
|
|
}
|
|
|
|
if (remove_anm != NULL) {
|
|
if (in_anm != NULL) {
|
|
gfxprint_printf(gfxprint, "%4x %3d %3d", remove_idx, remove_anm->id.name_id, in_anm->id.name_id);
|
|
}
|
|
else {
|
|
gfxprint_printf(gfxprint, "%4x %3d ---", remove_idx, remove_anm->id.name_id);
|
|
}
|
|
}
|
|
else if (in_anm != NULL) {
|
|
gfxprint_printf(gfxprint, "%4x --- %3d", remove_idx, in_anm->id.name_id);
|
|
}
|
|
else {
|
|
gfxprint_printf(gfxprint, "%4x --- ---", remove_idx);
|
|
}
|
|
}
|
|
|
|
static int l_mnpc_display = 0;
|
|
|
|
typedef struct npc_add_debug_s {
|
|
int add_switch;
|
|
u16 memory;
|
|
u8 count;
|
|
u8 add_bit;
|
|
u8 edit_bit;
|
|
int mtype;
|
|
int disp_add;
|
|
} mNpc_Add_Debug_c;
|
|
|
|
static mNpc_Add_Debug_c l_mnpc_addd;
|
|
|
|
/* @unused mNpc_set_switch 1.2.5 size: 0x14 */
|
|
/* @unused mNpc_clear_addd 1.2.5 size: 0x44 */
|
|
/* @unused mNpc_set_addd 1.2.5 size: 0x0C */
|
|
|
|
/* @unused @fabricated */
|
|
extern void mNpc_set_addd_bit(int bit) {
|
|
l_mnpc_addd.add_bit |= (1 << bit);
|
|
}
|
|
|
|
/* @unused */
|
|
extern void mNpc_set_addd_edit_bit(int bit) {
|
|
l_mnpc_addd.edit_bit |= (1 << bit);
|
|
}
|
|
|
|
/* @unused */
|
|
extern void mNpc_set_addd_edit_info(int mtype, int disp_add) {
|
|
l_mnpc_addd.mtype = mtype;
|
|
l_mnpc_addd.disp_add = disp_add;
|
|
}
|
|
|
|
/* @unused mNpc_set_addd_memory 1.2.5 size: 0x34 */
|
|
/* @unused mNpc_set_addd_count 1.2.5 size: 0x10 */
|
|
|
|
static void mNpc_PrintFriendship_fdebug_1(gfxprint_t* gfxprint) {
|
|
int add_switch = l_mnpc_addd.add_switch;
|
|
int i;
|
|
|
|
gfxprint_color(gfxprint, 250, 100, 170, 255);
|
|
gfxprint_locate8x8(gfxprint, 3, 18);
|
|
|
|
if (add_switch == 0) {
|
|
gfxprint_printf(gfxprint, "----------------------- %d", l_mnpc_addd.add_switch);
|
|
}
|
|
else if (add_switch == 1) {
|
|
gfxprint_printf(gfxprint, "*********************** %d", l_mnpc_addd.add_switch);
|
|
}
|
|
else if (add_switch == 2) {
|
|
gfxprint_printf(gfxprint, ">>>>>>>>>>>>>>>>>>>>>>> %d", l_mnpc_addd.add_switch);
|
|
}
|
|
else {
|
|
gfxprint_printf(gfxprint, "%2d ", l_mnpc_addd.count);
|
|
for (i = 0; i < ANIMAL_NUM_MAX; i++) {
|
|
gfxprint_printf(gfxprint, "%1d", (l_mnpc_addd.memory >> i) & 1);
|
|
}
|
|
|
|
gfxprint_locate8x8(gfxprint, 3, 19);
|
|
gfxprint_printf(gfxprint, "add bit ");
|
|
for (i = 0; i < 8; i++) {
|
|
gfxprint_printf(gfxprint, "%1d", (l_mnpc_addd.add_bit >> i) & 1);
|
|
}
|
|
|
|
gfxprint_printf(gfxprint, " edit bit ");
|
|
for (i = 0; i < 8; i++) {
|
|
gfxprint_printf(gfxprint, "%1d", (l_mnpc_addd.edit_bit >> i) & 1);
|
|
}
|
|
|
|
gfxprint_locate8x8(gfxprint, 3, 20);
|
|
gfxprint_printf(gfxprint, "mtype %d disp_add 0x%x", l_mnpc_addd.mtype, l_mnpc_addd.disp_add);
|
|
}
|
|
}
|
|
|
|
static int l_mnpc_animal_idx = -1;
|
|
|
|
extern void mNpc_SetTalkAnimalIdx_fdebug(AnmPersonalID_c* anm_id) {
|
|
if (anm_id != NULL) {
|
|
l_mnpc_animal_idx = mNpc_SearchAnimalPersonalID(anm_id);
|
|
}
|
|
}
|
|
|
|
static void mNpc_PrintFriendship_fdebug_0(gfxprint_t* gfxprint) {
|
|
Anmmem_c* memory;
|
|
Private_c* priv;
|
|
int x = 3;
|
|
int y = 19;
|
|
int i;
|
|
|
|
if (l_mnpc_animal_idx >= 0 && l_mnpc_animal_idx < ANIMAL_NUM_MAX) {
|
|
memory = Save_Get(animals[l_mnpc_animal_idx]).memories;
|
|
|
|
if (memory != NULL) {
|
|
gfxprint_color(gfxprint, 250, 100, 170, 255);
|
|
gfxprint_locate8x8(gfxprint, 3, 18);
|
|
priv = Common_Get(now_private);
|
|
|
|
if (priv != NULL) {
|
|
gfxprint_printf(gfxprint, "%4x ", priv->player_ID.player_id);
|
|
}
|
|
else {
|
|
gfxprint_printf(gfxprint, "**** ");
|
|
}
|
|
|
|
gfxprint_color(gfxprint, 50, 150, 50, 255);
|
|
|
|
for (i = 0; i < ANIMAL_MEMORY_NUM; i++) {
|
|
if (i == 4) {
|
|
y++;
|
|
x = 3;
|
|
}
|
|
|
|
gfxprint_locate8x8(gfxprint, x, y);
|
|
if (mNpc_CheckFreeAnimalMemory(memory) == FALSE) {
|
|
gfxprint_printf(gfxprint, "%4x %3d ", memory->memory_player_id.player_id, memory->friendship);
|
|
}
|
|
else {
|
|
gfxprint_printf(gfxprint, "**** *** ");
|
|
}
|
|
|
|
memory++;
|
|
x += 9;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef void (*mNpc_PRINT_FRIENDSHIP_PROC)(gfxprint_t*);
|
|
|
|
extern void mNpc_PrintFriendship_fdebug(gfxprint_t* gfxprint) {
|
|
static mNpc_PRINT_FRIENDSHIP_PROC print_proc[2] = {
|
|
&mNpc_PrintFriendship_fdebug_0,
|
|
&mNpc_PrintFriendship_fdebug_1
|
|
};
|
|
|
|
(*print_proc[l_mnpc_display & 1])(gfxprint);
|
|
}
|