Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Ganonhunt goal #1033

Merged
merged 7 commits into from
Feb 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion app/Filler/RandomAssumed.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ protected function fillItemsInLocations($fill_items, $locations, $base_assumed_i
));

if ($remaining_fill_items->count() > $locations->getEmptyLocations()->count()) {
throw new \Exception("Trying to fill more items than available locations.");
throw new \Exception(
"Trying to fill more items than available locations." .
$remaining_fill_items->count() . ' ' . $locations->getEmptyLocations()->count()
);
}

$worlds = new WorldCollection($this->worlds);
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/CustomizerController.php
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ protected function prepSeed(Request $request, bool $save = false)
// some simple validation
// @TODO: move to validator type classes later
if (
$request->input('goal', 'ganon') === 'triforce-hunt'
in_array($request->input('goal', 'ganon'), ['triforce-hunt', 'ganonhunt'])
&& ($custom_data['item.Goal.Required'] ?? 0)
> ($custom_data['item.count.TriforcePiece'] ?? 0) + ($placed_item_count['TriforcePiece:1'] ?? 0)
) {
Expand Down
15 changes: 11 additions & 4 deletions app/Randomizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public function prepareWorld(World $world): void
case 'ganon':
case 'fast_ganon':
case 'dungeons':
case 'ganonhunt':
$world->getLocation("Ganon")->setItem(Item::get('Triforce', $world));
break;
}
Expand Down Expand Up @@ -982,12 +983,12 @@ public function setTexts(World $world)

switch ($world->config('goal')) {
case 'ganon':
$ganon_crystals_singular = 'To beat Ganon you must collect %d crystal and defeat his minion at the top of his tower.';
$ganon_crystals_plural = 'To beat Ganon you must collect %d crystals and defeat his minion at the top of his tower.';
$ganon_crystals_singular = 'To beat Ganon you must collect %d Crystal and defeat his minion at the top of his tower.';
$ganon_crystals_plural = 'To beat Ganon you must collect %d Crystals and defeat his minion at the top of his tower.';
break;
default:
$ganon_crystals_singular = 'You need %d crystal to beat Ganon.';
$ganon_crystals_plural = 'You need %d crystals to beat Ganon.';
$ganon_crystals_singular = 'You need %d Crystal to beat Ganon.';
$ganon_crystals_plural = 'You need %d Crystals to beat Ganon.';
break;
}

Expand All @@ -1005,6 +1006,12 @@ public function setTexts(World $world)
$world->setText('sign_ganon', $ganon_require);
$world->setText('ganon_fall_in_alt', "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!");

break;
case 'ganonhunt':
$ganon_require = sprintf('To beat Ganon you must collect %d Triforce Pieces.', $world->config('item.Goal.Required'));
$world->setText('sign_ganon', $ganon_require);
$world->setText('ganon_fall_in_alt', "You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!");

break;
case 'pedestal':
$world->setText('ganon_fall_in_alt', "You cannot\nkill me. You\nshould go for\nyour real goal\nIt's on the\npedestal.\n\nYou dingus!\n");
Expand Down
7 changes: 7 additions & 0 deletions app/Region/Standard/DarkWorld/NorthEast.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,13 @@ public function initalize()
};

