mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-18 19:58:00 +00:00
- Added the capability to play Challenges vs predetermined decks (along with a few other related options to disallow specific quest mode things)
- Added Sorin vs Tibalt, and Tibalt vs Sorin as examples of Challenges that force you to use a specific Deck. (They seemed to be the best duel deck compatibility for the AI).
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -12816,12 +12816,14 @@ res/quest/challenges/Private[!!-~]Domain.dck -text
|
|||||||
res/quest/challenges/Quest[!!-~]for[!!-~]Ula's[!!-~]Temple.dck -text
|
res/quest/challenges/Quest[!!-~]for[!!-~]Ula's[!!-~]Temple.dck -text
|
||||||
res/quest/challenges/Reactor[!!-~]Meltdown.dck -text
|
res/quest/challenges/Reactor[!!-~]Meltdown.dck -text
|
||||||
res/quest/challenges/Repressed[!!-~]Magic.dck -text
|
res/quest/challenges/Repressed[!!-~]Magic.dck -text
|
||||||
|
res/quest/challenges/Sorin[!!-~]vs[!!-~]Tibalt.dck -text
|
||||||
res/quest/challenges/The[!!-~]Backlash[!!-~]Machine.dck -text
|
res/quest/challenges/The[!!-~]Backlash[!!-~]Machine.dck -text
|
||||||
res/quest/challenges/The[!!-~]Court[!!-~]Jester.dck -text
|
res/quest/challenges/The[!!-~]Court[!!-~]Jester.dck -text
|
||||||
res/quest/challenges/The[!!-~]Desert[!!-~]Caravan.dck -text
|
res/quest/challenges/The[!!-~]Desert[!!-~]Caravan.dck -text
|
||||||
res/quest/challenges/The[!!-~]King's[!!-~]Contest.dck -text
|
res/quest/challenges/The[!!-~]King's[!!-~]Contest.dck -text
|
||||||
res/quest/challenges/The[!!-~]Pied[!!-~]Piper.dck -text
|
res/quest/challenges/The[!!-~]Pied[!!-~]Piper.dck -text
|
||||||
res/quest/challenges/The[!!-~]Torpor[!!-~]Orb.dck -text
|
res/quest/challenges/The[!!-~]Torpor[!!-~]Orb.dck -text
|
||||||
|
res/quest/challenges/Tibalt[!!-~]vs[!!-~]Sorin.dck -text
|
||||||
res/quest/challenges/Zombie[!!-~]Attack!.dck -text
|
res/quest/challenges/Zombie[!!-~]Attack!.dck -text
|
||||||
res/quest/duels/Abraham[!!-~]Lincoln[!!-~]3.dck -text
|
res/quest/duels/Abraham[!!-~]Lincoln[!!-~]3.dck -text
|
||||||
res/quest/duels/Air-Walker[!!-~]2.dck -text
|
res/quest/duels/Air-Walker[!!-~]2.dck -text
|
||||||
|
|||||||
56
res/quest/challenges/Sorin vs Tibalt.dck
Normal file
56
res/quest/challenges/Sorin vs Tibalt.dck
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
[quest]
|
||||||
|
id=29
|
||||||
|
AILife=20
|
||||||
|
HumanLife=20
|
||||||
|
HumanDeck=Tibalt vs Sorin.dck
|
||||||
|
UseBazaar=False
|
||||||
|
ForceAnte=False
|
||||||
|
Repeat=false
|
||||||
|
Wins=20
|
||||||
|
Card Reward=2 random rares
|
||||||
|
Credit Reward=200
|
||||||
|
[metadata]
|
||||||
|
Name=quest29
|
||||||
|
Title=Tibalt (vs Sorin)
|
||||||
|
Difficulty=hard
|
||||||
|
Description=Play as Sorin vs Tibalt the Pain Mage. NOTE: This challenge uses a preselected deck and ignores specific Bazaar settings.
|
||||||
|
Icon=Tibalt.jpg
|
||||||
|
Deck Type=constructed
|
||||||
|
[main]
|
||||||
|
14 Mountain
|
||||||
|
6 Swamp
|
||||||
|
2 Ashmouth Hound
|
||||||
|
1 Coal Stoker
|
||||||
|
1 Corpse Connoisseur
|
||||||
|
1 Gang of Devils
|
||||||
|
1 Goblin Arsonist
|
||||||
|
1 Hellrider
|
||||||
|
2 Hellspark Elemental
|
||||||
|
1 Lavaborn Muse
|
||||||
|
1 Mad Prophet
|
||||||
|
1 Reassembling Skeleton
|
||||||
|
1 Scorched Rusalka
|
||||||
|
1 Scourge Devil
|
||||||
|
1 Shambling Remains
|
||||||
|
1 Skirsdag Cultist
|
||||||
|
2 Vithian Stinger
|
||||||
|
1 Sulfuric Vortex
|
||||||
|
1 Blazing Salvo
|
||||||
|
1 Flame Javelin
|
||||||
|
1 Geistflame
|
||||||
|
1 Strangling Soot
|
||||||
|
1 Terminate
|
||||||
|
2 Akoum Refuge
|
||||||
|
2 Rakdos Carnarium
|
||||||
|
1 Tibalt, the Fiend-Blooded
|
||||||
|
1 Blightning
|
||||||
|
1 Breaking Point
|
||||||
|
2 Browbeat
|
||||||
|
1 Bump in the Night
|
||||||
|
1 Devil's Play
|
||||||
|
1 Faithless Looting
|
||||||
|
1 Flame Slash
|
||||||
|
1 Pyroclasm
|
||||||
|
1 Recoup
|
||||||
|
1 Torrent of Souls
|
||||||
|
[sideboard]
|
||||||
56
res/quest/challenges/Tibalt vs Sorin.dck
Normal file
56
res/quest/challenges/Tibalt vs Sorin.dck
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
[quest]
|
||||||
|
id=28
|
||||||
|
AILife=20
|
||||||
|
HumanLife=20
|
||||||
|
HumanDeck=Sorin vs Tibalt.dck
|
||||||
|
UseBazaar=False
|
||||||
|
ForceAnte=False
|
||||||
|
Repeat=false
|
||||||
|
Wins=20
|
||||||
|
Card Reward=2 random rares
|
||||||
|
Credit Reward=200
|
||||||
|
[metadata]
|
||||||
|
Name=quest28
|
||||||
|
Title=Sorin (vs Tibalt)
|
||||||
|
Difficulty=medium
|
||||||
|
Description=Play as Tibalt vs Sorin, Lord of Innistrad. NOTE: This challenge uses a preselected deck and ignores specific Bazaar settings.
|
||||||
|
Icon=Sorin.jpg
|
||||||
|
Deck Type=constructed
|
||||||
|
[main]
|
||||||
|
9 Plains
|
||||||
|
12 Swamp
|
||||||
|
1 Bloodrage Vampire
|
||||||
|
1 Butcher of Malakir
|
||||||
|
1 Child of Night
|
||||||
|
2 Doomed Traveler
|
||||||
|
2 Duskhunter Bat
|
||||||
|
1 Fiend Hunter
|
||||||
|
1 Gatekeeper of Malakir
|
||||||
|
1 Mausoleum Guard
|
||||||
|
1 Mesmeric Fiend
|
||||||
|
1 Phantom General
|
||||||
|
1 Revenant Patriarch
|
||||||
|
1 Sengir Vampire
|
||||||
|
1 Twilight Drover
|
||||||
|
1 Vampire Lacerator
|
||||||
|
1 Vampire Nighthawk
|
||||||
|
1 Vampire Outcasts
|
||||||
|
1 Wall of Omens
|
||||||
|
1 Field of Souls
|
||||||
|
1 Mark of the Vampire
|
||||||
|
1 Mortify
|
||||||
|
1 Sorin's Thirst
|
||||||
|
1 Unmake
|
||||||
|
1 Urge to Feed
|
||||||
|
1 Vampire's Bite
|
||||||
|
1 Zealous Persecution
|
||||||
|
2 Evolving Wilds
|
||||||
|
2 Tainted Field
|
||||||
|
1 Sorin, Lord of Innistrad
|
||||||
|
1 Absorb Vis
|
||||||
|
1 Ancient Craving
|
||||||
|
1 Death Grasp
|
||||||
|
1 Decompose
|
||||||
|
1 Lingering Souls
|
||||||
|
2 Spectral Procession
|
||||||
|
[sideboard]
|
||||||
@@ -215,8 +215,9 @@ public class GameNew {
|
|||||||
*
|
*
|
||||||
* TODO: Accept something like match state as parameter. Match should be aware of players,
|
* TODO: Accept something like match state as parameter. Match should be aware of players,
|
||||||
* their decks and other special starting conditions.
|
* their decks and other special starting conditions.
|
||||||
|
* @param forceAnte Forces ante on or off no matter what your preferences
|
||||||
*/
|
*/
|
||||||
public static void newGame(final GameState game, final boolean canRandomFoil) {
|
public static void newGame(final GameState game, final boolean canRandomFoil, Boolean forceAnte) {
|
||||||
|
|
||||||
Card.resetUniqueNumber();
|
Card.resetUniqueNumber();
|
||||||
// need this code here, otherwise observables fail
|
// need this code here, otherwise observables fail
|
||||||
@@ -225,7 +226,7 @@ public class GameNew {
|
|||||||
trigHandler.clearDelayedTrigger();
|
trigHandler.clearDelayedTrigger();
|
||||||
|
|
||||||
// friendliness
|
// friendliness
|
||||||
boolean useAnte = preferences.getPrefBoolean(FPref.UI_ANTE);
|
boolean useAnte = forceAnte != null ? forceAnte : preferences.getPrefBoolean(FPref.UI_ANTE);
|
||||||
final Set<CardPrinted> rAICards = new HashSet<CardPrinted>();
|
final Set<CardPrinted> rAICards = new HashSet<CardPrinted>();
|
||||||
|
|
||||||
Map<Player, Set<CardPrinted>> removedAnteCards = new HashMap<Player, Set<CardPrinted>>();
|
Map<Player, Set<CardPrinted>> removedAnteCards = new HashMap<Player, Set<CardPrinted>>();
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ public class MatchController {
|
|||||||
private int gamesPerMatch = 3;
|
private int gamesPerMatch = 3;
|
||||||
private int gamesToWinMatch = 2;
|
private int gamesToWinMatch = 2;
|
||||||
|
|
||||||
|
private Boolean forceAnte = false;
|
||||||
|
|
||||||
private GameState currentGame = null;
|
private GameState currentGame = null;
|
||||||
|
|
||||||
private final List<GameOutcome> gamesPlayed = new ArrayList<GameOutcome>();
|
private final List<GameOutcome> gamesPlayed = new ArrayList<GameOutcome>();
|
||||||
@@ -67,6 +69,11 @@ public class MatchController {
|
|||||||
gameType = type;
|
gameType = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MatchController(GameType type, Map<LobbyPlayer, PlayerStartConditions> map, Boolean forceAnte) {
|
||||||
|
this(type, map);
|
||||||
|
this.forceAnte = forceAnte;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the games played.
|
* Gets the games played.
|
||||||
*
|
*
|
||||||
@@ -145,7 +152,7 @@ public class MatchController {
|
|||||||
attachUiToMatch(this, FControl.SINGLETON_INSTANCE.getLobby().getGuiPlayer());
|
attachUiToMatch(this, FControl.SINGLETON_INSTANCE.getLobby().getGuiPlayer());
|
||||||
|
|
||||||
final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed;
|
final boolean canRandomFoil = Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_RANDOM_FOIL) && gameType == GameType.Constructed;
|
||||||
GameNew.newGame(currentGame, canRandomFoil);
|
GameNew.newGame(currentGame, canRandomFoil, this.forceAnte);
|
||||||
|
|
||||||
currentGame.setAge(GameAge.Mulligan);
|
currentGame.setAge(GameAge.Mulligan);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -353,7 +353,15 @@ public class SSubmenuQuestUtil {
|
|||||||
}
|
}
|
||||||
final QuestController qData = Singletons.getModel().getQuest();
|
final QuestController qData = Singletons.getModel().getQuest();
|
||||||
|
|
||||||
Deck deck = SSubmenuQuestUtil.getCurrentDeck();
|
Deck deck = null;
|
||||||
|
if (event instanceof QuestEventChallenge) {
|
||||||
|
// Predefined HumanDeck
|
||||||
|
deck = ((QuestEventChallenge) event).getHumanDeck();
|
||||||
|
}
|
||||||
|
if (deck == null) {
|
||||||
|
// If no predefined Deck, use the Player's Deck
|
||||||
|
deck = SSubmenuQuestUtil.getCurrentDeck();
|
||||||
|
}
|
||||||
if (deck == null) {
|
if (deck == null) {
|
||||||
String msg = "Please select a Quest Deck.";
|
String msg = "Please select a Quest Deck.";
|
||||||
JOptionPane.showMessageDialog(null, msg, "No Deck", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(null, msg, "No Deck", JOptionPane.ERROR_MESSAGE);
|
||||||
@@ -393,22 +401,36 @@ public class SSubmenuQuestUtil {
|
|||||||
worker.execute();
|
worker.execute();
|
||||||
|
|
||||||
int extraLifeHuman = 0;
|
int extraLifeHuman = 0;
|
||||||
|
Integer lifeHuman = null;
|
||||||
|
boolean useBazaar = true;
|
||||||
|
Boolean forceAnte = false;
|
||||||
int lifeAI = 20;
|
int lifeAI = 20;
|
||||||
if (event instanceof QuestEventChallenge) {
|
if (event instanceof QuestEventChallenge) {
|
||||||
lifeAI = ((QuestEventChallenge) event).getAILife();
|
lifeAI = ((QuestEventChallenge) event).getAILife();
|
||||||
|
lifeHuman = ((QuestEventChallenge) event).getHumanLife();
|
||||||
|
|
||||||
if (qData.getAssets().hasItem(QuestItemType.ZEPPELIN)) {
|
if (qData.getAssets().hasItem(QuestItemType.ZEPPELIN)) {
|
||||||
extraLifeHuman = 3;
|
extraLifeHuman = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useBazaar = ((QuestEventChallenge) event).isUseBazaar();
|
||||||
|
forceAnte = ((QuestEventChallenge) event).isForceAnte();
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerStartConditions humanStart = new PlayerStartConditions(SSubmenuQuestUtil.getCurrentDeck());
|
PlayerStartConditions humanStart = new PlayerStartConditions(deck);
|
||||||
humanStart.setStartingLife(qData.getAssets().getLife(qData.getMode()) + extraLifeHuman);
|
|
||||||
humanStart.setCardsOnBattlefield(QuestUtil.getHumanStartingCards(qData, event));
|
|
||||||
|
|
||||||
PlayerStartConditions aiStart = new PlayerStartConditions(event.getEventDeck());
|
PlayerStartConditions aiStart = new PlayerStartConditions(event.getEventDeck());
|
||||||
aiStart.setStartingLife(lifeAI);
|
|
||||||
aiStart.setCardsOnBattlefield(QuestUtil.getComputerStartingCards(event));
|
if (lifeHuman != null) {
|
||||||
|
humanStart.setStartingLife(lifeHuman);
|
||||||
|
} else {
|
||||||
|
humanStart.setStartingLife(qData.getAssets().getLife(qData.getMode()) + extraLifeHuman);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useBazaar) {
|
||||||
|
humanStart.setCardsOnBattlefield(QuestUtil.getHumanStartingCards(qData, event));
|
||||||
|
aiStart.setStartingLife(lifeAI);
|
||||||
|
aiStart.setCardsOnBattlefield(QuestUtil.getComputerStartingCards(event));
|
||||||
|
}
|
||||||
|
|
||||||
MatchStartHelper msh = new MatchStartHelper();
|
MatchStartHelper msh = new MatchStartHelper();
|
||||||
msh.addPlayer(Singletons.getControl().getLobby().getQuestPlayer(), humanStart);
|
msh.addPlayer(Singletons.getControl().getLobby().getQuestPlayer(), humanStart);
|
||||||
@@ -417,7 +439,7 @@ public class SSubmenuQuestUtil {
|
|||||||
aiPlayer.setIconImageKey(event.getIconImageKey());
|
aiPlayer.setIconImageKey(event.getIconImageKey());
|
||||||
msh.addPlayer(aiPlayer, aiStart);
|
msh.addPlayer(aiPlayer, aiStart);
|
||||||
|
|
||||||
final MatchController mc = new MatchController(GameType.Quest, msh.getPlayerMap());
|
final MatchController mc = new MatchController(GameType.Quest, msh.getPlayerMap(), forceAnte);
|
||||||
FThreads.invokeInEdtLater(new Runnable(){
|
FThreads.invokeInEdtLater(new Runnable(){
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
|
|
||||||
|
import forge.deck.Deck;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* QuestQuest class.
|
* QuestQuest class.
|
||||||
@@ -47,12 +49,18 @@ public class QuestEventChallenge extends QuestEvent {
|
|||||||
/** The ai life. */
|
/** The ai life. */
|
||||||
private int aiLife = 25;
|
private int aiLife = 25;
|
||||||
|
|
||||||
|
private Integer humanLife = null;
|
||||||
|
|
||||||
/** The credits reward. */
|
/** The credits reward. */
|
||||||
private int creditsReward = 100;
|
private int creditsReward = 100;
|
||||||
|
|
||||||
/** The repeatable. */
|
/** The repeatable. */
|
||||||
private boolean repeatable = false;
|
private boolean repeatable = false;
|
||||||
|
|
||||||
|
private boolean useBazaar = true;
|
||||||
|
|
||||||
|
private Boolean forceAnte = null;
|
||||||
|
|
||||||
/** The wins reqd. */
|
/** The wins reqd. */
|
||||||
private int winsReqd = 20;
|
private int winsReqd = 20;
|
||||||
|
|
||||||
@@ -63,6 +71,8 @@ public class QuestEventChallenge extends QuestEvent {
|
|||||||
/** The ai extra cards. */
|
/** The ai extra cards. */
|
||||||
private List<String> aiExtraCards = new ArrayList<String>();
|
private List<String> aiExtraCards = new ArrayList<String>();
|
||||||
|
|
||||||
|
private Deck humanDeck = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instantiates a new quest challenge.
|
* Instantiates a new quest challenge.
|
||||||
*/
|
*/
|
||||||
@@ -248,4 +258,60 @@ public class QuestEventChallenge extends QuestEvent {
|
|||||||
public void setHumanExtraCards(final List<String> humanExtraCards0) {
|
public void setHumanExtraCards(final List<String> humanExtraCards0) {
|
||||||
this.humanExtraCards = humanExtraCards0;
|
this.humanExtraCards = humanExtraCards0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the humanLife
|
||||||
|
*/
|
||||||
|
public Integer getHumanLife() {
|
||||||
|
return humanLife;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param humanLife the humanLife to set
|
||||||
|
*/
|
||||||
|
public void setHumanLife(Integer humanLife) {
|
||||||
|
this.humanLife = humanLife;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the useBazaar
|
||||||
|
*/
|
||||||
|
public boolean isUseBazaar() {
|
||||||
|
return useBazaar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param useBazaar the useBazaar to set
|
||||||
|
*/
|
||||||
|
public void setUseBazaar(boolean useBazaar) {
|
||||||
|
this.useBazaar = useBazaar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the forceAnte
|
||||||
|
*/
|
||||||
|
public Boolean isForceAnte() {
|
||||||
|
return forceAnte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param forceAnte the forceAnte to set
|
||||||
|
*/
|
||||||
|
public void setForceAnte(Boolean forceAnte) {
|
||||||
|
this.forceAnte = forceAnte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the humanDeck
|
||||||
|
*/
|
||||||
|
public Deck getHumanDeck() {
|
||||||
|
return humanDeck;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param humanDeck the humanDeck to set
|
||||||
|
*/
|
||||||
|
public void setHumanDeck(Deck humanDeck) {
|
||||||
|
this.humanDeck = humanDeck;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.util.Map;
|
|||||||
import forge.ImageCache;
|
import forge.ImageCache;
|
||||||
import forge.deck.Deck;
|
import forge.deck.Deck;
|
||||||
import forge.deck.io.DeckSerializer;
|
import forge.deck.io.DeckSerializer;
|
||||||
|
import forge.properties.NewConstants;
|
||||||
import forge.quest.QuestEventChallenge;
|
import forge.quest.QuestEventChallenge;
|
||||||
import forge.quest.QuestEventDifficulty;
|
import forge.quest.QuestEventDifficulty;
|
||||||
import forge.util.FileSection;
|
import forge.util.FileSection;
|
||||||
@@ -38,6 +39,23 @@ public class QuestChallengeReader extends StorageReaderFolder<QuestEventChalleng
|
|||||||
qc.setCardReward(sectionQuest.get("Card Reward"));
|
qc.setCardReward(sectionQuest.get("Card Reward"));
|
||||||
qc.setHumanExtraCards(Arrays.asList(TextUtil.split(sectionQuest.get("HumanExtras", ""), '|')));
|
qc.setHumanExtraCards(Arrays.asList(TextUtil.split(sectionQuest.get("HumanExtras", ""), '|')));
|
||||||
qc.setAiExtraCards(Arrays.asList(TextUtil.split(sectionQuest.get("AIExtras", ""), '|')));
|
qc.setAiExtraCards(Arrays.asList(TextUtil.split(sectionQuest.get("AIExtras", ""), '|')));
|
||||||
|
// Less common properties
|
||||||
|
int humanLife = sectionQuest.getInt("HumanLife", 0);
|
||||||
|
if (humanLife != 0) {
|
||||||
|
qc.setHumanLife(humanLife);
|
||||||
|
}
|
||||||
|
qc.setUseBazaar(sectionQuest.getBoolean("UseBazaar", qc.isUseBazaar()));
|
||||||
|
|
||||||
|
String force = sectionQuest.get("ForceAnte", null);
|
||||||
|
if (force != null) {
|
||||||
|
qc.setForceAnte(Boolean.valueOf(force));
|
||||||
|
}
|
||||||
|
|
||||||
|
String humanDeck = sectionQuest.get("HumanDeck", null);
|
||||||
|
if (humanDeck != null) {
|
||||||
|
File humanFile = new File(NewConstants.DEFAULT_CHALLENGES_DIR, humanDeck);
|
||||||
|
qc.setHumanDeck(Deck.fromFile(humanFile));
|
||||||
|
}
|
||||||
|
|
||||||
// Common properties
|
// Common properties
|
||||||
FileSection sectionMeta = FileSection.parse(contents.get("metadata"), "=");
|
FileSection sectionMeta = FileSection.parse(contents.get("metadata"), "=");
|
||||||
|
|||||||
Reference in New Issue
Block a user