static void aHNW_setupAction(ACTOR* actor, GAME* game, int action);

static int aHNW_set_save_permission() {
    u32 player_no = Common_Get(player_no);
    PersonalID_c* pid;
    mHm_hs_c* house;
    int res = FALSE;

    if (player_no < PLAYER_NUM) {
        int arrange_idx = mHS_get_arrange_idx(player_no);
        house = Save_GetPointer(homes[arrange_idx]);
        pid = &Save_Get(private_data[player_no]).player_ID;

        if (mPr_NullCheckPersonalID(pid) != TRUE && mPr_CheckCmpPersonalID(pid, &house->ownerID) == TRUE) {
            res = TRUE;
            house->flags.has_saved = TRUE;
        }
    }

    return res;
}

static void aHNW_search_player(ACTOR* actor) {
    chase_angle(&actor->shape_info.rotation.y, actor->player_angle_y, 0x0600);
}

static void aHNW_search_front(ACTOR* actor, int house_idx) {
    s16 target_angle[mHS_HOUSE_NUM] = { 8000, -8000, 8000, -8000 };

    chase_angle(&actor->shape_info.rotation.y, target_angle[house_idx], 0x0600);
}

static int aHNW_check_keep_item(ACTOR* actor) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    Haniwa_Item_c* haniwa_item = Save_Get(homes[haniwa->house_idx]).haniwa.items;
    int i;
    int res = FALSE;

    for (i = 0; i < HANIWA_ITEM_HOLD_NUM; i++) {
        if (haniwa_item->item != EMPTY_NO) {
            res = TRUE;
            break;
        }

        haniwa_item++;
    }

    return res;
}

static void aHNW_set_proceeds_str(Haniwa_c* haniwa) {
    u8 str[7];

    mFont_UnintToString(str, 7, haniwa->bells, 6, TRUE, FALSE, TRUE);
    mMsg_Set_free_str(mMsg_Get_base_window_p(), 0, str, 7);
}

static int aHNW_check_handOver_proceeds(Haniwa_c* haniwa) {
    int handOver = FALSE;
    u32 money = Common_Get(now_private)->inventory.wallet + haniwa->bells;

    if (money < mPr_WALLET_MAX) {
        Common_Get(now_private)->inventory.wallet = money;
        handOver = TRUE;
    } else {
        u32 num_30k_bell_bags = (money - mPr_WALLET_MAX) / 30000 + 1;

        if (mPr_GetPossessionItemSumWithCond(Common_Get(now_private), EMPTY_NO, mPr_ITEM_COND_NORMAL) >=
            num_30k_bell_bags) {
            handOver = TRUE;

            for (num_30k_bell_bags; num_30k_bell_bags != 0; num_30k_bell_bags--) {
                mPr_SetFreePossessionItem(Common_Get(now_private), ITM_MONEY_30000, mPr_ITEM_COND_NORMAL);
                money -= 30000;
            }

            Common_Get(now_private)->inventory.wallet = money;
        } else {
            u8 str[5];

            mFont_UnintToString(str, 5, num_30k_bell_bags, 5, TRUE, FALSE, TRUE);
            mMsg_Set_free_str(mMsg_Get_base_window_p(), 1, str, 5);
        }
    }

    return handOver;
}

static int aHNW_check_house_door(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    int res = FALSE;
    GAME_PLAY* play = (GAME_PLAY*)game;

    if (Common_Get(reset_flag) == TRUE) {
        ACTOR* talk_actor = mDemo_Get_talk_actor();

        if (talk_actor == NULL && chkTrigger(BUTTON_A)) {
            PLAYER_ACTOR* player = get_player_actor_withoutCheck(play);

            if (mDemo_Check_DiffAngle_forTalk((actor->player_angle_y - player->actor_class.shape_info.rotation.y) -
                                              -0x8000) == TRUE) {
                Common_Set(reset_type, 4);
            }
        }

        res = TRUE;
    } else {
        mDemo_Clip_c* demo_clip = Common_Get(clip).demo_clip;

        if (demo_clip != NULL) {
            INTRO_DEMO_ACTOR* demo_class = (INTRO_DEMO_ACTOR*)demo_clip->demo_class;
            if (demo_class != NULL && demo_clip->type == mDemo_CLIP_TYPE_INTRO_DEMO && mEv_CheckFirstIntro() &&
                demo_class->player_intro_demo_state != 0) {
                res = TRUE;
            }
        }
    }

    return res;
}

