// ==UserScript==
// @name F95Zone, Ryuugames, and DLsite Search Buttons for Steam
// @namespace http://tampermonkey.net/
// @version 6.7.0
// @description Adds buttons to search for the current game and developer on F95Zone and its forums, and search for the game on Ryuugames, OtomiGames, and DLsite. Buttons in Game Searches are stacked in a two-column grid with reduced spacing.
// @author FunkyJustin
// @match https://store.steampowered.com/app/*
// @grant none
// @license MIT
// @supportURL https://greasyfork.org/en/scripts/490884-f95zone-ryuugames-and-dlsite-search-buttons-for-steam/feedback
// ==/UserScript==
(function() {
'use strict';
function createF95ZoneButton() {
const titleElement = document.querySelector('.apphub_AppName');
if (!titleElement) return;
const gameTitle = titleElement.innerText.trim() || 'Unknown Game';
const developerElement = document.querySelector('#developers_list a');
const developerName = developerElement ? developerElement.innerText.trim() : null;
// Add margin below the game title for spacing
titleElement.style.marginBottom = '15px';
// Container for game search buttons
const buttonContainer = createSectionContainer('Game Searches', true, 'game-searches', true);
const buttonWrapper = buttonContainer.querySelector('.button-wrapper');
titleElement.parentElement.appendChild(buttonContainer);
// Configuration for game search buttons
const gameSearchSites = [
{ name: 'F95Zone', url: `https://f95zone.to/sam/latest_alpha/#/cat=games/page=1/search=${encodeURIComponent(gameTitle)}` },
{ name: 'F95Zone Forums', url: `https://f95zone.to/search/?q=${encodeURIComponent(gameTitle)}&t=post&c[child_nodes]=1&c[nodes][0]=2&c[title_only]=1&o=relevance` },
{ name: 'Ryuugames', url: `https://www.ryuugames.com/?s=${encodeURIComponent(gameTitle)}` },
{ name: 'OtomiGames', url: `https://otomi-games.com/?s=${encodeURIComponent(gameTitle)}` },
{ name: 'DLsite (Pro)', url: `https://www.dlsite.com/pro/fsr/=/language/jp/sex_category%5B0%5D/male/keyword/${encodeURIComponent(gameTitle).replace(/%20/g, '+')}/work_category%5B0%5D/pc/order%5B0%5D/trend/options_and_or/and/per_page/30/page/1/from/fs.header` },
{ name: 'DLsite (Maniax)', url: `https://www.dlsite.com/maniax/fsr/=/language/jp/sex_category%5B0%5D/male/keyword/${encodeURIComponent(gameTitle).replace(/%20/g, '+')}/work_category%5B0%5D/doujin/work_category%5B1%5D/books/work_category%5B2%5D/pc/work_category%5B3%5D/app/order%5B0%5D/trend/options_and_or/and/per_page/30/page/1/from/fs.header` }
];
// Create game search buttons in the specified order
gameSearchSites.forEach(site => {
const button = createButton(site.name, site.url, '#d0d8e0', 'linear-gradient(135deg, #2a3e4f 0%, #1b2838 100%)', '#66c0f4');
button.setAttribute('aria-label', `Search ${site.name} for ${gameTitle}`);
button.setAttribute('title', `Search for ${gameTitle} on ${site.name}`);
buttonWrapper.appendChild(button);
});
// Container for developer search buttons
if (developerName) {
const devContainer = createSectionContainer('Developer Searches', true, 'dev-searches', false);
const devWrapper = devContainer.querySelector('.button-wrapper');
titleElement.parentElement.appendChild(devContainer);
const devSearchSites = [
{ name: 'F95Zone', url: `https://f95zone.to/sam/latest_alpha/#/cat=games/page=1/creator=${encodeURIComponent(developerName)}` },
{ name: 'DLsite', url: `https://www.dlsite.com/pro/fsr/=/language/jp/sex_category%5B0%5D/male/keyword/${encodeURIComponent(developerName).replace(/%20/g, '+')}/order%5B0%5D/trend/options_and_or/and/per_page/30/page/1/from/fs.header` }
];
devSearchSites.forEach(site => {
const button = createButton(`Search Developer on ${site.name}`, site.url, '#d0d8e0', 'linear-gradient(135deg, #2a3e4f 0%, #1b2838 100%)', '#66c0f4');
button.setAttribute('aria-label', `Search ${site.name} for developer ${developerName}`);
button.setAttribute('title', `Search for developer ${developerName} on ${site.name}`);
devWrapper.appendChild(button);
});
}
}
function createSectionContainer(labelText, isExpandedByDefault, sectionId, isGridLayout) {
const container = document.createElement('div');
container.style.margin = '8px 0';
container.style.background = 'linear-gradient(135deg, #1b2838 0%, #2a3e4f 100%)';
container.style.padding = '8px';
container.style.borderRadius = '8px';
container.style.border = '1px solid rgba(102, 192, 244, 0.15)';
container.style.boxShadow = '0 6px 15px rgba(0, 0, 0, 0.25), inset 0 1px 3px rgba(255, 255, 255, 0.05)';
container.style.backdropFilter = 'blur(6px)';
container.style.webkitBackdropFilter = 'blur(6px)';
container.style.opacity = '0';
container.style.transition = 'opacity 0.5s ease-in-out, box-shadow 0.3s ease';
const header = document.createElement('div');
header.style.display = 'flex';
header.style.alignItems = 'center';
header.style.padding = '6px';
header.style.cursor = 'pointer';
header.style.borderRadius = '6px';
header.style.transition = 'background 0.3s ease, opacity 0.3s ease';
header.setAttribute('role', 'button');
header.setAttribute('tabindex', '0');
header.setAttribute('aria-expanded', isExpandedByDefault.toString());
header.setAttribute('aria-controls', sectionId);
const toggleIcon = document.createElement('span');
toggleIcon.innerHTML = isExpandedByDefault ? '▼' : '▶';
toggleIcon.style.marginRight = '8px';
toggleIcon.style.background = 'linear-gradient(90deg, #66c0f4, #a7d7f9)';
toggleIcon.style.webkitBackgroundClip = 'text';
toggleIcon.style.backgroundClip = 'text';
toggleIcon.style.color = 'transparent';
toggleIcon.style.transition = 'transform 0.2s ease';
header.appendChild(toggleIcon);
const label = document.createElement('span');
label.innerText = labelText;
label.style.fontWeight = '700';
label.style.color = '#e0e8f0';
label.style.fontFamily = '"Motiva Sans", Arial, sans-serif';
label.style.fontSize = '15px';
label.style.letterSpacing = '0.5px';
label.style.background = 'linear-gradient(90deg, #a7d7f9, #e0e8f0)';
label.style.webkitBackgroundClip = 'text';
label.style.backgroundClip = 'text';
label.style.color = 'transparent';
label.style.textShadow = '0 1px 2px rgba(0, 0, 0, 0.3)';
header.appendChild(label);
const buttonWrapper = document.createElement('div');
buttonWrapper.className = 'button-wrapper';
buttonWrapper.id = sectionId;
buttonWrapper.style.display = isExpandedByDefault ? 'grid' : 'none';
buttonWrapper.style.padding = '8px';
buttonWrapper.style.justifyContent = 'flex-start';
// Apply grid layout for Game Searches, flex for Developer Searches
if (isGridLayout) {
buttonWrapper.style.display = isExpandedByDefault ? 'grid' : 'none';
buttonWrapper.style.gridTemplateColumns = 'auto auto'; // Two columns
buttonWrapper.style.gap = '6px'; // Reduced gap between buttons
buttonWrapper.style.alignItems = 'center';
} else {
buttonWrapper.style.display = isExpandedByDefault ? 'flex' : 'none';
buttonWrapper.style.flexWrap = 'wrap';
buttonWrapper.style.gap = '6px'; // Reduced gap
}
container.appendChild(header);
container.appendChild(buttonWrapper);
header.addEventListener('click', () => {
const isExpanded = buttonWrapper.style.display === (isGridLayout ? 'grid' : 'flex');
buttonWrapper.style.display = isExpanded ? 'none' : (isGridLayout ? 'grid' : 'flex');
toggleIcon.innerHTML = isExpanded ? '▶' : '▼';
header.setAttribute('aria-expanded', (!isExpanded).toString());
header.style.opacity = isExpanded ? '0.9' : '1';
});
header.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
header.click();
}
});
header.addEventListener('mouseover', () => {
header.style.background = 'rgba(66, 133, 244, 0.1)';
toggleIcon.style.transform = 'scale(1.2)';
header.style.opacity = '1';
});
header.addEventListener('mouseout', () => {
header.style.background = 'transparent';
toggleIcon.style.transform = 'scale(1)';
header.style.opacity = '0.9';
});
header.addEventListener('focus', () => {
header.style.outline = '2px solid #4285f4';
header.style.boxShadow = '0 0 10px rgba(66, 133, 244, 0.5)';
});
header.addEventListener('blur', () => {
header.style.outline = 'none';
header.style.boxShadow = 'none';
});
container.addEventListener('mouseover', () => {
container.style.boxShadow = '0 8px 18px rgba(66, 133, 244, 0.2), inset 0 1px 3px rgba(255, 255, 255, 0.05)';
});
container.addEventListener('mouseout', () => {
container.style.boxShadow = '0 6px 15px rgba(0, 0, 0, 0.25), inset 0 1px 3px rgba(255, 255, 255, 0.05)';
});
setTimeout(() => {
container.style.opacity = '1';
}, 100);
const styleSheet = document.createElement('style');
styleSheet.textContent = `
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(66, 133, 244, 0.7); }
50% { box-shadow: 0 0 0 8px rgba(66, 133, 244, 0.3); }
100% { box-shadow: 0 0 0 0 rgba(66, 133, 244, 0.7); }
}
`;
document.head.appendChild(styleSheet);
return container;
}
function createButton(text, link, textColor, backgroundStyle, iconColor) {
const buttonContainer = document.createElement('div');
buttonContainer.setAttribute('role', 'button');
buttonContainer.setAttribute('tabindex', '0');
buttonContainer.style.padding = '8px 14px'; // Reduced padding
buttonContainer.style.borderRadius = '8px';
buttonContainer.style.transition = 'all 0.3s ease';
buttonContainer.style.background = backgroundStyle;
buttonContainer.style.color = textColor;
buttonContainer.style.border = '1px solid rgba(102, 192, 244, 0.2)';
buttonContainer.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
buttonContainer.style.display = 'flex';
buttonContainer.style.alignItems = 'center';
buttonContainer.style.fontWeight = '600';
buttonContainer.style.fontFamily = '"Motiva Sans", Arial, sans-serif';
buttonContainer.style.fontSize = '13px'; // Slightly smaller font
buttonContainer.style.lineHeight = '18px';
buttonContainer.style.cursor = 'pointer';
buttonContainer.style.opacity = '0.95';
const icon = document.createElement('span');
icon.innerHTML = `<svg style="width: 14px; height: 14px; margin-right: 8px;" fill="${iconColor}" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>`;
buttonContainer.appendChild(icon);
const buttonText = document.createElement('span');
buttonText.innerText = text;
buttonText.style.whiteSpace = 'nowrap';
buttonText.style.overflow = 'hidden';
buttonText.style.textOverflow = 'ellipsis';
buttonContainer.appendChild(buttonText);
buttonContainer.addEventListener('click', function(event) {
event.preventDefault();
openUniqueTab(link);
});
buttonContainer.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
event.preventDefault();
openUniqueTab(link);
}
});
buttonContainer.addEventListener('mouseover', () => {
buttonContainer.style.background = 'linear-gradient(135deg, #3a5f7d 0%, #1b2838 100%)';
buttonContainer.style.transform = 'scale(1.05)';
buttonContainer.style.opacity = '1';
buttonContainer.style.boxShadow = '0 5px 15px rgba(66, 133, 244, 0.4)';
});
buttonContainer.addEventListener('mouseout', () => {
buttonContainer.style.background = backgroundStyle;
buttonContainer.style.transform = 'scale(1)';
buttonContainer.style.opacity = '0.95';
buttonContainer.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
});
buttonContainer.addEventListener('focus', () => {
buttonContainer.style.outline = '2px solid #4285f4';
buttonContainer.style.boxShadow = '0 0 12px rgba(66, 133, 244, 0.5)';
buttonContainer.style.animation = 'pulse 1.5s infinite';
});
buttonContainer.addEventListener('blur', () => {
buttonContainer.style.outline = 'none';
buttonContainer.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
buttonContainer.style.animation = 'none';
});
return buttonContainer;
}
function openUniqueTab(url) {
window.open(url, '_blank');
}
window.addEventListener('load', createF95ZoneButton);
})();