SC CODE: // Copyright 2024. Civilware. All rights reserved.
// TELA Decentralized Web Document (TELA-DOC-1)
Function InitializePrivate() Uint64
10 IF init() == 0 THEN GOTO 30
20 RETURN 1
30 STORE("nameHdr", "update.js")
31 STORE("descrHdr", "Update file")
32 STORE("iconURLHdr", "")
33 STORE("dURL", "update.js")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "")
36 STORE("fileCheckC", "3131974678dd600eea7d67914582bad7124ed77d89d9da43117e9884406d9b")
37 STORE("fileCheckS", "7b84519ecce10a4ccf545dcdc3d86b6ef09bc7c6f321d18598ff265677a44b2")
100 RETURN 0
End Function
Function init() Uint64
10 IF EXISTS("owner") == 0 THEN GOTO 30
20 RETURN 1
30 STORE("owner", address())
50 STORE("docVersion", "1.0.0")
60 STORE("hash", HEX(TXID()))
70 STORE("likes", 0)
80 STORE("dislikes", 0)
100 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
Function Rate(r Uint64) Uint64
10 DIM addr as String
15 LET addr = address()
16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
20 RETURN 1
30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
40 IF r < 50 THEN GOTO 70
50 STORE("likes", LOAD("likes")+1)
60 RETURN 0
70 STORE("dislikes", LOAD("dislikes")+1)
100 RETURN 0
End Function
/*window.addEventListener("load", resizeSVG);
window.addEventListener("resize", resizeSVG);
function resizeSVG() {
sw = window.innerWidth;
sh = window.innerHeight;
updateViewBox();
}
function updateViewBox() {
if (keys["="]) zoom *= 0.98;
if (keys["-"]) zoom *= 1.02;
const viewW = sw * zoom;
const viewH = sh * zoom;
const camX = camCenter.x - viewW / 2;
const camY = camCenter.y - viewH / 2;
camLeft = camCenter.x - (sw * zoom) / 2;
camTop = camCenter.y - (sh * zoom) / 2;
camRight = camCenter.x + (sw * zoom) / 2;
camBottom = camCenter.y + (sh * zoom) / 2;
svg.setAttribute("viewBox", `${camX} ${camY} ${viewW} ${viewH}`);
}
function updateCamera(dt) {
// camera follows player
camCenter.x = game.player.x;
camCenter.y = game.player.y;
}
//----------
// Get Input
window.addEventListener("wheel", e => {
if (e.deltaY < 0) zoom *= 0.9; // zoom in
else zoom *= 1.1; // zoom out
zoom = Math.max(0.2, Math.min(zoom, 5)); // clamp
});
window.addEventListener("keydown", e => {
keys[e.key] = true;
});
window.addEventListener("keyup", e => {
keys[e.key] = false;
});
let prevStartPressed = false;
function gatherInput(dt) {
const p = game.player;
// --- KEYBOARD ---
let move = (keys["a"] ? -1 : 0) + (keys["d"] ? 1 : 0);
let vert = (keys["w"] ? 1 : 0) + (keys["s"] ? -1 : 0);
let wantPunch = keys["l"];
let wantKick = keys[";"];
let wantJump = keys[" "];
let startPressed = keys["Escape"]
// --- GAMEPAD ---
const gp = navigator.getGamepads()[0];
if (gp) {
// D-pad
if (gp.buttons[14].pressed) move = -1; // left
if (gp.buttons[15].pressed) move = 1; // right
if (gp.buttons[12].pressed) vert = 1; // up
if (gp.buttons[13].pressed) vert = -1; // down
// Face buttons
if (gp.buttons[0].pressed) wantJump = true; // A = jump
if (gp.buttons[2].pressed) wantPunch = true; // X = punch
if (gp.buttons[1].pressed) wantKick = true; // B = kick
startPressed = gp.buttons[9].pressed || startPressed;
}
// --- MENU ---
menu(startPressed,move);
prevStartPressed = startPressed;
// --- APPLY TO PLAYER ---
p.moveInput = move;
p.verticalInput = vert;
p.wantPunch = wantPunch;
p.wantKick = wantKick;
p.wantJump = wantJump;
if (p.wantJump) jump();
}
function jump(p = game.player) {
if (p.onGround && p.anim.state !=="kick") {
p.vy = -game.jumpStrength;
p.onGround = false;
p.inAir = true;
}
}
function applyIntent(entity,dt) {
const p = entity;
if (p.stun > 0) {
p.stun -= dt/1000;
return;
}
if ((!p.inAir && p.wantKick)||
(!p.inAir && entity.verticalInput < 0)){
if (p.moveInput !== 0) {
if(p.moveInput > 0 ){
p.facing = 1;
}else{
p.facing = -1;
}
p.moveInput = 0;
}
}
p.vx = p.moveInput * p.speed;
if (p.moveInput !== 0) {
if(p.moveInput > 0 ){
p.facing = 1;
}else{
p.facing = -1;
}
}
}
function updateMovementState(entity) {
if(entity.moveState === "dying")return;
if (entity.inAir && entity.vy < -0.2) entity.moveState = "jump";
else if (entity.inAir && entity.vy > 0.2) entity.moveState = "fall";
else if (entity.moveInput !== 0) entity.moveState = "walk";
else if (entity.verticalInput < 0 && !entity.inAir) entity.moveState = "crouch";
else entity.moveState = "idle";
if (entity.moveState === "crouch") {
entity.height = 60;
entity.skeletonOffsetY = 12
} else {
entity.height = 100;
entity.skeletonOffsetY = 0;
}
}
function updateActionState(entity) {
entity.actionState = "none";
if (entity === game.player) {
if (!entity.inAir) {
if (entity.verticalInput < 0) {
if (entity.wantPunch) return entity.actionState = "crouchPunch";
if (entity.wantKick) return entity.actionState = "crouchKick";
return entity.actionState = "crouch";
}else if(entity.verticalInput > 0){
if (entity.wantPunch) return entity.actionState = (entity.moveState === "walk") ? "walkHighPunch" : "highPunch";
if (entity.wantKick) return entity.actionState = "highKick";
}else{
if (entity.wantPunch)return entity.actionState = (entity.moveState === "walk") ? "walkPunch" : "punch";
if (entity.wantKick)return entity.actionState = "kick";
}
} else {
if (entity.verticalInput < 0) {
if (entity.wantPunch) return entity.actionState = "jumpLowPunch";
if (entity.wantKick) return entity.actionState = "jumpLowKick";
}else if(entity.verticalInput > 0){
if (entity.wantPunch) return entity.actionState = "jumpHighPunch";
if (entity.wantKick) return entity.actionState = "jumpHighKick";
}else{
if (entity.wantPunch) return entity.actionState = "jumpPunch";
if (entity.wantKick) return entity.actionState = "jumpKick";
}
}
}else if(entity.type !== "boss" ){
if (entity.verticalInput == 1) {
if (entity.wantPunch) {entity.actionState = "highPunch"}
if (entity.wantKick) {entity.actionState = "highKick"}
}else{
if (entity.wantPunch) {entity.actionState = "punch"}
if (entity.wantKick) {entity.actionState = "kick"}
}
}else{
//boss
if (entity.verticalInput == 1) {
if (entity.wantPunch) {entity.actionState = (entity.moveState === "walk") ? "walkPunch" : "punch";}
if (entity.wantKick) {entity.actionState = "highKick"}
}else{
if (entity.wantPunch) {entity.actionState = (entity.moveState === "walk") ? "walkHighPunch" : "highPunch";}
if (entity.wantKick) {entity.actionState = "kick"}
}
}
}
function selectAnimationState(entity) {
let finalState = entity.moveState;
if (entity.actionState !== "none") {
finalState = entity.actionState;
}
if (finalState !== entity.anim.state) {
playAnimation(entity, finalState);
}
}
function playAnimation(entity, name) {
const anim = entity.anim;
if (anim.state === name) return;
anim.state = name;
anim.nextPose = poses[name];
}
function updateAnimation(entity, dt) {
const anim = entity.anim;
anim.frame++
// If blending to a new pose
if (anim.nextPose) {
anim.currentPose = anim.nextPose;
anim.nextPose = null;
anim.frame = 0;
}
// Walk cycle override
if (anim.state === "walk" || anim.state === "walkPunch" || anim.state === "walkHighPunch") {
applyWalk(entity, dt);
if (anim.state === "walkPunch") {
applyBlendedPose(entity, poses.walkPunch, poses.walkPunch, 1);
}else if (anim.state === "walkHighPunch") {
applyBlendedPose(entity, poses.walkHighPunch, poses.walkHighPunch, 1);
}
} else if(anim.state !== "dying"){
// Idle, punch, kick, jump, fall, etc.
applyBlendedPose(entity, anim.currentPose, anim.currentPose, 1);
}else{
applyDeath(entity, dt);
}
}
function applyWalk(entity,dt){
const t = performance.now() * .01; // walk speed multiplier
const torso = entity.skeleton.torso;
torso.rotationTarget = Math.sin(t * 0.5) * 0.05;
// head
torso.children.head.rotationTarget = 0;
// legs
torso.children.upperLegL.rotationTarget = Math.sin(t) * 0.6;
torso.children.upperLegR.rotationTarget = -Math.sin(t) * 0.6;
torso.children.upperLegL.children.lowerLegL.rotationTarget = Math.max(-Math.sin(t + 0.5) * 0.4, -0.1);
torso.children.upperLegR.children.lowerLegR.rotationTarget = Math.max(Math.sin(t + 0.5) * 0.4, -0.1);
// arms
torso.children.upperArmL.rotationTarget = -Math.sin(t) * 0.4;
torso.children.upperArmR.rotationTarget = Math.sin(t) * 0.4;
torso.children.upperArmL.children.lowerArmL.rotationTarget = ( Math.sin(t + 0.3) * 0.1) -1;
torso.children.upperArmR.children.lowerArmR.rotationTarget = ( Math.sin(t + 0.3) * 0.1) -.5;
}
function applyDeath(entity,dt){
const t = performance.now() * .01; // walk speed multiplier
const torso = entity.skeleton.torso;
const { segment: seg, platform: enemyPlatform } = getPlatformUnder(entity);
const baseAngle = seg ? getSegmentAngle(seg) : 0; // fallback to flat
torso.rotationTarget = entity.facing * (baseAngle - Math.PI / 2);
// legs
torso.children.upperLegL.rotationTarget = 0;
torso.children.upperLegR.rotationTarget = 0;
torso.children.upperLegL.children.lowerLegL.rotationTarget = 0;
torso.children.upperLegR.children.lowerLegR.rotationTarget = 0;
}
function applyBlendedPose(entity, a, b, t) {
const torso = entity.skeleton.torso;
torso.children.upperArmL.rotationTarget =
lerp(a.upperArmL, b.upperArmL, t);
torso.children.upperArmL.children.lowerArmL.rotationTarget =
lerp(a.lowerArmL, b.lowerArmL, t);
torso.children.upperArmR.rotationTarget =
lerp(a.upperArmR, b.upperArmR, t);
torso.children.upperArmR.children.lowerArmR.rotationTarget =
lerp(a.lowerArmR, b.lowerArmR, t);
if(entity.anim.state !== "walkPunch" && entity.anim.state !== "walkHighPunch"){
torso.rotationTarget = lerp(a.torso, b.torso, t);
torso.children.head.rotationTarget = lerp(a.head, b.head, t);
torso.children.upperLegL.rotationTarget =
lerp(a.upperLegL, b.upperLegL, t);
torso.children.upperLegL.children.lowerLegL.rotationTarget =
lerp(a.lowerLegL, b.lowerLegL, t);
torso.children.upperLegR.rotationTarget =
lerp(a.upperLegR, b.upperLegR, t);
torso.children.upperLegR.children.lowerLegR.rotationTarget =
lerp(a.lowerLegR, b.lowerLegR, t);
}
}
function lerp(a, b, t) {
return a + (b - a) * t;
}
function angleDiff( b,a) {
let d = b - a;
while (d > Math.PI) d -= Math.PI * 2;
while (d < -Math.PI) d += Math.PI * 2;
return d;
}
function smoothAngle(current, target, dt, baseSpeed) {
let diff = target - current;
// wrap into (-PI, PI)
diff = (diff + Math.PI) % (Math.PI * 2) - Math.PI;
const t = Math.min(Math.abs(diff) / Math.PI, 1);
const ease = t * t; // quadratic ease-in
const speed = baseSpeed + ease * 20;
return current + diff * dt * speed;
}
function smoothBoneRotations(entity,dt) {
const speed = 20; // how fast bones rotate toward targets
dt=dt / 1000 // has to be in seconds
const torso = entity.skeleton.torso;
torso.children.head.rotation = smoothAngle(torso.children.head.rotation, torso.children.head.rotationTarget, dt, speed);
torso.children.upperArmL.rotation = smoothAngle(torso.children.upperArmL.rotation, torso.children.upperArmL.rotationTarget, dt, speed);
torso.children.upperArmL.children.lowerArmL.rotation = smoothAngle(torso.children.upperArmL.children.lowerArmL.rotation, torso.children.upperArmL.children.lowerArmL.rotationTarget, dt, speed);
torso.children.upperArmR.rotation = smoothAngle(torso.children.upperArmR.rotation, torso.children.upperArmR.rotationTarget, dt, speed);
torso.children.upperArmR.children.lowerArmR.rotation = smoothAngle(torso.children.upperArmR.children.lowerArmR.rotation, torso.children.upperArmR.children.lowerArmR.rotationTarget, dt, speed);
torso.rotation = smoothAngle(torso.rotation, torso.rotationTarget, dt, speed);
torso.children.upperLegL.rotation = smoothAngle(torso.children.upperLegL.rotation, torso.children.upperLegL.rotationTarget, dt, speed);
torso.children.upperLegL.children.lowerLegL.rotation = smoothAngle(torso.children.upperLegL.children.lowerLegL.rotation, torso.children.upperLegL.children.lowerLegL.rotationTarget, dt, speed);
torso.children.upperLegR.rotation = smoothAngle(torso.children.upperLegR.rotation, torso.children.upperLegR.rotationTarget, dt, speed);
torso.children.upperLegR.children.lowerLegR.rotation = smoothAngle(torso.children.upperLegR.children.lowerLegR.rotation, torso.children.upperLegR.children.lowerLegR.rotationTarget, dt, speed);
}
//--- Collision
// Get world entity polygon
function getBoneWorldPoly(bone) {
const cos = Math.cos(bone.worldRot);
const sin = Math.sin(bone.worldRot);
return bone.poly.map(p => ({
x: bone.worldX + p.x * cos - p.y * sin,
y: bone.worldY + p.x * sin + p.y * cos
}));
}
function getHit(a, b) {
const pen = sat(a, b);
if (!pen || pen.overlap < 0.1)return false;
return pen;
}
// Determine the type of collisions to detect (hands / feet) depending on the attack
const actions = {
punch:["punch","highPunch","walkPunch","walkHighPunch","crouchPunch","jumpPunch","jumpLowPunch","jumpHighPunch"],
kick:["kick","highKick","crouchKick","jumpKick","jumpLowKick","jumpHighKick"]
};
const actionType = {};
for (const type in actions) {
for (const name of actions[type]) {
actionType[name] = type; // "punch" or "kick"
}
}
function getActiveHitPolys(entity) {
const type = actionType[entity.anim.state];
if (!type) return null;
if (type === "punch") {
return [
getBoneWorldPoly(entity.skeleton.torso.children.upperArmL.children.lowerArmL),
getBoneWorldPoly(entity.skeleton.torso.children.upperArmR.children.lowerArmR)
];
}
if (type === "kick") {
return [
getBoneWorldPoly(entity.skeleton.torso.children.upperLegL.children.lowerLegL),
getBoneWorldPoly(entity.skeleton.torso.children.upperLegR.children.lowerLegR)
];
}
return null;
}
function getHurtPolys(entity) {
return {
head: getBoneWorldPoly(entity.skeleton.torso.children.head),
torso: getBoneWorldPoly(entity.skeleton.torso)
};
}
function resolveAttack(attacker, target) {
const hitPolys = getActiveHitPolys(attacker);
if (!hitPolys) return;
const hurt = getHurtPolys(target);
for (const poly of hitPolys) {
const hitHead = getHit(poly, hurt.head);
if (hitHead) {
if(!target.prevHit)sfxHit();
target.prevHit = true;
applyHitReaction(target, "head", hitHead);
return; // head takes priority
}
const hitTorso = getHit(poly, hurt.torso);
if (hitTorso) {
if(!target.prevHit)sfxHit();
target.prevHit = true;
applyHitReaction(target, "torso", hitTorso);
return;
}
}
}
function playerEnemyCollision() {
const p = game.player;
// If no attack is active, skip everything
if (isAttackActive(p)){
for (const e of game.enemies) {
resolveAttack(p,e);
}
}
for (const e of game.enemies) {
if (!isAttackActive(e)) continue;
resolveAttack(e,p);
}
}
//SAT collsion poly vs poly
function sat(polyA, polyB) {
if (!polyA || polyA.length < 3) return null;
if (!polyB || polyB.length < 3) return null;
let overlap = Infinity;
let smallestAxis = null;
for (const poly of [polyA, polyB]) {
for (let i = 0; i < poly.length; i++) {
const p1 = poly[i];
const p2 = poly[(i + 1) % poly.length];
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
if (dx === 0 && dy === 0) continue;
const nx = dy;
const ny = -dx;
let [minA, maxA] = project(polyA, nx, ny);
let [minB, maxB] = project(polyB, nx, ny);
if (maxA < minB || maxB < minA) return null;
const o = Math.min(maxA, maxB) - Math.max(minA, minB);
if (o < overlap) {
overlap = o;
smallestAxis = { x: nx, y: ny };
}
}
}
if (!smallestAxis) return null;
const len = Math.hypot(smallestAxis.x, smallestAxis.y);
if (len === 0) return null;
return {
overlap,
nx: smallestAxis.x / len,
ny: smallestAxis.y / len
};
}
function project(poly, nx, ny) {
let min = Infinity, max = -Infinity;
for (const p of poly) {
const dot = p.x * nx + p.y * ny;
if (dot < min) min = dot;
if (dot > max) max = dot;
}
return [min, max];
}
function isAttackActive(entity) {
const state = entity.anim.state;
const frame = entity.anim.frame;
if (!attackFrames[state]) return false;
const { start, end } = attackFrames[state];
return frame >= start && frame <= end;
}
function applyHitReaction(target, hitType, result) {
if(target.type !== "boss"){
target.stun = .2;
target.attackCooldown = 1;
}else{
target.attackCooldown = .2;
}
if (hitType === "head") {
if(target.id !== "player")game.score += Math.ceil(result.overlap * .15);
target.hitPoints -= Math.ceil(result.overlap * .0750);
target.vx = -(result.nx * .3);
target.vy = -(result.ny * .5);
target.skeleton.torso.children.head.rotation -= 0.3;
target.headRecoil = 0.15;
//hats
if(target.hasHat){
spawnHat(target)
}
}
if (hitType === "torso") {
target.vx = -(result.nx * .3);
target.vy = -(result.ny * .5);
target.skeleton.torso.rotation -= 0.3;
target.skeleton.torso.children.upperLegL.rotation += 0.3;
target.skeleton.torso.children.upperLegR.rotation += 0.3;
if(target.id !== "player")game.score += Math.ceil(result.overlap * .1);
target.hitPoints -= Math.ceil(result.overlap * .05);
}
if(target.id !== "player")game.score += 10;
}
function updateRecoil(entity, dt) {
if (entity.headRecoil > 0) {
entity.headRecoil -= dt;
// Smoothly return to neutral rotation
const head = entity.skeleton.torso.children.head;
head.rotation = head.rotation * 0.1; // dampen toward 0
}
}
*/ |
| SC Arguments: [Name:SC_ACTION Type:uint64 Value:'1' Name:SC_CODE Type:string Value:'// Copyright 2024. Civilware. All rights reserved.
// TELA Decentralized Web Document (TELA-DOC-1)
Function InitializePrivate() Uint64
10 IF init() == 0 THEN GOTO 30
20 RETURN 1
30 STORE("nameHdr", "update.js")
31 STORE("descrHdr", "Update file")
32 STORE("iconURLHdr", "")
33 STORE("dURL", "update.js")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "")
36 STORE("fileCheckC", "3131974678dd600eea7d67914582bad7124ed77d89d9da43117e9884406d9b")
37 STORE("fileCheckS", "7b84519ecce10a4ccf545dcdc3d86b6ef09bc7c6f321d18598ff265677a44b2")
100 RETURN 0
End Function
Function init() Uint64
10 IF EXISTS("owner") == 0 THEN GOTO 30
20 RETURN 1
30 STORE("owner", address())
50 STORE("docVersion", "1.0.0")
60 STORE("hash", HEX(TXID()))
70 STORE("likes", 0)
80 STORE("dislikes", 0)
100 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
Function Rate(r Uint64) Uint64
10 DIM addr as String
15 LET addr = address()
16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
20 RETURN 1
30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
40 IF r < 50 THEN GOTO 70
50 STORE("likes", LOAD("likes")+1)
60 RETURN 0
70 STORE("dislikes", LOAD("dislikes")+1)
100 RETURN 0
End Function
/*window.addEventListener("load", resizeSVG);
window.addEventListener("resize", resizeSVG);
function resizeSVG() {
sw = window.innerWidth;
sh = window.innerHeight;
updateViewBox();
}
function updateViewBox() {
if (keys["="]) zoom *= 0.98;
if (keys["-"]) zoom *= 1.02;
const viewW = sw * zoom;
const viewH = sh * zoom;
const camX = camCenter.x - viewW / 2;
const camY = camCenter.y - viewH / 2;
camLeft = camCenter.x - (sw * zoom) / 2;
camTop = camCenter.y - (sh * zoom) / 2;
camRight = camCenter.x + (sw * zoom) / 2;
camBottom = camCenter.y + (sh * zoom) / 2;
svg.setAttribute("viewBox", `${camX} ${camY} ${viewW} ${viewH}`);
}
function updateCamera(dt) {
// camera follows player
camCenter.x = game.player.x;
camCenter.y = game.player.y;
}
//----------
// Get Input
window.addEventListener("wheel", e => {
if (e.deltaY < 0) zoom *= 0.9; // zoom in
else zoom *= 1.1; // zoom out
zoom = Math.max(0.2, Math.min(zoom, 5)); // clamp
});
window.addEventListener("keydown", e => {
keys[e.key] = true;
});
window.addEventListener("keyup", e => {
keys[e.key] = false;
});
let prevStartPressed = false;
function gatherInput(dt) {
const p = game.player;
// --- KEYBOARD ---
let move = (keys["a"] ? -1 : 0) + (keys["d"] ? 1 : 0);
let vert = (keys["w"] ? 1 : 0) + (keys["s"] ? -1 : 0);
let wantPunch = keys["l"];
let wantKick = keys[";"];
let wantJump = keys[" "];
let startPressed = keys["Escape"]
// --- GAMEPAD ---
const gp = navigator.getGamepads()[0];
if (gp) {
// D-pad
if (gp.buttons[14].pressed) move = -1; // left
if (gp.buttons[15].pressed) move = 1; // right
if (gp.buttons[12].pressed) vert = 1; // up
if (gp.buttons[13].pressed) vert = -1; // down
// Face buttons
if (gp.buttons[0].pressed) wantJump = true; // A = jump
if (gp.buttons[2].pressed) wantPunch = true; // X = punch
if (gp.buttons[1].pressed) wantKick = true; // B = kick
startPressed = gp.buttons[9].pressed || startPressed;
}
// --- MENU ---
menu(startPressed,move);
prevStartPressed = startPressed;
// --- APPLY TO PLAYER ---
p.moveInput = move;
p.verticalInput = vert;
p.wantPunch = wantPunch;
p.wantKick = wantKick;
p.wantJump = wantJump;
if (p.wantJump) jump();
}
function jump(p = game.player) {
if (p.onGround && p.anim.state !=="kick") {
p.vy = -game.jumpStrength;
p.onGround = false;
p.inAir = true;
}
}
function applyIntent(entity,dt) {
const p = entity;
if (p.stun > 0) {
p.stun -= dt/1000;
return;
}
if ((!p.inAir && p.wantKick)||
(!p.inAir && entity.verticalInput < 0)){
if (p.moveInput !== 0) {
if(p.moveInput > 0 ){
p.facing = 1;
}else{
p.facing = -1;
}
p.moveInput = 0;
}
}
p.vx = p.moveInput * p.speed;
if (p.moveInput !== 0) {
if(p.moveInput > 0 ){
p.facing = 1;
}else{
p.facing = -1;
}
}
}
function updateMovementState(entity) {
if(entity.moveState === "dying")return;
if (entity.inAir && entity.vy < -0.2) entity.moveState = "jump";
else if (entity.inAir && entity.vy > 0.2) entity.moveState = "fall";
else if (entity.moveInput !== 0) entity.moveState = "walk";
else if (entity.verticalInput < 0 && !entity.inAir) entity.moveState = "crouch";
else entity.moveState = "idle";
if (entity.moveState === "crouch") {
entity.height = 60;
entity.skeletonOffsetY = 12
} else {
entity.height = 100;
entity.skeletonOffsetY = 0;
}
}
function updateActionState(entity) {
entity.actionState = "none";
if (entity === game.player) {
if (!entity.inAir) {
if (entity.verticalInput < 0) {
if (entity.wantPunch) return entity.actionState = "crouchPunch";
if (entity.wantKick) return entity.actionState = "crouchKick";
return entity.actionState = "crouch";
}else if(entity.verticalInput > 0){
if (entity.wantPunch) return entity.actionState = (entity.moveState === "walk") ? "walkHighPunch" : "highPunch";
if (entity.wantKick) return entity.actionState = "highKick";
}else{
if (entity.wantPunch)return entity.actionState = (entity.moveState === "walk") ? "walkPunch" : "punch";
if (entity.wantKick)return entity.actionState = "kick";
}
} else {
if (entity.verticalInput < 0) {
if (entity.wantPunch) return entity.actionState = "jumpLowPunch";
if (entity.wantKick) return entity.actionState = "jumpLowKick";
}else if(entity.verticalInput > 0){
if (entity.wantPunch) return entity.actionState = "jumpHighPunch";
if (entity.wantKick) return entity.actionState = "jumpHighKick";
}else{
if (entity.wantPunch) return entity.actionState = "jumpPunch";
if (entity.wantKick) return entity.actionState = "jumpKick";
}
}
}else if(entity.type !== "boss" ){
if (entity.verticalInput == 1) {
if (entity.wantPunch) {entity.actionState = "highPunch"}
if (entity.wantKick) {entity.actionState = "highKick"}
}else{
if (entity.wantPunch) {entity.actionState = "punch"}
if (entity.wantKick) {entity.actionState = "kick"}
}
}else{
//boss
if (entity.verticalInput == 1) {
if (entity.wantPunch) {entity.actionState = (entity.moveState === "walk") ? "walkPunch" : "punch";}
if (entity.wantKick) {entity.actionState = "highKick"}
}else{
if (entity.wantPunch) {entity.actionState = (entity.moveState === "walk") ? "walkHighPunch" : "highPunch";}
if (entity.wantKick) {entity.actionState = "kick"}
}
}
}
function selectAnimationState(entity) {
let finalState = entity.moveState;
if (entity.actionState !== "none") {
finalState = entity.actionState;
}
if (finalState !== entity.anim.state) {
playAnimation(entity, finalState);
}
}
function playAnimation(entity, name) {
const anim = entity.anim;
if (anim.state === name) return;
anim.state = name;
anim.nextPose = poses[name];
}
function updateAnimation(entity, dt) {
const anim = entity.anim;
anim.frame++
// If blending to a new pose
if (anim.nextPose) {
anim.currentPose = anim.nextPose;
anim.nextPose = null;
anim.frame = 0;
}
// Walk cycle override
if (anim.state === "walk" || anim.state === "walkPunch" || anim.state === "walkHighPunch") {
applyWalk(entity, dt);
if (anim.state === "walkPunch") {
applyBlendedPose(entity, poses.walkPunch, poses.walkPunch, 1);
}else if (anim.state === "walkHighPunch") {
applyBlendedPose(entity, poses.walkHighPunch, poses.walkHighPunch, 1);
}
} else if(anim.state !== "dying"){
// Idle, punch, kick, jump, fall, etc.
applyBlendedPose(entity, anim.currentPose, anim.currentPose, 1);
}else{
applyDeath(entity, dt);
}
}
function applyWalk(entity,dt){
const t = performance.now() * .01; // walk speed multiplier
const torso = entity.skeleton.torso;
torso.rotationTarget = Math.sin(t * 0.5) * 0.05;
// head
torso.children.head.rotationTarget = 0;
// legs
torso.children.upperLegL.rotationTarget = Math.sin(t) * 0.6;
torso.children.upperLegR.rotationTarget = -Math.sin(t) * 0.6;
torso.children.upperLegL.children.lowerLegL.rotationTarget = Math.max(-Math.sin(t + 0.5) * 0.4, -0.1);
torso.children.upperLegR.children.lowerLegR.rotationTarget = Math.max(Math.sin(t + 0.5) * 0.4, -0.1);
// arms
torso.children.upperArmL.rotationTarget = -Math.sin(t) * 0.4;
torso.children.upperArmR.rotationTarget = Math.sin(t) * 0.4;
torso.children.upperArmL.children.lowerArmL.rotationTarget = ( Math.sin(t + 0.3) * 0.1) -1;
torso.children.upperArmR.children.lowerArmR.rotationTarget = ( Math.sin(t + 0.3) * 0.1) -.5;
}
function applyDeath(entity,dt){
const t = performance.now() * .01; // walk speed multiplier
const torso = entity.skeleton.torso;
const { segment: seg, platform: enemyPlatform } = getPlatformUnder(entity);
const baseAngle = seg ? getSegmentAngle(seg) : 0; // fallback to flat
torso.rotationTarget = entity.facing * (baseAngle - Math.PI / 2);
// legs
torso.children.upperLegL.rotationTarget = 0;
torso.children.upperLegR.rotationTarget = 0;
torso.children.upperLegL.children.lowerLegL.rotationTarget = 0;
torso.children.upperLegR.children.lowerLegR.rotationTarget = 0;
}
function applyBlendedPose(entity, a, b, t) {
const torso = entity.skeleton.torso;
torso.children.upperArmL.rotationTarget =
lerp(a.upperArmL, b.upperArmL, t);
torso.children.upperArmL.children.lowerArmL.rotationTarget =
lerp(a.lowerArmL, b.lowerArmL, t);
torso.children.upperArmR.rotationTarget =
lerp(a.upperArmR, b.upperArmR, t);
torso.children.upperArmR.children.lowerArmR.rotationTarget =
lerp(a.lowerArmR, b.lowerArmR, t);
if(entity.anim.state !== "walkPunch" && entity.anim.state !== "walkHighPunch"){
torso.rotationTarget = lerp(a.torso, b.torso, t);
torso.children.head.rotationTarget = lerp(a.head, b.head, t);
torso.children.upperLegL.rotationTarget =
lerp(a.upperLegL, b.upperLegL, t);
torso.children.upperLegL.children.lowerLegL.rotationTarget =
lerp(a.lowerLegL, b.lowerLegL, t);
torso.children.upperLegR.rotationTarget =
lerp(a.upperLegR, b.upperLegR, t);
torso.children.upperLegR.children.lowerLegR.rotationTarget =
lerp(a.lowerLegR, b.lowerLegR, t);
}
}
function lerp(a, b, t) {
return a + (b - a) * t;
}
function angleDiff( b,a) {
let d = b - a;
while (d > Math.PI) d -= Math.PI * 2;
while (d < -Math.PI) d += Math.PI * 2;
return d;
}
function smoothAngle(current, target, dt, baseSpeed) {
let diff = target - current;
// wrap into (-PI, PI)
diff = (diff + Math.PI) % (Math.PI * 2) - Math.PI;
const t = Math.min(Math.abs(diff) / Math.PI, 1);
const ease = t * t; // quadratic ease-in
const speed = baseSpeed + ease * 20;
return current + diff * dt * speed;
}
function smoothBoneRotations(entity,dt) {
const speed = 20; // how fast bones rotate toward targets
dt=dt / 1000 // has to be in seconds
const torso = entity.skeleton.torso;
torso.children.head.rotation = smoothAngle(torso.children.head.rotation, torso.children.head.rotationTarget, dt, speed);
torso.children.upperArmL.rotation = smoothAngle(torso.children.upperArmL.rotation, torso.children.upperArmL.rotationTarget, dt, speed);
torso.children.upperArmL.children.lowerArmL.rotation = smoothAngle(torso.children.upperArmL.children.lowerArmL.rotation, torso.children.upperArmL.children.lowerArmL.rotationTarget, dt, speed);
torso.children.upperArmR.rotation = smoothAngle(torso.children.upperArmR.rotation, torso.children.upperArmR.rotationTarget, dt, speed);
torso.children.upperArmR.children.lowerArmR.rotation = smoothAngle(torso.children.upperArmR.children.lowerArmR.rotation, torso.children.upperArmR.children.lowerArmR.rotationTarget, dt, speed);
torso.rotation = smoothAngle(torso.rotation, torso.rotationTarget, dt, speed);
torso.children.upperLegL.rotation = smoothAngle(torso.children.upperLegL.rotation, torso.children.upperLegL.rotationTarget, dt, speed);
torso.children.upperLegL.children.lowerLegL.rotation = smoothAngle(torso.children.upperLegL.children.lowerLegL.rotation, torso.children.upperLegL.children.lowerLegL.rotationTarget, dt, speed);
torso.children.upperLegR.rotation = smoothAngle(torso.children.upperLegR.rotation, torso.children.upperLegR.rotationTarget, dt, speed);
torso.children.upperLegR.children.lowerLegR.rotation = smoothAngle(torso.children.upperLegR.children.lowerLegR.rotation, torso.children.upperLegR.children.lowerLegR.rotationTarget, dt, speed);
}
//--- Collision
// Get world entity polygon
function getBoneWorldPoly(bone) {
const cos = Math.cos(bone.worldRot);
const sin = Math.sin(bone.worldRot);
return bone.poly.map(p => ({
x: bone.worldX + p.x * cos - p.y * sin,
y: bone.worldY + p.x * sin + p.y * cos
}));
}
function getHit(a, b) {
const pen = sat(a, b);
if (!pen || pen.overlap < 0.1)return false;
return pen;
}
// Determine the type of collisions to detect (hands / feet) depending on the attack
const actions = {
punch:["punch","highPunch","walkPunch","walkHighPunch","crouchPunch","jumpPunch","jumpLowPunch","jumpHighPunch"],
kick:["kick","highKick","crouchKick","jumpKick","jumpLowKick","jumpHighKick"]
};
const actionType = {};
for (const type in actions) {
for (const name of actions[type]) {
actionType[name] = type; // "punch" or "kick"
}
}
function getActiveHitPolys(entity) {
const type = actionType[entity.anim.state];
if (!type) return null;
if (type === "punch") {
return [
getBoneWorldPoly(entity.skeleton.torso.children.upperArmL.children.lowerArmL),
getBoneWorldPoly(entity.skeleton.torso.children.upperArmR.children.lowerArmR)
];
}
if (type === "kick") {
return [
getBoneWorldPoly(entity.skeleton.torso.children.upperLegL.children.lowerLegL),
getBoneWorldPoly(entity.skeleton.torso.children.upperLegR.children.lowerLegR)
];
}
return null;
}
function getHurtPolys(entity) {
return {
head: getBoneWorldPoly(entity.skeleton.torso.children.head),
torso: getBoneWorldPoly(entity.skeleton.torso)
};
}
function resolveAttack(attacker, target) {
const hitPolys = getActiveHitPolys(attacker);
if (!hitPolys) return;
const hurt = getHurtPolys(target);
for (const poly of hitPolys) {
const hitHead = getHit(poly, hurt.head);
if (hitHead) {
if(!target.prevHit)sfxHit();
target.prevHit = true;
applyHitReaction(target, "head", hitHead);
return; // head takes priority
}
const hitTorso = getHit(poly, hurt.torso);
if (hitTorso) {
if(!target.prevHit)sfxHit();
target.prevHit = true;
applyHitReaction(target, "torso", hitTorso);
return;
}
}
}
function playerEnemyCollision() {
const p = game.player;
// If no attack is active, skip everything
if (isAttackActive(p)){
for (const e of game.enemies) {
resolveAttack(p,e);
}
}
for (const e of game.enemies) {
if (!isAttackActive(e)) continue;
resolveAttack(e,p);
}
}
//SAT collsion poly vs poly
function sat(polyA, polyB) {
if (!polyA || polyA.length < 3) return null;
if (!polyB || polyB.length < 3) return null;
let overlap = Infinity;
let smallestAxis = null;
for (const poly of [polyA, polyB]) {
for (let i = 0; i < poly.length; i++) {
const p1 = poly[i];
const p2 = poly[(i + 1) % poly.length];
const dx = p2.x - p1.x;
const dy = p2.y - p1.y;
if (dx === 0 && dy === 0) continue;
const nx = dy;
const ny = -dx;
let [minA, maxA] = project(polyA, nx, ny);
let [minB, maxB] = project(polyB, nx, ny);
if (maxA < minB || maxB < minA) return null;
const o = Math.min(maxA, maxB) - Math.max(minA, minB);
if (o < overlap) {
overlap = o;
smallestAxis = { x: nx, y: ny };
}
}
}
if (!smallestAxis) return null;
const len = Math.hypot(smallestAxis.x, smallestAxis.y);
if (len === 0) return null;
return {
overlap,
nx: smallestAxis.x / len,
ny: smallestAxis.y / len
};
}
function project(poly, nx, ny) {
let min = Infinity, max = -Infinity;
for (const p of poly) {
const dot = p.x * nx + p.y * ny;
if (dot < min) min = dot;
if (dot > max) max = dot;
}
return [min, max];
}
function isAttackActive(entity) {
const state = entity.anim.state;
const frame = entity.anim.frame;
if (!attackFrames[state]) return false;
const { start, end } = attackFrames[state];
return frame >= start && frame <= end;
}
function applyHitReaction(target, hitType, result) {
if(target.type !== "boss"){
target.stun = .2;
target.attackCooldown = 1;
}else{
target.attackCooldown = .2;
}
if (hitType === "head") {
if(target.id !== "player")game.score += Math.ceil(result.overlap * .15);
target.hitPoints -= Math.ceil(result.overlap * .0750);
target.vx = -(result.nx * .3);
target.vy = -(result.ny * .5);
target.skeleton.torso.children.head.rotation -= 0.3;
target.headRecoil = 0.15;
//hats
if(target.hasHat){
spawnHat(target)
}
}
if (hitType === "torso") {
target.vx = -(result.nx * .3);
target.vy = -(result.ny * .5);
target.skeleton.torso.rotation -= 0.3;
target.skeleton.torso.children.upperLegL.rotation += 0.3;
target.skeleton.torso.children.upperLegR.rotation += 0.3;
if(target.id !== "player")game.score += Math.ceil(result.overlap * .1);
target.hitPoints -= Math.ceil(result.overlap * .05);
}
if(target.id !== "player")game.score += 10;
}
function updateRecoil(entity, dt) {
if (entity.headRecoil > 0) {
entity.headRecoil -= dt;
// Smoothly return to neutral rotation
const head = entity.skeleton.torso.children.head;
head.rotation = head.rotation * 0.1; // dampen toward 0
}
}
*/'] |