Commit bb7f6a1b authored by nanahira's avatar nanahira

Merge branch 'master' of github.com:IceYGO/windbot

parents 202c6e67 f79e942c
Pipeline #2670 failed with stages
in 54 seconds
......@@ -20,7 +20,7 @@ namespace WindBot.Game.AI.Decks
{
AddExecutor(ExecutorType.SpSummon);
AddExecutor(ExecutorType.Activate, DefaultDontChainMyself);
AddExecutor(ExecutorType.SummonOrSet);
AddExecutor(ExecutorType.SummonOrSet, DefaultMonsterSummon);
AddExecutor(ExecutorType.Repos, DefaultMonsterRepos);
AddExecutor(ExecutorType.SpellSet);
}
......
......@@ -83,18 +83,10 @@ namespace WindBot.Game.AI.Decks
private int m_swapFrogSummoned;
private int m_flipFlopFrogSummoned;
private int m_treebornFrogCount = 0;
public override void OnNewTurn()
{
m_treebornFrogCount = 0;
base.OnNewTurn();
}
private bool TreebornFrog()
{
m_treebornFrogCount++;
return m_treebornFrogCount <= 5;
return true;
}
private bool SwapFrogSummon()
......
......@@ -56,7 +56,7 @@ namespace WindBot.Game.AI.Decks
AddExecutor(ExecutorType.Activate, CardId.MonsterReborn, MonsterReborn);
AddExecutor(ExecutorType.Summon, CardId.WhiteNightDragon, WhiteNightDragon);
AddExecutor(ExecutorType.Summon, CardId.HorusTheBlackFlameDragonLv6, DefaultTributeSummon);
AddExecutor(ExecutorType.Summon, CardId.HorusTheBlackFlameDragonLv6, DefaultMonsterSummon);
AddExecutor(ExecutorType.Summon, CardId.AlexandriteDragon);
AddExecutor(ExecutorType.SummonOrSet, CardId.AxeDragonute);
AddExecutor(ExecutorType.SummonOrSet, CardId.DodgerDragon);
......@@ -157,7 +157,7 @@ namespace WindBot.Game.AI.Decks
if (card.IsCode(11224103))
return false;
return DefaultTributeSummon();
return DefaultMonsterSummon();
}
private bool HorusTheBlackFlameDragonLv8()
......
......@@ -184,7 +184,6 @@ namespace WindBot.Game.AI.Decks
private bool JetSynchronUsed = false;
private bool ScrapWyvernUsed = false;
private bool MaskedChameleonUsed = false;
private int ShootingRiserDragonCount = 0;
private int[] HandCosts = new[]
{
......@@ -218,7 +217,6 @@ namespace WindBot.Game.AI.Decks
JetSynchronUsed = false;
ScrapWyvernUsed = false;
MaskedChameleonUsed = false;
ShootingRiserDragonCount = 0;
}
public override void OnChainEnd()
......@@ -845,9 +843,8 @@ namespace WindBot.Game.AI.Decks
}
else
{
if (Duel.LastChainPlayer == 0 || ShootingRiserDragonCount >= 10)
if (Duel.LastChainPlayer == 0)
return false;
ShootingRiserDragonCount++;
AI.SelectCard(new[] {
CardId.BlackRoseMoonlightDragon,
CardId.ScrapDragon,
......
......@@ -230,7 +230,6 @@ namespace WindBot.Game.AI.Decks
}
int Ultimate_ss = 0;
int Enemy_atk = 0;
int TG_WonderMagician_count = 0;
bool Pillused = false;
bool CrystronNeedlefibereff_used = false;
bool OvertexCoatlseff_used = false;
......@@ -275,7 +274,6 @@ namespace WindBot.Game.AI.Decks
ShaddollSquamata_used = false;
ShaddollDragon_used = false;
ShaddollHedgehog_used = false;
TG_WonderMagician_count = 0;
}
private bool Luminasummon()
......@@ -576,8 +574,7 @@ namespace WindBot.Game.AI.Decks
private bool TG_WonderMagicianeff()
{
TG_WonderMagician_count++;
return TG_WonderMagician_count <= 10;
return true;
}
private bool AllureofDarkness()
{
......
......@@ -12,10 +12,14 @@ namespace WindBot.Game.AI.Decks
public LuckyExecutor(GameAI ai, Duel duel)
: base(ai, duel)
{
AddExecutor(ExecutorType.SpSummon, ImFeelingLucky);
AddExecutor(ExecutorType.Activate, ImFeelingLucky);
AddExecutor(ExecutorType.SummonOrSet, ImFeelingLucky);
AddExecutor(ExecutorType.SpellSet, ImFeelingLucky);
AddExecutor(ExecutorType.SpSummon, ImFeelingLucky);
AddExecutor(ExecutorType.SpSummon, ImFeelingUnlucky);
AddExecutor(ExecutorType.Activate, ImFeelingUnlucky);
AddExecutor(ExecutorType.SummonOrSet, DefaultMonsterSummon);
AddExecutor(ExecutorType.SpellSet, DefaultSpellSet);
AddExecutor(ExecutorType.Repos, DefaultMonsterRepos);
AddExecutor(ExecutorType.Activate, _CardId.MysticalSpaceTyphoon, DefaultMysticalSpaceTyphoon);
......@@ -90,9 +94,25 @@ namespace WindBot.Game.AI.Decks
return Program.Rand.Next(options.Count);
}
public override CardPosition OnSelectPosition(int cardId, IList<CardPosition> positions)
{
YGOSharp.OCGWrapper.NamedCard cardData = YGOSharp.OCGWrapper.NamedCard.Get(cardId);
if (cardData != null)
{
if (cardData.Attack <= 1000)
return CardPosition.FaceUpDefence;
}
return 0;
}
private bool ImFeelingLucky()
{
return Program.Rand.Next(9) >= 3 && DefaultDontChainMyself();
return Program.Rand.Next(9) >= 6 && DefaultDontChainMyself();
}
private bool ImFeelingUnlucky()
{
return DefaultDontChainMyself();
}
}
}
\ No newline at end of file
......@@ -46,8 +46,8 @@ namespace WindBot.Game.AI.Decks
AddExecutor(ExecutorType.Activate, CardId.SwordsOfRevealingLight, SwordsOfRevealingLight);
AddExecutor(ExecutorType.Activate, CardId.DoubleSummon, DoubleSummon);
AddExecutor(ExecutorType.Summon, CardId.AncientGearGolem, DefaultTributeSummon);
AddExecutor(ExecutorType.Summon, CardId.Frostosaurus, DefaultTributeSummon);
AddExecutor(ExecutorType.Summon, CardId.AncientGearGolem, DefaultMonsterSummon);
AddExecutor(ExecutorType.Summon, CardId.Frostosaurus, DefaultMonsterSummon);
AddExecutor(ExecutorType.SummonOrSet, CardId.AlexandriteDragon);
AddExecutor(ExecutorType.SummonOrSet, CardId.GeneWarpedWarwolf);
AddExecutor(ExecutorType.MonsterSet, CardId.GearGolemTheMovingFortress);
......
......@@ -181,7 +181,6 @@ namespace WindBot.Game.AI.Decks
private bool CymbalSkeletonUsed = false;
private bool BorrelswordDragonUsed = false;
private ClientCard RustyBardicheTarget = null;
private int ShootingRiserDragonCount = 0;
private int[] HandCosts = new[]
{
......@@ -215,7 +214,6 @@ namespace WindBot.Game.AI.Decks
CymbalSkeletonUsed = false;
BorrelswordDragonUsed = false;
RustyBardicheTarget = null;
ShootingRiserDragonCount = 0;
}
public override void OnChainEnd()
......@@ -611,10 +609,7 @@ namespace WindBot.Game.AI.Decks
}
else
{
if (Duel.LastChainPlayer == 0)
return false;
ShootingRiserDragonCount++;
return ShootingRiserDragonCount <= 10;
return Duel.LastChainPlayer != 0;
}
}
......@@ -825,8 +820,8 @@ namespace WindBot.Game.AI.Decks
if (ActivateDescription == 96)
{
// TODO: more FogBlade lost target
if ((Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) && Duel.CurrentChain.Count == 0)
return false;
if ((Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2) && Duel.CurrentChain.Count == 0)
return false;
AI.SelectCard(CardId.OrcustCymbalSkeleton);
return true;
}
......@@ -1130,22 +1125,22 @@ namespace WindBot.Game.AI.Decks
{
return Duel.LastChainPlayer == 1;
}
else if (Duel.Phase == DuelPhase.End)
{
ClientCard target = null;
target = Bot.Banished.GetFirstMatchingFaceupCard(card=>card.IsCode(CardId.OrcustCymbalSkeleton));
if (target == null)
target = Bot.Banished.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.OrcustHarpHorror));
if (target != null)
{
AI.SelectCard(target);
return true;
}
if(!Bot.HasInHand(CardId.OrcustHarpHorror) && Bot.GetRemainingCount(CardId.OrcustHarpHorror, 2) > 1)
{
AI.SelectCard(CardId.OrcustHarpHorror);
return true;
}
else if (Duel.Phase == DuelPhase.End)
{
ClientCard target = null;
target = Bot.Banished.GetFirstMatchingFaceupCard(card=>card.IsCode(CardId.OrcustCymbalSkeleton));
if (target == null)
target = Bot.Banished.GetFirstMatchingFaceupCard(card => card.IsCode(CardId.OrcustHarpHorror));
if (target != null)
{
AI.SelectCard(target);
return true;
}
if(!Bot.HasInHand(CardId.OrcustHarpHorror) && Bot.GetRemainingCount(CardId.OrcustHarpHorror, 2) > 1)
{
AI.SelectCard(CardId.OrcustHarpHorror);
return true;
}
}
return false;
}
......
......@@ -117,13 +117,11 @@ namespace WindBot.Game.AI.Decks
bool summon_used = false;
bool CardOfDemiseeff_used = false;
bool SeaStealthAttackeff_used = false;
int City_count = 0;
public override void OnNewTurn()
{
summon_used = false;
CardOfDemiseeff_used = false;
SeaStealthAttackeff_used = false;
City_count = 0;
base.OnNewTurn();
}
private bool PreventFeatherDustereff()
......@@ -356,9 +354,6 @@ namespace WindBot.Game.AI.Decks
}
else
{
if (City_count > 10)
return false;
ClientCard target = null;
foreach(ClientCard s in Bot.GetSpells())
{
......@@ -380,7 +375,6 @@ namespace WindBot.Game.AI.Decks
break;
}
}
City_count++;
AI.SelectPlace(Zones.z1 | Zones.z3);
AI.SelectCard(CardId.PhantasmSprialBattle);
return true;
......
......@@ -215,8 +215,7 @@ namespace WindBot.Game.AI.Decks
private bool Summon_used;
private bool Pilica_eff;
private bool plan_A;
private int SnowBell_count = 0;
//TODO: reset the flags when they should reset ( public override void OnNewTurn() )
public PureWindsExecutor(GameAI ai, Duel duel)
: base(ai, duel)
{
......@@ -302,7 +301,6 @@ namespace WindBot.Game.AI.Decks
Summon_used = false;
Pilica_eff = false;
plan_A = false;
SnowBell_count = 0;
base.OnNewTurn();
}
private bool windaset()
......@@ -770,7 +768,6 @@ namespace WindBot.Game.AI.Decks
private bool WindwitchSnowBellsp()
{
if (SnowBell_count >= 5) return false;
if ((Bot.HasInMonstersZone(CardId.CrystalWingSynchroDragon) ||
Bot.HasInMonstersZone(CardId.DaigustoSphreez) ||
Bot.HasInMonstersZone(CardId.MistWurm)) &&
......@@ -786,7 +783,6 @@ namespace WindBot.Game.AI.Decks
(Util.GetBotAvailZonesFromExtraDeck() == 0))
return false;
AI.SelectPosition(CardPosition.FaceUpDefence);
SnowBell_count++;
return true;
}
private bool DaigustoSphreezsp()
......
......@@ -73,7 +73,6 @@ namespace WindBot.Game.AI.Decks
bool snake_four_s = false;
bool tuner_eff_used = false;
bool crystal_eff_used = false;
int red_ss_count = 0;
bool white_eff_used = false;
bool lockbird_useful = false;
bool lockbird_used = false;
......@@ -758,7 +757,6 @@ namespace WindBot.Game.AI.Decks
public bool Red_ss()
{
if (red_ss_count >= 6) return false;
if ((Util.ChainContainsCard(CardId.DarkHole) || Util.ChainContainsCard(99330325) || Util.ChainContainsCard(53582587)) && Util.ChainContainsCard(CardId.Red)) return false;
if (Duel.LastChainPlayer == 0 && Util.GetLastChainCard().IsCode(CardId.Red))
{
......@@ -766,7 +764,6 @@ namespace WindBot.Game.AI.Decks
{
if (Util.IsChainTarget(m) && IsTrickstar(m.Id))
{
red_ss_count += 1;
AI.SelectCard(m);
Red_SelectPos();
return true;
......@@ -789,7 +786,6 @@ namespace WindBot.Game.AI.Decks
{
AI.SelectCard(c);
Red_SelectPos(c);
red_ss_count += 1;
return true;
}
if (c.IsCode(CardId.Pink)) return false;
......@@ -800,14 +796,12 @@ namespace WindBot.Game.AI.Decks
if (tosolve_enemy.Attack > 3200) AI.SelectPosition(CardPosition.FaceUpDefence);
AI.SelectCard(c);
Red_SelectPos(c);
red_ss_count += 1;
return true;
}
if (!Bot.HasInHand(CardId.White) && tosolve_enemy.Attack <= 3200 && c.IsCode(CardId.White))
{
AI.SelectCard(c);
Red_SelectPos(c);
red_ss_count += 1;
return true;
}
if (!Bot.HasInHand(CardId.White) && c.Attack < tosolve_enemy.Attack)
......@@ -824,7 +818,6 @@ namespace WindBot.Game.AI.Decks
if (tosolve_enemy.Attack > 1600) AI.SelectPosition(CardPosition.FaceUpDefence);
AI.SelectCard(c);
Red_SelectPos(c);
red_ss_count += 1;
return true;
}
}
......@@ -845,7 +838,6 @@ namespace WindBot.Game.AI.Decks
{
AI.SelectCard(card);
Red_SelectPos(card);
red_ss_count += 1;
return true;
}
}
......@@ -1710,7 +1702,6 @@ namespace WindBot.Game.AI.Decks
pink_ss = false;
snake_four_s = false;
crystal_eff_used = false;
red_ss_count = 0;
white_eff_used = false;
lockbird_useful = false;
lockbird_used = false;
......
......@@ -197,7 +197,6 @@ namespace WindBot.Game.AI.Decks
bool MagicianRightHand_used = false;
ClientCard MagiciansLeftHand_negate = null;
ClientCard MagicianRightHand_negate = null;
int PSYOmega_count = 0;
// go first
public override bool OnSelectHand()
......@@ -271,7 +270,6 @@ namespace WindBot.Game.AI.Decks
public override void OnNewTurn()
{
CrossoutDesignatorTarget = 0;
PSYOmega_count = 0;
MadameVerreGainedATK = false;
summoned = false;
enemy_activate_MaxxC = false;
......@@ -2501,20 +2499,15 @@ namespace WindBot.Game.AI.Decks
// recycle from grave
if (Card.Location == CardLocation.Grave)
{
if (PSYOmega_count >= 5){
return false;
}
List<ClientCard> enemy_danger = CheckDangerousCardinEnemyGrave();
if (enemy_danger.Count > 0)
{
AI.SelectCard(enemy_danger);
PSYOmega_count ++;
return true;
}
if (!Bot.HasInHandOrInSpellZoneOrInGraveyard(CardId.Holiday) && Bot.HasInGraveyard(important_witchcraft))
{
AI.SelectCard(important_witchcraft);
PSYOmega_count ++;
return true;
}
if (CheckProblematicCards() == null)
......@@ -2523,7 +2516,6 @@ namespace WindBot.Game.AI.Decks
CardId.MaxxC, CardId.AshBlossom_JoyousSpring,
CardId.MagicianRightHand, CardId.MagiciansLeftHand, CardId.MagiciansRestage, CardId.Patronus,
CardId.LightningStorm, CardId.Reasoning);
PSYOmega_count ++;
return true;
}
}
......
......@@ -100,6 +100,7 @@ namespace WindBot.Game.AI.Decks
AddExecutor(ExecutorType.Summon, CardId.Goblindbergh);
AddExecutor(ExecutorType.Summon, CardId.TinGoldfish);
AddExecutor(ExecutorType.Summon, CardId.SummonerMonk);
AddExecutor(ExecutorType.Summon, CardId.Honest);
// Summons: Effects
AddExecutor(ExecutorType.Activate, CardId.Goblindbergh, GoblindberghEffect);
......@@ -122,13 +123,6 @@ namespace WindBot.Game.AI.Decks
AddExecutor(ExecutorType.Activate, CardId.SolemnStrike, DefaultSolemnStrike);
}
private int ZwCount = 0;
public override void OnNewTurn()
{
ZwCount = 0;
}
public override bool OnSelectHand()
{
return false;
......@@ -177,8 +171,7 @@ namespace WindBot.Game.AI.Decks
private bool ZwWeapon()
{
ZwCount++;
return ZwCount < 10;
return true;
}
private bool ReinforcementOfTheArmy()
......
......@@ -47,7 +47,6 @@ namespace WindBot.Game.AI.Decks
bool TigermortarSpsummoned = false;
bool ChakanineSpsummoned = false;
bool BroadbullSpsummoned = false;
int WhiptailEffectCount = 0;
public ZoodiacExecutor(GameAI ai, Duel duel)
: base(ai, duel)
......@@ -128,7 +127,6 @@ namespace WindBot.Game.AI.Decks
TigermortarSpsummoned = false;
ChakanineSpsummoned = false;
BroadbullSpsummoned = false;
WhiptailEffectCount = 0;
}
public override bool OnPreBattleBetween(ClientCard attacker, ClientCard defender)
......@@ -431,7 +429,7 @@ namespace WindBot.Game.AI.Decks
{
if (Duel.Phase == DuelPhase.Main1 || Duel.Phase == DuelPhase.Main2)
return false;
if (Card.IsDisabled() || WhiptailEffectCount >= 3)
if (Card.IsDisabled())
return false;
ClientCard target = null;
List<ClientCard> monsters = Bot.GetMonsters();
......@@ -461,7 +459,6 @@ namespace WindBot.Game.AI.Decks
CardId.Drident
});
}
WhiptailEffectCount++;
return true;
}
......
......@@ -105,8 +105,6 @@ namespace WindBot.Game.AI
public const int lightningStorm = 14532163;
}
int HonestEffectCount = 0;
protected DefaultExecutor(GameAI ai, Duel duel)
: base(ai, duel)
{
......@@ -263,9 +261,12 @@ namespace WindBot.Game.AI
return false;
}
public override void OnNewTurn()
/// <summary>
/// Set when this card can't beat the enemies
/// </summary>
public override bool OnSelectMonsterSummonOrSet(ClientCard card)
{
HonestEffectCount = 0;
return card.Level <= 4 && Util.IsAllEnemyBetter(true) && Util.IsAllEnemyBetterThanValue(card.Attack + 300, false);
}
/// <summary>
......@@ -647,10 +648,13 @@ namespace WindBot.Game.AI
}
/// <summary>
/// Summon with tributes ATK lower.
/// Summon with no tribute, or with tributes ATK lower.
/// </summary>
protected bool DefaultTributeSummon()
protected bool DefaultMonsterSummon()
{
if (Card.Level <= 4)
return true;
if (!UniqueFaceupMonster())
return false;
int tributecount = (int)Math.Ceiling((Card.Level - 4.0d) / 2.0d);
......@@ -785,6 +789,8 @@ namespace WindBot.Game.AI
/// </summary>
protected bool DefaultDontChainMyself()
{
if (Type != ExecutorType.Activate)
return true;
if (Executors.Any(exec => exec.Type == Type && exec.CardId == Card.Id))
return false;
return Duel.LastChainPlayer != 0;
......@@ -797,6 +803,8 @@ namespace WindBot.Game.AI
{
if (Executors.Count(exec => exec.Type == Type && exec.CardId == Card.Id) > 1)
return false;
if (Card.IsFacedown())
return true;
if (Bot.LifePoints <= 1000)
return false;
if (Bot.LifePoints <= Enemy.LifePoints && ActivateDescription == Util.GetStringId(_CardId.ChickenGame, 0))
......@@ -1110,13 +1118,7 @@ namespace WindBot.Game.AI
|| ((Bot.BattlingMonster.Attack < Enemy.BattlingMonster.Defense) && (Bot.BattlingMonster.Attack + Enemy.BattlingMonster.Attack > Enemy.BattlingMonster.Defense)));
}
if (Util.IsTurn1OrMain2() && HonestEffectCount <= 5)
{
HonestEffectCount++;
return true;
}
return false;
return Util.IsTurn1OrMain2();
}
protected bool DefaultLightingStorm()
......
......@@ -188,6 +188,16 @@ namespace WindBot.Game.AI
return false;
}
/// <summary>
/// Called when the executor type is SummonOrSet
/// </summary>
/// <returns>True if select to set the monster.</returns>
public virtual bool OnSelectMonsterSummonOrSet(ClientCard card)
{
// Overrided in DefalultExecutor
return false;
}
/// <summary>
/// Called when bot is going to annouce a card
/// </summary>
......
......@@ -13,12 +13,16 @@ namespace WindBot.Game
private Dialogs _dialogs;
// record activated count to prevent infinite actions
private Dictionary<int, int> _activatedCards;
public GameAI(GameClient game, Duel duel)
{
Game = game;
Duel = duel;
_dialogs = new Dialogs(game);
_activatedCards = new Dictionary<int, int>();
}
/// <summary>
......@@ -81,6 +85,7 @@ namespace WindBot.Game
/// </summary>
public void OnNewTurn()
{
_activatedCards.Clear();
Executor.OnNewTurn();
}
......@@ -439,8 +444,7 @@ namespace WindBot.Game
}
if (ShouldExecute(exec, card, ExecutorType.SummonOrSet))
{
if (Executor.Util.IsAllEnemyBetter(true) && Executor.Util.IsAllEnemyBetterThanValue(card.Attack + 300, false) &&
main.MonsterSetableCards.Contains(card))
if (main.MonsterSetableCards.Contains(card) && Executor.OnSelectMonsterSummonOrSet(card))
{
_dialogs.SendSetMonster();
return new MainPhaseAction(MainPhaseAction.MainAction.SetMonster, card.ActionIndex);
......@@ -448,7 +452,7 @@ namespace WindBot.Game
_dialogs.SendSummon(card.Name);
return new MainPhaseAction(MainPhaseAction.MainAction.Summon, card.ActionIndex);
}
}
}
foreach (ClientCard card in main.SpellSetableCards)
{
if (ShouldExecute(exec, card, ExecutorType.SpellSet))
......@@ -1107,11 +1111,28 @@ namespace WindBot.Game
private bool ShouldExecute(CardExecutor exec, ClientCard card, ExecutorType type, int desc = -1)
{
if (card.Id != 0 && type == ExecutorType.Activate &&
_activatedCards.ContainsKey(card.Id) && _activatedCards[card.Id] >= 9)
{
return false;
}
Executor.SetCard(type, card, desc);
return card != null &&
exec.Type == type &&
(exec.CardId == -1 || exec.CardId == card.Id) &&
(exec.Func == null || exec.Func());
bool result = card != null && exec.Type == type &&
(exec.CardId == -1 || exec.CardId == card.Id) &&
(exec.Func == null || exec.Func());
if (card.Id != 0 && type == ExecutorType.Activate && result)
{
int count = card.IsDisabled() ? 3 : 1;
if (!_activatedCards.ContainsKey(card.Id))
{
_activatedCards.Add(card.Id, count);
}
else
{
_activatedCards[card.Id] += count;
}
}
return result;
}
}
}
......@@ -1095,7 +1095,7 @@ namespace WindBot.Game
for (int i = 0; i < count; ++i)
{
packet.ReadByte(); // flag
packet.ReadInt32(); // card id
int id = packet.ReadInt32();
int con = GetLocalPlayer(packet.ReadByte());
int loc = packet.ReadByte();
int seq = packet.ReadByte();
......@@ -1106,7 +1106,12 @@ namespace WindBot.Game
{
desc = 0;
}
cards.Add(_duel.GetCard(con, loc, seq, sseq));
ClientCard card = _duel.GetCard(con, loc, seq, sseq);
if (card.Id == 0)
card.SetId(id);
cards.Add(card);
descs.Add(desc);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment