first commit

This commit is contained in:
Oleg 2025-08-16 10:24:23 +00:00
commit a5247d668d
37 changed files with 2007 additions and 0 deletions

703
battle.js Normal file
View File

@ -0,0 +1,703 @@
/////////////////////
const canvas = document.querySelector("#gameCanvas");
const intro_canvas = document.querySelector("#introCanvas");
const text_canvas = document.querySelector("#textCanvas");
const ctx = canvas.getContext('2d');
const intro_ctx = intro_canvas.getContext('2d');
const text_ctx = text_canvas.getContext('2d');
const black_color = "#1B1212";
var game, round = 1, bot_win = 0, player_win = 0, images, arrow_down, mouse_down;
var key_down = []; // действия, привязанные к нажатым кнопкам
var keyboard_pressed = []; // зажатые кнопки клавиатуры
var btn_pressed = []; // нажатые gui кнопки канваса
const frame_data = [];
var selected_level = 1;
const directions = {
"_Left" : -1,
"_Right" : 1,
};
const actions = ["punch", "jump", "block"];
const levels = ["gg_cabinet", "bar", "car_salon"];
function loaderPicture(path){
const image = new Image();
image.src = path;
return new Promise((resolve, reject) => {
image.addEventListener('load', ()=>{
resolve(image);
});
image.addEventListener('error', ()=>{
reject("Error of load");
});
})
}
function setFrameData(level){
frame_data["Игрок"] = new FrameData(timing["player"].value);
frame_data["Компьютер"] = new FrameData(timing[levels[level] + "_enemy_timing"].value);
// frame_data["Бармэн"] = new FrameData(bar_enemy_timing);
for(var name in frame_data){
for(var key in frame_data[name]){
// есть start в свойстве класса и есть такой же ключ в тайминге
if("start" in frame_data[name][key] && frame_data[name].timing[key]){
frame_data[name][key].start = frame_data[name].timing[key].start;
frame_data[name][key].end = frame_data[name].timing[key].end;
if(frame_data[name].timing[key].speed){
frame_data[name][key].speed = frame_data[name].timing[key].speed;
}
if(frame_data[name].timing[key].active){
frame_data[name][key].active = frame_data[name].timing[key].active;
}
}
}
}
}
function beforeGame(){
game.start_menu = false;
game.over = true;
game.intro = true;
setFrameData(selected_level);
let player = new Player(canvas.width, canvas.height, game.images.player, 0, "player", "Игрок");
game.player = player;
game.characters = {
player: player,
enemy: new Enemy(canvas.width, canvas.height, game.images["enemy" + (selected_level + 1)], 500,
"bot", "Компьютер",
timing[levels[selected_level] + "_enemy_timing"].size.w,
timing[levels[selected_level] + "_enemy_timing"].size.h,
timing[levels[selected_level] + "_enemy_timing"].speed
),
// npc: new Player(canvas.width, canvas.height, images.enemy2, 800, "npc", "Бармэн", 148, 200, 2)
}
game.buttons.button1.caption = "";
game.buttons.button2.caption = "";
game.buttons.button3.caption = "";
}
const keyToFrame = {
'ArrowLeft': 'walk_Left',
'ArrowRight': 'walk_Right',
'ArrowDown': 'crouch',
'q': 'punch',
'w': 'block',
'e': 'block_Up',
's': 'kick_Up',
' ': 'jump',
// 'x,z': 'test'
};
const test_actions = [
'punch_Left',
'kick_Left_Up',
'crouch_Left_Down',
'block_Left_Up',
'stand_Left',
'jump_Left',
]
function getRand(min, max){
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min
}
class Game{
constructor(canvas, images, start = true){
this.images = images;
this.ui = null;
this.start_menu = start;
this.over = true;
this.intro = false;
this.timer = 0;
this.delta = 0;
this.interval = 3 * 1.5; // скорость обновления канваса
this.info_animation_timer = undefined;
this.start_screen_timer = 0;
this.start_screen_delta = 0;
this.round_time = 60;
this.round_amount = 3;
this.time_bar = new Button(text_ctx, 5 * 1.5, 0.2, 100 / 1.5, 50 / 1.5 );
this.buttons = [];
this.buttons["button1"] = new Button(text_ctx, 0.82, 2, 400, 50 );
this.buttons["button2"] = new Button(text_ctx, 0.82, 3.5, 400, 50 );
this.buttons["button3"] = new Button(text_ctx, 0.82, 5, 400, 50 );
const w = 100;
this.buttons["q"] = new Button(text_ctx, 0.5, 0.25, w, w );
this.buttons["w"] = new Button(text_ctx, 2, 0.25, w, w );
this.buttons["e"] = new Button(text_ctx, 3.5, 0.25, w, w );
this.buttons["a"] = new Button(text_ctx, 1, 1.5, w, w );
this.buttons["s"] = new Button(text_ctx, 2.5, 1.5, w, w );
this.buttons["d"] = new Button(text_ctx, 4, 1.5, w, w );
this.buttons["left"] = new Button(text_ctx, 7.5, 0.25, w, w );
this.buttons["right"] = new Button(text_ctx, 9, 0.25, w, w );
this.buttons["down"] = new Button(text_ctx, 8.25, 1.5, w, w );
this.buttons["space"] = new Button(text_ctx, 1.5 / 8.5, 2.75, w * 8.5, w );
this.buttons["title"] = new Button(text_ctx, 4.5 / 3, 0.25, w * 3, w );
this.buttons["close"] = new Button(text_ctx, 40.5, 0.5, w / 4, w / 4 );
this.buttons["bottom_text"] = new Button(text_ctx, 0.6, 11.3, w * 5, w / 3 );
this.info_animation = [
{button: "q", action: "punch_Right"},
{button: "w", action: "block_Right"},
{button: "e", action: "block_Right_Up"},
{button: "s", action: "kick_Right_Up"},
{button: "left", action: "walk_Left"},
{button: "right", action: "walk_Right"},
{button: "down", action: "crouch_Right_Down"},
{button: "down_q", action: "punch_Right_Down"},
{button: "down_w", action: "block_Right_Down"},
{button: "space", action: "jump_Right"}
];
this.ui = new UI();
}
updateAll(key){
// if(this.characters.npc.currentFrame == 0){
// this.characters.npc.currentAction = test_actions[getRand(0, 5)];
// }
if(!this.start_menu){
ctx.drawImage(this.images["background" + (selected_level + 1)], 0, 0, canvas.width, canvas.height);
}
else{
ctx.fillStyle = black_color;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
if(this.over){
if(!this.start_menu){
if(!this.intro){
if(!this.buttons.button2.caption){
setTimeout(function(){
game.ui.clearAll();
game.buttons.button2.background = "purple";
game.buttons.button2.caption = "Press to continue";
}, 1000);
}
else{
this.buttons.button2.background = "purple";
this.buttons.button2.caption = "Press to continue";
}
}
}
else if(this.ui){
for(var i in this.buttons){
var button = this.buttons[i];
if(button.caption){
if(button.focus){
button.background = "orange";
}
else{
button.background = "purple";
}
}
}
if(game.info_animation_timer == undefined){
this.buttons.button1.caption = "Chief cabinet";
this.buttons.button2.caption = "Bar room";
this.buttons.button3.caption = "Car showroom";
}
else{
this.buttons.button1.caption = "";
this.buttons.button2.caption = "";
this.buttons.button3.caption = "";
}
}
if(game.info_animation_timer != undefined){
this.buttons.title.background = black_color;
this.buttons.bottom_text.background = black_color;
this.buttons.close.background = "maroon";
}
}
// for(let key in this.characters){
// this.characters[key].update();
// }
if(!this.over){
if(this.player){
this.player.update();
}
if(this.characters && this.characters.enemy){
this.characters.enemy.update();
}
}
if(!this.start_menu){
this.start_screen_timer += 1;
const vs_delay = 60;
if(this.start_screen_timer > vs_delay){
this.start_screen_delta += 20;
}
if(!this.over && this.characters){
this.ui.drawUserInfo(50, 50, 200, images.player_avatar, this.player, this.player.name, 210);
this.ui.drawUserInfo(canvas.width - 250, 50, 200, images["bot_avatar" + (selected_level + 1)], this.characters.enemy, this.characters.enemy.name, -70);
// this.ui.clearAll();
// this.ui.drawTimeBar(intro_ctx, images.time_bar, false, 0, 1.5);
// this.ui.drawTimeBar(text_ctx, images.time_bar_mask, true, 0, 1.5);
}
if(this.intro){
this.ui.drawStartScreen(images.player_avatar, images["bot_avatar" + (selected_level + 1)], "", this.start_screen_delta, vs_delay);
if(this.start_screen_timer > vs_delay){
this.ui.drawGameCaption(intro_ctx, images.fight, false, this.start_screen_delta);
this.ui.drawGameCaption(text_ctx, images.fight, true, this.start_screen_delta);
}
else if(this.start_screen_timer > vs_delay / 2){
this.ui.drawGameCaption(text_ctx, images["round" + round], false, this.start_screen_delta);
}
else{
this.ui.drawGameCaption(text_ctx, images.vs, false, this.start_screen_delta);
}
if(this.start_screen_delta >= canvas.width / 2){
this.ui.clearAll();
this.intro = false;
this.over = false;
}
}
if(!this.over){
this.timer++;
}
// 60 fps канваса, но функция выывается в interval раз реже
this.seconds = Math.floor(this.timer / (60 / this.interval) );
if(this.seconds >= this.round_time && !this.over && this.characters.enemy){
this.over = true;
if(this.player.health.current < this.characters.enemy.health.current){
bot_win++;
round++;
}
if(this.player.health.current >= this.characters.enemy.health.current){
player_win++;
round++;
}
this.checkWin();
}
}
if(this.info_animation_timer != undefined){
this.buttons.q.caption = "Q";
this.buttons.w.caption = "W";
this.buttons.e.caption = "E";
this.buttons.a.caption = "A";
this.buttons.s.caption = "S";
this.buttons.d.caption = "D";
this.buttons.left.caption = "<";
this.buttons.right.caption = ">";
this.buttons.down.caption = "|";
this.buttons.space.caption = " ";
this.buttons.close.caption = "X";
this.buttons.bottom_text.caption = "look or press keyboard yourself";
const index = Math.round(this.info_animation_timer / 30);
const obj = this.info_animation[index];
if(index < this.info_animation.length){
if(Object.keys(btn_pressed).length == 0){
this.info_animation_timer++;
if(obj){
const action = timing["player"].value[obj.action];
const frameWidth = 224;
const frameHeight = 200;
const image = game.images.player;
// const x = 100;
// const y = 100;
const { start, end } = action;
const rate = 10;
const frames = this.info_animation_timer % rate
if(this.info_animation[index - 1]){
const previous_obj = this.info_animation[index - 1];
const previous_button = previous_obj.button.split("_");
if(!previous_button[1]){
this.buttons[previous_button[0]].focus = false;
}
else{
this.buttons[previous_button[0]].focus = false;
this.buttons[previous_button[1]].focus = false;
}
}
const button = obj.button.split("_");
if(!button[1]){
this.buttons[button[0]].focus = true;
}
else{
this.buttons[button[0]].focus = true;
this.buttons[button[1]].focus = true;
}
if(frames && this.player.currentFrame == 0){
if(key_down[obj.action] != false){
if(obj.action == "walk_Right"){
this.player.direction = "_Right";
}
key_down[obj.action] = true;
}
}
}
}
else{
for(var i in this.buttons){
this.buttons[i].focus = false;
}
for(var i in btn_pressed){
if(this.buttons[i]){
this.buttons[i].focus = true;
}
}
}
if(this.player){
if(Object.keys(btn_pressed).length == 0){
var first_part = obj.action.split("_");
}
else{
var first_part = this.player.currentAction.split("_");
}
var direction_vert = first_part[2];
if(direction_vert == "Up"){
var str = direction_vert.toLowerCase();
}
else{
var str = "";
}
this.buttons.title.caption = first_part[0] + " " + str;
this.player.update();
}
}
else{
this.info_animation_timer = undefined;
key_down = {};
beforeGame();
}
this.player.stopped = true;
}
else{
this.buttons.q.caption = "";
this.buttons.w.caption = "";
this.buttons.e.caption = "";
this.buttons.a.caption = "";
this.buttons.s.caption = "";
this.buttons.d.caption = "";
this.buttons.left.caption = "";
this.buttons.right.caption = "";
this.buttons.down.caption = "";
this.buttons.space.caption = "";
}
}
checkWin(){
if(round <= this.round_amount && player_win != 2 && bot_win != 2){
firstState(false);
beforeGame();
}
else if(!this.buttons.button2.caption){
if(bot_win > player_win){
this.ui.drawGameCaption(text_ctx, images.over, false, this.start_screen_delta);
}
else{
this.ui.drawGameCaption(text_ctx, images.win, false, this.start_screen_delta);
}
}
}
}
function runGame(){
game.delta++;
if (game.delta > game.interval) {
game.updateAll();
game.delta = 0;
}
requestAnimationFrame(runGame);
}
window.addEventListener("load", start);
function firstState(start = true){
const container = document.querySelector(".container");
const css_string = "position: absolute;"
canvas.style.cssText = css_string + "z-index: 0";
intro_canvas.style.cssText = css_string + "z-index: 1";
text_canvas.style.cssText = css_string + "z-index: 2";
document.body.style.cssText = "display: flex; justify-content: center; height: 100vh; align-items: center";
// document.body.style.background = "black";
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
canvas.width = intro_canvas.width = text_canvas.width = images.background1.width;
canvas.height = intro_canvas.height = text_canvas.height = images.background1.height;
container.style.width = canvas.width + "px";
container.style.height = canvas.height + "px";
game = new Game(canvas, images, start);
}
async function start(){
images = {
background1: await loaderPicture("./content/background1.png"),
background2: await loaderPicture("./content/background2.png"),
background3: await loaderPicture("./content/background3.png"),
player: await loaderPicture("./content/player.png"),
enemy1: await loaderPicture("./content/enemy1.png"),
enemy2: await loaderPicture("./content/enemy2.png"),
enemy3: await loaderPicture("./content/enemy3.png"),
over: await loaderPicture("./content/over.png"),
win: await loaderPicture("./content/win.png"),
player_avatar: await loaderPicture("./content/player_avatar.png"),
bot_avatar1: await loaderPicture("./content/bot_avatar1.png"),
bot_avatar2: await loaderPicture("./content/bot_avatar2.png"),
bot_avatar3: await loaderPicture("./content/bot_avatar3.png"),
player_avatar_rage: await loaderPicture("./content/player_avatar_rage.png"),
bot_avatar_rage: await loaderPicture("./content/bot_avatar_rage.png"),
fight: await loaderPicture("./content/fight.png"),
vs: await loaderPicture("./content/vs.png"),
round1: await loaderPicture("./content/round1.png"),
round2: await loaderPicture("./content/round2.png"),
round3: await loaderPicture("./content/round3.png"),
time_bar: await loaderPicture("./content/time_bar.png"),
time_bar_mask: await loaderPicture("./content/time_bar_mask.png"),
lock: await loaderPicture("./content/lock.png"),
block: await loaderPicture("./content/block.png"),
}
window.loading_text = document.querySelector('.top-div');
loading_text.style.display= "none";
firstState();
// beforeGame(); // здесь для теста
runGame(game);
// game.over = false; // здесь для теста
// game.intro = false; // здесь для теста
window.addEventListener('resize', event => {
canvas.width = images.background1.width;
canvas.height = images.background1.height;
})
document.addEventListener('keydown', event => {
if(game.player){
if(event.code.toLowerCase().split("key")[1]){
var key = event.code.toLowerCase().split("key")[1]; // e.key привязан к раскладке, поэтому берем e.code.
// Если клавиша с буквой, то забираем код и берем из него название буквы.
// Например, для клавиши "А" код "KeyA", значит переменная index будет "A".
}
else{// Клавиши без букв, например, стрелки. Для них можно использовать и e.code
var key = event.key;
}
if(keyboard_pressed.indexOf(key) == -1 && (!key.split("Arrow")[1] || keyboard_pressed.length == 0) ){
keyboard_pressed.push(key);
}
else{
return;
}
var index = keyboard_pressed.sort().toString();
if(!keyToFrame[index]){
index = key;
if(!keyToFrame[index]){
// продолжаем, если есть такое действие
return;
}
}
const direction_hor = game.player.direction;
const name = keyToFrame[index].split("_");
const str = name[0];
// содержит "_Up" в конце
const str2 = name[1];
// флаг для нижних ударов.
if(index == "ArrowDown"){
arrow_down = true;
}
if(str2){
var direction_vert = "_" + str2;
}
else if(arrow_down){
var direction_vert = "_Down";
}
else{
var direction_vert = "";
}
if(index == "ArrowLeft" || index == "ArrowRight"){
key_down[keyToFrame[index]] = true;
// console.log(index)
}
else if(game.player[str + direction_hor + direction_vert]){
key_down[str + direction_hor + direction_vert] = true;
}
}
// Ниндзя код
var k = event.code;
var n = k.toLowerCase() == "space" ? "space" : k.toLowerCase().split("key")[1]
|| k.toLowerCase().split("arrow")[1];
if(n){
btn_pressed[n] = true;
if(game.buttons[n]){
game.buttons[n].focus = true;
}
}
});
document.addEventListener('keyup', event => {
if(event.code.toLowerCase().split("key")[1]){
var key = event.code.toLowerCase().split("key")[1]; // e.key привязан к раскладке, поэтому берем e.code.
// Если клавиша с буквой, то забираем код и берем из него название буквы.
// Например, для клавиши "А" код "KeyA", значит переменная index будет "A".
}
else{// Клавиши без букв, например, стрелки. Для них можно использовать и e.code
var key = event.key;
}
const index = key;
if(game.player && game.player.direction){
const direction_hor = game.player.direction;
if(keyToFrame[index]){
const name = keyToFrame[index].split("_");
const str = name[0];
// содержит "_Up" в конце
const str2 = name[1];
if(str2){
var direction_vert = "_" + str2;
}
else if(arrow_down){
var direction_vert = "_Down";
}
else{
var direction_vert = "";
}
if(index == "ArrowLeft" || index == "ArrowRight"){
delete key_down[keyToFrame[index]];
}
else if(game.player[str + direction_hor + direction_vert]){
console.log(index, str + direction_hor + direction_vert)
delete key_down[str + direction_hor + direction_vert];
}
}
}
// Ниндзя код
var k = event.code;
var n = k.toLowerCase() == "space" ? "space" : k.toLowerCase().split("key")[1]
|| k.toLowerCase().split("arrow")[1];
if(n){
delete btn_pressed[n];
if(game.buttons[n]){
game.buttons[n].focus = false;
}
}
// Если не зажата кнопка "Вниз", сбрасываем индикатор зажатой клавиши
if(index == "ArrowDown"){
arrow_down = false;
}
if(keyboard_pressed.indexOf(index) >= 0){
keyboard_pressed.splice(keyboard_pressed.indexOf(index), 1);
}
});
text_canvas.addEventListener('mousemove', event => {
if(game.over && game.info_animation_timer == undefined){
for(var i in game.buttons){
var button = game.buttons[i];
if(button && button.caption){
if(event.offsetX > button.x * button.width
&& event.offsetX < button.x * button.width + button.width
&& event.offsetY > button.y * button.height
&& event.offsetY < button.y * button.height + button.height){
button.focus = true;
return;
}
else{
button.focus = false;
}
}
}
}
});
text_canvas.addEventListener('mousedown', event => {
for(var i in game.buttons){
var button = game.buttons[i];
if(button && button.caption){
if(event.offsetX > button.x * button.width
&& event.offsetX < button.x * button.width + button.width
&& event.offsetY > button.y * button.height
&& event.offsetY < button.y * button.height + button.height){
button.pressed = true;
if(!isNaN(Number(i.split("button")[1]) - 1) ){
selected_level = Number(i.split("button")[1]) - 1;
}
if(i == "close"){
game.info_animation_timer = undefined;
key_down = {};
beforeGame();
return;
}
// Не средняя кнопка
if(game.start_menu){
game.info_animation_timer = 0;
setFrameData(selected_level);
let player = new Player(canvas.width, canvas.height, game.images.player, 450, "player", "Игрок", undefined, undefined, undefined, 1.5)
game.player = player;
game.characters = {
player: player,
}
}
else{
alert("Следующий уровень")
game.over = true;
return;
}
return;
}
else{
button.pressed = false;
}
}
}
});
}

76
chars.js Normal file
View File

@ -0,0 +1,76 @@
var letters = [];
letters["A"] = [[0,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1]];
letters["B"] = [[1,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,0]];
letters["C"] = [[0,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1]];
letters["D"] = [[1,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1]];
letters["E"] = [[1,1,1,1,1],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,1],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,1]];
letters["F"] = [[1,1,1,1,1],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0]];
letters["G"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,0],[1,0,0,0,0],[1,0,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["H"] = [[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1]];
letters["I"] = [[1,1,1,1,1],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[1,1,1,1,1]];
letters["J"] = [[1,1,1,1,1],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[1,1,1,0,0]];
letters["K"] = [[1,0,0,0,1],[1,0,0,1,0],[1,0,0,1,0],[1,1,1,0,0],[1,0,0,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1]];
letters["L"] = [[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,1]];
letters["M"] = [[1,0,0,0,1],[1,0,0,0,1],[1,1,0,1,1],[1,0,1,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1]];
letters["N"] = [[1,0,0,0,1],[1,1,0,0,1],[1,0,1,0,1],[1,0,0,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1]];
letters["O"] = [[0,1,1,1],[1,0,0,0,1,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0,0],[1,0,0,0,1,0,0,0],[1,0,0,0,1,0,0,0],[1,0,0,0,1],[0,1,1,1]];
letters["P"] = [[1,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0]];
letters["Q"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,1,0,1],[1,0,0,1,0],[0,1,1,1,1]];
letters["R"] = [[1,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,0],[1,0,0,1,0],[1,0,0,0,1],[1,0,0,0,1]];
letters["S"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,0],[1,1,0,0,0],[0,0,1,1,1],[0,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["T"] = [[1,1,1,1,1],[0,0,1,0,0,null,0,null,null,null,0],[0,0,1,0,0,null,0,0,null,0,0],[0,0,1,0,0,null,0,null,0,null,0],[0,0,1,0,0,null,0],[0,0,1,0,0,null,0],[0,0,1,0,0,null,0],[0,0,1,0,0,null,0]];
letters["U"] = [[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["V"] = [[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,0,1,0],[0,0,1,0,0]];
letters["W"] = [[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,1,0,1],[1,1,0,1,1],[1,0,0,0,1],[1,0,0,0,1]];
letters["X"] = [[1,0,0,0,1],[1,0,0,0,1],[0,1,0,1,0],[0,0,1,0,0],[0,1,0,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1]];
letters["Y"] = [[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,0,1,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0]];
letters["Z"] = [[1,1,1,1,1],[1,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0],[1,0,0,0,1],[1,1,1,1,1]];
letters[" "] = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]];
letters["a"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0,0],[0,1,1,1,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[0,1,1,1,1,0,0]];
letters["b"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,1,1,1,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,1,1,1,0,0,0]];
letters["c"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[0,1,1,1,0,0,0]];
letters["d"] = [[0,0,0,0,1],[0,0,0,0,1,0,0,0,0,0,0],[0,0,0,0,1,0,0,0,0,0,0],[1,1,1,1,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,1,1,1,1,0,0]];
letters["e"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,1,1,1,1,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0],[0,1,1,1,0,0,0]];
letters["f"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,1,1,1,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0]];
letters["g"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,1,1,0,0],[0,1,1,0,1,0,0],[0,0,0,0,1,0,0],[0,1,1,1,1,0,0]];
letters["h"] = [[1,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0]];
letters["i"] = [[0,0,1,0,0],[0,0,0,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[1,1,1,1,1,0,0]];
letters["j"] = [[0,0,1,0,0],[0,0,0,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[1,1,1,0,0,0,0]];
letters["k"] = [[1,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,1,0,0,0,0,0,0,0],[1,0,1,0,0,0,0,0,0,0,0],[1,1,1,1,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0]];
letters["l"] = [[0,1,1,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[1,1,1,1,1,0,0]];
letters["m"] = [[0,0,0,0,0],[1,1,1,1,0,0,0,0,0,0,0],[1,0,1,0,1,0,0,0,0,0,0],[1,0,1,0,1,0,0,0,0,0,0],[1,0,1,0,1,0,0],[1,0,1,0,1,0,0],[1,0,1,0,1,0,0],[1,0,1,0,1,0,0]];
letters["n"] = [[0,0,0,0,0],[1,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0]];
letters["o"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[0,1,1,1,0,0,0]];
letters["p"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,1,1,1,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0]];
letters["q"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[0,1,1,1,1,0,0],[0,0,0,0,1,0,0],[0,0,0,0,1,0,0]];
letters["r"] = [[0,0,0,0,0],[1,0,1,1,0,0,0,0,0,0,0],[1,1,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0]];
letters["s"] = [[0,0,0,0,0],[0,1,1,1,0,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,1,0,0,0,0,0,0,0,0,0],[0,0,1,1,0,0,0],[0,0,0,0,1,0,0],[1,0,0,0,1,0,0],[0,1,1,1,0,0,0]];
letters["t"] = [[0,0,1,0,0],[1,1,1,1,1,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,1,0,0,0]];
letters["u"] = [[0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,0,1,1,0,0],[0,1,1,0,1,0,0]];
letters["v"] = [[0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,0,0,1,0,0,0],[0,1,1,0,0,0,0],[0,1,0,0,0,0,0]];
letters["w"] = [[0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[1,0,1,0,1,0,0],[0,1,0,1,0,0,0]];
letters["x"] = [[0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[0,1,0,1,0,0,0,0,0,0,0],[0,0,1,0,0,0,0,0,0,0,0],[0,1,0,1,0,0,0],[1,0,0,0,1,0,0],[1,0,0,0,1,0,0],[0,0,0,0,0,0,0]];
letters["y"] = [[0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[0,1,1,1,1,0,0,0,0,0,0],[0,0,0,0,1,0,0],[0,0,0,1,1,0,0],[0,1,1,0,0,0,0],[0,0,0,0,0,0,0]];
letters["z"] = [[0,0,0,0,0],[1,1,1,1,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[0,0,0,1,0,0,0,0,0,0,0],[0,0,1,0,0,0,0],[0,1,0,0,1,0,0],[1,1,1,1,1,0,0],[0,0,0,0,0,0,0]];
letters["-"] = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[1,1,1,1,1],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]];
letters["("] = [[0,0,1,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,1,0,0,0],[0,0,1,0,0]];
letters[")"] = [[0,0,1,0,0],[0,0,0,1,0],[0,0,0,1,0],[0,0,0,1,0],[0,0,0,1,0],[0,0,0,1,0],[0,0,0,1,0],[0,0,1,0,0]];
letters["."] = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,1,0,0,0]];
letters[","] = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,1,0,0,0],[0,1,0,0,0]];
letters[":"] = [[0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[0,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0]];
letters["!"] = [[1,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0,0,0,0,0],[1,0,0,0,0,0,0],[1,0,0,0,0,0,0],[0,0,0,0,0,0,0],[1,0,0,0,0,0,0]];
letters["?"] = [[0,1,1,1,0],[1,0,0,0,1,0,0,0,0,0,0],[1,0,0,0,1,0,0,0,0,0,0],[0,0,0,1,0,0,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,0,0,0,0,0]];
letters["1"] = [[0,0,1,0,0],[0,1,1,0,0],[1,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[1,1,1,1,1]];
letters["2"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0],[1,1,1,1,1]];
letters["3"] = [[0,1,1,1,0],[1,0,0,0,1],[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,1],[0,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["4"] = [[0,0,0,1,1],[0,0,1,0,1],[0,1,0,0,1],[1,0,0,0,1],[1,1,1,1,1],[0,0,0,0,1],[0,0,0,0,1],[0,0,0,0,1]];
letters["5"] = [[1,1,1,1,1],[1,0,0,0,0],[1,0,0,0,0],[1,1,1,1,0],[0,0,0,0,1],[0,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["6"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,0],[1,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["7"] = [[1,1,1,1,1],[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[1,0,0,0,0]];
letters["8"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["9"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1,1],[0,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["0"] = [[0,1,1,1,0],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[0,1,1,1,0]];
letters["<"] = [[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0]];
letters[">"] = [[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1],[0,0,0,0,1],[0,0,0,1,0],[0,0,1,0,0],[0,1,0,0,0]];
letters["|"] = [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[1,0,0,0,1],[1,0,0,0,1],[0,1,0,1,0],[0,1,0,1,0],[0,0,1,0,0]];

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
content/background1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

BIN
content/background2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

BIN
content/background3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

BIN
content/block.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
content/bot_avatar1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
content/bot_avatar2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
content/bot_avatar3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
content/bot_avatar_rage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
content/enemy1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
content/enemy2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1007 KiB

BIN
content/enemy3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
content/fight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
content/lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
content/over.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
content/player.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 KiB

BIN
content/player_avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
content/round1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
content/round2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
content/round3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
content/time_bar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
content/time_bar_mask.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 B

BIN
content/vs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
content/win.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

87
enemy.js Normal file
View File

@ -0,0 +1,87 @@
class Enemy extends Player{
constructor(w, h, image, dx, type, name, frameWidth, frameHeight, frameSpeed, size){
super(w, h, image, dx, type, name, frameWidth, frameHeight, frameSpeed, size);
}
reaction(nemesis_action_params){
// Если противник встал, то мы тоже встаем
if(this.nemesis && this.nemesis.currentAction == "crouch" + this.nemesis.direction + "_Up"){
this.currentFrame = 0;
this.crouch = false;
}
// Если предыдущая анимация завершилась или мы присели (тогда
// текущий кадр будет равен последнему кадру анимации)
if(this.currentFrame == 0 || frame_data[this.name][this.currentAction].no_return
&& this.currentFrame >= frame_data[this.name][this.currentAction].end - frame_data[this.name][this.currentAction].start - 1){
this.idleCount = 0;
if( "reaction" in nemesis_action_params){
const rand = getRand(1, 100); // число для случайного ответа
for(var obj of nemesis_action_params.reaction){
const range = obj.chance.split("-");
// если число в диапазоне
if(rand >= range[0] && rand < range[1]){
// В это условие мы попадаем в двух случаях: Если идет бой
// или мы приблизились на расстояние, когда бот начинает подбегать к нам.
// Здесь мы вызываем реакцию из объекта nemesis_action_params
// Реакция это имя действия ("walk", "punch", "stand") плюс направление
// по горизонтали ("_Left" или "_Right"), плюс направление по вертикали —
// "Up", "Down", или "". Направление по вертикали это либо имя реакции плюс
// вертикальное направление удара врага (например, враг бьет сверху kick_Left_Up,
// тогда наша рекация будет "block" + "_Right" + "_Up"), либо имя реакции может отсылать
// к действию, которое само содержит в названии "_Up", тогда так же добавляем его к имени/
// Либо это удары "punch", "block", "crouch" — тогда добавляем третий параметр
// после "_" в названии удара противника (то есть верхним ударам соотвествуют верхние,
// средним средние, нижним нижние)
if(this.nemesis){
const nemesis_action = this.nemesis.currentAction;
var nemesis_action_name = this.nemesis.currentAction.split("_")[0];
var nemesis_action_vert_direct = this.nemesis.currentAction.split("_")[2];
}
if( (obj.name == "punch" || obj.name == "block" || obj.name == "crouch")
&& this.nemesis && nemesis_action_vert_direct){
if(obj.name != "punch"){
var vert_direct = "_" + nemesis_action_vert_direct;
}
else{
var vert_direct = "";
}
}
// у kick только верхняя она удара
else if(obj.name == "kick"){
var vert_direct = "_Up" ;
}
else{
var vert_direct = "";
}
const action_name = obj.name + this.direction + vert_direct;
// Если бот присел и ничего не делает (то есть action_name == "crouch"),
// то doAction не вызываем,
// вызывается, если он производит действие — блок, удар и тп
const condition = !this.crouch || action_name != "crouch" + this.direction + "_Down";
if(condition){
this.doAction(action_name, this.crouch);
break;
}
}
}
}
}
}
collision(){
super.collision();
if(this.potential_nemesis && !this.potential_nemesis.dy){
if(!this.stopped){
// подбегаем, если дистанция между противниками меньше нужной величины
// или бездействие больше определенной величины (увеличивается в update класса Player)
if(this.collision_distance > this.min_distance && (this.collision_distance < this.max_distance || this.idleCount > this.maxIdle) ){
// если игрок на дистанции больше и меньше определенного значения,
// то бот реагирует как на действие walk в его направлении
if(!this.complex){
this.reaction(frame_data[this.name]["walk" + this.potential_nemesis.direction]);
}
}
}
}
}
}

221
frame_data.js Normal file
View File

@ -0,0 +1,221 @@
const timing = {
player: {
value: {
punch_Right: {start: 0, end: 9},
punch_Left: {start: 10, end: 18},
punch_Right_Down: {start: 105, end: 112},
punch_Left_Down: {start: 113, end: 120},
block_Right: {start: 19, end: 27},
block_Left: {start: 28, end: 36},
block_Right_Up: {start: 37, end: 44},
block_Left_Up: {start: 45, end: 54},
block_Right_Down: {start: 126, end: 133},
block_Left_Down: {start: 134, end: 146},
stand_Left: {start: 55, end: 60},
stand_Right: {start: 61, end: 67},
jump_Right: {start: 68, end: 76 },
jump_Left: {start: 77, end: 84 },
crouch_Right_Down: {start: 85, end: 90},
crouch_Right_Up: {start: 91, end: 94},
crouch_Left_Down: {start: 95, end: 100},
crouch_Left_Up: {start: 101, end: 104},
walk_Right: {start: 146, end: 154},
walk_Left: {start: 155, end: 162},
kick_Right_Up: {start: 163, end: 167},
kick_Left_Up: {start: 168, end: 172},
},
size: {
w: 224,
h: 214
}
},
bar_enemy_timing:{
value: {
punch_Right: {start: 261, end: 276},
punch_Left: {start: 15, end: 30},
punch_Right_Down: {start: 181, end: 195, active: {start:192, end:195} },
punch_Left_Down: {start: 167, end: 180, active: {start:176, end:179} },
block_Right: {start: 254, end: 259, speed: 1},
block_Left: {start: 248, end: 253, speed: 1},
block_Right_Up: {start: 73, end: 85},
block_Left_Up: {start: 49, end: 57},
block_Right_Down: {start: 243, end: 247, speed: 1},
block_Left_Down: {start: 238, end: 242, speed: 1},
stand_Left: {start: 0, end: 6, speed: 1},
stand_Right: {start: 7, end: 13, speed: 1},
jump_Right: {start: 120, end: 131 },
jump_Left: {start: 97, end: 108 },
crouch_Right_Down: {start: 150, end: 165},
crouch_Right_Up: {start: 162, end: 155},
crouch_Left_Down: {start: 133, end: 148},
crouch_Left_Up: {start: 145, end: 140},
walk_Right: {start: 223, end: 237},
walk_Left: {start: 208, end: 222},
kick_Right_Up: {start: 203, end: 208},
kick_Left_Up: {start: 197, end: 202}
},
size: {
w: 148,
h: 214
},
speed: 2
},
car_salon_enemy_timing:{
value: {
punch_Right: {start: 159, end: 166},
punch_Left: {start: 8, end: 15},
punch_Right_Down: {start: 64, end: 70},
punch_Left_Down: {start: 134, end: 140},
block_Right: {start: 0, end: 7},
block_Left: {start: 39, end: 45},
block_Right_Up: {start: 18, end: 22},
block_Left_Up: {start: 26, end: 30},
block_Right_Down: {start: 125, end: 133},
block_Left_Down: {start: 149, end: 157},
stand_Left: {start: 56, end: 63},
stand_Right: {start: 47, end: 55},
jump_Right: {start: 80, end: 86 },
jump_Left: {start: 87, end: 93 },
crouch_Right_Down: {start: 101, end: 107},
crouch_Right_Up: {start: 106, end: 109},
crouch_Left_Down: {start: 169, end: 172},
crouch_Left_Up: {start: 173, end: 174},
walk_Right: {start: 117, end: 124},
walk_Left: {start: 141, end: 148},
kick_Right_Up: {start: 109, end: 116},
kick_Left_Up: {start: 93, end: 100},
},
size: {
// w: 224,
// h: 214
}
},
gg_cabinet_enemy_timing:{
value: {
punch_Right: {start: 9, end: 17},
punch_Left: {start: 0, end: 8},
punch_Right_Down: {start: 124, end: 129},
punch_Left_Down: {start: 118, end: 123},
block_Right: {start: 26, end: 33},
block_Left: {start: 18, end: 25},
block_Right_Up: {start: 42, end: 49},
block_Left_Up: {start: 34, end: 41},
block_Right_Down: {start: 154, end: 161},
block_Left_Down: {start: 130, end: 137},
stand_Left: {start: 50, end: 57},
stand_Right: {start: 58, end: 65},
jump_Right: {start: 74, end: 81 },
jump_Left: {start: 66, end: 73 },
crouch_Right_Down: {start: 110, end: 114},
crouch_Right_Up: {start: 115, end: 117},
crouch_Left_Down: {start: 98, end: 104},
crouch_Left_Up: {start: 105, end: 109},
walk_Right: {start: 146, end: 153},
walk_Left: {start: 138, end: 145},
kick_Right_Up: {start: 90, end: 97},
kick_Left_Up: {start: 82, end: 89},
},
size: {
// w: 224,
// h: 214
}
}
}
// reaction — ответная рекация бота на наши действия
// chance — вероятность того, что это действие будет выбрано ботом
// name — имя реакции, отсылающее к названию действия (метода классов Player и Enemy)
// damage — урон
// resist — уменьшение урона
// complex — сложное действие, состоящее из двух или более. У него нет параметров,
// оно просто отсылает к методу классов Player и Enemy
// start и end — номера кадров начала и конца анимации
// once — действие, выванное единожды за одно нажатие клавиши (если зажали кнопку и не отпускаем),
// например walk вызывается постоянно, пока зажата клавиша, а удар при зажатой клавише
// вызовется только один раз — чтобы вызвать его еще раз надо отпустить клавишу и снова нажать
// no_return — значит, мы не возвращаемся в стойку после действия once. Например, мы присели crouch
// active — активные кадры, когда персонаж способен наносить урон, если не указано,
// то активна середина анимации
// cool_down — число ударов, после которых наступит заморозка
class FrameData{
constructor(timing){
this.timing = timing;
this.punch_Right = {once: true, start: undefined, end: undefined,
damage: 0.5,
reaction: [{name: "block", chance: "0-50"}, {name: "punch", chance: "50-80"}, {name: "stand", chance: "80-100"}]
};
this.punch_Left = {once: true, start: undefined, end: undefined,
damage: 0.5,
reaction: [{name: "block", chance: "0-50"}, {name: "punch", chance: "50-80"}, {name: "stand", chance: "80-100"}]
};
this.punch_Right_Down = {once: true, no_return: true, start: undefined, end: undefined,
damage: 0.5,
reaction: [{name: "block", chance: "0-50"}, {name: "punch", chance: "50-80"}, {name: "crouch", chance: "80-100"}]
};
this.punch_Left_Down = {once: true, no_return: true, start: undefined, end: undefined,
damage: 0.5,
reaction: [{name: "block", chance: "0-50"}, {name: "punch", chance: "50-80"}, {name: "crouch", chance: "80-100"}]
};
this.block_Right = { start: undefined, end: undefined, resist: 1, active: {start:20, end:24},
reaction: [{name: "punch", chance: "00-80"}, {name: "stand", chance: "80-100"}]
};
this.block_Left = { start: undefined, end: undefined, resist: 1 , active: {start:29, end:33},
reaction: [{name: "punch", chance: "00-80"}, {name: "stand", chance: "80-100"}]
};
this.block_Right_Up = { start: undefined, end: undefined, resist: 1, active: {start:39, end:42},
reaction: [{name: "punch", chance: "00-80"}, {name: "stand", chance: "80-100"}]
};
this.block_Left_Up = { start: undefined, end: undefined, resist: 1, active: {start:48, end:52},
reaction: [{name: "punch", chance: "00-80"}, {name: "stand", chance: "80-100"}]
};
this.block_Right_Down = { start: undefined, end: undefined, resist: 1, active: {start:39, end:42},
reaction: [{name: "punch", chance: "00-80"}, {name: "stand", chance: "80-100"}]
};
this.block_Left_Down = { start: undefined, end: undefined, resist: 1, active: {start:48, end:52},
reaction: [{name: "punch", chance: "00-80"}, {name: "stand", chance: "80-100"}]
};
this.stand_Left = { start: undefined, end: undefined,
reaction: [{name: "punch", chance: "0-60"}, {name: "kick", chance: "60-80"}, {name: "stand", chance: "80-90"}, {name: "walkAway", chance: "90-100"}]
};
this.stand_Right = { start: undefined, end: undefined,
reaction: [{name: "punch", chance: "0-60"}, {name: "kick", chance: "60-80"}, {name: "stand", chance: "80-90"}, {name: "walkAway", chance: "90-100"}]
};
this.jump_Right = { start: undefined, end: 76,
reaction: [{name: "jumpAsidePunch", chance: "0-50"}, {name: "stand", chance: "0-50"}]
},
this.jump_Left = { start: undefined, end: 84,
reaction: [{name: "jumpAsidePunch", chance: "0-50"}, {name: "stand", chance: "0-50"}]
},
this.crouch_Right_Down = {start: undefined, end: undefined, resist: 0.2, once: true , no_return: true,
reaction: [{name: "crouch", chance: "0-50"}, {name: "punch", chance: "50-100"}]
};
this.crouch_Right_Up = { start: undefined, end: 94 , once: true , no_return: true,
reaction: [{name: "crouch", chance: "0-100"}]
};
this.crouch_Left_Down = {start: undefined, end: undefined, resist: 0.2, once: true, no_return: true,
reaction: [{name: "crouch", chance: "0-50"}, {name: "punch", chance: "50-100"}]
};
this.crouch_Left_Up = { start: undefined, end: 104 , once: true, no_return: true,
reaction: [{name: "crouch", chance: "0-100"}]
};
this.walk_Right = { start: undefined, end: undefined,
reaction: [{name: "walk", chance: "0-50"}, {name: "jumpAsidePunch", chance: "50-60"}, {name: "stand", chance: "60-80"}, {name: "jumpAside", chance: "80-100"}]
};
this.walk_Left = { start: undefined, end: undefined,
reaction: [{name: "walk", chance: "0-50"}, {name: "jumpAsidePunch", chance: "50-60"}, {name: "stand", chance: "60-80"}, {name: "jumpAside", chance: "80-100"}]
};
this.kick_Right_Up = { start: undefined, end: undefined, once: true, damage: 1, cool_down: 4,
reaction: [{name: "block", chance: "0-50"}, {name: "punch", chance: "50-80"}, {name: "stand", chance: "80-100"}]
};
this.kick_Left_Up = { start: undefined, end: undefined, once: true, damage: 1, cool_down: 4,
reaction: [{name: "block", chance: "0-50"}, {name: "punch", chance: "50-80"}, {name: "stand", chance: "80-100"}]
};
this.jumpAside_Right = {};
this.jumpAside_Left = {};
this.jumpAsidePunch_Right = {};
this.jumpAsidePunch_Left = {};
this.walkAway_Right = {};
this.walkAway_Left = {};
}
}

66
index.html Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>2D Игра</title>
</head>
<body>
<style type="text/css">
body{
background: black;
overflow: hidden;
}
.top-div {
display: flex;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
padding: 10px;
font-family: "Arial";
font-size: 10px;
background: black;
color: white;
}
.top-div::after {
content: "Загрузка";
display: flex;
position: absolute;
width: 100%;
height: 100%;
animation: loading 1s ease-in-out infinite;
font-size: 30px;
justify-content: center;
align-items: center;
}
@keyframes loading{
0% {
content: "Загрузка\00a0\00a0\00a0";
}
35% {
content: "Загрузка.\00a0\00a0";
}
65% {
content: "Загрузка..\00a0";
}
100% {
content: "Загрузка...";
}
}
</style>
<div class="container">
<canvas id="gameCanvas"></canvas>
<canvas id="introCanvas"></canvas>
<canvas id="textCanvas"></canvas>
<div class="top-div"></div>
</div>
<script src="./chars.js"></script>
<script src="./typing.js"></script>
<script src="./frame_data.js"></script>
<script src="./ui_panel.js"></script>
<script src="./player.js"></script>
<script src="./enemy.js"></script>
<script src="./battle.js"></script>
</body>
</html>

647
player.js Normal file
View File

@ -0,0 +1,647 @@
class Player{
constructor(w, h, image, dx, type, name, frameWidth = 224, frameHeight = 214,
frameSpeed = 1, size = 1){
if(type == "bot"){
this.direction = '_Left';
}
else{
this.direction = '_Right';
}
this.size = size;
this.x = w / 10 + dx;
this.y = canvas.height - this.frameHeight / 2;
this.frameWidth = frameWidth;
this.frameHeight = frameHeight;
this.currentFrame = 0;
this.currentAction = "stand" + this.direction;
this.frameSpeed = frameSpeed;
this.speed = 30;
this.dx = 0;
this.dy = 0;
this.image = image;
this.stopped = false; // блокировка передвижения
this.stopCount = 0; // счетчик блокировки передвижения, по достижении нужного значения
// персонаж снова сможет передвигаться
this.nemesis = null; // противник
this.type = type; // тип персонажа: игрок или робот
this.name = name;
this.idleCount = 0; // счтечик бездействия
this.maxIdle = 60;
this.min_distance = 70; // минимальное значение дистанции коллизии
this.max_distance = 300; // максимальное значение дистанции коллизии
this.health = {
max: 100,
current: 100
}
this.rage = { //Ярость
max: 100,
current: 0,
}
this.stunCount = 0; // оглушение, когда соперник бьет критом после накопления яорсти
this.coolDown = 0; // кулдаун на все действия после обычного действия, например, блока
this.coolDownArray = []; // массив кулдаунов конкретных ударов
}
collision(){
if(this.type == "bot"){
this.potential_nemesis = game.player;
}
else{
this.potential_nemesis = game.characters.enemy;
}
// бот всегда смотрит в сторону игрока
if(this.type == "bot" && this.potential_nemesis){
if(this.potential_nemesis.x <= this.x){
this.direction = "_Left";
}
else{
this.direction = "_Right";
}
}
if(this.potential_nemesis){
this.collision_distance = Math.abs(this.potential_nemesis.x - this.x);
}
// переходим в остояние боя, если дистанция меньше определенного значения
if(this.collision_distance <= this.min_distance){
if(!this.potential_nemesis.dy && !this.dy){ // Если мы в прыжке, то коллизия не срабатывает
this.nemesis = this.potential_nemesis;
if(this.stopCount >= 5){
this.stopped = false;
}
else if(this.nemesis.stunCount == 0){
if(!this.stopped){
this.stopCount = 1;
}
this.stopped = true;
}
if(this.stopCount > 10){
this.stopCount = 1;
}
if(this.stopCount > 0){
this.stopCount++;
}
}
}
else if(this.nemesis){ // вышли из коллизии
this.nemesis = null;
this.stopped = false;
this.stopCount = 0;
}
if(this.collision_distance < this.max_distance){
this.idleCount = 0;
}
if(this.collision_distance < this.max_distance || this.idleCount > this.maxIdle){
}
else{
this.stopped = false;
this.stopCount = 0;
}
}
update(){
if(!game.intro){
this.idleCount++;
}
if(this.type == "player"){
const action = Object.keys(key_down)[Object.keys(key_down).length - 1];
if(action){
// Вызываем действие, если кнопка нажата и действие еще не выполнено
// либо кнопка зажата и мы идем
// При этом предыдущее действие либо закончено (т.е. currentFrame == 0),
// либо мы стоим, прыгаем или присели (т.е. действия не once и не no_return)
if( (key_down[action] != false || action.split("_")[0] == "walk")
&& (this.currentFrame == 0
|| !frame_data[this.name][this.currentAction].once
|| (frame_data[this.name][this.currentAction].no_return
&&
this.currentFrame >= frame_data[this.name][this.currentAction].end - frame_data[this.name][this.currentAction].start - 1
)
)
){
if(action.split("_")[0] != "walk"){
key_down[action] = false
}
if( !( action.split("_")[0] == "walk" && this.currentAction == "walk" + this.direction ) ){
// console.log(this.currentAction, this.currentFrame)
this.doAction(action);
}
}
}
// Если идем, и нажата кнопка, лействие которой не jump и не walk
if(
key_down["walk" + this.direction]
&& this.currentAction.split("_")[0] != "walk"
&& this.currentAction.split("_")[0] != "jump"
||
this.currentAction == "walk" + this.direction
&& !key_down[this.currentAction]
){
this.dx = 0;
}
}
if(this.stunCount && this.stunCount <= 2){
this.stunCount++;
const count = 5; // число шагов (чем выше, тем быстрее)
// Если противник кританул, нас отталкивает за дистанцию боя,
// то есть меняется dx, замедляясь к концу движения
if(this.nemesis && this.stunCount > 0 && this.stunCount < count){
this.nemesis.stopped = false;
this.doAction('stand' + this.direction);
// косинус менятеся от 1 (при аргументе 0) до 0 (при аргументе Math.PI/2)
// this.stunCount изначальо будет равен 2, поэтому чтобы получить ноль, пишем this.stunCount-2
// делим Math.PI / 2 на count и получаем число секторов, которые надо тпройти
// 27 — целое число, при умножении на которое при count = 5 получим целое число, ближайшее к 71 (дистанция боя)
// 71 = x * ( Math.cos( 0 * Math.PI / 2 / 5) + Math.cos( 1 * Math.PI / 2 / 5) + Math.cos( 2 * Math.PI / 2 / 5) )
// x = 71 / 2.7 = 27
this.nemesis.dx = 27 * directions[this.nemesis.direction] * -1 * Math.cos( (this.stunCount - 2) * Math.PI / 2 / count)
// console.log(this.nemesis.dx, this.stunCount)
}
}
else{
this.stunCount = 0;
}
if(this.coolDown && this.coolDown <= 2){
this.coolDown++;
}
else{
this.coolDown = 0;
}
this.collision();
const my_action_params = frame_data[this.name][this.currentAction];
if(!game.over || game.info_animation_timer){
if(this.rage.current > 0){
if(!this.nemesis){
this.rage.current -= 0.2;
}
else{
this.rage.current -= 0.1;
}
}
else{
this.rage.current = 0;
}
if(this.nemesis){
const nemesis_action_params = frame_data[this.nemesis.name][this.nemesis.currentAction];
if(this.type == "bot"){
if(this.nemesis.currentAction.split("_")[0] != "walk"){
this.reaction(nemesis_action_params);
}
// если идем во время боя, то реакция как на действие stand
else{
this.reaction(frame_data[this.name]["stand" + this.nemesis.direction]);
}
}
this.block(nemesis_action_params, my_action_params);
}
this.countFrameData(my_action_params);
}
this.draw();
}
countFrameData(my_action_params){
// game.player.rage.current = 100;
// Если анимаци продолжается, то есть номер кадра менбше разнициы end и start
var action = frame_data[this.name][this.currentAction];
if(action && this.currentFrame < frame_data[this.name][this.currentAction].end - frame_data[this.name][this.currentAction].start - 1){
if(frame_data[this.name][this.currentAction].speed){
this.currentFrame = Math.round(this.currentFrame
+ frame_data[this.name][this.currentAction].speed);
}
else{
this.currentFrame = Math.round(this.currentFrame + this.frameSpeed);
}
// Если прыжок и нет верхнего удара
if(this.dy && this.currentAction.split("_")[0] != "kick"
&& this.currentAction.split("_")[2] != "Up"){
// узнаем номер кадра в прыжке делением текущего значения dy на восьмую часть максимального значения dy.
// 8 — число кадров в анимации прыжка
this.currentFrame = Math.ceil( this.dy / (Math.PI / 8) );
}
if(!this.stopped && this.x >= 0 && this.x <= canvas.width){
this.x += this.dx;
if(this.x < 0){
this.x = 0;
}
else if(this.x > canvas.width){
this.x = canvas.width;
}
}
if(this.type == "player"){
// console.log(key_down["walk" + this.direction], "walk" + this.direction, this.currentAction)
}
}
// Если анимация завершилась
else{
this.complex = false;
// Если отжата кнопка,
// то меняем действие на stand и обнуляем dx, на случай если персонаж шел
// if(this.type == "player"){
// console.log(key_down[this.currentAction.split("_")[0]], this.currentAction.split("_")[0], this.currentAction)
// }
if(this.currentAction.split("_")[0] == "walk"){
var current_action = this.currentAction;
}
else{
var current_action = this.currentAction + this.direction;
}
if(!key_down[current_action] || this.type == "bot"){
if(this.type == "player"){
// console.log("Отжата", current_action, key_down[current_action])
}
if(this.type != "bot" && !arrow_down || this.type == "bot" && !this.crouch ||
this.stunCount > 0){
if(this.type == "bot"){
// console.log(this.currentFrame, this.currentAction, frame_data[this.name][this.currentAction].end - frame_data[this.name][this.currentAction].start - 1)
}
// Встаем
this.doAction('stand' + this.direction);
if(this.type != "bot" && this.nemesis && this.nemesis.crouch){
this.nemesis.crouch = false;
}
}
// Если не нажата кнопка вниз
else{
// Продолжаем сидеть
this.currentAction = "crouch" + this.direction + "_Down";
}
this.dx = 0;
}
// Иначе просто обнуляем currentFrame кадра и анимация начнется снова
else{
if(!my_action_params.no_return
&& this.currentAction.split("_")[0] != "walk"){
key_down[this.currentAction] = false;
this.doAction('stand' + this.direction);
}
else if(!my_action_params.once){
this.currentFrame = 0;
if(!this.stopped && this.x >= 0 && this.x <= canvas.width){
this.x += this.dx; // добавляем для плавности перемещение в конце анимации walk
if(this.x < 0){
this.x = 0;
}
else if(this.x > canvas.width){
this.x = canvas.width;
}
}
}
}
}
// условие для прыжка
if(this.dy){
// примем максимально значение dy за число пи
if(this.dy <= Math.PI){
// косинус менятеся от 1 (при dy = 0) до 0 (при dy = Math.PI/2) и снова до 1 (при dy = Math.PI)
// Поэтому к середине прыжка скорость замедлится до 0 (5 * 0 = 0)
this.y -= 50 * Math.cos(this.dy);
this.dy += Math.PI / 8;
}
// прыжок закончился
if(this.dy > Math.PI){
this.dy = 0;
this.dx = 0;
// key_down["walk" + this.direction] = false;
if(!game.info_animation_timer){
this.y = canvas.height - this.frameHeight / 2;
}
else{
this.y = 100;
}
this.doAction('stand' + this.direction);
this.currentFrame = 0;
}
}
// если прыжка нет, то персонаж всегда внизу экрана, даже если изменили размер окна
else{
if(!game.info_animation_timer){
this.y = canvas.height - this.frameHeight / 2;
}
else{
this.y = 100;
}
}
}
block(nemesis_action_params, my_action_params){
// Проверка на блок и пересчет здоровья
const [action_name, action_hor_direct, action_vert_direct] = this.currentAction.split("_");
const [nemesis_action_name, nemesis_action_hor_direct, nemesis_action_vert_direct] = this.nemesis.currentAction.split("_");
const condition = this.nemesis.currentFrame == Math.round((nemesis_action_params.end - nemesis_action_params.start) / 2);
const start = nemesis_action_params.start;
const condition2 = "active" in nemesis_action_params
&& start + this.nemesis.currentFrame > nemesis_action_params.active.start
&& start + this.nemesis.currentFrame < nemesis_action_params.active.end;
// if(this.type == "player" && nemesis_action_params.active){
// console.log(start + this.nemesis.currentFrame , nemesis_action_params.active.start
// , nemesis_action_params.active.end, start + this.nemesis.currentFrame > nemesis_action_params.active.start
// , start + this.nemesis.currentFrame < nemesis_action_params.active.end)
// }
if("damage" in nemesis_action_params &&
(nemesis_action_hor_direct == "Left" && this.nemesis.x > this.x ||
nemesis_action_hor_direct == "Right" && this.nemesis.x < this.x)
&& (condition || condition2) ){
let resist = 0;
// противодействие урону
const start2 = frame_data[this.name][this.currentAction].start;
const condition3 = "active" in frame_data[this.name][this.currentAction]
&& start2 + this.currentFrame > frame_data[this.name][this.currentAction].active.start
&& start2 + this.currentFrame < frame_data[this.name][this.currentAction].active.end;
// if(this.type == "player"){
// console.log()
// }
let damage = nemesis_action_params.damage * 3;
// Чем выше урон от противника, тем выше ярость
const rage_delta = 10 * damage;
if(condition3 && "resist" in my_action_params){ // если действие содержит resist
// проверяем разнонаправленность по горизонтали ("_Left", "_Right")
if(action_hor_direct != nemesis_action_hor_direct){
// проверяем совпадение направления по вертиакали ("Up", "Down", undefined)
if(action_vert_direct == nemesis_action_vert_direct){
resist = my_action_params.resist;
this.nemesis.coolDown = 2;
}
}
else{
this.rage.current += rage_delta;
}
this.stopCount += 25;
}
else{
this.rage.current += rage_delta;
}
if(this.rage.current > this.rage.max){
this.rage.current = this.rage.max;
}
if(this.nemesis.rage.current >= this.nemesis.rage.max * 0.9){
damage *= 10;
this.nemesis.stunCount = 1;
if(this.type == "player"){
this.nemesis.doAction("punch" + this.nemesis.direction);
}
this.nemesis.rage.current = 0;
}
this.health.current -= damage - resist;
if(this.health.current <= 0){
this.health.current = 0;
game.over = true;
if(this.type == "bot"){
player_win++;
}
else{
bot_win++;
}
round++;
game.checkWin();
}
}
}
draw(){
const key = frame_data[this.name][this.currentAction];
if(key){
const { start, end } = key;
ctx.drawImage(this.image,
(start + this.currentFrame) * this.frameWidth, 0,
this.frameWidth, this.frameHeight,
this.x - this.frameWidth / 2, this.y - this.frameHeight / 2, this.frameWidth * this.size, this.frameHeight * this.size);
}
}
// метод объединяет вызов анимации и метода действия (чтобы не вызывать анимацию в каждом действии отдельно)
doAction(action){
if(this.type != "bot"){
if(frame_data[this.name][action]){
if("cool_down" in frame_data[this.name][action]){
if(!this.coolDownArray[action]){
this.coolDownArray[action] = 0;
}
if(action == this.currentAction){
this.coolDownArray[action]++;
}
else{
this.coolDownArray[action] = 0;
}
// console.log(this.coolDownArray[action], action, this.currentAction)
}
}
}
// frame_data[this.name][action].cool_down - 1 — отсчет от нуля, а ключ объекта от единицы
if(this.stunCount > 0 || this.coolDown > 0
|| frame_data[this.name][action]
&& frame_data[this.name][action].cool_down
&& this.coolDownArray[action]
&& this.coolDownArray[action] >= frame_data[this.name][action].cool_down - 1 ){
if(this.coolDownArray[action] >= frame_data[this.name][action].cool_down - 1){
this.coolDownArray[action] = 0;
this.stunCount = 1;
}
// перезаписываем action
action = "stand" + this.direction;
}
if(!this.dy){ // производим удар, только если мы не в прыжке
// Сложные действия, состоящие из нескольких, вернут флаг true.
let complicated_action_flag;
if(this[action]){
complicated_action_flag = this[action](action); // вызываем конкретный метод действия или удара
}
else{
console.log("Нет такого действия " + action + " в объекте frame_data[this.name]")
}
// Без этого флага будет пытаться названчить в this.currentAction несуществующее действие
if(frame_data[this.name][action] && !complicated_action_flag){
this.currentAction = action; // это в том числе идентификатор для анимации
}
}
// если в прыжке, то выполняются только верхние удары (все верхние удары называются kick)
else{
if(action.split("_")[0] == "kick" && action.split("_")[2] == "Up"){
this.currentAction = action;
}
}
this.currentFrame = 0;
}
jumpAside_Right(){
this.doAction("walk_Right");
this.doAction("jump_Right");
return true;
}
jumpAside_Left(){
this.doAction("walk_Left");
this.doAction("jump_Left");
return true;
}
jumpAsidePunch_Right(){
this.doAction("walk_Right");
this.doAction("jump_Right");
let context = this;
setTimeout(function(){
context.complex = true;
context.direction = "_Right"
context.doAction("kick_Right_Up")
console.log(context.currentAction, context.direction)
} , 100);
return true;
}
jumpAsidePunch_Left(){
this.doAction("walk_Left");
this.doAction("jump_Left");
let context = this;
setTimeout(function(){
context.complex = true;
context.direction = "_Left"
context.doAction("kick_Left_Up")
} , 100);
return true;
}
walkAway_Left(){
if(!this.crouch){
this.doAction("walk_Right");
return true;
}
}
walkAway_Right(){
if(!this.crouch){
this.doAction("walk_Left");
return true;
}
}
walk_Left(){
if(!this.stopped){
this.dx = -this.speed;
}
this.direction = '_Left';
}
walk_Right(){
if(!this.stopped){
this.dx = this.speed;
}
this.direction = '_Right';
}
punch_Right(){
}
punch_Left(){
}
punch_Right_Down(){
}
punch_Left_Down(){
}
block_Right(){
}
block_Left(){
}
block_Right_Up(){
}
block_Left_Up(){
}
block_Right_Down(){
}
block_Left_Down(action){
// if(this.currentAction != "crouch_Left"){
// this.doAction("crouch_Left");
// // let context = this;
// // setTimeout(function(){
// // // context.complex = true;
// // context.doAction("punch_Left")
// // } , 100);
// }
// // else{
// // this.currentAction = action;
// // }
// return true;
}
jump_Right(){
this.dy = 0.1;
}
jump_Left(){
this.dy = 0.1;
}
crouch_Right_Down(){
if(this.type == "bot"){
this.crouch = true;
}
}
crouch_Left_Down(){
if(this.type == "bot"){
this.crouch = true;
}
}
crouch_Right_Up(){
if(this.type == "bot"){
this.crouch = false;
}
}
crouch_Left_Up(){
if(this.type == "bot"){
this.crouch = false;
}
}
kick_Right_Up(){
}
kick_Left_Up(){
}
stand_Left(action){
if(this.currentAction == "crouch_Left_Down"){
this.currentAction = "crouch_Left_Up";
}
else{
this.currentAction = action;
}
return true;
}
stand_Right(action){
if(this.currentAction == "crouch_Right_Down"){
this.currentAction = "crouch_Right_Up";
}
else{
this.currentAction = action;
}
return true;
}
test_Left(){
console.log("Left_________________________");
}
test_Right(){
console.log("Right_________________________");
}
}

39
typing.js Normal file
View File

@ -0,0 +1,39 @@
function type(ctx_name, mystring, x0, y0, size, align, input_width, input_height){
var newline = 0;
var outdent = 0;
texgo = letters["a"];
if(align == "center"){
var cell_size = Math.floor(size / texgo.length);
x0 = x0 + input_width / 2 - mystring.length * ((texgo[0].length + 1) * cell_size ) / 2;
y0 = y0 + input_height / 2- ((texgo.length + 1) * cell_size ) / 2;
}
else{
var cell_size = size;
}
// ctx_name.clearRect(x0, y0, x0 + mystring.length * ((texgo[0].length + 1) * cell_size ), y0 + texgo.length * cell_size);
for (var i = 0; i < mystring.length; i++){
if (mystring[i] == "&"){
newline+= 8 + 4;
outdent = 0;
}
else{
texgo = letters[mystring[i]];
if(texgo){
for (var x = 0; x < texgo.length; x++){
for (var y = 0; y <= texgo[0].length; y++){
if (texgo[x][y] == 1){
ctx_name.fillStyle = "white";
ctx_name.fillRect(x0 + (y + (texgo[0].length + 1) *outdent)*cell_size, y0 + (newline + x)*cell_size, cell_size, cell_size);
}
}
}
}
outdent++;
}
}
}

168
ui_panel.js Normal file
View File

@ -0,0 +1,168 @@
class UI{
drawUserInfo(x, y, size, avatar, persone, name, n) {
const ratio = avatar.width / avatar.height;
const avatar_w = size / 2.5;
const avatar_h = avatar_w * ratio;
var j = 10, j2 = 5, outdent_x1, outdent_x2;
if(n > 0){
outdent_x1 = 0 - j;
outdent_x2 = avatar_w - j;
}
else{
outdent_x1 = -(avatar_w - 20 - j2);
outdent_x2 = avatar_w - 20 + j2;
}
ctx.fillStyle = "rgba(28, 128, 128, 0.7)";
ctx.fillRect(x + outdent_x1, y - 40, size + outdent_x2, y + 17 + 10)
ctx.fillStyle = "#FFFFFF";
ctx.font = "25px Arial ";
ctx.textAlign = "center";
ctx.fillText(name, x + size / 2, y - 10);
ctx.drawImage(avatar, x + n, 10, avatar_h, avatar_w);
var rage;
if(persone.rage.current >= persone.rage.max * 0.9){
ctx.drawImage(images[persone.type + "_avatar_rage"], x + n, 10, avatar_h, avatar_w);
rage = size;
}
else{
rage = size / persone.rage.max * persone.rage.current;
}
ctx.fillStyle = "FireBrick";
ctx.fillRect(x, y + 5, size / persone.health.max * persone.health.current, 10);
ctx.fillStyle = "DarkOrange";
ctx.fillRect(x, y + 17, rage, 10);
// if(persone.stopped || persone.stunCount > 0){
// ctx.drawImage(images.lock, x + n + outdent_x1 + 55, 10, 20, 20);
// }
// if(persone.nemesis && persone.nemesis.coolDown > 0 || persone.block_ind){
// if(!persone.block_ind){
// setTimeout(function(){
// persone.block_ind = false;
// }, 200, persone)
// }
// persone.block_ind = true;
// ctx.drawImage(images.block, x + n + outdent_x1 + 55, y + 17 + 10, 20, 20);
// }
game.time_bar.caption = "Time left: " + String( game.round_time - game.seconds );
}
drawStartScreen(pers_image, bot_image, text_image, n, delay){
this.clearAll();
intro_ctx.beginPath();
intro_ctx.moveTo(canvas.width - n, canvas.height);
intro_ctx.lineTo(0 - n, canvas.height);
intro_ctx.lineTo(canvas.width - n, 0);
intro_ctx.closePath();
intro_ctx.fill();
intro_ctx.globalCompositeOperation="source-out";
this.drawStartImage(intro_ctx, pers_image, -n - 1);
intro_ctx.globalCompositeOperation="source-over";
this.drawStartImage(intro_ctx, bot_image, n + 1);
}
drawStartImage(ctx, image, n){
if(n > 0){
var x0 = canvas.width - image.width * 1.5;
}
else{
var x0 = 0;
}
ctx.drawImage(image,
x0 + n, 0,
image.width * 1.5, image.height * 1.5);
}
drawGameCaption(ctx, image, mask, n){
const scale = 1.5;
ctx.drawImage(image,
canvas.width / 2 - image.width / 2 / scale,
canvas.height / 2 - image.height / 2 / scale,
image.width / scale, image.height / scale);
if(mask){
ctx.globalCompositeOperation="source-out";
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.beginPath();
ctx.moveTo(n * 2, canvas.height / 2 - 50);
ctx.lineTo(n * 2 + 20, canvas.height / 2 + 50);
ctx.lineTo(n * 2 + 30, canvas.height / 2 + 50);
ctx.lineTo(n * 2 + 20, canvas.height / 2 - 50);
ctx.moveTo(n * 2, canvas.height / 2 - 50);
ctx.closePath();
ctx.fill();
}
else{
ctx.globalCompositeOperation="source-over";
}
}
clearAll(){
intro_ctx.clearRect(0,0,canvas.width,canvas.height);
text_ctx.clearRect(0,0,canvas.width,canvas.height);
}
drawTimeBar(ctx, image, mask, n, scale){
ctx.drawImage(image,
canvas.width / 2 - image.width / 2 * scale,
canvas.height / 2 - image.height / 2 * scale,
image.width * scale, image.height * scale);
if(mask){
ctx.globalCompositeOperation="source-out";
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = "red";
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
else{
ctx.globalCompositeOperation="source-over";
}
}
}
class Square{
constructor(ctx_name, x, y, width, height, background, value) {
this.ctx = ctx_name;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.background = background;
this.value = value;
this.v;
this.cap;
}
set value(value){
this.v = value;
}
get value(){
return this.v;
}
set background(background){
if (background == "" || background == undefined){
// ctx.clearRect(this.x * this.width, this.y * this.height, this.width, this.height);
}
else{
ctx.fillStyle = background;
ctx.beginPath();
ctx.roundRect(this.x * this.width, this.y * this.height, this.width, this.height, [10]);
ctx.fill();
}
}
}
class Button extends Square {
set caption(caption){
this.cap = caption;
type(ctx, caption, this.x * this.width, this.y * this.height, this.height / 2, "center", this.width, this.height);
}
get caption(){
return this.cap;
}
}