70395

Coffescript Callbacks & Functions

Question:

I have this function and I am trying to use two large functions made up of smaller ones and one while loops. One for when it's the players turn, one when it's the opponent's turn, and a while loop when the player has won. When I run it in the browser, the buttons do not do anything. What is wrong?

$attackButtonClick = (opponent) -> if playersTurn and not player.Win player.attack(opponent) if opponent.currentHealth <= 0 player.Win = true; allOff() postBattle(opponent) else playersTurn = false; opponentTurn(opponent) $defendButtonClick = (opponent) -> if playersTurn and not player.Win player.defend() appendToBattleOutputBox "

Your defense has been doubled for one turn.

" if opponent.currentHealth <= 0 player.Win = true; allOff() return postBattle(opponent) else playersTurn = false; opponentTurn(opponent) $useItemButtonClick = (opponent) -> if playersTurn and not player.Win if $useItemSelection.html().length > 15 itemBeingUsed = $("select[name='useItemSelection'] option:selected").text() switch itemBeingUsed when "Book of Spells" player.Items.bookOfSpells.use() if player.Items.bookOfSpells.effect is "burn" or player.Items.bookOfSpells.effect is "poison" appendToBattleOutputBox "

You have #{player.Items.bookOfSpells.effect}ed #{opponent.Name}.

" if player.Items.bookOfSpells.effect is "burn" opponent.Burned = true; else opponent.Poisoned = true; else appendToBattleOutputBox "

You have frozen #{opponent.Name}.

" opponent.Frozen = true; player.Items.bookOfSpells.used = true; when "Shield Charm" if (player.Items.shieldCharm.used is false) player.Items.shieldCharm.use() appendToBattleOutputBox "

You will block the next attack with you shield charm.

" player.Items.shieldCharm.used = true; when "Normal Potion" player.Items.normalPotion.use() if opponent.currentHealth <= 0 player.Win = true; allOff() postBattle(opponent) else playersTurn = false; opponentTurn(opponent) else noUsableItemP = $("<p id='noUsableItemP'>You have no usable items. Select another command.

") $battleCommandPromptDiv.empty() $battleCommandPromptDiv.append(noUsableItemP) battle = (opponent) -> console.log(p) appendToBattleOutputBox "

You make the first move!

" battleInProgress = yes; playersTurn = true; playerTurn = (opponent) -> $attackButton.click -> $attackButtonClick(opponent) $defendButton.click -> $defendButtonClick(opponent) $useItemButton.click -> $useItemButtonClick(opponent) status.Poison("opponent", opponent) if opponent.Poisoned status.Burn("opponent", opponent) if opponent.Burned status.Freeze("opponent", opponent) if opponent.Frozen opponent.undefend() if opponent.defenseDoubled or opponent.defenseTripled refresh(opponent) opponentTurn = (opponent) -> if not playersTurn and not player.Win if rndmNumber(10) > 5 thisTurnAttack = opponent.attack(opponent.Attack, opponent.Luck) player.currentHealth -= thisTurnAttack refresh(opponent) if thisTurnAttack is 0 then appendToBattleOutputBox "<p class='right'>You have blocked the attack.

" else appendToBattleOutputBox "<p class='right'>#{thisTurnAttack} damage has been inflicted upon you.

" else opponent.defend() appendToBattleOutputBox "<p class='right'>#{opponent.Name} has doubled his defense for one turn.

" player.undefend() if player.defenseDoubled or player.defenseTripled player.Poisoned = true if (opponent.Type is "Snake" and rndmNumber(10) > 7) player.Burned = true if (opponent.Name is "Salamander" and rndmNumber(10) > 3) or opponent.Name is "Fire Dragon" player.Frozen = true if (opponent.Name is "Penguin" and rndmNumber(10) > 5) or (opponent.Name is "Wolf" and rndmNumber(10) > 6) or (opponent.Name is "Polar Bear" and rndmNumber(10) > 4) status.Poison("player") if player.Poisoned status.Burn("player") if player.Burned status.Freeze("player") if player.Frozen if player.currentHealth <= 0 then postBattle(opponent) else playerTurn(opponent) playersTurn = true; playerTurn(opponent) while (player.Win is true) player.Win is false; postBattle(opponent)