static void aHNW_wait(ACTOR* actor, GAME* game) {
    if (actor->player_distance_xz < 80.0f) {
        aHNW_setupAction(actor, game, aHNW_ACTION_DANCE);
    }
}

static int aHNW_decide_msg_idx_dance(ACTOR* actor) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    Haniwa_c* house_haniwa;
    int house_idx = haniwa->house_idx;
    int player_is_owner = Common_Get(player_no) == mHS_get_pl_no(haniwa->house_idx);
    int res;

    if (mPr_NullCheckPersonalID(&Save_Get(homes[house_idx]).ownerID) == TRUE) {
        res = aHNW_MSG_NO_OWNER; /* no one owns this house */
    } else if (player_is_owner == TRUE) {
        Haniwa_c* house_haniwa = &Save_Get(homes[house_idx]).haniwa;
        int house_arrange_idx = mHS_get_arrange_idx(Common_Get(player_no));
        mHm_hs_c* house = Save_GetPointer(homes[house_arrange_idx]);
        if (house->flags.has_saved == FALSE && mEv_CheckFirstJob() == TRUE &&
            mNpc_GetFriendAnimalNum(&Common_Get(now_private)->player_ID) == 0) {
            res =
                aHNW_MSG_NEED_FRIEND; /* player owns this house, but is in intro and has not spoken to any villagers */
        } else if (house_haniwa->bells != 0) {
            res = aHNW_MSG_PROCEEDS;
        } else {
            res = aHNW_MSG_NORMAL;
        }

        /* player owns house but gyroid has bells from other players */
    } else {
        res = aHNW_MSG_OTHER_OWNER; /* another player owns house */
    }

    return res;
}

static void aHNW_set_talk_info_dance(ACTOR* actor) {
    static int msg_no[aHNW_MSG_NUM] = {
        0x0934, /* aHNW_MSG_NO_OWNER */
        0x0935, /* aHNW_MSG_PROCEEDS */
        0x0925, /* aHNW_MSG_NORMAL */
        0x0928, /* aHNW_MSG_OTHER_OWNER */
        0x092E  /* aHNW_MSG_NEED_FRIEND */
    };

    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    int house_idx = haniwa->house_idx;
    Haniwa_c* house_haniwa = &Save_Get(homes[house_idx]).haniwa;
    int msg_idx = aHNW_decide_msg_idx_dance(actor);

    switch (msg_idx) {
        case aHNW_MSG_PROCEEDS: {
            aHNW_set_proceeds_str(house_haniwa);
            break;
        }

        case aHNW_MSG_OTHER_OWNER: {
            mMsg_Set_mail_str(mMsg_Get_base_window_p(), 0, house_haniwa->message, HANIWA_MESSAGE_LEN);
            break;
        }
    }

    mDemo_Set_msg_num(msg_no[msg_idx]);
}

static void aHNW_dance(ACTOR* actor, GAME* game) {
    static int next_act_idx[aHNW_MSG_NUM] = {
        aHNW_ACTION_TALK_END_WAIT,    /* aHNW_MSG_NO_OWNER */
        aHNW_ACTION_CHECK_PROCEEDS,   /* aHNW_MSG_PROCEEDS */
        aHNW_ACTION_TALK_WITH_MASTER, /* aHNW_MSG_NORMAL */
        aHNW_ACTION_TALK_WITH_GUEST,  /* aHNW_MSG_OTHER_OWNER */
        aHNW_ACTION_TALK_END_WAIT     /* aHNW_MSG_NEED_FRIEND */
    };

    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;

    if (actor->player_distance_xz > 90.0f) {
        aHNW_setupAction(actor, game, aHNW_ACTION_WAIT);
    } else {
        if (mDemo_Check(mDemo_TYPE_TALK, (ACTOR*)haniwa) == TRUE && mDemo_Check_ListenAble() == FALSE) {
            int msg_idx = aHNW_decide_msg_idx_dance(actor);
            mDemo_Set_ListenAble();
            aHNW_setupAction(actor, game, next_act_idx[msg_idx]);
        } else if (aHNW_check_house_door((ACTOR*)haniwa, game) == FALSE) {
            mDemo_Request(mDemo_TYPE_TALK, (ACTOR*)haniwa, &aHNW_set_talk_info_dance);
        }
    }
}

