landgreen / n-gon

2-d physics rogue-lite platformer shooter
https://landgreen.github.io/n-gon/
GNU General Public License v3.0
154 stars 225 forks source link

Slasher 3 rework: #60

Closed Whyisthisnotavalable closed 1 year ago

Whyisthisnotavalable commented 1 year ago
Screen Shot 2023-07-27 at 5 37 56 PM
Whyisthisnotavalable commented 1 year ago
slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) {
    const sides = 6
    mobs.spawn(x, y, sides, radius, "rgb(180,215,235)");
    let me = mob[mob.length - 1];
    Matter.Body.rotate(me, 2 * Math.PI * Math.random());
    me.accelMag = 0.0005 * simulation.accelScale;
    me.frictionStatic = 0;
    me.friction = 0;
    me.frictionAir = 0.02;
    me.delay = 150 * simulation.CDScale;
    me.cd = 0;
    me.cycle = 0;
    me.swordVertex = 1
    me.swordRadiusInitial = radius / 2;
    me.swordRadius = me.swordRadiusInitial;
    me.swordRadiusMax = 750 + 6 * simulation.difficulty;
    me.swordRadiusGrowRateInitial = 1.08
    me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty)
    me.isSlashing = false;
    me.swordDamage = 0.04 * simulation.dmgScale
    me.laserAngle = 3 * Math.PI / 5
    const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax
    spawn.shield(me, x, y);
    me.onDamage = function () { };
    me.do = function () {
        this.checkStatus();
        this.seePlayerByHistory(15);
        this.sword() //does various things depending on what stage of the sword swing
    };
    me.swordWaiting = function () {
        this.attraction();
        if (
            this.seePlayer.recall &&
            this.cd < simulation.cycle &&
            this.distanceToPlayer2() < seeDistance2 &&
            Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
            Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
        ) {
            //find vertex closest to the player
            let dist = Infinity
            for (let i = 0, len = this.vertices.length; i < len; i++) {
                const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos))
                if (D < dist) {
                    dist = D
                    this.swordVertex = i
                }
            }
            this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides
            this.sword = this.swordGrow
            this.cycle = 0
            this.swordRadius = this.swordRadiusInitial
            //slow velocity but don't stop
            Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5))
            //set angular velocity to 50%
            // Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5)
            //gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise
            const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex])
            const playerVector = Vector.sub(this.position, m.pos)
            const cross = Matter.Vector.cross(laserStartVector, playerVector)
            this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1)
        }
    }
    me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
    me.swordGrow = function () {
        const angle = this.angle + this.laserAngle;
        const end = {
          x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
          y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
        };

        const dx = end.x - this.vertices[this.swordVertex + 1 > 5 ? 0 : this.swordVertex + 1].x;
        const dy = end.y - this.vertices[this.swordVertex + 1 > 5 ? 0 : this.swordVertex + 1].y;
        const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);

        const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? 5 : this.swordVertex - 1].x;
        const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? 5 : this.swordVertex - 1].y;
        const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);

        this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle);
        this.laserSpear(this.vertices[this.swordVertex + 1 > 5 ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
        this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? 5 : this.swordVertex - 1], angle2 * (Math.PI / 180))

        Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9))
        // this.swordRadius += this.swordRadiusGrowRate
        this.cycle++
        // this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03)
        this.swordRadius *= this.swordRadiusGrowRate

        if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial
        // if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate)
        if (this.swordRadius < this.swordRadiusInitial || this.isStunned) {
            // this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate)
            this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial
            this.sword = this.swordWaiting
            this.swordRadius = 0
            this.cd = simulation.cycle + this.delay;
        }
    }
    me.laserSpear = function (where, angle) {
        const vertexCollision = function (v1, v1End, domain) {
            for (let i = 0; i < domain.length; ++i) {
                let v = domain[i].vertices;
                const len = v.length - 1;
                for (let j = 0; j < len; j++) {
                    results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
                    if (results.onLine1 && results.onLine2) {
                        const dx = v1.x - results.x;
                        const dy = v1.y - results.y;
                        const dist2 = dx * dx + dy * dy;
                        if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
                    }
                }
                results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
                if (results.onLine1 && results.onLine2) {
                    const dx = v1.x - results.x;
                    const dy = v1.y - results.y;
                    const dist2 = dx * dx + dy * dy;
                    if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
                }
            }
        };
        best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
        const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
        vertexCollision(where, look, body); // vertexCollision(where, look, mob);
        vertexCollision(where, look, map);
        if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
        if (best.who && (best.who === playerBody || best.who === playerHead)) {
            this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player

            if (m.immuneCycle < m.cycle) {
                m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
                m.damage(this.swordDamage);
                simulation.drawList.push({ //add dmg to draw queue
                    x: best.x,
                    y: best.y,
                    radius: this.swordDamage * 1500,
                    color: "rgba(80,0,255,0.5)",
                    time: 20
                });
            }
        }
        if (best.dist2 === Infinity) best = look;
        ctx.beginPath(); //draw beam
        ctx.moveTo(where.x, where.y);
        ctx.lineTo(best.x, best.y);
        ctx.strokeStyle = "rgba(100,100,255,0.1)"; // Purple path
        ctx.lineWidth = 15;
        ctx.stroke();
        ctx.strokeStyle = "rgba(100,100,255,0.5)"; // Purple path
        ctx.lineWidth = 4;
        ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
        ctx.stroke(); // Draw it
        ctx.setLineDash([]);
    }
},
Whyisthisnotavalable commented 1 year ago

Right now it's pretty easy to dodge, adding 2 extra swords would make it a bit harder.

landgreen commented 1 year ago

Difficulty scaling can be done by adjusting range and speed. I thought the difficulty was fine in my limited testing, but I'll rebalance if it seems too easy or hard. I'm not looking for code contributions for mobs. This is mostly because writing the mob code is very quick and balance and testing is very slow. Even with a community written mob I'd still have to do balance and testing, so there isn't any benefit.

Whyisthisnotavalable commented 1 year ago

This is just a concept, it doesn't have to be added.

landgreen commented 1 year ago

OK. It got me thinking. Thanks.