[Scummvm-git-logs] scummvm master -> 68bc11b73d5ab830129132566285276e0ba7e5f3

elasota noreply at scummvm.org
Thu Apr 27 13:48:26 UTC 2023


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
e4d63a3280 VCRUISE: Add in-game menu.
68bc11b73d VCRUISE: Permit duplicate inventory items. Fixes potentially getting stuck in the final area.


Commit: e4d63a3280d17084e1be3ce6aba30a31e805c135
    https://github.com/scummvm/scummvm/commit/e4d63a3280d17084e1be3ce6aba30a31e805c135
Author: elasota (ejlasota at gmail.com)
Date: 2023-04-27T09:47:43-04:00

Commit Message:
VCRUISE: Add in-game menu.

Changed paths:
    engines/vcruise/menu.cpp
    engines/vcruise/menu.h
    engines/vcruise/runtime.cpp
    engines/vcruise/runtime.h
    engines/vcruise/vcruise.cpp
    engines/vcruise/vcruise.h


diff --git a/engines/vcruise/menu.cpp b/engines/vcruise/menu.cpp
index 3268fc93739..c4f3499cf33 100644
--- a/engines/vcruise/menu.cpp
+++ b/engines/vcruise/menu.cpp
@@ -811,16 +811,14 @@ void ReahMainMenuPage::start() {
 
 	Graphics::Surface *buttonGraphic = _menuInterface->getUIGraphic(1);
 
-	// FIXME: Unused
-	//Common::Point buttonStateOffset = Common::Point(112, 0);
-	//ommon::Point buttonTopLeft = Common::Point(492, 66);
-
 	const int buttonTopYs[6] = {66, 119, 171, 224, 277, 330};
 
 	for (int i = 0; i < 6; i++) {
 		bool isEnabled = true;
 		if (i == kButtonContinue)
 			isEnabled = _menuInterface->hasDefaultSave();
+		else if (i == kButtonLoad)
+			isEnabled = _menuInterface->hasAnySave();
 
 		_buttons.push_back(Button(buttonGraphic, Common::Rect(0, i * 44, 112, i * 44 + 44), Common::Rect(492, buttonTopYs[i], 492 + 112, buttonTopYs[i] + 44), Common::Point(112, 0), isEnabled));
 	}
@@ -885,4 +883,16 @@ MenuPage *createMenuReahMain() {
 	return new ReahMainMenuPage();
 }
 
+MenuPage *createMenuReahQuit() {
+	return new ReahQuitMenuPage();
+}
+
+MenuPage *createMenuReahHelp() {
+	return new ReahHelpMenuPage();
+}
+
+MenuPage *createMenuReahSound() {
+	return new ReahSoundMenuPage();
+}
+
 } // End of namespace VCruise
diff --git a/engines/vcruise/menu.h b/engines/vcruise/menu.h
index 5fd1beab317..9806f1bedd9 100644
--- a/engines/vcruise/menu.h
+++ b/engines/vcruise/menu.h
@@ -53,6 +53,7 @@ public:
 	virtual Graphics::Surface *getUIGraphic(uint index) const = 0;
 	virtual Graphics::ManagedSurface *getMenuSurface() const = 0;
 	virtual bool hasDefaultSave() const = 0;
+	virtual bool hasAnySave() const = 0;
 	virtual Common::Point getMouseCoordinate() const = 0;
 	virtual void restartGame() const = 0;
 	virtual void goToCredits() const = 0;
@@ -77,6 +78,9 @@ protected:
 };
 
 MenuPage *createMenuReahMain();
+MenuPage *createMenuReahHelp();
+MenuPage *createMenuReahSound();
+MenuPage *createMenuReahQuit();
 
 } // End of namespace VCruise
 
diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index 40791fd07fa..fa8a3d86654 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -66,6 +66,7 @@ public:
 	Graphics::Surface *getUIGraphic(uint index) const override;
 	Graphics::ManagedSurface *getMenuSurface() const override;
 	bool hasDefaultSave() const override;
+	bool hasAnySave() const override;
 	Common::Point getMouseCoordinate() const override;
 	void restartGame() const override;
 	void goToCredits() const override;