static void aHNW_check_proceeds(ACTOR* actor, GAME* game) {
    static int msg_no[aHNW_HANDOVER_NUM] = {
        0x0936, /* aHNW_HANDOVER_YES */
        0x0937  /* aHNW_HANDOVER_NO */
    };

    static int next_act_idx[aHNW_HANDOVER_NUM] = {
        aHNW_ACTION_TALK_WITH_MASTER, /* aHNW_HANDOVER_YES */
        aHNW_ACTION_TALK_END_WAIT     /* aHNW_HANDOVER_NO */
    };

    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;

    if (mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9)) {
        int house_idx = haniwa->house_idx;
        Haniwa_c* house_haniwa = &Save_Get(homes[house_idx]).haniwa;
        int handover_state;

        if (aHNW_check_handOver_proceeds(house_haniwa) == TRUE) {
            house_haniwa->bells = 0;
            handover_state = aHNW_HANDOVER_YES;
        } else {
            handover_state = aHNW_HANDOVER_NO;
        }

        mMsg_Set_continue_msg_num(mMsg_Get_base_window_p(), msg_no[handover_state]);
        aHNW_setupAction(actor, game, next_act_idx[handover_state]);
        mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0);
    }
}

static void aHNW_talk_with_master(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    mMsg_Window_c* msg_win = mMsg_Get_base_window_p();

    if (mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9) && mMsg_Check_MainNormalContinue(msg_win) == TRUE) {
        int action = -1;

        switch (mChoice_Get_ChoseNum(mChoice_Get_base_window_p())) {
            case mChoice_CHOICE0: {
                action = aHNW_ACTION_SAVE_CHECK;
                break;
            }

            case mChoice_CHOICE1: {
                action = aHNW_ACTION_MENU_OPEN_WAIT;
                haniwa->submenu_type = mSM_OVL_INVENTORY;
                break;
            }

            case mChoice_CHOICE2: {
                action = aHNW_ACTION_TALK_WITH_MASTER2;
                break;
            }

            case mChoice_CHOICE3: {
                action = aHNW_ACTION_TALK_END_WAIT;
                break;
            }
        }

        if (action != -1) {
            mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0);
            aHNW_setupAction(actor, game, action);
        }
    }
}

static void aHNW_talk_with_master2(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    mMsg_Window_c* msg_win = mMsg_Get_base_window_p();

    if (mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9) && mMsg_Check_MainNormalContinue(msg_win) == TRUE) {
        int action = -1;

        switch (mChoice_Get_ChoseNum(mChoice_Get_base_window_p())) {
            case mChoice_CHOICE0: {
                action = aHNW_ACTION_ROOF_CHECK;
                break;
            }

            case mChoice_CHOICE1: {
                action = aHNW_ACTION_MENU_OPEN_WAIT;
                haniwa->submenu_type = mSM_OVL_HBOARD;
                break;
            }

            case mChoice_CHOICE2: {
                action = aHNW_ACTION_TALK_WITH_MASTER;
                break;
            }

            case mChoice_CHOICE3: {
                action = aHNW_ACTION_TALK_END_WAIT;
                break;
            }
        }

        if (action != -1) {
            mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0);
            aHNW_setupAction(actor, game, action);
        }
    }
}

static void aHNW_talk_end_wait(ACTOR* actor, GAME* game) {
    if (mDemo_Check(mDemo_TYPE_TALK, actor) == FALSE) {
        aHNW_setupAction(actor, game, aHNW_ACTION_DANCE);
    }
}

static void aHNW_menu_open_wait(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;
    mMsg_Window_c* msg_win = mMsg_Get_base_window_p();

    if (mMsg_Check_main_wait(msg_win) == TRUE) {
        int submenu_type = haniwa->submenu_type;
        Submenu* submenu = &play->submenu;
        int arg1 = haniwa->house_idx;

        switch (submenu_type) {
            case mSM_OVL_HBOARD: {
                mSM_open_submenu(submenu, mSM_OVL_HBOARD, 0, arg1);
                break;
            }

            case mSM_OVL_INVENTORY: {
                mSM_open_submenu(submenu, mSM_OVL_INVENTORY, 2, arg1);
                break;
            }

            case mSM_OVL_NEEDLEWORK: {
                mSM_open_submenu(submenu, mSM_OVL_NEEDLEWORK, 0, arg1);
                break;
            }
        }

        mMsg_ChangeMsgData(msg_win, 0x0927);
        mMsg_Set_ForceNext(msg_win);
        aHNW_setupAction(actor, game, aHNW_ACTION_MENU_END_WAIT);
    }
}