$this->prize_location->setRequirements(function ($locations, $items) {
if (
$this->world->config('goal') == 'ganonhunt'
&& (!$items->has('TriforcePiece', $this->world->config('item.Goal.Required')))
) {
return false;
}

if (
$this->world->config('goal') == 'dungeons'
&& (!$items->has('PendantOfCourage')
Expand Down
19 changes: 18 additions & 1 deletion app/Rom.php
Original file line number Diff line number Diff line change
Expand Up @@ -581,9 +581,26 @@ public function setGanonInvincible(string $setting = 'no'): self
case 'yes':
$byte = pack('C*', 0x01);
break;
case 'custom':
case 'crystals_only':
$byte = pack('C', 0x04);
break;
case 'ganonhunt':
$byte = pack('C', 0x05);
break;
case 'lightspeed':
// light world only, pull ped, kill aga 1
$byte = pack('C', 0x06);
break;
case 'crystals_bosses':
$byte = pack('C', 0x07);
break;
case 'bosses_only':
$byte = pack('C', 0x08);
break;
case 'dungeons_no_agahnim':
// all dungeons, aga 1 not required
$byte = pack('C', 0x09);
break;
case 'no':
default:
$byte = pack('C*', 0x00);
Expand Down
20 changes: 14 additions & 6 deletions app/World.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ public function __construct(int $id = 0, array $config = [])
$collected_items->setChecksForWorld($this->id);
return $collected_items->has('Triforce')
|| ($this->regions['North East Light World']->canEnter($this->locations, $collected_items) &&
$collected_items->has('TriforcePiece', $this->config('item.Goal.Required')));
($this->config('goal', 'ganon') == 'triforce-hunt' && $collected_items->has('TriforcePiece', $this->config('item.Goal.Required'))));
};

// Handle configuration options that map to switches.
Expand Down Expand Up @@ -375,7 +375,8 @@ public function copy()
*
* @return array
*/
public function getConfig(): array {
public function getConfig(): array
{
$config_copy = $this->config;
return $config_copy;
}
Expand Down Expand Up @@ -1180,12 +1181,16 @@ public function writeToRom(Rom $rom, bool $save = false): Rom
case 'dungeons':
$rom->setGanonInvincible('dungeons');
break;
case 'ganonhunt':
$rom->initial_sram->preOpenPyramid();
$rom->setGanonInvincible('triforce_pieces');
break;
case 'fast_ganon':
$rom->initial_sram->preOpenPyramid();

// no break
default:
$rom->setGanonInvincible('custom');
$rom->setGanonInvincible('crystals_only');
}

if ($this->config('rom.mapOnPickup', false)) {
Expand Down Expand Up @@ -1325,7 +1330,8 @@ public function writeToRom(Rom $rom, bool $save = false): Rom
*
* @return void
*/
public function setEscapeFills(Rom $rom) {
public function setEscapeFills(Rom $rom)
{
$uncle_items = new ItemCollection;
$uncle_items->setChecksForWorld($this->id);
$uncle_items = $uncle_items->addItem($this->getLocation("Link's Uncle")->getItem());
Expand All @@ -1343,9 +1349,11 @@ public function setEscapeFills(Rom $rom) {
$rom->setUncleSpawnRefills(0, 0, 0);
$rom->setZeldaSpawnRefills(0, 0, 0);
$rom->setMantleSpawnRefills(0, 0, 0);
} elseif ($uncle_items->has('FireRod')
} elseif (
$uncle_items->has('FireRod')
|| $uncle_items->has('CaneOfSomaria')
|| ($uncle_items->has('CaneOfByrna') && $this->config('enemizer.enemyHealth', 'default') == 'default')) {
|| ($uncle_items->has('CaneOfByrna') && $this->config('enemizer.enemyHealth', 'default') == 'default')
) {
$rom->setEscapeFills(0b00000100);
$rom->setUncleSpawnRefills(
$this->config('rom.EscapeRefills.Uncle.Magic', 0x80),
Expand Down
13 changes: 13 additions & 0 deletions config/alttp.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@
],
],
],
'ganonhunt' => [
'item' => [
'count' => [
'TriforcePiece' => 60,
],
'Goal' => [
'Required' => 40,
'Icon' => 'triforce',
],
],
],
],
'randomizer' => [
'item' => [
Expand Down Expand Up @@ -206,6 +217,7 @@
'dungeons' => 'All Dungeons',
'pedestal' => 'Master Sword Pedestal',
'triforce-hunt' => 'Triforce Pieces',
'ganonhunt' => 'Ganonhunt',
],
'tower_open' => [
'0' => 'none',
Expand Down Expand Up @@ -326,6 +338,7 @@
'dungeons' => 10,
'pedestal' => 10,
'triforce-hunt' => 10,
'ganonhunt' => 0,
],
'tower_open' => [
'0' => 5,
Expand Down
9 changes: 9 additions & 0 deletions resources/js/store/modules/randomizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ export default {
if (state.goal.value === "dungeons") {
commit("setGanonOpen", "7");
}
if (state.goal.value === "ganonhunt") {
commit("setEntranceShuffle", "none");
}
},
setGanonOpen({ commit, state }, value) {
commit("setGanonOpen", value);
Expand Down Expand Up @@ -216,6 +219,12 @@ export default {
if (["full", "standard"].indexOf(state.dungeon_items.value) === -1) {
commit("setDungeonItems", "standard");
}

// ER doesn't currently support Ganonhunt
if (state.goal.value === "ganonhunt") {
commit("setGoal", "fast_ganon");
}

if (state.item_pool.value === "crowd_control") {
commit("setItemPool", "expert");
}
Expand Down
6 changes: 6 additions & 0 deletions resources/lang/de/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@
'Karten, Kompasse, kleine Schlüssel und große Schlüssel sind nicht mehr an ihren Palast gebunden (könnten trotzdem noch drin landen).',
],
],
[
'header' => __('randomizer.goal.options.ganonhunt'),
'content' => [
'The Triforce has been shattered into 60 pieces and scattered throughout Hyrule! You must first collect 40 of the 60 pieces, and only then will Ganon be vulnerable to your attacks! Like Fast Ganon, the hole leading to Ganon has been made permanently accessible. This goal is not available if entrances are randomized.',
],
],
],
],
'accessibility' => [
Expand Down
1 change: 1 addition & 0 deletions resources/lang/de/randomizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'dungeons' => 'Alle Dungeons',
'pedestal' => 'Master-Schwert Sockel',
'triforce-hunt' => 'Triforce-Splitter',
'ganonhunt' => 'Ganonhunt',
],
],
'tower_open' => [
Expand Down
6 changes: 6 additions & 0 deletions resources/lang/en/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@
'The Triforce has been shattered into 30 pieces and scattered throughout Hyrule! You must collect 20 of the 30 pieces and take them to Murahdahla to receive the Triforce. Who is Murahdahla I hear you ask? Why, he is obviously the younger brother of Sahasrahla and Aginah! Back from his vacation in Lorule you can find him hanging out around Hyrule Castle courtyard.',
],
],
[
'header' => __('randomizer.goal.options.ganonhunt'),
'content' => [
'The Triforce has been shattered into 60 pieces and scattered throughout Hyrule! You must first collect 40 of the 60 pieces, and only then will Ganon be vulnerable to your attacks! Like Fast Ganon, the hole leading to Ganon has been made permanently accessible. This goal is not available if entrances are randomized.',
],
],
],
],
'tower_open' => [
Expand Down
1 change: 1 addition & 0 deletions resources/lang/en/randomizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'dungeons' => 'All Dungeons',
'pedestal' => 'Master Sword Pedestal',
'triforce-hunt' => 'Triforce Pieces',
'ganonhunt' => 'Ganonhunt',
],
],
'tower_open' => [
Expand Down
6 changes: 6 additions & 0 deletions resources/lang/es/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@
'¡La Trifuerza se ha roto en 30 fragmentos esparcidos por todo Hyrule! Tienes que encontrar 20 de las 30 piezas y llevárselas a Murahdahla para recibir la Trifuerza. ¿Que preguntas quién es Murahdahala? ¡Pues obviamente es el hermano menor de Sahasrahla y Aginah! Ha vuelto de sus vacaciones en Lorule y puedes encontrarle pasando el rato en el patio del Castillo de Hyrule.',
],
],
[
'header' => __('randomizer.goal.options.ganonhunt'),
'content' => [
'The Triforce has been shattered into 60 pieces and scattered throughout Hyrule! You must first collect 40 of the 60 pieces, and only then will Ganon be vulnerable to your attacks! Like Fast Ganon, the hole leading to Ganon has been made permanently accessible. This goal is not available if entrances are randomized.',
],
],
],
],
'tower_open' => [
Expand Down
1 change: 1 addition & 0 deletions resources/lang/es/randomizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'dungeons' => 'Todas las mazmorras',
'pedestal' => 'Pedestal de la Espada Maestra',
'triforce-hunt' => 'Piezas de la Trifuerza',
'ganonhunt' => 'Ganonhunt',
],
],
'tower_open' => [
Expand Down
6 changes: 6 additions & 0 deletions resources/lang/fr/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,12 @@
'La Triforce a été brisée en 30 morceaux, éparpillés à travers Hyrule! Vous devez retrouver 20 des 30 pièces, et les emmener à Murahdhala pour recevoir la Triforce. Qui est ce Murahdahla, me direz-vous? Mais enfin, il est évidemment le petit frère de Sahasrahla et Aginah! De retour de ses vacances à Lorule, vous le trouverez dans la cour du Château d’Hyrule.',
],
],
[
'header' => __('randomizer.goal.options.ganonhunt'),
'content' => [
'The Triforce has been shattered into 60 pieces and scattered throughout Hyrule! You must first collect 40 of the 60 pieces, and only then will Ganon be vulnerable to your attacks! Like Fast Ganon, the hole leading to Ganon has been made permanently accessible. This goal is not available if entrances are randomized.',
],
],
],
],
'tower_open' => [
Expand Down
1 change: 1 addition & 0 deletions resources/lang/fr/randomizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'dungeons' => 'Tous les Donjons',
'pedestal' => 'Piédestal de la Master Sword',
'triforce-hunt' => 'Morceaux de Triforce ',
'ganonhunt' => 'Ganonhunt',
],
],
'tower_open' => [
Expand Down
1 change: 1 addition & 0 deletions resources/views/updates.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<li>New Features</li>
<ul>
<li>The randomizer now uses a new font that is more compact and contains lowercase characters! Thank you to total, kan, kara, Aerinon, and cassidymoen for the work on this feature.</li>
<li>A new goal called "Ganonhunt" has been added! This goal is similar to the "Triforce Pieces" goal, but requires the player to defeat Ganon after collecting the triforce pieces instead of turning them into Murahdahla.</li>
<li>Players may now include lowercase letters and numbers in their file name.</li>
<li>Winners of the 2022 Main Tournament, and 2022 No Logic tournaments have been added to their respective in-game signs.</li>
<li>Hybrid Major Glitches is now a fully supported logic option.</li>
Expand Down
13 changes: 10 additions & 3 deletions tests/RomTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -679,9 +679,16 @@ public function testSetGanonInvincibleYes()
$this->assertEquals(0x01, $this->rom->read(0x18003E));
}

public function testSetGanonInvincibleCustom()
public function testSetGanonInvincibleTriforcePieces()
{
$this->rom->setGanonInvincible('custom');
$this->rom->setGanonInvincible('triforce_pieces');

$this->assertEquals(0x05, $this->rom->read(0x18003E));
}

public function testSetGanonInvincibleCrystalsOnly()
{
$this->rom->setGanonInvincible('crystals_only');

$this->assertEquals(0x04, $this->rom->read(0x18003E));
}
Expand Down Expand Up @@ -762,7 +769,7 @@ private function assertHeartColorSetting($expectedColor)
$this->assertEquals($expectedByte, $this->rom->read(0x6FA2C));
$this->assertEquals($expectedByte, $this->rom->read(0x6FA2E));
$this->assertEquals($expectedByte, $this->rom->read(0x6FA30));

$this->assertEquals($expectedFileByte, $this->rom->read(0x65561));
}

Expand Down