P.S. disregard the semicolons after true and false. I know they aren't supposed to be there.

<hr />

UPDATE

Now my problem is that when I try calling opponentTurn it is not defined. I have tried looking up a way to hoist a function in Coffeescript, but have found nothing. Is there a way to hoist the function or just another way to order the code?

$attackButtonClick = (opponent) -> console.log(playersTurn) if playersTurn and not player.Win player.attack(opponent) if opponent.currentHealth <= 0 player.Win = true; allOff() postBattle(opponent) else playersTurn = false; opponentTurn(opponent) $defendButtonClick = (opponent) -> if playersTurn and not player.Win player.defend() appendToBattleOutputBox "

Your defense has been doubled for one turn.

" if opponent.currentHealth <= 0 player.Win = true; allOff() return postBattle(opponent) else playersTurn = false; opponentTurn(opponent) $useItemButtonClick = (opponent) -> if playersTurn and not player.Win if $useItemSelection.html().length > 15 itemBeingUsed = $("select[name='useItemSelection'] option:selected").text() switch itemBeingUsed when "Book of Spells" player.Items.bookOfSpells.use() if player.Items.bookOfSpells.effect is "burn" or player.Items.bookOfSpells.effect is "poison" appendToBattleOutputBox "

You have #{player.Items.bookOfSpells.effect}ed #{opponent.Name}.

" if player.Items.bookOfSpells.effect is "burn" opponent.Burned = true; else opponent.Poisoned = true; else appendToBattleOutputBox "

You have frozen #{opponent.Name}.

" opponent.Frozen = true; player.Items.bookOfSpells.used = true; when "Shield Charm" if (player.Items.shieldCharm.used is false) player.Items.shieldCharm.use() appendToBattleOutputBox "

You will block the next attack with you shield charm.

" player.Items.shieldCharm.used = true; when "Normal Potion" player.Items.normalPotion.use() if opponent.currentHealth <= 0 player.Win = true; allOff() postBattle(opponent) else playersTurn = false; opponentTurn(opponent) else noUsableItemP = $("<p id='noUsableItemP'>You have no usable items. Select another command.

") $battleCommandPromptDiv.empty() $battleCommandPromptDiv.append(noUsableItemP) battle = (opponent) -> appendToBattleOutputBox "

You make the first move!

" battleInProgress = yes; playerTurn = (opponent) -> $attackButton.click -> $attackButtonClick(opponent) $defendButton.click -> $defendButtonClick(opponent) $useItemButton.click -> $useItemButtonClick(opponent) status.Poison("opponent", opponent) if opponent.Poisoned status.Burn("opponent", opponent) if opponent.Burned status.Freeze("opponent", opponent) if opponent.Frozen opponent.undefend() if opponent.defenseDoubled or opponent.defenseTripled refresh(opponent) opponentTurn = (opponent) -> if not playersTurn and not player.Win if rndmNumber(10) > 5 thisTurnAttack = opponent.attack(opponent.Attack, opponent.Luck) player.currentHealth -= thisTurnAttack refresh(opponent) if thisTurnAttack is 0 then appendToBattleOutputBox "<p class='right'>You have blocked the attack.

" else appendToBattleOutputBox "<p class='right'>#{thisTurnAttack} damage has been inflicted upon you.

" else opponent.defend() appendToBattleOutputBox "<p class='right'>#{opponent.Name} has doubled his defense for one turn.

" player.undefend() if player.defenseDoubled or player.defenseTripled player.Poisoned = true if (opponent.Type is "Snake" and rndmNumber(10) > 7) player.Burned = true if (opponent.Name is "Salamander" and rndmNumber(10) > 3) or opponent.Name is "Fire Dragon" player.Frozen = true if (opponent.Name is "Penguin" and rndmNumber(10) > 5) or (opponent.Name is "Wolf" and rndmNumber(10) > 6) or (opponent.Name is "Polar Bear" and rndmNumber(10) > 4) status.Poison("player") if player.Poisoned status.Burn("player") if player.Burned status.Freeze("player") if player.Frozen if player.currentHealth <= 0 then postBattle(opponent) else playerTurn(opponent) playersTurn = true; playerTurn(opponent) while (player.Win is true) player.Win is false; postBattle(opponent)