static void aHNW_menu_end_wait(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;
    Submenu* submenu = &play->submenu;
    Submenu_Item_c* item;

    if (submenu->open_flag == FALSE) {
        if (mMsg_Check_not_series_main_wait(mMsg_Get_base_window_p()) == TRUE) {
            aHNW_setupAction(actor, game, aHNW_ACTION_TALK_WITH_MASTER);
            switch (haniwa->submenu_type) {
                case mSM_OVL_NEEDLEWORK: {
                    Submenu_Item_c* item = submenu->item_p;
                    if (item->item == RSV_NO) {
                        Save_Get(homes[haniwa->house_idx]).door_original = mNW_get_image_no(submenu, item->slot_no);
                        sAdo_SysTrgStart(0x461);
                    }

                    break;
                }
            }

            haniwa->submenu_type = mSM_OVL_NONE;
        }
    }
}

static void aHNW_talk_with_guest(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;

    if (mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 0)) {
        mMsg_Window_c* msg_win = mMsg_Get_base_window_p();
        if (mMsg_Check_MainNormalContinue(msg_win) == TRUE) {
            int action = -1;

            switch (mChoice_Get_ChoseNum(mChoice_Get_base_window_p())) {
                case mChoice_CHOICE0: {
                    if (aHNW_check_keep_item(actor) == FALSE) {
                        mMsg_Set_continue_msg_num(msg_win, 0x092C);
                        action = aHNW_ACTION_TALK_END_WAIT;
                    } else {
                        action = aHNW_ACTION_MENU_OPEN_WAIT_FOR_GUEST;
                    }
                    break;
                }

                case mChoice_CHOICE1: {
                    mMsg_Set_continue_msg_num(msg_win, 0x092A);
                    action = aHNW_ACTION_TALK_END_WAIT;
                    break;
                }
            }

            if (action != -1) {
                mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 0, 0);
                aHNW_setupAction(actor, game, action);
            }
        }
    }
}

static void aHNW_menu_open_wait_for_guest(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;
    mMsg_Window_c* msg_win = mMsg_Get_base_window_p();

    if (mMsg_Check_main_wait(msg_win) == TRUE) {
        mSM_open_submenu(&play->submenu, mSM_OVL_INVENTORY, 3, haniwa->house_idx);
        mMsg_ChangeMsgData(msg_win, 0x092B);
        aHNW_setupAction(actor, game, aHNW_ACTION_MENU_END_WAIT_FOR_GUEST);
    }
}

static void aHNW_menu_end_wait_for_guest(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;

    if (play->submenu.open_flag == FALSE && mMsg_Check_not_series_main_wait(mMsg_Get_base_window_p()) == TRUE) {
        aHNW_setupAction(actor, game, aHNW_ACTION_TALK_END_WAIT);
    }
}

static void aHNW_save_check(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    mMsg_Window_c* msg_win = mMsg_Get_base_window_p();
    int order_value = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9);
    int action = -1;

    if (order_value && mMsg_Check_MainNormalContinue(msg_win) == TRUE) {
        switch (mChoice_Get_ChoseNum(mChoice_Get_base_window_p())) {
            case mChoice_CHOICE0: {
                action = aHNW_ACTION_SAVE_END_WAIT;
                mBGMPsComp_scene_mode(13);
                mDemo_Set_talk_return_demo_wait(1);
                mPlib_Set_able_force_speak_label(actor);
                break;
            }

            case mChoice_CHOICE1: {
                action = aHNW_ACTION_TALK_WITH_MASTER;
                break;
            }
        }

        if (action != -1) {
            mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0);
            aHNW_setupAction(actor, game, action);
        }
    }
}

