647 lines
28 KiB
JavaScript
647 lines
28 KiB
JavaScript
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_________________________");
|
||
}
|
||
} |