We all have a good fun creating these custom cards and debating usefulness, synergy, balance, etc. But what if Blizzard provided a way to let us actually get those cards into a custom game?
My idea would be to have a third category of play, similar to Wild, but that would allow custom, programmable cards to be created. This category (named "Custom") would allow for all Wild cards, plus 2 custom created cards (allowing for 2 copies of regular cards, or 1 copy of a Legendary). The custom cards data would be sent among the players client when the game is being prepared, and would be compact enough so very little data is sent. Blizzard would have a library of possible images to use and summon/attack/death sounds. You would be able to add custom images and sounds, but those would only be visible on your own side (to avoid profanity). Players would be able to flag offensive or disruptive cards and report them, of course.
Creating a vanilla card would be very easy, similar to how the Hearthstone web card creator allows. Pick a type (minion, weapon, spell), a class (neutral or priest, warlock, mage, etc), optional tribe, mana cost, attack and health (or durability) if it's a minion or weapon. There would be no way to validate balance, of course, so Custom games would often see some troll players having 0 mana 12/12, of course, but the nature of that game category is experimenting with other players (there would be no rank and no coin quest - unless specific, like "create a card and test it").
Other options you can set without actually coding a script are certain standard elements that are part of a card, such as Stealth, Taunt, Windfury, "Can't attack", "Can't be targeted by spells or hero powers", "Can't attack heroes", etc.
Now, creating specific effects for a card would require some coding, and I believe I've got a way to easily script it, if you are familiar with coding. I will now try to explain how that would work - please bear in mind this will be easier to understand to those who are familiar with any kind of coding.
Card effects are coded in a simple text editor with a parser and interpreter. It will use object oriented notation, albeit a simplified one.
There are two types of operations, objects and functions.
Objects are elements that are part of the game, such as cards, a board, heroes, etc. All objects are globally defined. Functions are events that may be triggered at a certain stage of the game, or at a certain condition, and may be nested (one being called inside the other) They don't necessarily require parameters.
Let's start with a few examples.
The object "this" always refers to that instance of a card, and has some properties that may be accessed or changed. It refers to ONE single card instance for a single game.
Now, the other game elements may be accessed by object notation. There are two main objects at a higher level, "friendly" and "enemy". From these, you may drill down to what you need, for instance:
friendly.hero.health enemy.hero.attack enemy.hero.hasWeapon friendly.board //this is an array of the minions on the board friendly.deck //this is an array of the cards in the current deck friendly.board[1].isDamaged //is the first minion from the left damaged? friendly.board.isDamaged //returns an array of all damaged minions enemy.board[rnd].isDamaged //returns one random enemy damaged minion friendly.dead.isMurloc //returns an array of all friendly killed murlocs
Functions are delimited by brackets { } and will state a certain event or element that will be triggered, conditioned or set at a certain part of the game. So, to better illustrate this, let me give some examples based on real cards - this is the WHOLE code needed for each (remember elements such as attack, mana and health are defined outside).
//using rnd(x,y), pick a random number between x and y //in our case, it's between 0 and the maximum amount of enemy characters //The enemy returned gets a damage of 1
//searchMinion allows you to return a minion based on name OR card code (all cards will have a numeric code) //You'd need to have the mirror image minion created beforehand
//You can also use an else-target function to establish a target that is outside all previously defined //The target function accepts "character" or "minion" as parameters, and you can have all parameters you want to define //what are the elements acceptable for a target, all being an "AND" statement //if you want an "OR", you need to create another target function
I intend to improve and expand upon this, but I feel that a LOT of cards can be programmable using this technique. I will give an example for more advanced cards (such as Cthun) soon.
Also, you guys can post here the custom cards you have created, and I will try to post the code it would use.
There are some more elements I am adjusting, I intend to have an actual script specification that could be applied to any circumstance and is flexible but consistent. Then, who knows, maybe someone is interested in creating a way to simulate hearthstone play and card creation with it.
Rollback Post to RevisionRollBack
"You keep him in here, and make sure, he doesn't leave the room."
This is fairly impressive, especially in how surprisingly simple it is. It would be interesting to see if this sort of basic coding could be implemented into a cardbuilder format like hearthcards for the simple-er designs, though you've laid it out in a very clear format. I think it might be a bit smurf-y to implement directly into hearthstone because there's always that one guy who wants to win son much he'll just overpower the format just to say that he could, but I like the groundwork you put into this. Perhaps if this could be implemented into a barebones simulator it might be really useful to test for balance and actual random effects. I've worked on custom CCGs (And fake hearthstone cards) but the ability totest specific random effects has always eluded, so this may come in really handy. Thanks and cheers!
This doesn't work as the effect would stay active if the card were transformed or returned to a hand. You can't really make aura effects by having them do something on play and then undoing it when it dies.
I ran into this problem when I was coding a hearthstone simulator that was intended to work along these lines (being able to easily code in new card effects), and for the most part it worked except for when it came to auras. I wasn't able to code dire wolf alpha, even though I did have a system built in for auras of some kind.
friendly.hand.count = 10; //count can be overriden and establish a new value until reset enemy.hand.count = 10;
}
Sorry that's not how arrays, lists, or any kind of collections work.
I know. This is a object, not an actual array data type. i used the name "array" to make it easier to understand. Keep in mind this is supposed to work ONLY for Hearthstone, and not meant to be used for anything else. Therefore, it's a special object that not only allows you to access elements on [ ] , it also has its own methods and dynamic variables.
I am compiling a list of functions and usage for this. It is meant to be as easy as possible, and as flexible as possible.
Rollback Post to RevisionRollBack
"You keep him in here, and make sure, he doesn't leave the room."
OBJECTS - These are the main elements of objects that are part of my code suggestion. This is incomplete and possibly could be adjusted, but it gives a clear idea of what could be provided for regular gamers to make their custom creations actually playable (on a controlled custom game mode). I will add a "triggers" section later, explaining the other element of the whole custom code idea.
Main objects:
friendly enemy
Properties for friendly and enemy:
.hero: returns a hero object.
.cards: returns an array object of all cards a player assembled for this deck for that game, regardless of their status. .hand: returns an array object of all the cards in the player's hand. .deck: returns an array object of all the cards in the player's deck. .dead: returns an array object of all the cards that were tossed out of play due to killing, casting, destroying or discarding.
.board: returns an array object of minions in the board. .characters: returns an array object of the minions in the board, plus the hero in position 0.
.rope: integer variable for the current rope time. .resetRope(): reset the rope time to the default configurations.
Properties for hero:
.id: returns an integer that indicates which hero is being played, it can be overriden to change the hero.
.isFriendly: boolean, returns if a hero is friendly (useful for trigger functions). Cannot be overriden.
.health: returns the current hero's health. It can be overriden, but it's better to use the takeDamage function. .maxHealth: returns the maximum amount a hero's health can reach. It can be overriden. .resetMaxHealth(): set the hero's maximum health to the default value. To heal a hero to full health, just do frienly.hero.health = frienly.hero.maxHealth;
.armor: returns the current hero's armor. It can be overriden. .attack: returns the current hero's attack power. It can be overriden.
.destroyArmor(x): destroys an x (integer) amount of the hero's armor, if it exists. .takeDamage(x): takes an x (integer) amount of damage. .heal(x): heals an x (integer) amount.
.heroPowerMana: integer variable for the hero power mana cost, can be overriden. .heroPowerActive: boolean variable indicating if the hero power can be used or not, can be overriden. .heroPowerId: an integer value that indicates which hero power is associated with this hero, can be overriden.
Properties for cards, hand, dead, board & character:
[x]: the array position will return a single card object that could be a minion, spell or weapon. characters[0] returns the hero object. .count: returns an integer indicating the count of card objects for each array. It can be overriden with caution that this may bring issues with loops. characters.count doesn't count the hero. .resetCount(): resets the count for the appropriated amount of cards or characters.
.summon(card): adds a card (card is a generic object; hero, minion, weapon or spell all extend it) to the array, and applying all effects (summoning minion to board, adding card to hand, etc). The position is determined by the game rules (hand cards and minions, go to the right, deck cards are added at a random spot). Will not add if the board is full, will discard if hand is full.
.summon(card, x): adds a card to the array at the X integer position. The cards equal or larger to this position will be pushed to the right, and discarded if reach the maximum.
Properties for card objects:
.id: the numeric id to identify the card. .name: a string for the card's name.
.mana: the mana cost. Can be overriden. .resetMana(): reset to the default card mana cost.
.makeCopyHand(boolean, boolean, x): add a copy of the card to a hand, same parameters as above. The first boolean parameter indicates if the card status are kept (true) or if it's set to default (false). The second boolean indicates if it's friendly (true) or enemy (false). The third parameter indicates a position (optional). The parameters are optional, and assume true values as default.
.makeCopyDeck(boolean, boolean, x): add a copy of the card to a deck, same parameters as above.
.attack: gives the minion or weapon attack, can be overriden. Spells have default 0 and no changes if overriden.
.health: gives the minion health or weapon durability, can be overriden. Spells have default 0 and no changes if overriden.
.isMinion: boolean, returns if its a minion, cannot be overriden. .isWeapon .isSpell
Properties for minions (extend the card object):
.isMurloc, .isDemon, .isPirate: etc... pretty straightforward. Minion subtypes cannot be coded (must "come from Blizzard").
.makeCopyBoard(boolean, boolean, x): add a copy of the card on the board.
Rollback Post to RevisionRollBack
"You keep him in here, and make sure, he doesn't leave the room."
To post a comment, please login or register a new account.
We all have a good fun creating these custom cards and debating usefulness, synergy, balance, etc. But what if Blizzard provided a way to let us actually get those cards into a custom game?
My idea would be to have a third category of play, similar to Wild, but that would allow custom, programmable cards to be created. This category (named "Custom") would allow for all Wild cards, plus 2 custom created cards (allowing for 2 copies of regular cards, or 1 copy of a Legendary). The custom cards data would be sent among the players client when the game is being prepared, and would be compact enough so very little data is sent. Blizzard would have a library of possible images to use and summon/attack/death sounds. You would be able to add custom images and sounds, but those would only be visible on your own side (to avoid profanity). Players would be able to flag offensive or disruptive cards and report them, of course.
Creating a vanilla card would be very easy, similar to how the Hearthstone web card creator allows. Pick a type (minion, weapon, spell), a class (neutral or priest, warlock, mage, etc), optional tribe, mana cost, attack and health (or durability) if it's a minion or weapon. There would be no way to validate balance, of course, so Custom games would often see some troll players having 0 mana 12/12, of course, but the nature of that game category is experimenting with other players (there would be no rank and no coin quest - unless specific, like "create a card and test it").
Other options you can set without actually coding a script are certain standard elements that are part of a card, such as Stealth, Taunt, Windfury, "Can't attack", "Can't be targeted by spells or hero powers", "Can't attack heroes", etc.
Now, creating specific effects for a card would require some coding, and I believe I've got a way to easily script it, if you are familiar with coding. I will now try to explain how that would work - please bear in mind this will be easier to understand to those who are familiar with any kind of coding.
Card effects are coded in a simple text editor with a parser and interpreter. It will use object oriented notation, albeit a simplified one.
There are two types of operations, objects and functions.
Objects are elements that are part of the game, such as cards, a board, heroes, etc. All objects are globally defined. Functions are events that may be triggered at a certain stage of the game, or at a certain condition, and may be nested (one being called inside the other) They don't necessarily require parameters.
Let's start with a few examples.
The object "this" always refers to that instance of a card, and has some properties that may be accessed or changed. It refers to ONE single card instance for a single game.
this.isDamaged
this.isInHand
this.isInBoard
this.isInDeck
this.isDead
this.isDiscarded
this.isStealth
this.manaCost
this.attack
this.health //also durability
Now, the other game elements may be accessed by object notation. There are two main objects at a higher level, "friendly" and "enemy". From these, you may drill down to what you need, for instance:
friendly.hero.health
enemy.hero.attack
enemy.hero.hasWeapon
friendly.board //this is an array of the minions on the board
friendly.deck //this is an array of the cards in the current deck
friendly.board[1].isDamaged //is the first minion from the left damaged?
friendly.board.isDamaged //returns an array of all damaged minions
enemy.board[rnd].isDamaged //returns one random enemy damaged minion
friendly.dead.isMurloc //returns an array of all friendly killed murlocs
Functions are delimited by brackets { } and will state a certain event or element that will be triggered, conditioned or set at a certain part of the game. So, to better illustrate this, let me give some examples based on real cards - this is the WHOLE code needed for each (remember elements such as attack, mana and health are defined outside).
Rumbling Elemental
board{ //meaning, this effect bellow only takes place if the minion is on the board
play(minion){ //when a minion is played - there is also a summoned function
if(minion.hasBattlecry){ //the minion played has battlecry
enemy.board[rnd].takeDamage(2); //pick one random enemy, damage it for 2.
}
}
}
Succubus
battlecry{
friendly.hand[rnd].discard();
}
Tanaris Hogchopper
battlecry{
if(enemy.hand.count == 0){
this.isCharge = true;
}
}
Echoing Ooze
battlecry{
end-of-turn{ //an example of a nested function, this will only trigger if the battlecry was triggered
this.makeCopyBoard();
}
}
Bloodmage Thalnos
board{
friendly.spelldamage = friendly.spelldamage + 1;
}
deathrattle{
friendly.hero.drawCard(1);
}
Arcane Missiles
cast{
//the enemy.character will bring an array of all enemy minions + the enemy hero at position 0
for(1 to Spell(3)){
//The Spell function returns a number changed by the current Spell Damage bonus
// if there is no spell damage, it returns that same number
enemy.character[rnd(0,enemy.character.count)].takeDamage(1);
//using rnd(x,y), pick a random number between x and y
//in our case, it's between 0 and the maximum amount of enemy characters
//The enemy returned gets a damage of 1
}
}
Ice Barrier
secret-trigger(isTrigger){
if(friendly.hero.isAttacked){
isTrigger = true; //if you set the secret-trigger input parameter to yes, this means the trigger was established
}
}
secret{ //only calls if a secre-trigger got a true value
friendly.hero.getArmor(8);
}
Ice Lance
target(character){
if(character.isFrozen){
character.takeDamage(Spell(4));
}
else{
character.setFreeze(1); //parameter = amount of turns frozen
}
}
Mirror Image
cast{
for(1 to 2){
friendly.board.summon(searchMinion("Mirror Image"));
//searchMinion allows you to return a minion based on name OR card code (all cards will have a numeric code)
//You'd need to have the mirror image minion created beforehand
}
}
Demonfire
//You can also use an else-target function to establish a target that is outside all previously defined
//The target function accepts "character" or "minion" as parameters, and you can have all parameters you want to define
//what are the elements acceptable for a target, all being an "AND" statement
//if you want an "OR", you need to create another target function
target(minion.isDemon, minion.isFriendly){
minion.attack = minion.attack + 2;
minion.health = minion.health + 2;
}
target(!minion.isDemon){ //the ! character indicates a negative
minion.takeDamage(Spell(2));
}
Tentacles for Arms
deathrattle{
this.returnHand(1, false); //first parameter, how many copies return to hand, second parameter, keep the current stats
}
Spirit Claws
equiped{
if(friendly.spelldamage > 0){
this.attack = this.attack + 2;
}
}
I intend to improve and expand upon this, but I feel that a LOT of cards can be programmable using this technique.
I will give an example for more advanced cards (such as Cthun) soon.
Also, you guys can post here the custom cards you have created, and I will try to post the code it would use.
"You keep him in here, and make sure, he doesn't leave the room."
I made this card quite a while ago. What code do you think will it use?
//Cardfiend
board{
friendly.hand.count = 10; //count can be overriden and establish a new value until reset
enemy.hand.count = 10;
}
silence{ //triggered when silenced, reverts effects
friendly.hand.countReset();
enemy.hand.countReset();
}
dead{ //dead with no arguments is triggered when this minion's instance is removed from the board
friendly.hand.countReset();
enemy.hand.countReset();
}
There are some more elements I am adjusting, I intend to have an actual script specification that could be applied to any circumstance and is flexible but consistent. Then, who knows, maybe someone is interested in creating a way to simulate hearthstone play and card creation with it.
"You keep him in here, and make sure, he doesn't leave the room."
This is fairly impressive, especially in how surprisingly simple it is. It would be interesting to see if this sort of basic coding could be implemented into a cardbuilder format like hearthcards for the simple-er designs, though you've laid it out in a very clear format. I think it might be a bit smurf-y to implement directly into hearthstone because there's always that one guy who wants to win son much he'll just overpower the format just to say that he could, but I like the groundwork you put into this. Perhaps if this could be implemented into a barebones simulator it might be really useful to test for balance and actual random effects. I've worked on custom CCGs (And fake hearthstone cards) but the ability totest specific random effects has always eluded, so this may come in really handy. Thanks and cheers!
I have thougt about having cards that change the hand limit. But more like `set the hand limit to 5`. Cursed tiny hands.
Din mor er et fallossymbol
This doesn't work as the effect would stay active if the card were transformed or returned to a hand. You can't really make aura effects by having them do something on play and then undoing it when it dies.
I ran into this problem when I was coding a hearthstone simulator that was intended to work along these lines (being able to easily code in new card effects), and for the most part it worked except for when it came to auras. I wasn't able to code dire wolf alpha, even though I did have a system built in for auras of some kind.
I am compiling a list of functions and usage for this. It is meant to be as easy as possible, and as flexible as possible.
"You keep him in here, and make sure, he doesn't leave the room."
OBJECTS - These are the main elements of objects that are part of my code suggestion. This is incomplete and possibly could be adjusted, but it gives a clear idea of what could be provided for regular gamers to make their custom creations actually playable (on a controlled custom game mode). I will add a "triggers" section later, explaining the other element of the whole custom code idea.
Main objects:
friendly
enemy
Properties for friendly and enemy:
.hero: returns a hero object.
.cards: returns an array object of all cards a player assembled for this deck for that game, regardless of their status.
.hand: returns an array object of all the cards in the player's hand.
.deck: returns an array object of all the cards in the player's deck.
.dead: returns an array object of all the cards that were tossed out of play due to killing, casting, destroying or discarding.
.board: returns an array object of minions in the board.
.characters: returns an array object of the minions in the board, plus the hero in position 0.
.rope: integer variable for the current rope time.
.resetRope(): reset the rope time to the default configurations.
Properties for hero:
.id: returns an integer that indicates which hero is being played, it can be overriden to change the hero.
.isFriendly: boolean, returns if a hero is friendly (useful for trigger functions). Cannot be overriden.
.health: returns the current hero's health. It can be overriden, but it's better to use the takeDamage function.
.maxHealth: returns the maximum amount a hero's health can reach. It can be overriden.
.resetMaxHealth(): set the hero's maximum health to the default value. To heal a hero to full health, just do frienly.hero.health = frienly.hero.maxHealth;
.armor: returns the current hero's armor. It can be overriden.
.attack: returns the current hero's attack power. It can be overriden.
.destroyArmor(x): destroys an x (integer) amount of the hero's armor, if it exists.
.takeDamage(x): takes an x (integer) amount of damage.
.heal(x): heals an x (integer) amount.
.heroPowerMana: integer variable for the hero power mana cost, can be overriden.
.heroPowerActive: boolean variable indicating if the hero power can be used or not, can be overriden.
.heroPowerId: an integer value that indicates which hero power is associated with this hero, can be overriden.
Properties for cards, hand, dead, board & character:
[x]: the array position will return a single card object that could be a minion, spell or weapon. characters[0] returns the hero object.
.count: returns an integer indicating the count of card objects for each array. It can be overriden with caution that this may bring issues with loops. characters.count doesn't count the hero.
.resetCount(): resets the count for the appropriated amount of cards or characters.
.summon(card): adds a card (card is a generic object; hero, minion, weapon or spell all extend it) to the array, and applying all effects (summoning minion to board, adding card to hand, etc). The position is determined by the game rules (hand cards and minions, go to the right, deck cards are added at a random spot). Will not add if the board is full, will discard if hand is full.
.summon(card, x): adds a card to the array at the X integer position. The cards equal or larger to this position will be pushed to the right, and discarded if reach the maximum.
Properties for card objects:
.id: the numeric id to identify the card.
.name: a string for the card's name.
.mana: the mana cost. Can be overriden.
.resetMana(): reset to the default card mana cost.
.makeCopyHand(boolean, boolean, x): add a copy of the card to a hand, same parameters as above. The first boolean parameter indicates if the card status are kept (true) or if it's set to default (false). The second boolean indicates if it's friendly (true) or enemy (false). The third parameter indicates a position (optional). The parameters are optional, and assume true values as default.
.makeCopyDeck(boolean, boolean, x): add a copy of the card to a deck, same parameters as above.
.attack: gives the minion or weapon attack, can be overriden. Spells have default 0 and no changes if overriden.
.health: gives the minion health or weapon durability, can be overriden. Spells have default 0 and no changes if overriden.
.isMinion: boolean, returns if its a minion, cannot be overriden.
.isWeapon
.isSpell
Properties for minions (extend the card object):
.isMurloc, .isDemon, .isPirate: etc... pretty straightforward. Minion subtypes cannot be coded (must "come from Blizzard").
.makeCopyBoard(boolean, boolean, x): add a copy of the card on the board.
"You keep him in here, and make sure, he doesn't leave the room."