@@ -104,6 +105,10 @@ bool RuntimeMenuInterface::hasDefaultSave() const {
 	return static_cast<VCruiseEngine *>(g_engine)->hasDefaultSave();
 }
 
+bool RuntimeMenuInterface::hasAnySave() const {
+	return static_cast<VCruiseEngine *>(g_engine)->hasAnySave();
+}
+
 Common::Point RuntimeMenuInterface::getMouseCoordinate() const {
 	return _runtime->_mousePos;
 }
@@ -845,6 +850,7 @@ Runtime::Runtime(OSystem *system, Audio::Mixer *mixer, const Common::FSNode &roo
 	  _loadedAnimation(0), _loadedAnimationHasSound(false), _animPendingDecodeFrame(0), _animDisplayingFrame(0), _animFirstFrame(0), _animLastFrame(0), _animStopFrame(0),
 	  _animStartTime(0), _animFramesDecoded(0), _animDecoderState(kAnimDecoderStateStopped),
 	  _animPlayWhileIdle(false), _idleIsOnInteraction(false), _idleHaveClickInteraction(false), _idleHaveDragInteraction(false), _idleInteractionID(0), _haveIdleStaticAnimation(false),
+	  _inGameMenuState(kInGameMenuStateInvisible), _inGameMenuActiveElement(0), _inGameMenuButtonActive {false, false, false, false, false},
 	  /*_loadedArea(0), */_lmbDown(false), _lmbDragging(false), _lmbReleaseWasClick(false), _lmbDownTime(0),
 	  _delayCompletionTime(0),
 	  _panoramaState(kPanoramaStateInactive),
@@ -1196,32 +1202,36 @@ bool Runtime::runIdle() {
 				return true;
 			}
 		} else if (osEvent.type == kOSEventTypeLButtonUp) {
-			PanoramaState oldPanoramaState = _panoramaState;
-			_panoramaState = kPanoramaStateInactive;
+			if (_inGameMenuState != kInGameMenuStateInvisible) {
+				dischargeInGameMenuMouseUp();
+			} else {
+				PanoramaState oldPanoramaState = _panoramaState;
+				_panoramaState = kPanoramaStateInactive;
 
-			// This is the correct place for matching the original game's behavior, not switching to panorama
-			resetInventoryHighlights();
+				// This is the correct place for matching the original game's behavior, not switching to panorama
+				resetInventoryHighlights();
 
-			if (_lmbReleaseWasClick) {
-				bool changedState = dischargeIdleClick();
-				if (changedState) {
-					drawCompass();
-					return true;
+				if (_lmbReleaseWasClick) {
+					bool changedState = dischargeIdleClick();
+					if (changedState) {
+						drawCompass();
+						return true;
+					}
 				}
-			}
 
-			// If the released from panorama mode, pick up any interactions at the new mouse location, and change the mouse back
-			if (oldPanoramaState != kPanoramaStateInactive) {
-				changeToCursor(_cursors[kCursorArrow]);
+				// If the released from panorama mode, pick up any interactions at the new mouse location, and change the mouse back
+				if (oldPanoramaState != kPanoramaStateInactive) {
+					changeToCursor(_cursors[kCursorArrow]);
 
-				// Clear idle interaction so that if a drag occurs but doesn't trigger a panorama or other state change,
-				// interactions are re-detected here.
-				_idleIsOnInteraction = false;
+					// Clear idle interaction so that if a drag occurs but doesn't trigger a panorama or other state change,
+					// interactions are re-detected here.
+					_idleIsOnInteraction = false;
 
-				bool changedState = dischargeIdleMouseMove();
-				if (changedState) {
-					drawCompass();
-					return true;
+					bool changedState = dischargeIdleMouseMove();
+					if (changedState) {
+						drawCompass();
+						return true;
+					}
 				}
 			}
 		} else if (osEvent.type == kOSEventTypeLButtonDown) {
@@ -2258,43 +2268,15 @@ void Runtime::changeToCursor(const Common::SharedPtr<Graphics::WinCursorGroup> &
 bool Runtime::dischargeIdleMouseMove() {
 	const MapScreenDirectionDef *sdDef = _map.getScreenDirection(_screenNumber, _direction);
 
-	if (_panoramaState == kPanoramaStateInactive) {
-		Common::Point relMouse(_mousePos.x - _gameSection.rect.left, _mousePos.y - _gameSection.rect.top);
-
-		bool isOnInteraction = false;
-		uint interactionID = 0;
-		if (sdDef) {
-			for (const InteractionDef &idef : sdDef->interactions) {
-				if (idef.objectType == 1 && idef.rect.contains(relMouse)) {
-					isOnInteraction = true;
-					interactionID = idef.interactionID;
-					break;
-				}
-			}
-		}
-
-		if (_idleIsOnInteraction && (!isOnInteraction || interactionID != _idleInteractionID)) {
-			// Mouse left the previous interaction
-			_idleIsOnInteraction = false;
-			_idleHaveClickInteraction = false;
-			_idleHaveDragInteraction = false;
-			changeToCursor(_cursors[kCursorArrow]);
-			resetInventoryHighlights();
-		}
-
-		if (isOnInteraction && _idleIsOnInteraction == false) {
-			_idleIsOnInteraction = true;
-			_idleInteractionID = interactionID;
+	if (_inGameMenuState != kInGameMenuStateInvisible) {
+		checkInGameMenuHover();
 
-			// New interaction, is there a script?
-			Common::SharedPtr<Script> script = findScriptForInteraction(interactionID);
+		// If still in the menu, ignore anything else
+		if (_inGameMenuState != kInGameMenuStateInvisible)
+			return false;
+	}
 
-			if (script) {
-				activateScript(script, ScriptEnvironmentVars());
-				return true;
-			}
-		}
-	} else {
+	if (_panoramaState != kPanoramaStateInactive) {
 		uint interactionID = 0;
 
 		Common::Point panRelMouse = _mousePos - _panoramaAnchor;
@@ -2328,11 +2310,58 @@ bool Runtime::dischargeIdleMouseMove() {
 		}
 	}
 
+	Common::Point relMouse(_mousePos.x - _gameSection.rect.left, _mousePos.y - _gameSection.rect.top);
+
+	bool isOnInteraction = false;
+	uint interactionID = 0;
+	if (sdDef) {
+		for (const InteractionDef &idef : sdDef->interactions) {
+			if (idef.objectType == 1 && idef.rect.contains(relMouse)) {
+				isOnInteraction = true;
+				interactionID = idef.interactionID;
+				break;
+			}
+		}
+	}
+
+	if (_idleIsOnInteraction && (!isOnInteraction || interactionID != _idleInteractionID)) {
+		// Mouse left the previous interaction
+		_idleIsOnInteraction = false;
+		_idleHaveClickInteraction = false;
+		_idleHaveDragInteraction = false;
+		changeToCursor(_cursors[kCursorArrow]);
+		resetInventoryHighlights();
+	}
+
+	if (isOnInteraction && _idleIsOnInteraction == false) {
+		_idleIsOnInteraction = true;
+		_idleInteractionID = interactionID;
+
+		// New interaction, is there a script?
+		Common::SharedPtr<Script> script = findScriptForInteraction(interactionID);
+
+		if (script) {
+			activateScript(script, ScriptEnvironmentVars());
+			return true;
+		}
+	}
+
+	if (_panoramaState == kPanoramaStateInactive)
+		checkInGameMenuHover();
+
 	// Didn't do anything
 	return false;
 }
 
 bool Runtime::dischargeIdleMouseDown() {
+	if (_inGameMenuState != kInGameMenuStateInvisible) {
+		if (_inGameMenuState == kInGameMenuStateHoveringActive) {
+			_inGameMenuState = kInGameMenuStateClickingOver;
+			drawInGameMenuButton(_inGameMenuActiveElement);
+		}
+		return false;
+	}
+
 	if (_idleIsOnInteraction && _idleHaveDragInteraction) {
 		// Interaction, is there a script?
 		Common::SharedPtr<Script> script = findScriptForInteraction(_idleInteractionID);
@@ -3261,7 +3290,7 @@ void Runtime::detectPanoramaDirections() {
 }
 
 void Runtime::detectPanoramaMouseMovement(uint32 timestamp) {
-	if (_panoramaState == kPanoramaStateInactive && (_lmbDragging || (_lmbDown && (timestamp - _lmbDownTime) >= 500)))
+	if (_panoramaState == kPanoramaStateInactive && _inGameMenuState == kInGameMenuStateInvisible && (_lmbDragging || (_lmbDown && (timestamp - _lmbDownTime) >= 500)))
 		panoramaActivate();
 }
 
@@ -3377,7 +3406,7 @@ void Runtime::clearTray() {
 }
 
 void Runtime::drawInventory(uint slot) {
-	if (_subtitleQueue.size() > 0 || _loadedAnimationHasSound || !_isInGame)
+	if (!isTrayVisible())
 		return;
 
 	Common::Rect trayRect = _traySection.rect;
@@ -3417,7 +3446,7 @@ void Runtime::drawInventory(uint slot) {
 }
 
 void Runtime::drawCompass() {
-	if (_subtitleQueue.size() > 0 || _loadedAnimationHasSound || !_isInGame)
+	if (!isTrayVisible())
 		return;
 
 	bool haveHorizontalRotate = false;
@@ -3486,6 +3515,10 @@ void Runtime::drawCompass() {
 	commitSectionToScreen(_traySection, lowerRightRect);
 }
 
+bool Runtime::isTrayVisible() const {
+	return _subtitleQueue.size() == 0 && !_loadedAnimationHasSound && _isInGame && (_gameState != kGameStateMenu);
+}
+
 void Runtime::resetInventoryHighlights() {
 	for (uint slot = 0; slot < kNumInventorySlots; slot++) {
 		InventoryItem &item = _inventory[slot];
@@ -3606,6 +3639,171 @@ void Runtime::changeToMenuPage(MenuPage *menuPage) {
 	menuPage->start();
 }
 
+void Runtime::checkInGameMenuHover() {
+	if (_inGameMenuState == kInGameMenuStateInvisible) {
+		if (_menuSection.rect.contains(_mousePos)) {
+			// Figure out what elements should be visible
+
+			// Help
+			_inGameMenuButtonActive[0] = true;
+
+			// Save
+			_inGameMenuButtonActive[1] = (_saveGame != nullptr);
+
+			// Load
+			_inGameMenuButtonActive[2] = static_cast<VCruiseEngine *>(g_engine)->hasAnySave();
+
+			// Sound
+			_inGameMenuButtonActive[3] = true;
+
+			// Quit
+			_inGameMenuButtonActive[4] = true;
+
+			_inGameMenuState = kInGameMenuStateVisible;
+			for (uint i = 0; i < 5; i++)
+				drawInGameMenuButton(i);
+		}
+	}
+
+	if (_inGameMenuState == kInGameMenuStateInvisible)
+		return;
+
+	if (!_menuSection.rect.contains(_mousePos)) {
+		if (_inGameMenuState != kInGameMenuStateClickingOver && _inGameMenuState != kInGameMenuStateClickingNotOver && _inGameMenuState != kInGameMenuStateClickingInactive) {
+			dismissInGameMenu();
+			return;
+		}
+	}
+
+	uint activeElement = 0;
+	if (_mousePos.x >= _menuSection.rect.left && _mousePos.y < _menuSection.rect.right)
+		activeElement = static_cast<uint>(_mousePos.x - _menuSection.rect.left) / 128u;
+
+	assert(activeElement < 5);
+
+	switch (_inGameMenuState) {
+	case kInGameMenuStateVisible:
+		if (_inGameMenuButtonActive[activeElement]) {
+			_inGameMenuState = kInGameMenuStateHoveringActive;
+			_inGameMenuActiveElement = activeElement;
+			drawInGameMenuButton(activeElement);
+		}
+		break;
+	case kInGameMenuStateHoveringActive:
+		if (activeElement != _inGameMenuActiveElement) {
+			uint oldElement = _inGameMenuActiveElement;
+
+			if (_inGameMenuButtonActive[activeElement]) {
+				_inGameMenuState = kInGameMenuStateHoveringActive;
+				_inGameMenuActiveElement = activeElement;
+				drawInGameMenuButton(activeElement);
+			} else
+				_inGameMenuState = kInGameMenuStateVisible;
+
+			drawInGameMenuButton(oldElement);
+		}
+		break;
+	case kInGameMenuStateClickingOver:
+		if (activeElement != _inGameMenuActiveElement) {
+			_inGameMenuState = kInGameMenuStateClickingNotOver;
+			drawInGameMenuButton(_inGameMenuActiveElement);
+		}
+		break;
+	case kInGameMenuStateClickingNotOver:
+		if (activeElement == _inGameMenuActiveElement) {
+			_inGameMenuState = kInGameMenuStateClickingOver;
+			drawInGameMenuButton(_inGameMenuActiveElement);
+		}
+		break;
+	case kInGameMenuStateClickingInactive:
+		break;
+	default:
+		error("Invalid menu state");
+		break;
+	}
+}
+
+void Runtime::dismissInGameMenu() {
+	const Common::Rect menuRect(0, 0, _menuSection.surf->w, _menuSection.surf->h);
+
+	uint32 blackColor = _menuSection.surf->format.RGBToColor(0, 0, 0);
+	_menuSection.surf->fillRect(menuRect, blackColor);
+
+	commitSectionToScreen(_menuSection, menuRect);
+
+	_inGameMenuState = kInGameMenuStateInvisible;
+}
+
+void Runtime::dischargeInGameMenuMouseUp() {
+	if (_inGameMenuState == kInGameMenuStateClickingOver) {
+		dismissInGameMenu();
+
+		// Handle click event
+		switch (_inGameMenuActiveElement) {
+		case 0:
+			changeToMenuPage(createMenuReahHelp());
+			break;
+		case 1:
+			g_engine->saveGameDialog();
+			break;
+		case 2:
+			g_engine->loadGameDialog();
+			break;
+		case 3:
+			changeToMenuPage(createMenuReahSound());
+			break;
+		case 4:
+			changeToMenuPage(createMenuReahQuit());
+			break;
+		default:
+			break;
+		}
+	} else {
+		_inGameMenuState = kInGameMenuStateVisible;
+		drawInGameMenuButton(_inGameMenuActiveElement);
+
+		checkInGameMenuHover();
+	}
+}
+
+void Runtime::drawInGameMenuButton(uint element) {
+	Common::Rect buttonDestRect = Common::Rect(element * 128u, 0, element * 128u + 128u, _menuSection.rect.height());
+
+	int buttonState = 0;
+	if (_inGameMenuButtonActive[element])
+		buttonState = 1;
+
+	switch (_inGameMenuState) {
+	case kInGameMenuStateVisible:
+		break;
+	case kInGameMenuStateHoveringActive:
+		if (element == _inGameMenuActiveElement)
+			buttonState = 2;
+		break;
+	case kInGameMenuStateClickingOver:
+		if (element == _inGameMenuActiveElement)
+			buttonState = 3;
+		break;
+	case kInGameMenuStateClickingNotOver:
+		if (element == _inGameMenuActiveElement)
+			buttonState = 2;
+		break;
+	case kInGameMenuStateClickingInactive:
+		break;
+	default:
+		error("Invalid menu state");
+		break;
+	}
+
+	Common::Point buttonTopLeftPoint = Common::Point(buttonDestRect.left, buttonDestRect.top);
+	buttonTopLeftPoint.y += buttonState * 44;
+
+	Common::Rect buttonSrcRect = Common::Rect(buttonTopLeftPoint.x, buttonTopLeftPoint.y, buttonTopLeftPoint.x + 128, buttonTopLeftPoint.y + _menuSection.rect.height());
+
+	_menuSection.surf->blitFrom(*_uiGraphics[4], buttonSrcRect, buttonDestRect);
+	commitSectionToScreen(_menuSection, buttonDestRect);
+}
+
 void Runtime::onLButtonDown(int16 x, int16 y) {
 	onMouseMove(x, y);
 
diff --git a/engines/vcruise/runtime.h b/engines/vcruise/runtime.h
index 8aed91311ee..491a9448f07 100644
--- a/engines/vcruise/runtime.h
+++ b/engines/vcruise/runtime.h
@@ -558,6 +558,16 @@ private:
 		kPanoramaStatePanningDown,
 	};
 
+	enum InGameMenuState {
+		kInGameMenuStateInvisible,
+		kInGameMenuStateVisible,
+		kInGameMenuStateHoveringInactive,
+		kInGameMenuStateHoveringActive,
+		kInGameMenuStateClickingOver,		// Mouse was pressed on a button and is holding on it
+		kInGameMenuStateClickingNotOver,	// Mouse was pressed on a button and dragged off
+		kInGameMenuStateClickingInactive,
+	};
+
 	static const uint kPanLeftInteraction = 1;
 	static const uint kPanDownInteraction = 2;
 	static const uint kPanRightInteraction = 3;
@@ -703,6 +713,7 @@ private:
 	void clearTray();
 	void drawInventory(uint slot);
 	void drawCompass();
+	bool isTrayVisible() const;
 	void resetInventoryHighlights();
 
 	Common::String getFileNameForItemGraphic(uint itemID) const;
@@ -712,6 +723,11 @@ private:
 
 	void changeToMenuPage(MenuPage *menuPage);
 
+	void checkInGameMenuHover();
+	void dismissInGameMenu();
+	void dischargeInGameMenuMouseUp();
+	void drawInGameMenuButton(uint element);
+
 	// Script things
 	void scriptOpNumber(ScriptArg_t arg);
 	void scriptOpRotate(ScriptArg_t arg);
@@ -949,6 +965,10 @@ private:
 	bool _idleHaveDragInteraction;
 	uint _idleInteractionID;
 
+	InGameMenuState _inGameMenuState;
+	uint _inGameMenuActiveElement;
+	bool _inGameMenuButtonActive[5];
+
 	Audio::Mixer *_mixer;
 
 	MapDef _map;
diff --git a/engines/vcruise/vcruise.cpp b/engines/vcruise/vcruise.cpp
index cb51405fa3b..46ea020ec37 100644
--- a/engines/vcruise/vcruise.cpp
+++ b/engines/vcruise/vcruise.cpp
@@ -291,5 +291,9 @@ bool VCruiseEngine::hasDefaultSave() {
 	return autoSaveExists;
 }
 
+bool VCruiseEngine::hasAnySave() {
+	return hasDefaultSave();	// Maybe could do this better, but with how ScummVM works, if there are any saves at all, then the autosave should exist.
+}
+
 
 } // End of namespace VCruise
diff --git a/engines/vcruise/vcruise.h b/engines/vcruise/vcruise.h
index 53b60919c01..ef0e1905d95 100644
--- a/engines/vcruise/vcruise.h
+++ b/engines/vcruise/vcruise.h
@@ -63,6 +63,7 @@ public:
 	void initializePath(const Common::FSNode &gamePath) override;
 
 	bool hasDefaultSave();
+	bool hasAnySave();
 
 protected:
 	void pauseEngineIntern(bool pause) override;


Commit: 68bc11b73d5ab830129132566285276e0ba7e5f3
    https://github.com/scummvm/scummvm/commit/68bc11b73d5ab830129132566285276e0ba7e5f3
Author: elasota (ejlasota at gmail.com)
Date: 2023-04-27T09:47:43-04:00

Commit Message:
VCRUISE: Permit duplicate inventory items. Fixes potentially getting stuck in the final area.

Changed paths:
    engines/vcruise/runtime.cpp


diff --git a/engines/vcruise/runtime.cpp b/engines/vcruise/runtime.cpp
index fa8a3d86654..89502f806d8 100644
--- a/engines/vcruise/runtime.cpp
+++ b/engines/vcruise/runtime.cpp
@@ -3354,8 +3354,6 @@ void Runtime::inventoryAddItem(uint item) {
 	uint firstOpenSlot = kNumInventorySlots;
 
 	for (uint i = 0; i < kNumInventorySlots; i++) {
-		if (_inventory[i].itemID == item)
-			return;
 		if (_inventory[i].itemID == 0 && firstOpenSlot == kNumInventorySlots)
 			firstOpenSlot = i;
 	}
@@ -3380,6 +3378,7 @@ void Runtime::inventoryRemoveItem(uint itemID) {
 			item.itemID = 0;
 			item.graphic.reset();
 			drawInventory(slot);
+			break;
 		}
 	}
 }
@@ -4478,6 +4477,7 @@ void Runtime::scriptOpItemHighlightSet(ScriptArg_t arg) {
 		if (item.itemID == static_cast<uint>(stackArgs[0])) {
 			item.highlighted = isHighlighted;
 			drawInventory(slot);
+			break;
 		}
 	}
 }




More information about the Scummvm-git-logs mailing list