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", "ai.js")
31 STORE("descrHdr", "AI file")
32 STORE("iconURLHdr", "")
33 STORE("dURL", "ai.js")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "")
36 STORE("fileCheckC", "33d63e3d835c8b729446155d1f7695833cf6b74ef433793e3334dde944f0852")
37 STORE("fileCheckS", "dfa45f2c8426c4315761590fecd51028f81a80da3e24151963051283763e202")
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
/*//----------------
// Update Entities
const enemySight = 1000;
function updateEnemyAI(enemy) {
if (enemy.state === "dying" || (enemy.state === "dead")) return;
if (enemy.stun > 0) {
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy);
return;
}
const dx = game.player.x - enemy.x;
const dy = game.player.y - enemy.y;
const dist = Math.hypot(dx, dy);
if(dist > enemySight){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy)
return;
}
if (enemy.attackCooldown > 0) enemy.attackCooldown -= dt/1000;
if (enemy.jumpCooldown > 0) enemy.jumpCooldown -= dt/1000;
if (enemy.faceLockTimer > 0) enemy.faceLockTimer -= dt/1000;
if (enemy.faceLockTimer <= 0) {
enemy.facing = (dx < 0 ? -1 : 1);
}
const { segment: playerSegment, platform: playerPlatform } = getPlatformUnder(game.player);
const { segment: enemySegment, platform: enemyPlatform } = getPlatformUnder(enemy);
const playerAbove = game.player.y < enemy.y - 40;
let wallSeg = null;
// --- PLATFORM NAVIGATION ---
// --- DROP DOWN LOGIC ---
if (enemyPlatform){
if(enemyPlatform.id != enemy.onPlatform){
enemy.blockedRight = false
enemy.blockedLeft = false
enemy.blockedP = null
enemy.onPlatform = enemyPlatform.id;
}
wallSeg = detectWallAhead(enemy)
// Reset all platform enemy lists
for (const plat of game.platforms) {
plat.enemies.length = 0;
// Assign enemies to their platform lists
for (const e of game.enemies) {
if (e.onPlatform === plat.id) {
plat.enemies.push(e);
}
}
const range = 60; // or whatever feels right
for (const other of plat.enemies) {
if (other === enemy | !enemy.onGround | !other.onGround | other.onPlatform !== enemy.onPlatform | other.state === "dead" | other.state === "dying") continue;
const dx = Math.abs(other.x - enemy.x);
if (dx <= range) {
if (enemy.blockedLeft){ other.blockedLeft = true; other.blockedRight = false; enemy.blockedP = plat.id;}
if (enemy.blockedRight){ other.blockedRight = true; other.blockedLeft = false; enemy.blockedP = plat.id;}
}
}
}
const playerBelowPlatform = isPlayerBelowPlatform(enemyPlatform)
if(enemy.type !== "grunt" && isNearEdgeFacing(enemy, enemyPlatform, 10)){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy);
return;
}
// if player is below, move off closest edge
if (playerBelowPlatform) {
const leftEdge = enemyPlatform.minX;
const rightEdge = enemyPlatform.maxX;
const distToLeft = Math.abs(game.player.x - leftEdge);
const distToRight = Math.abs(game.player.x - rightEdge);
const targetEdge = (distToLeft < distToRight) ? leftEdge : rightEdge;
const move = Math.sign(targetEdge - enemy.x);
if(enemy.type !== "grunt" ){
if(move !== Math.sign(game.player.x - enemy.x)|| !isNearEdgeFacing(enemy, enemyPlatform, 10) ){//&& Math.abs(dx)>4)check distance here(for slopes)
pursue(enemy, dx, enemySegment, playerPlatform, wallSeg);
}else{
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
}
resetAttacks(enemy);
return;
}else{
// Move toward that edge
enemy.moveInput = move;
enemy.facing = enemy.moveInput;
if(directionBlocked(enemy)){
enemy.facing = -enemy.moveInput;
enemy.moveInput = enemy.facing;
}
// BOOST: ensure they actually fall off
if (isNearPlatformEdge(enemy, enemyPlatform, 10) && isSafeToDrop(enemy)) {
enemy.faceLockTimer = .75;
enemy.vx = enemy.facing * enemy.speed * 1.2; // small push
enemy.vy = -0.4; // tiny hop upward to break platform glue
enemy.inAir = true;
enemy.onGround = false;
}
}
return;
}
if (playerAbove) {
// don't get led off the edge
if(playerSegment){
// if near edge, check if able to jump up, if not, stop
if (isNearEdgeFacing(enemy, enemyPlatform, 12)) {
if (tryJumpAbove(enemy,enemySegment))return;
if(!isSafeToDrop(enemy)){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
return;
}
}
}else{
//no player platform / segment
if (isNearEdgeFacing(enemy, enemyPlatform, 12)
) {
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
return;
}
pursue(enemy, dx, enemySegment, playerPlatform);
return
}
}
// Try to get to player platform
if (playerSegment && enemySegment !== playerSegment ) {
let target;
if(enemyPlatform != playerPlatform){
if(playerAbove){
const closestSegments = findClosestSegmentsBetween(enemyPlatform, playerPlatform);
const closestSegmentsMidPoint = findClosestSegmentsBetweenMidPoints(enemyPlatform, playerPlatform);
const segmentOverPlatform = getSegmentOverPlatform(enemyPlatform);
const segmentOverCurrentSegment = getSegmentOverCurrent(enemySegment)
const bestJumpSegment = findBestJumpSegment(enemy, enemyPlatform, playerPlatform);
target = closestSegments.segA;
if(target !== enemySegment){
if(closestSegments.segA !== closestSegmentsMidPoint.segA ){
if(segmentOverCurrentSegment){
target = segmentOverCurrentSegment;
}else if(segmentOverPlatform){
target = segmentOverPlatform;
}
}
if(playerAbove && target){
if (tryTarget(enemy,enemySegment,target))return;
}
moveTowardSegment(enemy, enemyPlatform, target, wallSeg)
avoidWallsOnSamePlatform(enemy, wallSeg)
return;
}
target = bestJumpSegment;
if(!target && playerAbove){
target = segmentOverCurrentSegment;
}
if(!target && playerAbove){
target = segmentOverPlatform;
}
if(!target && closestSegments.segA === enemySegment){
target = closestSegments.segB;
}
if(!isSegmentBelowPlatform(target, playerPlatform)){
target = playerSegment;
}
if(target ){
if (tryJumpAbove(enemy,enemySegment))return;
if( typeof segmentOverPlatform !== "undefined" && segmentOverPlatform !==null){
if(segmentOverPlatform.pid === playerPlatform.id){
if(jumpToSegment(enemy,enemyPlatform,segmentOverPlatform,wallSeg)){
avoidWallsOnSamePlatform(enemy,enemyPlatform,wallSeg)
return;
}
}
}
if(!moveTowardSegment(enemy, enemyPlatform, target, wallSeg)&&segmentOverPlatform){
moveTowardSegment(enemy, enemyPlatform, segmentOverPlatform, wallSeg)
avoidWallsOnSamePlatform(enemy,enemyPlatform,wallSeg)
return
}
if (tryTarget(enemy,enemySegment,target))return;
}
}
}
if (canJumpToSegment(enemy, enemySegment, playerSegment)&& !isSegmentBelowSegment(playerSegment, enemySegment)){//
if(platformJump(enemy,enemyPlatform)) return;
}
pursue(enemy, dx, enemySegment, playerPlatform, wallSeg);
return;
}
}
if(!enemy.inAir){enemy.faceLockTimer = 0;}//don't walk off edge if just landed on player platform
//--- SAME PLATFORM: NORMAL PURSUIT + ATTACK ---
if (dist < 50) {
// attack logic...
let delay = 0.3 + Math.random() * 0.7; // 0.3-1.0 sec
if(enemy.type === "boss") delay = 0.5;
// Only punch if cooldown is done
if (enemy.attackCooldown <= 0) {
enemy.verticalInput = 0;
enemy.attackCooldown = delay;
if (Math.random() < 0.5) {
enemy.wantPunch = true;
}else{
enemy.wantKick = true;
}
if(!game.player.inAir && game.player.verticalInput < 0){
enemy.verticalInput = 0;
}else if (Math.random() < 0.5){
enemy.verticalInput = 0;
}else{
enemy.verticalInput = 1;
}
}
if(enemy.anim.state === "walk" && enemy.type !== "boss"){
resetAttacks(enemy)
}
if (enemy.anim.state === "punch" && enemy.anim.frame > attackFrames.punch.end ) {
enemy.wantPunch = false;
}
if (enemy.anim.state === "highPunch" && enemy.anim.frame > attackFrames.highPunch.end ) {
enemy.wantPunch = false;
}
if (enemy.anim.state === "kick" && enemy.anim.frame > attackFrames.kick.end ) {
enemy.wantKick = false;
}
if (enemy.anim.state === "highKick" && enemy.anim.frame > attackFrames.highKick.end ) {
enemy.wantKick = false;
}
if(enemy.type !== "boss"){
enemy.moveInput = 0;
}else{
//boss
if (enemy.anim.state === "walkPunch" && enemy.anim.frame > attackFrames.walkPunch.end ) {
enemy.wantPunch = false;
}
if (enemy.anim.state === "walkHighPunch" && enemy.anim.frame > attackFrames.walkHighPunch.end ) {
enemy.wantPunch = false;
}
if(enemy.type === "boss" && !enemy.wantPunch ) {
enemy.moveInput = 0;
if(enemy.anim.state === "walk"){
resetAttacks(enemy)
}
}
}
// for audio
const attackPressed = (enemy.wantPunch || enemy.wantKick) && !enemy.prevAttack;
enemy.prevAttack = enemy.wantPunch || enemy.wantKick;
if (attackPressed) {
sfxHuh();
game.player.prevHit =false;
}
} else {
pursue(enemy, dx, enemySegment, playerPlatform, wallSeg);
}
}
function isSegmentBelowPlatform(seg, plat){
for (const pseg of plat.segments) {
// Optional: only treat floor-type segments as platform surface
if (pseg.isWall)continue;
if(isSegmentBelowSegment(seg,pseg)) return true;
}
return false;
}
function pursue(enemy,dx, enemySegment, playerPlatform,wallSeg){
let wallJumped = false;
if(wallSeg){
if(tryJumpWall(enemy,wallSeg)){
resetAttacks(enemy);
return
}
}
if (keepEnemySpacing(enemy)) {
enemy.anim.state = "none";
enemy.moveState = "idle";
enemy.moveInput = 0;
return;
}
if (enemy.type !== "grunt" && enemySegment){
const plat = getPlatformById(enemy.onPlatform)
if(typeof plat !== "undefined"){
if(isNearEdgeFacing(enemy,plat , 10)){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy);
return
}
}
}
if(playerPlatform && enemySegment){
if (playerPlatform.id === enemy.onPlatform){
const behind = countEnemiesBehind(enemy);
if (behind >= 1 && countEnemiesAhead(enemy) < behind && Math.abs(dx) >= 50 && Math.abs(dx) < 100) {//&& Math.abs(dx) >= 65
if (enemy.onGround && enemy.jumpCooldown <= 0) {
enemy.moveInput = enemy.facing;
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
return;
}
}
if(
playerIsThreatening(game.player) && enemy.onGround &&
(enemy.facing === 1 && game.player.facing === -1 ||
enemy.facing === -1 && game.player.facing === 1)
) {
const safeDist = 70;
if (Math.abs(dx) < safeDist) {
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
return;
}
}
}
}
// normal movement
if (Math.abs(dx) > 3 ) {
if(enemySegment && Math.abs(enemySegment.angle) > maxClimbAngle){
enemy.moveInput = enemy.facing;
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
}else{
enemy.moveInput = enemy.facing;
enemy.anim.state = "walk";
}
} else if(playerPlatform && enemySegment){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
}
avoidWallsOnSamePlatform(enemy, wallSeg)
resetAttacks(enemy)
}
function moveTowardSegment(enemy,enemyPlatform,target, wallSeg){
// walk toward it
let left = Math.min(target.x1, target.x2);
let right = Math.max(target.x1, target.x2);
let ytarget = Math.min(target.y1, target.y2);
const enemyPlatLeft = Math.min(enemyPlatform.x1, enemyPlatform.x2);
const enemyPlatRight = Math.max(enemyPlatform.x1, enemyPlatform.x2);
//target the lower edge if there is one
if(target.y1 > target.y2){
left = target.x1;
right = target.x1 + 8;
ytarget = target.y1;
}else if(target.y1 < target.y2){
left = target.x2 - 8;
right = target.x2;
ytarget = target.y2;
}
if (enemy.x < left -8 ){
enemy.moveInput = 1;
enemy.anim.state = "walk";
enemy.facing = enemy.moveInput;
if(tryJumpWall(enemy,wallSeg))return true;
}
else if (enemy.x > right +8) {
enemy.moveInput = -1;
enemy.anim.state = "walk";
enemy.facing = enemy.moveInput;
if(tryJumpWall(enemy,wallSeg))return true;
}
else if(enemy.y - ytarget < 128 && !enemy.inAir && (enemy.onPlatform !== target.pid)){
// directly under it or close to it, jump next chance
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
}else{
enemy.moveInput = enemy.facing
if(directionBlocked(enemy)){
enemy.facing = -enemy.moveInput;
enemy.moveInput = enemy.facing;
}
resetAttacks(enemy)
return false
}
resetAttacks(enemy)
return true
}
function jumpToSegment(enemy,enemyPlatform,target,wallSeg){
let left = Math.min(target.x1, target.x2);
let right = Math.max(target.x1, target.x2);
//target the lower edge if there is one
if(target.y1 > target.y2){
left = target.x1;
right = target.x1 + 8;
}else if(target.y1 < target.y2){
left = target.x2 - 8;
right = target.x2;
}
if(enemy.y - Math.min(target.y1, target.y2) < 128
&& isUnderOrFacingSegment(enemy, target)){
// directly under it, jump next chance
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
return true
}
return false
}
function platformJump(enemy,enemyPlatform){
if(isNearEdgeFacing(enemy, enemyPlatform, 20) &&
enemy.jumpCooldown <= 0 &&
!enemy.inAir && !directionBlocked(enemy)) {
if(enemy.vx !=0){
enemy.moveInput = enemy.facing;
}
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
return true; // commit jump
}
return false;
}
function tryTarget(enemy,enemySegment,target){
if(enemy.y - Math.min(target.y1, target.y2) > 128)return false;
const left = Math.min(target.x1, target.x2);
const right = Math.max(target.x1, target.x2);
let distToTarget = 25;
if (enemy.x < left) distToTarget = (enemy.x + 25) - left;
if (enemy.x > right) distToTarget = (right + 25) - enemy.x;
distToTarget = Math.max(distToTarget, 25)
if ((enemy.facing === 1 && enemy.moveInput ===1 && enemy.x < left - 5 && enemy.x > left - distToTarget )|| // might try relative to right minus left
(enemy.facing === -1 && enemy.moveInput === -1 && enemy.x > right + 5 && enemy.x < right + distToTarget )){ //getting near, jump now
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
return true;
}
return false;
}
function tryJumpAbove(enemy,currentSeg){
const target = getSegmentOverCurrent(currentSeg);//get lowest platform over current platform
if(target){
const left = Math.min(target.x1, target.x2);
const right = Math.max(target.x1, target.x2);
if(!canJumpToSegment(enemy,currentSeg,target)|enemy.y - Math.min(target.y1, target.y2) > 128){
return false
}
if(enemy.x > left && enemy.x < right){
// directly under it, jump next chance
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
return true;
}
}
return false;
}
function tryJumpWall(enemy,wallSeg){
if(wallSeg){
if((enemy.facing === 1 && enemy.blockedRight) || (enemy.facing === -1 && enemy.blockedLeft)){
if (aiCanJumpOver(enemy, wallSeg)){
enemy.moveInput = enemy.facing;
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
resetAttacks(enemy)
return true
}
}
}
return false;
}
*/ |
| 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", "ai.js")
31 STORE("descrHdr", "AI file")
32 STORE("iconURLHdr", "")
33 STORE("dURL", "ai.js")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "")
36 STORE("fileCheckC", "33d63e3d835c8b729446155d1f7695833cf6b74ef433793e3334dde944f0852")
37 STORE("fileCheckS", "dfa45f2c8426c4315761590fecd51028f81a80da3e24151963051283763e202")
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
/*//----------------
// Update Entities
const enemySight = 1000;
function updateEnemyAI(enemy) {
if (enemy.state === "dying" || (enemy.state === "dead")) return;
if (enemy.stun > 0) {
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy);
return;
}
const dx = game.player.x - enemy.x;
const dy = game.player.y - enemy.y;
const dist = Math.hypot(dx, dy);
if(dist > enemySight){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy)
return;
}
if (enemy.attackCooldown > 0) enemy.attackCooldown -= dt/1000;
if (enemy.jumpCooldown > 0) enemy.jumpCooldown -= dt/1000;
if (enemy.faceLockTimer > 0) enemy.faceLockTimer -= dt/1000;
if (enemy.faceLockTimer <= 0) {
enemy.facing = (dx < 0 ? -1 : 1);
}
const { segment: playerSegment, platform: playerPlatform } = getPlatformUnder(game.player);
const { segment: enemySegment, platform: enemyPlatform } = getPlatformUnder(enemy);
const playerAbove = game.player.y < enemy.y - 40;
let wallSeg = null;
// --- PLATFORM NAVIGATION ---
// --- DROP DOWN LOGIC ---
if (enemyPlatform){
if(enemyPlatform.id != enemy.onPlatform){
enemy.blockedRight = false
enemy.blockedLeft = false
enemy.blockedP = null
enemy.onPlatform = enemyPlatform.id;
}
wallSeg = detectWallAhead(enemy)
// Reset all platform enemy lists
for (const plat of game.platforms) {
plat.enemies.length = 0;
// Assign enemies to their platform lists
for (const e of game.enemies) {
if (e.onPlatform === plat.id) {
plat.enemies.push(e);
}
}
const range = 60; // or whatever feels right
for (const other of plat.enemies) {
if (other === enemy | !enemy.onGround | !other.onGround | other.onPlatform !== enemy.onPlatform | other.state === "dead" | other.state === "dying") continue;
const dx = Math.abs(other.x - enemy.x);
if (dx <= range) {
if (enemy.blockedLeft){ other.blockedLeft = true; other.blockedRight = false; enemy.blockedP = plat.id;}
if (enemy.blockedRight){ other.blockedRight = true; other.blockedLeft = false; enemy.blockedP = plat.id;}
}
}
}
const playerBelowPlatform = isPlayerBelowPlatform(enemyPlatform)
if(enemy.type !== "grunt" && isNearEdgeFacing(enemy, enemyPlatform, 10)){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy);
return;
}
// if player is below, move off closest edge
if (playerBelowPlatform) {
const leftEdge = enemyPlatform.minX;
const rightEdge = enemyPlatform.maxX;
const distToLeft = Math.abs(game.player.x - leftEdge);
const distToRight = Math.abs(game.player.x - rightEdge);
const targetEdge = (distToLeft < distToRight) ? leftEdge : rightEdge;
const move = Math.sign(targetEdge - enemy.x);
if(enemy.type !== "grunt" ){
if(move !== Math.sign(game.player.x - enemy.x)|| !isNearEdgeFacing(enemy, enemyPlatform, 10) ){//&& Math.abs(dx)>4)check distance here(for slopes)
pursue(enemy, dx, enemySegment, playerPlatform, wallSeg);
}else{
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
}
resetAttacks(enemy);
return;
}else{
// Move toward that edge
enemy.moveInput = move;
enemy.facing = enemy.moveInput;
if(directionBlocked(enemy)){
enemy.facing = -enemy.moveInput;
enemy.moveInput = enemy.facing;
}
// BOOST: ensure they actually fall off
if (isNearPlatformEdge(enemy, enemyPlatform, 10) && isSafeToDrop(enemy)) {
enemy.faceLockTimer = .75;
enemy.vx = enemy.facing * enemy.speed * 1.2; // small push
enemy.vy = -0.4; // tiny hop upward to break platform glue
enemy.inAir = true;
enemy.onGround = false;
}
}
return;
}
if (playerAbove) {
// don't get led off the edge
if(playerSegment){
// if near edge, check if able to jump up, if not, stop
if (isNearEdgeFacing(enemy, enemyPlatform, 12)) {
if (tryJumpAbove(enemy,enemySegment))return;
if(!isSafeToDrop(enemy)){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
return;
}
}
}else{
//no player platform / segment
if (isNearEdgeFacing(enemy, enemyPlatform, 12)
) {
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
return;
}
pursue(enemy, dx, enemySegment, playerPlatform);
return
}
}
// Try to get to player platform
if (playerSegment && enemySegment !== playerSegment ) {
let target;
if(enemyPlatform != playerPlatform){
if(playerAbove){
const closestSegments = findClosestSegmentsBetween(enemyPlatform, playerPlatform);
const closestSegmentsMidPoint = findClosestSegmentsBetweenMidPoints(enemyPlatform, playerPlatform);
const segmentOverPlatform = getSegmentOverPlatform(enemyPlatform);
const segmentOverCurrentSegment = getSegmentOverCurrent(enemySegment)
const bestJumpSegment = findBestJumpSegment(enemy, enemyPlatform, playerPlatform);
target = closestSegments.segA;
if(target !== enemySegment){
if(closestSegments.segA !== closestSegmentsMidPoint.segA ){
if(segmentOverCurrentSegment){
target = segmentOverCurrentSegment;
}else if(segmentOverPlatform){
target = segmentOverPlatform;
}
}
if(playerAbove && target){
if (tryTarget(enemy,enemySegment,target))return;
}
moveTowardSegment(enemy, enemyPlatform, target, wallSeg)
avoidWallsOnSamePlatform(enemy, wallSeg)
return;
}
target = bestJumpSegment;
if(!target && playerAbove){
target = segmentOverCurrentSegment;
}
if(!target && playerAbove){
target = segmentOverPlatform;
}
if(!target && closestSegments.segA === enemySegment){
target = closestSegments.segB;
}
if(!isSegmentBelowPlatform(target, playerPlatform)){
target = playerSegment;
}
if(target ){
if (tryJumpAbove(enemy,enemySegment))return;
if( typeof segmentOverPlatform !== "undefined" && segmentOverPlatform !==null){
if(segmentOverPlatform.pid === playerPlatform.id){
if(jumpToSegment(enemy,enemyPlatform,segmentOverPlatform,wallSeg)){
avoidWallsOnSamePlatform(enemy,enemyPlatform,wallSeg)
return;
}
}
}
if(!moveTowardSegment(enemy, enemyPlatform, target, wallSeg)&&segmentOverPlatform){
moveTowardSegment(enemy, enemyPlatform, segmentOverPlatform, wallSeg)
avoidWallsOnSamePlatform(enemy,enemyPlatform,wallSeg)
return
}
if (tryTarget(enemy,enemySegment,target))return;
}
}
}
if (canJumpToSegment(enemy, enemySegment, playerSegment)&& !isSegmentBelowSegment(playerSegment, enemySegment)){//
if(platformJump(enemy,enemyPlatform)) return;
}
pursue(enemy, dx, enemySegment, playerPlatform, wallSeg);
return;
}
}
if(!enemy.inAir){enemy.faceLockTimer = 0;}//don't walk off edge if just landed on player platform
//--- SAME PLATFORM: NORMAL PURSUIT + ATTACK ---
if (dist < 50) {
// attack logic...
let delay = 0.3 + Math.random() * 0.7; // 0.3-1.0 sec
if(enemy.type === "boss") delay = 0.5;
// Only punch if cooldown is done
if (enemy.attackCooldown <= 0) {
enemy.verticalInput = 0;
enemy.attackCooldown = delay;
if (Math.random() < 0.5) {
enemy.wantPunch = true;
}else{
enemy.wantKick = true;
}
if(!game.player.inAir && game.player.verticalInput < 0){
enemy.verticalInput = 0;
}else if (Math.random() < 0.5){
enemy.verticalInput = 0;
}else{
enemy.verticalInput = 1;
}
}
if(enemy.anim.state === "walk" && enemy.type !== "boss"){
resetAttacks(enemy)
}
if (enemy.anim.state === "punch" && enemy.anim.frame > attackFrames.punch.end ) {
enemy.wantPunch = false;
}
if (enemy.anim.state === "highPunch" && enemy.anim.frame > attackFrames.highPunch.end ) {
enemy.wantPunch = false;
}
if (enemy.anim.state === "kick" && enemy.anim.frame > attackFrames.kick.end ) {
enemy.wantKick = false;
}
if (enemy.anim.state === "highKick" && enemy.anim.frame > attackFrames.highKick.end ) {
enemy.wantKick = false;
}
if(enemy.type !== "boss"){
enemy.moveInput = 0;
}else{
//boss
if (enemy.anim.state === "walkPunch" && enemy.anim.frame > attackFrames.walkPunch.end ) {
enemy.wantPunch = false;
}
if (enemy.anim.state === "walkHighPunch" && enemy.anim.frame > attackFrames.walkHighPunch.end ) {
enemy.wantPunch = false;
}
if(enemy.type === "boss" && !enemy.wantPunch ) {
enemy.moveInput = 0;
if(enemy.anim.state === "walk"){
resetAttacks(enemy)
}
}
}
// for audio
const attackPressed = (enemy.wantPunch || enemy.wantKick) && !enemy.prevAttack;
enemy.prevAttack = enemy.wantPunch || enemy.wantKick;
if (attackPressed) {
sfxHuh();
game.player.prevHit =false;
}
} else {
pursue(enemy, dx, enemySegment, playerPlatform, wallSeg);
}
}
function isSegmentBelowPlatform(seg, plat){
for (const pseg of plat.segments) {
// Optional: only treat floor-type segments as platform surface
if (pseg.isWall)continue;
if(isSegmentBelowSegment(seg,pseg)) return true;
}
return false;
}
function pursue(enemy,dx, enemySegment, playerPlatform,wallSeg){
let wallJumped = false;
if(wallSeg){
if(tryJumpWall(enemy,wallSeg)){
resetAttacks(enemy);
return
}
}
if (keepEnemySpacing(enemy)) {
enemy.anim.state = "none";
enemy.moveState = "idle";
enemy.moveInput = 0;
return;
}
if (enemy.type !== "grunt" && enemySegment){
const plat = getPlatformById(enemy.onPlatform)
if(typeof plat !== "undefined"){
if(isNearEdgeFacing(enemy,plat , 10)){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
resetAttacks(enemy);
return
}
}
}
if(playerPlatform && enemySegment){
if (playerPlatform.id === enemy.onPlatform){
const behind = countEnemiesBehind(enemy);
if (behind >= 1 && countEnemiesAhead(enemy) < behind && Math.abs(dx) >= 50 && Math.abs(dx) < 100) {//&& Math.abs(dx) >= 65
if (enemy.onGround && enemy.jumpCooldown <= 0) {
enemy.moveInput = enemy.facing;
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
return;
}
}
if(
playerIsThreatening(game.player) && enemy.onGround &&
(enemy.facing === 1 && game.player.facing === -1 ||
enemy.facing === -1 && game.player.facing === 1)
) {
const safeDist = 70;
if (Math.abs(dx) < safeDist) {
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
return;
}
}
}
}
// normal movement
if (Math.abs(dx) > 3 ) {
if(enemySegment && Math.abs(enemySegment.angle) > maxClimbAngle){
enemy.moveInput = enemy.facing;
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
}else{
enemy.moveInput = enemy.facing;
enemy.anim.state = "walk";
}
} else if(playerPlatform && enemySegment){
enemy.moveInput = 0;
enemy.anim.state = "none";
enemy.moveState = "idle";
}
avoidWallsOnSamePlatform(enemy, wallSeg)
resetAttacks(enemy)
}
function moveTowardSegment(enemy,enemyPlatform,target, wallSeg){
// walk toward it
let left = Math.min(target.x1, target.x2);
let right = Math.max(target.x1, target.x2);
let ytarget = Math.min(target.y1, target.y2);
const enemyPlatLeft = Math.min(enemyPlatform.x1, enemyPlatform.x2);
const enemyPlatRight = Math.max(enemyPlatform.x1, enemyPlatform.x2);
//target the lower edge if there is one
if(target.y1 > target.y2){
left = target.x1;
right = target.x1 + 8;
ytarget = target.y1;
}else if(target.y1 < target.y2){
left = target.x2 - 8;
right = target.x2;
ytarget = target.y2;
}
if (enemy.x < left -8 ){
enemy.moveInput = 1;
enemy.anim.state = "walk";
enemy.facing = enemy.moveInput;
if(tryJumpWall(enemy,wallSeg))return true;
}
else if (enemy.x > right +8) {
enemy.moveInput = -1;
enemy.anim.state = "walk";
enemy.facing = enemy.moveInput;
if(tryJumpWall(enemy,wallSeg))return true;
}
else if(enemy.y - ytarget < 128 && !enemy.inAir && (enemy.onPlatform !== target.pid)){
// directly under it or close to it, jump next chance
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
}else{
enemy.moveInput = enemy.facing
if(directionBlocked(enemy)){
enemy.facing = -enemy.moveInput;
enemy.moveInput = enemy.facing;
}
resetAttacks(enemy)
return false
}
resetAttacks(enemy)
return true
}
function jumpToSegment(enemy,enemyPlatform,target,wallSeg){
let left = Math.min(target.x1, target.x2);
let right = Math.max(target.x1, target.x2);
//target the lower edge if there is one
if(target.y1 > target.y2){
left = target.x1;
right = target.x1 + 8;
}else if(target.y1 < target.y2){
left = target.x2 - 8;
right = target.x2;
}
if(enemy.y - Math.min(target.y1, target.y2) < 128
&& isUnderOrFacingSegment(enemy, target)){
// directly under it, jump next chance
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
return true
}
return false
}
function platformJump(enemy,enemyPlatform){
if(isNearEdgeFacing(enemy, enemyPlatform, 20) &&
enemy.jumpCooldown <= 0 &&
!enemy.inAir && !directionBlocked(enemy)) {
if(enemy.vx !=0){
enemy.moveInput = enemy.facing;
}
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
return true; // commit jump
}
return false;
}
function tryTarget(enemy,enemySegment,target){
if(enemy.y - Math.min(target.y1, target.y2) > 128)return false;
const left = Math.min(target.x1, target.x2);
const right = Math.max(target.x1, target.x2);
let distToTarget = 25;
if (enemy.x < left) distToTarget = (enemy.x + 25) - left;
if (enemy.x > right) distToTarget = (right + 25) - enemy.x;
distToTarget = Math.max(distToTarget, 25)
if ((enemy.facing === 1 && enemy.moveInput ===1 && enemy.x < left - 5 && enemy.x > left - distToTarget )|| // might try relative to right minus left
(enemy.facing === -1 && enemy.moveInput === -1 && enemy.x > right + 5 && enemy.x < right + distToTarget )){ //getting near, jump now
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
return true;
}
return false;
}
function tryJumpAbove(enemy,currentSeg){
const target = getSegmentOverCurrent(currentSeg);//get lowest platform over current platform
if(target){
const left = Math.min(target.x1, target.x2);
const right = Math.max(target.x1, target.x2);
if(!canJumpToSegment(enemy,currentSeg,target)|enemy.y - Math.min(target.y1, target.y2) > 128){
return false
}
if(enemy.x > left && enemy.x < right){
// directly under it, jump next chance
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
return true;
}
}
return false;
}
function tryJumpWall(enemy,wallSeg){
if(wallSeg){
if((enemy.facing === 1 && enemy.blockedRight) || (enemy.facing === -1 && enemy.blockedLeft)){
if (aiCanJumpOver(enemy, wallSeg)){
enemy.moveInput = enemy.facing;
jump(enemy);
enemy.jumpCooldown = 1.0 + Math.random() * 0.5;
enemy.faceLockTimer = .5;
resetAttacks(enemy)
return true
}
}
}
return false;
}
*/'] |