P.S. disregard the semicolons after true and false. I know they aren't supposed to be there.

Answer1:

There's no easy way to explain this. You've fundamentally misunderstood how Javascript (and by extension Coffeescript) works.

Javascript is an asynchronous language which doesn't block on io. When you do things like this

while (playerTurn is true and player.Win is false) $attackButton.click -> player.attack(opponent) ...

You're entering a loop which registers a callback for a click event and then runs the loop again. This is happening, hundreds, probably thousands of times because of the nature of your while loop. That's why your browser has crashed.

A good way to check this kind of thing out is to use debugging statements with console.log, this way you'll be able to see how often different sections of your code are being entered by looking at the number of printed outputs in your console.

You need to restructure the logic of your game, so that rather than doing while loops and waiting for blocking conditions to happen; you have a set of functions that call each other when events take place.

What I would suggest is something like this:

attack = -> # conditions go here instead if playerTurn and not player.Win player.attack(opponent) if opponent.currentHealth <= 0 player.Win = true allOff() # return statements aren't needed inside callback functions postBattle(opponent) else playerTurn = false; # not inside a while loop $attackButton.click attack

You can use this model for any 'event' in your game e.g. a button click or a changed condition etc.

<hr />

UPDATE

Coffeescript doesn't support function hoisting, so you've only really got two options here. Try to untangle your code so that you can write it out in fashion that doesn't require using functions that haven't yet been defined, <strong>or</strong> as I prefer to do, use <a href="http://livescript.net/" rel="nofollow">Livescript</a>.

Livescript has a lot of compatibility with Coffeescript and it brings a lot of useful features to the table with a similar syntax. One of the features is function hoisting. Your hoisted function would be declared like this:

function opponentTurn (opponent) ... # rather than opponentTurn = (opponent) -> ...

Which will respectively compile to

function opponentTurn(opponent){ ... } var opponentTurn; opponentTurn = function(opponent){ ... };

I couldn't recommend Livescript enough as an alternative to Coffeescript.

<a href="http://livescript.net/blog/ten-reasons-to-switch-from-coffeescript.html" rel="nofollow">http://livescript.net/blog/ten-reasons-to-switch-from-coffeescript.html</a>

Recommend

  • Parse fetchIfNeeded() not working
  • Multiple inclusion in multiple files
  • In objective C, when presenting a new scene, how do you remove the old scene?
  • Get Users end point location using Uber API
  • WebGL: Access buffer from shader
  • Files in gitignore
  • Cannot save model when using ember render helper
  • Android Database Error - getWriteableDatabase
  • Use tryCatch within R loop
  • How to use jQuery's $.post() method with async/await and typescript
  • TFS 2015 - Waiting for an agent to be requested
  • ListItem.Attributes.Add not working
  • Detect when Facebook like button is clicked
  • Needing to do .toArray() to get output of mongodb .find() on key name not value
  • onBackPressed() not being executed
  • Meteor: Do Something On Email Verification Confirmation
  • Q promise. Difference between .when and .then
  • Jenkins: How To Build multiple projects from a TFS repository?
  • How do I fake an specific browser client when using Java's Net library?
  • Volley JsonObjectRequest send headers in GET Request
  • Is there a amazon webstore API for customers?
  • How to get a value (ex: baseURL) in every Karate feature?
  • Perl system calls when running as another user using sudo
  • Can a Chrome extension content script make an jQuery AJAX request for an html file that is itself a
  • Upload files with Ajax and Jquery
  • ORA-29908: missing primary invocation for ancillary operator
  • How to get next/previous record number?
  • Apache 2.4 - remove | delete | uninstall
  • How to pass list parameters for each object using Spring MVC?
  • php design question - will a Helper help here?
  • Run Powershell script from inside other Powershell script with dynamic redirection to file
  • json Serialization in asp
  • AngularJs get employee from factory
  • Rails 2: use form_for to build a form covering multiple objects of the same class
  • Proper way to use connect-multiparty with express.js?
  • How do you join a server to an Active Directory (domain)?
  • How to stop GridView from loading again when I press back button?
  • How does Linux kernel interrupt the application?
  • jQuery Masonry / Isotope and fluid images: Momentary overlap on window resize
  • How do I use LINQ to get all the Items that have a particular SubItem?