Trap Improvements (#6302)

* Move some trap stuff to `Traps.cpp`

* Split `GetTrapTrickModel` into a function

* Split `ShouldJunkItemBeTrap` into a function

* Move `BuildIceTrapMessage` to `Traps.cpp`

* Allow ice trap messages to have the item name. Only for english for now due to article issues.
This commit is contained in:
Pepe20129
2026-02-28 16:53:05 +01:00
committed by GitHub
parent a393f48c7c
commit 8dd03ef097
9 changed files with 1845 additions and 1760 deletions

View File

@@ -240,6 +240,8 @@ typedef enum {
#ifdef __cplusplus
#include <string>
typedef struct {
uint16_t giid;
ItemID iid;

View File

@@ -7,6 +7,7 @@
#include "pool_functions.hpp"
#include "random.hpp"
#include "spoiler_log.hpp"
#include "soh/Enhancements/randomizer/Traps.h"
#include "z64item.h"
#include <spdlog/spdlog.h>
@@ -68,11 +69,10 @@ void AddFixedItemToPool(RandomizerGet item, int count = 1, bool iceTrapModel = t
}
RandomizerGet GetJunkItem() {
auto ctx = Rando::Context::GetInstance();
if (ctx->GetOption(RSK_ICE_TRAP_PERCENT).IsNot(0) &&
(ctx->GetOption(RSK_ICE_TRAP_PERCENT).Is(100) || Random(0, 100) < ctx->GetOption(RSK_ICE_TRAP_PERCENT).Get())) {
if (Rando::Traps::ShouldJunkItemBeTrap()) {
return RG_ICE_TRAP;
}
return RandomElement(JunkPoolItems);
}
@@ -550,7 +550,7 @@ void GenerateItemPool() {
}
if (ctx->GetOption(RSK_SHUFFLE_BEAN_SOULS)) {
ctx->possibleIceTrapModels.insert(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL); // ice traps reroll this into a random bean soul
ctx->possibleIceTrapModels.insert(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL); // ice traps reroll this into a random bean soul in Rando::Traps::GetTrapTrickModel
AddItemToPool(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, 2, 1, 1, 1, false);
AddItemToPool(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, 2, 1, 1, 1, false);
AddItemToPool(RG_DESERT_COLOSSUS_BEAN_SOUL, 2, 1, 1, 1, false);
@@ -635,7 +635,7 @@ void GenerateItemPool() {
// Keys
if (ctx->GetOption(RSK_LOCK_OVERWORLD_DOORS)) {
// only 1 is added to the ice trap pool, to avoid the pool being filled with them.
// a random one is chosen in CreateItemOverrides
// a random one is chosen in Rando::Traps::GetTrapTrickModel
AddItemToPool(RG_GUARD_HOUSE_KEY, 2, 1, 1, 1);
AddItemToPool(RG_MARKET_BAZAAR_KEY, 2, 1, 1, 1, false);
AddItemToPool(RG_MARKET_POTION_SHOP_KEY, 2, 1, 1, 1, false);
@@ -819,7 +819,7 @@ void GenerateItemPool() {
bottleCount--;
}
ctx->possibleIceTrapModels.insert(RG_EMPTY_BOTTLE); // ice traps reroll this into a random normal bottle
ctx->possibleIceTrapModels.insert(RG_EMPTY_BOTTLE); // ice traps reroll this into a random normal bottle in Rando::Traps::GetTrapTrickModel
for (uint8_t i = 0; i < bottleCount; i++) {
AddFixedItemToPool(RandomElement(Rando::StaticData::normalBottles), 1, false);
}

File diff suppressed because it is too large Load Diff

View File

@@ -27,4 +27,3 @@ extern std::vector<RandomizerGet> GetMinVanillaShopItems(int total_replaced);
extern uint16_t GetRandomPrice(Rando::Location* loc, PriceSettingsStruct priceSettings);
extern uint16_t GetCheapBalancedPrice();
extern int GetShopsanityReplaceAmount();
extern Text GetIceTrapName(uint16_t id);

View File

@@ -1,4 +1,3 @@
// RANDOTODO: Consider putting Ice Traps in their own file.
/**
* This file handles custom messages relating to Items,
* such as Get Item messages for non-vanilla items,
@@ -8,6 +7,7 @@
#include <soh/OTRGlobals.h>
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/Enhancements/custom-message/CustomMessageTypes.h"
#include "soh/Enhancements/randomizer/Traps.h"
#include "soh/ShipInit.hpp"
#include "z64item.h"
#include <soh/ResourceManagerHelpers.h>
@@ -18,316 +18,6 @@ extern "C" {
extern PlayState* gPlayState;
}
static const char* const englishIceTrapMessages[] = {
"You are a #FOOL#!",
"You are a #FOWL#!",
"#FOOL#!",
"You just got #PUNKED#!",
"Stay #frosty#, @.",
"Take a #chill pill#, @.",
"#Winter# is coming.",
"#ICE# to see you, @.",
"Feeling a little %rhot%w under the collar? #Let's fix that#.",
"It's a #cold day# in the Evil Realm.",
"Getting #cold feet#?",
"Say hello to the #Zoras# for me!",
"Can you keep a #cool head#?",
"Ganondorf used #Ice Trap#!&It's super effective!",
"Allow me to break the #ice#!",
"#Cold pun#.",
"The #Titanic# would be scared of you, @.",
"Oh no!",
"Uh oh!",
"What killed the dinosaurs?&The #ICE# age!",
"Knock knock. Who's there? Ice. Ice who? Ice see that you're a #FOOL#.",
"Never gonna #give you up#. Never gonna #let you down#. Never gonna run around and #desert you#.",
"Thank you #@#! But your item is in another castle!",
"#FREEZE#! Don't move!",
"Wouldn't it be #ice# if we were colder?",
"Greetings from #Snowhead#! Wish you were here.",
"Too #cool# for you?",
"#Ice#, #ice#, baby...",
"Time to break the #ice#.",
"We wish that you would read this... We wish that you would read this... But we set our bar low.",
"#Freeze# and put your hands in the air!",
"#Ice# to meet you!",
"Do you want to #freeze# a snowman?",
"Isn't there a #mansion# around here?",
"Now you know how #King Zora# feels.",
"May the #Frost# be with you.",
"Carpe diem. #Freeze# the day.",
"There #snow# place like home.",
"That'll do, #ice#. That'll do.",
"All that is #cold# does not glitter. Not all those who wander are #frost#.",
"I Used To Be An Adventurer Like You. Then I Took An #Icetrap# To The Knee.",
"Would you like #ice# with that?",
"You have obtained the #Ice# Medallion!",
"Quick, do a #Zora# impression!",
"One item #on the rocks#!", // would be better if it could display the name of the item
"How much does a polar bear weigh?&Enough to break the #ice#.",
"You got Din's #Ice#!",
"You got Nayru's #Cold#!",
"You got Farore's #Freeze#!",
"KEKW",
"You just got #ICE TRAPPED#! Tag your friends to totally #ICE TRAP# them!",
"Are you okay, @? You're being #cold# today.",
"In a moment, your game might experience some #freezing#.",
"Breeze? Trees? Squeeze? No, it's a #freeze#!",
"After collecting this item, @ was assaulted in #cold# blood.",
"Only #chill# vibes around here!",
"Here's a #cool# gift for you!",
"Aha! You THOUGHT.",
"Stay hydrated and brush your teeth!",
"Isn't it too hot here? Let's turn the #AC# on.",
"One serving of #cold# @, coming right up!",
"Is it #cold# in here or is that just me?",
"Yahaha! You found me!",
"You'd make a great #ice#-tronaut!",
"That's just the tip of the #iceberg#!",
"It's the triforce!&No, just kidding, it's an #ice trap#.",
"WINNER!",
"LOSER!",
"Greetings from #Cold Miser#!",
"Pardon me while I turn up the #AC#.",
"If you can't stand the #cold#, get out of the #freezer#.",
"Oh, goodie! #Frozen @# for the main course!",
"You have #freeze# power!",
"You obtained the #Ice Beam#! No wait, wrong game.",
"Here's to another lousy millenium!",
"You've activated my #trap card#!",
"I love #refrigerators#!",
"You expected an item,&BUT IT WAS I, AN #ICE TRAP#!",
"It's dangerous to go alone! Take #this#!",
"soh.exe has #stopped responding#.",
"Enough! My #Ice Trap# thaws in the morning!",
"Nobody expects the span-#ice# inquisition!",
"This is one #cool# item!",
"Say hello to my #little friend#!",
"We made you an offer you #can't refuse#.",
"Hyrule? More like #Hycool#!",
"Ice puns are #snow# problem!",
"This #ice# is #snow# joke!",
"There's no business like #snow# business!",
"No, dude.",
"N#ice# trap ya got here!",
"Quick do your best impression of #Zoras Domain#!",
"Ganon used #ice beam#, it's super effective!",
"I was #frozen# today.",
"You're not in a #hurry#, right?",
"It's a #trap#!",
"At least it's not a VC crash and only Link is #frozen#!",
"Oh no! #BRAIN FREEZE#!",
"Looks like your game #froze#! Nope just you!",
"PK #FREEZE#!",
"May I interest you in some #iced# Tea?",
"Time for some Netflix and #chill#.",
"I know, I know... #FREEZE#!",
"#Ice# of you to drop by!",
"STOP!&You violated the #Thaw#!",
"I wanted to give you a treasure, but it looks like you got #cold feet#.",
"You told me you wanted to deliver #just ice# to Ganondorf!",
"You got the triforce!&This ancient artifact of divine power can grant any- wait, no, sorry, it's just an ice "
"trap. My bad.",
"Time to #cool off#!",
"The #Ice Cavern# sends its regards.",
"Loading item, please #wait#...",
"Mash A+B to not #die#.",
"Sorry, your item is in another location.", // would be better if it could have the name of the item
"You only wish this was %gGreg%w.",
"Do you want to drink a hot chocolate?",
"The #cold# never bothered me anyway.",
"Hope you're too school for #cool#!",
"Be thankful this isn't #absolute zero#.",
"Did you know the F in ZFG stands for #Freeze#?",
"You got #Ice Age (2002)#!",
"Now you can cast a #spell# you don't know.",
"How's about a hero #on the rocks#?",
"Ain't no tunic for #this#!",
"I knew you were #part metroid#!",
"That's just the #icing on the cake#!",
"You're so #cool#, @!",
"You found #disappointment#!",
"You got #FOOLED#!",
"Start Mashing.",
"This item will #self-destruct# in 5 seconds...",
"Remember, there may be some momentary #discomfort#.",
"In a perfect world #ice traps# like me would not exist, but this is not a perfect world.",
"Gee, it sure is #cold# around here.",
"You tested the item with your #ice detector#, it beeped.", // would be better if it could have the name of the item
"You have found the way of the zero. The #sub-zero#.",
"Mweep... mweep... mweep...",
"Scum, #freezebag#! I mean #freeze#, scumbag!",
"Is it #chilly# in here or is it just #you#?",
"#Proceed#.",
"WHAT'S SHE GONNA DO, MAKE ME AN #[Ice Cream]#!?",
"You've met with a #terrible fate#, haven't you?",
"So I heard you like the Shining, here's how it #ends#.",
"Minor routing mistake. #I win#.",
"Hold this #L#, @.",
"#SKILL ISSUE#.",
"All your heat are belong to us.",
"Wait a second, don't you already have #this item#?",
"#Freeze#! We have you surrounded!",
"Error 404 - Item not #found#.",
"Hydration break! Hey, who #froze# my water?",
"Oops, wrong #item model#.",
"Whoops! You have to put the item #in your inventory#.",
"You dropped the item, shattering it into #shards of ice#!", // would be better if it could have the name of the
// item
"Is this... golden age Simpsons?&BECAUSE I'M ABOUT TO #CHOKE A CHILD#.",
"You are the weakest @, #goodbye#!",
"Ugh... Why did we even randomize #this item#?",
"The #Frost Moon# is rising...",
"According to all known laws of physics and biology, there is no way that @ should be able to survive #getting "
"fully encased in ice#. The cells in @'s body would all die by the time they #unthaw#. Of course, this is a video "
"game, so @ survives anyway... #Probably#.",
"Okay, so stop me if you've heard this one - a gamer and a bottle of #liquid nitrogen# walk into a milk bar...",
"Lástima, es una #trampa de hielo#...&&Nobody expects the Spanish #ice trap#!",
"Gee, it sure is #BURR#ing around here.",
"Navi? Oh! I thought she was called #Névé#!",
"It's fine, @ knew this was a #trap#, they're just using it to take damage intentionally to manipulate RNG.",
"Unfortunately, the item has #stopped#.", // would be better if it could have the name of the item
"This item is #not available# in your country.", // would be better if it could have the name of the item
"#Ice# try. #;)#",
"D'oh, I #missed#!",
"Where is my #super suit#?",
"#Titanic's revenge#.",
};
static const char* const germanIceTrapMessages[] = {
"Du bist ein #DUMMKOPF#!",
"Du bist eine #Frostbeule#!",
"#DUMMKOPF#!",
"Du wurdest #eiskalt# erwischt!",
"Es läuft Dir #eiskalt# den Rücken hinunter, @.",
"Bleib #cool#, @.",
"Der #Winter# naht.",
"Alles #cool# im Pool?",
"#Abkühlung gefällig#?",
"Es ist ein %kalter%w Tag im Herzen von Hyrule.",
"Bekommst Du etwa #kalte# Füße?",
"Sag den #Zoras# viele Grüße von mir!",
"Bewahre einen #kühlen#! Kopf.",
"Ganondorf setzt #Eisstrahl# ein. Das ist sehr effektiv!",
"Ein Lächeln ist der beste Weg, um das #Eis# zu brechen!",
"#Eiskalt# lässt du meine Seele erfrier'n.",
"Die #Titanic# hätte Angst vor Dir, @.",
"Oh nein!",
"Was die Dinosaurier getötet hat?&Die #Eiszeit#!",
"Nachts ist es #kälter# als draußen.",
"Never gonna #give you up#. Never gonna #let you down#. Never gonna run around and #desert you#.",
"Danke #@#! Aber der Gegenstand ist in einem anderem Schloß!",
"Kalt. Kalt. Kälter. #EISKALT#!",
};
static const char* const frenchIceTrapMessages[] = {
"#Pauvre fou#...",
"Tu es un #glaçon#, Harry!",
"#Sot# que tu es.",
"Ça me #glace# le sang!",
"#Reste au frais#, @.",
"Et c'est la douche #froide#!",
"L'#hiver# vient.",
"#Glacier#!",
"Ça en jette un #froid#.",
"Est-ce que tu as déjà eu des sueurs #froides#?",
"La vengeance est un plat qui se mange #froid#!",
"Dit bonjour aux #Zoras# pour moi!",
"Il faut parfois savoir garder la tête #froide#!",
"Ganondorf utilise #Piège de Glace#! C'est super efficace!",
"Laisse moi briser la #glace#!",
"Balance man..., Cadence man..., Trace la #glace#..., c'est le Cooooolllll Rasta!",
"Le #Titanic# aurait peur de toi, @.",
"Oh non!",
"Qu'est-ce qui a tué les dinosaures?&L'ère #glacière#!",
"L'imbécile réfléchit uniquement quand il s'observe dans la #glace#.",
"Never gonna #give you up#. Never gonna #let you down#. Never gonna run around and #desert you#.",
"Merci #@#! Mais ton objet est dans un autre château!",
"J'espère que ça ne te fait ni chaud, ni #froid#.",
"Je voulais t'offrir un trésor, mais il semble que tu aies eu #froid aux pieds#",
"Tu m'as dit que tu voulais livrer #de la glace# à Ganondorf!",
"Tu as obtenu la Triforce!&Cet ancien artefact divin peut exaucer n'importe quel... ah non, désolé, c'est juste un "
"piège de glace.",
"Il est temps de #te rafraîchir#!",
"La #Caverne Polaire# te passe le bonjour.",
"Chargement de l'objet, veuillez #patienter#...",
"Martèle A+B pour ne pas #mourir#.",
"Désolé, ton objet est à un autre endroit.", // would be better if it could have the name of the item
"Tu espérais que ce soit %gGreg%w.",
"Tu veux boire un chocolat chaud?",
"Le #froid# ne m'a jamais dérangé, de toute façon.",
"J'espère que tu es trop cool pour être #cool#!",
"Sois reconnaissant que ce n'est pas le #zéro absolu#.",
"Tu savais que le G de ZFG signifie #Glace#?",
"Tu as obtenu #L'Âge de Glace (2002)#!",
"Maintenant, tu peux lancer un #sort# que tu ne connais pas.",
"Que dirais-tu d'un héros #sur glace# ?",
"Pas de tunique pour #ça#!",
"Je savais que tu étais #partiellement Metroid#!",
"Voilà juste la #cerise sur le gâteau#!",
"Tu es tellement #cool#, @!",
"Tu as trouvé de la #déception#!",
"Tu t'es fait #BERNER#!",
"Commence à marteler.",
"Cet objet va #s'autodétruire# dans 5 secondes...",
"Souviens-toi, il pourrait y avoir un léger #inconfort#.",
"Dans un monde parfait, les #pièges de glace# comme moi n'existeraient pas, mais ce n'est pas un monde parfait.",
"Mon dieu qu'il fait #froid# ici.",
"Tu as testé l'objet avec ton #détecteur de glace#, il a bipé.", // would be better if it could have the name of the
// item
"Tu as découvert le chemin du zéro. Le #sub-zéro#.",
"Mweep... mweep... mweep...",
"Gelé, #sac à glace#! Je veux dire #gèle-toi#, racaille!",
"Est-ce qu'il fait #frais# ici ou est-ce juste #toi#?",
"#Continue#",
"QU'EST-CE QU'ELLE VA FAIRE, ME FAIRE UNE #[Glace]#!?",
"Tu as rencontré un #terrible destin#, n'est-ce pas?",
"Alors comme ça, tu aimes Shining ? Voici comment ça #finit#.",
"Petite erreur de trajectoire. #Je gagne#.",
"Prends ce #L#, @.",
"#Problème de compétence#",
"Tout ton chauffage nous appartient.",
"Attends une seconde, tu as déjà #cet objet#, non?",
"#Gèle#! Tu es entouré !",
"Erreur 404 - Objet non #trouvé#.",
"Pause hydratation ! Hé, qui a #gelé# mon eau?",
"Oups, mauvais #modèle d'objet#.",
"Oups! Tu dois mettre l'objet #dans ton inventaire#.",
"Tu as fait tomber l'objet, le brisant en #éclats de glace#!", // would be better if it could have the name of the
// item
"Tu es le maillon faible @, #au revoir#!",
"Ugh... Pourquoi avons-nous même randomisé #cet objet#?",
"La #Lune de Givre# se lève...",
"Selon toutes les lois connues de la physique et de la biologie, @ ne devrait pas survivre à #être complètement "
"enfermé dans la glace#. Les cellules de @ mourraient avant qu'elles ne #dégèlent#. Mais c'est un jeu vidéo, alors "
"@ survit... #Probablement#.",
"OK, arrête-moi si tu l'as déjà entendue - un joueur et une bouteille de #nitrogène liquide# entrent dans un bar à "
"lait...",
"Lástima, c'est un #piège de glace#...&&Personne ne s'attend à un #piège de glace espagnol#!",
"Mon dieu qu'il fait #GLAGLA# ici.",
"C'est bon, @ savait que c'était un #piège#, il l'utilise juste pour prendre des dégâts intentionnellement et "
"manipuler la RNG.",
"Cet objet n'est #pas disponible# dans votre pays.", // would be better if it could have the name of the item
"#Bonne# tentative. #;)#",
"Où est mon #Super Costume#?",
"#La revanche du Titanic#.",
};
void BuildIceTrapMessage(CustomMessage& msg) {
if (CVarGetInteger(CVAR_GENERAL("LetItSnow"), 0)) {
msg = CustomMessage(
/*english*/ "This year for Christmas, all you get is #COAL#!",
/*german*/ "This year for Christmas, all you get is #COAL#!",
/*french*/ "Pour Noël, cette année, tu n'auras que du #CHARBON#! %rJoyeux Noël%w!", { QM_BLUE });
} else {
msg = CustomMessage(ShipUtils::RandomElement(englishIceTrapMessages),
ShipUtils::RandomElement(germanIceTrapMessages),
ShipUtils::RandomElement(frenchIceTrapMessages), { QM_BLUE, QM_BLUE, QM_BLUE });
}
msg.AutoFormat();
}
void BuildTriforcePieceMessage(CustomMessage& msg) {
uint8_t current = gSaveContext.ship.quest.data.randomizer.triforcePiecesCollected + 1;
uint8_t required = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) + 1;
@@ -398,7 +88,7 @@ void BuildItemMessage(u16* textId, bool* loadFromMessageTable) {
CustomMessage msg;
if (player->getItemEntry.getItemId == RG_ICE_TRAP) {
BuildIceTrapMessage(msg);
Rando::Traps::BuildIceTrapMessage(msg, player->getItemEntry);
} else if (player->getItemEntry.getItemId == RG_TRIFORCE_PIECE) {
BuildTriforcePieceMessage(msg);
} else {

View File

@@ -15,6 +15,7 @@
#include "soh/Enhancements/randomizer/logic.h"
#include "soh/Enhancements/randomizer/randomizer_check_objects.h"
#include "soh/Enhancements/randomizer/rando_hash.h"
#include "soh/Enhancements/randomizer/Traps.h"
#include "soh/Enhancements/randomizer/3drando/shops.hpp"
extern "C" {
@@ -875,9 +876,10 @@ void PlandomizerDrawIceTrapSetup(uint32_t index) {
.Color(THEME_COLOR)
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(10.f, 6.f)))) {
plandoLogData[index].iceTrapName = GetIceTrapName(plandoLogData[index].iceTrapModel.GetRandomizerGet())
.GetForLanguage(CVarGetInteger(CVAR_SETTING("Languages"), 0))
.c_str();
plandoLogData[index].iceTrapName =
Rando::Traps::GetTrapName(plandoLogData[index].iceTrapModel.GetRandomizerGet())
.GetForLanguage(CVarGetInteger(CVAR_SETTING("Languages"), 0))
.c_str();
}
ImGui::SameLine();
}

View File

@@ -13,6 +13,7 @@
#include "3drando/hints.hpp"
#include "soh/util.h"
#include "../kaleido.h"
#include "soh/Enhancements/randomizer/Traps.h"
#include <fstream>
#include <spdlog/spdlog.h>
@@ -337,19 +338,9 @@ void Context::CreateItemOverrides() {
// If this is an ice trap, store the disguise model in iceTrapModels
const auto itemLoc = GetItemLocation(locKey);
if (itemLoc->GetPlacedRandomizerGet() == RG_ICE_TRAP) {
RandomizerGet trickModel = RandomElementFromSet(possibleIceTrapModels);
if (trickModel == RG_EMPTY_BOTTLE) {
trickModel = RandomElement(StaticData::normalBottles);
}
if (trickModel == RG_GUARD_HOUSE_KEY) {
trickModel = RandomElement(StaticData::overworldKeys);
}
if (trickModel == RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) {
trickModel = RandomElement(StaticData::beanSouls);
}
ItemOverride val(locKey, trickModel);
ItemOverride val(locKey, Traps::GetTrapTrickModel());
iceTrapModels[locKey] = val.LooksLike();
val.SetTrickName(GetIceTrapName(val.LooksLike()));
val.SetTrickName(Traps::GetTrapName(val.LooksLike()));
// If this is ice trap is in a shop, change the name based on what the model will look like
overrides[locKey] = val;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
#pragma once
#ifndef __cplusplus
#error This header should not be used in C files
#endif
#include "soh/Enhancements/custom-message/CustomMessageManager.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/randomizer/3drando/text.hpp"
#include "libultraship/libultra/types.h"
namespace Rando {
namespace Traps {
Text GetTrapName(uint16_t id);
RandomizerGet GetTrapTrickModel();
bool ShouldJunkItemBeTrap();
void BuildIceTrapMessage(CustomMessage& msg, GetItemEntry getItemEntry);
} // namespace Traps
} // namespace Rando