Centralize all dreadcast scripts in one single source, integrated to the game.
// ==UserScript==
// @name Dreadcast Script Manager
// @namespace Dreadcast
// @match https://www.dreadcast.net/Main
// @match https://www.dreadcast.net/Forum
// @match https://www.dreadcast.net/Forum/*
// @match https://www.dreadcast.net/EDC
// @match https://www.dreadcast.net/EDC/*
// @version 1.4.0
// @author Pelagia/Isilin
// @description Centralize all dreadcast scripts in one single source, integrated to the game.
// @license https://github.com/Isilin/dreadcast-scripts?tab=GPL-3.0-1-ov-file
// @require https://update.greasyfork.org/scripts/507382/Dreadcast%20Development%20Kit.user.js?version=1533476
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_listValues
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @connect update.greasyfork.org
// @connect docs.google.com
// @connect googleusercontent.com
// @connect sheets.googleapis.com
// @connect raw.githubusercontent.com
// ==/UserScript==
// TODO remplacer petit à petit les scripts par les versions locales nettoyées.
// TODO use a recent jquery with noConflict
// TODO Reset button in Com'Back reset all the settings from DCSM (including scripts then).
// Offline copy of the script list, used as a last resort when both the cache
// and the remote list are unavailable. It may lag behind data/scripts.json.
// === DCSM_FALLBACK_START - generated by tools/sync-fallback.mjs, do not edit by hand ===
const DCSM_FALLBACK_LIST = JSON.parse(`
[
{
"id": "comback",
"name": "Com'Back",
"description": "Il ajoute des fonctions d'en-tête aux messages, ainsi que de blocs pré-écrits, et rend les liens cliquables.",
"authors": "Valion, Naugriim, Solon, Harlinde",
"icon": "",
"url": "https://update.greasyfork.org/scripts/17200/Com'back.user.js?version=1074459",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"mailing"
],
"experimental": false
},
{
"id": "comcount",
"name": "Com'Count",
"description": "Il permet d'ajuster manuellement le nombre de messages non lus, lorsque celui-ci se désynchronise.",
"authors": "Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/395938/Com'Count.user.js?version=830889",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"mailing"
],
"experimental": false
},
{
"id": "vousavezunmessage",
"name": "\\"Vous avez un message\\"",
"description": "Il émet une alerte sonore à la réception d'un nouveau message.",
"authors": "Gideon, Sÿ, Odul, Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/396697/VousAvezUnMessage.user.js?version=830556",
"doc": "",
"rp": "",
"contact": "Odul",
"settings": false,
"section": [
"game"
],
"category": [
"mailing"
],
"experimental": false
},
{
"id": "messagerietitreperso",
"name": "Messagerie : Titres Perso",
"description": "Personnalisation des titres et des avatars d'aperçu, sur la messagerie.",
"authors": "Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/415294/Messagerie%20:%20Titres%20Perso.user.js?version=1559798",
"doc": "",
"rp": "",
"contact": "Pelagia",
"settings": false,
"section": [
"game"
],
"category": [
"mailing"
],
"experimental": false
},
{
"id": "lockddmail",
"name": "LockD&DMail",
"description": "Bloque le drag and drop sur la messagerie.",
"authors": "Odul, Pelagia",
"icon": "",
"url": "https://raw.githubusercontent.com/Isilin/dreadcast-scripts/main/src/scripts/messagerie/lockd%26dmail.js",
"doc": "",
"rp": "",
"contact": "Odul, Pelagia",
"settings": false,
"section": [
"game"
],
"category": [
"mailing"
],
"experimental": false
},
{
"id": "mailprotect",
"name": "DC - Mail Protect",
"description": "Ajoute un mode permettant de demander la confirmation avant de fermer un message en cours de réaction.",
"authors": "Nasty, Odul, Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/480707/DC%20-%20Mail%20Protect.user.js?version=1285527",
"doc": "",
"rp": "",
"contact": "Nasty, Odul",
"settings": false,
"section": [
"game"
],
"category": [
"mailing"
],
"experimental": false
},
{
"id": "roll",
"name": "Roll",
"description": "Faire des jets avec prise en compte des stats via des compétences.",
"authors": "Odul, Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/447640/Roll.user.js?version=1070850",
"doc": "https://www.dreadcast.net/Forum/2-56453-script-roll#1",
"rp": "",
"contact": "Odul",
"settings": false,
"section": [
"game"
],
"category": [
"chat"
],
"experimental": false
},
{
"id": "dcce",
"name": "DreadCast Chat Enhancer",
"description": "Il ajoute de nombreuses fonctionnalités pour le chat et des options de customisation.",
"authors": "Valion, Odul, Ladoria, Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/21359/Dreadcast%20Chat%20Enhancer.user.js?version=1068608",
"doc": "https://www.dreadcast.net/Forum/2-67353-script-dc-chat-enhancer?5",
"rp": "",
"contact": "Odul, Pelagia",
"settings": false,
"section": [
"game"
],
"category": [
"chat"
],
"experimental": false
},
{
"id": "chatinputdc",
"name": "ChatInputDC",
"description": "Automatically 'closing' the following characters in the chat input area: \\", *, [, (, or {.",
"authors": "Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/388245/ChatInputDC.user.js?version=722725",
"doc": "",
"rp": "",
"contact": "Pelagia",
"settings": false,
"section": [
"game"
],
"category": [
"chat"
],
"experimental": false
},
{
"id": "skinsilhouette",
"name": "SkinSilhouette",
"description": "Voir des silhouettes personnalisées.",
"authors": "Odul, Lorkah, Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/6081/SkinSilhouette.user.js?version=1271247",
"doc": "",
"rp": "",
"contact": "Odul",
"settings": false,
"section": [
"game"
],
"category": [
"silhouette"
],
"experimental": false
},
{
"id": "showsilhouettebyisilin",
"name": "ShowSilhouette - By Isilin",
"description": "Un skin réorganisant l'inventaire pour mieux voir les silhouettes.",
"authors": "Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/415259/ShowSilhouette%20-%20By%20Isilin.user.js?version=864933",
"doc": "",
"rp": "",
"contact": "Pelagia",
"settings": false,
"section": [
"game"
],
"category": [
"silhouette"
],
"experimental": false
},
{
"id": "showsilhouettevalion",
"name": "Show Silhouette Valion",
"description": "Un skin réorganisant l'inventaire pour mieux voir les silhouettes.",
"authors": "Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/415246/Show%20Silhouette%20Valion.user.js?version=864762",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"silhouette"
],
"experimental": false
},
{
"id": "stopmultiplemessageforumonly",
"name": "StopMultipleMessageForumOnly",
"description": "Empêche les messages doublons accidentels sur le forum.",
"authors": "Kmaschta, Valion, Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/508039/StopMultipleMessageForumOnly.user.js?version=1446184",
"doc": "",
"rp": "",
"contact": "Pelagia",
"settings": false,
"section": [
"forum"
],
"category": [
"fix"
],
"experimental": false
},
{
"id": "séparationsujets",
"name": "Séparation sujets",
"description": "Separe le RP du HRP dans la section 'Derniers Sujets",
"authors": "Aversiste, MockingJay, Odul, Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/508226/S%C3%A9paration%20sujets.user.js?version=1448853",
"doc": "",
"rp": "",
"contact": "Odul, Pelagia",
"settings": false,
"section": [
"forum"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "breakinglinecode",
"name": "BreakingLineCode",
"description": "Ajoute un retour à la ligne automatique pour la balise code.",
"authors": "Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/493103/BreakingLineCode.user.js?version=1363923",
"doc": "https://www.dreadcast.net/Forum/2-108077-themefix-dc-forum-fix?1#2",
"rp": "",
"contact": "Pelagia",
"settings": false,
"section": [
"forum"
],
"category": [
"fix"
],
"experimental": false
},
{
"id": "mapoverlay",
"name": "DC Map Overlay",
"description": "Modification de la carte initiale dans DC, par remplacement et/ou ajout d'une surcouche.",
"authors": "Radium",
"icon": "",
"url": "https://update.greasyfork.org/scripts/394964/DC_MapOverlay%20fouilleur.user.js?version=764590",
"doc": "https://www.dreadcast.net/Forum/2-83878-script-dc_mapoverlay?1",
"rp": "",
"contact": "Radium",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "nightmode",
"name": "DC Nightmode",
"description": "Un script qui enlève certains éléments graphiques pour un thème plus sombre.",
"authors": "Lorkah",
"icon": "",
"url": "https://update.greasyfork.org/scripts/370999/DC%20-%20Nightmode.user.js?version=945864",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "datefixed",
"name": "DC - Date fixed",
"description": "Affiche la date en jeu, et des commandes sur le deck.",
"authors": "Ianouf, Ladoria, Nasty",
"icon": "",
"url": "https://update.greasyfork.org/scripts/453281/DC%20-%20Date%20fixed.user.js?version=1106311",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"mech"
],
"experimental": false
},
{
"id": "compteursac",
"name": "Compteur Sac",
"description": "Affiche sur le sac le nombre d'objets qui y est. Se rafraichit toutes les 3 secondes.",
"authors": "Anon, Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/25490/Compteur%20Sac.user.js?version=750393",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "blockuse",
"name": "DC - BlockUse",
"description": "Bloque l'utilisation de consommables par défaut. Faut déverouiller le petit cadenas 'BU' pour pouvoir.",
"authors": "Odul, Lorkah",
"icon": "",
"url": "https://update.greasyfork.org/scripts/412356/DC%20-%20BlockUse%20Fixed.user.js?version=888873",
"doc": "",
"rp": "",
"contact": "Odul",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "autorefresh",
"name": "DC - AutoRefresh",
"description": "Tente automatiquement de rafraîchir le jeu si la connexion est perdue, et affiche le ping du serveur.",
"authors": "Ladoria, Lorkah",
"icon": "",
"url": "https://update.greasyfork.org/scripts/415271/DC%20-%20AutoRefresh%20Fixed.user.js?version=864870",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"fix"
],
"experimental": false
},
{
"id": "bulletsalert",
"name": "Bullets Alert",
"description": "Joue un son lorsque votre chargeur est vide.",
"authors": "Ladoria, Lorkah",
"icon": "",
"url": "https://update.greasyfork.org/scripts/404024/DC%20-%20Bullets_alert_fixed.user.js?version=888872",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"mech"
],
"experimental": false
},
{
"id": "bougetafeuille",
"name": "DC - Bouge ta feuille",
"description": "Rend la lecture plus agréable en permettant de déplacer plus facilement une feuille.",
"authors": "Nasty",
"icon": "",
"url": "https://update.greasyfork.org/scripts/453476/DC%20-%20Bouge%20ta%20feuille.user.js?version=1107622",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "copyterminal",
"name": "Copy Terminal",
"description": "Copie rapidement le contenu d'une page du TP (STV, Cryo, Historiques des entreprises et OI).",
"authors": "RedLine",
"icon": "",
"url": "https://update.greasyfork.org/scripts/486524/copyTerminal.user.js?version=1322828",
"doc": "",
"rp": "",
"contact": "Redline",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "neuvopackoverhaul",
"name": "Neuvopack Overhaul",
"description": "Modifie l'interface des neuvopacks pour un usage plus agréable (suivi en direct du contenu, extraction des condensés rapide).",
"authors": "Altaïr",
"icon": "https://www.dreadcast.net/images/objets/NEUVOPACK_262px.png",
"url": "https://update.greasyfork.org/scripts/468141/Neuvopack%20Overhaul.user.js?version=1491022",
"doc": "https://www.dreadcast.net/Forum/2-144257-script-neuvopack-overhaul?1",
"rp": "",
"contact": "Altaïr",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "tpoverhaul",
"name": "TP Overhaul",
"description": "Modernisation de l'interface du Terminal Portable.",
"authors": "Altaïr",
"icon": "https://www.dreadcast.net/images/objets/mini/TERMINAL%20PORTABLE2.png",
"url": "https://update.greasyfork.org/scripts/488053/TP%20Overhaul.user.js?version=1558625",
"doc": "https://www.dreadcast.net/Forum/2-145540-script-tp-overhaul",
"rp": "",
"contact": "Altaïr",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "visio3d",
"name": "Visio 3D",
"description": "Utilitaire pour récupérer les plans et FW d'un bâtiment.",
"authors": "Pelagia",
"icon": "https://www.dreadcast.net/images/objets/decker-casque2.png",
"url": "https://update.greasyfork.org/scripts/481981/Visio%203D.user.js?version=1533889",
"doc": "https://www.dreadcast.net/Forum/2-144695-ns-v13612u-visio-3d---logiciel-utilitaire-pour-la-decoration-dinterieur?1",
"rp": "https://www.dreadcast.net/Forum/2-144695-ns-v13612u-visio-3d---logiciel-utilitaire-pour-la-decoration-dinterieur?1",
"contact": "Pelagia",
"settings": true,
"section": [
"game"
],
"category": [
"ui",
"mech"
],
"experimental": false
},
{
"id": "eccb",
"name": "Editeur de Commentaires de Conteneurs en Banque",
"description": "Permet de mettre un commentaire sur ses sacs en banque pour mieux s'y retrouver!",
"authors": "Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/488837/ECCB.user.js?version=1337142",
"doc": "https://www.dreadcast.net/Forum/2-144702-ns-e13612u-eccb---editeur-de-commentaires-de-conteneurs-en-banque?1",
"rp": "https://www.dreadcast.net/Forum/2-136024--aequilibrium-financial-#10",
"contact": "Pelagia",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"mech"
],
"experimental": false
},
{
"id": "autostock",
"name": "AutoStock",
"description": "Permet le réajustement des stocks en vente ou les prix dans l'usine de production pour DC.",
"authors": "Amane-Mochizuki",
"icon": "",
"url": "https://update.greasyfork.org/scripts/493495/DC%20-%20AutoStock%20%28channel%20Beta%29.user.js?version=1366383",
"doc": "https://www.dreadcast.net/Forum/2-145375-script-autostock?1",
"rp": "",
"contact": "Amane-Mochizuki",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"mech"
],
"experimental": false
},
{
"id": "silmerionwindowfix",
"name": "Silmerion Window Fix",
"description": "Règle le problème faisant que la fenêtre du Silmerion empêche le clic ailleurs sur la page.",
"authors": "Arrakis",
"icon": "",
"url": "https://update.greasyfork.org/scripts/371206/Silmerion%20Window%20Fix.user.js?version=641774",
"doc": "",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"fix"
],
"experimental": false
},
{
"id": "skwikker",
"name": "Skwikker",
"description": "Pour accéder au célèbre réseau social Skwikker.",
"authors": "Valion",
"icon": "",
"url": "https://update.greasyfork.org/scripts/30182/Skwikker.user.js?version=201187",
"doc": "https://www.dreadcast.net/Forum/2-77724-script-skwikker",
"rp": "",
"contact": "",
"settings": false,
"section": [
"forum",
"edc"
],
"category": [
"mech"
],
"experimental": false
},
{
"id": "silhouettePlus",
"name": "Silhouette+",
"description": "Pour personnaliser entièrement sa feuille RP avec silhouette et plus. Réunit les scripts SkinSilhouette et ShowSilhouette.",
"authors": "Isilin/Pelagia",
"icon": "",
"url": "https://raw.githubusercontent.com/Isilin/dreadcast-scripts/main/src/scripts/silhouette/silhouette-plus.js",
"doc": "https://www.dreadcast.net/Forum/2-150257-script-silhouette",
"rp": "",
"contact": "Pelagia",
"settings": true,
"section": [
"game"
],
"category": [
"silhouette"
],
"experimental": false
},
{
"id": "vitrineStock",
"name": "Vitrine vs Stocks",
"description": "Check stock et vitrine / Check quantité",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/527017/Vitrine%20vs%20Stocks.user.js?version=1584063",
"doc": "https://greasyfork.org/en/scripts/527017-vitrine-vs-stocks",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"mech"
],
"experimental": false
},
{
"id": "objectLinking",
"name": "Partage d'objet chat",
"description": "Permet de partager un objet dans le chat facilement",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/528245/Partage%20d%27objet%20chat.user.js?version=1583032",
"doc": "https://greasyfork.org/en/scripts/528245-partage-d-objet-chat",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"chat"
],
"experimental": false
},
{
"id": "copyPasteAll",
"name": "Copy Paste all",
"description": "Ajoute un bouton qui vous permet de copier tous les posts d'une page forum dans votre presse-papier.",
"authors": "Silly",
"icon": "",
"url": "https://update.greasyfork.org/scripts/529290/%5BDC%5D%20Copy%20Paste%20All%20-%20Forum.user.js?version=1550441",
"doc": "https://www.dreadcast.net/Forum/2-151693-script-copy-paste-all---forum",
"rp": "",
"contact": "Silly",
"settings": false,
"section": [
"forum"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "panFenestra",
"name": "P.A.N. Fenestra",
"description": "Un nouveau système d'exploitation pour votre espace matriciel personnel.",
"authors": "Isilin/Pelagia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/530381/PAN%20Fenestra.user.js?version=1557231",
"doc": "https://www.dreadcast.net/Forum/2-152039-script-pan-fenestra?1",
"rp": "",
"contact": "Pelagia",
"settings": false,
"section": [
"forum"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "deckExportData",
"name": "DeckExportData",
"description": "Permet de copier les données du deck",
"authors": "Amane-Mochizuki",
"icon": "https://i.imgur.com/Wn8zYJ8.png",
"url": "https://update.greasyfork.org/scripts/530389-dc-deckexportdata.user.js?version=1563381",
"doc": "https://www.dreadcast.net/Forum/2-152205-script-deck-export-data?1",
"rp": "",
"contact": "Amane-Mochizuki",
"settings": false,
"section": [
"game"
],
"category": [
"fix"
],
"experimental": false
},
{
"id": "fixUsine",
"name": "Fix Usine",
"description": "Empêche la fenêtre d'usine de se fermer quand on achète",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/527127-fix-usine.user.js?version=1554902",
"doc": "",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"fix"
],
"experimental": false
},
{
"id": "holovision",
"name": "HoloVision",
"description": "Historique chat malléable",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/532962/Holovision.user.js?version=1573401",
"doc": "https://www.dreadcast.net/Forum/2-152896-script-holovision?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"chat"
],
"experimental": false
},
{
"id": "neuvosmart",
"name": "NeuvoSmart",
"description": "Son quand le neuvopack est plein / UI de suivi",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/544413/NeuvoSmart.user.js?version=1634380",
"doc": "https://www.dreadcast.net/Forum/2-155651-script-neuvosmart?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"mech"
],
"experimental": false
},
{
"id": "dcmobilefix",
"name": "DC Mobile Fix",
"description": "Divers fix mobile",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/537990/Mobile%20DC%20Fix.user.js?version=1600207",
"doc": "https://www.dreadcast.net/Forum/2-155650-script-dc-mobile---fix?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"fix"
],
"experimental": true
},
{
"id": "aitlDuPremierCoup",
"name": "AITL - Du premier coup ! ",
"description": "Preview de l'annonce AITL",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/533831/AITL%20-%20Du%20premier%20coup%20%21.user.js?version=1677497",
"doc": "https://www.dreadcast.net/Forum/2-153038-script-aitl---du-premier-coup?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "details",
"name": "Details!",
"description": "Log de combat détaillé",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/535028/Details%21.user.js?version=1687389",
"doc": "https://www.dreadcast.net/Forum/2-153293-scripts-details?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "chatBulle",
"name": "ChatBulle",
"description": "Affiche une bulle de tchat au-dessus des personnages",
"authors": "Asalia",
"icon": "",
"url": "https://update.greasyfork.org/scripts/554530/ChatBulle.user.js?version=1696150",
"doc": "https://www.dreadcast.net/Forum/2-158092-script-chatbulle?1",
"rp": "",
"contact": "Asalia",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"chat"
],
"experimental": false
},
{
"id": "dcnotif",
"name": "DC Notif'",
"description": "Notifications traitées dans une interface",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/534899/DC%20Notif%27.user.js?version=1590384",
"doc": "https://www.dreadcast.net/Forum/2-153271-script-dc-notif?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"ui",
"fix"
],
"experimental": false
},
{
"id": "pimpmypion",
"name": "Pimp My Pion",
"description": "Remplace les pions bleus par les avatars des joueurs et ajoute des paramètres de personnalisation",
"authors": "Darlene",
"icon": "",
"url": "https://update.greasyfork.org/scripts/556113/Dreadcast%20-%20PimpMyPion%20-%20Public%20v%20063.user.js?version=1709845",
"doc": "https://www.dreadcast.net/Forum/2-158238-script-pimpmypion?1",
"rp": "",
"contact": "Darlene",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "fouilletracker",
"name": "DC Fouille Tracker",
"description": "Minuteur fouille draggable, lock/unlock, historique déroulant, vide de l'historique, copier de l'historique (thème bleu)",
"authors": "Yenzelle",
"icon": "",
"url": "https://update.greasyfork.org/scripts/557196/DC%20-%20Fouille%20Tracker%20%28Bleu%29.user.js?version=1703636",
"doc": "https://www.dreadcast.net/Forum/2-158505-script-dc-fouille-tracker?1",
"rp": "",
"contact": "",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "ddm",
"name": "DC Dynamic Message V2",
"description": "Messagerie version dynamique et réactive",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/557947/DDM%20V2.user.js?version=1800579",
"doc": "https://www.dreadcast.net/Forum/2-158665-script-ddm-v2?4",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": true
},
{
"id": "fpl",
"name": "FPLive",
"description": "Notifs en jeu lorsqu'un FP suivi reçoit un message",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/565766/FPLive.user.js?version=1752030",
"doc": "https://www.dreadcast.net/Forum/2-159824-script-fpl?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game",
"forum"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "igforum",
"name": "IGForum",
"description": "Permet d'accéder au forum depuis le jeu",
"authors": "Ciel",
"icon": "",
"url": "https://update.greasyfork.org/scripts/565953/IGForum.user.js?version=1752886",
"doc": "https://www.dreadcast.net/Forum/2-159840-script-igforum?1",
"rp": "",
"contact": "Ciel",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "gpsrouge",
"name": "GPS Rouge",
"description": "Remplace la flèche bleue du GPS par une flèche rouge.",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/532463/GPS%20Rouge.user.js?version=1571171",
"doc": "https://www.dreadcast.net/Forum/2-152684-script---sommaire-lain?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"ui"
],
"experimental": false
},
{
"id": "kobsteak",
"name": "Kobsteak!",
"description": "Un snake à l'intérieur de Dreadcast. Quand l'autre met trop de temps à répondre pendant un RPQ.",
"authors": "Laïn",
"icon": "",
"url": "https://update.greasyfork.org/scripts/531236/KobSteak%21.user.js?version=1571171",
"doc": "https://www.dreadcast.net/Forum/2-152684-script---sommaire-lain?1",
"rp": "",
"contact": "Laïn",
"settings": false,
"section": [
"game"
],
"category": [
"mech"
],
"experimental": false
}
]
`);
// === DCSM_FALLBACK_END ===
$(() => {
// To check if a script is used in a DSM context.
Util.isDSM = () => true;
const LIST_TAG = 'dcsm_list';
const ALL_DISABLED_TAG = 'dcsm_all_disabled';
const INTRO_TAG = 'dcsm_intro_disabled';
const DEV_MODE_TAG = 'dcsm_dev_mode';
const CACHE_TAG = 'dcsm_scripts_cache';
const LIST_URL =
'https://raw.githubusercontent.com/Isilin/dreadcast-scripts/main/data/scripts.json';
const CACHE_TTL = 60 * 60 * 1000; // 1 hour.
const FETCH_TIMEOUT = 8000;
let settings, allDisabled, introDisabled, devMode;
let newSettings, newAllDisabled, newDevMode;
// ===== CORE =====
const initPersistence = () => {
// Init persistent memory if needed.
DC.LocalMemory.init(LIST_TAG, {});
DC.LocalMemory.init(ALL_DISABLED_TAG, false);
DC.LocalMemory.init(INTRO_TAG, false);
DC.LocalMemory.init(DEV_MODE_TAG, false);
// TODO to delete at next major version.
if (DC.LocalMemory.get('dcm_list') !== undefined) {
DC.LocalMemory.set(LIST_TAG, DC.LocalMemory.get('dcm_list'));
DC.LocalMemory.delete('dcm_list');
}
if (DC.LocalMemory.get('dcm_all_disabled') !== undefined) {
DC.LocalMemory.set(
ALL_DISABLED_TAG,
DC.LocalMemory.get('dcm_all_disabled'),
);
DC.LocalMemory.delete('dcm_all_disabled');
}
// Load the current settings.
settings = DC.LocalMemory.get(LIST_TAG);
allDisabled = DC.LocalMemory.get(ALL_DISABLED_TAG);
introDisabled = DC.LocalMemory.get(INTRO_TAG);
devMode = DC.LocalMemory.get(DEV_MODE_TAG);
};
const isValidList = (list) =>
Util.isArray(list) &&
list.length > 0 &&
list.every(
(script) =>
Util.isObject(script) &&
Util.isString(script.id) &&
script.id !== '' &&
Util.isString(script.url) &&
script.url !== '' &&
Util.isArray(script.section) &&
Util.isArray(script.category),
);
// Undefined when there is nothing usable in persistent memory: a missing
// cache and a corrupted one are handled the same way.
const readCache = () => {
const cache = DC.LocalMemory.get(CACHE_TAG);
if (
!Util.isObject(cache) ||
!Number.isFinite(cache.ts) ||
!isValidList(cache.scripts)
) {
return undefined;
}
return cache;
};
const writeCache = (scripts) => {
// Single entry rather than two keys, so the list and its timestamp can
// never end up out of sync.
const cache = { ts: Date.now(), scripts };
DC.LocalMemory.set(CACHE_TAG, cache);
return cache;
};
const fetchList = async () => {
// 'timeout' is forwarded as is to GM_xmlhttpRequest, whose 'ontimeout' is
// wired to reject: without it, an host that hangs would leave the request
// pending forever and nothing would ever be loaded.
const list = await DC.Network.fetch({
method: 'GET',
url: LIST_URL,
headers: { 'Content-Type': 'application/json' },
responseType: 'json',
timeout: FETCH_TIMEOUT,
});
// The request resolves whatever the HTTP status is, so a 4xx/5xx comes back
// here as null or as an error page: only the payload tells us the truth.
if (!isValidList(list)) {
throw new Error('the remote list is empty or malformed');
}
return list;
};
// Cache younger than an hour, else remote list, else cache whatever its age,
// else the copy embedded in this userscript.
const resolveList = async () => {
const cache = readCache();
if (cache !== undefined && Date.now() - cache.ts < CACHE_TTL) {
console.info('DCSM - List of scripts loaded from the cache.');
return { scripts: cache.scripts, source: 'cache', ts: cache.ts };
}
try {
const scripts = await fetchList();
console.info('DCSM - List of scripts updated from the remote source.');
return { scripts, source: 'remote', ts: writeCache(scripts).ts };
} catch (err) {
if (cache !== undefined) {
// The timestamp is left untouched, so the update is attempted again on
// the next page load instead of being held off for a full hour.
console.warn(
'DCSM - Error updating the list of scripts, keeping the cached one: ' +
err,
);
return { scripts: cache.scripts, source: 'cache-stale', ts: cache.ts };
}
if (!isValidList(DCSM_FALLBACK_LIST)) {
throw new Error('no cache, no remote list and no usable embedded list');
}
console.error(
'DCSM - Error loading the list of scripts, using the embedded one: ' +
err,
);
return {
scripts: DCSM_FALLBACK_LIST,
source: 'embedded',
ts: writeCache(DCSM_FALLBACK_LIST).ts,
};
}
};
// 'prune' should only be true when 'scripts' comes from the remote list. On a
// degraded list (cache or embedded fallback), removing the unknown entries
// would silently drop the settings of the scripts missing from it.
const synchronizeSettings = (settings, scripts, prune) => {
let tmp = settings;
scripts.forEach((script) => {
if (!Object.hasOwn(tmp, script.id)) {
// Update the settings, if there is new scripts.
tmp[script.id] = false;
}
});
if (prune) {
// Remove in settings, scripts that doesn't exist anymore.
tmp = Object.keys(tmp)
.filter(
(key) => scripts.find((script) => script.id === key) !== undefined,
)
.reduce((obj, key) => {
obj[key] = tmp[key];
return obj;
}, {});
}
// Save the new settings in persistent memory.
DC.LocalMemory.set(LIST_TAG, tmp);
return tmp;
};
const createScriptLine = (script, index) => {
const line = $(`
<tr style="border-top: 1px solid white; border-left: 1px solid white; border-right: 1px solid white;">
<td style="padding: 5px 0 0 5px" rowspan="2">${index}</td>
${
script.icon && script.icon !== ''
? `<td style="padding: 5px" rowspan="2"><img src="${script.icon}" width="48" height="48" /></td>`
: '<td class="short" style="width: 58px;" rowspan="2" />'
}
<td style="padding: 5px 0; min-width: 120px; text-align: left;">${
script.experimental ? '<span style="color: red;">[DEV]</span>' : ''
} ${script.name || ''}</td>
<td style="padding: 5px 0; min-width: 120px; text-align: left;"><small>${
script.authors || ''
}</small></td>
<td class="enabled_cell" style="padding: 5px 0; display: flex; justify-content: center;"></td>
<td class="setting_cell" style="padding: 5px 5px 0 0;"></td>
<td class="doc_cell" style="padding: 5px 5px 0 0;"></td>
<td class="rp_cell" style="padding: 5px 5px 0 0;"></td>
<td class="contact_cell" style="padding: 5px 5px 0 0;"></td>
</tr>
<tr style="border-bottom: 1px solid white; border-left: 1px solid white; border-right: 1px solid white;">
<td colspan="7" style="padding: 0 5px 5px 5px; text-align: left;"><small><em class="couleur5">${
script.description || ''
}</em></small></td>
</tr>
`);
$('.enabled_cell', line).append(
DC.UI.Tooltip(
'Activer/Désactiver le script ne perdra pas sa configuration.',
DC.UI.Checkbox(
`${script.id}_check`,
newSettings[script.id],
() => (newSettings[script.id] = !newSettings[script.id]),
),
),
);
if (script.settings) {
$('.setting_cell', line).append(
DC.UI.Tooltip(
'Settings',
DC.UI.Button(
`${script.id}_setting`,
'<i class="fas fa-cog"></i>',
() => {},
),
),
);
}
if (script.doc && script.doc !== '') {
$('.doc_cell', line).append(
DC.UI.Tooltip(
'Documentation',
DC.UI.Button(`${script.id}_doc`, '<i class="fas fa-book"></i>', () =>
window.open(script.doc, '_blank'),
),
),
);
}
if (script.rp && script.rp !== '') {
$('.rp_cell', line).append(
DC.UI.Tooltip(
'Topic RP',
DC.UI.Button(
`${script.id}_rp`,
'<div class=""gridCenter>RP</div>',
() => window.open(script.doc, '_blank'),
),
),
);
}
if (script.contact && script.contact !== '') {
$('.contact_cell', line).append(
DC.UI.Tooltip(
'Contact',
DC.UI.Button(
`${script.id}_rp`,
'<i class="fas fa-envelope"></i>',
() => nav.getMessagerie().newMessage(script.contact),
),
),
);
}
return line;
};
const createIntro = () => {
if (Util.isGame() && !introDisabled) {
introDisabled = true;
DC.LocalMemory.set(INTRO_TAG, introDisabled);
DC.UI.PopUp(
'dcsm_intro',
'Bienvenue sur le Dreadcast Script Manager !',
$(`
<div style="color: white;">
<h3>Merci d'avoir installé le DreaCast Script Manager (DCSM).</h3><br />
<p>Cet utilitaire va vous permettre de gérer vos scripts directement en jeu. Pensez à désactiver/désinstaller dans votre GreaseMonkey/TamperMonkey (ou équivalent), les scripts que vous activerez dans le DCSM, pour éviter des doublons.</p><br />
<p>La suite se passe dans Paramètres > Scripts & Skins.</p><br />
<p>Vous pourrez obtenir des réponses à vos questions sur le <a href="https://github.com/Isilin/dreadcast-scripts/wiki">wiki</a>, sur le forum, ou en me contactant directement par Com' HRP : (<em>JD Pelagia</em>).</p><br />
<p>Bon jeu ! (Vous ne verrez plus cette fenêtre d'information par la suite).</p>
</div>
`),
);
}
};
// Tells where the displayed list comes from, so a missing script can be told
// apart from a script that is simply disabled.
const createListStatus = (meta) => {
const date = new Date(meta.ts).toLocaleString('fr-FR', {
dateStyle: 'short',
timeStyle: 'short',
});
const messages = {
remote: `Liste à jour (${date}).`,
cache: `Liste en cache (${date}).`,
'cache-stale': `⚠ Source injoignable : liste en cache du ${date}.`,
embedded:
'⚠ Source injoignable : liste de secours embarquée, potentiellement incomplète.',
};
const degraded =
meta.source === 'cache-stale' || meta.source === 'embedded';
const decoration = degraded ? 'style="color: red;"' : 'class="couleur5"';
return $(
`<p style="margin-bottom: 1rem;"><small><em ${decoration}>${
messages[meta.source]
}</em></small></p>`,
);
};
const createUI = (scripts, settings, meta) => {
DC.UI.addSubMenuTo(
'Paramètres ▾',
DC.UI.SubMenu(
'Scripts & Skins',
() => {
// On récupère une config temporaire qu'on appliquera uniquement si sauvegardée.
newSettings = settings;
newAllDisabled = allDisabled;
newDevMode = devMode;
const sections = [
{ id: 'all', label: 'Tous' },
{ id: 'game', label: 'Jeu' },
{ id: 'forum', label: 'Forum' },
{ id: 'edc', label: 'EDC' },
];
const categories = [
{ id: 'all', label: 'Tous' },
{ id: 'mailing', label: 'Messagerie' },
{ id: 'chat', label: 'Chat' },
{ id: 'silhouette', label: 'Silhouette' },
{ id: 'ui', label: 'UI' },
{ id: 'mech', label: 'Mécaniques' },
{ id: 'fix', label: 'Correctifs' },
{ id: 'misc', label: 'Autres' },
];
const content = $(`<div style="color: white;">
<div id="developper_mode_switch" style="display: flex; justify-content: flex-begin;gap: 1rem;margin-bottom: 1rem;">
<p>Mode développeur</p>
</div>
<div style="display: flex; justify-content: space-between">
<div id="scripts_all_switch" style="display: flex;gap: 1rem;margin-bottom: 1rem;">
<p>Tout désactiver</p>
</div>
<div style="display: flex; gap: 1rem; margin-bottom: 1rem;">
<label for="search_script">Recherche</label>
<input id="search_script" name="search_script" type="text" size="50" style="color: white;" />
</div>
</div>
<div style="display: flex; gap: 1rem; margin-bottom: 1rem;">
<legend style="margin-right: 1rem; min-width: 60px;">Filtrer :</legend>
<div style="display: flex; gap: 5%; flex-wrap: wrap; width: 100%;">
${sections
.map(
(section, index) => `
<div>
<input type="radio" id="${
section.id
}_section" name ="section" value ="${section.id}" ${
index === 0 ? 'checked' : ''
} />
<label for="${section.id}_section">${section.label}</label>
</div>
`,
)
.join('')}
</div>
</div>
<div style="display: flex; gap: 1rem; margin-bottom: 1rem;">
<legend style="margin-right: 1rem; min-width: 60px;">Filtrer :</legend>
<div style="display: flex; gap: 5%; flex-wrap: wrap; width: 100%;">
${categories
.map(
(category, index) => `
<div>
<input type="radio" id="${
category.id
}_category" name ="category" value ="${category.id}" ${
index === 0 ? 'checked' : ''
} />
<label for="${category.id}_category">${
category.label
}</label>
</div>
`,
)
.join('')}
</div>
</div>
<div style="overflow-y: scroll; overflow-x: hidden; max-height: 350px;">
<table style="border-collapse: collapse; width: 100%; border: 1px solid white; padding: 5px; font-size: 15px; text-align: center;">
<thead>
<th style="padding: 5px 0 5px 5px" scope="col">#</th>
<th class="short" style="width:58px;" />
<th style="padding: 5px 0 5px 0" scope="col">Nom</th>
<th style="padding: 5px 0 5px 0" scope="col">Auteurs</th>
<th style="padding: 5px 0 5px 0" scope="col">Actif</th>
<th class="short" style="width: 40px;" />
<th class="short" style="width: 40px;" />
<th class="short" style="width: 40px;" />
<th class="short" style="width: 40px;" />
</thead>
<tbody></tbody>
</table>
</div>
</div>`);
content.prepend(createListStatus(meta));
$(document).on('change', "input[name='category']", (e) => {
const category = e.target.value;
const section = $("input[name='section']:checked").val();
const search = $("input[name='search_script']").val().toLowerCase();
// Empty the table
$('tbody', content).empty();
// Add filtered lines
scripts
.filter(
(script) =>
(script.section.includes(section) || section === 'all') &&
(script.category.includes(category) || category === 'all') &&
(script.name.toLowerCase().includes(search) ||
script.description.toLowerCase().includes(search)),
)
.forEach((script, index) => {
const line = createScriptLine(script, index);
$('tbody', content).append(line);
});
});
$(document).on('change', "input[name='section']", (e) => {
const section = e.target.value;
const category = $("input[name='category']:checked").val();
const search = $("input[name='search_script']").val().toLowerCase();
// Empty the table
$('tbody', content).empty();
// Add filtered lines
scripts
.filter(
(script) =>
(script.section.includes(section) || section === 'all') &&
(script.category.includes(category) || category === 'all') &&
(script.name.toLowerCase().includes(search) ||
script.description.toLowerCase().includes(search)),
)
.forEach((script, index) => {
const line = createScriptLine(script, index);
$('tbody', content).append(line);
});
});
$(document).on('input', "input[name='search_script']", (e) => {
const search = e.target.value.toLowerCase();
const category = $("input[name='category']:checked").val();
const section = $("input[name='section']:checked").val();
// Empty the table
$('tbody', content).empty();
// Add filtered lines
scripts
.filter(
(script) =>
(script.section.includes(section) || section === 'all') &&
(script.category.includes(category) || category === 'all') &&
(script.name.toLowerCase().includes(search) ||
script.description.toLowerCase().includes(search)),
)
.forEach((script, index) => {
const line = createScriptLine(script, index);
$('tbody', content).append(line);
});
});
// Sauvegarder les paramètres.
content.append(
DC.UI.TextButton('scripts_refresh', 'Sauvegarder', () => {
settings = newSettings;
allDisabled = newAllDisabled;
devMode = newDevMode;
DC.LocalMemory.set(LIST_TAG, settings);
DC.LocalMemory.set(ALL_DISABLED_TAG, allDisabled);
DC.LocalMemory.set(DEV_MODE_TAG, devMode);
location.replace('https://www.dreadcast.net/Main'); // Better than reload() with Chrome.
}),
);
content.append(
$(
`<p><em class="couleur5">⚠ Sauvegarder votre configuration va raffraichir la page.<br />
Pensez à sauvegarder votre travail en cours avant.</em></p>`,
),
);
const resetConfig = () => {
const list = DC.LocalMemory.list();
list.forEach((key) => {
DC.LocalMemory.delete(key);
});
};
// Import/Export
content.append(
$(
'<div id="config_buttons" style="display: flex; justify-content: end; gap: 1rem; margin-bottom: 1rem;"></div>',
),
);
$('#config_buttons', content).append(
DC.UI.TextButton(
'config_reset',
'<i class="fas fa-undo"></i> Réinitialiser',
() => {
resetConfig();
location.replace('https://www.dreadcast.net/Main');
},
),
);
$('#config_buttons', content).append(
DC.UI.TextButton(
'config_import',
'<i class="fas fa-upload"></i> Importer la configuration',
() => {
resetConfig();
const anchor = document.createElement('input');
anchor.style.display = 'none';
anchor.type = 'file';
anchor.accept = 'application.json';
anchor.onchange = (e) => {
var reader = new FileReader();
reader.onload = (e) => {
const data = JSON.parse(e.target.result);
Object.keys(data)
// Never import someone else's cached list: it would be
// restored with their timestamp and used as ours.
.filter((key) => key !== CACHE_TAG)
.forEach((key) => {
DC.LocalMemory.set(key, data[key]);
});
};
reader.readAsText(e.target.files[0]);
document.body.removeChild(anchor);
location.replace('https://www.dreadcast.net/Main');
};
document.body.appendChild(anchor);
anchor.click();
},
),
);
$('#config_buttons', content).append(
DC.UI.TextButton(
'config_export',
'<i class="fas fa-download"></i> Exporter la configuration',
function () {
const list = DC.LocalMemory.list();
let data = {};
// The cached list is rebuilt on demand: it has nothing to do in
// a configuration file, and would only bloat it.
list
.filter((key) => key !== CACHE_TAG)
.forEach((key) => {
data[key] = DC.LocalMemory.get(key);
});
const anchor = document.createElement('a');
anchor.style.display = 'none';
anchor.href = URL.createObjectURL(
new Blob([JSON.stringify(data)], {
type: 'application/json',
}),
);
anchor.download = 'dcsm_config.json';
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
},
),
);
// Switch button pour désactiver tous les scripts.
$('#scripts_all_switch', content).append(
DC.UI.Checkbox(
'scripts_all_check',
newAllDisabled,
() => (newAllDisabled = !newAllDisabled),
),
);
// Switch button pour le développeur mode.
$('#developper_mode_switch', content).append(
DC.UI.Tooltip(
'Attention, ces scripts sont encore en développement !',
DC.UI.Checkbox(
'developper_mode_check',
newDevMode,
() => (newDevMode = !newDevMode),
),
),
);
scripts
.filter((script) => devMode || !script.experimental)
.forEach((script, index) => {
const line = createScriptLine(script, index);
$('tbody', content).append(line);
});
return DC.UI.PopUp('scripts_modal', 'Scripts & Skins', content);
},
true,
),
5,
);
};
// ===============
$(document).ready(() => {
initPersistence();
createIntro();
// Load list of scripts
resolveList()
.then(({ scripts, source, ts }) => {
settings = synchronizeSettings(settings, scripts, source === 'remote');
// Create the interface.
if (Util.isGame()) {
createUI(scripts, settings, { source, ts });
}
// Load the scripts
if (!allDisabled) {
const context = Util.getContext();
scripts
.filter((script) => script.section.includes(context))
.filter((script) => devMode || !script.experimental)
.forEach((script) => {
if (settings[script.id]) {
DC.Network.loadScript(script.url)
.then(() => {
console.info(
`DCSM - '${script.name}' script has been loaded successfully.`,
);
})
.catch((err) => {
console.error(
`DCSM - Error loading '${script.name}' script: ` + err,
);
});
}
});
}
})
.catch((err) => {
console.error('DCSM - Error loading the list of scripts :' + err);
});
});
});