static void aHNW_roof_check(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    mMsg_Window_c* msg_win = mMsg_Get_base_window_p();
    int order_value = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9);
    int action = -1;

    if (order_value && mMsg_Check_MainNormalContinue(msg_win) == TRUE) {
        switch (mChoice_Get_ChoseNum(mChoice_Get_base_window_p())) {
            case mChoice_CHOICE0: {
                action = aHNW_ACTION_MENU_OPEN_WAIT;
                haniwa->submenu_type = mSM_OVL_NEEDLEWORK;
                break;
            }

            case mChoice_CHOICE1: {
                Save_Get(homes[haniwa->house_idx]).door_original = 0xFF;
                sAdo_SysTrgStart(0x461);
                action = aHNW_ACTION_TALK_WITH_MASTER;
                break;
            }

            case mChoice_CHOICE2: {
                action = aHNW_ACTION_TALK_WITH_MASTER;
                break;
            }
        }

        if (action != -1) {
            mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0);
            aHNW_setupAction(actor, game, action);
        }
    }
}

static void aHNW_save_end_wait(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;

    if (haniwa->playing_save_bgm == FALSE && mMsg_Check_MainDisappear(mMsg_Get_base_window_p())) {
        haniwa->playing_save_bgm = TRUE;
        mBGMPsComp_make_ps_demo(0x41, 0x168);
    }

    if (mDemo_Check(mDemo_TYPE_TALK, actor) == FALSE) {
        aHNW_setupAction(actor, game, aHNW_ACTION_PL_APPROACH_DOOR);
        aHNW_set_save_permission();
    }
}

static void aHNW_pl_approach_door(ACTOR* actor, GAME* game) {
    static f32 chk_posX[mHS_HOUSE_NUM] = { 2095.0f, 2385.0f, 2095.0f, 2385.0f };
    static f32 chk_val[mHS_HOUSE_NUM] = { 1.0f, -1.0f, 1.0f, -1.0f };
    static xyz_t goal_pos[mHS_HOUSE_NUM][2] = { { { 2098.0f, 0.0f, 1540.0f }, { 2110.0f, 0.0f, 1474.0f } },
                                                { { 2382.0f, 0.0f, 1540.0f }, { 2369.0f, 0.0f, 1474.0f } },
                                                { { 2098.0f, 0.0f, 1820.0f }, { 2110.0f, 0.0f, 1755.0f } },
                                                { { 2382.0f, 0.0f, 1820.0f }, { 2369.0f, 0.0f, 1755.0f } } };

    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;
    PLAYER_ACTOR* player = get_player_actor_withoutCheck((GAME_PLAY*)game);

    haniwa->door_approach_frame++;

    if (player != NULL) {
        int house_idx = haniwa->house_idx;
        int stage = ((chk_posX[house_idx] - player->actor_class.world.position.x) * chk_val[house_idx]) <= 0.0f;
        xyz_t* goal = goal_pos[house_idx] + stage;

        if (haniwa->player_approach_door_stage != stage &&
            mPlib_request_main_demo_walk_type1(game, goal->x, goal->z, 3.0f, FALSE)) {
            haniwa->player_approach_door_stage = stage;
        }

        mPlib_Set_goal_player_demo_walk(goal->x, goal->z, 3.0f);

        if (haniwa->door_approach_frame > 160) {
            MY_HOUSE_ACTOR* house_actor = (MY_HOUSE_ACTOR*)Actor_info_fgName_search(
                &play->actor_info, HOUSE0 + haniwa->house_idx, ACTOR_PART_ITEM);

            if (house_actor != NULL) {
                house_actor->structure_class.actor_class.world.angle.z = 1;
                aHNW_setupAction(actor, game, aHNW_ACTION_WAIT);
            }
        } else if (stage == 1 && search_position_distanceXZ(goal, &player->actor_class.world.position) < 3.0f) {
            aHNW_setupAction(actor, game, aHNW_ACTION_DOOR_OPEN_WAIT);
        }
    }
}

static void aHNW_door_open_wait(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;
    MY_HOUSE_ACTOR* house_actor =
        (MY_HOUSE_ACTOR*)Actor_info_fgName_search(&play->actor_info, HOUSE0 + haniwa->house_idx, ACTOR_PART_ITEM);

    if (house_actor != NULL) {
        house_actor->structure_class.request_type = 6; // TODO: this is probably an enum
        if (house_actor != get_player_actor_withoutCheck((GAME_PLAY*)gamePT)->get_door_label_proc(gamePT)) {
            aHNW_setupAction(actor, game, aHNW_ACTION_DOOR_OPEN_TIMER);
            haniwa->door_approach_frame = 0;
        } else {
            aHNW_setupAction(actor, game, aHNW_ACTION_WAIT);
        }
    }
}

