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_________________________"); } }