From 2d12e141a8f6ef39c65abde1c4a4e73b3bbd240f Mon Sep 17 00:00:00 2001 From: XProger Date: Fri, 23 Nov 2018 06:49:25 +0300 Subject: [PATCH] "Simple Items" selector in detail settings for pickups --- src/controller.h | 36 ++++++++++++- src/core.h | 4 +- src/format.h | 135 ++++++++++++++++++++++++++++++++++------------- src/inventory.h | 20 +++---- src/lara.h | 16 +++--- src/level.h | 69 +++++++++--------------- src/sprite.h | 11 +--- src/ui.h | 2 + 8 files changed, 181 insertions(+), 112 deletions(-) diff --git a/src/controller.h b/src/controller.h index 782fbd5..195fd9e 100644 --- a/src/controller.h +++ b/src/controller.h @@ -199,6 +199,16 @@ struct Controller { deactivate(true); } + void updateModel() { + const TR::Model *model = getModel(); + + if (!model || model == animation.model) + return; + animation.setModel(model); + delete[] joints; + joints = new Basis[model->mCount]; + } + bool fixRoomIndex() { // TODO: remove this and fix braid vec3 p = getPos(); if (insideRoom(p, roomIndex)) @@ -1172,6 +1182,9 @@ struct Controller { } virtual void update() { + if (getEntity().modelIndex <= 0) + return; + if (explodeMask) updateExplosion(); else @@ -1345,14 +1358,35 @@ struct Controller { return joints[index]; } + void renderSprite(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics, int frame) { + Basis b; + b.w = 1.0f; + b.pos = pos; + #ifdef MERGE_SPRITES + b.rot = Core::mViewInv.getRot(); + #else + b.rot = quat(0, 0, 0, 1); + #endif + Core::setBasis(&b, 1); + mesh->renderSprite(-(getEntity().modelIndex + 1), frame); + } + + virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { + if (getEntity().modelIndex < 0) { + renderSprite(frustum, mesh, type, caustics, 0); + return; + } + ASSERT(getEntity().modelIndex > 0); + + const TR::Model *model = getModel(); + mat4 matrix = getMatrix(); Box box = animation.getBoundingBox(vec3(0, 0, 0), 0); if (!explodeMask && frustum && !frustum->isVisible(matrix, box.min, box.max)) return; - const TR::Model *model = getModel(); ASSERT(model); flags.rendered = true; diff --git a/src/core.h b/src/core.h index 0c4d917..bf3b996 100644 --- a/src/core.h +++ b/src/core.h @@ -230,7 +230,7 @@ namespace Core { #endif } support; -#define SETTINGS_VERSION 2 +#define SETTINGS_VERSION 3 #define SETTINGS_READING 0xFF struct Settings { @@ -249,6 +249,7 @@ namespace Core { }; uint8 quality[4]; }; + uint8 simple; uint8 vsync; uint8 stereo; void setFilter(Quality value) { @@ -674,6 +675,7 @@ namespace Core { settings.detail.setLighting (Core::Settings::HIGH); settings.detail.setShadows (Core::Settings::HIGH); settings.detail.setWater (Core::Settings::HIGH); + settings.detail.simple = false; settings.detail.vsync = true; settings.detail.stereo = Settings::STEREO_OFF; settings.audio.music = 14; diff --git a/src/format.h b/src/format.h index 00ae1d1..23ad930 100644 --- a/src/format.h +++ b/src/format.h @@ -151,10 +151,10 @@ E( KEY_ITEM_2 ) \ E( KEY_ITEM_3 ) \ E( KEY_ITEM_4 ) \ - E( INV_KEY_1 ) \ - E( INV_KEY_2 ) \ - E( INV_KEY_3 ) \ - E( INV_KEY_4 ) \ + E( INV_KEY_ITEM_1 ) \ + E( INV_KEY_ITEM_2 ) \ + E( INV_KEY_ITEM_3 ) \ + E( INV_KEY_ITEM_4 ) \ E( KEY_HOLE_1 ) \ E( KEY_HOLE_2 ) \ E( KEY_HOLE_3 ) \ @@ -407,10 +407,10 @@ E( _KEY_ITEM_2 ) \ E( _KEY_ITEM_3 ) \ E( _KEY_ITEM_4 ) \ - E( _INV_KEY_1 ) \ - E( _INV_KEY_2 ) \ - E( _INV_KEY_3 ) \ - E( _INV_KEY_4 ) \ + E( _INV_KEY_ITEM_1 ) \ + E( _INV_KEY_ITEM_2 ) \ + E( _INV_KEY_ITEM_3 ) \ + E( _INV_KEY_ITEM_4 ) \ E( _KEY_HOLE_1 ) \ E( _KEY_HOLE_2 ) \ E( _KEY_HOLE_3 ) \ @@ -704,10 +704,10 @@ E( __KEY_ITEM_2 ) \ E( __KEY_ITEM_3 ) \ E( __KEY_ITEM_4 ) \ - E( __INV_KEY_1 ) \ - E( __INV_KEY_2 ) \ - E( __INV_KEY_3 ) \ - E( __INV_KEY_4 ) \ + E( __INV_KEY_ITEM_1 ) \ + E( __INV_KEY_ITEM_2 ) \ + E( __INV_KEY_ITEM_3 ) \ + E( __INV_KEY_ITEM_4 ) \ E( __KEY_HOLE_1 ) \ E( __KEY_HOLE_2 ) \ E( __KEY_HOLE_3 ) \ @@ -1617,10 +1617,10 @@ namespace TR { REMAP_2( KEY_ITEM_2 ); REMAP_2( KEY_ITEM_3 ); REMAP_2( KEY_ITEM_4 ); - REMAP_2( INV_KEY_1 ); - REMAP_2( INV_KEY_2 ); - REMAP_2( INV_KEY_3 ); - REMAP_2( INV_KEY_4 ); + REMAP_2( INV_KEY_ITEM_1 ); + REMAP_2( INV_KEY_ITEM_2 ); + REMAP_2( INV_KEY_ITEM_3 ); + REMAP_2( INV_KEY_ITEM_4 ); REMAP_2( KEY_HOLE_1 ); REMAP_2( KEY_HOLE_2 ); REMAP_2( KEY_HOLE_3 ); @@ -1756,10 +1756,10 @@ namespace TR { REMAP_3( KEY_ITEM_2 ); REMAP_3( KEY_ITEM_3 ); REMAP_3( KEY_ITEM_4 ); - REMAP_3( INV_KEY_1 ); - REMAP_3( INV_KEY_2 ); - REMAP_3( INV_KEY_3 ); - REMAP_3( INV_KEY_4 ); + REMAP_3( INV_KEY_ITEM_1 ); + REMAP_3( INV_KEY_ITEM_2 ); + REMAP_3( INV_KEY_ITEM_3 ); + REMAP_3( INV_KEY_ITEM_4 ); REMAP_3( KEY_HOLE_1 ); REMAP_3( KEY_HOLE_2 ); REMAP_3( KEY_HOLE_3 ); @@ -1866,10 +1866,10 @@ namespace TR { || type == INV_PUZZLE_2 || type == INV_PUZZLE_3 || type == INV_PUZZLE_4 - || type == INV_KEY_1 - || type == INV_KEY_2 - || type == INV_KEY_3 - || type == INV_KEY_4 + || type == INV_KEY_ITEM_1 + || type == INV_KEY_ITEM_2 + || type == INV_KEY_ITEM_3 + || type == INV_KEY_ITEM_4 || type == INV_LEADBAR || type == INV_SCION; } @@ -1938,10 +1938,10 @@ namespace TR { case PUZZLE_3 : return INV_PUZZLE_3; case PUZZLE_4 : return INV_PUZZLE_4; - case KEY_ITEM_1 : return INV_KEY_1; - case KEY_ITEM_2 : return INV_KEY_2; - case KEY_ITEM_3 : return INV_KEY_3; - case KEY_ITEM_4 : return INV_KEY_4; + case KEY_ITEM_1 : return INV_KEY_ITEM_1; + case KEY_ITEM_2 : return INV_KEY_ITEM_2; + case KEY_ITEM_3 : return INV_KEY_ITEM_3; + case KEY_ITEM_4 : return INV_KEY_ITEM_4; case LEADBAR : return INV_LEADBAR; case SCION_PICKUP_QUALOPEC : @@ -1971,10 +1971,10 @@ namespace TR { case INV_PUZZLE_3 : return PUZZLE_3; case INV_PUZZLE_4 : return PUZZLE_4; - case INV_KEY_1 : return KEY_ITEM_1; - case INV_KEY_2 : return KEY_ITEM_2; - case INV_KEY_3 : return KEY_ITEM_3; - case INV_KEY_4 : return KEY_ITEM_4; + case INV_KEY_ITEM_1 : return KEY_ITEM_1; + case INV_KEY_ITEM_2 : return KEY_ITEM_2; + case INV_KEY_ITEM_3 : return KEY_ITEM_3; + case INV_KEY_ITEM_4 : return KEY_ITEM_4; case INV_LEADBAR : return LEADBAR; case INV_SCION : return SCION_PICKUP_DROP; @@ -3740,7 +3740,7 @@ namespace TR { e.type = Entity::remap(version, e.type); e.controller = NULL; - e.modelIndex = getModelIndex(e.type); + e.modelIndex = 0; // turn off interpolation for some entities e.flags.smooth = !((id == LVL_TR2_CUT_1 && (e.type == Entity::CUT_6 || e.type == Entity::CUT_8 || e.type == Entity::CUT_9)) @@ -3894,10 +3894,10 @@ namespace TR { case Entity::INV_PUZZLE_3 : extra.inv.puzzle[2] = i; break; case Entity::INV_PUZZLE_4 : extra.inv.puzzle[3] = i; break; - case Entity::INV_KEY_1 : extra.inv.key[0] = i; break; - case Entity::INV_KEY_2 : extra.inv.key[1] = i; break; - case Entity::INV_KEY_3 : extra.inv.key[2] = i; break; - case Entity::INV_KEY_4 : extra.inv.key[3] = i; break; + case Entity::INV_KEY_ITEM_1 : extra.inv.key[0] = i; break; + case Entity::INV_KEY_ITEM_2 : extra.inv.key[1] = i; break; + case Entity::INV_KEY_ITEM_3 : extra.inv.key[2] = i; break; + case Entity::INV_KEY_ITEM_4 : extra.inv.key[3] = i; break; case Entity::INV_LEADBAR : extra.inv.leadbar = i; break; case Entity::INV_SCION : extra.inv.scion = i; break; @@ -3948,6 +3948,67 @@ namespace TR { } } + void initModelIndices(bool simpleItems) { + #define OVERRIDE(a) { TR::Entity::##a, TR::Entity::INV_##a } + + struct { + TR::Entity::Type src, dst; + } overrides[] = { + // weapon + OVERRIDE(PISTOLS), + OVERRIDE(SHOTGUN), + OVERRIDE(MAGNUMS), + OVERRIDE(UZIS), + OVERRIDE(AUTOPISTOLS), + OVERRIDE(HARPOON), + OVERRIDE(M16), + OVERRIDE(GRENADE), + // ammo + OVERRIDE(AMMO_PISTOLS), + OVERRIDE(AMMO_SHOTGUN), + OVERRIDE(AMMO_MAGNUMS), + OVERRIDE(AMMO_UZIS), + OVERRIDE(AMMO_AUTOPISTOLS), + OVERRIDE(AMMO_HARPOON), + OVERRIDE(AMMO_M16), + OVERRIDE(AMMO_GRENADE), + // items + OVERRIDE(MEDIKIT_BIG), + OVERRIDE(MEDIKIT_SMALL), + OVERRIDE(FLARES), + // key items + OVERRIDE(KEY_ITEM_1), + OVERRIDE(KEY_ITEM_2), + OVERRIDE(KEY_ITEM_3), + OVERRIDE(KEY_ITEM_4), + // puzzle items + OVERRIDE(PUZZLE_1), + OVERRIDE(PUZZLE_2), + OVERRIDE(PUZZLE_3), + OVERRIDE(PUZZLE_4), + // other items + OVERRIDE(LEADBAR), + OVERRIDE(QUEST_ITEM_1), + OVERRIDE(QUEST_ITEM_2), + { TR::Entity::SCION_PICKUP_QUALOPEC, TR::Entity::INV_SCION }, + { TR::Entity::SCION_PICKUP_DROP, TR::Entity::INV_SCION }, + }; + + for (int i = 0; i < entitiesCount; i++) { + TR::Entity &e = entities[i]; + e.modelIndex = getModelIndex(e.type); + if (!simpleItems) { + for (int j = 0; j < COUNT(overrides); j++) + if (e.type == overrides[j].src) { + e.modelIndex = getModelIndex(overrides[j].dst); + break; + } + } + } + + #undef OVERRIDE + } + LevelID getTitleId() const { return TR::getTitleId(version); } diff --git a/src/inventory.h b/src/inventory.h index f835bb5..56847cc 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -126,6 +126,7 @@ static const OptionItem optDetail[] = { OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_LIGHTING, SETTINGS( detail.lighting ), STR_QUALITY_LOW, 0, 2 ), OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_SHADOWS, SETTINGS( detail.shadows ), STR_QUALITY_LOW, 0, 2 ), OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_WATER, SETTINGS( detail.water ), STR_QUALITY_LOW, 0, 2 ), + OptionItem( OptionItem::TYPE_PARAM, STR_OPT_SIMPLE_ITEMS, SETTINGS( detail.simple ), STR_OFF, 0, 1 ), #if defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_PSP) || defined(_OS_RPI) OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_VSYNC, SETTINGS( detail.vsync ), STR_OFF, 0, 1 ), #endif @@ -177,7 +178,6 @@ static const OptionItem optControls[] = { OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cRoll , SETTINGS( controls[0].keys[ cRoll ] ), STR_KEY_FIRST ), OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cInventory , SETTINGS( controls[0].keys[ cInventory ] ), STR_KEY_FIRST ), OptionItem( OptionItem::TYPE_KEY, STR_CTRL_FIRST + cStart , SETTINGS( controls[0].keys[ cStart ] ), STR_KEY_FIRST ), - }; static OptionItem optControlsPlayer[COUNT(optControls)]; @@ -272,10 +272,10 @@ struct Inventory { case TR::Entity::INV_PUZZLE_3 : desc = Desc( STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[2] ); break; case TR::Entity::INV_PUZZLE_4 : desc = Desc( STR_PUZZLE, PAGE_ITEMS, level->extra.inv.puzzle[3] ); break; - case TR::Entity::INV_KEY_1 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[0] ); break; - case TR::Entity::INV_KEY_2 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[1] ); break; - case TR::Entity::INV_KEY_3 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[2] ); break; - case TR::Entity::INV_KEY_4 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[3] ); break; + case TR::Entity::INV_KEY_ITEM_1 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[0] ); break; + case TR::Entity::INV_KEY_ITEM_2 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[1] ); break; + case TR::Entity::INV_KEY_ITEM_3 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[2] ); break; + case TR::Entity::INV_KEY_ITEM_4 : desc = Desc( STR_KEY, PAGE_ITEMS, level->extra.inv.key[3] ); break; case TR::Entity::INV_LEADBAR : desc = Desc( STR_LEAD_BAR, PAGE_ITEMS, level->extra.inv.leadbar ); break; case TR::Entity::INV_SCION : desc = Desc( STR_SCION, PAGE_ITEMS, level->extra.inv.scion ); break; @@ -641,15 +641,15 @@ struct Inventory { } if (level->id == TR::LVL_TR2_HOUSE) { - add(TR::Entity::INV_KEY_1); + add(TR::Entity::INV_KEY_ITEM_1); add(TR::Entity::INV_PUZZLE_1); } #ifdef _DEBUG addWeapons(); - add(TR::Entity::INV_KEY_1, 3); - add(TR::Entity::INV_KEY_2, 3); - add(TR::Entity::INV_KEY_3, 3); - add(TR::Entity::INV_KEY_4, 3); + add(TR::Entity::INV_KEY_ITEM_1, 3); + add(TR::Entity::INV_KEY_ITEM_2, 3); + add(TR::Entity::INV_KEY_ITEM_3, 3); + add(TR::Entity::INV_KEY_ITEM_4, 3); add(TR::Entity::INV_PUZZLE_1, 3); add(TR::Entity::INV_PUZZLE_2, 3); diff --git a/src/lara.h b/src/lara.h index ab770a9..733b750 100644 --- a/src/lara.h +++ b/src/lara.h @@ -1686,14 +1686,14 @@ struct Lara : Character { game->playSound(TR::SND_HEALTH, pos, Sound::PAN); //TODO: remove medikit item break; - case TR::Entity::INV_PUZZLE_1 : - case TR::Entity::INV_PUZZLE_2 : - case TR::Entity::INV_PUZZLE_3 : - case TR::Entity::INV_PUZZLE_4 : - case TR::Entity::INV_KEY_1 : - case TR::Entity::INV_KEY_2 : - case TR::Entity::INV_KEY_3 : - case TR::Entity::INV_KEY_4 : + case TR::Entity::INV_PUZZLE_1 : + case TR::Entity::INV_PUZZLE_2 : + case TR::Entity::INV_PUZZLE_3 : + case TR::Entity::INV_PUZZLE_4 : + case TR::Entity::INV_KEY_ITEM_1 : + case TR::Entity::INV_KEY_ITEM_2 : + case TR::Entity::INV_KEY_ITEM_3 : + case TR::Entity::INV_KEY_ITEM_4 : if (usedKey == item) return false; usedKey = item; diff --git a/src/level.h b/src/level.h index 15a2360..edd8155 100644 --- a/src/level.h +++ b/src/level.h @@ -348,10 +348,12 @@ struct Level : IGame { if (settings.detail.filter != Core::settings.detail.filter) atlas->setFilterQuality(settings.detail.filter); - bool rebuildMesh = settings.detail.water != Core::settings.detail.water; + bool rebuildMesh = settings.detail.water != Core::settings.detail.water; bool rebuildAmbient = settings.detail.lighting != Core::settings.detail.lighting; bool rebuildShadows = settings.detail.shadows != Core::settings.detail.shadows; - bool rebuildWater = settings.detail.water != Core::settings.detail.water; + bool rebuildWater = settings.detail.water != Core::settings.detail.water; + bool switchModels = settings.detail.simple != Core::settings.detail.simple; + bool rebuildShaders = rebuildWater || rebuildAmbient || rebuildShadows; bool redraw = memcmp(&settings.detail, &Core::settings.detail, sizeof(settings.detail)) != 0; @@ -392,6 +394,9 @@ struct Level : IGame { if (redraw && inventory->active && !level.isTitle()) needRedrawTitleBG = true; + + if (switchModels) + resetModels(); } virtual TR::Level* getLevel() { @@ -795,6 +800,8 @@ struct Level : IGame { //============================== Level(Stream &stream) : level(stream), waitTrack(false), isEnded(false), cutsceneWaitTimer(0.0f), animTexTimer(0.0f), statsTimeDelta(0.0f) { + level.initModelIndices(Core::settings.detail.simple == 1); + #ifdef _OS_PSP GAPI::freeEDRAM(); #endif @@ -819,7 +826,6 @@ struct Level : IGame { initTextures(); mesh = new MeshBuilder(&level, atlas); - initOverrides(); initEntities(); shadow = NULL; @@ -909,6 +915,16 @@ struct Level : IGame { Sound::listenersCount = 1; } + void resetModels() { + level.initModelIndices(Core::settings.detail.simple == 1); + for (int i = 0; i < level.entitiesCount; i++) { + TR::Entity &e = level.entities[i]; + if (!e.controller) continue; + Controller *controller = (Controller*)e.controller; + controller->updateModel(); + } + } + void addPlayer(int index) { if (level.isCutsceneLevel()) return; @@ -997,10 +1013,10 @@ struct Level : IGame { case TR::Entity::GEARS_1 : case TR::Entity::GEARS_2 : case TR::Entity::GEARS_3 : return new Gear(this, index); - case TR::Entity::INV_KEY_1 : - case TR::Entity::INV_KEY_2 : - case TR::Entity::INV_KEY_3 : - case TR::Entity::INV_KEY_4 : return new KeyItemInv(this, index); + case TR::Entity::INV_KEY_ITEM_1 : + case TR::Entity::INV_KEY_ITEM_2 : + case TR::Entity::INV_KEY_ITEM_3 : + case TR::Entity::INV_KEY_ITEM_4 : return new KeyItemInv(this, index); case TR::Entity::TRAP_FLOOR : return new TrapFloor(this, index); case TR::Entity::CRYSTAL : return new Crystal(this, index); case TR::Entity::TRAP_SWING_BLADE : return new TrapSwingBlade(this, index); @@ -1109,7 +1125,7 @@ struct Level : IGame { case TR::Entity::WINDOW_1 : case TR::Entity::WINDOW_2 : return new BreakableWindow(this, index); - default : return (level.entities[index].modelIndex > 0) ? new Controller(this, index) : new Sprite(this, index, 0); + default : return new Controller(this, index); } } @@ -1425,43 +1441,6 @@ struct Level : IGame { #endif } - void initOverrides() { - /* - for (int i = 0; i < level.entitiesCount; i++) { - int16 &id = level.entities[i].id; - switch (id) { - // weapon - case 84 : id = 99; break; // pistols - case 85 : id = 100; break; // shotgun - case 86 : id = 101; break; // magnums - case 87 : id = 102; break; // uzis - // ammo - case 88 : id = 103; break; // for pistols - case 89 : id = 104; break; // for shotgun - case 90 : id = 105; break; // for magnums - case 91 : id = 106; break; // for uzis - // medikit - case 93 : id = 108; break; // big - case 94 : id = 109; break; // small - // keys - case 110 : id = 114; break; - case 111 : id = 115; break; - case 112 : id = 116; break; - case 113 : id = 117; break; - case 126 : id = 127; break; - case 129 : id = 133; break; - case 130 : id = 134; break; - case 131 : id = 135; break; - case 132 : id = 136; break; - case 141 : id = 145; break; - case 142 : id = 146; break; - case 143 : id = 150; break; - case 144 : id = 150; break; - } - } - */ - } - void initReflections() { for (int i = 0; i < level.entitiesBaseCount; i++) { TR::Entity &e = level.entities[i]; diff --git a/src/sprite.h b/src/sprite.h index 3f263de..0a39ff1 100644 --- a/src/sprite.h +++ b/src/sprite.h @@ -53,16 +53,7 @@ struct Sprite : Controller { } virtual void render(Frustum *frustum, MeshBuilder *mesh, Shader::Type type, bool caustics) { - Basis b; - b.w = 1.0f; - b.pos = pos; - #ifdef MERGE_SPRITES - b.rot = Core::mViewInv.getRot(); - #else - b.rot = quat(0, 0, 0, 1); - #endif - Core::setBasis(&b, 1); - mesh->renderSprite(-(getEntity().modelIndex + 1), frame); + renderSprite(frustum, mesh, type, caustics, frame); } }; diff --git a/src/ui.h b/src/ui.h index f8d7d8f..89d8184 100644 --- a/src/ui.h +++ b/src/ui.h @@ -68,6 +68,7 @@ enum StringID { , STR_OPT_DETAIL_WATER , STR_OPT_DETAIL_VSYNC , STR_OPT_DETAIL_STEREO + , STR_OPT_SIMPLE_ITEMS // sound options , STR_SET_VOLUMES , STR_REVERBERATION @@ -196,6 +197,7 @@ const char *STR[STR_MAX] = { , "Water" , "VSync" , "Stereo" + , "Simple Items" // sound options , "Set Volumes" , "Reverberation"