landgreen / n-gon

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

New map: soft #189

Closed Whyisthisnotavalable closed 3 months ago

Whyisthisnotavalable commented 3 months ago
soft() {
    simulation.makeTextLog(`<img src="https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/Hotpot-removed.png" width="100" height="100" style="background-image: radial-gradient(circle, gray, black, transparent)">`);
    simulation.makeTextLog(`<strong>soft</strong> by <span class='color-var'>Richard0820</span>`);
    simulation.makeTextLog("<em>The lasers deal less damage the higher level you are</em>")
    const portals = [];
    portals.push(level.portal({
        x: -1525,
        y: -250
    }, Math.PI / 2, {
        x: 1100,
        y: -1025
    }, Math.PI / 2))
    const soft = {
        createCloth(x, y, radius, width, height, attachToPlayer = false, stayStill = false, options, touchPlayer = true, constrictionStrength = 0.001) {
            const bodies = [];
            const constraints = [];
            const otherCons = [];
            const bodyWidth = radius;
            const bodyHeight = radius;
            const numRows = Math.ceil(height / bodyHeight);
            const numCols = Math.ceil(width / bodyWidth);

            for (let i = 0; i < numRows; i++) {
                for (let j = 0; j < numCols; j++) {
                    const posX = x + j * bodyWidth + bodyWidth / 2;
                    const posY = y + i * bodyHeight + bodyHeight / 2;

                    const rect = Matter.Bodies.circle(posX, posY, (bodyWidth + bodyHeight) / 4, options);
                    rect.collisionFilter.category = cat.body;
                    rect.collisionFilter.mask = (touchPlayer ? cat.player | cat.body | cat.bullet | cat.mob | cat.mobBullet : cat.body | cat.bullet | cat.mob | cat.mobBullet);
                    rect.classType = "body";

                    Composite.add(engine.world, rect);

                    bodies.push(rect);
                }
            }

            for (let i = 0; i < numRows; i++) {
                for (let j = 0; j < numCols; j++) {
                    const bodyIndexA = i * numCols + j;
                    if (j < numCols - 1) {
                        const bodyIndexB = i * numCols + (j + 1);
                        const constraint = Constraint.create({
                            bodyA: bodies[bodyIndexA],
                            bodyB: bodies[bodyIndexB],
                            stiffness: 0.06,
                            damping: 0.001
                        });
                        Composite.add(engine.world, constraint);
                        constraints.push(constraint);
                    }
                    if (i < numRows - 1) {
                        const bodyIndexB = (i + 1) * numCols + j;
                        const constraint = Constraint.create({
                            bodyA: bodies[bodyIndexA],
                            bodyB: bodies[bodyIndexB],
                            stiffness: 0.06,
                            damping: 0.001
                        });
                        Composite.add(engine.world, constraint);
                        constraints.push(constraint);
                    }
                }
            }

            for (let i = 0; i < numRows - 1; i++) {
                for (let j = 0; j < numCols - 1; j++) {
                    const bodyA = bodies[i * numCols + j];
                    const bodyB = bodies[(i + 1) * numCols + j + 1];
                    const constraint = Constraint.create({
                        bodyA: bodyA,
                        bodyB: bodyB,
                        stiffness: 0.02
                    });
                    constraints.push(constraint);
                }
            }

            for (let i = 0; i < numRows - 1; i++) {
                for (let j = 1; j < numCols; j++) {
                    const bodyA = bodies[i * numCols + j];
                    const bodyB = bodies[(i + 1) * numCols + j - 1];
                    const constraint = Constraint.create({
                        bodyA: bodyA,
                        bodyB: bodyB,
                        stiffness: 0.02
                    });
                    constraints.push(constraint);
                }
            }
            if (stayStill) {
                for (let i = 0; i < bodies.length; i++) {
                    const by = bodies[i];
                    const spawnX = by.position.x + bodyWidth / 2;
                    const spawnY = by.position.y + bodyHeight / 2;
                    const isLastColumn = (i + 1) % numCols === 0;
                    const isFirstColumn = i % numCols === 0;
                    const stiffness = constrictionStrength * (isLastColumn || isFirstColumn ? 100 : 1); // Apply extra stiffness to first and last columns

                    const cost = Constraint.create({
                        bodyA: by,
                        pointB: { x: spawnX, y: spawnY },
                        stiffness: stiffness,
                        length: 0
                    });

                    Composite.add(engine.world, cost);
                    otherCons.push(cost);
                }
            }
            if(attachToPlayer) {
                for(let i = 0; i < bodies.length; i++) {
                    const cost = Constraint.create({
                        bodyA: bodies[i],
                        pointB: player.position,
                        stiffness: 0.0005,
                        length: 0
                    });
                    Composite.add(engine.world, cost);
                }
            }

            return { bodies, constraints, otherCons};
        },
        clothOptions: {
            frictionAir: 0.005,
        },
        isOuterBoundary(body, bodies) { //unused
            const neighbors = [
                { x: body.position.x + 1, y: body.position.y },
                { x: body.position.x - 1, y: body.position.y },
                { x: body.position.x, y: body.position.y + 1 },
                { x: body.position.x, y: body.position.y - 1 }
            ];

            for (let i = 0; i < neighbors.length; i++) {
                const neighbor = neighbors[i];
                const isNeighbor = bodies.some(b => b.position.x === neighbor.x && b.position.y === neighbor.y);
                if (!isNeighbor) {
                    return true;
                }
            }
            return false;
        },
        draw(cloth) {
            ctx.beginPath();
            ctx.lineWidth = 2;
            ctx.strokeStyle = "rgba(0,0,0,0.3)";
            ctx.fillStyle = "black";
            for (let i = 0, len = cloth.constraints.length; i < len; ++i) {
                const constraint = cloth.constraints[i];
                ctx.moveTo(constraint.bodyA.position.x, constraint.bodyA.position.y);
                ctx.lineTo(constraint.bodyB.position.x, constraint.bodyB.position.y);
            }
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
        },
        addGravity(bodies, magnitude) {
            for (var i = 0; i < bodies.length; i++) {
                bodies[i].force.y += bodies[i].mass * magnitude;
            }
        },
        gravity(cloth) {
            this.addGravity(cloth.bodies, simulation.g);
        },
        breaker(cloth, percentage = 0.5) {
            const totalConstraints = cloth.constraints.length;
            const constraintsToRemove = Math.ceil(totalConstraints * percentage);

            for (let i = 0; i < constraintsToRemove; i++) {
                const randomIndex = Math.floor(Math.random() * cloth.constraints.length);

                let removedConstraint = cloth.constraints.splice(randomIndex, 1)[0];
                Composite.remove(engine.world, removedConstraint);
            }
        },
        destroyer(cloth, percentage = 0.99999) {
            const otherCons = cloth.otherCons.length;
            const otherCons2Remove = Math.ceil(otherCons * percentage);

            for (let i = 0; i < otherCons2Remove; i++) {
                const randomIndex = Math.floor(Math.random() * cloth.otherCons.length);

                let removedConstraint = cloth.otherCons.splice(randomIndex, 1)[0];
                Composite.remove(engine.world, removedConstraint);
            }
        },
        annihilate(cloth) {
            const totalBodies = cloth.bodies.length;
            for (let i = 0; i < totalBodies; i++) {
                const removeBody = cloth.bodies[i];
                Composite.remove(engine.world, removeBody);
            }
            cloth.bodies.length = 0; // Clear the bodies array after removal
        }
    }
    const clothArray = [];
    clothArray.push(soft.createCloth(-100, 0, 50, 1000, 300, false, true, soft.clothOptions, true))
    clothArray.push(soft.createCloth(-2000, 2375, 50, 1525, 200, false, true, soft.clothOptions, true))
    clothArray.push(soft.createCloth(-3950, 125, 50, 1800, 125, false, true, soft.clothOptions, true))
    const annoyingStuff = {
        lasers(where, angle) {
            const vertexCollision = function (v1, v1End, domain) {
                for (let i = 0; i < domain.length; ++i) {
                    let vertices = domain[i].vertices;
                    const len = vertices.length - 1;
                    for (let j = 0; j < len; j++) {
                        results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[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: vertices[j],
                                v2: vertices[j + 1]
                            };
                        }
                    }
                    results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[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: vertices[0],
                            v2: vertices[len]
                        };
                    }
                }
            };

            const seeRange = 7000;
            best = {
                x: null,
                y: null,
                dist2: Infinity,
                who: null,
                v1: null,
                v2: null
            };
            const look = {
                x: where.x + seeRange * Math.cos(angle),
                y: where.y + seeRange * Math.sin(angle)
            };
            // vertexCollision(where, look, mob);
            vertexCollision(where, look, map);
            vertexCollision(where, look, body);
            if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
            if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
                m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
                const dmg = 0.5 / simulation.dmgScale;
                m.damage(dmg);
                simulation.drawList.push({ //add dmg to draw queue
                    x: best.x,
                    y: best.y,
                    radius: dmg * 1500,
                    color: "rgba(80,0,255,0.5)",
                    time: 20
                });
            }
            //draw beam
            if (best.dist2 === Infinity) best = look;
            ctx.moveTo(where.x, where.y);
            ctx.lineTo(best.x, best.y);
        },
        laserBoss(x, y, radius = 30) {
            mobs.spawn(x, y, 6, radius, "#f00");
            let me = mob[mob.length - 1];

            setTimeout(() => { //fix mob in place, but allow rotation
                me.constraint = Constraint.create({
                    pointA: {
                        x: me.position.x,
                        y: me.position.y
                    },
                    bodyB: me,
                    stiffness: 1,
                    damping: 1
                });
                Composite.add(engine.world, me.constraint);
            }, 2000); //add in a delay in case the level gets flipped left right
            me.count = 0;
            me.frictionAir = 0.03;
            // me.torque -= me.inertia * 0.002
            spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random())
            Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger
            me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
            me.isBoss = true;
            // spawn.shield(me, x, y, 1);  //not working, not sure why
            me.onDeath = function () {
                powerUps.spawnBossPowerUp(this.position.x, this.position.y)
            };
            me.rotateVelocity = -Math.min(0.0045, 0.0015 * simulation.accelScale * simulation.accelScale) * (level.levelsCleared > 8 ? 1 : -1)
            me.do = function () {
                this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors
                this.checkStatus();

                if (!this.isStunned) {
                    //check if slowed
                    let slowed = false
                    for (let i = 0; i < this.status.length; i++) {
                        if (this.status[i].type === "slow") {
                            slowed = true
                            break
                        }
                    }
                    if (!slowed) {
                        this.count++
                        Matter.Body.setAngle(this, this.count * this.rotateVelocity)
                        Matter.Body.setAngularVelocity(this, 0)
                    }

                    ctx.beginPath();
                    for(let i = 0; i < this.vertices.length; i++) {
                        if(Math.sin((2 * Math.PI * simulation.cycle) / (50 + i)) > 0) {
                            this.lasers(this.vertices[i], Math.atan2(this.vertices[i].y - this.position.y, this.vertices[i].x - this.position.x));
                        }
                    }
                    ctx.strokeStyle = "#50f";
                    ctx.lineWidth = 1.5;
                    ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
                    ctx.stroke(); // Draw it
                    ctx.setLineDash([]);
                    ctx.lineWidth = 20;
                    ctx.strokeStyle = "rgba(80,0,255,0.07)";
                    ctx.stroke(); // Draw it
                }
            };
            me.lasers = function (where, angle) {
                const vertexCollision = function (v1, v1End, domain) {
                    for (let i = 0; i < domain.length; ++i) {
                        let vertices = domain[i].vertices;
                        const len = vertices.length - 1;
                        for (let j = 0; j < len; j++) {
                            results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[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: vertices[j],
                                    v2: vertices[j + 1]
                                };
                            }
                        }
                        results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[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: vertices[0],
                                v2: vertices[len]
                            };
                        }
                    }
                };

                const seeRange = 7000;
                best = {
                    x: null,
                    y: null,
                    dist2: Infinity,
                    who: null,
                    v1: null,
                    v2: null
                };
                const look = {
                    x: where.x + seeRange * Math.cos(angle),
                    y: where.y + seeRange * Math.sin(angle)
                };
                // vertexCollision(where, look, mob);
                vertexCollision(where, look, map);
                vertexCollision(where, look, body);
                if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
                if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
                    m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
                    const dmg = 0.5 / simulation.dmgScale;
                    m.damage(dmg);
                    simulation.drawList.push({ //add dmg to draw queue
                        x: best.x,
                        y: best.y,
                        radius: dmg * 1500,
                        color: "rgba(80,0,255,0.5)",
                        time: 20
                    });
                }
                //draw beam
                if (best.dist2 === Infinity) best = look;
                ctx.moveTo(where.x, where.y);
                ctx.lineTo(best.x, best.y);
            }
        }
    }
    level.setPosToSpawn(-350, 0); 
    level.exit.x = 1075;
    level.exit.y = 20;
    spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
    spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
    level.defaultZoom = 1800
    simulation.zoomTransition(level.defaultZoom)
    document.body.style.backgroundColor = "#aaFFFF55";
    spawn.mapRect(900, 50, 425, 250);
    // spawn.mapRect(900, -1050, 0.1, 1350);
    spawn.mapRect(-475, 2375, 1800, 250);
    spawn.mapRect(-4400, 2375, 2475, 250);
    spawn.mapRect(-4400, -450, 250, 3075);
    spawn.mapRect(-4400, -450, 2225, 250);
    spawn.mapRect(-2425, -1300, 250, 1100);
    spawn.mapRect(-2425, -1300, 3825, 250);
    spawn.mapRect(1325, -1300, 250, 3925);
    spawn.mapRect(-875, -1300, 250, 1375);
    spawn.mapRect(-725, 50, 675, 250);
    spawn.mapRect(-875, 175, 175, 125);
    for(let i = 0; i < 6; i++) {
        spawn.mapRect(-4175, 2000 - i*375, 50, 125);
    }
    spawn.mapRect(-3925, 162.5, 50, 125);
    spawn.mapRect(-2175, 162.5, 50, 125);
    spawn.mapRect(300, 2025, 250, 600);
    spawn.mapRect(-2150, 175, 50, 25);
    spawn.mapRect(-2150, 250, 50, 25);
    spawn.mapRect(-900, 175, 50, 25);
    spawn.mapRect(-900, 250, 50, 25);
    spawn.mapRect(-1600, 175, 50, 25);
    spawn.mapRect(-1500, 175, 50, 25);
    spawn.mapRect(-1600, 250, 50, 25);
    spawn.mapRect(-1500, 250, 50, 25);
    spawn.mapRect(-1925, 175, 50, 25);
    spawn.mapRect(-1925, 250, 50, 25);
    spawn.mapRect(-1200, 175, 50, 25);
    spawn.mapRect(-1200, 250, 50, 25);
    spawn.bodyRect(-2125, 200, 1250, 50);
    spawn.debris(425, 200, 50);
    spawn.debris(-650, 2100, 50);
    spawn.debris(-3000, 1925, 50);
    spawn.debris(-3825, 1550, 50);
    spawn.debris(-2475, -50, 50);

    const bouncyBody = body[body.length - 1];
    bouncyBody.restitution = 0.9;
    spawn.mapVertex(-2175 + 1300 / 2, -1050 + 1225 / 2, "0 -400 -100 -300 -100 0 100 0 100 -300");

    spawn.mapVertex(-4150 + 1975 / 2, -200 + 2575 / 2, "0 -800 -200 -600 -200 0 0 200 200 0 200 -600 0 200");
    const mapWithVertex = map[map.length - 1];
    let index1 = 0;
    level.custom = () => {
        level.exit.drawAndCheck();

        level.enter.draw();

        if(player.position.x > 425 && index1 === 0) {
            soft.breaker(clothArray[0], 0.7);
            soft.destroyer(clothArray[0]);
            index1++;
        }
        if (player.position.y > 1300 && index1 === 1) {
            setTimeout(() => {
                soft.breaker(clothArray[0], 1);
                soft.annihilate(clothArray[0]);
                clothArray.splice(0, 1); 
            }, 1000); //prevents bugs
            simulation.makeTextLog("Couldn't be so simple, could it?", 2000 * Math.random());
            index1++;
        }
    };
    level.customTopLayer = () => { 
        for(let i = 0; i < portals.length; i++) {
            portals[i][2].query();
            portals[i][3].query();
            portals[i][0].draw();
            portals[i][1].draw();
            portals[i][2].draw();
            portals[i][3].draw();
        }
        ctx.beginPath();
        if(Math.sin((2 * Math.PI * simulation.cycle) / (50)) > 0) {
            annoyingStuff.lasers(mapWithVertex.vertices[0], Math.atan2(mapWithVertex.vertices[0].y - mapWithVertex.position.y, mapWithVertex.vertices[0].x - mapWithVertex.position.x));
            annoyingStuff.lasers(mapWithVertex.vertices[3], Math.atan2(mapWithVertex.vertices[3].y - mapWithVertex.position.y, mapWithVertex.vertices[3].x - mapWithVertex.position.x));
        } 
        if(Math.sin((2 * Math.PI * simulation.cycle) / (51)) > 0) {
            annoyingStuff.lasers(mapWithVertex.vertices[1], Math.atan2(mapWithVertex.vertices[1].y - mapWithVertex.position.y, mapWithVertex.vertices[1].x - mapWithVertex.position.x));
            annoyingStuff.lasers(mapWithVertex.vertices[4], Math.atan2(mapWithVertex.vertices[4].y - mapWithVertex.position.y, mapWithVertex.vertices[4].x - mapWithVertex.position.x));
        } 
        if(Math.sin((2 * Math.PI * simulation.cycle) / (52)) > 0) {
            annoyingStuff.lasers(mapWithVertex.vertices[2], Math.atan2(mapWithVertex.vertices[2].y - mapWithVertex.position.y, mapWithVertex.vertices[2].x - mapWithVertex.position.x));
            annoyingStuff.lasers(mapWithVertex.vertices[5], Math.atan2(mapWithVertex.vertices[5].y - mapWithVertex.position.y, mapWithVertex.vertices[5].x - mapWithVertex.position.x));
        }

        ctx.strokeStyle = "#000";
        ctx.lineWidth = 1.5;
        ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
        ctx.stroke(); // Draw it
        ctx.setLineDash([]);
        ctx.lineWidth = 20;
        ctx.strokeStyle = "rgba(0,0,0,0.07)";
        ctx.stroke(); // Draw it

        for(let i = 0; i < clothArray.length; i++) {
            soft.draw(clothArray[i]);
            soft.gravity(clothArray[i]);
        }

        ctx.beginPath();
        ctx.fillStyle = "rgba(69, 69, 69, 0.1)";
        ctx.rect(-475, 175, 425, 2300);
        ctx.rect(900, 175, 425, 2300);
        ctx.rect(-875, 175, 400, 10000);
        ctx.rect(-4200, -250, 2025, 2775);
        ctx.fill();

        ctx.beginPath();
        ctx.fillStyle = (m.pos.x < -725 && m.pos.y < 175) ? `rgba(68, 68, 68, ${Math.max(0.3, Math.min((-775 - m.pos.x) / 100, 0.99))})` : color.map;
        ctx.rect(-875, 50, 175, 150);
        ctx.fill();

    };
    annoyingStuff.laserBoss(-1525, 1025);
    spawn.pulsar(-1525, -850);
    spawn.pulsar(1125, 1600);
    spawn.pulsar(-250, 1600);
    spawn.pulsar(-1450, 1600);
    spawn.pulsar(-2950, 1750);
    spawn.pulsar(-3375, 1750);
    spawn.pulsar(-3825, 1300);
    spawn.pulsar(-3825, 850);
    spawn.pulsar(-3450, 50);
    spawn.pulsar(-2925, 50);
    spawn.pulsar(-1900, -400);
    spawn.pulsar(-1200, -400);

    powerUps.addResearchToLevel()
    powerUps.directSpawn(-775, 125, "tech");
    powerUp[powerUp.length - 1].collisionFilter.mask = cat.map | cat.body | cat.powerUp
    spawn.bodyRect(-875, 75, 25, 100);
    let hardBody = body[body.length - 1];
    hardBody.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.powerUp
},
landgreen commented 3 months ago

added to patch. another great map. keep em coming!