static void aHNW_door_open_timer(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;
    MY_HOUSE_ACTOR* house_actor =
        (MY_HOUSE_ACTOR*)Actor_info_fgName_search(&play->actor_info, HOUSE0 + haniwa->house_idx, ACTOR_PART_ITEM);

    if (house_actor != NULL) {
        if (house_actor == get_player_actor_withoutCheck((GAME_PLAY*)gamePT)->get_door_label_proc(gamePT)) {
            aHNW_setupAction(actor, game, aHNW_ACTION_WAIT);
        } else {
            haniwa->door_approach_frame++;
            if (haniwa->door_approach_frame > 80) {
                house_actor->structure_class.actor_class.world.angle.z = 1;
                aHNW_setupAction(actor, game, aHNW_ACTION_WAIT);
            }
        }
    }
}

static void aHNW_menu_open_wait_init(ACTOR* actor, GAME* game) {
    mMsg_request_main_disappear_wait_type1(mMsg_Get_base_window_p());
}

static void aHNW_menu_end_wait_init(ACTOR* actor, GAME* game) {
    mMsg_request_main_appear_wait_type1(mMsg_Get_base_window_p());
}

static void aHNW_talk_with_guest_init(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;

    mMsg_Set_free_str(mMsg_Get_base_window_p(), 2, Save_Get(homes[haniwa->house_idx]).ownerID.player_name,
                      PLAYER_NAME_LEN);
}

static void aHNW_pl_approach_door_init(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;

    game->pad_initialized = FALSE;
    haniwa->player_approach_door_stage = -1;
    haniwa->door_approach_frame = 0;
}

typedef void (*HANIWA_PROC)(ACTOR*, GAME*);

static void aHNW_init_proc(ACTOR* actor, GAME* game, int action) {
    static HANIWA_PROC init_proc[aHNW_ACTION_NUM] = {
        (HANIWA_PROC)&none_proc1,   (HANIWA_PROC)&none_proc1,  (HANIWA_PROC)&none_proc1,    (HANIWA_PROC)&none_proc1,
        (HANIWA_PROC)&none_proc1,   (HANIWA_PROC)&none_proc1,  &aHNW_menu_open_wait_init,   &aHNW_menu_end_wait_init,
        &aHNW_talk_with_guest_init, &aHNW_menu_open_wait_init, &aHNW_menu_end_wait_init,    (HANIWA_PROC)&none_proc1,
        (HANIWA_PROC)&none_proc1,   (HANIWA_PROC)&none_proc1,  &aHNW_pl_approach_door_init, (HANIWA_PROC)&none_proc1,
        (HANIWA_PROC)&none_proc1
    };

    (*init_proc[action])(actor, game);
}

static void aHNW_setupAction(ACTOR* actor, GAME* game, int action) {
    static HANIWA_PROC process[aHNW_ACTION_NUM] = { &aHNW_wait,
                                                    &aHNW_dance,
                                                    &aHNW_check_proceeds,
                                                    &aHNW_talk_with_master,
                                                    &aHNW_talk_with_master2,
                                                    &aHNW_talk_end_wait,
                                                    &aHNW_menu_open_wait,
                                                    &aHNW_menu_end_wait,
                                                    &aHNW_talk_with_guest,
                                                    &aHNW_menu_open_wait_for_guest,
                                                    &aHNW_menu_end_wait_for_guest,
                                                    &aHNW_roof_check,
                                                    &aHNW_save_check,
                                                    &aHNW_save_end_wait,
                                                    &aHNW_pl_approach_door,
                                                    &aHNW_door_open_wait,
                                                    &aHNW_door_open_timer };

    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;

    int house_idx = haniwa->house_idx;
    int animation_state = haniwa->common_actor_class.anime.anime_no;
    int no_owner = mPr_NullCheckPersonalID(&Save_Get(homes[house_idx]).ownerID);
    int owner_is_player = mHS_get_pl_no(house_idx) == Common_Get(player_no);

    haniwa->common_actor_class.act = action;
    haniwa->action_proc = process[action];

    if (action >= aHNW_ACTION_CHECK_PROCEEDS) {
        haniwa->anim_frame_speed = 0.3f;
    } else {
        if (no_owner) {
            if (haniwa->anim_frame_speed != 0.0f) {
                haniwa->anim_frame_speed = 0.075f;
            }
        } else {
            if (owner_is_player) {
                if (action == aHNW_ACTION_WAIT || action == aHNW_ACTION_DOOR_OPEN_TIMER) {
                    haniwa->anim_frame_speed = 0.3f;
                } else {
                    haniwa->anim_frame_speed = 0.45f;
                }
            } else {
                haniwa->anim_frame_speed = 0.1f;
            }
        }
    }

    if (animation_state == 2) {
        cKF_SkeletonInfo_R_init(&haniwa->common_actor_class.anime.keyframe,
                                haniwa->common_actor_class.anime.keyframe.skeleton, &cKF_ba_r_hnw_move, 1.0f, 9.0f,
                                1.0f, haniwa->anim_frame_speed, 0.0f, cKF_FRAMECONTROL_REPEAT, NULL);
        haniwa->common_actor_class.anime.cur_keyframe =
            haniwa->common_actor_class.anime.keyframe.frame_control.current_frame;
    }

    haniwa->common_actor_class.anime.anime_no = 0;
    aHNW_init_proc(actor, game, action);
    if (no_owner && action < aHNW_ACTION_CHECK_PROCEEDS) {
        haniwa->common_actor_class.anime.keyframe.frame_control.mode = cKF_FRAMECONTROL_STOP;
    } else {
        haniwa->common_actor_class.anime.keyframe.frame_control.mode = cKF_FRAMECONTROL_REPEAT;
    }
}

