diff --git a/assets/bigbug/menu/bb_menu0.png b/assets/bigbug/menu/bb_menu0.png index d47a023c6..9a3a720e1 100644 Binary files a/assets/bigbug/menu/bb_menu0.png and b/assets/bigbug/menu/bb_menu0.png differ diff --git a/assets/bigbug/menu/bb_menu2.png b/assets/bigbug/menu/bb_menu2.png index db7aad01c..2bb29a67f 100644 Binary files a/assets/bigbug/menu/bb_menu2.png and b/assets/bigbug/menu/bb_menu2.png differ diff --git a/assets/bigbug/menu/bb_menu3.png b/assets/bigbug/menu/bb_menu3.png index d7c0e8d0b..0deaa7afe 100644 Binary files a/assets/bigbug/menu/bb_menu3.png and b/assets/bigbug/menu/bb_menu3.png differ diff --git a/assets/bigbug/music/BigBug_Boss.mid b/assets/bigbug/music/BigBug_Boss.mid index 37cfe6030..67641969f 100644 Binary files a/assets/bigbug/music/BigBug_Boss.mid and b/assets/bigbug/music/BigBug_Boss.mid differ diff --git a/main/modes/games/bigbug/entityManager_bigbug.c b/main/modes/games/bigbug/entityManager_bigbug.c index acdd0b0e9..a467323e6 100644 --- a/main/modes/games/bigbug/entityManager_bigbug.c +++ b/main/modes/games/bigbug/entityManager_bigbug.c @@ -236,6 +236,10 @@ void bb_loadSprites(bb_entityManager_t* entityManager) entityManager->sprites[BB_HOTDOG].allocated = true; entityManager->sprites[BB_HOTDOG].brightnessLevels = 1; } + + // final boss is unloaded until needed. + entityManager->sprites[BB_FINAL_BOSS].originX = 30; + entityManager->sprites[BB_FINAL_BOSS].originY = 40; } void bb_updateEntities(bb_entityManager_t* entityManager, bb_camera_t* camera) @@ -275,11 +279,6 @@ void bb_updateEntities(bb_entityManager_t* entityManager, bb_camera_t* camera) } break; } - case BB_GRABBY_HAND: - { - bb_loadSprite("grab", 3, 1, &entityManager->sprites[BB_GRABBY_HAND]); - break; - } case BB_DOOR: { bb_loadSprite("door", 2, 1, &entityManager->sprites[BB_DOOR]); @@ -295,11 +294,7 @@ void bb_updateEntities(bb_entityManager_t* entityManager, bb_camera_t* camera) bb_foodCartData_t* fcData = (bb_foodCartData_t*)curEntity->data; // tell this partner of the change in address ((bb_foodCartData_t*)fcData->partner->data)->partner = foundSpot; - if (((bb_foodCartData_t*)fcData->partner->data)->isCached) - { - bb_loadSprite("foodCart", 2, 1, &entityManager->sprites[BB_FOOD_CART]); - } - fcData->isCached = false; + bb_loadSprite("foodCart", 2, 1, &entityManager->sprites[BB_FOOD_CART]); break; } default: @@ -404,6 +399,11 @@ void bb_updateEntities(bb_entityManager_t* entityManager, bb_camera_t* camera) { curEntity->updateFarFunction(curEntity); } + else if (curEntity->spriteIndex == BB_GRABBY_HAND && !entityManager->sprites[BB_GRABBY_HAND].allocated) + { + bb_loadSprite("grab", 3, 1, &entityManager->sprites[BB_GRABBY_HAND]); + curEntity->drawFunction = bb_drawGrabbyHand; + } } if (curEntity->collisions != NULL) @@ -568,14 +568,16 @@ void bb_drawEntity(bb_entity_t* currentEntity, bb_entityManager_t* entityManager { currentEntity->drawFunction(entityManager, camera, currentEntity); } + // If the sprite is out-of-bounds + else if (currentEntity->spriteIndex >= NUM_SPRITES + // Or the sprite is in-bounds, but not allocated + || !entityManager->sprites[currentEntity->spriteIndex].allocated) + { + // Immediately return + return; + } else if (entityManager->sprites[currentEntity->spriteIndex].brightnessLevels == 6) { - // Don't draw unallocated sprites - if (NUM_SPRITES > currentEntity->spriteIndex && !entityManager->sprites[currentEntity->spriteIndex].allocated) - { - return; - } - uint8_t brightness = 5; int16_t xOff = (currentEntity->pos.x >> DECIMAL_BITS) - entityManager->sprites[currentEntity->spriteIndex].originX - camera->pos.x; @@ -616,12 +618,6 @@ void bb_drawEntity(bb_entity_t* currentEntity, bb_entityManager_t* entityManager } else { - // Don't draw unallocated sprites - if (NUM_SPRITES > currentEntity->spriteIndex && !entityManager->sprites[currentEntity->spriteIndex].allocated) - { - return; - } - drawWsgSimple(&entityManager->sprites[currentEntity->spriteIndex].frames[currentEntity->currentAnimationFrame], (currentEntity->pos.x >> DECIMAL_BITS) - entityManager->sprites[currentEntity->spriteIndex].originX - camera->pos.x, @@ -636,7 +632,8 @@ void bb_drawEntity(bb_entity_t* currentEntity, bb_entityManager_t* entityManager currentEntity->currentAnimationFrame = currentEntity->animationTimer / currentEntity->gameFramesPerAnimationFrame; // if frame reached the end of the animation - if (currentEntity->currentAnimationFrame >= entityManager->sprites[currentEntity->spriteIndex].numFrames) + if (currentEntity->spriteIndex < NUM_SPRITES + && currentEntity->currentAnimationFrame >= entityManager->sprites[currentEntity->spriteIndex].numFrames) { switch (currentEntity->type) { @@ -867,33 +864,33 @@ bb_entity_t* bb_createEntity(bb_entityManager_t* entityManager, bb_animationType bb_entity_t* ovo = bb_createEntity(&entity->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, entity->gameData->camera.camera.pos.x, entity->gameData->camera.camera.pos.y, false, true); - - bb_dialogueData_t* dData = bb_createDialogueData(13, "Ovo"); - - bb_setCharacterLine(dData, 0, "Ovo", "Wow, look at that juicy bug!"); - bb_setCharacterLine(dData, 1, "Ovo", "Ah, who am I kidding?"); - bb_setCharacterLine(dData, 2, "Ovo", "My time in this landfill is limited."); - bb_setCharacterLine(dData, 3, "Ovo", - "So I'm going to break the fourth wall and tell you how to play the freaking game!"); - bb_setCharacterLine( - dData, 4, "Ovo", - "There are a bunch of ways to incapacitate that bug. But let's learn how to use harpoons first."); - bb_setCharacterLine(dData, 5, "Ovo", "Hold your right thumb on the C-Touchpad to aim."); - bb_setCharacterLine(dData, 6, "Ovo", "Hey! Wait until I'm done talking, you doofus."); - bb_setCharacterLine(dData, 7, "Ovo", "MAGFest attendees are the actual worst."); - bb_setCharacterLine(dData, 8, "Ovo", - "Don't take it personally, but I can detect your stench through the swadge's smelliphone."); - bb_setCharacterLine(dData, 9, "Ovo", - "If your touch vector is outside of the purple circle that appears on-screen, harpoons " - "will fire steadily."); - bb_setCharacterLine(dData, 10, "Ovo", "Three hits will flip the bug upside down!"); - bb_setCharacterLine(dData, 11, "Ovo", - "There's a nifty detail where harpoons with upward velocity pass right through terrain."); - bb_setCharacterLine(dData, 12, "Ovo", "Now, let's get that bug!"); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(11, "Ovo"); + + bb_setCharacterLine(dData, 0, "Ovo", "Wow, look at that juicy bug!"); + bb_setCharacterLine(dData, 1, "Ovo", "Ah, who am I kidding?"); + bb_setCharacterLine(dData, 2, "Ovo", "My time in this landfill is limited."); + bb_setCharacterLine(dData, 3, "Ovo", + "So I'm going to break the fourth wall and tell you how to play the freaking game!"); + bb_setCharacterLine( + dData, 4, "Ovo", + "There are a bunch of ways to incapacitate that bug. But let's learn how to use harpoons first."); + bb_setCharacterLine(dData, 5, "Ovo", "Hold your right thumb on the C-Touchpad to aim."); + bb_setCharacterLine(dData, 6, "Ovo", "Hey! Wait until I'm done talking, you doofus."); + bb_setCharacterLine(dData, 7, "Ovo", + "If your touch vector is outside of the purple circle that appears on-screen, harpoons " + "will fire steadily."); + bb_setCharacterLine(dData, 8, "Ovo", "Three hits will flip the bug upside down!"); + bb_setCharacterLine( + dData, 9, "Ovo", + "There's a nifty detail where harpoons with upward velocity pass right through terrain."); + bb_setCharacterLine(dData, 10, "Ovo", "Now, let's get that bug!"); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } entity->spriteIndex = spriteIndex; @@ -1151,14 +1148,20 @@ bb_entity_t* bb_createEntity(bb_entityManager_t* entityManager, bb_animationType { bb_menuData_t* mData = heap_caps_calloc(1, sizeof(bb_menuData_t), MALLOC_CAP_SPIRAM); + bb_ensureEntitySpace(entityManager, 1); mData->cursor = bb_createEntity( entityManager, LOOPING_ANIMATION, false, HARPOON, 3, (entity->pos.x >> DECIMAL_BITS) - 22, 0, false, false); // y position doesn't matter here. It will be handled in the update loop. - // This will make it draw pointed right - ((bb_projectileData_t*)mData->cursor->data)->vel = (vec_t){10, 0}; + if (mData->cursor != NULL) + { + bb_clearCollisions(mData->cursor, false); - mData->cursor->updateFunction = NULL; + // This will make it draw pointed right + ((bb_projectileData_t*)mData->cursor->data)->vel = (vec_t){10, 0}; + + mData->cursor->updateFunction = NULL; + } bb_setData(entity, mData, MENU_DATA); @@ -1306,7 +1309,7 @@ bb_entity_t* bb_createEntity(bb_entityManager_t* entityManager, bb_animationType = (bb_grabbyHandData_t*)heap_caps_calloc(1, sizeof(bb_grabbyHandData_t), MALLOC_CAP_SPIRAM); bb_setData(entity, ghData, GRABBY_HAND_DATA); - entity->cacheable = true; + entity->cacheable = false; entity->halfWidth = 7 << DECIMAL_BITS; entity->halfHeight = 26 << DECIMAL_BITS; @@ -1323,8 +1326,9 @@ bb_entity_t* bb_createEntity(bb_entityManager_t* entityManager, bb_animationType *collision = (bb_collision_t){others, bb_onCollisionGrabbyHand}; push(entity->collisions, (void*)collision); - entity->updateFunction = &bb_updateGrabbyHand; - entity->drawFunction = &bb_drawGrabbyHand; + entity->updateFunction = &bb_updateGrabbyHand; + entity->updateFarFunction = &bb_updateFarGrabbyHand; + entity->drawFunction = &bb_drawGrabbyHand; // sprites loaded just-in-time bb_loadSprite("grab", 3, 1, &entityManager->sprites[BB_GRABBY_HAND]); @@ -1403,20 +1407,23 @@ bb_entity_t* bb_createEntity(bb_entityManager_t* entityManager, bb_animationType entity->gameData->isPaused = true; // set the tutorial flag entity->gameData->tutorialFlags |= 0b10000; - bb_entity_t* ovo = bb_createEntity(&entity->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, + bb_entity_t* ovo = bb_createEntity(entityManager, NO_ANIMATION, true, OVO_TALK, 1, entity->gameData->camera.camera.pos.x, entity->gameData->camera.camera.pos.y, false, true); - bb_dialogueData_t* dData = bb_createDialogueData(3, "Ovo"); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(3, "Ovo"); - bb_setCharacterLine(dData, 0, "Ovo", "I want to eat that donut RIGHT NOW!"); - bb_setCharacterLine(dData, 1, "Ovo", "No. I need to focus. I'll tow it back to the booster."); - bb_setCharacterLine(dData, 2, "Ovo", - "When I eat it at home, it will be the be the impulse for my next great wile."); + bb_setCharacterLine(dData, 0, "Ovo", "I want to eat that donut RIGHT NOW!"); + bb_setCharacterLine(dData, 1, "Ovo", "No. I need to focus. I'll tow it back to the booster."); + bb_setCharacterLine(dData, 2, "Ovo", + "When I eat it at home, it will be the be the impulse for my next great wile."); - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } entity->halfWidth = 8 << DECIMAL_BITS; @@ -1498,6 +1505,8 @@ bb_entity_t* bb_createEntity(bb_entityManager_t* entityManager, bb_animationType } case BB_501KG: { + // just in time loading + bb_loadSprite("501kg", 1, 1, &entityManager->sprites[BB_501KG]); bb_setData(entity, heap_caps_calloc(1, sizeof(bb_501kgData_t), MALLOC_CAP_SPIRAM), BB_501KG_DATA); entity->updateFunction = &bb_update501kg; entity->drawFunction = &bb_draw501kg; @@ -1663,6 +1672,42 @@ bb_entity_t* bb_createEntity(bb_entityManager_t* entityManager, bb_animationType entity->drawFunction = &bb_drawGarbotnikUI; break; } + case BB_QUICKPLAY_CONFIRM: + { + entity->updateFunction = &bb_updateQuickplay; + entity->drawFunction = &bb_drawQuickplay; + break; + } + case BB_FINAL_BOSS: + { + bb_finalBossData_t* fbData = heap_caps_calloc(1, sizeof(bb_finalBossData_t), MALLOC_CAP_SPIRAM); + fbData->health = 5500; + bb_setData(entity, fbData, FINAL_BOSS_DATA); + + entity->halfWidth = 15 << DECIMAL_BITS; + entity->halfHeight = 15 << DECIMAL_BITS; + + entity->collisions = heap_caps_calloc_tag(1, sizeof(list_t), MALLOC_CAP_SPIRAM, "rCollisions"); + list_t* others = heap_caps_calloc_tag(1, sizeof(list_t), MALLOC_CAP_SPIRAM, "rOthers"); + push(others, (void*)GARBOTNIK_FLYING); + bb_collision_t* collision + = heap_caps_calloc_tag(1, sizeof(bb_collision_t), MALLOC_CAP_SPIRAM, "rCollision"); + *collision = (bb_collision_t){others, bb_onCollisionSpaceLaserGarbotnik}; + push(entity->collisions, (void*)collision); + + list_t* others2 = heap_caps_calloc_tag(1, sizeof(list_t), MALLOC_CAP_SPIRAM, "rOthers"); + push(others2, (void*)HARPOON); + bb_collision_t* collision2 + = heap_caps_calloc_tag(1, sizeof(bb_collision_t), MALLOC_CAP_SPIRAM, "rCollision"); + *collision2 = (bb_collision_t){others2, bb_onCollisionBoss}; + push(entity->collisions, (void*)collision2); + + // just in time loading + bb_loadSprite("ovovoBoss", 8, 1, &entityManager->sprites[BB_FINAL_BOSS]); + entity->updateFunction = &bb_updateFinalBoss; + entity->drawFunction = &bb_drawFinalBoss; + break; + } default: // FLAME_ANIM and others need nothing set { break; diff --git a/main/modes/games/bigbug/entityManager_bigbug.h b/main/modes/games/bigbug/entityManager_bigbug.h index 4af705a73..02215b2de 100644 --- a/main/modes/games/bigbug/entityManager_bigbug.h +++ b/main/modes/games/bigbug/entityManager_bigbug.h @@ -18,7 +18,7 @@ //============================================================================== #define MAX_ENTITIES 200 #define MAX_FRONT_ENTITIES 10 -#define NUM_SPRITES 34 // The number of bb_sprite_t last accounted for BB_HOTDOG +#define NUM_SPRITES 35 // The number of bb_sprite_t last accounted for BB_FINAL_BOSS //============================================================================== // Structs diff --git a/main/modes/games/bigbug/entity_bigbug.c b/main/modes/games/bigbug/entity_bigbug.c index 63a22d0dc..bb2039734 100644 --- a/main/modes/games/bigbug/entity_bigbug.c +++ b/main/modes/games/bigbug/entity_bigbug.c @@ -78,16 +78,16 @@ void bb_destroyEntity(bb_entity_t* self, bool caching, bool wasInTheMainArray) } else if (self->spriteIndex == BB_FOOD_CART) { - bb_foodCartData_t* fcData = (bb_foodCartData_t*)self->data; - // The food cart needs to track its own caching status to communicate just-in-time loading between both - // pieces. - fcData->isCached = caching; - if (fcData->partner->active == false || ((bb_foodCartData_t*)fcData->partner->data)->isCached) + uint8_t thisFrame = self->currentAnimationFrame; + if (self->currentAnimationFrame > 1) { - for (int frame = 0; frame < self->gameData->entityManager.sprites[self->spriteIndex].numFrames; frame++) - { - freeWsg(&self->gameData->entityManager.sprites[self->spriteIndex].frames[frame]); - } + thisFrame = 1; + } + if (caching + && (self->gameData->entityManager.sprites[self->spriteIndex].frames[thisFrame].w + || self->gameData->entityManager.sprites[self->spriteIndex].frames[thisFrame].h)) + { + freeWsg(&self->gameData->entityManager.sprites[self->spriteIndex].frames[thisFrame]); } } @@ -187,7 +187,8 @@ void bb_updateRocketLanding(bb_entity_t* self) } if (self->pos.y > terrainY - 3000 && rData->flame == NULL) { - rData->flame = bb_createEntity(&(self->gameData->entityManager), LOOPING_ANIMATION, false, FLAME_ANIM, 16, + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + rData->flame = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, false, FLAME_ANIM, 16, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); } @@ -326,24 +327,28 @@ void bb_updateRocketLiftoff(bb_entity_t* self) } } bb_generateWorld(&(self->gameData->tilemap), oldBoosterYs); - // brute force recalculate activeEntities count to clean up any inaccuracies. - self->gameData->entityManager.activeEntities = 0; - for (int i = 0; i < MAX_ENTITIES; i++) - { - self->gameData->entityManager.activeEntities += self->gameData->entityManager.entities[i].active; - } + } + // brute force recalculate activeEntities count to clean up any inaccuracies. + self->gameData->entityManager.activeEntities = 0; + for (int i = 0; i < MAX_ENTITIES; i++) + { + self->gameData->entityManager.activeEntities += self->gameData->entityManager.entities[i].active; } bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, true, true); - bb_dialogueData_t* dData = bb_createDialogueData(1, "Ovo"); - bb_setCharacterLine(dData, 0, "Ovo", "Time to check the loadout!"); - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikIntro; - bb_setData(ovo, dData, DIALOGUE_DATA); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(1, "Ovo"); + bb_setCharacterLine(dData, 0, "Ovo", "Time to check the loadout!"); + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikIntro; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + bb_ensureEntitySpace(&self->gameData->entityManager, 1); self->gameData->entityManager.viewEntity = bb_createEntity( &(self->gameData->entityManager), NO_ANIMATION, true, NO_SPRITE_POI, 1, self->gameData->camera.camera.pos.x + 140, self->gameData->camera.camera.pos.y + 120, false, false); @@ -366,6 +371,7 @@ void bb_updateRocketLiftoff(bb_entity_t* self) { self->gameData->endDayChecks = self->gameData->endDayChecks | (1 << 0); // set the pause illusion bit. self->gameData->endDayChecks = self->gameData->endDayChecks | (1 << 1); // set the pango has spoken bit. + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&(self->gameData->entityManager), LOOPING_ANIMATION, false, BB_PANGO_AND_FRIENDS, 3, (self->pos.x >> DECIMAL_BITS) - 77, (self->pos.y >> DECIMAL_BITS) - 100, true, false); } @@ -490,26 +496,38 @@ void bb_updateGarbotnikDeploy(bb_entity_t* self) freeWsg(&self->gameData->entityManager.sprites[ROCKET_ANIM].frames[frame]); } + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* arm = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, ATTACHMENT_ARM, 1, self->pos.x >> DECIMAL_BITS, (self->pos.y >> DECIMAL_BITS) - 33, false, false); - ((bb_rocketData_t*)self->data)->armAngle = 2880; // That is 180 down position. - ((bb_attachmentArmData_t*)arm->data)->rocket = self; + if (arm != NULL) + { + ((bb_rocketData_t*)self->data)->armAngle = 2880; // That is 180 down position. + ((bb_attachmentArmData_t*)arm->data)->rocket = self; + } + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* grabbyHand - = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, true, BB_GRABBY_HAND, 5, - self->pos.x >> DECIMAL_BITS, (self->pos.y >> DECIMAL_BITS) - 53, false, false); - ((bb_grabbyHandData_t*)grabbyHand->data)->rocket = self; + = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, true, BB_GRABBY_HAND, 6, + self->pos.x >> DECIMAL_BITS, (self->pos.y >> DECIMAL_BITS) - 53, true, false); + if (grabbyHand != NULL) + { + ((bb_grabbyHandData_t*)grabbyHand->data)->rocket = self; + } self->paused = true; self->gameData->isPaused = true; // deploy garbotnik!!! + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* garbotnik = bb_createEntity(&(self->gameData->entityManager), NO_ANIMATION, true, GARBOTNIK_FLYING, 1, self->pos.x >> DECIMAL_BITS, (self->pos.y >> DECIMAL_BITS) - 50, true, false); - self->gameData->entityManager.viewEntity = garbotnik; - self->updateFunction = bb_updateHeavyFalling; - bb_startGarbotnikLandingTalk(garbotnik); + if (garbotnik != NULL) + { + self->gameData->entityManager.viewEntity = garbotnik; + self->updateFunction = bb_updateHeavyFalling; + bb_startGarbotnikLandingTalk(garbotnik); + } } } @@ -543,9 +561,9 @@ void bb_updateGarbotnikFlying(bb_entity_t* self) if (gData->r > gData->activationRadius && gData->fire && gData->activeWile != 255) { // Throw a wile! + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* wile = bb_createEntity(&(self->gameData->entityManager), NO_ANIMATION, true, BB_WILE, 1, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); - if (wile != NULL) { midiPlayer_t* sfx = soundGetPlayerSfx(); @@ -595,6 +613,7 @@ void bb_updateGarbotnikFlying(bb_entity_t* self) { gData->harpoonCooldown = self->gameData->GarbotnikStat_fireTime; // Create a harpoon + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* harpoon = bb_createEntity(&(self->gameData->entityManager), LOOPING_ANIMATION, false, HARPOON, 1, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); if (harpoon != NULL) @@ -1152,29 +1171,31 @@ void bb_updateGarbotnikFlying(bb_entity_t* self) bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); - - bb_dialogueData_t* dData = bb_createDialogueData(9, "Ovo"); - - bb_setCharacterLine(dData, 0, "Ovo", "Glitch my circuits!"); - bb_setCharacterLine(dData, 1, "Ovo", "Low fuel!"); - bb_setCharacterLine(dData, 2, "Ovo", "How could you do this to me?!"); - bb_setCharacterLine(dData, 3, "Ovo", "Extraction is one of the hardest parts of the job."); - bb_setCharacterLine( - dData, 4, "Ovo", - "Plan ahead and clear out any threats around the booster for an uninterrupted extraction."); - bb_setCharacterLine( - dData, 5, "Ovo", - "Keep in mind that my headlamps stimulate the bug eggs. So my facing direction is important."); - bb_setCharacterLine(dData, 6, "Ovo", - "Remember that bugs don't usually dig. So use the garbage to your advantage."); - bb_setCharacterLine(dData, 7, "Ovo", "And bugs far off-screen will stop moving and shooting."); - bb_setCharacterLine( - dData, 8, "Ovo", - "Sometimes you just need to let them wander away to sit safely on the booster for 30 seconds."); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(9, "Ovo"); + + bb_setCharacterLine(dData, 0, "Ovo", "Glitch my circuits!"); + bb_setCharacterLine(dData, 1, "Ovo", "Low fuel!"); + bb_setCharacterLine(dData, 2, "Ovo", "How could you do this to me?!"); + bb_setCharacterLine(dData, 3, "Ovo", "Extraction is one of the hardest parts of the job."); + bb_setCharacterLine( + dData, 4, "Ovo", + "Plan ahead and clear out any threats around the booster for an uninterrupted extraction."); + bb_setCharacterLine( + dData, 5, "Ovo", + "Keep in mind that my headlamps stimulate the bug eggs. So my facing direction is important."); + bb_setCharacterLine(dData, 6, "Ovo", + "Remember that bugs don't usually dig. So use the garbage to your advantage."); + bb_setCharacterLine(dData, 7, "Ovo", "And bugs far off-screen will stop moving and shooting."); + bb_setCharacterLine( + dData, 8, "Ovo", + "Sometimes you just need to let them wander away to sit safely on the booster for 30 seconds."); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } // exploration song length 5537 // hurry up song length 4833 @@ -1377,10 +1398,10 @@ void bb_updateEggLeaves(bb_entity_t* self) // about before hatching. // create a bug + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* bug = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, false, bb_randomInt(8, 13), 1, elData->egg->pos.x >> DECIMAL_BITS, elData->egg->pos.y >> DECIMAL_BITS, false, false); - if (bug != NULL) { // destroy the egg @@ -1490,10 +1511,11 @@ void bb_updateBugShooting(bb_entity_t* self) { if (bb_randomInt(0, 100) < 1) { + bb_ensureEntitySpace(&self->gameData->entityManager, 1); // call it paused and update frames in it's own update function because this one uses another spriteIdx. bb_entity_t* spit = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, true, BB_SPIT, 10, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, true, false); - if (self->gameData->entityManager.playerEntity != NULL) + if (self->gameData->entityManager.playerEntity != NULL && spit != NULL) { bb_spitData_t* sData = (bb_spitData_t*)spit->data; sData->vel = subVec2d(self->gameData->entityManager.playerEntity->pos, spit->pos); @@ -1784,17 +1806,17 @@ void bb_updateFlyingBug(bb_entity_t* self) void bb_updateMenu(bb_entity_t* self) { bb_menuData_t* mData = (bb_menuData_t*)self->data; - if (self->gameData->btnDownState & PB_UP) + if (self->gameData->btnDownState & PB_UP && mData->selectionIdx < 2) { mData->selectionIdx--; mData->selectionIdx = mData->selectionIdx < 0 ? 1 : mData->selectionIdx; } - if (self->gameData->btnDownState & PB_DOWN) + if (self->gameData->btnDownState & PB_DOWN && mData->selectionIdx < 2) { mData->selectionIdx++; mData->selectionIdx = mData->selectionIdx > 1 ? 0 : mData->selectionIdx; } - if (self->gameData->btnDownState & PB_A) + if (self->gameData->btnDownState & PB_A && mData->selectionIdx < 2) { if (mData->selectionIdx == 0) { @@ -1814,13 +1836,15 @@ void bb_updateMenu(bb_entity_t* self) bb_rocketData_t* rData = (bb_rocketData_t*)self->gameData->entityManager.boosterEntities[rocketIdx]->data; - + bb_ensureEntitySpace(&self->gameData->entityManager, 1); rData->flame = bb_createEntity( &(self->gameData->entityManager), LOOPING_ANIMATION, false, FLAME_ANIM, 6, self->gameData->entityManager.boosterEntities[rocketIdx]->pos.x >> DECIMAL_BITS, self->gameData->entityManager.boosterEntities[rocketIdx]->pos.y >> DECIMAL_BITS, true, false); - - rData->flame->updateFunction = &bb_updateFlame; + if (rData->flame != NULL) + { + rData->flame->updateFunction = &bb_updateFlame; + } } // create garbotnik's UI @@ -1832,13 +1856,16 @@ void bb_updateMenu(bb_entity_t* self) self->gameData->entityManager.deathDumpster = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_DEATH_DUMPSTER, 1, self->pos.x >> DECIMAL_BITS, (self->pos.y >> DECIMAL_BITS) + 400, false, true); - tData->tracking = self->gameData->entityManager.deathDumpster; - tData->midPointSqDist - = sqMagVec2d(divVec2d((vec_t){(tData->tracking->pos.x >> DECIMAL_BITS) - - (self->gameData->entityManager.viewEntity->pos.x >> DECIMAL_BITS), - (tData->tracking->pos.y >> DECIMAL_BITS) - - (self->gameData->entityManager.viewEntity->pos.y >> DECIMAL_BITS)}, - 2)); + if (self->gameData->entityManager.deathDumpster != NULL) + { + tData->tracking = self->gameData->entityManager.deathDumpster; + tData->midPointSqDist = sqMagVec2d( + divVec2d((vec_t){(tData->tracking->pos.x >> DECIMAL_BITS) + - (self->gameData->entityManager.viewEntity->pos.x >> DECIMAL_BITS), + (tData->tracking->pos.y >> DECIMAL_BITS) + - (self->gameData->entityManager.viewEntity->pos.y >> DECIMAL_BITS)}, + 2)); + } self->gameData->entityManager.viewEntity->updateFunction = &bb_updatePOI; @@ -1847,9 +1874,14 @@ void bb_updateMenu(bb_entity_t* self) } else if (mData->selectionIdx == 1) { - // exit the game - self->gameData->exit = true; + // quick play + mData->selectionIdx = 2; + bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_QUICKPLAY_CONFIRM, 1, + self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, true, true); } + + self->gameData->btnState = 0; + self->gameData->btnDownState = 0; } mData->cursor->pos.y = self->pos.y - (209 << DECIMAL_BITS) + mData->selectionIdx * (22 << DECIMAL_BITS); @@ -1882,7 +1914,10 @@ void bb_updateMenu(bb_entity_t* self) bb_entity_t* treadmillDust = bb_createEntity( &self->gameData->entityManager, NO_ANIMATION, true, NO_SPRITE_STAR, 1, (self->pos.x >> DECIMAL_BITS) + 140, (self->pos.y >> DECIMAL_BITS) - 165 + bb_randomInt(-10, 10), false, false); - treadmillDust->updateFunction = &bb_updateMoveLeft; + if (treadmillDust != NULL) + { + treadmillDust->updateFunction = &bb_updateMoveLeft; + } } } @@ -2011,7 +2046,10 @@ void bb_updateCharacterTalk(bb_entity_t* self) freeWsg(&dData->sprite); freeWsg(&dData->spriteNext); int8_t characterSprite = 0; - if (strcmp(dData->characters[dData->curString], "Ovo") == 0) + if (strcmp(dData->characters[dData->curString], "Ovo") == 0 + || strcmp(dData->characters[dData->curString], "???") == 0 + || strcmp(dData->characters[dData->curString], "Ovo???") == 0 + || strcmp(dData->characters[dData->curString], "DOCTOR OVO") == 0) { characterSprite = bb_randomInt(0, 6); loadWsgInplace("dialogue_next.wsg", &dData->spriteNext, true, bb_decodeSpace, bb_hsd); // TODO free @@ -2080,12 +2118,15 @@ void bb_updateAttachmentArm(bb_entity_t* self) self->gameData->entityManager.viewEntity = aData->rocket; aData->rocket->currentAnimationFrame = 0; + bb_ensureEntitySpace(&self->gameData->entityManager, 1); rData->flame = bb_createEntity(&(self->gameData->entityManager), LOOPING_ANIMATION, false, FLAME_ANIM, 6, aData->rocket->pos.x >> DECIMAL_BITS, aData->rocket->pos.y >> DECIMAL_BITS, true, false); - - rData->flame->updateFunction = &bb_updateFlame; - aData->rocket->updateFunction = &bb_updateRocketLiftoff; + if (rData->flame != NULL) + { + rData->flame->updateFunction = &bb_updateFlame; + aData->rocket->updateFunction = &bb_updateRocketLiftoff; + } bb_destroyEntity(self, false, true); } @@ -2198,8 +2239,10 @@ void bb_updateGrabbyHand(bb_entity_t* self) self->pos.y = ghData->rocket->pos.y - 848; // that is 53 << 4 // retreat into the booster - if (self->gameData->entityManager.sprites[BB_GRABBY_HAND].originY > -26) + if (self->gameData->entityManager.sprites[BB_GRABBY_HAND].originY > -26 + && (ghData->grabbed == NULL || self->currentAnimationFrame == 2)) { + self->paused = true; self->gameData->entityManager.sprites[BB_GRABBY_HAND].originY -= 2; if (ghData->grabbed != NULL) { @@ -2208,8 +2251,11 @@ void bb_updateGrabbyHand(bb_entity_t* self) = self->pos.y - (self->gameData->entityManager.sprites[BB_GRABBY_HAND].originY << DECIMAL_BITS); } } - else if (ghData->grabbed != NULL) + else if (self->gameData->entityManager.sprites[BB_GRABBY_HAND].originY <= -26 && ghData->grabbed != NULL + && self->currentAnimationFrame == 2) { + self->currentAnimationFrame = 0; + self->animationTimer = 0; if (self->gameData->entityManager.playerEntity != NULL && self->gameData->entityManager.playerEntity->dataType == GARBOTNIK_DATA) { @@ -2241,27 +2287,30 @@ void bb_updateGrabbyHand(bb_entity_t* self) if (!(self->gameData->tutorialFlags & 0b100)) { - self->gameData->isPaused = true; // set the tutorial flag self->gameData->tutorialFlags |= 0b100; bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); - - bb_dialogueData_t* dData = bb_createDialogueData(5, "Ovo"); - - bb_setCharacterLine(dData, 0, "Ovo", "One down, nine more to go!"); - bb_setCharacterLine(dData, 1, "Ovo", "For every 10 bugs collected, the booster will emit a mega ping!"); - bb_setCharacterLine(dData, 2, "Ovo", - "When the mega ping is done, you can tune into some of the more important results."); - bb_setCharacterLine(dData, 3, "Ovo", - "Oh, you can pulse your own radar anytime with the start button. It takes some time to " - "interpret the pings."); - bb_setCharacterLine(dData, 4, "Ovo", "So keep exploring while you wait."); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(5, "Ovo"); + + bb_setCharacterLine(dData, 0, "Ovo", "One down, nine more to go!"); + bb_setCharacterLine(dData, 1, "Ovo", "For every 10 bugs collected, the booster will emit a mega ping!"); + bb_setCharacterLine( + dData, 2, "Ovo", + "When the mega ping is done, you can tune into some of the more important results."); + bb_setCharacterLine( + dData, 3, "Ovo", + "Oh, you can pulse your own radar anytime with the start button. It takes some time to " + "interpret the pings."); + bb_setCharacterLine(dData, 4, "Ovo", "So keep exploring while you wait."); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } rData->numBugs++; @@ -2274,9 +2323,12 @@ void bb_updateGrabbyHand(bb_entity_t* self) bb_entity_t* radarPing = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_RADAR_PING, 1, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, true, false); - bb_radarPingData_t* rpData = (bb_radarPingData_t*)radarPing->data; - rpData->color = c541; - rpData->executeAfterPing = &bb_upgradeRadar; + if (radarPing != NULL) + { + bb_radarPingData_t* rpData = (bb_radarPingData_t*)radarPing->data; + rpData->color = c541; + rpData->executeAfterPing = &bb_upgradeRadar; + } } self->currentAnimationFrame = 0; @@ -2288,6 +2340,15 @@ void bb_updateGrabbyHand(bb_entity_t* self) } } +void bb_updateFarGrabbyHand(bb_entity_t* self) +{ + if (self->gameData->entityManager.sprites[BB_GRABBY_HAND].allocated) + { + bb_freeSprite(&self->gameData->entityManager.sprites[BB_GRABBY_HAND]); + self->drawFunction = bb_drawNothing; + } +} + void bb_updateDoor(bb_entity_t* self) { if (self->gameData->carFightState == 0) // no fight @@ -2345,6 +2406,7 @@ void bb_updateCarOpen(bb_entity_t* self) case BB_DONUT: { // spawn a donut as a reward for completing the fight + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_DONUT, 1, (self->pos.x >> DECIMAL_BITS) + 5, (self->pos.y >> DECIMAL_BITS), true, false); break; @@ -2352,6 +2414,7 @@ void bb_updateCarOpen(bb_entity_t* self) default: // BB_SWADGE { // spawn a swadge as a reward for completing the fight + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, false, BB_SWADGE, 9, (self->pos.x >> DECIMAL_BITS) + 40, (self->pos.y >> DECIMAL_BITS) - 32, true, false); break; @@ -2434,125 +2497,129 @@ void bb_updatePangoAndFriends(bb_entity_t* self) bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); - - if (self->gameData->day == 0) - { - bb_dialogueData_t* dData = bb_createDialogueData(18, "Pixel"); - - // longest possible string " " - // bb_setCharacterLine(dData, 0, "A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A - // A A A A A A A A A A A A A A A A A A A A A A A"); - bb_setCharacterLine(dData, 0, "Pixel", "huff... huff... we actually caught up to it!"); - bb_setCharacterLine(dData, 1, "Pixel", - "ugfhh, my hair is starting to hurt. Can you make this quick, Pango?"); - bb_setCharacterLine(dData, 2, "Pixel", "Hey, it looks like it's Garbotnik after all!"); - bb_setCharacterLine(dData, 3, "Po", "That's a cool ride, bro."); - bb_setCharacterLine(dData, 4, "Ovo", "What could you idiots possibly want?"); - bb_setCharacterLine(dData, 5, "Pango", - "We were fascinated with your shiny metal tube falling from space this morning."); - bb_setCharacterLine(dData, 6, "Po", "Yeah, we were wondering if you could give us a ride."); - bb_setCharacterLine(dData, 7, "Ovo", "I'm not a taxi service."); - bb_setCharacterLine(dData, 8, "Pixel", "We'll pay you in swadges."); - bb_setCharacterLine(dData, 9, "Po", "What are you doing at the landfill, anyway?"); - bb_setCharacterLine(dData, 10, "Ovo", "None of your business!"); - bb_setCharacterLine(dData, 11, "Ovo", "Get off my freaking back!"); - bb_setCharacterLine(dData, 12, "Ovo", "I have a dive summary to review."); - bb_setCharacterLine(dData, 13, "Pixel", "What's a dive summary?"); - bb_setCharacterLine(dData, 14, "Ovo", "It tells me how many bugs I annihilated."); - bb_setCharacterLine(dData, 15, "Pixel", "Hmm... That sounds like he's up to no good."); - bb_setCharacterLine(dData, 16, "Ovo", "Bug off!"); - bb_setCharacterLine(dData, 17, "Ovo", "Filthy peasants."); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterLiftoffInteraction; - bb_setData(ovo, dData, DIALOGUE_DATA); - } - else if (self->gameData->day == 1) - { - bb_dialogueData_t* dData = bb_createDialogueData(10, "Pixel"); - - bb_setCharacterLine(dData, 0, "Pixel", - "Hey, Mr. Garbotnik! I really love the color of your umbrella so much!"); - bb_setCharacterLine(dData, 1, "Ovo", "Glitch my circuits!"); - bb_setCharacterLine(dData, 2, "Ovo", "Now I have to find a new one!"); - bb_setCharacterLine(dData, 3, "Po", "What's your problem, dude? Pink is a great color!"); - bb_setCharacterLine(dData, 4, "Pango", "Nobody spends their Thursdays at the dump for leisure."); - bb_setCharacterLine(dData, 5, "Pango", "We're going to find out what you're up to here."); - bb_setCharacterLine(dData, 6, "Ovo", "Oh, bug off!"); - bb_setCharacterLine(dData, 7, "Ovo", "Don't you have anything better to do?"); - bb_setCharacterLine(dData, 8, "Ovo", "Lousy ingrates."); - bb_setCharacterLine(dData, 9, "Pixel", "That man doesn't know the meaning of friendship."); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterLiftoffInteraction; - bb_setData(ovo, dData, DIALOGUE_DATA); - } - else if (self->gameData->day == 2) - { - bb_dialogueData_t* dData = bb_createDialogueData(8, "Pango"); - - bb_setCharacterLine(dData, 0, "Pango", "Why do you have better shading than us?"); - bb_setCharacterLine(dData, 1, "Ovo", "Because I'm the main character."); - bb_setCharacterLine(dData, 2, "Pango", "But MAGFast is literally named after me!"); - bb_setCharacterLine(dData, 3, "Ovo", "You fool! It's called mag FEST because of PEST like you."); - bb_setCharacterLine(dData, 4, "Po", "That's so metal."); - bb_setCharacterLine(dData, 5, "Ovo", - "POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" - "OOOOOOOOOOOOOOOOOOOO"); - bb_setCharacterLine(dData, 6, "Ovo", - "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" - "OOOOOOOOOOOOOOOOOOOOOOO!"); - bb_setCharacterLine(dData, 7, "Pixel", "Hey, don't yell at my friend like that!"); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterLiftoffInteraction; - bb_setData(ovo, dData, DIALOGUE_DATA); - } - else if (self->gameData->day == 3) - { - bb_dialogueData_t* dData = bb_createDialogueData(8, "Pango"); - - bb_setCharacterLine(dData, 0, "Pango", "GARBOTNIK! Are you feeling well?"); - bb_setCharacterLine(dData, 1, "Ovo", "Huh?"); - bb_setCharacterLine(dData, 2, "Pango", "Looks like you've got a case of the T-rash!"); - bb_setCharacterLine(dData, 3, "Ovo", "Aaaaaargh! Darn it! Darn it! DARN IT! DARN IT!"); - bb_setCharacterLine(dData, 4, "Ovo", "GLITCH MY CIRCUITS!"); - bb_setCharacterLine(dData, 5, "Pixel", "Freaking got him, Pango!"); - bb_setCharacterLine(dData, 6, "Ovo", "T-raaaaash goes in the T-raaaaaash can."); - bb_setCharacterLine(dData, 7, "Po", "Huh?"); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterLiftoffInteraction; - bb_setData(ovo, dData, DIALOGUE_DATA); - } - else if (self->gameData->day == 99) - { - bb_dialogueData_t* dData = bb_createDialogueData(3, "Pango"); - - bb_setCharacterLine(dData, 0, "Pango", "You're completely unhinged."); - bb_setCharacterLine(dData, 1, "Ovo", "Aaaaaaaand..."); - bb_setCharacterLine(dData, 2, "Ovo", "You're not?"); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterLiftoffInteraction; - bb_setData(ovo, dData, DIALOGUE_DATA); - } - else + if (ovo != NULL) { - bb_dialogueData_t* dData = bb_createDialogueData(3, "Po"); + if (self->gameData->day == 0) + { + bb_dialogueData_t* dData = bb_createDialogueData(18, "Pixel"); + + // longest possible string " " + // bb_setCharacterLine(dData, 0, "A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A + // A A A A A A A A A A A A A A A A A A A A A A A A A"); + bb_setCharacterLine(dData, 0, "Pixel", "huff... huff... we actually caught up to it!"); + bb_setCharacterLine(dData, 1, "Pixel", + "ugfhh, my hair is starting to hurt. Can you make this quick, Pango?"); + bb_setCharacterLine(dData, 2, "Pixel", "Hey, it looks like it's Garbotnik after all!"); + bb_setCharacterLine(dData, 3, "Po", "That's a cool ride, bro."); + bb_setCharacterLine(dData, 4, "Ovo", "What could you idiots possibly want?"); + bb_setCharacterLine(dData, 5, "Pango", + "We were fascinated with your shiny metal tube falling from space this morning."); + bb_setCharacterLine(dData, 6, "Po", "Yeah, we were wondering if you could give us a ride."); + bb_setCharacterLine(dData, 7, "Ovo", "I'm not a taxi service."); + bb_setCharacterLine(dData, 8, "Pixel", "We'll pay you in swadges."); + bb_setCharacterLine(dData, 9, "Po", "What are you doing at the landfill, anyway?"); + bb_setCharacterLine(dData, 10, "Ovo", "None of your business!"); + bb_setCharacterLine(dData, 11, "Ovo", "Get off my freaking back!"); + bb_setCharacterLine(dData, 12, "Ovo", "I have a dive summary to review."); + bb_setCharacterLine(dData, 13, "Pixel", "What's a dive summary?"); + bb_setCharacterLine(dData, 14, "Ovo", "It tells me how many bugs I annihilated."); + bb_setCharacterLine(dData, 15, "Pixel", "Hmm... That sounds like he's up to no good."); + bb_setCharacterLine(dData, 16, "Ovo", "Bug off!"); + bb_setCharacterLine(dData, 17, "Ovo", "Filthy peasants."); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterLiftoffInteraction; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + else if (self->gameData->day == 1) + { + bb_dialogueData_t* dData = bb_createDialogueData(10, "Pixel"); + + bb_setCharacterLine(dData, 0, "Pixel", + "Hey, Mr. Garbotnik! I really love the color of your umbrella so much!"); + bb_setCharacterLine(dData, 1, "Ovo", "Glitch my circuits!"); + bb_setCharacterLine(dData, 2, "Ovo", "Now I have to find a new one!"); + bb_setCharacterLine(dData, 3, "Po", "What's your problem, dude? Pink is a great color!"); + bb_setCharacterLine(dData, 4, "Pango", "Nobody spends their Thursdays at the dump for leisure."); + bb_setCharacterLine(dData, 5, "Pango", "We're going to find out what you're up to here."); + bb_setCharacterLine(dData, 6, "Ovo", "Oh, bug off!"); + bb_setCharacterLine(dData, 7, "Ovo", "Don't you have anything better to do?"); + bb_setCharacterLine(dData, 8, "Ovo", "Lousy ingrates."); + bb_setCharacterLine(dData, 9, "Pixel", "That man doesn't know the meaning of friendship."); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterLiftoffInteraction; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + else if (self->gameData->day == 2) + { + bb_dialogueData_t* dData = bb_createDialogueData(8, "Pango"); + + bb_setCharacterLine(dData, 0, "Pango", "Why do you have better shading than us?"); + bb_setCharacterLine(dData, 1, "Ovo", "Because I'm the main character."); + bb_setCharacterLine(dData, 2, "Pango", "But MAGFast is literally named after me!"); + bb_setCharacterLine(dData, 3, "Ovo", "You fool! It's called mag FEST because of PEST like you."); + bb_setCharacterLine(dData, 4, "Po", "That's so metal."); + bb_setCharacterLine( + dData, 5, "Ovo", + "POOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" + "OOOOOOOOOOOOOOOOOOOO"); + bb_setCharacterLine( + dData, 6, "Ovo", + "OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO" + "OOOOOOOOOOOOOOOOOOOOOOO!"); + bb_setCharacterLine(dData, 7, "Pixel", "Hey, don't yell at my friend like that!"); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterLiftoffInteraction; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + else if (self->gameData->day == 3) + { + bb_dialogueData_t* dData = bb_createDialogueData(8, "Pango"); + + bb_setCharacterLine(dData, 0, "Pango", "GARBOTNIK! Are you feeling well?"); + bb_setCharacterLine(dData, 1, "Ovo", "Huh?"); + bb_setCharacterLine(dData, 2, "Pango", "Looks like you've got a case of the T-rash!"); + bb_setCharacterLine(dData, 3, "Ovo", "Aaaaaargh! Darn it! Darn it! DARN IT! DARN IT!"); + bb_setCharacterLine(dData, 4, "Ovo", "GLITCH MY CIRCUITS!"); + bb_setCharacterLine(dData, 5, "Pixel", "Freaking got him, Pango!"); + bb_setCharacterLine(dData, 6, "Ovo", "T-raaaaash goes in the T-raaaaaash can."); + bb_setCharacterLine(dData, 7, "Po", "Huh?"); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterLiftoffInteraction; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + else if (self->gameData->day == 99) + { + bb_dialogueData_t* dData = bb_createDialogueData(3, "Pango"); - bb_setCharacterLine(dData, 0, "Po", "Still looking?"); - char pangosLine[34]; - snprintf(pangosLine, sizeof(pangosLine), "It's been %d days at the dump.", self->gameData->day + 1); - bb_setCharacterLine(dData, 1, "Pango", pangosLine); - bb_setCharacterLine(dData, 2, "Pixel", "I guess that means you're having no luck."); - bb_setCharacterLine(dData, 2, "Ovo", "Glitch my circuits!"); - bb_setCharacterLine(dData, 2, "Ovo", "I. know."); - bb_setCharacterLine(dData, 2, "Ovo", "Bug off!"); + bb_setCharacterLine(dData, 0, "Pango", "You're completely unhinged."); + bb_setCharacterLine(dData, 1, "Ovo", "Aaaaaaaand..."); + bb_setCharacterLine(dData, 2, "Ovo", "You're not?"); - dData->curString = -1; - dData->endDialogueCB = &bb_afterLiftoffInteraction; - bb_setData(ovo, dData, DIALOGUE_DATA); + dData->curString = -1; + dData->endDialogueCB = &bb_afterLiftoffInteraction; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + else + { + bb_dialogueData_t* dData = bb_createDialogueData(3, "Po"); + + bb_setCharacterLine(dData, 0, "Po", "Still looking?"); + char pangosLine[34]; + snprintf(pangosLine, sizeof(pangosLine), "It's been %d days at the dump.", self->gameData->day + 1); + bb_setCharacterLine(dData, 1, "Pango", pangosLine); + bb_setCharacterLine(dData, 2, "Pixel", "I guess that means you're having no luck."); + bb_setCharacterLine(dData, 2, "Ovo", "Glitch my circuits!"); + bb_setCharacterLine(dData, 2, "Ovo", "I. know."); + bb_setCharacterLine(dData, 2, "Ovo", "Bug off!"); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterLiftoffInteraction; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } self->updateFunction = NULL; } @@ -2660,12 +2727,15 @@ void bb_update501kg(bb_entity_t* self) if (fData->vel.y <= 0) { // detonate - freeWsg(&self->gameData->entityManager.sprites[BB_501KG].frames[0]); + bb_freeSprite(&self->gameData->entityManager.sprites[BB_501KG]); bb_entity_t* explosion = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_EXPLOSION, 1, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); - bb_explosionData_t* eData = (bb_explosionData_t*)explosion->data; - eData->radius = 180; + if (explosion != NULL) + { + bb_explosionData_t* eData = (bb_explosionData_t*)explosion->data; + eData->radius = 180; + } bb_destroyEntity(self, false, true); } @@ -2750,7 +2820,6 @@ void bb_updateExplosion(bb_entity_t* self) // tell this partner of the change in address ((bb_foodCartData_t*)fcData->partner->data)->partner = foundSpot; bb_loadSprite("foodCart", 2, 1, &self->gameData->entityManager.sprites[BB_FOOD_CART]); - fcData->isCached = false; } continue; } @@ -3006,75 +3075,399 @@ void bb_updateSpaceLaser(bb_entity_t* self) self->pos.y = (slData->highestGarbage << 9) - self->halfHeight; } -void bb_drawGarbotnikFlying(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self) +void bb_updateQuickplay(bb_entity_t* self) { - if (GARBOTNIK_DATA != self->dataType) + bb_menuData_t* mData = NULL; + for (int eIdx = 0; eIdx < MAX_ENTITIES; eIdx++) { - return; + bb_entity_t* menu = &self->gameData->entityManager.entities[eIdx]; + if (menu != NULL && menu->spriteIndex == BB_MENU && menu->updateFunction != NULL) + { + mData = (bb_menuData_t*)menu->data; + break; + } } - - bb_garbotnikData_t* gData = (bb_garbotnikData_t*)self->data; - - // draw the tow cables - node_t* current = gData->towedEntities.first; - while (current != NULL) + if (mData == NULL) { - bb_entity_t* curEntity = (bb_entity_t*)current->val; - drawLineFast((self->pos.x >> DECIMAL_BITS) - camera->pos.x, (self->pos.y >> DECIMAL_BITS) - camera->pos.y, - (curEntity->pos.x >> DECIMAL_BITS) - camera->pos.x, - (curEntity->pos.y >> DECIMAL_BITS) - camera->pos.y, c425); - current = current->next; + return; } - int16_t xOff = (self->pos.x >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originX - camera->pos.x; - int16_t yOff = (self->pos.y >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originY - camera->pos.y; - - uint8_t frameIndex; - bool useSimple = true; - - // Determine frameIndex and draw style - if (gData->yaw.x < -1400) - { - frameIndex = 0; - } - else if (gData->yaw.x < -400) - { - frameIndex = 1; - } - else if (gData->yaw.x < 400) + if (self->gameData->btnDownState & PB_LEFT && mData->selectionIdx >= 2) { - frameIndex = 2; + mData->selectionIdx--; + mData->selectionIdx = mData->selectionIdx < 2 ? 3 : mData->selectionIdx; } - else if (gData->yaw.x < 1400) + if (self->gameData->btnDownState & PB_RIGHT && mData->selectionIdx >= 2) { - frameIndex = 1; - useSimple = false; + mData->selectionIdx++; + mData->selectionIdx = mData->selectionIdx > 3 ? 2 : mData->selectionIdx; } - else + if (self->gameData->btnDownState & PB_A && mData->selectionIdx >= 2) { - frameIndex = 0; - useSimple = false; + if (mData->selectionIdx == 2) + { + // no + // set selectionIdx to 1 + mData->selectionIdx = 1; + } + else if (mData->selectionIdx == 3) + { + // yes + // quick start the game + self->gameData->tutorialFlags = 255; + + uint32_t deathDumpsterX = (TILE_FIELD_WIDTH / 2) * TILE_SIZE + HALF_TILE - 1; + uint32_t deathDumpsterY = -2173; + + // create 3 rockets + for (int rocketIdx = 0; rocketIdx < 3; rocketIdx++) + { + self->gameData->entityManager.boosterEntities[rocketIdx] + = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, ROCKET_ANIM, 16, + deathDumpsterX - 96 + 96 * rocketIdx, deathDumpsterY, false, true); + + // bigbug->gameData.entityManager.boosterEntities[rocketIdx]->updateFunction = NULL; + + if (rocketIdx >= 1) + { + bb_rocketData_t* rData + = (bb_rocketData_t*)self->gameData->entityManager.boosterEntities[rocketIdx]->data; + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + rData->flame = bb_createEntity( + &(self->gameData->entityManager), LOOPING_ANIMATION, false, FLAME_ANIM, 6, + self->gameData->entityManager.boosterEntities[rocketIdx]->pos.x >> DECIMAL_BITS, + self->gameData->entityManager.boosterEntities[rocketIdx]->pos.y >> DECIMAL_BITS, true, false); + if (rData->flame != NULL) + { + rData->flame->updateFunction = &bb_updateFlame; + } + } + else // rocketIdx == 0 + { + self->gameData->entityManager.activeBooster + = self->gameData->entityManager.boosterEntities[rocketIdx]; + self->gameData->entityManager.activeBooster->currentAnimationFrame = 40; + self->gameData->entityManager.activeBooster->pos.y = 50; + self->gameData->entityManager.activeBooster->updateFunction = bb_updateHeavyFalling; + + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + bb_entity_t* arm = bb_createEntity( + &self->gameData->entityManager, NO_ANIMATION, true, ATTACHMENT_ARM, 1, + self->gameData->entityManager.activeBooster->pos.x >> DECIMAL_BITS, + (self->gameData->entityManager.activeBooster->pos.y >> DECIMAL_BITS) - 33, true, false); + if (arm != NULL) + { + ((bb_attachmentArmData_t*)arm->data)->rocket = self->gameData->entityManager.activeBooster; + } + + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + bb_entity_t* grabbyHand = bb_createEntity( + &self->gameData->entityManager, LOOPING_ANIMATION, true, BB_GRABBY_HAND, 6, + self->gameData->entityManager.activeBooster->pos.x >> DECIMAL_BITS, + (self->gameData->entityManager.activeBooster->pos.y >> DECIMAL_BITS) - 53, true, false); + if (grabbyHand != NULL) + { + ((bb_grabbyHandData_t*)grabbyHand->data)->rocket = self->gameData->entityManager.activeBooster; + } + } + } + + // create the death dumpster + self->gameData->entityManager.deathDumpster + = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_DEATH_DUMPSTER, 1, + deathDumpsterX, deathDumpsterY, false, true); + + // create garbotnik's UI + bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_GARBOTNIK_UI, 1, 0, 0, false, true); + + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + self->gameData->entityManager.viewEntity = bb_createEntity( + &(self->gameData->entityManager), NO_ANIMATION, true, GARBOTNIK_FLYING, 1, + self->gameData->entityManager.activeBooster->pos.x >> DECIMAL_BITS, + (self->gameData->entityManager.activeBooster->pos.y >> DECIMAL_BITS) - 90, true, false); + if (self->gameData->entityManager.viewEntity != NULL) + { + self->gameData->entityManager.playerEntity = self->gameData->entityManager.viewEntity; + } + + bb_loadWsgs(&self->gameData->tilemap); + + int8_t oldBoosterYs[3] = {-1, -1, -1}; + bb_generateWorld(&(self->gameData->tilemap), oldBoosterYs); + + // Set the mode to game mode + self->gameData->screen = BIGBUG_GAME; + + bb_setupMidi(); + unloadMidiFile(&self->gameData->bgm); + loadMidiFile("BigBugExploration.mid", &self->gameData->bgm, true); + globalMidiPlayerPlaySong(&self->gameData->bgm, MIDI_BGM); + self->gameData->camera.camera.pos = (vec_t){0, 0}; + } + self->gameData->btnDownState = 0; + self->gameData->btnState = 0; + + // for testing final boss + // self->gameData->entityManager.playerEntity->pos.y = 181 << 9; + // ((bb_garbotnikData_t*)self->gameData->entityManager.playerEntity->data)->numHarpoons = 250; + // self->gameData->GarbotnikStat_fireTime = 50; + + // destroy self + bb_destroyEntity(self, false, false); } +} - // Draw the sprite - if (gData->damageEffect > 70 || (gData->damageEffect > 0 && bb_randomInt(0, 1))) +void bb_updateFinalBoss(bb_entity_t* self) +{ + if (self->gameData->entityManager.playerEntity == NULL || FINAL_BOSS_DATA != self->dataType) { - drawWsgPalette(&entityManager->sprites[self->spriteIndex].frames[frameIndex], xOff, yOff, - &self->gameData->damagePalette, !useSimple, false, 0); + return; } - else if (useSimple) + + bb_finalBossData_t* fbData = (bb_finalBossData_t*)self->data; + if (fbData == NULL) { - drawWsgSimple(&entityManager->sprites[self->spriteIndex].frames[frameIndex], xOff, yOff); + return; } - else + + if (fbData->health <= 0) { - drawWsg(&entityManager->sprites[self->spriteIndex].frames[frameIndex], xOff, yOff, true, false, 0); + self->gameData->isPaused = true; + bb_entity_t* ovo + = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, + self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(12, "Ovo"); + + bb_setCharacterLine(dData, 0, "Ovo", "Wow, I didn't think you would get this far."); + bb_setCharacterLine(dData, 1, "Ovo", + "I'm about to grab the chaos orb, but I can see the future and the past now."); + bb_setCharacterLine(dData, 2, "Ovo", "So here's how it goes down."); + bb_setCharacterLine(dData, 3, "Ovo", "With the chaos orb, I boot up the time machine but it malfunctions!"); + bb_setCharacterLine( + dData, 4, "Ovo", + "Stuck in the carboniferous period, I have to fight off the the biggest bugs Earth has ever known."); + bb_setCharacterLine(dData, 5, "Ovo", "I had to shatter the chaos orb to freeze time and save myself."); + bb_setCharacterLine(dData, 6, "Ovo", + "And that's how all these big bugs ended up in the G.S.I. dump in the 21st century."); + bb_setCharacterLine(dData, 7, "Ovo", "And the calendar doesn't work right past January 2025."); + bb_setCharacterLine(dData, 8, "Ovo", "But that's a future problem."); + bb_setCharacterLine(dData, 9, "Ovo", "It's all very confusing, I know."); + bb_setCharacterLine(dData, 10, "Ovo", "Anyways, thanks for playing the game!"); + bb_setCharacterLine(dData, 11, "Ovo", "This universe's timeline cannot process your awesomeness!"); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterEnding; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + + // destroy self + bb_destroyEntity(self, false, true); + return; } - if (gData->touching) + if (!fbData->firstDialogeDone) { - xOff += entityManager->sprites[self->spriteIndex].originX; - yOff += entityManager->sprites[self->spriteIndex].originY; + fbData->firstDialogeDone = true; + self->gameData->isPaused = true; + bb_entity_t* ovo + = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, + self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(6, "???"); + + bb_setCharacterLine(dData, 0, "???", "muahahahahaha!"); + bb_setCharacterLine(dData, 1, "Ovo", "Who's there?"); + bb_setCharacterLine(dData, 2, "???", "..."); + bb_setCharacterLine(dData, 3, "Ovo", "Who the heck else is down here?"); + bb_setCharacterLine(dData, 4, "???", "..."); + bb_setCharacterLine(dData, 5, "???", "Right. On. Time."); + + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + dData->curString = -1; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + } + else if (!fbData->secondDialogeDone && fbData->health < 3500) + { + fbData->secondDialogeDone = true; + self->gameData->isPaused = true; + bb_entity_t* ovo + = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, + self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(16, "Ovo"); + + bb_setCharacterLine(dData, 0, "Ovo", "WHO ARE YOU?"); + bb_setCharacterLine(dData, 1, "Ovo???", "HA! I'm future you from the past!"); + bb_setCharacterLine(dData, 2, "Ovo", "How is this possible?"); + bb_setCharacterLine(dData, 3, "Ovo???", + "Do you think Garbotnik stays in heaven because he, too, lives in fear of what he's " + "created here on earth?"); + bb_setCharacterLine(dData, 4, "Ovo", "That actually makes no sense. "); + bb_setCharacterLine(dData, 5, "Ovo???", "How could I expect YOU to understand?"); + bb_setCharacterLine(dData, 6, "DOCTOR OVO", "You're stuck living in the present, Ovo. And I have a PHD!"); + bb_setCharacterLine(dData, 7, "Ovo", "Oh nice! What will my degree be in?"); + bb_setCharacterLine(dData, 8, "DOCTOR OVO", "How naive!"); + bb_setCharacterLine(dData, 9, "DOCTOR OVO", "There are no fancy titles in death, fool."); + bb_setCharacterLine(dData, 10, "Ovo", "You watch your mouth! Because I'm the real O.G.!"); + bb_setCharacterLine(dData, 11, "Ovo", "WHERE'S MY CHAOS ORB?"); + char voiceLine[30]; + snprintf(voiceLine, sizeof(voiceLine), "You're %d day%s too late.", self->gameData->day, + self->gameData->day == 1 ? "" : "s"); + bb_setCharacterLine(dData, 12, "DOCTOR OVO", voiceLine); + bb_setCharacterLine(dData, 13, "DOCTOR OVO", "Now there's just one loose end I have to tie up."); + bb_setCharacterLine(dData, 14, "DOCTOR OVO", "AND IT'S YOU!"); + bb_setCharacterLine(dData, 15, "Ovo", "You think you can defeat me? I egg to differ."); + + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + dData->curString = -1; + bb_setData(ovo, dData, DIALOGUE_DATA); + } + } + + if (fbData->damageEffect > 0) + { + fbData->damageEffect -= self->gameData->elapsedUs >> 11; + } + + // get a normalized vector from self to the player + vec_t toPlayer = subVec2d(self->gameData->entityManager.playerEntity->pos, self->pos); + fastNormVec(&toPlayer.x, &toPlayer.y); + fbData->vel = addVec2d(fbData->vel, mulVec2d(toPlayer, self->gameData->elapsedUs >> 14)); + // apply drag to velocity + fbData->vel = divVec2d(fbData->vel, 2); + + // move the boss + self->pos = addVec2d(self->pos, divVec2d(mulVec2d(fbData->vel, self->gameData->elapsedUs >> 14), 16)); + + for (int egg = 0; egg < 10; egg++) + { + if (fbData->bossEggs[egg] != NULL && fbData->bossEggs[egg]->active + && EGG_LEAVES_DATA == fbData->bossEggs[egg]->dataType) + { + bb_eggLeavesData_t* elData = (bb_eggLeavesData_t*)fbData->bossEggs[egg]->data; + // calculate new egg pos relative to self. + + vec_t toEgg = subVec2d(fbData->bossEggs[egg]->pos, self->pos); + toEgg = rotateVec2d(toEgg, 1); + fastNormVec(&toEgg.x, &toEgg.y); + toEgg = mulVec2d(toEgg, 5); + fbData->bossEggs[egg]->pos = addVec2d(self->pos, toEgg); + elData->egg->pos = fbData->bossEggs[egg]->pos; + } + } + + // increment the stateTimer + fbData->stateTimer += self->gameData->elapsedUs >> 9; + if (fbData->stateTimer > 0) + { + uint8_t check = fbData->stateTimer / 3277; + if (fbData->bossEggs[check] == NULL || fbData->bossEggs[check]->active == false + || EGG_LEAVES_DATA != fbData->bossEggs[check]->dataType) + { + bb_ensureEntitySpace(&self->gameData->entityManager, 2); + fbData->bossEggs[check] + = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, EGG_LEAVES, 1, + self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); + if (fbData->bossEggs[check] != NULL) + { + ((bb_eggLeavesData_t*)fbData->bossEggs[check]->data)->egg + = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, EGG, 1, + (fbData->bossEggs[check]->pos.x >> DECIMAL_BITS) - 200 + bb_randomInt(0, 400), + 195 << 5, false, false); + if (((bb_eggLeavesData_t*)fbData->bossEggs[check]->data)->egg == NULL) + { + bb_destroyEntity(fbData->bossEggs[check], false, true); + } + else + { + fbData->bossEggs[check]->collisions + = heap_caps_calloc_tag(1, sizeof(list_t), MALLOC_CAP_SPIRAM, "rCollisions"); + list_t* others = heap_caps_calloc_tag(1, sizeof(list_t), MALLOC_CAP_SPIRAM, "rOthers"); + push(others, (void*)GARBOTNIK_FLYING); + bb_collision_t* collision + = heap_caps_calloc_tag(1, sizeof(bb_collision_t), MALLOC_CAP_SPIRAM, "rCollision"); + *collision = (bb_collision_t){others, bb_onCollisionBossEgg}; + push(fbData->bossEggs[check]->collisions, (void*)collision); + } + } + } + } +} + +void bb_drawGarbotnikFlying(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self) +{ + if (GARBOTNIK_DATA != self->dataType) + { + return; + } + + bb_garbotnikData_t* gData = (bb_garbotnikData_t*)self->data; + + // draw the tow cables + node_t* current = gData->towedEntities.first; + while (current != NULL) + { + bb_entity_t* curEntity = (bb_entity_t*)current->val; + drawLineFast((self->pos.x >> DECIMAL_BITS) - camera->pos.x, (self->pos.y >> DECIMAL_BITS) - camera->pos.y, + (curEntity->pos.x >> DECIMAL_BITS) - camera->pos.x, + (curEntity->pos.y >> DECIMAL_BITS) - camera->pos.y, c425); + current = current->next; + } + + int16_t xOff = (self->pos.x >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originX - camera->pos.x; + int16_t yOff = (self->pos.y >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originY - camera->pos.y; + + uint8_t frameIndex; + bool useSimple = true; + + // Determine frameIndex and draw style + if (gData->yaw.x < -1400) + { + frameIndex = 0; + } + else if (gData->yaw.x < -400) + { + frameIndex = 1; + } + else if (gData->yaw.x < 400) + { + frameIndex = 2; + } + else if (gData->yaw.x < 1400) + { + frameIndex = 1; + useSimple = false; + } + else + { + frameIndex = 0; + useSimple = false; + } + + // Draw the sprite + if (gData->damageEffect > 70 || (gData->damageEffect > 0 && bb_randomInt(0, 1))) + { + drawWsgPalette(&entityManager->sprites[self->spriteIndex].frames[frameIndex], xOff, yOff, + &self->gameData->damagePalette, !useSimple, false, 0); + } + else if (useSimple) + { + drawWsgSimple(&entityManager->sprites[self->spriteIndex].frames[frameIndex], xOff, yOff); + } + else + { + drawWsg(&entityManager->sprites[self->spriteIndex].frames[frameIndex], xOff, yOff, true, false, 0); + } + + if (gData->touching) + { + xOff += entityManager->sprites[self->spriteIndex].originX; + yOff += entityManager->sprites[self->spriteIndex].originY; int32_t x; int32_t y; getTouchCartesian(gData->phi, gData->r, &x, &y); @@ -3415,6 +3808,12 @@ void bb_drawCharacterTalk(bb_entityManager_t* entityManager, rectangle_t* camera { textColor = c544; } + else if (strcmp(dData->characters[dData->curString], "???") == 0 + || strcmp(dData->characters[dData->curString], "Ovo???") == 0 + || strcmp(dData->characters[dData->curString], "DOCTOR OVO") == 0) + { + textColor = c550; + } if (dData->blinkTimer > 0) { // The sprites I took from UTT need to be drawn a little more to the left. @@ -3654,7 +4053,7 @@ void bb_drawGrabbyHand(bb_entityManager_t* entityManager, rectangle_t* camera, b // don't draw the hand if it is fully retracted. Cuts down on overdraw a lot of the time. if (self->gameData->entityManager.sprites[BB_GRABBY_HAND].originY > -26) { - drawWsgSimple(&entityManager->sprites[self->spriteIndex].frames[0], + drawWsgSimple(&entityManager->sprites[self->spriteIndex].frames[self->currentAnimationFrame], (self->pos.x >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originX - camera->pos.x, (self->pos.y >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originY - camera->pos.y); @@ -4049,7 +4448,7 @@ void bb_drawSpaceLaser(bb_entityManager_t* entityManager, rectangle_t* camera, b bb_entity_t* bump = bb_createEntity(entityManager, ONESHOT_ANIMATION, false, BUMP_ANIM, 1, (self->pos.x >> DECIMAL_BITS) - 12 + foo % 25, (self->pos.y + self->halfHeight) >> DECIMAL_BITS, true, false); - if (foo >= 25) + if (foo >= 45 && bump != NULL) { bump->drawFunction = bb_drawHitEffect; } @@ -4093,6 +4492,91 @@ void bb_drawDeadBug(bb_entityManager_t* entityManager, rectangle_t* camera, bb_e false, true, 0); } +void bb_drawQuickplay(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self) +{ + bb_menuData_t* mData = NULL; + for (int eIdx = 0; eIdx < MAX_ENTITIES; eIdx++) + { + bb_entity_t* menu = &entityManager->entities[eIdx]; + if (menu != NULL && menu->spriteIndex == BB_MENU && menu->updateFunction != NULL) + { + mData = (bb_menuData_t*)menu->data; + break; + } + } + if (mData == NULL) + { + return; + } + + drawRectFilled(0, 0, TFT_WIDTH, TFT_HEIGHT, c000); + int16_t xOff = 20; + int16_t yOff = 30; + + drawTextWordWrapCentered( + &self->gameData->font, c444, + "Quick Play mode skips the intro sequence and all tutorial dialogue. Are you sure you want to continue?", &xOff, + &yOff, TFT_WIDTH - 5, TFT_HEIGHT - 5); + drawRect((TFT_WIDTH >> 2) - 40, TFT_HEIGHT >> 1, (TFT_WIDTH >> 2) + 40, (TFT_HEIGHT >> 1) + 40, + mData->selectionIdx == 2 ? c550 : c444); + xOff = (TFT_WIDTH >> 2) - 40; + yOff = (TFT_HEIGHT >> 1) + 15; + drawTextWordWrapCentered(&self->gameData->font, mData->selectionIdx == 2 ? c550 : c444, "NO", &xOff, &yOff, + (TFT_WIDTH >> 2) + 40, (TFT_HEIGHT >> 1) + 60); + drawRect((TFT_WIDTH >> 1) + (TFT_WIDTH >> 2) - 40, TFT_HEIGHT >> 1, (TFT_WIDTH >> 1) + (TFT_WIDTH >> 2) + 40, + (TFT_HEIGHT >> 1) + 40, mData->selectionIdx == 3 ? c550 : c444); + xOff = (TFT_WIDTH >> 1) + (TFT_WIDTH >> 2) - 40; + yOff = (TFT_HEIGHT >> 1) + 15; + drawTextWordWrapCentered(&self->gameData->font, mData->selectionIdx == 3 ? c550 : c444, "YES", &xOff, &yOff, + (TFT_WIDTH >> 1) + (TFT_WIDTH >> 2) + 40, (TFT_HEIGHT >> 1) + 60); +} + +void bb_drawFinalBoss(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self) +{ + if (FINAL_BOSS_DATA != self->dataType) + { + return; + } + + bb_finalBossData_t* fbData = (bb_finalBossData_t*)self->data; + + // draw the tow cables + for (int egg = 0; egg < 10; egg++) + { + if (fbData->bossEggs[egg] != NULL && fbData->bossEggs[egg]->active + && EGG_LEAVES_DATA == fbData->bossEggs[egg]->dataType) + { + drawLineFast((self->pos.x >> DECIMAL_BITS) - camera->pos.x, (self->pos.y >> DECIMAL_BITS) - camera->pos.y, + (fbData->bossEggs[egg]->pos.x >> DECIMAL_BITS) - camera->pos.x, + (fbData->bossEggs[egg]->pos.y >> DECIMAL_BITS) - camera->pos.y, c425); + } + } + // node_t* current = gData->towedEntities.first; + // while (current != NULL) + // { + // bb_entity_t* curEntity = (bb_entity_t*)current->val; + // drawLineFast((self->pos.x >> DECIMAL_BITS) - camera->pos.x, (self->pos.y >> DECIMAL_BITS) - camera->pos.y, + // (curEntity->pos.x >> DECIMAL_BITS) - camera->pos.x, + // (curEntity->pos.y >> DECIMAL_BITS) - camera->pos.y, c425); + // current = current->next; + // } + + int16_t xOff = (self->pos.x >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originX - camera->pos.x; + int16_t yOff = (self->pos.y >> DECIMAL_BITS) - entityManager->sprites[self->spriteIndex].originY - camera->pos.y; + + // Draw the sprite + if (fbData->damageEffect > 70 || (fbData->damageEffect > 0 && bb_randomInt(0, 1))) + { + drawWsgPalette(&entityManager->sprites[self->spriteIndex].frames[self->currentAnimationFrame], xOff, yOff, + &self->gameData->damagePalette, fbData->vel.x > 0, false, 0); + } + else + { + drawWsg(&entityManager->sprites[self->spriteIndex].frames[self->currentAnimationFrame], xOff, yOff, + fbData->vel.x > 0, false, 0); + } +} + // void bb_drawRect(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self) // { // drawRect (((self->pos.x - self->halfWidth) >>DECIMAL_BITS) - camera->pos.x, @@ -4109,8 +4593,41 @@ void bb_onCollisionHarpoon(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* // pop that egg int32_t tile_i = other->pos.x >> 9; // 4 decimal bits and 5 bitshifts is divide by 32. int32_t tile_j = other->pos.y >> 9; // 4 decimal bits and 5 bitshifts is divide by 32. - self->gameData->tilemap.fgTiles[tile_i][tile_j].health = 0; - bb_crumbleDirt(other->gameData, 2, tile_i, tile_j, true, true); + if (self->gameData->tilemap.fgTiles[tile_i][tile_j].health + == 0) // This case is for boss eggs that are in the air. + { + // spawn a bug + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + bb_entity_t* bug = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, false, + bb_randomInt(8, 13), 1, tile_i << 5, tile_j << 5, false, false); + + if (bug != NULL) + { + midiPlayer_t* sfx = soundGetPlayerSfx(); + midiPlayerReset(sfx); + soundPlaySfx(&self->gameData->sfxEgg, 0); + + if (self->gameData->tilemap.fgTiles[tile_i][tile_j].entity != NULL) + { + bb_entity_t* egg + = ((bb_eggLeavesData_t*)(self->gameData->tilemap.fgTiles[tile_i][tile_j].entity->data))->egg; + if (egg != NULL) + { + // destroy the egg + bb_destroyEntity(egg, false, true); + } + // destroy this (eggLeaves) + bb_destroyEntity(self->gameData->tilemap.fgTiles[tile_i][tile_j].entity, false, true); + } + self->gameData->tilemap.fgTiles[tile_i][tile_j].embed = NOTHING_EMBED; + } + bb_crumbleDirt(other->gameData, 2, tile_i, tile_j, true, true); + } + else + { + self->gameData->tilemap.fgTiles[tile_i][tile_j].health = 0; + bb_crumbleDirt(other->gameData, 2, tile_i, tile_j, true, true); + } // destroy this harpoon bb_destroyEntity(self, false, true); } @@ -4135,26 +4652,28 @@ void bb_onCollisionHarpoon(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); - - bb_dialogueData_t* dData = bb_createDialogueData(7, "Ovo"); - - bb_setCharacterLine(dData, 0, "Ovo", "ARLIGHT! You got it!!!"); - bb_setCharacterLine(dData, 1, "Ovo", "Ahem... I mean, not bad."); - bb_setCharacterLine(dData, 2, "Ovo", "In fact, I'm the one who got it."); - bb_setCharacterLine(dData, 3, "Ovo", - "Press the 'A' button near a bug to attach a cable and tow it back to the " - "booster to get credit."); - bb_setCharacterLine( - dData, 4, "Ovo", - "Careful with that tow cable, because a right-side-up bug will drag me around."); - bb_setCharacterLine(dData, 5, "Ovo", - "Did you know bugs can carry over 100 times their body weight?"); - bb_setCharacterLine(dData, 6, "Ovo", - "They're pretty strong, but I wonder if I could pry them off the ceiling."); - - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(7, "Ovo"); + + bb_setCharacterLine(dData, 0, "Ovo", "ALRIGHT! You got it!!!"); + bb_setCharacterLine(dData, 1, "Ovo", "Ahem... I mean, not bad."); + bb_setCharacterLine(dData, 2, "Ovo", "In fact, I'm the one who got it."); + bb_setCharacterLine(dData, 3, "Ovo", + "Press the 'A' button near a bug to attach a cable and tow it back to the " + "booster to get credit."); + bb_setCharacterLine( + dData, 4, "Ovo", + "Careful with that tow cable, because a right-side-up bug will drag me around."); + bb_setCharacterLine(dData, 5, "Ovo", + "Did you know bugs can carry over 100 times their body weight?"); + bb_setCharacterLine(dData, 6, "Ovo", + "They're pretty strong, but I wonder if I could pry them off the ceiling."); + + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } } else @@ -4275,16 +4794,18 @@ void bb_onCollisionRocketGarbotnik(bb_entity_t* self, bb_entity_t* other, bb_hit { rData->numDonuts--; // Get the donut back out of the busted booster + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_DONUT, 1, - (self->pos.x >> DECIMAL_BITS) + 5, (self->pos.y >> DECIMAL_BITS), true, false); + (self->pos.x >> DECIMAL_BITS) + 5, (self->pos.y >> DECIMAL_BITS) - 40, true, false); } if ((rData->numBugs % 10 != 0) && bb_randomInt(0, 60) == 60) { rData->numBugs--; // Get the bug back out of the busted booster + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* bug = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, bb_randomInt(8, 13), 1, - (self->pos.x >> DECIMAL_BITS) + 5, (self->pos.y >> DECIMAL_BITS), true, false); + (self->pos.x >> DECIMAL_BITS) + 5, (self->pos.y >> DECIMAL_BITS) - 40, true, false); bb_bugDeath(bug, NULL); ((bb_physicsData_t*)bug->data)->vel = (vec_t){bb_randomInt(-100, 100), bb_randomInt(-100, 0)}; } @@ -4295,7 +4816,7 @@ void bb_onCollisionCarIdle(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* { bb_setupMidi(); unloadMidiFile(&self->gameData->bgm); - loadMidiFile("sh_revenge.mid", &self->gameData->bgm, true); + loadMidiFile("BigBug_Boss.mid", &self->gameData->bgm, true); globalMidiPlayerPlaySong(&self->gameData->bgm, MIDI_BGM); // close the door and make it not cacheable so bugs don't walk out offscreen. @@ -4410,10 +4931,11 @@ void bb_onCollisionGrabbyHand(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_ // if nothing grabbed yet and there is something in hand to grab if (self->currentAnimationFrame == 0 && self->pos.y - (self->gameData->entityManager.sprites[BB_GRABBY_HAND].originY << DECIMAL_BITS) - < other->pos.y - 128) + < other->pos.y - 95) { self->paused = false; ghData->grabbed = other; + bb_clearCollisions(other, false); } } @@ -4502,11 +5024,6 @@ void bb_onCollisionSpit(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hit midiPlayer_t* sfx = soundGetPlayerSfx(); midiPlayerReset(sfx); soundPlaySfx(&self->gameData->sfxDamage, 0); - if (gData->fuel < 0) - { - gData->fuel - = 1; // It'll decrement soon anyways. Keeps more of the game over code on Garbotnik's side of the fence. - } } else // PHYSICS_DATA { @@ -4641,28 +5158,31 @@ void bb_onCollisionBrickTutorial(bb_entity_t* self, bb_entity_t* other, bb_hitIn bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(12, "Ovo"); - bb_dialogueData_t* dData = bb_createDialogueData(12, "Ovo"); - - bb_setCharacterLine(dData, 0, "Ovo", "\"---Incoming Transmission from Mr. Dev---\""); - bb_setCharacterLine(dData, 1, "Ovo", "What in the heck?"); - bb_setCharacterLine(dData, 2, "Ovo", "Dang radio's acting up again!"); - bb_setCharacterLine(dData, 3, "Ovo", "\"The bricks can be destroyed by dealing 100 damage.\""); - bb_setCharacterLine(dData, 4, "Ovo", "\"Bring Ovo Garbotnik to a complete stop in a tight gap.\""); - bb_setCharacterLine(dData, 5, "Ovo", - "\"Then move briefly toward the brick to initiate a ping-ponging movement pattern.\""); - bb_setCharacterLine( - dData, 6, "Ovo", - "\"Same as the other garbage densities, bricks will crumble if they are totally disconnected.\""); - bb_setCharacterLine(dData, 7, "Ovo", "\"Or they can be smashed easily with heavy falling objects.\""); - bb_setCharacterLine(dData, 8, "Ovo", "\"Big Bug's early-game design is all about trading fuel for upgrades.\""); - bb_setCharacterLine(dData, 9, "Ovo", "\"So be sure to strategize how to use your time!\""); - bb_setCharacterLine(dData, 10, "Ovo", "\"The late-game design has a shift in focus away from fuel.\""); - bb_setCharacterLine(dData, 11, "Ovo", "\"You'll just have to see it for yourself!\""); + bb_setCharacterLine(dData, 0, "Ovo", "\"---Incoming Transmission from Mr. Dev---\""); + bb_setCharacterLine(dData, 1, "Ovo", "What in the heck?"); + bb_setCharacterLine(dData, 2, "Ovo", "Dang radio's acting up again!"); + bb_setCharacterLine(dData, 3, "Ovo", "\"The bricks can be destroyed by dealing 100 damage.\""); + bb_setCharacterLine(dData, 4, "Ovo", "\"Bring Ovo Garbotnik to a complete stop in a tight gap.\""); + bb_setCharacterLine(dData, 5, "Ovo", + "\"Then move briefly toward the brick to initiate a ping-ponging movement pattern.\""); + bb_setCharacterLine( + dData, 6, "Ovo", + "\"Same as the other garbage densities, bricks will crumble if they are totally disconnected.\""); + bb_setCharacterLine(dData, 7, "Ovo", "\"Or they can be smashed easily with heavy falling objects.\""); + bb_setCharacterLine(dData, 8, "Ovo", + "\"Big Bug's early-game design is all about using your fuel to get upgrades.\""); + bb_setCharacterLine(dData, 9, "Ovo", "\"So be sure to strategize how to use your time!\""); + bb_setCharacterLine(dData, 10, "Ovo", "\"The late-game design has a shift in focus away from fuel.\""); + bb_setCharacterLine(dData, 11, "Ovo", "\"You'll just have to see it for yourself!\""); - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } bb_destroyEntity(self, false, true); } @@ -4697,276 +5217,332 @@ void bb_onCollisionSpaceLaserBug(bb_entity_t* self, bb_entity_t* other, bb_hitIn } } +void bb_onCollisionBossEgg(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hitInfo) +{ + if (other->dataType != GARBOTNIK_DATA) + { + return; + } + bb_garbotnikData_t* gData = (bb_garbotnikData_t*)other->data; + gData->damageEffect = 100; + gData->fuel -= 10000; + midiPlayer_t* sfx = soundGetPlayerSfx(); + midiPlayerReset(sfx); + soundPlaySfx(&self->gameData->sfxDamage, 0); + // transform "hatch" into a bug... + bb_eggLeavesData_t* elData = (bb_eggLeavesData_t*)self->data; + // create a bug + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + bb_entity_t* bug + = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, false, bb_randomInt(8, 13), 1, + elData->egg->pos.x >> DECIMAL_BITS, elData->egg->pos.y >> DECIMAL_BITS, false, false); + if (bug != NULL) + { + // destroy the egg + bb_destroyEntity(elData->egg, false, true); + // destroy this + bb_destroyEntity(self, false, true); + } +} + +void bb_onCollisionBoss(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hitInfo) +{ + // damage self + bb_finalBossData_t* fbData = (bb_finalBossData_t*)self->data; + fbData->damageEffect = 100; + fbData->health -= 34; + + // destroy other + bb_destroyEntity(other, false, true); +} + void bb_startGarbotnikIntro(bb_entity_t* self) { bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(24, "Ovo"); + + // longest possible string " " + // bb_setCharacterLine(dData, 0, "A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A + // A A A A A A A A A A A A A A A A A A A A A"); + bb_setCharacterLine(dData, 0, "Ovo", "Holy bug farts!"); // + bb_setCharacterLine( + dData, 1, "Ovo", + "After I sold the chilidog car fresheners to MAGFest, Garbotnik Industries' stock went up by 6,969%!"); + bb_setCharacterLine( + dData, 2, "Ovo", + "I'm going to use my time machine to steal the next big-selling trinket from the future now."); + bb_setCharacterLine(dData, 3, "Ovo", "That will floor all my stakeholders and make me UNDEFINED money!"); + bb_setCharacterLine(dData, 4, "Ovo", + "With that kind of cash, I can recruit 200 professional bassoon players to the MAGFest " + "Community Orchestra."); + bb_setCharacterLine(dData, 5, "Ovo", "I'm so hyped to turn on my time machine for the first time!"); + bb_setCharacterLine(dData, 6, "Ovo", "Everything's in order."); + bb_setCharacterLine(dData, 7, "Ovo", "Even Pango can't stop me!"); + bb_setCharacterLine(dData, 8, "Ovo", "I just have to attach the chaos orb right here."); + bb_setCharacterLine(dData, 9, "Ovo", "Where did I put that orb?"); + bb_setCharacterLine(dData, 10, "Ovo", "hmmm..."); + bb_setCharacterLine(dData, 11, "Ovo", "What about in the freezer?"); + bb_setCharacterLine(dData, 12, "Ovo", "I've checked every inch of the death dumpster."); + bb_setCharacterLine(dData, 13, "Ovo", "Glitch my circuits!"); + bb_setCharacterLine(dData, 14, "Ovo", "It must have gone out with the trash last Wednesday."); + bb_setCharacterLine(dData, 15, "Ovo", "Can I get an F in the chat?"); + bb_setCharacterLine(dData, 16, "Ovo", "..."); + bb_setCharacterLine(dData, 17, "Ovo", "The chaos orb is three times denser than a black hole."); + bb_setCharacterLine( + dData, 18, "Ovo", + "Well if Garbotnik Sanitation Industries took it to the landfill, then it is definitely at the " + "VERY BOTTOM of the dump."); + bb_setCharacterLine(dData, 19, "Ovo", "Not a problem."); + bb_setCharacterLine(dData, 20, "Ovo", "We have the technology to retrieve it."); + bb_setCharacterLine( + dData, 21, "Ovo", + "Safety first. Activate the cloning machine in case I should perish on that nuclear wasteland."); + bb_setCharacterLine(dData, 22, "Ovo", "fine."); + bb_setCharacterLine(dData, 23, "Ovo", "Stupid safety rules. YOLO!"); + + dData->curString = -1; - bb_dialogueData_t* dData = bb_createDialogueData(24, "Ovo"); - - // longest possible string " " - // bb_setCharacterLine(dData, 0, "A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A - // A A A A A A A A A A A A A A A A A A A"); - bb_setCharacterLine(dData, 0, "Ovo", "Holy bug farts!"); // - bb_setCharacterLine( - dData, 1, "Ovo", - "After I sold the chilidog car fresheners to MAGFest, Garbotnik Industries' stock went up by 6,969%!"); - bb_setCharacterLine(dData, 2, "Ovo", - "I'm going to use my time machine to steal the next big-selling trinket from the future now."); - bb_setCharacterLine(dData, 3, "Ovo", "That will floor all my stakeholders and make me UNDEFINED money!"); - bb_setCharacterLine( - dData, 4, "Ovo", - "With that kind of cash, I can recruit 200 professional bassoon players to the MAGFest Community Orchestra."); - bb_setCharacterLine(dData, 5, "Ovo", "I'm so hyped to turn on my time machine for the first time!"); - bb_setCharacterLine(dData, 6, "Ovo", "Everything's in order."); - bb_setCharacterLine(dData, 7, "Ovo", "Even Pango can't stop me!"); - bb_setCharacterLine(dData, 8, "Ovo", "I just have to attach the chaos orb right here."); - bb_setCharacterLine(dData, 9, "Ovo", "Where did I put that orb?"); - bb_setCharacterLine(dData, 10, "Ovo", "hmmm..."); - bb_setCharacterLine(dData, 11, "Ovo", "What about in the freezer?"); - bb_setCharacterLine(dData, 12, "Ovo", "I've checked every inch of the death dumpster."); - bb_setCharacterLine(dData, 13, "Ovo", "Glitch my circuits!"); - bb_setCharacterLine(dData, 14, "Ovo", "It must have gone out with the trash last Wednesday."); - bb_setCharacterLine(dData, 15, "Ovo", "Can I get an F in the chat?"); - bb_setCharacterLine(dData, 16, "Ovo", "..."); - bb_setCharacterLine(dData, 17, "Ovo", "The chaos orb is three times denser than a black hole."); - bb_setCharacterLine(dData, 18, "Ovo", - "Well if Garbotnik Sanitation Industries took it to the landfill, then it is definitely at the " - "VERY BOTTOM of the dump."); - bb_setCharacterLine(dData, 19, "Ovo", "Not a problem."); - bb_setCharacterLine(dData, 20, "Ovo", "We have the technology to retrieve it."); - bb_setCharacterLine( - dData, 21, "Ovo", - "Safety first. Activate the cloning machine in case I should perish on that nuclear wasteland."); - bb_setCharacterLine(dData, 22, "Ovo", "fine."); - bb_setCharacterLine(dData, 23, "Ovo", "Stupid safety rules. YOLO!"); - - dData->curString = -1; - - dData->endDialogueCB = &bb_afterGarbotnikIntro; - - bb_setData(ovo, dData, DIALOGUE_DATA); + dData->endDialogueCB = &bb_afterGarbotnikIntro; + + bb_setData(ovo, dData, DIALOGUE_DATA); + } } void bb_startGarbotnikLandingTalk(bb_entity_t* self) { + if (self == NULL) + { + return; + } bb_garbotnikData_t* gData = (bb_garbotnikData_t*)self->data; bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); - - bb_dialogueData_t* dData = bb_createDialogueData(1, "Ovo"); - - int16_t phraseIdx = 0; - int16_t arraySize = sizeof(gData->landingPhrases) / sizeof(gData->landingPhrases[0]); - while (gData->landingPhrases[phraseIdx] == -1) + if (ovo != NULL) { - if (phraseIdx == arraySize - 1) + bb_dialogueData_t* dData = bb_createDialogueData(1, "Ovo"); + + int16_t phraseIdx = 0; + int16_t arraySize = sizeof(gData->landingPhrases) / sizeof(gData->landingPhrases[0]); + while (gData->landingPhrases[phraseIdx] == -1) { - // We've reached the end of saying everything. - // create sequential numbers of all phrase indices - for (int16_t i = 0; i < arraySize; i++) - gData->landingPhrases[i] = i; - // shuffle the array - for (int16_t i = arraySize - 1; i > 0; i--) + if (phraseIdx == arraySize - 1) { - int16_t j = (int16_t)(bb_randomInt(0, INT_MAX) % (i + 1)); - int16_t temp = gData->landingPhrases[i]; - gData->landingPhrases[i] = gData->landingPhrases[j]; - gData->landingPhrases[j] = temp; + // We've reached the end of saying everything. + // create sequential numbers of all phrase indices + for (int16_t i = 0; i < arraySize; i++) + gData->landingPhrases[i] = i; + // shuffle the array + for (int16_t i = arraySize - 1; i > 0; i--) + { + int16_t j = (int16_t)(bb_randomInt(0, INT_MAX) % (i + 1)); + int16_t temp = gData->landingPhrases[i]; + gData->landingPhrases[i] = gData->landingPhrases[j]; + gData->landingPhrases[j] = temp; + } + phraseIdx = 0; + } + else + { + phraseIdx++; } - phraseIdx = 0; - } - else - { - phraseIdx++; - } - } - switch (gData->landingPhrases[phraseIdx]) - { - case 0: - { - // Max dialogue string roughly: here----V - bb_setCharacterLine(dData, 0, "Ovo", "Ah, sweet stench! How I've longed for your olfactory embrace."); - break; - } - case 1: - { - bb_setCharacterLine(dData, 0, "Ovo", - "Tonight's special: Beetle Bruschetta with a side of centipede salad!"); - break; - } - case 2: - { - bb_setCharacterLine(dData, 0, "Ovo", "Another day, another dump full of delectable delights!"); - break; - } - case 3: - { - bb_setCharacterLine(dData, 0, "Ovo", "Step aside, garbage! The doctor is in!"); - break; - } - case 4: - { - bb_setCharacterLine(dData, 0, "Ovo", "I must find that chaos orb at all costs!"); - break; - } - case 5: - { - bb_setCharacterLine(dData, 0, "Ovo", "The Pango pest must be stopped!"); - break; - } - case 6: - { - bb_setCharacterLine(dData, 0, "Ovo", "I'll get you next time Pango! NEXT TIME!"); - break; - } - case 7: - { - bb_setCharacterLine(dData, 0, "Ovo", "Would you look at the time... It's GARBAGE TIME!"); - break; - } - case 8: - { - bb_setCharacterLine(dData, 0, "Ovo", "Did I remember to wax my moustache today?"); - break; - } - case 9: - { - bb_setCharacterLine(dData, 0, "Ovo", "Is it spelled \"moustache\" or \"mustache?\" Better look it up..."); - break; - } - case 10: - { - bb_setCharacterLine(dData, 0, "Ovo", "I wonder how my buddy Hank Waddle is holding up..."); - break; - } - case 11: - { - bb_setCharacterLine(dData, 0, "Ovo", "Man, I sure hope I see some cool bugs today!"); - break; - } - case 12: - { - bb_setCharacterLine(dData, 0, "Ovo", "I'm here to take in the trash."); - break; - } - case 13: - { - bb_setCharacterLine(dData, 0, "Ovo", - "I must remember to research what dastardly technology this dump used to ensure new " - "arrivals wind up at the bottom..."); - break; - } - case 14: - { - bb_setCharacterLine(dData, 0, "Ovo", - "I promise, this is perfectly sanitary, the Chaos Orb has antimicrobial properties."); - break; - } - case 15: - { - bb_setCharacterLine(dData, 0, "Ovo", "Of course you can trust me; I'm a doctor."); - break; - } - case 16: - { - bb_setCharacterLine(dData, 0, "Ovo", "Come on and jump and welcome to the dump!"); - break; - } - case 17: - { - bb_setCharacterLine(dData, 0, "Ovo", "Oh, I'd been looking for that garbage ray!"); - break; - } - case 18: - { - bb_setCharacterLine( - dData, 0, "Ovo", - "If you ask me what I have a degree in one more time, I'm asking the Internet to draw fanart of you."); - break; - } - case 19: - { - bb_setCharacterLine(dData, 0, "Ovo", "Oh, I'd been looking for that garbage ray!"); - break; - } - case 20: - { - bb_setCharacterLine(dData, 0, "Ovo", "Did I remember to turn off the disintegrator before I left the lab?"); - break; - } - case 21: - { - bb_setCharacterLine(dData, 0, "Ovo", "Oooh piece of candy!"); - break; - } - case 22: - { - bb_setCharacterLine(dData, 0, "Ovo", - "If they beat this game fast enough, does that mean I too have to strip?"); - break; - } - case 23: - { - bb_setCharacterLine(dData, 0, "Ovo", "Sluuurrrrrrrrrrp"); - break; - } - case 24: - { - bb_setCharacterLine(dData, 0, "Ovo", "A chili dog? No no, I've already had my... FILL."); - break; - } - case 25: - { - bb_setCharacterLine(dData, 0, "Ovo", "CHAOS... CONT.... oh, I appear to have been sent a C&D."); - break; - } - case 26: - { - bb_setCharacterLine(dData, 0, "Ovo", "I didn't like Clone #61 much anyway."); - break; - } - case 27: - { - bb_setCharacterLine(dData, 0, "Ovo", "Remember, Ovo is pronounced like 'oooh-voo' with a wink at the end."); - break; - } - case 28: - { - bb_setCharacterLine(dData, 0, "Ovo", "You don't have enough badge ribbons to train me."); - break; } - default: + switch (gData->landingPhrases[phraseIdx]) { - break; + case 0: + { + // Max dialogue string roughly: here----V + bb_setCharacterLine(dData, 0, "Ovo", "Ah, sweet stench! How I've longed for your olfactory embrace."); + break; + } + case 1: + { + bb_setCharacterLine(dData, 0, "Ovo", + "Tonight's special: Beetle Bruschetta with a side of centipede salad!"); + break; + } + case 2: + { + bb_setCharacterLine(dData, 0, "Ovo", "Another day, another dump full of delectable delights!"); + break; + } + case 3: + { + bb_setCharacterLine(dData, 0, "Ovo", "Step aside, garbage! The doctor is in!"); + break; + } + case 4: + { + bb_setCharacterLine(dData, 0, "Ovo", "I must find that chaos orb at all costs!"); + break; + } + case 5: + { + bb_setCharacterLine(dData, 0, "Ovo", "The Pango pest must be stopped!"); + break; + } + case 6: + { + bb_setCharacterLine(dData, 0, "Ovo", "I'll get you next time Pango! NEXT TIME!"); + break; + } + case 7: + { + bb_setCharacterLine(dData, 0, "Ovo", "Would you look at the time... It's GARBAGE TIME!"); + break; + } + case 8: + { + bb_setCharacterLine(dData, 0, "Ovo", "Did I remember to wax my moustache today?"); + break; + } + case 9: + { + bb_setCharacterLine(dData, 0, "Ovo", + "Is it spelled \"moustache\" or \"mustache?\" Better look it up..."); + break; + } + case 10: + { + bb_setCharacterLine(dData, 0, "Ovo", "I wonder how my buddy Hank Waddle is holding up..."); + break; + } + case 11: + { + bb_setCharacterLine(dData, 0, "Ovo", "Man, I sure hope I see some cool bugs today!"); + break; + } + case 12: + { + bb_setCharacterLine(dData, 0, "Ovo", "I'm here to take in the trash."); + break; + } + case 13: + { + bb_setCharacterLine( + dData, 0, "Ovo", + "I must remember to research what dastardly technology this dump used to ensure new " + "arrivals wind up at the bottom..."); + break; + } + case 14: + { + bb_setCharacterLine( + dData, 0, "Ovo", + "I promise, this is perfectly sanitary, the Chaos Orb has antimicrobial properties."); + break; + } + case 15: + { + bb_setCharacterLine(dData, 0, "Ovo", "Of course you can trust me; I'm a doctor."); + break; + } + case 16: + { + bb_setCharacterLine(dData, 0, "Ovo", "Come on and jump and welcome to the dump!"); + break; + } + case 17: + { + bb_setCharacterLine(dData, 0, "Ovo", "Oh, I'd been looking for that garbage ray!"); + break; + } + case 18: + { + bb_setCharacterLine(dData, 0, "Ovo", + "If you ask me what I have a degree in one more time, I'm asking the Internet to " + "draw fanart of you."); + break; + } + case 19: + { + bb_setCharacterLine(dData, 0, "Ovo", "Oh, I'd been looking for that garbage ray!"); + break; + } + case 20: + { + bb_setCharacterLine(dData, 0, "Ovo", + "Did I remember to turn off the disintegrator before I left the lab?"); + break; + } + case 21: + { + bb_setCharacterLine(dData, 0, "Ovo", "Oooh piece of candy!"); + break; + } + case 22: + { + bb_setCharacterLine(dData, 0, "Ovo", + "If they beat this game fast enough, does that mean I too have to strip?"); + break; + } + case 23: + { + bb_setCharacterLine(dData, 0, "Ovo", "Sluuurrrrrrrrrrp"); + break; + } + case 24: + { + bb_setCharacterLine(dData, 0, "Ovo", "A chili dog? No no, I've already had my... FILL."); + break; + } + case 25: + { + bb_setCharacterLine(dData, 0, "Ovo", "CHAOS... CONT.... oh, I appear to have been sent a C&D."); + break; + } + case 26: + { + bb_setCharacterLine(dData, 0, "Ovo", "I didn't like Clone #61 much anyway."); + break; + } + case 27: + { + bb_setCharacterLine(dData, 0, "Ovo", + "Remember, Ovo is pronounced like 'oooh-voo' with a wink at the end."); + break; + } + case 28: + { + bb_setCharacterLine(dData, 0, "Ovo", "You don't have enough badge ribbons to train me."); + break; + } + default: + { + break; + } } - } - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikLandingTalk; + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikLandingTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + bb_setData(ovo, dData, DIALOGUE_DATA); + } } void bb_startGarbotnikCloningTalk(bb_entity_t* self) { bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, 0, 0, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(2, "Ovo"); // 29 - bb_dialogueData_t* dData = bb_createDialogueData(2, "Ovo"); // 29 - - bb_setCharacterLine(dData, 0, "Ovo", "I'm feeling fresh, baby!"); // V longest possible string here - bb_setCharacterLine(dData, 1, "Ovo", - "It was a good move taking those omega3 fish oils before backing up my brain."); + bb_setCharacterLine(dData, 0, "Ovo", "I'm feeling fresh, baby!"); // V longest possible string here + bb_setCharacterLine(dData, 1, "Ovo", + "It was a good move taking those omega3 fish oils before backing up my brain."); - dData->curString = -1; + dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikIntro; + dData->endDialogueCB = &bb_afterGarbotnikIntro; - bb_setData(ovo, dData, DIALOGUE_DATA); + bb_setData(ovo, dData, DIALOGUE_DATA); + } } void bb_startGarbotnikEggTutorialTalk(bb_entity_t* self) @@ -4974,18 +5550,20 @@ void bb_startGarbotnikEggTutorialTalk(bb_entity_t* self) bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(2, "Ovo"); - bb_dialogueData_t* dData = bb_createDialogueData(2, "Ovo"); - - // Max dialogue string roughly: here----V - bb_setCharacterLine(dData, 0, "Ovo", "Oooey Gooey! Look at that egg sack!"); - bb_setCharacterLine(dData, 1, "Ovo", - "I can use the directional buttons on my swadge to fly over there and check it out."); + // Max dialogue string roughly: here----V + bb_setCharacterLine(dData, 0, "Ovo", "Oooey Gooey! Look at that egg sack!"); + bb_setCharacterLine(dData, 1, "Ovo", + "I can use the directional buttons on my swadge to fly over there and check it out."); - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikEggTutorialTalk; + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikEggTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + bb_setData(ovo, dData, DIALOGUE_DATA); + } } void bb_startGarbotnikFuelTutorialTalk(bb_entity_t* self) @@ -4996,26 +5574,30 @@ void bb_startGarbotnikFuelTutorialTalk(bb_entity_t* self) bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(5, "Ovo"); - bb_dialogueData_t* dData = bb_createDialogueData(5, "Ovo"); - - // longest possible string " " - // bb_setCharacterLine(dData, 0, "A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A - // A A A A A A A A A A A A A A A A A A A"); - bb_setCharacterLine(dData, 0, "Ovo", - "When I travel away from the booster, I've got to keep an eye on my fuel level at all times."); - bb_setCharacterLine(dData, 1, "Ovo", - "Sit back atop the booster before all the lights around the outside of the swadge turn off."); - bb_setCharacterLine(dData, 2, "Ovo", - "safety first. Once back on the rocket, it takes a stupid long time for the " - "launch sequence to initiate."); - bb_setCharacterLine(dData, 3, "Ovo", "Too many regulations on equipment these days."); - bb_setCharacterLine(dData, 4, "Ovo", "Let a trashman go to space in peace."); + // longest possible string " " + // bb_setCharacterLine(dData, 0, "A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A + // A A A A A A A A A A A A A A A A A A A A A"); + bb_setCharacterLine( + dData, 0, "Ovo", + "When I travel away from the booster, I've got to keep an eye on my fuel level at all times."); + bb_setCharacterLine( + dData, 1, "Ovo", + "Sit back atop the booster before all the lights around the outside of the swadge turn off."); + bb_setCharacterLine(dData, 2, "Ovo", + "safety first. Once back on the rocket, it takes a stupid long time for the " + "launch sequence to initiate."); + bb_setCharacterLine(dData, 3, "Ovo", "Too many regulations on equipment these days."); + bb_setCharacterLine(dData, 4, "Ovo", "Let a trashman go to space in peace."); - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + bb_setData(ovo, dData, DIALOGUE_DATA); + } } void bb_afterGarbotnikTutorialTalk(bb_entity_t* self) @@ -5079,16 +5661,18 @@ void bb_afterGarbotnikLandingTalk(bb_entity_t* self) bb_entity_t* ovo = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, OVO_TALK, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, true); + if (ovo != NULL) + { + bb_dialogueData_t* dData = bb_createDialogueData(2, "Ovo"); - bb_dialogueData_t* dData = bb_createDialogueData(2, "Ovo"); - - bb_setCharacterLine(dData, 0, "Ovo", - "If I find my old booster, I can recover any unprocessed bugs and donuts."); - bb_setCharacterLine(dData, 1, "Ovo", "Just push steadily into the derelict booster to coax them out."); + bb_setCharacterLine(dData, 0, "Ovo", + "If I find my old booster, I can recover any unprocessed bugs and donuts."); + bb_setCharacterLine(dData, 1, "Ovo", "Just push steadily into the derelict booster to coax them out."); - dData->curString = -1; - dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; - bb_setData(ovo, dData, DIALOGUE_DATA); + dData->curString = -1; + dData->endDialogueCB = &bb_afterGarbotnikTutorialTalk; + bb_setData(ovo, dData, DIALOGUE_DATA); + } } } @@ -5112,21 +5696,25 @@ void bb_afterGarbotnikLandingTalk(bb_entity_t* self) bb_entity_t* curEntity = &self->gameData->entityManager.entities[i]; if (curEntity->spriteIndex == EGG_LEAVES) { + bb_ensureEntitySpace(&self->gameData->entityManager, 1); self->gameData->entityManager.viewEntity = bb_createEntity(&(self->gameData->entityManager), NO_ANIMATION, true, NO_SPRITE_POI, 1, (self->gameData->entityManager.playerEntity->pos.x >> DECIMAL_BITS), (self->gameData->entityManager.playerEntity->pos.y >> DECIMAL_BITS), false, false); - bb_goToData* tData = (bb_goToData*)self->gameData->entityManager.viewEntity->data; - tData->tracking = curEntity; - - tData->midPointSqDist - = sqMagVec2d(divVec2d((vec_t){(tData->tracking->pos.x >> DECIMAL_BITS) - - (self->gameData->entityManager.viewEntity->pos.x >> DECIMAL_BITS), - (tData->tracking->pos.y >> DECIMAL_BITS) - - (self->gameData->entityManager.viewEntity->pos.y >> DECIMAL_BITS)}, - 2)); - tData->executeOnArrival = &bb_startGarbotnikEggTutorialTalk; - self->gameData->entityManager.viewEntity->updateFunction = &bb_updatePOI; + if (self->gameData->entityManager.viewEntity != NULL) + { + bb_goToData* tData = (bb_goToData*)self->gameData->entityManager.viewEntity->data; + tData->tracking = curEntity; + + tData->midPointSqDist = sqMagVec2d( + divVec2d((vec_t){(tData->tracking->pos.x >> DECIMAL_BITS) + - (self->gameData->entityManager.viewEntity->pos.x >> DECIMAL_BITS), + (tData->tracking->pos.y >> DECIMAL_BITS) + - (self->gameData->entityManager.viewEntity->pos.y >> DECIMAL_BITS)}, + 2)); + tData->executeOnArrival = &bb_startGarbotnikEggTutorialTalk; + self->gameData->entityManager.viewEntity->updateFunction = &bb_updatePOI; + } break; } } @@ -5250,6 +5838,7 @@ void bb_triggerGameOver(bb_entity_t* self) self->gameData->camera.camera.pos = (vec_t){(self->gameData->entityManager.deathDumpster->pos.x >> DECIMAL_BITS), (self->gameData->entityManager.deathDumpster->pos.y >> DECIMAL_BITS)}; + bb_ensureEntitySpace(&self->gameData->entityManager, 1); self->gameData->entityManager.viewEntity = bb_createEntity(&(self->gameData->entityManager), NO_ANIMATION, true, NO_SPRITE_POI, 1, self->gameData->camera.camera.pos.x, self->gameData->camera.camera.pos.y, false, false); @@ -5347,7 +5936,10 @@ void bb_bugDeath(bb_entity_t* self, bb_hitInfo_t* hitInfo) bb_entity_t* hitEffect = bb_createEntity(&(self->gameData->entityManager), ONESHOT_ANIMATION, false, BUMP_ANIM, 6, hitInfo->pos.x >> DECIMAL_BITS, hitInfo->pos.y >> DECIMAL_BITS, true, false); - hitEffect->drawFunction = &bb_drawHitEffect; + if (hitEffect != NULL) + { + hitEffect->drawFunction = &bb_drawHitEffect; + } } if (self->dataType == WALKING_BUG_DATA) @@ -5395,6 +5987,7 @@ void bb_cartDeath(bb_entity_t* self, bb_hitInfo_t* hitInfo) case BB_DONUT: { // spawn a donut as a reward for completing the fight + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_DONUT, 1, (self->pos.x >> DECIMAL_BITS), (self->pos.y >> DECIMAL_BITS), true, false); break; @@ -5402,6 +5995,7 @@ void bb_cartDeath(bb_entity_t* self, bb_hitInfo_t* hitInfo) default: // BB_SWADGE { // spawn a swadge as a reward for completing the fight + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, false, BB_SWADGE, 9, (self->pos.x >> DECIMAL_BITS), (self->pos.y >> DECIMAL_BITS), true, false); break; @@ -5412,7 +6006,10 @@ void bb_cartDeath(bb_entity_t* self, bb_hitInfo_t* hitInfo) bb_entity_t* hitEffect = bb_createEntity(&(self->gameData->entityManager), ONESHOT_ANIMATION, false, BUMP_ANIM, 6, hitInfo->pos.x >> DECIMAL_BITS, hitInfo->pos.y >> DECIMAL_BITS, true, false); - hitEffect->drawFunction = &bb_drawHitEffect; + if (hitEffect != NULL) + { + hitEffect->drawFunction = &bb_drawHitEffect; + } } void bb_spawnHorde(bb_entity_t* self, uint8_t numBugs) @@ -5482,6 +6079,7 @@ void bb_spawnHorde(bb_entity_t* self, uint8_t numBugs) } if (consecutiveGarbage == 2) { + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* jankyBugDig = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_JANKY_BUG_DIG, 1, (spawnPos.x << 5) + 16, (spawnPos.y << 5) + 16, false, false); @@ -5491,6 +6089,7 @@ void bb_spawnHorde(bb_entity_t* self, uint8_t numBugs) return; } + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_entity_t* bug = bb_createEntity(&self->gameData->entityManager, LOOPING_ANIMATION, false, bb_randomInt(8, 13), 1, (spawnPos.x << 5) + 16, (spawnPos.y << 5) + 16, false, false); @@ -5564,56 +6163,72 @@ void bb_spawnHorde(bb_entity_t* self, uint8_t numBugs) } } +void bb_afterEnding(bb_entity_t* self) +{ + self->gameData->exit = true; +} + void bb_trigger501kg(bb_entity_t* self) { - bb_loadSprite("501kg", 1, 1, &self->gameData->entityManager.sprites[BB_501KG]); + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + bb_entity_t* missile = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_501KG, 1, + self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, true, false); + if (missile != NULL) + { + bb_501kgData_t* kgData = ((bb_501kgData_t*)missile->data); - bb_entity_t* missile = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_501KG, 1, - self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, true, false); - bb_501kgData_t* kgData = ((bb_501kgData_t*)missile->data); + kgData->vel = (vec_t){bb_randomInt(-60, 60), 60}; - kgData->vel = (vec_t){bb_randomInt(-60, 60), 60}; + while (missile->pos.y > -2173) + { + missile->pos.x -= (self->gameData->elapsedUs >> 12) * kgData->vel.x; + missile->pos.y -= (self->gameData->elapsedUs >> 12) * kgData->vel.y; + } - while (missile->pos.y > -2173) - { - missile->pos.x -= (self->gameData->elapsedUs >> 12) * kgData->vel.x; - missile->pos.y -= (self->gameData->elapsedUs >> 12) * kgData->vel.y; - } + vecFl_t floatVel = {(float)kgData->vel.x, (float)kgData->vel.y}; - vecFl_t floatVel = {(float)kgData->vel.x, (float)kgData->vel.y}; + kgData->angle = (int16_t)(atan2f(floatVel.y, floatVel.x) * (180.0 / M_PI)); + kgData->angle += 270; + while (kgData->angle < 0) + { + kgData->angle += 360; + } + while (kgData->angle > 359) + { + kgData->angle -= 360; + } - kgData->angle = (int16_t)(atan2f(floatVel.y, floatVel.x) * (180.0 / M_PI)); - kgData->angle += 270; - while (kgData->angle < 0) - { - kgData->angle += 360; + kgData->targetY = self->pos.y; } - while (kgData->angle > 359) - { - kgData->angle -= 360; - } - - kgData->targetY = self->pos.y; } void bb_triggerFaultyWile(bb_entity_t* self) { - bb_entity_t* explosion = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_EXPLOSION, 1, - self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); - bb_explosionData_t* eData = (bb_explosionData_t*)explosion->data; - eData->radius = 60; + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + bb_entity_t* explosion = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_EXPLOSION, 1, + self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); + if (explosion != NULL) + { + bb_explosionData_t* eData = (bb_explosionData_t*)explosion->data; + eData->radius = 60; + } } void bb_triggerDrillBotWile(bb_entity_t* self) { - bb_entity_t* drillBot = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_DRILL_BOT, 1, - self->pos.x >> DECIMAL_BITS, -800, false, false); - bb_drillBotData_t* dbData = (bb_drillBotData_t*)drillBot->data; - dbData->targetY = self->pos.y - 256; + bb_ensureEntitySpace(&self->gameData->entityManager, 1); + bb_entity_t* drillBot = bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_DRILL_BOT, 1, + self->pos.x >> DECIMAL_BITS, -800, false, false); + if (drillBot != NULL) + { + bb_drillBotData_t* dbData = (bb_drillBotData_t*)drillBot->data; + dbData->targetY = self->pos.y - 256; + } } void bb_triggerAtmosphericAtomizerWile(bb_entity_t* self) { + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_ATMOSPHERIC_ATOMIZER, 1, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); if (self->gameData->entityManager.playerEntity != NULL @@ -5626,18 +6241,21 @@ void bb_triggerAtmosphericAtomizerWile(bb_entity_t* self) void bb_triggerAmmoSupplyWile(bb_entity_t* self) { + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_AMMO_SUPPLY, 1, self->pos.x >> DECIMAL_BITS, -800, false, false); } void bb_triggerPacifierWile(bb_entity_t* self) { + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_PACIFIER, 1, self->pos.x >> DECIMAL_BITS, -800, false, false); } void bb_triggerSpaceLaserWile(bb_entity_t* self) { + bb_ensureEntitySpace(&self->gameData->entityManager, 1); bb_createEntity(&self->gameData->entityManager, NO_ANIMATION, true, BB_SPACE_LASER, 1, self->pos.x >> DECIMAL_BITS, self->pos.y >> DECIMAL_BITS, false, false); } @@ -5677,6 +6295,7 @@ void bb_crumbleDirt(bb_gameData_t* gameData, uint8_t gameFramesPerAnimationFrame { vec_t tilePos = {.x = tile_i * TILE_SIZE + HALF_TILE, .y = tile_j * TILE_SIZE + HALF_TILE}; // create a bug + bb_ensureEntitySpace(&gameData->entityManager, 1); bb_entity_t* bug = bb_createEntity(&gameData->entityManager, LOOPING_ANIMATION, false, bb_randomInt(8, 13), 1, tilePos.x, tilePos.y, false, false); @@ -5708,6 +6327,7 @@ void bb_crumbleDirt(bb_gameData_t* gameData, uint8_t gameFramesPerAnimationFrame gameData->tilemap.fgTiles[tile_i][tile_j].embed = NOTHING_EMBED; // create fuel + bb_ensureEntitySpace(&gameData->entityManager, 1); bb_createEntity(&gameData->entityManager, LOOPING_ANIMATION, false, BB_FUEL, 10, tilePos.x, tilePos.y, true, false); break; @@ -5726,7 +6346,8 @@ bb_dialogueData_t* bb_createDialogueData(uint8_t numStrings, const char* firstCh dData->numStrings = numStrings; dData->offsetY = -240; int8_t characterSprite = 0; - if (strcmp(firstCharacter, "Ovo") == 0) + if (strcmp(firstCharacter, "Ovo") == 0 || strcmp(firstCharacter, "???") == 0 + || strcmp(firstCharacter, "Ovo???") == 0 || strcmp(firstCharacter, "DOCTOR OVO") == 0) { characterSprite = bb_randomInt(0, 6); loadWsgInplace("dialogue_next.wsg", &dData->spriteNext, true, bb_decodeSpace, bb_hsd); diff --git a/main/modes/games/bigbug/entity_bigbug.h b/main/modes/games/bigbug/entity_bigbug.h index 9e2c6f5df..2d4a27d55 100644 --- a/main/modes/games/bigbug/entity_bigbug.h +++ b/main/modes/games/bigbug/entity_bigbug.h @@ -52,6 +52,7 @@ typedef enum ATMOSPHERIC_ATOMIZER_DATA, DRILL_BOT_DATA, SPACE_LASER_DATA, + FINAL_BOSS_DATA, } bb_data_type_t; //============================================================================== @@ -262,7 +263,6 @@ typedef struct bb_entity_t* jankyBugDig[6]; // When a bug collides with this, the dirt "digs" toward the car fight arena bb_spriteDef_t reward; // The sprite to spawn when the food cart is destroyed. bb_entity_t* partner; // the other piece of the food cart - bool isCached; // tracking this to only unload sprites when both pieces are cached. int8_t damageEffect; // decrements over time. Render damagePalette color swap if > 0, and if this cart is not the // zero animation frame because the cart background gets not graphical effect. } bb_foodCartData_t; @@ -351,6 +351,17 @@ typedef struct uint8_t highestGarbage; } bb_spaceLaserData_t; +typedef struct +{ + vec_t vel; + int8_t damageEffect; // decrements over time. + int16_t health; + bb_entity_t* bossEggs[10]; + int16_t stateTimer; // increments over time. Doing some attacks when positive. Overflows intentionally. + bool firstDialogeDone; + bool secondDialogeDone; +} bb_finalBossData_t; + typedef void (*bb_updateFunction_t)(bb_entity_t* self); typedef void (*bb_updateFarFunction_t)(bb_entity_t* self); typedef void (*bb_drawFunction_t)(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); @@ -431,6 +442,7 @@ void bb_updateAttachmentArm(bb_entity_t* self); void bb_updateGameOver(bb_entity_t* self); void bb_updateRadarPing(bb_entity_t* self); void bb_updateGrabbyHand(bb_entity_t* self); +void bb_updateFarGrabbyHand(bb_entity_t* self); void bb_updateDoor(bb_entity_t* self); void bb_updateCarActive(bb_entity_t* self); void bb_updateCarOpen(bb_entity_t* self); @@ -446,6 +458,8 @@ void bb_updateDrillBot(bb_entity_t* self); void bb_updateTimedPhysicsObject(bb_entity_t* self); void bb_updatePacifier(bb_entity_t* self); void bb_updateSpaceLaser(bb_entity_t* self); +void bb_updateQuickplay(bb_entity_t* self); +void bb_updateFinalBoss(bb_entity_t* self); void bb_drawGarbotnikFlying(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); void bb_drawHarpoon(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); @@ -480,6 +494,8 @@ void bb_drawPacifier(bb_entityManager_t* entityManager, rectangle_t* camera, bb_ void bb_drawSpaceLaser(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); void bb_drawDeadBug(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); void bb_drawGarbotnikUI(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); +void bb_drawQuickplay(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); +void bb_drawFinalBoss(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); // void bb_drawRect(bb_entityManager_t* entityManager, rectangle_t* camera, bb_entity_t* self); @@ -502,6 +518,8 @@ void bb_onCollisionAmmoSupply(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_ void bb_onCollisionBrickTutorial(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hitInfo); void bb_onCollisionSpaceLaserGarbotnik(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hitInfo); void bb_onCollisionSpaceLaserBug(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hitInfo); +void bb_onCollisionBossEgg(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hitInfo); +void bb_onCollisionBoss(bb_entity_t* self, bb_entity_t* other, bb_hitInfo_t* hitInfo); // callbacks void bb_startGarbotnikIntro(bb_entity_t* self); @@ -523,6 +541,7 @@ void bb_playCarAlarm(bb_entity_t* self); void bb_bugDeath(bb_entity_t* self, bb_hitInfo_t* hitInfo); void bb_cartDeath(bb_entity_t* self, bb_hitInfo_t* hitInfo); void bb_spawnHorde(bb_entity_t* self, uint8_t numBugs); +void bb_afterEnding(bb_entity_t* self); void bb_crumbleDirt(bb_gameData_t* gameData, uint8_t gameFramesPerAnimationFrame, uint8_t tile_i, uint8_t tile_j, bool zeroHealth, bool flagNeighborsForPathfinding); diff --git a/main/modes/games/bigbug/mode_bigbug.c b/main/modes/games/bigbug/mode_bigbug.c index fa9b8261a..ff212710e 100644 --- a/main/modes/games/bigbug/mode_bigbug.c +++ b/main/modes/games/bigbug/mode_bigbug.c @@ -72,7 +72,6 @@ static void bb_DrawScene_Garbotnik_Upgrade(void); static void bb_GameLoop_Garbotnik_Upgrade(int64_t elapsedUs); static void bb_GameLoop_Loadout_Select(int64_t elapsedUs); static void bb_GameLoop(int64_t elapsedUs); -static void bb_Reset(void); static void bb_SetLeds(void); static void bb_setPrimingLeds(uint8_t primingEffect); static void bb_UpdateTileSupport(void); @@ -189,8 +188,10 @@ static void bb_EnterMode(void) = bb_createEntity(&(bigbug->gameData.entityManager), NO_ANIMATION, true, NO_SPRITE_POI, 1, (foreground->pos.x >> DECIMAL_BITS), (foreground->pos.y >> DECIMAL_BITS) - 234, true, false); - bigbug->gameData.camera.camera.pos.x = (bigbug->gameData.entityManager.viewEntity->pos.x >> DECIMAL_BITS) - 140; - bigbug->gameData.camera.camera.pos.y = (bigbug->gameData.entityManager.viewEntity->pos.y >> DECIMAL_BITS) - 120; + bigbug->gameData.camera.camera.pos.x = (bigbug->gameData.entityManager.viewEntity->pos.x >> DECIMAL_BITS) - 140; + bigbug->gameData.camera.camera.pos.y = (bigbug->gameData.entityManager.viewEntity->pos.y >> DECIMAL_BITS) - 120; + bigbug->gameData.camera.camera.width = FIELD_WIDTH; + bigbug->gameData.camera.camera.height = FIELD_HEIGHT; ((bb_goToData*)bigbug->gameData.entityManager.viewEntity->data)->executeOnArrival = &bb_startGarbotnikIntro; @@ -204,8 +205,6 @@ static void bb_EnterMode(void) bb_setupMidi(); soundPlayBgm(&bigbug->gameData.bgm, MIDI_BGM); - - bb_Reset(); } #else @@ -323,8 +322,6 @@ static void bb_EnterModeSkipIntro(void) unloadMidiFile(&bigbug->gameData.bgm); loadMidiFile("BigBugExploration.mid", &bigbug->gameData.bgm, true); globalMidiPlayerPlaySong(&bigbug->gameData.bgm, MIDI_BGM); - - bb_Reset(); } #endif @@ -1643,10 +1640,13 @@ static void bb_GameLoop(int64_t elapsedUs) = bb_createEntity(&bigbug->gameData.entityManager, NO_ANIMATION, true, BB_RADAR_PING, 1, bigbug->gameData.entityManager.playerEntity->pos.x >> DECIMAL_BITS, bigbug->gameData.entityManager.playerEntity->pos.y >> DECIMAL_BITS, true, false); - bigbug->gameData.screen = BIGBUG_GAME_PINGING; - bb_radarPingData_t* rpData = (bb_radarPingData_t*)radarPing->data; - rpData->color = c415; - rpData->executeAfterPing = &bb_openMap; + if (radarPing != NULL) + { + bigbug->gameData.screen = BIGBUG_GAME_PINGING; + bb_radarPingData_t* rpData = (bb_radarPingData_t*)radarPing->data; + rpData->color = c415; + rpData->executeAfterPing = &bb_openMap; + } } } } @@ -1672,15 +1672,6 @@ static void bb_GameLoop(int64_t elapsedUs) // ESP_LOGD(BB_TAG,"FPS: %ld\n", 1000000 / elapsedUs); } -static void bb_Reset(void) -{ - ESP_LOGD(BB_TAG, "The width is: %d\n", FIELD_WIDTH); - ESP_LOGD(BB_TAG, "The height is: %d\n", FIELD_HEIGHT); - - bigbug->gameData.camera.camera.width = FIELD_WIDTH; - bigbug->gameData.camera.camera.height = FIELD_HEIGHT; -} - /** * @brief Set the LEDs */ diff --git a/main/modes/games/bigbug/tilemap_bigbug.c b/main/modes/games/bigbug/tilemap_bigbug.c index 39aca48eb..404b44018 100644 --- a/main/modes/games/bigbug/tilemap_bigbug.c +++ b/main/modes/games/bigbug/tilemap_bigbug.c @@ -388,6 +388,17 @@ void bb_drawTileMap(bb_tilemap_t* tilemap, rectangle_t* camera, vec_t* garbotnik } break; } + case FINAL_BOSS_EMBED: + { + bb_ensureEntitySpace(entityManager, 1); + if (bb_createEntity(entityManager, LOOPING_ANIMATION, false, BB_FINAL_BOSS, 4, + i * TILE_SIZE + HALF_TILE, j * TILE_SIZE + HALF_TILE, false, false) + != NULL) + { + tilemap->fgTiles[i][j].embed = NOTHING_EMBED; + } + break; + } default: { break; diff --git a/main/modes/games/bigbug/typedef_bigbug.h b/main/modes/games/bigbug/typedef_bigbug.h index ce03c2600..fe2b5c78b 100644 --- a/main/modes/games/bigbug/typedef_bigbug.h +++ b/main/modes/games/bigbug/typedef_bigbug.h @@ -67,6 +67,7 @@ typedef enum BB_AMMO_SUPPLY, // A barrel of harpoons BB_PACIFIER, // Calms down the bugs in range. BB_HOTDOG, // icon for use in the radar screen. + BB_FINAL_BOSS, // These things do not have sprites in the sprites array. But we need the enum for the sake of the entity. // Some may have wsgs, but they cleverly load and unload their own WSGs. @@ -83,6 +84,7 @@ typedef enum BB_SPACE_LASER, // A beam of pure damage straight down from the sky. BB_BRICK_TUTORIAL, // A particular spot on levels 3 & 4 that makes garbotnik talk about breaking bricks. BB_GARBOTNIK_UI, // The UI that shows the harpooon count and wile calldowns & cooldowns. + BB_QUICKPLAY_CONFIRM, // A screen that asks if you want to play quickplay. } bb_spriteDef_t; typedef enum @@ -98,13 +100,14 @@ typedef enum BB_FOOD_CART_WITH_DONUT_EMBED, BB_FOOD_CART_WITH_SWADGE_EMBED, BRICK_TUTORIAL_EMBED, + FINAL_BOSS_EMBED, } bb_embeddable_t; typedef enum { ONESHOT_ANIMATION, LOOPING_ANIMATION, - NO_ANIMATION + NO_ANIMATION, } bb_animationType_t; void bb_trigger501kg(bb_entity_t* self); diff --git a/main/modes/games/bigbug/worldGen_bigbug.c b/main/modes/games/bigbug/worldGen_bigbug.c index 80dfea5e7..0bd47afdd 100644 --- a/main/modes/games/bigbug/worldGen_bigbug.c +++ b/main/modes/games/bigbug/worldGen_bigbug.c @@ -35,13 +35,14 @@ void bb_generateWorld(bb_tilemap_t* tilemap, int8_t* oldBoosterYs) { for (int j = 0; j < TILE_FIELD_HEIGHT; j++) { - tilemap->fgTiles[i][j].pos = i | (j << 7) | (1 << 15); - - tilemap->mgTiles[i][j].pos = i | (j << 7); // z is implicitly zero - - tilemap->fgTiles[i][j].embed = NOTHING_EMBED; - + tilemap->fgTiles[i][j].pos = i | (j << 7) | (1 << 15); + tilemap->mgTiles[i][j].pos = i | (j << 7); // z is implicitly zero + tilemap->fgTiles[i][j].embed = NOTHING_EMBED; tilemap->fgTiles[i][j].entity = NULL; + tilemap->fgTiles[i][j].gCost = 0; + tilemap->mgTiles[i][j].gCost = 0; + tilemap->fgTiles[i][j].hCost = 0; + tilemap->mgTiles[i][j].hCost = 0; uint32_t rgbCol = paletteToRGB(levelWsg.px[(j * levelWsg.w) + i]); @@ -115,7 +116,7 @@ void bb_generateWorld(bb_tilemap_t* tilemap, int8_t* oldBoosterYs) } } - // green value used for midground tiles and doors + // green value used for midground tiles and doors and final boss switch ((rgbCol >> 8) & 255) { case 51: @@ -131,6 +132,12 @@ void bb_generateWorld(bb_tilemap_t* tilemap, int8_t* oldBoosterYs) tilemap->fgTiles[i][j].embed = DOOR_EMBED; break; } + case 153: + { + tilemap->mgTiles[i][j].health = 0; + tilemap->fgTiles[i][j].embed = FINAL_BOSS_EMBED; + break; + } default: { tilemap->mgTiles[i][j].health = 0;