diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0c7da4d35dcb6a7ff428bbba1ee4822e13478a69..041b653a569063f0cad45fe8dcb1d28a4aa9e319 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^0.26.0", "damerau-levenshtein": "^1.0.8", + "newton-raphson-method": "^1.0.2", "phaser": "^3.55.2" }, "devDependencies": { @@ -9222,6 +9223,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/newton-raphson-method": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/newton-raphson-method/-/newton-raphson-method-1.0.2.tgz", + "integrity": "sha512-UoBJJxNVVQhviRvled1bL76IjmPls4dNLPeF661FW2eNrv/pKKfRDpOdlN+vo9XamZvj8OMt/KtjOBTFN6I0gw==" + }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", @@ -21973,6 +21979,11 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "newton-raphson-method": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/newton-raphson-method/-/newton-raphson-method-1.0.2.tgz", + "integrity": "sha512-UoBJJxNVVQhviRvled1bL76IjmPls4dNLPeF661FW2eNrv/pKKfRDpOdlN+vo9XamZvj8OMt/KtjOBTFN6I0gw==" + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index 4c280c396a51cf7baa5d9f93d8ad54239ea7625a..4b088d87f94a1af2b0b63fdbec5cc39865830c31 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "dependencies": { "axios": "^0.26.0", "damerau-levenshtein": "^1.0.8", + "newton-raphson-method": "^1.0.2", "phaser": "^3.55.2" }, "devDependencies": { diff --git a/frontend/src/js/fight_scene.js b/frontend/src/js/fight_scene.js index d301a88ee8a789e973328007c11c3de1bae1a108..0816e9ffdd3ee26208fc297c83bb5e5a14e546ca 100644 --- a/frontend/src/js/fight_scene.js +++ b/frontend/src/js/fight_scene.js @@ -1,5 +1,7 @@ import Phaser from "phaser"; +import nr from "newton-raphson-method"; + import Foe from "./foe"; import backend from "./backend"; @@ -8,8 +10,10 @@ import levenshtein from "damerau-levenshtein"; const fightScene = new Phaser.Scene("fight"); fightScene.foes = []; +fightScene.spears = []; fightScene.preload = preload; fightScene.create = create; +fightScene.update = update; function setAnimation(obj, idleKey) { obj.play({ key: idleKey, repeat: -1 }); @@ -222,23 +226,11 @@ function initAndBindGuessPreview(scene) { } function shootSpear(enemy, hit, scene = fightScene) { - let startPoint = new Phaser.Math.Vector2(scene.player.x, scene.player.y); - let controlPoint1 = new Phaser.Math.Vector2( - enemy.x + (scene.player.x - enemy.x) / 2, - scene.cameras.main.height - (scene.player.x - enemy.x), - ); - - let endPoint = new Phaser.Math.Vector2( - enemy.x + enemy.width * 1.6, - scene.cameras.main.height - 60, - ); let message = scene.add .sprite(scene.cameras.main.width / 2, scene.cameras.main.height / 2) .setScale(1); - // hit / missing <--- message if (!hit) { - endPoint = new Phaser.Math.Vector2(-500, scene.cameras.main.height - 60); message.play({ key: "missing", repeat: 1 }); message.on("animationcomplete", () => { message.anims.remove("miss"); @@ -257,70 +249,47 @@ function shootSpear(enemy, hit, scene = fightScene) { scene.foes.splice(scene.foes.indexOf(enemy), 1); } - let curve = new Phaser.Curves.QuadraticBezier( - startPoint, - controlPoint1, - endPoint, - ); + let spear = scene.add.sprite(scene.player.x, scene.player.y, "spear"); + scene.spears.push(spear); + scene.physics.world.enable(spear); + scene.physics.add.collider(spear, scene.ground); + spear.body.setBounce(0.2); + + const dx = scene.player.x - enemy.x; + const dy = 0; + const v = 450; // MAGIC NUMBER + const w = 100; + const g = 200; + // TODO: maybe introduce damping + // TODO: expand and use analytic derivative + const f = (theta) => + 2 * dy * Math.pow(w - v * Math.cos(theta), 2) + + 2 * v * Math.sin(theta) * (w - v * Math.cos(theta)) * dx + + g * Math.pow(dx, 2); + + const theta = nr(f, Math.PI, { + verbose: true, + maxIterations: 100, + }); - let graphics = scene.add.graphics(); - graphics.lineStyle(1, 0xff00ff, 1); - - // curve.draw(graphics); // decomment to see the trajectory - - let spear = scene.add.follower(curve); - - // console.log(player.x + " " + enemy.x); - if (hit) { - spear.startFollow({ - positionOnPath: true, - duration: (scene.cameras.main.width - enemy.x) * 3, - rotateToPath: true, - verticalAdjust: true, - scale: 3, - onComplete: (x, y) => { - let spearHitObj = scene.add - .sprite( - enemy.x + enemy.width * 1.6 - 10, - scene.cameras.main.height - 60, - ) - .setScale(2); - setAnimation(spearHitObj, "spearHitAni"); - spear.anims.stop("spearAni"); - spear.anims.remove("spearAni"); - spear.destroy(); - setTimeout(() => { - enemy.flipX = false; - setAnimation(enemy, enemy.typeName + "_run"); - enemy.movement.stop(); - scene.tweens.add({ - targets: enemy, - x: -500, - duration: 5000, - onComplete: function () { - enemy.destroy(); - //callback(enemy); // ??? - }, - }); - }, 3000); - setTimeout(() => { - spearHitObj.destroy(); - }, 4000); - }, - }); - } else { - enemy.setInteractive(); - enemy.movement.resume(); - setAnimation(enemy, enemy.typeName + "_walk"); - spear.startFollow({ - positionOnPath: true, - duration: 3000, - rotateToPath: true, - verticalAdjust: true, - scale: 3, - }); + const t = dx / (w - v * Math.cos(theta)); + + if (theta) { + spear.body.setVelocity(v * Math.cos(theta), v * Math.sin(theta)); } + scene.physics.add.overlap(spear, enemy, (player, nemico) => { + scene.physics.world.removeCollider(this); + // TODO: fancy bounce + scene.spears.splice(scene.spears.indexOf(spear), 1); + spear.destroy(); + // TODO: refactor into flee method + nemico.play(nemico.species + "_run"); + nemico.flipX = false; + nemico.body.setVelocity(-200, 0); + setTimeout(() => nemico.destroy(), 2000); + }); + spear.scale = 2; spear.anims.play("spearAni"); } @@ -346,16 +315,8 @@ function submitTranscription(transcription, scene) { // TODO: we can have near misses depending on similarity! let hit = similarity >= 0.9; let enemy = match; - enemy.sprite.disableInteractive(); - // here invoke image - enemy.sprite.movement.pause(); - setAnimation(enemy.sprite, enemy.sprite.typeName + "_idle"); - // let beginTime = new Date().getTime(); - // let endTime = new Date().getTime(); - // let deltaTime = endTime - beginTime; - - shootSpear(enemy.sprite, hit); + shootSpear(enemy.animalSprite, hit); } function gameStart(scene) { @@ -371,4 +332,10 @@ function dispatchEnemy(scene) { }); } +function update() { + this.spears.forEach((spear) => { + spear.setRotation(spear.body.velocity.angle()); + }); +} + export default fightScene; diff --git a/frontend/src/js/foe.js b/frontend/src/js/foe.js index 1334707bfe842c8a5d23b93533bd27946c7aedd1..a8db37028aa93b739862df8d6d51b117c8e22561 100644 --- a/frontend/src/js/foe.js +++ b/frontend/src/js/foe.js @@ -35,8 +35,9 @@ class Foe { this.animalSprite = this.scene.physics.add .sprite(-100, this.scene.cameras.main.height - 100, this.species) - .setScale(-1 * scale, scale) + .setScale(scale) .setInteractive(); + this.animalSprite.flipX = true; this.scene.physics.add.collider(this.animalSprite, this.scene.ground);