diff --git a/data/behavior_table.c b/data/behavior_table.c index fbe918cd..41a88c6f 100644 --- a/data/behavior_table.c +++ b/data/behavior_table.c @@ -520,6 +520,7 @@ const BehaviorScript* gBehaviorTable[id_bhv_max_count] = { enum BehaviorId get_id_from_behavior(const BehaviorScript* behavior) { + if (behavior == NULL) { return 0; } return (enum BehaviorId)(behavior[1] & 0xFFFF); } diff --git a/src/game/behaviors/donut_platform.inc.c b/src/game/behaviors/donut_platform.inc.c index 526a23a9..5e807ca2 100644 --- a/src/game/behaviors/donut_platform.inc.c +++ b/src/game/behaviors/donut_platform.inc.c @@ -33,7 +33,6 @@ void bhv_donut_platform_update(void) { network_init_object(o, 4000.0f); network_init_object_field(o, &o->oGravity); network_init_object_field(o, &o->oIntangibleTimer); - network_init_object_field(o, &o->header.gfx.node.flags); } struct Object* player = nearest_player_to_object(o); diff --git a/src/game/behaviors/enemy_lakitu.inc.c b/src/game/behaviors/enemy_lakitu.inc.c index 120d2efe..ac1e3bc9 100644 --- a/src/game/behaviors/enemy_lakitu.inc.c +++ b/src/game/behaviors/enemy_lakitu.inc.c @@ -109,6 +109,7 @@ static void enemy_lakitu_sub_act_no_spiny(void) { cur_obj_init_animation_with_sound(1); + o->oEnemyLakituNumSpinies = cur_obj_count_objects_with_behavior(bhvSpiny, 2000); if (o->oEnemyLakituSpinyCooldown != 0) { o->oEnemyLakituSpinyCooldown -= 1; } else if (o->oEnemyLakituNumSpinies < 3 && distanceToPlayer < 800.0f @@ -140,6 +141,12 @@ static void enemy_lakitu_sub_act_no_spiny(void) { * enter the throw spiny sub-action. */ static void enemy_lakitu_sub_act_hold_spiny(void) { + if (o->prevObj == NULL) { + o->oSubAction = ENEMY_LAKITU_SUB_ACT_NO_SPINY; + o->oEnemyLakituSpinyCooldown = 0; + return; + } + struct Object* player = nearest_player_to_object(o); int distanceToPlayer = dist_between_objects(o, player); int angleToPlayer = obj_angle_to_object(o, player); @@ -191,6 +198,12 @@ static void enemy_lakitu_act_main(void) { obj_update_blinking(&o->oEnemyLakituBlinkTimer, 20, 40, 4); + if (o->prevObj != NULL) { + if (o->prevObj->behavior != bhvSpiny || o->prevObj->activeFlags == ACTIVE_FLAG_DEACTIVATED) { + o->prevObj = NULL; + } + } + switch (o->oSubAction) { case ENEMY_LAKITU_SUB_ACT_NO_SPINY: enemy_lakitu_sub_act_no_spiny(); @@ -203,7 +216,11 @@ static void enemy_lakitu_act_main(void) { break; } - cur_obj_move_standard(78); + struct Object* player = nearest_player_to_object(o); + int distanceToPlayer = dist_between_objects(o, player); + if (distanceToPlayer <= o->oDrawingDistance) { + cur_obj_move_standard(78); + } // Die and drop held spiny when attacked by mario if (obj_check_attacks(&sEnemyLakituHitbox, o->oAction)) { @@ -222,7 +239,6 @@ void bhv_enemy_lakitu_update(void) { network_init_object_field(o, &o->oEnemyLakituBlinkTimer); network_init_object_field(o, &o->oEnemyLakituSpinyCooldown); network_init_object_field(o, &o->oEnemyLakituFaceForwardCountdown); - network_init_object_field(o, &o->oEnemyLakituNumSpinies); } treat_far_home_as_mario(2000.0f, NULL, NULL); diff --git a/src/game/behaviors/spiny.inc.c b/src/game/behaviors/spiny.inc.c index 6548eb72..b3354cf2 100644 --- a/src/game/behaviors/spiny.inc.c +++ b/src/game/behaviors/spiny.inc.c @@ -33,12 +33,56 @@ static u8 sSpinyWalkAttackHandlers[] = { /* ATTACK_FROM_BELOW: */ ATTACK_HANDLER_KNOCKBACK, }; +static u32 spinyAnimCache = 0; + +static void spiny_to_anim_cache(void) { + if (o->oAnimations == (struct Animation**)spiny_egg_seg5_anims_050157E4) { + spinyAnimCache = 0; + } else if (o->oAnimations == (struct Animation**)spiny_seg5_anims_05016EAC) { + spinyAnimCache = 1; + } else { + assert(false); + } +} + +static void spiny_from_anim_cache(void) { + struct Animation** anim = NULL; + switch (spinyAnimCache) { + case 0: + anim = (struct Animation**)spiny_egg_seg5_anims_050157E4; + break; + case 1: + anim = (struct Animation**)spiny_seg5_anims_05016EAC; + break; + default: + assert(false); + } + + if (anim != o->oAnimations) { + obj_init_animation_with_sound(o, (const struct Animation* const*)anim, 0); + } +} + +static void bhv_spiny_on_received_post(UNUSED u8 localIndex) { + spiny_from_anim_cache(); +} + +static void bhv_spiny_on_sent_pre(void) { + spiny_to_anim_cache(); +} + /** * If the spiny was spawned by lakitu and mario is far away, despawn. */ static s32 spiny_check_active(void) { struct Object* player = nearest_player_to_object(o); int distanceToPlayer = dist_between_objects(o, player); + + if (o->parentObj == NULL || o->parentObj->behavior != bhvEnemyLakitu) { + obj_mark_for_deletion(o); + return FALSE; + } + if (o->parentObj != o) { if (distanceToPlayer > 2500.0f) { //! It's possible for the lakitu to despawn while the spiny still @@ -48,7 +92,7 @@ static s32 spiny_check_active(void) { // behave similar to a regular goomba. // It can also be used on a bob-omb respawner to change its model // to a butterfly or fish. - o->parentObj->oEnemyLakituNumSpinies -= 1; + //o->parentObj->oEnemyLakituNumSpinies -= 1; obj_mark_for_deletion(o); return FALSE; } @@ -65,6 +109,7 @@ static void spiny_act_walk(void) { cur_obj_update_floor_and_walls(); o->oGraphYOffset = -17.0f; + o->oFaceAnglePitch = 0; cur_obj_init_animation_with_sound(0); if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) { @@ -132,7 +177,7 @@ static void spiny_act_held_by_lakitu(void) { o->oParentRelativePosY = 35.0f; o->oParentRelativePosZ = -100.0f; - if (o->parentObj->prevObj == NULL) { + if (o->parentObj != NULL && o->parentObj->prevObj == NULL) { o->oAction = SPINY_ACT_THROWN_BY_LAKITU; o->oMoveAngleYaw = o->parentObj->oFaceAngleYaw; @@ -173,7 +218,7 @@ static void spiny_act_thrown_by_lakitu(void) { if (obj_check_attacks(&sSpinyHitbox, o->oAction)) { if (o->parentObj != o) { - o->parentObj->oEnemyLakituNumSpinies -= 1; + //o->parentObj->oEnemyLakituNumSpinies -= 1; } } } @@ -193,7 +238,10 @@ void bhv_spiny_update(void) { if (!network_sync_object_initialized(o)) { struct SyncObject* so = network_init_object(o, 4000.0f); so->syncDeathEvent = FALSE; + so->on_received_post = bhv_spiny_on_received_post; + so->on_sent_pre = bhv_spiny_on_sent_pre; so->override_ownership = bhv_spiny_override_ownership; + network_init_object_field(o, &o->oGraphYOffset); network_init_object_field(o, &o->oFaceAngleYaw); network_init_object_field(o, &o->oSpinyTimeUntilTurn); @@ -202,13 +250,17 @@ void bhv_spiny_update(void) { network_init_object_field(o, &o->oMoveFlags); network_init_object_field(o, &o->oInteractType); network_init_object_field(o, &o->oFaceAnglePitch); + network_init_object_field(o, &spinyAnimCache); struct Object* lakitu = cur_obj_nearest_object_with_behavior(bhvEnemyLakitu); if (lakitu != NULL) { - lakitu->prevObj = o; - o->oAction = SPINY_ACT_HELD_BY_LAKITU; - obj_init_animation_with_sound(o, spiny_egg_seg5_anims_050157E4, 0); + o->parentObj = lakitu; + if ((lakitu->oSubAction == ENEMY_LAKITU_SUB_ACT_HOLD_SPINY) && (o->oAction == SPINY_ACT_HELD_BY_LAKITU)) { + lakitu->prevObj = o; + o->oAction = SPINY_ACT_HELD_BY_LAKITU; + obj_init_animation_with_sound(o, spiny_egg_seg5_anims_050157E4, 0); + } } } diff --git a/src/game/object_helpers.c b/src/game/object_helpers.c index b9870877..7c65739e 100644 --- a/src/game/object_helpers.c +++ b/src/game/object_helpers.c @@ -959,6 +959,30 @@ struct Object *cur_obj_find_nearest_object_with_behavior(const BehaviorScript *b return closestObj; } +u16 cur_obj_count_objects_with_behavior(const BehaviorScript* behavior, f32 dist) { + u16 numObjs = 0; + uintptr_t* behaviorAddr = segmented_to_virtual(behavior); + struct Object* obj; + struct ObjectNode* listHead; + + listHead = &gObjectLists[get_object_list_from_behavior(behaviorAddr)]; + obj = (struct Object*)listHead->next; + + while (obj != (struct Object*)listHead) { + if (obj->behavior == behaviorAddr) { + if (obj->activeFlags != ACTIVE_FLAG_DEACTIVATED && obj != o) { + f32 objDist = dist_between_objects(o, obj); + if (objDist < dist) { + numObjs++; + } + } + } + obj = (struct Object*)obj->header.next; + } + + return numObjs; +} + struct Object *find_unimportant_object(void) { struct ObjectNode *listHead = &gObjectLists[OBJ_LIST_UNIMPORTANT]; struct ObjectNode *obj = listHead->next; diff --git a/src/game/object_helpers.h b/src/game/object_helpers.h index 477871de..5f3a81c4 100644 --- a/src/game/object_helpers.h +++ b/src/game/object_helpers.h @@ -145,6 +145,7 @@ struct Object *cur_obj_nearest_object_with_behavior(const BehaviorScript *behavi f32 cur_obj_dist_to_nearest_object_with_behavior(const BehaviorScript* behavior); struct Object* cur_obj_find_nearest_pole(void); struct Object *cur_obj_find_nearest_object_with_behavior(const BehaviorScript * behavior, f32 *dist); +u16 cur_obj_count_objects_with_behavior(const BehaviorScript* behavior, f32 dist); struct Object *find_unimportant_object(void); s32 count_unimportant_objects(void); s32 count_objects_with_behavior(const BehaviorScript *behavior); diff --git a/src/pc/network/packets/packet_debug_sync.c b/src/pc/network/packets/packet_debug_sync.c index 4ab1d855..d52c03dc 100644 --- a/src/pc/network/packets/packet_debug_sync.c +++ b/src/pc/network/packets/packet_debug_sync.c @@ -24,7 +24,7 @@ void network_send_debug_sync(void) { packet_write(&p, &objectCount, sizeof(u8)); for (int i = 0; i < MAX_SYNC_OBJECTS; i++) { if (gSyncObjects[i].o == NULL) { continue; } - u16 behaviorId = get_id_from_behavior(gSyncObjects[i].behavior); + u16 behaviorId = get_id_from_behavior((gSyncObjects[i].behavior == NULL) ? gSyncObjects[i].behavior : gSyncObjects[i].o->behavior); packet_write(&p, &i, sizeof(u8)); packet_write(&p, &behaviorId, sizeof(u16)); }