Code from Beached As
Post MTG Forge Related Programming Questions Here
Moderators: timmermac, Blacksmith, KrazyTheFox, Agetian, friarsol, CCGHQ Admins
63 posts
• Page 1 of 5 • 1, 2, 3, 4, 5
Code from Beached As
by Beached As » 19 Apr 2010, 12:10
Alaborn Zealot: Portal Second Age Uncommon
Add to CombatUtil.java after the Abomination code
Add to CombatUtil.java after the Abomination code
- Code: Select all
if(b.getName().equals("Alaborn Zealot") || b.getName().equals("Alaborn Zealot")) {
final Card blocker = b;
final Card attacker = a;
final Ability ability = new Ability(b, "0") {
@Override
public void resolve() {
AllZone.GameAction.destroy(attacker);
AllZone.GameAction.destroy(blocker);
}
};
ability.setStackDescription(b + " - destroy attacking creature.");
AllZone.Stack.add(ability);
}
- Code: Select all
Alaborn Zealot
W
Creature Human Soldier
When Alaborn Zealot blocks a creature, destroy that creature and Alaborn Zealot.
1/1
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by Beached As » 19 Apr 2010, 16:48
Buried Alive: Odyssey Uncommon
Add to Cardfactory.java
Add to Cardfactory.java
- Code: Select all
//*************** START *********** START **************************
else if(cardName.equals("Buried Alive")) {
final SpellAbility spell = new Spell(card) {
private static final long serialVersionUID = 5676345736475L;
@Override
public void resolve() {
String player = card.getController();
if(player.equals(Constant.Player.Human)) humanResolve();
else computerResolve();
}
public void humanResolve() {
for (int i=0;i<3;i++)
{
PlayerZone deck = AllZone.getZone(Constant.Zone.Library, card.getController());
CardList list = new CardList(deck.getCards());
list = list.getType("Creature");
Object check = AllZone.Display.getChoiceOptional("Select a creature card",
list.toArray());
if(check != null) {
PlayerZone grave = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
AllZone.GameAction.moveTo(grave, (Card) check);
}
AllZone.GameAction.shuffle(Constant.Player.Human);
}
}
public void computerResolve() {
for (int i=0;i<3;i++)
{
Card[] library = AllZone.Computer_Library.getCards();
CardList list = new CardList(library);
list = list.getType("Creature");
//pick best creature
Card c = CardFactoryUtil.AI_getBestCreature(list);
if(c == null) c = library[0];
//System.out.println("computer picked - " +c);
AllZone.Computer_Library.remove(c);
AllZone.Computer_Graveyard.add(c);
}
}
@Override
public boolean canPlay() {
PlayerZone library = AllZone.getZone(Constant.Zone.Library, card.getController());
return library.getCards().length != 0;
}
@Override
public boolean canPlayAI() {
CardList creature = new CardList();
creature.addAll(AllZone.Computer_Library.getCards());
creature = creature.getType("Creature");
return creature.size() > 2;
}
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
}//*************** END ************ END **************************
- Code: Select all
Buried Alive
2 B
Sorcery
Search your library for up to three creature cards and put them into your graveyard. Then shuffle your library.
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by Beached As » 19 Apr 2010, 16:53
Dissipate: Mirage Uncommon
Note that the computer didn't use the counterspell when it was tested. I assume that this card needs to be added to a list before the computer starts using it (judging by the code in ComputerAI_counterSpells.java). I'll leave this to the people that know what they're are doing (since i couldn't get it working myself)
Add to cardfactory.java
Note that the computer didn't use the counterspell when it was tested. I assume that this card needs to be added to a list before the computer starts using it (judging by the code in ComputerAI_counterSpells.java). I'll leave this to the people that know what they're are doing (since i couldn't get it working myself)
Add to cardfactory.java
- Code: Select all
//*************** START *********** START **************************
else if(cardName.equals("Dissipate")) {
SpellAbility spell = new Spell(card) {
private static final long serialVersionUID = 4207725602400789300L;
@Override
public void resolve() {
//counter spell, remove it from the game
SpellAbility sa = AllZone.Stack.pop();
PlayerZone rfg = AllZone.getZone(Constant.Zone.Removed_From_Play, sa.getSourceCard().getOwner());
AllZone.GameAction.moveTo(rfg, sa.getSourceCard());
}
@Override
public boolean canPlay() {
if(AllZone.Stack.size() == 0) return false;
//see if spell is on stack and that opponent played it
String opponent = AllZone.GameAction.getOpponent(card.getController());
SpellAbility sa = AllZone.Stack.peek();
return sa.isSpell() && opponent.equals(sa.getSourceCard().getController())
&& CardFactoryUtil.isCounterable(sa.getSourceCard());
}
};
card.clearSpellAbility();
card.addSpellAbility(spell);
}//*************** END ************ END **************************
- Code: Select all
Dissipate
1 U U
Instant
Card Text:
Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by Chris H. » 28 Apr 2010, 00:10
I am now in the process of merging in your three card submissions.
Thank you. I tested them and I found a way to get the computer to cast the Dissipate spell.

