I have in the global space a variable set as follows:
let hoverMessageLock = 0;
This allows the function below to operate how I want. Its function is to add a message upon button hover and then delete said message and replace it with a new message upon another hover.
The issue I'm having is I would like to NOT use the global space for variables and instead put it inside the function itself. However the function itself is getting called over and over again per each hover event, so if I set hoverMessageLock = 0 in the function scope, it just keeps getting set to 0 over and over again. Therefor my switch statement doesn't work as intended as when its in the global scope.
I know in other programming languages that utilizes classes, you can set variables in the class scope and then alter them in the same way you would global scope inside your functions. Is there a way to do this as well here in Javascript?
let macroManager = {
hoverMessage: function(message, style = "hovertext noTextSelect"){
switch (hoverMessageLock){
case 0:
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
break;
}
},
}
You already have an object, and that works nicely, just as you describe in class-based languages. Classes are simply a way of creating objects, and objects are all you need here.
Just add the property to your object:
let macroManager = {
hoverMessageLock: 0,
hoverMessage: function(message, style = "hovertext noTextSelect"){
switch (this.hoverMessageLock){
case 0:
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
this.hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage(""+message+"", 0, 0, style);
break;
}
},
}
Note that, depending on how you use the hoverMessageLock method, it's possible its this context could be lost (eg if you use it as an event handler) - this is easily fixed by defining it as an arrow function instead: hoverMessage: (message, style = "hovertext noTextSelect") => { ... }.
I would also recommend, if 0 and 1 are the only intended values of the hoverMessageLock variable/property, using boolean false/true values instead.
You can use an Immediately Executed Function Expression (IIFE) for this.
let macroManager = (function(hoverMessageLock) {
return {
hoverMessage: function(message, style = "hovertext noTextSelect") {
switch (hoverMessageLock) {
case 0:
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
break;
}
},
};
})(0);
class macroManger{
constructor(){
this.hoverMessageLock = 0;
}
hoverMessage(message, style = "hovertext noTextSelect"){
switch (this.hoverMessageLock) {
case 0:
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
this.hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
break;
}
}
}
macroManger1 = new macroManger();
macroManger1.hoverMessage(...);
You could create a local scope using the IIFE expression:
var macroManager = getMacroManager();
function getMacroManager() {
return (function () {
var hoverMessageLock = 0;
return {
hoverMessage: function (message, style = "hovertext noTextSelect") {
switch (hoverMessageLock) {
case 0:
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
hoverMessageLock = 1;
break;
case 1:
uiManager.messageDirector.removeMessage();
uiManager.messageDirector.addMessage("" + message + "", 0, 0, style);
break;
}
},
};
})();
}
So each time you call getMacroManager() you will get a separate instance, thus, hoverMessageLock will work as a local variable, which is what you expect to happen.
Here's more info about IIFE:
https://developer.mozilla.org/en-US/docs/Glossary/IIFE.
Related
I am getting a (what seems to be) random promise error during my switch statement below. I have created a random number, and associated it with a given array items position. I use this position inside my switch statement to play a specific sound. When testing this inside the browser I sometimes get an in promise exception like below:
playSound # game.js:29
nextSequence # game.js:18
(anonymous) # game.js:45
Even though I get this error everything works as intended. EDIT PIECE: When debugging with debugger; the above is present intermittently
//store colors
var buttonColors = [
"green", //0
"red", //1
"yellow", //2
"blue" //3
]
gamePattern = [ /*Added From nextSequence*/ ]
//Generate a random number
function nextSequence() {
randomNumber = Math.floor(Math.random() * 4)
randomChosenColor = buttonColors[randomNumber];
gamePattern.push(randomChosenColor);
$(`#` + randomChosenColor).fadeOut(100).fadeIn(100).delay(200);
playSound(randomChosenColor);
}
function playSound(color) {
switch (color) {
case 'green':
var greenButton = new Audio('sounds/green.mp3');
greenButton.play();
break;
case 'red':
var redButton = new Audio(`sounds/red.mp3`);
redButton.play();
break;
case `yellow`:
var yellowButton = new Audio(`sounds/yellow.mp3`);
yellowButton.play();
break;
case `blue`:
var blueButton = new Audio(`sounds/blue.mp3`);
blueButton.play();
break;
default:
console.log(`Play Sound Error in playSound Function`)
}
}
nextSequence();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
In C you can scope a variable to a switch case, like this.
With javascript I get unexpected token using the following:
const i = 1
switch (i) {
// variables scoped to switch
var s
var x = 2342
case 0:
s = 1 + x
break
case 1:
s = 'b'
break
}
Is there another way to do this or should I just declare my variables outside the switch?
EDIT:
This is a workaround I considered but it didn't end up working. The reason being that each case has its own scope.
const i = 1
switch (i) {
case i:
// variables scoped to switch
var s
var x = 2342
case 0:
s = 1 + x
break
case 1:
s = 'b'
break
}
some alternative:
/* curly braces inside the case */
const i = 1
switch (i) {
case 0: {
let x = 2342;
let s = 1 + x;
console.log(x+' & '+s+' from inside');
} break;
case 1: {
let x = 2342;
let s = 'b';
console.log(x+' & '+s+' from inside'); /* 2342 & b from inside */
} break;
}
console.log(x+' & '+s+' from outside'); /* Uncaught ReferenceError */
or
/* curly braces outside the switch */
const i = 1
{
let x = 2342;
let s;
switch (i) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
}
console.log(x+' & '+s+' from inside'); /* 2342 & b from inside */
}
console.log(x+' & '+s+' from outside'); /* Uncaught ReferenceError */
Since var creates variables at function scope anyway, using it is pretty pointless. For this to work at a granularity below function scopes you'll have to use let and a browser/compiler which supports it, and then introduce a new block which you can scope things to (within switch it's simply invalid syntax):
if (true) {
let s;
switch (i) {
...
}
}
This scopes s to the if block, which for all intents and purposes is identical to the "switch scope" here.
If you cannot support let, you'll need to use an IIFE:
(function () {
var s;
switch (...) ...
})();
No, this is invalid syntax. A case or default statement is expected within a switch. You can check the specification here: http://www.ecma-international.org/ecma-262/5.1/#sec-12.11
You can also try entering your code in a JSLinter and see that this an error: http://jslint.com/
Expected 'case' and instead saw 'var'.
The workaround that you're considering would be the same thing as putting them outside the switch statement. Remember, var has function-level scope, not block-level scope. That means they are bound to the entire function containing the switch. You should declare them outside of the switch because that is where they are accessible.
const i = 1;
var s;
var x = 2342;
switch (i) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
default:
break;
}
console.log("s is " + s);
It should be declared outside the switch. The below might help:
var i = 1, x = 2342;
var s;
switch (i)
{
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
}
console.log("s is " + s);
JavaScript defines 3 levels of scope:
Global - Anything not delcared in a function
Function - Anything declared in a function using the var keyword
Block - Anything declared in a block container ({}) using let
So, to creae a scope an entire construct, you have two choices: Function or Block
In order to get the the behavior you are looking for with a function:
const i = 1
function doSwitch(data){
// variables are not scoped to switch, but are
// scoped to the function, which only contains
// the switch.
var s;
var x = 2342;
switch (data) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
default:
s = "other";
}
console.log("s is " + s)
}
doSwitch(18);
Or, in order to get the functionality with a block using let
const i = 1;
// Wrapping the switch in its own block allows for let to work:
{
// variables are not scoped to switch, but are
// scoped to the function, which only contains
// the switch.
let s;
let x = 2342;
switch (i) {
case 0:
s = 1 + x;
break;
case 1:
s = 'b';
break;
default:
s = "other";
}
console.log("s is " + s)
}
// Test:
console.log(s, x); // ReferenceError
I have the following switch statement:
switch (type) {
case 1: // 1 BYTE 8-bit unsigned integer
pointer = count > 4 ? offset : pointer;
for (let i = 0; i < count; i++) {
value += dataView.getUint8(pointer + i);
}
tag.value = parseInt(value, 10);
return tag;
case 3: // 3 SHORT 16-bit unsigned integer
pointer = count > 2 ? offset : pointer;
for (let i = 0; i < count; i++) {
value += dataView.getUint16(pointer + 2 * i, littleEnd);
}
tag.value = parseInt(value, 10);
return tag;
case 4: // 4 LONG 32-bit unsigned integer
pointer = count > 1 ? offset : pointer;
for (let i = 0; i < count; i++) {
value += dataView.getUint32(pointer + 4 * i, littleEnd);
}
tag.value = parseInt(value, 10);
return tag;
case 5:
...
and so on.
The pattern is every time the same with some small variations. How can I refactor this? I want to refactor the pattern inside the case and I'm also trying to remove the whole switch block. Is that possible?
(This probably belongs on the Code Review Stack Exchange.)
Without a bit of larger context it's difficult to provide a reasonable refactoring, or even determine if such a refactoring would be worth the effort and additional maintenance.
The nutshell is that you have a number of type that need to be handled. Rather than a switch, you could implement a command pattern where each type is either a small class, or a simple object. (Using a class makes it marginally easier to pass in an "execution context" that contains the variables not shown in the snippet.)
For the sake of brevity, here's a (very) rough outline.
You'd have a base type handler. This wraps up dataView looping and tag value setting. Since I don't know the context, I'm pretending there's a context you pass in. I include all variables that weren't shown in your snippet.
(I didn't include value, which it looks like you should, but I didn't know the intent.)
class BaseTypeHandler {
constructor(ctx) {
this.ctx = ctx
}
getPointer = () => throw new Error('Missing getPointer implementation')
getViewData = () => throw new Error('Missing getViewData implementation')
getValueFromDataView = () => {
let value = 0
for (let i = 0; i < this.ctx.count; i++) {
value += this.getViewData(i, pointer)
}
return value
}
getTag = () => {
const pointer = this.getPointer()
, value = this.getValueFromDataView()
this.ctx.tag.value = parseInt(value, 10)
return this.ctx.tag
}
}
Each subclass implements the required unique functionality, here how to get the pointer, and how to get data from the dataView.
class Type1Handler extends BaseTypeHandler {
getPointer = () =>
this.ctx.count > 4 ? this.ctx.offset : this.ctx.pointer
getViewData = (i, pointer) =>
this.ctx.dataView.getUint8(pointer + i)
}
class Type3Handler extends BaseTypeHandler {
getPointer = () =>
this.ctx.count > 2 ? this.ctx.offset : this.ctx.pointer
getViewData = (i, pointer) =>
this.ctx.dataView.getUint16(pointer + 2 * i, littleEnd);
}
Then you wrap those up in an object of type handlers:
const typeHandlers = {
1: Type1Handler,
3: Type3Handler,
4: Type4Handler
}
const handler = new typeHandlers(type)
, tag = handler.getTag()
TL;DR
Unless you have a tremendous number of these, and you cannot use math to figure out the getPointer and getViewData implementations, you might want to stick with the switch.
Simple objects or immediate functions may be a significantly smaller implementation, although not necessarily easier to reason about. They also have the advantage of being able to close over variables you already have locally.
I'm relatively new to coding and working on a text based RPG game in JavaScript. The following code allows me to progress through the different scenarios where you are approached with a different bad guy.
I used a For loop in conjunction with a Switch statement and had it working prior, but then I re factored my code to make it more OO & prototypal. Now my For loop continues looping and does not exit. I checked the value of [i] throughout and see it properly goes 0-4, but then it restarts at 0 and I can't figure out why?
var scenario = new Array();
//simple function to create the number of scenarios
function Scenario () {
howManyScenarios = function(number) {
for (i=0; i <= number; i++) {
scenario[i] = ("Scenario " + (1 + i));
};
};
howManyScenarios(4); //if you change the argument, add additional switch cases
//iterating through my howManyScenarios function to build out scenarios using a switch case
createScenarios = function () {
var ii = scenario.length;
for (i=0; i < ii; i++) {
switch(scenario[i]) {
case 'Scenario 1':
alert("You run into a troll");
b = 0;
break;
case 'Scenario 2':
alert("You find a store to purchase goods from");
ItemShop();
break;
case 'Scenario 3':
alert("You run into a ogre");
b = 1;
break;
case 'Scenario 4':
alert("You run into a warewolf");
b = 2;
break;
case 'Scenario 5':
alert("You run into a wizard");
b = 3;
return;
break;
}; //close out switch cases
}; //close out my for loop
}; //close out createScenarios function
createScenarios();
}; //close out Scenario function
Scenario();
Your loop will obviously still continue because you just only ended a case of every loop of i and will still test each value in the array of scenario[i].
How about using variable b as a handler that if an event like you run into a troll has been executed, then set b to a number greater than 0 then check if a value has been inserted into b before switching into the array again using if (b) break; where if b has a value greater than 0 then it will be set as true.
var scenario = new Array();
var b;
//simple function to create the number of scenarios
function Scenario() {
howManyScenarios = function (number) {
for (i = 0; i <= number; i++) {
scenario[i] = ("Scenario " + (1 + i));
};
};
howManyScenarios(4); //if you change the argument, add additional switch cases
console.log(scenario[i]);
//iterating through my howManyScenarios function to build out scenarios using a switch case
createScenarios = function () {
var ii = scenario.length;
for (i = 0; i < ii; i++) {
if (b) break;
switch (scenario[i]) {
case 'Scenario 1':
alert("You run into a troll");
b = 1;
break;
case 'Scenario 2':
alert("You find a store to purchase goods from");
b = 2;
ItemShop();
break;
case 'Scenario 3':
alert("You run into a ogre");
b = 3;
break;
case 'Scenario 4':
alert("You run into a warewolf");
b = 4;
break;
case 'Scenario 5':
alert("You run into a wizard");
b = 5;
return;
break;
}; //close out switch cases
}; //close out my for loop
}; //close out createScenarios function
createScenarios();
}; //close out Scenario function
Scenario();
function ItemShop() {}
ANSWER 2
This one is one way on how we game developers make a functional game by using a series of object arrays, object classes and the like.
I remade your code into something easier to read, hope you learn something from this. :)
var numberofscenarios = 5;
var scenario = []; //array where scenarios will be
//this will be the accessible properties of scenario[] array
var _scenario = function(){
this.name = ""; //name of scenario
this.message = "";
this.doSomething = 0;
this.status = 0 ;//1 = finished and 0 = false
};
var _event = function(mobname){
this.mobname = mobname;
this.battle = function(){//doSomething
console.log("Battle VS "+ this.mobname +" Start!");
};
this.itemShop = function(){//doSomething
console.log(this.mobname + ": Welcome to the shop! How may I help you?");
};
};
//generate the scenarios in the scenario[] array
function generateScenarios() {
for (i = 0; i <= numberofscenarios; i++) {
scenario[i] = new _scenario();
scenario[i].name = i;
switch (scenario[i].name) {
case 1:
scenario[i].message = "You run into a Troll";
scenario[i].doSomething = new _event("Troll");
break;
case 2:
scenario[i].message = "You find a store to purchase goods from";
scenario[i].doSomething = new _event("Shop Keeper");
break;
case 3:
scenario[i].message = "You run into a Ogre";
scenario[i].doSomething = new _event("Ogre");
break;
case 4:
scenario[i].message = "You run into a Werewolf";
scenario[i].doSomething = new _event("Werewolf");
break;
case 5:
scenario[i].message = "You run into a Wizard";
scenario[i].doSomething = new _event("Wizard");
break;
}
}
}
generateScenarios(); //generate the scenarios
//test the array of scenario class
//test the battle with Troll
console.log(scenario[1].message);
scenario[1].doSomething.battle();
//test the shop
console.log(scenario[2].message);
scenario[2].doSomething.itemShop();
//attempt to fight the Shopkeeper
console.log(scenario[2].message);
scenario[2].doSomething.battle();
I'm making an ascii game.
$("#GameContainer").eq(i).contents().html(map[i]);
This line of code. I have no idea why, but it's not working. I've spent the past few hours trying to figure it out, but nope.
function startGame() {
map = map_Default.slice();
for (var i = 0; i < map.length; i++) {
$("#GameContainer").eq(i).contents().html(map[i]);
}
}
So in the for loop, when called in the eg() function, it acts like it should, being 0, 1, 2, 3, 4, 5, and so on. When called in the html() function, it seems to stay at 0, so instead of rendering the map like normal it repeats the first row of tiles. Also, I'm pretty sure I shouldn't have to use the contents() function, but when I don't the html() function just replaces every child of #GameContainer with map[i].
Here's more:
$(document).ready(function () {
var mapl_Half = Math.floor(map.length / 2);
var trl_Half = Math.floor(map[mapl_Half].length / 2);
var whitespace = Math.floor(trl_Half - (startmessage.length / 2));
map[mapl_Half] = map[mapl_Half].substring(0, whitespace) + startmessage + map[mapl_Half].substring(whitespace + startmessage.length);
for (var i = 0; i < map.length; i++) {
$("#GameContainer").append("<p class=\"tilerow\">" + map[i] + "</p>");
}
$(document).keydown(function (key) {
switch (key.keyCode) {
case 13:
startGame();
break;
}
});
});
The only things left out are the two variables map and map_Default which are arrays of strings.
Here it is published: http://camron.onyxtek.com/Main/AsciiGame/
You can only have one element with a given id.
So
$("#GameContainer")
contains only one element and thus
$("#GameContainer").eq(i)
makes no sense.
But it seems that what you want is the child of index i in #GameContainer. You can get it with
$("#GameContainer .tilerow").eq(i)