frontend/mario/triggers.js
2023-05-21 22:06:04 -04:00

308 lines
8.9 KiB
JavaScript

/* Triggers.js */
// Keeps track of triggers, which mainly consist of key presses,
// and messages, which would be from an index.html UI
function resetTriggers() {
// Make the controls object
window.controls = new Controls({
left: [37, 65, "AXIS_LEFT", "DPAD_LEFT"], // a, left
right: [39, 68, "AXIS_RIGHT", "DPAD_RIGHT"], // d, right
up: [38, 87, 32, "FACE_1", "DPAD_UP", "LEFT_BOTTOM_SHOULDER"], // w, up
down: [40, 83, "AXIS_DOWN", "DPAD_DOWN"], // s, down
sprint: [16, 17, "FACE_1"], // shift, ctrl
pause: [80, "START_FORWARD"], // p
mute: [77], // m
q: [81] // q
});
// Gamepad.js support for joysticks and controllers
window.gamepad = new Gamepad();
gamepad.bind(Gamepad.Event.BUTTON_DOWN, ControlsPipe("keydown", true));
gamepad.bind(Gamepad.Event.BUTTON_UP, ControlsPipe("keyup", false));
gamepad.bind(Gamepad.Event.AXIS_CHANGED, function(event) {
var value = event.value,
value_abs = abs(value);
// Don't allow tremors
if(value_abs < 0.1) return;
// Depending on the axis used...
switch(event.axis) {
// Left stick, vertical
case "LEFT_STICK_Y":
case "RIGHT_STICK_Y":
// If it actually has a direction, either go up or down
if(value_abs > 0.5) {
keydown(value > 0 ? "DPAD_DOWN" : "DPAD_UP");
}
// It doesn't have a direction, so they're both unpressed
else {
keyup("DPAD_UP");
keyup("DPAD_DOWN");
}
break;
// Left stick, horizontal
case "LEFT_STICK_X":
case "RIGHT_STICK_X":
// If it actually has a direction, either go left or right
if(value_abs > 0.5) {
keydown(value < 0 ? "DPAD_LEFT" : "DPAD_RIGHT");
}
// It doesn't have a direction, so they're both unpressed
else {
keyup("DPAD_UP");
keyup("DPAD_DOWN");
}
break;
}
});
gamepad.init();
// Set the key events on the body
proliferate(body, {
onkeydown: ControlsPipe("keydown", true),
onkeyup: ControlsPipe("keyup", false),
oncontextmenu: contextmenu,
onmousedown: mousedown
});
// Set UI triggers
setMessageTriggers();
}
// Hash table for onkeydown and onkeyup
function Controls(pipes, gamepadPipes) {
// Pipes is a listing of which actions are piped to by which character codes
this.pipes = pipes;
// Actions are piped to the corresponding keydown or keyup via the corresponding ControlsPipe
var keydown = this.keydown = {
// Left
left: function(keys) {
keys.run = -1;
keys.left_down = true;
},
// Right
right: function(keys) {
keys.run = 1;
keys.right_down = true; // independent of changes to mario.keys.run
},
// Up / Jump
up: function(keys) {
keys.up = true;
if(mario.canjump &&/* !mario.crouching &&*/ (mario.resting || map.underwater)) {
keys.jump = 1;
mario.canjump = keys.jumplev = 0;
// To do: can mario make a jumping sound during the spring, and during the pipe cutscenes?
if(mario.power > 1) play("Jump Super");
else play("Jump Small");
if(map.underwater) setTimeout(function() {
mario.jumping = keys.jump = false;
}, timer * 14);
}
},
// Down / Crouch
down: function(keys) {
keys.crouch = true;
},
// Sprint / Fire
sprint: function(keys) {
if(mario.power == 3 && keys.sprint == 0 && !keys.crouch)
mario.fire();
keys.sprint = 1;
},
// Pause
pause: function(keys) {
if(!paused && !(window.editing && !editor.playing))
setTimeout(function() { pause(true); }, 140);
},
// Mute / Unmute
mute: function(keys) {
toggleMute();
},
// qqqqqqq
q: function(keys) {
if(++qcount > 28) maxlulz();
switch(qcount) {
case 7: lulz(); break;
case 14: superlulz(); break;
case 21: hyperlulz(); break;
}
}
};
var keyup = this.keyup = {
// Left
left: function(keys) {
keys.run = 0;
keys.left_down = false;
},
// Right
right: function(keys) {
keys.run = 0;
keys.right_down = false;
},
// Up
up: function(keys) {
if(!map.underwater) keys.jump = keys.up = 0;
mario.canjump = true;
},
// Down
down: function(keys) {
keys.crouch = 0;
removeCrouch();
},
// Spring
sprint: function(keys) {
keys.sprint = 0;
},
// Pause (if held down)
pause: function(keys) {
unpause(true);
},
}
var tag, codes, code, i;
// Map each character code in pipes to the corresponding key event
// For each tag ("up", "down"...)
for(tag in pipes) {
// For each array of character codes, like 38 (up) or 40 (down)
codes = pipes[tag];
for(i in codes) {
code = codes[i];
// That code redirects to the equivalent tag (38 -> "up")
keydown[code] = keydown[tag];
keyup[code] = keyup[tag];
}
}
}
// Generates a pipe to the given name
// For example, ControlsPipe("keydown") pipes to Controls.keydown
function ControlsPipe(name, strict) {
var responses = controls[name];
return function(event) {
if((strict && ((mario && mario.dead) || window.paused)) || window.nokeys) return;
// Allow this to be used as keyup(37) or keyup({which: 37})
if(typeof(event) != "number" || event.which || event.control)
event = event.which || event.control;
// If there is a known response to this character code, do it
if(responses[event])
responses[event](mario.keys);
// Otherwise only complain if verbosity[name] is true
else mlog(name, "Could not", name, event);
// Record this in the history
window.gamehistory[gamecount] = [keydown, event];
};
}
function keydown(event) {
if((mario && mario.dead) || window.paused || window.nokeys) return;
var responses = controls["keydown"];
// Allow this to be used as keyup(37) or keyup({which: 37})
if(typeof(event) === "object" || event.which)
event = event.which;
if(responses[event])
responses[event](mario.keys);
window.gamehistory[gamecount] = [keydown, event];
}
function keyup(event) {
if(window.nokeys) return;
var responses = controls["keyup"];
// Allow this to be used as keyup(37) or keyup({which: 37})
if(typeof(event) === "object" || event.which)
event = event.which;
if(responses[event])
responses[event](mario.keys);
window.gamehistory[gamecount] = [keyup, event];
}
function contextmenu(event) {
if(event.preventDefault)
event.preventDefault();
}
function mousedown(event) {
// Right click
if(event.which == 3) {
if(paused) unpause();
else if((!window.editor) || (!editing && !editor.playing)) pause(true);
if(event.preventDefault)
event.preventDefault()
}
}
function scriptKeys(oldhistory) {
var i, entry;
for(i in oldhistory) {
entry = oldhistory[i];
TimeHandler.addEvent(entry[0], i, entry[1]);
TimeHandler.addEvent(function() { alert(entry[0].name + ", " + entry[1]) }, i);
}
}
function lulz(options, timer) {
mario.star = true;
options = options || [Goomba];
timer = timer || 7;
TimeHandler.addEvent(function() {
if(characters.length > 210) return;
var lul = new Thing(options[randInt(options.length)], randBoolJS(), randBoolJS());
lul.yvel = random() * -unitsizet4;
lul.xvel = lul.speed = random() * unitsizet2 * randSign();
addThing(lul, (32 * random() + 128) * unitsize, (88 * random()) * unitsize);
}, timer, Infinity);
}
function superlulz() {
lulz([Goomba, Koopa, Beetle, HammerBro, Lakitu, Podoboo, Blooper]);
}
function hyperlulz() {
lulz([Bowser], 21);
}
function maxlulz() {
// Sigh....
// window.palette = arrayShuffle(window.palette, 1);
// clearAllSprites(true);
TimeHandler.addEvent(function(arr) {
setAreaSetting(arr[randInt(arr.length)]);
}, 7, Infinity, ["Overworld", "Underworld", "Underwater", "Sky", "Castle"]);
}
/* Triggers (from a UI)
*/
function setMessageTriggers() {
// Commands will be sent in by these codes
var command_codes = {
setMap: triggerSetMap,
startEditor: function() { loadEditor(); }
};
// When a message is received, send it to the appropriate command code
window.addEventListener("message", function(event) {
var data = event.data,
type = data.type;
// If the type is known, do it
if(command_codes[type])
command_codes[type](data);
// Otherwise complain
else {
console.log("Unknown event type received:", type, ".\n", data);
}
});
}
// The UI has requested a map change
function triggerSetMap(data) {
clearMarioStats();
setMap.apply(this, data.map || []);
setLives(3);
}