static void aHNW_common_process(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    cKF_SkeletonInfo_R_c* keyframe = &haniwa->common_actor_class.anime.keyframe;
    int house_idx = haniwa->house_idx;
    int no_owner = mPr_NullCheckPersonalID(&Save_Get(homes[house_idx]).ownerID);
    f32 target;

    if (no_owner == FALSE && keyframe->frame_control.mode == cKF_FRAMECONTROL_STOP) {
        aHNW_setupAction((ACTOR*)haniwa, game, haniwa->common_actor_class.act);
        keyframe->frame_control.mode = cKF_FRAMECONTROL_REPEAT;
    } else if (no_owner && haniwa->common_actor_class.act < 2 && keyframe->frame_control.speed <= 0.1f) {
        keyframe->frame_control.mode = cKF_FRAMECONTROL_STOP;
    } else {
        keyframe->frame_control.mode = cKF_FRAMECONTROL_REPEAT;
    }

    if (no_owner == FALSE || haniwa->common_actor_class.act >= aHNW_ACTION_CHECK_PROCEEDS) {
        aHNW_search_player((ACTOR*)haniwa);
    } else {
        aHNW_search_front((ACTOR*)haniwa, house_idx);
    }

    target = haniwa->anim_frame_speed;

    {
        if (target > keyframe->frame_control.speed) {
            chase_f(&keyframe->frame_control.speed, target, 0.05f);
        } else {
            chase_f(&keyframe->frame_control.speed, target, 0.015f);
        }
    }
}

static void aHNW_actor_move(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    GAME_PLAY* play = (GAME_PLAY*)game;
    cKF_SkeletonInfo_R_c* keyframe = &haniwa->common_actor_class.anime.keyframe;

    haniwa->common_actor_class.anime.state = cKF_SkeletonInfo_R_play(keyframe);
    (*haniwa->action_proc)((ACTOR*)haniwa, game);
    aHNW_common_process(actor, game);
    CollisionCheck_Uty_ActorWorldPosSetPipeC(actor, &haniwa->common_actor_class.col_pipe);
    CollisionCheck_setOC(game, &play->collision_check, &haniwa->common_actor_class.col_pipe.collision_obj);
    Actor_world_to_eye(actor, 50.0f);
}

static void aHNW_actor_init(ACTOR* actor, GAME* game) {
    HANIWA_ACTOR* haniwa = (HANIWA_ACTOR*)actor;
    int house_idx = haniwa->house_idx;

    mFI_SetFG_common((mActor_name_t)(house_idx + DUMMY_HANIWA0), actor->world.position, FALSE);
    actor->mv_proc = &aHNW_actor_move;
    actor->dw_proc = &aHNW_actor_draw;
    aHNW_setupAction((ACTOR*)haniwa, game,
                     aHNW_ACTION_WAIT); // weird that we have to re-cast to ACTOR so fequently for matches
    haniwa->common_actor_class.anime.keyframe.morph_counter = 0.0f;
    aHNW_actor_move(actor, game);
}
