User prompt
Please fix the bug: 'TypeError: right-hand side of 'in' should be an object, got null' in or related to this line: 'tween(self.weapon, {' Line Number: 239 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: can't access property "toGlobal", obj.parent is undefined' in or related to this line: 'var localPos = joystick.parent.toLocal(obj.parent.toGlobal({' Line Number: 669
Code edit (1 edits merged)
Please save this source code
User prompt
Stickman Physics Arena
Initial prompt
Make a 2D physics-based fighting game similar to "Supreme Duelist Stickman" for mobile and PC. Game Style: Minimalistic stickman graphics with smooth ragdoll physics. Modes: Single Player (fight against AI with increasing difficulty) 2 Player Local (same device multiplayer) Endless Survival Mode Controls: Left/right joystick for movement, one attack button, and one special ability button. Weapons: Swords, spears, guns, laser weapons, random power-ups. Mechanics: Characters can pick up random weapons, physics-based hits cause knockback, environmental hazards (lava, spikes). Levels: 15 maps with different layouts and traps. Customization: Player can change stickman color, hat, and weapon skin. AI Behavior: Challenging but fair, reacts to player movement and weapon use. Sound: Cartoon-style sound effects and energetic background music. Extra: Random weapon drops every 10 seconds Slow-motion effect on final hit Simple but fun ragdoll animations
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bullet = Container.expand(function (x, y, velocityX, velocityY) {
var self = Container.call(this);
self.velocityX = velocityX;
self.velocityY = velocityY;
self.damage = 30;
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with stickmen
for (var i = 0; i < stickmen.length; i++) {
var stickman = stickmen[i];
if (stickman.isDead) continue;
var distance = Math.sqrt(Math.pow(stickman.x - self.x, 2) + Math.pow(stickman.y - self.y, 2));
if (distance < 40) {
stickman.takeDamage(self.damage, {
x: self.x,
y: self.y
});
self.destroy();
// Remove from bullets array
var index = bullets.indexOf(self);
if (index > -1) {
bullets.splice(index, 1);
}
return;
}
}
// Remove if out of bounds
if (self.x < -100 || self.x > 2148 || self.y > 2832) {
self.destroy();
var index = bullets.indexOf(self);
if (index > -1) {
bullets.splice(index, 1);
}
}
};
return self;
});
var ControlStick = Container.expand(function (x, y) {
var self = Container.call(this);
self.baseX = x;
self.baseY = y;
self.active = false;
self.inputX = 0;
self.inputY = 0;
var base = self.attachAsset('joystickBase', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.6
});
var knob = self.attachAsset('joystickKnob', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.x = x;
self.y = y;
self.updateInput = function (touchX, touchY) {
if (!self.active) return;
var deltaX = touchX - self.x;
var deltaY = touchY - self.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
var maxDistance = 50;
if (distance > maxDistance) {
deltaX = deltaX / distance * maxDistance;
deltaY = deltaY / distance * maxDistance;
}
knob.x = deltaX;
knob.y = deltaY;
self.inputX = deltaX / maxDistance;
self.inputY = deltaY / maxDistance;
};
self.reset = function () {
self.active = false;
knob.x = 0;
knob.y = 0;
self.inputX = 0;
self.inputY = 0;
};
return self;
});
var Stickman = Container.expand(function (x, y, color, isAI) {
var self = Container.call(this);
// Properties
self.isAI = isAI || false;
self.health = 100;
self.maxHealth = 100;
self.velocityX = 0;
self.velocityY = 0;
self.grounded = false;
self.weapon = null;
self.weaponType = 'none';
self.facingRight = true;
self.attackCooldown = 0;
self.specialCooldown = 0;
self.isDead = false;
self.ragdollMode = false;
// Physics properties
self.mass = 1;
self.friction = 0.85;
self.jumpPower = 15;
self.moveSpeed = 5;
// Create body parts
self.head = self.attachAsset('stickmanHead', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -60,
tint: color || 0xFFFFFF
});
self.body = self.attachAsset('stickmanBody', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -20,
tint: color || 0xFFFFFF
});
self.leftArm = self.attachAsset('stickmanArm', {
anchorX: 0.5,
anchorY: 0.1,
x: -15,
y: -40,
tint: color || 0xFFFFFF
});
self.rightArm = self.attachAsset('stickmanArm', {
anchorX: 0.5,
anchorY: 0.1,
x: 15,
y: -40,
tint: color || 0xFFFFFF
});
self.leftLeg = self.attachAsset('stickmanLeg', {
anchorX: 0.5,
anchorY: 0.1,
x: -10,
y: 20,
tint: color || 0xFFFFFF
});
self.rightLeg = self.attachAsset('stickmanLeg', {
anchorX: 0.5,
anchorY: 0.1,
x: 10,
y: 20,
tint: color || 0xFFFFFF
});
// Position
self.x = x || 0;
self.y = y || 0;
// Movement methods
self.move = function (direction) {
if (self.isDead || self.ragdollMode) return;
self.velocityX += direction * 0.8;
self.facingRight = direction > 0;
// Animation
if (direction !== 0) {
self.animateWalk();
}
};
self.jump = function () {
if (self.isDead || self.ragdollMode || !self.grounded) return;
self.velocityY = -self.jumpPower;
self.grounded = false;
};
self.attack = function () {
if (self.isDead || self.ragdollMode || self.attackCooldown > 0) return;
self.attackCooldown = 30;
if (self.weaponType === 'sword' || self.weaponType === 'spear') {
self.meleeAttack();
} else if (self.weaponType === 'gun') {
self.shootBullet();
} else if (self.weaponType === 'laser') {
self.fireLaser();
} else {
self.punchAttack();
}
};
self.meleeAttack = function () {
var swingAngle = self.facingRight ? -Math.PI / 4 : Math.PI / 4;
if (self.weapon) {
tween(self.weapon, {
rotation: swingAngle
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.weapon, {
rotation: 0
}, {
duration: 300
});
}
});
}
LK.getSound('swordHit').play();
// Check for hits
self.checkMeleeHit();
};
self.shootBullet = function () {
var bullet = new Bullet(self.x + (self.facingRight ? 30 : -30), self.y - 40, self.facingRight ? 15 : -15, -2);
game.addChild(bullet);
bullets.push(bullet);
LK.getSound('gunShot').play();
};
self.fireLaser = function () {
var laser = self.attachAsset('laserBeam', {
anchorX: 0,
anchorY: 0.5,
x: self.facingRight ? 30 : -430,
y: -40,
alpha: 0.8
});
// Laser damage check
self.checkLaserHit();
LK.getSound('laserFire').play();
// Remove laser after short duration
LK.setTimeout(function () {
if (laser.parent) {
laser.parent.removeChild(laser);
}
}, 200);
};
self.punchAttack = function () {
var arm = self.facingRight ? self.rightArm : self.leftArm;
var originalX = arm.x;
tween(arm, {
x: originalX + (self.facingRight ? 20 : -20)
}, {
duration: 150,
onFinish: function onFinish() {
tween(arm, {
x: originalX
}, {
duration: 200
});
}
});
self.checkMeleeHit();
};
self.checkMeleeHit = function () {
var attackRange = 80;
var attackX = self.x + (self.facingRight ? attackRange / 2 : -attackRange / 2);
for (var i = 0; i < stickmen.length; i++) {
var target = stickmen[i];
if (target === self || target.isDead) continue;
var distance = Math.abs(target.x - attackX);
var verticalDistance = Math.abs(target.y - self.y);
if (distance < attackRange && verticalDistance < 100) {
var damage = self.weaponType === 'spear' ? 40 : self.weaponType === 'sword' ? 35 : 25;
target.takeDamage(damage, self);
break;
}
}
};
self.checkLaserHit = function () {
for (var i = 0; i < stickmen.length; i++) {
var target = stickmen[i];
if (target === self || target.isDead) continue;
var inLaserPath = self.facingRight ? target.x > self.x && target.x < self.x + 400 : target.x < self.x && target.x > self.x - 400;
var verticalDistance = Math.abs(target.y - self.y);
if (inLaserPath && verticalDistance < 50) {
target.takeDamage(50, self);
}
}
};
self.takeDamage = function (damage, attacker) {
if (self.isDead) return;
self.health -= damage;
// Knockback
var knockbackForce = 8;
var direction = attacker.x < self.x ? 1 : -1;
self.velocityX += direction * knockbackForce;
self.velocityY -= 5;
// Flash red
LK.effects.flashObject(self, 0xFF0000, 300);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
self.isDead = true;
self.ragdollMode = true;
// Drop weapon
if (self.weapon) {
self.dropWeapon();
}
LK.getSound('death').play();
// Slow motion effect
gameSpeed = 0.3;
LK.setTimeout(function () {
gameSpeed = 1.0;
}, 1000);
// Update score if player died
if (!self.isAI) {
// Game over logic handled by main game
} else {
LK.setScore(LK.getScore() + 100);
}
};
self.pickupWeapon = function (weaponType) {
self.dropWeapon();
var weaponAsset = weaponType;
self.weapon = self.attachAsset(weaponAsset, {
anchorX: 0.5,
anchorY: 0.9,
x: self.facingRight ? 20 : -20,
y: -30
});
self.weaponType = weaponType;
LK.getSound('weaponPickup').play();
};
self.dropWeapon = function () {
if (self.weapon) {
// Create weapon drop
var weaponDrop = new WeaponDrop(self.x, self.y - 40, self.weaponType);
game.addChild(weaponDrop);
weaponDrops.push(weaponDrop);
self.removeChild(self.weapon);
self.weapon = null;
self.weaponType = 'none';
}
};
self.animateWalk = function () {
if (LK.ticks % 10 === 0) {
var legSwing = Math.sin(LK.ticks * 0.2) * 0.2;
self.leftLeg.rotation = legSwing;
self.rightLeg.rotation = -legSwing;
var armSwing = Math.sin(LK.ticks * 0.2) * 0.15;
self.leftArm.rotation = -armSwing;
self.rightArm.rotation = armSwing;
}
};
self.updatePhysics = function () {
// Apply gravity
if (!self.grounded) {
self.velocityY += gravity * gameSpeed;
}
// Apply friction
self.velocityX *= self.friction;
// Update position
self.x += self.velocityX * gameSpeed;
self.y += self.velocityY * gameSpeed;
// Ground collision
if (self.y > groundY - 50) {
self.y = groundY - 50;
self.velocityY = 0;
self.grounded = true;
}
// Boundary collision
if (self.x < 50) {
self.x = 50;
self.velocityX = 0;
}
if (self.x > 1998) {
self.x = 1998;
self.velocityX = 0;
}
// Update weapon position
if (self.weapon) {
self.weapon.x = self.facingRight ? 20 : -20;
if (!self.facingRight) {
self.weapon.scaleX = -1;
} else {
self.weapon.scaleX = 1;
}
}
// Update cooldowns
if (self.attackCooldown > 0) self.attackCooldown--;
if (self.specialCooldown > 0) self.specialCooldown--;
};
self.updateAI = function () {
if (self.isDead || self.ragdollMode || !self.isAI) return;
// Find nearest enemy (player)
var target = null;
var minDistance = Infinity;
for (var i = 0; i < stickmen.length; i++) {
var enemy = stickmen[i];
if (enemy === self || enemy.isDead || enemy.isAI) continue;
var distance = Math.abs(enemy.x - self.x);
if (distance < minDistance) {
minDistance = distance;
target = enemy;
}
}
if (!target) return;
var distance = Math.abs(target.x - self.x);
var direction = target.x > self.x ? 1 : -1;
// AI Behavior
if (distance > 100) {
// Move towards target
self.move(direction * 0.7);
} else if (distance < 50) {
// Too close, back away sometimes
if (Math.random() < 0.3) {
self.move(-direction * 0.5);
}
}
// Attack when in range
if (distance < 90 && Math.random() < 0.1) {
self.attack();
}
// Jump randomly or when stuck
if (Math.random() < 0.02 || Math.abs(self.velocityX) < 0.1 && distance > 80) {
self.jump();
}
// Pick up weapons
for (var j = 0; j < weaponDrops.length; j++) {
var weapon = weaponDrops[j];
var weaponDistance = Math.abs(weapon.x - self.x);
if (weaponDistance < 60 && !self.weapon) {
weapon.pickup(self);
break;
}
}
};
self.update = function () {
self.updatePhysics();
self.updateAI();
};
return self;
});
var WeaponDrop = Container.expand(function (x, y, weaponType) {
var self = Container.call(this);
self.weaponType = weaponType;
self.pickupTimer = 0;
var weaponGraphics = self.attachAsset(weaponType, {
anchorX: 0.5,
anchorY: 0.5
});
self.x = x;
self.y = y;
self.pickup = function (stickman) {
stickman.pickupWeapon(self.weaponType);
self.destroy();
var index = weaponDrops.indexOf(self);
if (index > -1) {
weaponDrops.splice(index, 1);
}
};
self.update = function () {
// Floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
// Auto-pickup timer
self.pickupTimer++;
if (self.pickupTimer > 600) {
// 10 seconds
self.destroy();
var index = weaponDrops.indexOf(self);
if (index > -1) {
weaponDrops.splice(index, 1);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Sounds
// UI Elements
// Environment
// Weapons
// Stickman body parts
// Game variables
var stickmen = [];
var bullets = [];
var weaponDrops = [];
var platforms = [];
var hazards = [];
// Physics constants
var gravity = 0.8;
var groundY = 2600;
var gameSpeed = 1.0;
// Game state
var gameMode = 'single'; // 'single', 'multiplayer', 'survival'
var currentWave = 1;
var enemiesSpawned = 0;
var maxEnemies = 2;
// Weapon spawn timer
var weaponSpawnTimer = 0;
var weaponSpawnInterval = 600; // 10 seconds at 60fps
// Controls
var joystick;
var attackButton;
var specialButton;
var dragNode = null;
// Create ground
var ground = game.addChild(LK.getAsset('ground', {
anchorX: 0.5,
anchorY: 0,
x: 1024,
y: groundY
}));
// Create platforms
var platform1 = game.addChild(LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0,
x: 512,
y: groundY - 200
}));
platforms.push(platform1);
var platform2 = game.addChild(LK.getAsset('platform', {
anchorX: 0.5,
anchorY: 0,
x: 1536,
y: groundY - 300
}));
platforms.push(platform2);
// Create environmental hazards
var lava1 = game.addChild(LK.getAsset('lava', {
anchorX: 0.5,
anchorY: 0,
x: 800,
y: groundY
}));
hazards.push({
obj: lava1,
type: 'lava'
});
var spike1 = game.addChild(LK.getAsset('spike', {
anchorX: 0.5,
anchorY: 0,
x: 1200,
y: groundY - 60
}));
hazards.push({
obj: spike1,
type: 'spike'
});
// Create player
var player = new Stickman(300, groundY - 100, 0x00FF00, false);
game.addChild(player);
stickmen.push(player);
// Create initial enemies
for (var i = 0; i < maxEnemies; i++) {
var enemy = new Stickman(1500 + i * 100, groundY - 100, 0xFF0000, true);
game.addChild(enemy);
stickmen.push(enemy);
enemiesSpawned++;
}
// Create controls
joystick = new ControlStick(150, 2550);
LK.gui.bottomLeft.addChild(joystick);
attackButton = LK.gui.bottomRight.addChild(LK.getAsset('attackButton', {
anchorX: 0.5,
anchorY: 0.5,
x: -150,
y: -150,
alpha: 0.7
}));
specialButton = LK.gui.bottomRight.addChild(LK.getAsset('specialButton', {
anchorX: 0.5,
anchorY: 0.5,
x: -150,
y: -280,
alpha: 0.7
}));
// Health bar
var healthBarBg = LK.gui.topLeft.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 120,
y: 50,
width: 200,
height: 20,
tint: 0x333333
}));
var healthBar = LK.gui.topLeft.addChild(LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 120,
y: 50,
width: 200,
height: 20,
tint: 0x00FF00
}));
// Score display
var scoreText = new Text2('Score: 0', {
size: 40,
fill: '#FFFFFF'
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Wave display
var waveText = new Text2('Wave: 1', {
size: 35,
fill: '#FFFF00'
});
waveText.anchor.set(1, 0);
LK.gui.topRight.addChild(waveText);
// Control handlers
game.down = function (x, y, obj) {
// Safe coordinate conversion for joystick
var globalPos = {
x: x,
y: y
};
if (obj && obj.parent && obj.parent.toGlobal) {
globalPos = obj.parent.toGlobal({
x: x,
y: y
});
}
var localPos = joystick.parent.toLocal(globalPos);
var distance = Math.sqrt(Math.pow(localPos.x - joystick.x, 2) + Math.pow(localPos.y - joystick.y, 2));
if (distance < 80) {
joystick.active = true;
joystick.updateInput(localPos.x, localPos.y);
dragNode = 'joystick';
}
// Check attack button
var attackGlobalPos = {
x: x,
y: y
};
if (obj && obj.parent && obj.parent.toGlobal) {
attackGlobalPos = obj.parent.toGlobal({
x: x,
y: y
});
}
var attackPos = attackButton.parent.toLocal(attackGlobalPos);
var attackDistance = Math.sqrt(Math.pow(attackPos.x - attackButton.x, 2) + Math.pow(attackPos.y - attackButton.y, 2));
if (attackDistance < 60) {
player.attack();
}
// Check special button
var specialGlobalPos = {
x: x,
y: y
};
if (obj && obj.parent && obj.parent.toGlobal) {
specialGlobalPos = obj.parent.toGlobal({
x: x,
y: y
});
}
var specialPos = specialButton.parent.toLocal(specialGlobalPos);
var specialDistance = Math.sqrt(Math.pow(specialPos.x - specialButton.x, 2) + Math.pow(specialPos.y - specialButton.y, 2));
if (specialDistance < 50 && player.specialCooldown === 0) {
player.jump();
player.specialCooldown = 120; // 2 second cooldown
}
};
game.move = function (x, y, obj) {
if (dragNode === 'joystick') {
// Safe coordinate conversion for joystick movement
var globalPos = {
x: x,
y: y
};
if (obj && obj.parent && obj.parent.toGlobal) {
globalPos = obj.parent.toGlobal({
x: x,
y: y
});
}
var localPos = joystick.parent.toLocal(globalPos);
joystick.updateInput(localPos.x, localPos.y);
}
};
game.up = function (x, y, obj) {
if (dragNode === 'joystick') {
joystick.reset();
}
dragNode = null;
};
// Spawn random weapon
function spawnRandomWeapon() {
var weaponTypes = ['sword', 'spear', 'gun', 'laser'];
var randomType = weaponTypes[Math.floor(Math.random() * weaponTypes.length)];
var randomX = 200 + Math.random() * 1648;
var weaponDrop = new WeaponDrop(randomX, groundY - 200, randomType);
game.addChild(weaponDrop);
weaponDrops.push(weaponDrop);
}
// Check hazard collisions
function checkHazardCollisions() {
for (var i = 0; i < stickmen.length; i++) {
var stickman = stickmen[i];
if (stickman.isDead) continue;
for (var j = 0; j < hazards.length; j++) {
var hazard = hazards[j];
var distance = Math.abs(stickman.x - hazard.obj.x);
if (distance < 60 && Math.abs(stickman.y - hazard.obj.y) < 60) {
if (hazard.type === 'lava') {
stickman.takeDamage(5, {
x: hazard.obj.x,
y: hazard.obj.y
});
} else if (hazard.type === 'spike') {
stickman.takeDamage(20, {
x: hazard.obj.x,
y: hazard.obj.y
});
}
}
}
}
}
// Spawn new enemies
function spawnEnemies() {
var aliveEnemies = 0;
for (var i = 0; i < stickmen.length; i++) {
if (stickmen[i].isAI && !stickmen[i].isDead) {
aliveEnemies++;
}
}
if (aliveEnemies === 0) {
// Next wave
currentWave++;
maxEnemies = Math.min(2 + Math.floor(currentWave / 2), 6);
for (var j = 0; j < maxEnemies; j++) {
var spawnX = Math.random() < 0.5 ? 100 : 1900;
var enemy = new Stickman(spawnX, groundY - 100, 0xFF0000, true);
// Give some enemies weapons based on wave
if (currentWave > 2 && Math.random() < 0.4) {
var weaponTypes = ['sword', 'spear'];
if (currentWave > 4) weaponTypes.push('gun');
if (currentWave > 6) weaponTypes.push('laser');
var randomWeapon = weaponTypes[Math.floor(Math.random() * weaponTypes.length)];
enemy.pickupWeapon(randomWeapon);
}
game.addChild(enemy);
stickmen.push(enemy);
}
waveText.setText('Wave: ' + currentWave);
}
}
// Main game loop
game.update = function () {
// Apply game speed (for slow motion effects)
if (gameSpeed < 1.0) {
gameSpeed = Math.min(gameSpeed + 0.02, 1.0);
}
// Update player movement from joystick
if (joystick.active && !player.isDead) {
if (Math.abs(joystick.inputX) > 0.2) {
player.move(joystick.inputX);
}
}
// Update all stickmen
for (var i = stickmen.length - 1; i >= 0; i--) {
var stickman = stickmen[i];
if (stickman.isDead && stickman.ragdollMode) {
// Remove dead stickmen after some time
if (LK.ticks % 600 === 0) {
// 10 seconds
stickman.destroy();
stickmen.splice(i, 1);
}
}
}
// Update bullets
for (var j = bullets.length - 1; j >= 0; j--) {
// Bullet update is handled in bullet class
}
// Update weapon drops
for (var k = weaponDrops.length - 1; k >= 0; k--) {
var weaponDrop = weaponDrops[k];
// Check player pickup
var distance = Math.abs(player.x - weaponDrop.x);
if (distance < 60 && Math.abs(player.y - weaponDrop.y) < 60 && !player.weapon) {
weaponDrop.pickup(player);
}
}
// Spawn weapons periodically
weaponSpawnTimer++;
if (weaponSpawnTimer >= weaponSpawnInterval) {
spawnRandomWeapon();
weaponSpawnTimer = 0;
}
// Check hazard collisions
checkHazardCollisions();
// Spawn new enemies
if (gameMode === 'single') {
spawnEnemies();
}
// Update UI
scoreText.setText('Score: ' + LK.getScore());
// Update health bar
var healthPercent = Math.max(0, player.health / player.maxHealth);
healthBar.width = 200 * healthPercent;
if (healthPercent > 0.6) {
healthBar.tint = 0x00FF00;
} else if (healthPercent > 0.3) {
healthBar.tint = 0xFFFF00;
} else {
healthBar.tint = 0xFF0000;
}
// Game over check
if (player.isDead) {
LK.showGameOver();
}
// Camera follow player (simple implementation)
var targetX = -player.x + 1024;
targetX = Math.max(-500, Math.min(500, targetX));
game.x += (targetX - game.x) * 0.05;
};
// Start background music
LK.playMusic('battleMusic'); ===================================================================
--- original.js
+++ change.js
@@ -611,44 +611,74 @@
waveText.anchor.set(1, 0);
LK.gui.topRight.addChild(waveText);
// Control handlers
game.down = function (x, y, obj) {
- var localPos = joystick.parent.toLocal(obj.parent.toGlobal({
+ // Safe coordinate conversion for joystick
+ var globalPos = {
x: x,
y: y
- }));
+ };
+ if (obj && obj.parent && obj.parent.toGlobal) {
+ globalPos = obj.parent.toGlobal({
+ x: x,
+ y: y
+ });
+ }
+ var localPos = joystick.parent.toLocal(globalPos);
var distance = Math.sqrt(Math.pow(localPos.x - joystick.x, 2) + Math.pow(localPos.y - joystick.y, 2));
if (distance < 80) {
joystick.active = true;
joystick.updateInput(localPos.x, localPos.y);
dragNode = 'joystick';
}
// Check attack button
- var attackPos = attackButton.parent.toLocal(obj.parent.toGlobal({
+ var attackGlobalPos = {
x: x,
y: y
- }));
+ };
+ if (obj && obj.parent && obj.parent.toGlobal) {
+ attackGlobalPos = obj.parent.toGlobal({
+ x: x,
+ y: y
+ });
+ }
+ var attackPos = attackButton.parent.toLocal(attackGlobalPos);
var attackDistance = Math.sqrt(Math.pow(attackPos.x - attackButton.x, 2) + Math.pow(attackPos.y - attackButton.y, 2));
if (attackDistance < 60) {
player.attack();
}
// Check special button
- var specialPos = specialButton.parent.toLocal(obj.parent.toGlobal({
+ var specialGlobalPos = {
x: x,
y: y
- }));
+ };
+ if (obj && obj.parent && obj.parent.toGlobal) {
+ specialGlobalPos = obj.parent.toGlobal({
+ x: x,
+ y: y
+ });
+ }
+ var specialPos = specialButton.parent.toLocal(specialGlobalPos);
var specialDistance = Math.sqrt(Math.pow(specialPos.x - specialButton.x, 2) + Math.pow(specialPos.y - specialButton.y, 2));
if (specialDistance < 50 && player.specialCooldown === 0) {
player.jump();
player.specialCooldown = 120; // 2 second cooldown
}
};
game.move = function (x, y, obj) {
if (dragNode === 'joystick') {
- var localPos = joystick.parent.toLocal(obj.parent.toGlobal({
+ // Safe coordinate conversion for joystick movement
+ var globalPos = {
x: x,
y: y
- }));
+ };
+ if (obj && obj.parent && obj.parent.toGlobal) {
+ globalPos = obj.parent.toGlobal({
+ x: x,
+ y: y
+ });
+ }
+ var localPos = joystick.parent.toLocal(globalPos);
joystick.updateInput(localPos.x, localPos.y);
}
};
game.up = function (x, y, obj) {