summaryrefslogtreecommitdiff
path: root/mm/2s2h/Rando/Logic/NearlyNoLogic.cpp
blob: ed22de007d165248c678c7bb3ead8f6c5ad39028 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include "Logic.h"

extern "C" {
#include "variables.h"
#include "ShipUtils.h"
}

namespace Rando {

namespace Logic {

void ApplyNearlyNoLogicToSaveContext(std::vector<RandoCheckId>& checkPool, std::vector<RandoItemId>& itemPool) {
    PreplaceConfinedItems(checkPool, itemPool);

    for (size_t i = 0; i < itemPool.size(); i++) {
        std::swap(itemPool[i], itemPool[Ship_Random(0, itemPool.size())]);
    }

    std::map<RandoItemId, RandoCheckId> importantItems;
    std::vector<RandoCheckId> safeChecks;

    std::map<RandoItemId, std::vector<SceneId>> itemToSceneBlacklist = {
        { RI_MASK_DEKU,
          { SCENE_MITURIN, SCENE_MITURIN_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK,
            SCENE_SOUGEN, SCENE_LAST_BS } },
        { RI_SONG_SONATA,
          { SCENE_MITURIN, SCENE_MITURIN_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK,
            SCENE_SOUGEN, SCENE_LAST_BS } },
        { RI_MASK_ZORA,
          { SCENE_SEA, SCENE_SEA_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN,
            SCENE_LAST_BS } },
        { RI_SONG_NOVA,
          { SCENE_SEA, SCENE_SEA_BS, SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN,
            SCENE_LAST_BS } },
        { RI_REMAINS_ODOLWA,
          { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } },
        { RI_REMAINS_GOHT,
          { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } },
        { RI_REMAINS_GYORG,
          { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } },
        { RI_REMAINS_TWINMOLD,
          { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } },
        { RI_SONG_OATH,
          { SCENE_LAST_DEKU, SCENE_LAST_GORON, SCENE_LAST_ZORA, SCENE_LAST_LINK, SCENE_SOUGEN, SCENE_LAST_BS } },
    };

    std::map<RandoItemId, RandoCheckType> itemToCheckTypeBlacklist = { { RI_SONG_EPONA, RCTYPE_COW } };

    auto IsSafeScene = [&](SceneId sceneId) {
        return sceneId != SCENE_MITURIN &&    // Woodfall Temple
               sceneId != SCENE_MITURIN_BS && // Woodfall Temple Boss
               sceneId != SCENE_SEA &&        // Great Bay Temple
               sceneId != SCENE_SEA_BS &&     // Great Bay Temple Boss
               sceneId != SCENE_LAST_DEKU &&  // Moon Deku
               sceneId != SCENE_LAST_GORON && // Moon Goron
               sceneId != SCENE_LAST_ZORA &&  // Moon Goron
               sceneId != SCENE_LAST_LINK &&  // Moon Human
               sceneId != SCENE_SOUGEN &&     // Moon
               sceneId != SCENE_LAST_BS;      // Moon Boss
    };

    auto IsSafeCheckType = [&](RandoCheckType randoCheckType) { return randoCheckType != RCTYPE_COW; };

    for (auto& randoCheckId : checkPool) {
        if (randoCheckId == RC_UNKNOWN) {
            continue;
        }

        RandoItemId randoItemId = itemPool.back();
        itemPool.pop_back();

        RANDO_SAVE_CHECKS[randoCheckId].shuffled = true;
        RANDO_SAVE_CHECKS[randoCheckId].randoItemId = randoItemId;

        auto randoStaticCheck = Rando::StaticData::Checks[randoCheckId];

        if (itemToSceneBlacklist.find(randoItemId) != itemToSceneBlacklist.end() ||
            itemToCheckTypeBlacklist.find(randoItemId) != itemToCheckTypeBlacklist.end()) {
            importantItems[randoItemId] = randoCheckId;
        } else if (IsSafeScene(randoStaticCheck.sceneId) && IsSafeCheckType(randoStaticCheck.randoCheckType)) {
            safeChecks.push_back(randoCheckId);
        }
    }

    auto PerformBlacklistReplacement = [&](RandoItemId randoItemId) {
        auto otherCheckIndex = Ship_Random(0, safeChecks.size());
        RANDO_SAVE_CHECKS[importantItems[randoItemId]].randoItemId =
            RANDO_SAVE_CHECKS[safeChecks[otherCheckIndex]].randoItemId;
        RANDO_SAVE_CHECKS[safeChecks[otherCheckIndex]].randoItemId = randoItemId;
        safeChecks.erase(safeChecks.begin() + otherCheckIndex);
    };

    for (auto& [randoItemId, blacklistedScenes] : itemToSceneBlacklist) {
        if (importantItems.find(randoItemId) != importantItems.end()) {
            auto randoStaticCheck = Rando::StaticData::Checks[importantItems.at(randoItemId)];
            const auto& blacklist = itemToSceneBlacklist.at(randoItemId);
            // This item is blacklisted from this scene, so replace it
            if (std::find(blacklist.begin(), blacklist.end(), randoStaticCheck.sceneId) != blacklist.end()) {
                PerformBlacklistReplacement(randoItemId);
            }
        }
    }

    for (auto& [randoItemId, blackListedChecks] : itemToCheckTypeBlacklist) {
        if (importantItems.find(randoItemId) != importantItems.end()) {
            auto randoStaticCheck = Rando::StaticData::Checks[importantItems.at(randoItemId)];
            const auto& blacklistedRandoCheckType = itemToCheckTypeBlacklist.at(randoItemId);
            // This item is blacklisted from this check type, so replace it
            if (randoStaticCheck.randoCheckType == blacklistedRandoCheckType) {
                PerformBlacklistReplacement(randoItemId);
            }
        }
    }
}

} // namespace Logic

} // namespace Rando