-
Chris H. - Forge Moderator
- Posts: 6320
- Joined: 04 Nov 2008, 12:11
- Location: Mac OS X Yosemite
- Has thanked: 644 times
- Been thanked: 643 times
Re: Code from Beached As
by Beached As » 28 Apr 2010, 13:12
Cheers Chris, i would add more cards but i'm busy with uni work. I do look forward to adding more when i have time
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by Chris H. » 28 Apr 2010, 13:24
`Beached As wrote:Cheers Chris, i would add more cards but i'm busy with uni work. I do look forward to adding more when i have time
We understand, sometimes real life gets in the way.

-
Chris H. - Forge Moderator
- Posts: 6320
- Joined: 04 Nov 2008, 12:11
- Location: Mac OS X Yosemite
- Has thanked: 644 times
- Been thanked: 643 times
Re: Code from Beached As
by Beached As » 29 Apr 2010, 03:28
Filigree Sages : Shards of Alara Uncommon
Tested for both player and AI. I would like to get it so you can click the card to select the target to untap. Right now a dialog box comes up.
Add to cardfactory_creatures.java
Tested for both player and AI. I would like to get it so you can click the card to select the target to untap. Right now a dialog box comes up.
Add to cardfactory_creatures.java
- Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Filigree Sages")) {
final Ability ability = new Ability(card, "2 U") {
@Override
public boolean canPlayAI() {
CardList list = new CardList();
list.addAll(AllZone.Computer_Play.getCards());
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isArtifact() && c.isTapped() && CardFactoryUtil.canTarget(card, c) && CardUtil.getConvertedManaCost(c.getManaCost()) > 5;
}
});
return list.size() >= 1;
}
@Override
public void resolve() {
if(card.getController().equals(Constant.Player.Human)) {
CardList artifacts = new CardList();
artifacts.addAll(AllZone.Human_Play.getCards());
artifacts.addAll(AllZone.Computer_Play.getCards());
artifacts = artifacts.getType("Artifact");
Object o = AllZone.Display.getChoice("Untap an artifact", artifacts.toArray());
if(o != null) {
Card c1 = (Card) o;
c1.untap();
}
} else {
CardList list = new CardList();
list.addAll(AllZone.Computer_Play.getCards());
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.isArtifact() && c.isTapped() && CardFactoryUtil.canTarget(card, c) && CardUtil.getConvertedManaCost(c.getManaCost()) > 5;
}
});
if(list.size() > 0) {
Card c1 = CardFactoryUtil.AI_getBestArtifact(list);
c1.untap();
}
}
}
};
card.clearSpellAbility();
card.addSpellAbility(ability);
ability.setStackDescription(cardName
+ " Untaps Target Artifact");
ability.setDescription("Untap Target Artifact");
ability.setStackDescription(card.getName()
+ " - Untaps Target Artifact");
card.addSpellAbility(new Spell_Permanent(card) {
private static final long serialVersionUID = -4621346284136305833L;
});
}//*************** END ************ END **************************
- Code: Select all
Filigree Sages
3 U
Artifact Creature Vedalken Wizard
no text
2/3
- Code: Select all
http://www.wizards.com/global/images/magic/general/filigree_sages.jpg
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by slapshot5 » 29 Apr 2010, 05:50
Thanks Beached As. I've committed this. I did a little simplification and fixed up the targeting. (It assumes you can target an untapped artifact, which is in line with the rulings for Icy Manipulator.)Beached As wrote:Filigree Sages : Shards of Alara Uncommon
Tested for both player and AI. I would like to get it so you can click the card to select the target to untap. Right now a dialog box comes up.
Your AI logic is unchanged.
-slapshot5
- slapshot5
- Programmer
- Posts: 1391
- Joined: 03 Jan 2010, 17:47
- Location: Mac OS X
- Has thanked: 25 times
- Been thanked: 68 times
Re: Code from Beached As
by Beached As » 01 May 2010, 00:57
Thanks slapshot, yeh excuse my poor coding, i'm still learning and im still mostly copying and pasting with small changes.
Anywho, heres a new card:
Doomed Necromancer : Tenth Edition Rare
Add to cardfactory_creatures.java:
Anywho, heres a new card:
Doomed Necromancer : Tenth Edition Rare
Add to cardfactory_creatures.java:
- Code: Select all
//*************** START *********** START **************************
if(cardName.equals("Doomed Necromancer")) {
final SpellAbility ability = new Ability_Tap(card, "B") {
private static final long serialVersionUID = -6432831150810562390L;
@Override
public boolean canPlayAI() {
return getCreatures().length != 0;
}
public Card[] getCreatures() {
CardList creature = new CardList();
PlayerZone zone = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
creature.addAll(zone.getCards());
creature = creature.getType("Creature");
creature = creature.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getNetAttack() > 4;
}
});
return creature.toArray();
}
@Override
public void chooseTargetAI() {
Card c[] = getCreatures();
Card biggest = c[0];
for(int i = 0; i < c.length; i++)
if(biggest.getNetAttack() < c[i].getNetAttack()) biggest = c[i];
setTargetCard(biggest);
}
@Override
public void resolve() {
if(card.getController().equals(Constant.Player.Human)) {
CardList creature = new CardList();
PlayerZone zone = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
creature.addAll(zone.getCards());
creature = creature.getType("Creature");
if(creature.size() != 0) {
Object o = AllZone.Display.getChoice("Choose a creature from the graveyard to return to the battlefield", creature.toArray());
if(o != null) {
Card c = (Card) o;
if(AllZone.GameAction.isCardInZone(c, zone)) {
PlayerZone play = AllZone.getZone(Constant.Zone.Play, c.getController());
AllZone.GameAction.moveTo(play, c);
AllZone.GameAction.sacrifice(card);
}
}
} else {
AllZone.GameAction.sacrifice(card);
}
} else {
CardList creature = new CardList();
PlayerZone zone = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
creature.addAll(zone.getCards());
creature = creature.getType("Creature");
Card c[] = getCreatures();
Card biggest = c[0];
for(int i = 0; i < c.length; i++)
if(biggest.getNetAttack() < c[i].getNetAttack()) biggest = c[i];
if(creature.size() != 0) {
if(biggest != null) {
if(AllZone.GameAction.isCardInZone(biggest, zone)) {
PlayerZone play = AllZone.getZone(Constant.Zone.Play, biggest.getController());
AllZone.GameAction.moveTo(play, biggest);
AllZone.GameAction.sacrifice(card);
}
}
} else {
AllZone.GameAction.sacrifice(card);
}
}
}//resolve()
};
card.clearSpellAbility();
card.addSpellAbility(ability);
ability.setStackDescription(cardName
+ " gets sacrificed to return target creature card from your graveyard to the battlefield");
ability.setDescription("B, Tap: Sacrifice Doomed Necromancer: Return target creature card from your graveyard to the battlefield");
card.addSpellAbility(new Spell_Permanent(card) {
private static final long serialVersionUID = -462134621235305833L;
});
}//*************** END ************ END **************************
- Code: Select all
Doomed Necromancer
2 B
Creature Human Cleric Mercenary
B, Tap, Sacrifice Doomed Necromancer: Return target creature card from your graveyard to the battlefield.
2/2
- Code: Select all
http://www.wizards.com/global/images/magic/general/doomed_necromancer.jpg
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by Beached As » 01 May 2010, 02:15
Stitch Together : Judgment Uncommon
Add to Cardfactory.java
Add to Cardfactory.java
- Code: Select all
//*************** START *********** START **************************
else if(cardName.equals("Stitch Together")) {
final SpellAbility spell = new Spell(card) {
private static final long serialVersionUID = -57996914115026814L;
@Override
public void resolve() {
CardList threshold = new CardList();
PlayerZone grave = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
threshold.addAll(grave.getCards());
Card c = getTargetCard();
if(threshold.size() >= 7) {
if(AllZone.GameAction.isCardInZone(c, grave)) {
PlayerZone play = AllZone.getZone(Constant.Zone.Play, card.getController());
AllZone.GameAction.moveTo(play, c);
}
}
else {
if(AllZone.GameAction.isCardInZone(c, grave)) {
PlayerZone hand = AllZone.getZone(Constant.Zone.Hand, card.getController());
AllZone.GameAction.moveTo(hand, c);
}
}
}//resolve()
@Override
public boolean canPlay() {
return getCreatures().length != 0;
}
public boolean canPlayAI() {
CardList check = new CardList();
PlayerZone zone = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
check.addAll(zone.getCards());
return getCreaturesAI().length != 0 || check.size() >= 7;
}
public Card[] getCreatures() {
CardList creature = new CardList();
PlayerZone zone = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
creature.addAll(zone.getCards());
creature = creature.getType("Creature");
return creature.toArray();
}
public Card[] getCreaturesAI() {
CardList creature = new CardList();
PlayerZone zone = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
creature.addAll(zone.getCards());
creature = creature.getType("Creature");
creature = creature.filter(new CardListFilter() {
public boolean addCard(Card c) {
return c.getNetAttack() > 4;
}
});
return creature.toArray();
}
@Override
public void chooseTargetAI() {
Card c[] = getCreatures();
Card biggest = c[0];
for(int i = 0; i < c.length; i++)
if(biggest.getNetAttack() < c[i].getNetAttack()) biggest = c[i];
setTargetCard(biggest);
}
};//SpellAbility
card.clearSpellAbility();
card.addSpellAbility(spell);
Input target = new Input() {
private static final long serialVersionUID = -3717723884199321767L;
@Override
public void showMessage() {
Object check = AllZone.Display.getChoiceOptional("Select creature", getCreatures());
if(check != null) {
spell.setTargetCard((Card) check);
stopSetNext(new Input_PayManaCost(spell));
} else stop();
}//showMessage()
public Card[] getCreatures() {
CardList creature = new CardList();
PlayerZone zone = AllZone.getZone(Constant.Zone.Graveyard, card.getController());
creature.addAll(zone.getCards());
creature = creature.getType("Creature");
return creature.toArray();
}
};//Input
spell.setBeforePayMana(target);
}//*************** END ************ END **************************
- Code: Select all
Stitch Together
B B
Sorcery
Return target creature card from your graveyard to your hand. Threshold — Return that card from your graveyard to the battlefield instead if seven or more cards are in your graveyard.
- Code: Select all
http://www.wizards.com/global/images/magic/general/stitch_together.jpg
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by Beached As » 01 May 2010, 02:43
It lags like a ***** but yeh:
Akroma's Memorial : Future Sight Rare
add to GameActionUtil.java under Levitation
Akroma's Memorial : Future Sight Rare
add to GameActionUtil.java under Levitation
- Code: Select all
public static Command Akromas_Memorial = new Command() {
private static final long serialVersionUID = -670715429635395830L;
CardList gloriousAnthemList = new CardList();
public void execute() {
String keyword1 = "Flying";
String keyword2 = "First Strike";
String keyword3 = "Vigilance";
String keyword4 = "Trample";
String keyword5 = "Haste";
String keyword6 = "Protection from Black";
String keyword7 = "Protection from Red";
CardList list = gloriousAnthemList;
Card c;
// reset all cards in list - aka "old" cards
for(int i = 0; i < list.size(); i++) {
c = list.get(i);
c.removeExtrinsicKeyword(keyword1);
c.removeExtrinsicKeyword(keyword2);
c.removeExtrinsicKeyword(keyword3);
c.removeExtrinsicKeyword(keyword4);
c.removeExtrinsicKeyword(keyword5);
c.removeExtrinsicKeyword(keyword6);
c.removeExtrinsicKeyword(keyword7);
}
list.clear();
PlayerZone[] zone = getZone("Akroma's Memorial");
for(int outer = 0; outer < zone.length; outer++) {
CardList creature = new CardList(
zone[outer].getCards());
creature = creature.getType("Creature");
for(int i = 0; i < creature.size(); i++) {
c = creature.get(i);
if(!c.getKeyword().contains(keyword1)) {
c.addExtrinsicKeyword(keyword1);
gloriousAnthemList.add(c);
}
if(!c.getKeyword().contains(keyword2)) {
c.addExtrinsicKeyword(keyword2);
gloriousAnthemList.add(c);
}
if(!c.getKeyword().contains(keyword3)) {
c.addExtrinsicKeyword(keyword3);
gloriousAnthemList.add(c);
}
if(!c.getKeyword().contains(keyword4)) {
c.addExtrinsicKeyword(keyword4);
gloriousAnthemList.add(c);
}
if(!c.getKeyword().contains(keyword5)) {
c.addExtrinsicKeyword(keyword5);
gloriousAnthemList.add(c);
}
if(!c.getKeyword().contains(keyword6)) {
c.addExtrinsicKeyword(keyword6);
gloriousAnthemList.add(c);
}
if(!c.getKeyword().contains(keyword7)) {
c.addExtrinsicKeyword(keyword7);
gloriousAnthemList.add(c);
}
}// for inner
}// for outer
}// execute()
};
- Code: Select all
commands.put("Akromas_Memorial", Akromas_Memorial);
- Code: Select all
cardToEffectsList.put("Akroma's Memorial", new String[] {"Akromas_Memorial"});
- Code: Select all
Akroma's Memorial
7
Legendary Artifact
Creatures you control have flying, first strike, vigilance, trample, haste, and protection from black and from red.
- Code: Select all
http://www.wizards.com/global/images/magic/general/akromas_memorial.jpg
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by Beached As » 01 May 2010, 02:50
I was also wondering, is it possible to add a "counter" which counts the number of spells played each turn (i.e. would go back to zero at the beginning of each turn). It could be useful to add storm cards and cards like Maelstrom Nexus
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
Re: Code from Beached As
by slapshot5 » 01 May 2010, 04:48
Check out Empty the Warrens in CardFactory.java for an example. Though, seems to me, it didn't Storm correctly last time I played it. I know it used to work.Beached As wrote:I was also wondering, is it possible to add a "counter" which counts the number of spells played each turn (i.e. would go back to zero at the beginning of each turn). It could be useful to add storm cards and cards like Maelstrom Nexus
Check out to see if that card works, and if so, use that code as a guide.
-slapshot5
- slapshot5
- Programmer
- Posts: 1391
- Joined: 03 Jan 2010, 17:47
- Location: Mac OS X
- Has thanked: 25 times
- Been thanked: 68 times
Re: Code from Beached As
by Beached As » 01 May 2010, 11:28
Yeh its definitely not doing the storm count right . I've improved the code but since the way the card does the storm count is dodgy ( It counts the cards entering the battlefield and graveyard), i dont think i'll be able to fix all the bugs.
Bugs fixed
- Fixed the storm count getting an artificial bonus of 2 on the first turn (i think because it was counting the mana pool as entering the battlefield)
- Increasing the number of times the spell plays by 1
- Storm count doesn't include tokens anymore
Bugs left (which i can find)
- Storm count doesn't account for spells that get exiled
- Storm count doesn't account for spells cast before the storm spell which are on the stack. In fact it will count cards which resolve after the storm spell (because they enter the graveyard before the storm spell resolves)
- If a card is moved from any zone into the battlefield or graveyard, then that will add to the storm count (A creature dying or discarding a card from the hand for example)
- It's resolving the spell as one instead X times, where X is the storm count + 1
Code: CardFactory.java
Bugs fixed
- Fixed the storm count getting an artificial bonus of 2 on the first turn (i think because it was counting the mana pool as entering the battlefield)
- Increasing the number of times the spell plays by 1
- Storm count doesn't include tokens anymore
Bugs left (which i can find)
- Storm count doesn't account for spells that get exiled
- Storm count doesn't account for spells cast before the storm spell which are on the stack. In fact it will count cards which resolve after the storm spell (because they enter the graveyard before the storm spell resolves)
- If a card is moved from any zone into the battlefield or graveyard, then that will add to the storm count (A creature dying or discarding a card from the hand for example)
- It's resolving the spell as one instead X times, where X is the storm count + 1
Code: CardFactory.java
- Code: Select all
//*************** START *********** START **************************
else if(cardName.equals("Empty the Warrens")) {
SpellAbility spell = new Spell(card) {
private static final long serialVersionUID = 1439643889038241969L;
@Override
public void resolve() {
int stormCount = 0;
//get storm count
CardList list = new CardList();
list.addAll(AllZone.Human_Graveyard.getCards());
list.addAll(AllZone.Computer_Graveyard.getCards());
list.addAll(AllZone.Human_Play.getCards());
list.addAll(AllZone.Computer_Play.getCards());
list = list.filter(new CardListFilter() {
public boolean addCard(Card c) {
return (c.isToken() == false);
}
});
for(int i = 0; i < list.size(); i++)
if(list.get(i).getTurnInZone() == AllZone.Phase.getTurn()) stormCount++;
if(AllZone.Phase.getTurn() == 1) {
for(int i = 0; i < 2 * (stormCount - 1); i++)
CardFactoryUtil.makeToken("Goblin", "R 1 1 Goblin", card, "R", new String[] {
"Creature", "Goblin"}, 1, 1, new String[] {""});
} else {
for(int i = 0; i < 2 * (stormCount + 1); i++)
CardFactoryUtil.makeToken("Goblin", "R 1 1 Goblin", card, "R", new String[] {
"Creature", "Goblin"}, 1, 1, new String[] {""});
}
}//resolve()
};
card.clearSpellAbility();
card.addSpellAbility(spell);
}//*************** END ************ END **************************
- Beached As
- Programmer
- Posts: 110
- Joined: 23 Feb 2010, 07:48
- Has thanked: 0 time
- Been thanked: 0 time
63 posts
• Page 1 of 5 • 1, 2, 3, 4, 5
Who is online
Users browsing this forum: No registered users and 48 guests