Skip to content

Instantly share code, notes, and snippets.

@agrancini-sc
Created July 23, 2025 02:00
Show Gist options
  • Save agrancini-sc/61fcf6888fb6071446bb736c6041714e to your computer and use it in GitHub Desktop.
Save agrancini-sc/61fcf6888fb6071446bb736c6041714e to your computer and use it in GitHub Desktop.
// INSTANTIATE
// @typename Instantiator
// @input Instantiator instantiator
// @input Instantiator instantiator1
//@input Component.SceneObject sceneRoot
//@input Component.SceneObject sceneRoot1
//@input Component.ScriptComponent prefabAccessorA
//@input Component.ScriptComponent prefabAccessorB
//@input Asset.ObjectPrefab[] prefabs
// Dictionary to keep track of instantiated prefabs
var instantiatedPrefabs = {};
// Function to instantiate a prefab based on name
function instantiatePrefab(prefabName) {
// Destroy any existing prefab first to avoid duplicates
// if (instantiatedPrefabs[prefabName]) {
// destroyPrefab(prefabName);
// }
// Switch case for different crate instantiation
switch (prefabName) {
case "crateB1":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[0]);
break;
case "crateB1a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[2]);
break;
case "crateB2":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[1]);
break;
case "crateB2a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[3]);
break;
case "crateXL":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[12]);
break;
case "crateXLa":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[13]);
break;
case "crateM1":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[4]);
break;
case "crateM1a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[11]);
break;
case "crateM2":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[5]);
break;
case "crateM2a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[6]);
break;
case "crateM3":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[7]);
break;
case "crateM3a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[8]);
break;
case "crateM4":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[9]);
break;
case "crateM4a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[10]);
break;
// Add Slices objects
case "SlicesP1":
case "sliceP1":
print("DEBUG: Attempting to instantiate SlicesP1 with prefab index 14");
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[14]);
break;
case "SlicesP1a":
case "sliceP1a":
print("DEBUG: Attempting to instantiate SlicesP1a with prefab index 15");
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[15]);
break;
case "SlicesP2":
case "sliceP2":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[16]);
break;
case "SlicesP2a":
case "sliceP2a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[17]);
break;
case "SlicesP3":
case "sliceP3":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[18]);
break;
case "SlicesP3a":
case "sliceP3a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[19]);
break;
// Add Ballons objects
case "BallonsM1":
case "ballonM1":
print("DEBUG: Attempting to instantiate BallonsM1 with prefab index 20");
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[20]);
break;
case "BallonsM1a":
case "ballonM1a":
print("DEBUG: Attempting to instantiate BallonsM1a with prefab index 21");
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[21]);
break;
case "BallonsM2":
case "ballonM2":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[22]);
break;
case "BallonsM2a":
case "ballonM2a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[23]);
break;
case "BallonsM3":
case "ballonM3":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[24]);
break;
case "BallonsM3a":
case "ballonM3a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[25]);
break;
case "BallonsM4":
case "ballonM4":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[26]);
break;
case "BallonsM4a":
case "ballonM4a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[27]);
break;
case "BallonsM5":
case "ballonM5":
script.prefabAccessorA.InstantiatorPrefab(script.prefabs[28]);
break;
case "BallonsM5a":
case "ballonM5a":
script.prefabAccessorB.InstantiatorPrefab(script.prefabs[29]);
break;
default:
print("No specific instantiation logic for: " + prefabName);
return null;
}
}
// Function to destroy a specific prefab instance
function destroyPrefab(prefabName, PA) {
try {
// Check if the prefab is currently instantiated
if (script.prefabAccessorA && script.prefabAccessorB ) {
// PrefabDestroy.destroy();
// delete instantiatedPrefabs[prefabName];
if(PA == 1){
script.prefabAccessorA.destroyInstances(prefabName, PA);
}else if (PA == 2){
script.prefabAccessorB.destroyInstances(prefabName, PA);
}
// print(prefabName +" prefab destroyed");
} else {
print("No instantiated prefab found with name: " + prefabName);
}
} catch (error) {
print("Error destroying prefab " + prefabName + ": " + error);
}
}
function instantiatePrefabx(prefabName) {
// Destroy any existing prefab first to avoid duplicates
// if (instantiatedPrefabs[prefabName]) {
// destroyPrefab(prefabName);
// }
// Switch case for different crate instantiation
switch (prefabName) {
case "crateB1":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[0]);
break;
case "crateB1a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[2]);
break;
case "crateB2":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[1]);
break;
case "crateB2a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[3]);
break;
case "crateXL":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[12]);
break;
case "crateXLa":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[13]);
break;
case "crateM1":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[4]);
break;
case "crateM1a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[11]);
break;
case "crateM2":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[5]);
break;
case "crateM2a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[6]);
break;
case "crateM3":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[7]);
break;
case "crateM3a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[8]);
break;
case "crateM4":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[9]);
break;
case "crateM4a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[10]);
break;
// Add Slices objects for InstantiatorPrefabx
case "SlicesP1":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[14]);
break;
case "SlicesP1a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[15]);
break;
case "SlicesP2":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[16]);
break;
case "SlicesP2a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[17]);
break;
case "SlicesP3":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[18]);
break;
case "SlicesP3a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[19]);
break;
// Add Ballons objects for InstantiatorPrefabx
case "BallonsM1":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[20]);
break;
case "BallonsM1a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[21]);
break;
case "BallonsM2":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[22]);
break;
case "BallonsM2a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[23]);
break;
case "BallonsM3":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[24]);
break;
case "BallonsM3a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[25]);
break;
case "BallonsM4":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[26]);
break;
case "BallonsM4a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[27]);
break;
case "BallonsM5":
script.prefabAccessorA.InstantiatorPrefabx(script.prefabs[28]);
break;
case "BallonsM5a":
script.prefabAccessorB.InstantiatorPrefabx(script.prefabs[29]);
break;
default:
print("No specific instantiation logic for: " + prefabName);
return null;
}
}
// Expose functions globally
global.instantiatePrefab = instantiatePrefab;
global.destroyPrefab = destroyPrefab;
global.instantiatePrefabx = instantiatePrefabx;
// global.destroyPrefab("crateB1");
// PREFAB ACCESSOR
import {InstantiationOptions, Instantiator} from "./SpectaclesSyncKit/Components/Instantiator"
import {NetworkRootInfo} from "./SpectaclesSyncKit/Core/NetworkRootInfo"
import {SyncKitLogger} from "./SpectaclesSyncKit/Utils/SyncKitLogger"
import { SyncEntity } from "./SpectaclesSyncKit/Core/SyncEntity"
@component
export class InstantiateTestA extends BaseScriptComponent {
private readonly log: SyncKitLogger = new SyncKitLogger(
InstantiateTestA.name
)
@input() instantiator: Instantiator
@input() claimOwnership: boolean = false
instantiatedObjects: SceneObject[]=[]
@input("Component.ScriptComponent") Surface: any;
@input("Component.ScriptComponent") stageManager: any;
i = 0;
private syncEntity: SyncEntity = null
onAwake() {
this.syncEntity = new SyncEntity(this)
}
InstantiatorPrefab(prefab) {
const options = new InstantiationOptions()
options.claimOwnership = this.claimOwnership
// Get the transform to access current position
const transform = this.sceneObject.getTransform();
const groundPosition = transform.getWorldPosition();
// Get the Y position from the Surface
let Y = this.Surface.getYPos();
print(Y);
// Set the position in the options using original X and Z coordinates with Y from Surface
options.worldPosition = new vec3(groundPosition.x, Y-140, groundPosition.z);
this.instantiator.instantiate(prefab, options, (instantiatedEntity) => {
if (!instantiatedEntity) {
print("Failed to instantiate the prefab.")
return
}
print("Prefab instantiated successfully.")
// Store instantiated prefab into an array
this.instantiatedObjects.push(instantiatedEntity.sceneObject)
if(this.i == 6){
this.instantiatedObjects.forEach((obj) => {
print("In Objects: " + obj.name);
var index = this.instantiatedObjects.findIndex(obj => {
// Extract the base name by splitting at colon and underscore
print("Object Instantiated Name"+ obj.name );
const parts = obj.name.split(':');
// if (parts.length < 2) return false;
const baseName = parts[1].split('_')[0];
print(baseName);
});
});
}else{
this.i++;
}
print(instantiatedEntity.sceneObject.getChildrenCount())
})
}
InstantiatorPrefabx(prefab) {
const options = new InstantiationOptions()
options.claimOwnership = this.claimOwnership
// Get the transform to access current position
const transform = this.sceneObject.getTransform();
const groundPosition = transform.getWorldPosition();
// Get the Y position from the Surface
let Y = this.Surface.getYPos();
print(Y);
// Set the position in the options using original X and Z coordinates with Y from Surface
options.worldPosition = new vec3(groundPosition.x, Y+70, groundPosition.z);
this.instantiator.instantiate(prefab, options, (instantiatedEntity) => {
if (!instantiatedEntity) {
print("Failed to instantiate the prefab.")
return
}
print("Prefab instantiated successfully.")
// Store instantiated prefab into an array
this.instantiatedObjects.push(instantiatedEntity.sceneObject)
if(this.i == 6){
this.instantiatedObjects.forEach((obj) => {
print("In Objects: " + obj.name);
var index = this.instantiatedObjects.findIndex(obj => {
// Extract the base name by splitting at colon and underscore
print("Object Instantiated Name"+ obj.name );
const parts = obj.name.split(':');
// if (parts.length < 2) return false;
const baseName = parts[1].split('_')[0];
print(baseName);
});
});
}else{
this.i++;
}
print(instantiatedEntity.sceneObject.getChildrenCount())
})
}
destroyInstances(prefabName: string, PA) {
try {
// First, log all object names to see what we're working with
print("Looking for prefab with name: " + prefabName);
// Try a more flexible search pattern
const index = this.instantiatedObjects.findIndex(obj => {
// Log the full name first
print("Checking object: " + obj.name);
// Try various ways to extract the base name
const colonParts = obj.name.split(':');
const namePart = colonParts.length > 1 ? colonParts[1] : obj.name;
const baseName = namePart.split('_')[0];
print("Extracted base name: " + baseName);
// Check if the base name contains or matches the target prefab name
return baseName === prefabName || baseName.includes(prefabName) || prefabName.includes(baseName);
});
if (index !== -1) {
const obj = this.instantiatedObjects[index];
print("Found object to destroy: " + obj.name);
if (!isNull(obj)) {
// Completely destroy the object instance
print("Destroying object instance: " + obj.name);
obj.destroy();
// Remove the prefab from the array
this.instantiatedObjects.splice(index, 1);
}
return true;
} else {
print("Prefab with name '" + prefabName + "' not found after searching.");
return false;
}
} catch (error) {
print("Error in destroyInstances for " + prefabName + ": " + error);
return false;
}
}
}
// SET HIT STATE
// SetHitState.js
// Version: 0.1.0
// Event: Initialized
// Description: This script sets Chick from Kinematic State to Hit State.
//@input SceneObject hit
//@input SceneObject cameraNear
//@input SceneObject rayEnd
//@input vec3 physicsApplyForceForceValue
//@input string physicsApplyForceSpace "Local to Object"
//@input SceneObject physicsApplyForceObjectSpace
//@input Component.ScriptComponent SyncTransR2D2
//@input Component.ScriptComponent characterHealthManager // Reference to the CharacterHealthManager script
// @input Component.AudioComponent pullAudio
// @input Component.AudioComponent pushAudio
// @input Component.ScriptComponent R2D2Movement
//@input Component.ScriptComponent forceEnergyManager // Reference to the ForceEnergyManager script
//@input Component.ScriptComponent stageManager // Reference to the StageManager script
// Custom force values for different object types
//@input vec3 balloonForceValue
//@input vec3 sliceForceValue
var CanPull = true;
global.HitOnce = 0;
var obj = script.getSceneObject();
script.hit.enabled = false;
script.pullF = function(){
global.ForceState = "pull";
global.HitOnce = 0;
}
script.pushF = function(){
global.ForceState = "push";
}
script.noneF = function(){
global.ForceState = "none";
}
// Set phyics body to dynamic, detach rotating parent with current position and pause the animation
script.setHitState = function(bodyComponent) {
if(!bodyComponent){
// No body component detected
} else if(bodyComponent){
var objectName = bodyComponent.getSceneObject().name;
if(global.ForceState == "pull" && script.forceEnergyManager.hasEnoughEnergy(objectName)){
// Check if we have enough force energy for pulling this object
script.hit.enabled = true;
if(global.HitOnce == 0){
global.prevModel = bodyComponent;
pull(bodyComponent);
global.HitOnce=1;
} else if(global.HitOnce == 1){
// Already pulling something
}
} else if(global.ForceState == "push"){
// Check if we have enough force energy for pushing this object
script.hit.enabled = true;
global.HitOnce=0;
EventStatusPull = "off";
} else if(global.ForceState == "none"){
script.hit.enabled = false;
}
}
};
function pull(bodyComponent){
let object = bodyComponent.getSceneObject();
let nameObj = bodyComponent.getSceneObject().name;
script.forceEnergyManager.consumeEnergyCopy(nameObj);
print(nameObj);
switch (nameObj) {
case "R2D2":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
script.SyncTransR2D2.startFullSynchronization();
script.R2D2Movement.stopMovement();
break;
case "crateB1":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateB1 = object.getComponent('Component.ScriptComponent');
SyncTransCrateB1.startFullSynchronization();
break;
case "crateB2":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateB2 = object.getComponent('Component.ScriptComponent');
SyncTransCrateB2.startFullSynchronization();
break;
case "crateXL":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateXL = object.getComponent('Component.ScriptComponent');
SyncTransCrateXL.startFullSynchronization();
break;
case "crateM1":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM1 = object.getComponent('Component.ScriptComponent');
SyncTransCrateM1.startFullSynchronization();
break;
case "crateM2":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM2 = object.getComponent('Component.ScriptComponent');
SyncTransCrateM2.startFullSynchronization();
break;
case "crateM3":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM3 = object.getComponent('Component.ScriptComponent');
SyncTransCrateM3.startFullSynchronization();
break;
case "crateM4":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM4 = object.getComponent('Component.ScriptComponent');
SyncTransCrateM4.startFullSynchronization();
break;
case "crateB1a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateB1a = object.getComponent('Component.ScriptComponent');
SyncTransCrateB1a.startFullSynchronization();
break;
case "crateB2a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateB2a = object.getComponent('Component.ScriptComponent');
SyncTransCrateB2a.startFullSynchronization();
break;
case "crateXLa":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateXLa = object.getComponent('Component.ScriptComponent');
SyncTransCrateXLa.startFullSynchronization();
break;
case "crateM1a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM1a = object.getComponent('Component.ScriptComponent');
SyncTransCrateM1a.startFullSynchronization();
break;
case "crateM2a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM2a = object.getComponent('Component.ScriptComponent');
SyncTransCrateM2a.startFullSynchronization();
break;
case "crateM3a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM3a = object.getComponent('Component.ScriptComponent');
SyncTransCrateM3a.startFullSynchronization();
break;
case "crateM4a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTransCrateM4a = object.getComponent('Component.ScriptComponent');
SyncTransCrateM4a.startFullSynchronization();
break;
// Handle balloon objects
case "BallonsM1":
case "ballonM1":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM2":
case "ballonM2":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM3":
case "ballonM3":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM4":
case "ballonM4":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM5":
case "ballonM5":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM1a":
case "ballonM1a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM2a":
case "ballonM2a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM3a":
case "ballonM3a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM4a":
case "ballonM4a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "BallonsM5a":
case "ballonM5a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
// Handle slice objects
case "SlicesP1":
case "sliceP1":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "SlicesP2":
case "sliceP2":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "SlicesP3":
case "sliceP3":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "SlicesP1a":
case "sliceP1a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "SlicesP2a":
case "sliceP2a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
case "SlicesP3a":
case "sliceP3a":
var parent1 = object.getParent();
var parentMain = parent1.getParent();
var SyncTrans = object.getComponent('Component.ScriptComponent');
if (SyncTrans) SyncTrans.startFullSynchronization();
break;
default:
print("No matching animation found.");
}
script.pullAudio.play(1);
// Define start and end positions
var colliderObj = bodyComponent.getSceneObject();
var transform = colliderObj.getTransform();
print("Hit object: " + colliderObj.name);
var startPosition = transform.getWorldPosition();
var transform1 = script.cameraNear.getTransform();
print(startPosition);
var endPosition = transform1.getWorldPosition();
print(endPosition);
// Define speed (units per second)
var speed = 650.0;
// Calculate direction
var direction = endPosition.sub(startPosition).normalize();
global.EventStatusPull = "on";
// Initialize current position
var currentPosition = startPosition;
roats(bodyComponent);
global.onTrack = true;
// Update function to be called every frame
function update(eventData) {
// Calculate the distance to move this frame
var distance = speed * eventData.getDeltaTime();
// Update current position
currentPosition = currentPosition.add(direction.uniformScale(distance));
// Check if the object has reached or passed the end position
if (currentPosition.distance(endPosition) < distance) {
EventStatusPull = "off";
}
// Set the object's position
if(EventStatusPull == "on"){
colliderObj.getTransform().setWorldPosition(currentPosition);
} else if(EventStatusPull == "off"){
if(global.onTrack == true){
currentPosition = transform1.getWorldPosition();
colliderObj.getTransform().setWorldPosition(currentPosition);
} else {
updateEvent.enabled = false;
}
}
}
// Register the update function
var updateEvent = script.createEvent("UpdateEvent");
updateEvent.bind(update);
}
function roats(bodyComponent){
// Define start and end positions
var colliderObj = bodyComponent.getSceneObject();
var transformto = colliderObj.getTransform();
var transformfrom = script.cameraNear.getTransform();
global.onRotate = true;
var rots;
// Update function to be called every frame
function update(eventData) {
if(global.onRotate == true){
rots = transformfrom.getWorldRotation();
transformto.setWorldRotation(transformfrom.getWorldRotation());
} else {
transformto.setWorldRotation(rots);
updateEventR.enabled = false;
}
}
// Register the update function
var updateEventR = script.createEvent("UpdateEvent");
updateEventR.bind(update);
}
function triggerPhysicsApplyForce(colliderObj1, physicsBody) {
//var body = getFallbackComponent(script.physicsApplyForceBody, "Physics.BodyComponent");
var physicsApplyForceMode = "Set Velocity";
if (!physicsBody) {
debugPrint("Physics Body must be set or present on SceneObject!");
return;
}
script.pushAudio.play(1);
var velocityPropName;
var forceMethodName;
var physicsApplyForceForceType = "Position";
switch (physicsApplyForceForceType) {
case "Position":
default:
velocityPropName = "velocity";
forceMethodName = "addForce";
break;
case "Rotation":
velocityPropName = "angularVelocity";
forceMethodName = "addTorque";
break;
}
// Get the default force value from script
var forceToApply = script.physicsApplyForceForceValue;
// Check object name to apply different force values
var objectName = colliderObj1.name.toLowerCase();
// Use custom force values from script inputs if available
if (objectName.indexOf("ballon") !== -1 || objectName.indexOf("balloon") !== -1) {
// Use custom balloon force from script input if available
if (script.balloonForceValue) {
forceToApply = script.balloonForceValue;
print("Applying custom balloon force to: " + colliderObj1.name + " - " + forceToApply);
}
} else if (objectName.indexOf("slice") !== -1 || objectName.indexOf("pizza") !== -1) {
// Use custom slice force from script input if available
if (script.sliceForceValue) {
forceToApply = script.sliceForceValue;
print("Applying custom slice force to: " + colliderObj1.name + " - " + forceToApply);
}
}
// Otherwise, use the default force value from script.physicsApplyForceForceValue
var magnitude;
switch (script.physicsApplyForceSpace) {
case "World":
default:
break;
case "Local to Object":
magnitude = forceToApply.length;
forceToApply = (script.physicsApplyForceObjectSpace || colliderObj1).getTransform().getWorldTransform().multiplyDirection(forceToApply).normalize().uniformScale(magnitude);
break;
}
switch (physicsApplyForceMode) {
case "Force":
physicsBody[forceMethodName](forceToApply, Physics.ForceMode.Force);
break;
case "Acceleration":
physicsBody[forceMethodName](forceToApply, Physics.ForceMode.Acceleration);
break;
case "Impulse":
default:
physicsBody[forceMethodName](forceToApply, Physics.ForceMode.Impulse);
break;
case "VelocityChange":
physicsBody[forceMethodName](forceToApply, Physics.ForceMode.VelocityChange);
break;
case "Set Velocity":
physicsBody[velocityPropName] = forceToApply;
break;
}
}
// Helper function to destroy an object directly
function destroyObjectDirectly(objectName, PA) {
print("Attempting to destroy object: " + objectName + " with PA: " + PA);
// Add debug logging to check if prefabAccessors exist
if (!script.prefabAccessorA) {
print("ERROR: prefabAccessorA is not set!");
}
if (!script.prefabAccessorB) {
print("ERROR: prefabAccessorB is not set!");
}
// Directly destroy the prefab instance without scaling
try {
// Check if global.destroyPrefab exists before calling it
if (typeof global.destroyPrefab !== 'function') {
print("ERROR: global.destroyPrefab is not a function!");
return;
}
global.destroyPrefab(objectName, PA);
print("Successfully called destroyPrefab for: " + objectName);
} catch (e) {
print("ERROR destroying object: " + e);
}
}
function pushin(){
global.onRotate = false;
global.onTrack = false;
if(!global.prevModel) {
return;
} else if(global.prevModel.getSceneObject()) {
var colliderObj1 = global.prevModel.getSceneObject();
var physicsBody = colliderObj1.getComponent("Physics.BodyComponent");
let nameObjPush = global.prevModel.getSceneObject().name;
print(nameObjPush);
if(script.forceEnergyManager.hasEnoughEnergy(nameObjPush)) {
script.forceEnergyManager.consumeEnergyCopy(nameObjPush);
switch (nameObjPush) {
case "R2D2":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
print("d");
});
delayedEvent.reset(4);
break;
case "crateB1":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
print("rest");
destroyObjectDirectly("cratesB1", 1);
});
delayedEvent.reset(4);
break;
case "crateB2":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesB2", 1);
});
delayedEvent.reset(4);
break;
case "crateXL":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesXL", 1);
});
delayedEvent.reset(4);
break;
case "crateM1":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM1", 1);
});
delayedEvent.reset(4);
break;
case "crateM2":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM2", 1);
});
delayedEvent.reset(4);
break;
case "crateM3":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM3", 1);
});
delayedEvent.reset(4);
break;
case "crateM4":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM4", 1);
});
delayedEvent.reset(4);
break;
case "crateB1a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesB1a", 2);
});
delayedEvent.reset(4);
break;
case "crateB2a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesB2a", 2);
});
delayedEvent.reset(4);
break;
case "crateXLa":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesXLa", 2);
});
delayedEvent.reset(4);
break;
case "crateM1a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM1a", 2);
});
delayedEvent.reset(4);
break;
case "crateM2a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM2a", 2);
});
delayedEvent.reset(4);
break;
case "crateM3a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM3a", 2);
});
delayedEvent.reset(4);
break;
case "crateM4a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("cratesM4a", 2);
});
delayedEvent.reset(4);
break;
// Handle balloon objects
case "BallonsM1":
case "ballonM1":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM1s", 1);
});
delayedEvent.reset(6); // Longer time before destruction
break;
case "BallonsM2":
case "ballonM2":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM2s", 1);
});
delayedEvent.reset(6);
break;
case "BallonsM3":
case "ballonM3":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM3s", 1);
});
delayedEvent.reset(6);
break;
case "BallonsM4":
case "ballonM4":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM4s", 1);
});
delayedEvent.reset(6);
break;
case "BallonsM5":
case "ballonM5":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM5s", 1);
});
delayedEvent.reset(6);
break;
case "BallonsM1a":
case "ballonM1a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM1as", 2);
});
delayedEvent.reset(6);
break;
case "BallonsM2a":
case "ballonM2a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM2as", 2);
});
delayedEvent.reset(6);
break;
case "BallonsM3a":
case "ballonM3a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM3as", 2);
});
delayedEvent.reset(6);
break;
case "BallonsM4a":
case "ballonM4a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM4as", 2);
});
delayedEvent.reset(6);
break;
case "BallonsM5a":
case "ballonM5a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("BallonsM5as", 2);
});
delayedEvent.reset(6);
break;
// Handle slice objects
case "SlicesP1":
case "sliceP1":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("SlicesP1s", 1);
});
delayedEvent.reset(6);
break;
case "SlicesP2":
case "sliceP2":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("SlicesP2s", 1);
});
delayedEvent.reset(6);
break;
case "SlicesP3":
case "sliceP3":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("SlicesP3s", 1);
});
delayedEvent.reset(6);
break;
case "SlicesP1a":
case "sliceP1a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("SlicesP1as", 2);
});
delayedEvent.reset(6);
break;
case "SlicesP2a":
case "sliceP2a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("SlicesP2as", 2);
});
delayedEvent.reset(6);
break;
case "SlicesP3a":
case "sliceP3a":
var delayedEvent = script.createEvent("DelayedCallbackEvent");
delayedEvent.bind(function(eventData) {
destroyObjectDirectly("SlicesP3as", 2);
});
delayedEvent.reset(6);
break;
default:
print("No matching animation found.");
}
physicsBody.dynamic = true;
physicsBody.massOrDensity = 5.0;
physicsBody.density = 5.0;
triggerPhysicsApplyForce(colliderObj1, physicsBody);
script.forceEnergyManager.objectPushed();
}
else if(!script.forceEnergyManager.hasEnoughEnergy(nameObjPush)) {
physicsBody.dynamic = true;
physicsBody.massOrDensity = 5.0;
physicsBody.density = 5.0;
script.forceEnergyManager.objectPushed();
}
} else {
print("failed");
}
}
script.api.pushin = pushin;
script.api.pull = pull;
// HEALTH MANAGER
// CharacterHealthManager.js
// Version: 1.0.6
// Event: Initialized
// Description: Manages the health of the player, reducing health when sabers hit opponent players
// Updated to work with player1/player2 objects instead of characters
//
// Version 1.0.5 Changes:
// - Added specific saber name detection for Player1Saber1, Player1Saber2, Player2Saber1, Player2Saber2
// - Set individual damage values for each saber (10 damage each)
// - Added collision cooldown to prevent spam damage from movement lag
// - Improved saber ownership detection to prevent self-collision
// - Added safety checks to prevent friendly fire damage
//
// Version 1.0.6 Changes:
// - Fixed health regeneration not updating on remote player's screen
// - Added network synchronization for regenerated health values
// - Separated regeneration events from damage events for better tracking
// - Enhanced safety checks in opponent hit handler to prevent self-collision
// - Added detailed logging for regeneration and collision events
//@input Component.Text healthTextOpp // Text component to display current opponent health
//@input Component.Text healthTextOur // Text component to display current health
//@input Component.Text healthTextOpp2 // Text component to display second opponent health
//@input Component.Image healthBarOpp // Optional visual bar representation
//@input Component.Image healthBarOur // Optional visual bar representation
//@input Component.Image healthBarOpp2 // Optional visual bar representation for second opponent
//@input float maxHealth = 100.0 // Maximum health
//@input SceneObject player1 // Reference to player1 object
//@input SceneObject player2 // Reference to player2 object
//@input SceneObject[] sabers // Reference to saber objects for collision detection
//@input bool enableInvulnerabilityFrames = true // Whether to enable temporary invulnerability after taking damage
//@input float invulnerabilityDuration = 1.0 // How long the character stays invulnerable after taking damage (in seconds)
//@input Component.AudioComponent damageSoundEffect // Sound to play when taking damage
//@input Component.AudioComponent deathSoundEffect // Sound to play when health reaches zero
//@input SceneObject gameOverScreen // Reference to game over UI to enable when health reaches zero
// Health regeneration settings
//@input bool enableHealthRegeneration = true // Whether health regeneration is enabled
//@input float regenerationDelay = 2.0 // Time in seconds before regeneration starts after taking damage
//@input float regenerationRate = 0.10 // Amount of health to regenerate per second
//@input Component.AudioComponent regenerationSoundEffect // Optional sound to play when regeneration begins
//@input Component.Text EndText
//@input SceneObject Hands
//@input SceneObject Bodys
//@input SceneObject MainScene
//@input SceneObject Saber1
//@input SceneObject Saber2
//@input SceneObject Saber3
//@input SceneObject Saber4
// Object damage amounts - how much damage each object deals on collision
var player1State;
var player2State;
var localPlayerNumber; // Track which player is local ("player1" or "player2")
var syncEntityPlayer1;
var syncEntityPlayer2;
var syncEntityDeath;
// Track last sent health values to avoid sending duplicate updates
var lastSentHealth = {
player1: -1,
player2: -1
};
// Track last displayed health values to avoid updating UI unnecessarily
var lastDisplayedHealth = {
our: -1,
opp: -1,
opp2: -1
};
// Function to check if an object is a saber
function isSaberObject(objectName) {
// Specific saber names (exact match required)
var specificSaberNames = [
"Player1Saber1", "Player1Saber2", "Player2Saber1", "Player2Saber2"
];
// Legacy saber names for backward compatibility
var legacySaberNames = [
"player1saber1", "player1saber2", "player2saber1", "player2saber2",
"saber1", "saber2", "saber3", "lightsaber", "Saber", "Lightsaber",
"OurSaber1", "OurSaber2", "OppSaber1", "OppSaber2"
];
// First check for exact matches with specific saber names
for (var i = 0; i < specificSaberNames.length; i++) {
if (objectName === specificSaberNames[i]) {
return true;
}
}
// Then check for exact matches with legacy saber names
for (var j = 0; j < legacySaberNames.length; j++) {
if (objectName === legacySaberNames[j]) {
return true;
}
}
// Finally check if the object name contains "saber" (case insensitive) - but be more restrictive
var lowerName = objectName.toLowerCase();
if (lowerName.indexOf("saber") !== -1 || lowerName.indexOf("lightsaber") !== -1) {
// Additional check to prevent false positives - make sure it's not just any object with "saber" in the name
return true;
}
return false;
}
// Function to determine if a saber belongs to the local player
function isLocalPlayerSaber(objectName) {
// print("Checking if saber is local: " + objectName + " (Local player: " + localPlayerNumber + ")");
if (!localPlayerNumber) {
// print("Warning: localPlayerNumber not set, cannot determine saber ownership");
return false;
}
var isLocal = false;
// Direct name-based check for specific saber names
if (localPlayerNumber === "player1") {
isLocal = (objectName === "Player1Saber1" || objectName === "Player1Saber2" ||
objectName === "player1saber1" || objectName === "player1saber2");
// print("Player1 direct name check - matches: " + isLocal);
} else if (localPlayerNumber === "player2") {
isLocal = (objectName === "Player2Saber1" || objectName === "Player2Saber2" ||
objectName === "player2saber1" || objectName === "player2saber2");
// print("Player2 direct name check - matches: " + isLocal);
}
// If direct name check didn't work, fall back to parent-based check
if (!isLocal) {
var sceneObject = null;
// Try to get the scene object by name
try {
sceneObject = global.scene.getSceneObject(objectName);
} catch (e) {
// print("Warning: Could not find scene object: " + objectName);
return false;
}
if (sceneObject) {
// Get the parent object
var parent = sceneObject.getParent();
if (parent) {
var parentName = parent.name.toLowerCase();
// print("Parent object name: " + parentName);
// Check if the parent name matches the local player
if (localPlayerNumber === "player1") {
isLocal = parentName.indexOf("player1") !== -1;
// print("Player1 parent check - matches: " + isLocal);
} else if (localPlayerNumber === "player2") {
isLocal = parentName.indexOf("player2") !== -1;
// print("Player2 parent check - matches: " + isLocal);
}
}
}
}
// print("Final result: " + isLocal);
return isLocal;
}
// Function to determine if a player object is the local player
function isLocalPlayer(objectName) {
if (!localPlayerNumber) {
// print("Warning: localPlayerNumber not set, cannot determine player ownership");
return false;
}
return objectName === localPlayerNumber;
}
// Function to get the owner of a saber based on its name
function getSaberOwner(saberName) {
// Check specific saber names first
if (saberName === "Player1Saber1" || saberName === "Player1Saber2" ||
saberName === "player1saber1" || saberName === "player1saber2") {
return "player1";
}
if (saberName === "Player2Saber1" || saberName === "Player2Saber2" ||
saberName === "player2saber1" || saberName === "player2saber2") {
return "player2";
}
// Check for generic saber names by examining the name pattern
var lowerName = saberName.toLowerCase();
if (lowerName.indexOf("player1") !== -1) {
return "player1";
} else if (lowerName.indexOf("player2") !== -1) {
return "player2";
}
// If no clear owner can be determined, return null
return null;
}
var objectDamageAmounts = {
// Small objects (low damage)
"crateB1": 5,
"crateB2": 5,
"crateM1": 7,
"crateM2": 7,
"crateM3": 7,
"crateM4": 7,
"crateB1a": 5,
"crateB2a": 5,
"crateM1a": 7,
"crateM2a": 7,
"crateM3a": 7,
"crateM4a": 7,
"Object_15": 5,
"mesh_0_002": 5,
// Add Ballons objects (low damage like crateM)
"BallonsM1": 7,
"BallonsM2": 7,
"BallonsM3": 7,
"BallonsM4": 7,
"BallonsM5": 7,
"BallonsM1a": 7,
"BallonsM2a": 7,
"BallonsM3a": 7,
"BallonsM4a": 7,
"BallonsM5a": 7,
"ballonM1": 7,
"ballonM2": 7,
"ballonM3": 7,
"ballonM4": 7,
"ballonM5": 7,
// Medium objects (medium damage)
"crateXL": 15,
"crateXLa": 15,
"R2D2": 20,
// Add Slices objects (medium damage like crateXL)
"SlicesP1": 15,
"SlicesP2": 15,
"SlicesP3": 15,
"SlicesP1a": 15,
"SlicesP2a": 15,
"SlicesP3a": 15,
"sliceP1": 15,
"sliceP2": 15,
"sliceP3": 15,
// Large objects (high damage)
"AT-STtes": 30,
"tieCube": 25,
"xwing_sfoil3": 25,
"millennium_falcon": 40,
// Specific saber damage (individual damage values)
"Player1Saber1": 10,
"Player1Saber2": 10,
"Player2Saber1": 10,
"Player2Saber2": 10,
// Legacy saber names (for backward compatibility)
"player1saber1": 10,
"player1saber2": 10,
"player2saber1": 10,
"player2saber2": 10,
"saber1": 10,
"saber2": 10,
"saber3": 10,
"lightsaber": 10
};
// Default damage if object is not in the list
var defaultDamage = 10;
// Initialize the current health
var currentHealth = script.maxHealth;
// Track opponent's health locally (for attacking player)
var opponentHealth = script.maxHealth;
// Flag to track if character is currently invulnerable
var isInvulnerable = false;
// Health regeneration variables
var isRegenerating = false;
var regenerationEvent = null;
var regenerationUpdateEvent = null;
// Collision cooldown to prevent spam damage from movement lag
var lastCollisionTime = {};
var collisionCooldown = 0.5; // 0.5 seconds between damage from same object
const textureControlOppHealth = script.healthBarOpp.getMaterial(0).getPass(0).baseTex.control;
const textureControlOurHealth = script.healthBarOur.getMaterial(0).getPass(0).baseTex.control;
const textureControlOpp2Health = script.healthBarOpp2.getMaterial(0).getPass(0).baseTex.control;
var ReceivedHealth;
var ReceivedHealthOpp2;
// Global variables to track timing
let lastUpdateTime = 0;
const updateInterval = 0.5; // Reduced to 0.5 second interval for more responsive updates
// Update the UI display of health and send to other players if changed
function updateHealthDisplay(forceUpdate) {
// Get current time
const currentTime = getTime();
// Ensure health is never below 0 for display purposes
var displayHealth = Math.max(0, Math.floor(currentHealth));
// Check if interval has passed since last update, or if we're forcing an update
if (forceUpdate || currentTime - lastUpdateTime >= updateInterval) {
// Update the last update time
lastUpdateTime = currentTime;
// Execute health display logic - only update local player's own health display
if (script.healthBarOur) {
let localPlayerHealth = displayHealth;
// Update our own health display (healthTextOur + healthBarOur)
if (forceUpdate || lastDisplayedHealth.our !== localPlayerHealth) {
script.healthTextOur.text = "Health: " + localPlayerHealth + "/" + script.maxHealth;
var frameIndex = Math.max(0, localPlayerHealth - 1);
textureControlOurHealth.pauseAtFrame(frameIndex);
lastDisplayedHealth.our = localPlayerHealth;
// print(localPlayerNumber + " updated own health display: " + localPlayerHealth);
}
}
}
}
// Legacy function - kept for backward compatibility but no longer used
function updateWithDelayPlayer() {
// print("updateWithDelayPlayer called - this function is deprecated in the new health system");
}
// Apply damage to a specific player and handle health synchronization
// NEW LOGIC: Attacking player reduces opponent's health locally first, then sends the new health
function applyDamageToPlayer(targetPlayer, damageAmount, objectName, impactVelocity) {
// print("=== APPLYING DAMAGE ===");
// print("Target Player: " + targetPlayer);
// print("Damage Amount: " + damageAmount);
// print("Caused by: " + objectName);
// print("Local Player: " + localPlayerNumber);
// Determine if we're damaging the local player or opponent
var isDamagingLocalPlayer = (targetPlayer === localPlayerNumber);
// Check if the damage is caused by a balloon or slice
var objectNameLower = objectName.toLowerCase();
var hitAnimationType = null;
if (objectNameLower.indexOf("ballon") !== -1) {
hitAnimationType = "balloon";
// If we're being hit, play animation locally
if (isDamagingLocalPlayer && global.playBalloonHitAnimation) {
// print("Playing balloon hit animation locally (we were hit)");
global.playBalloonHitAnimation();
}
// If we're hitting opponent, send message to opponent to play animation
else if (!isDamagingLocalPlayer) {
// print("Sending balloon hit animation message to opponent");
sendHitAnimationToOpponent("balloon");
}
} else if (objectNameLower.indexOf("slice") !== -1) {
hitAnimationType = "slice";
// If we're being hit, play animation locally
if (isDamagingLocalPlayer && global.playSliceHitAnimation) {
// print("Playing slice hit animation locally (we were hit)");
global.playSliceHitAnimation();
}
// If we're hitting opponent, send message to opponent to play animation
else if (!isDamagingLocalPlayer) {
// print("Sending slice hit animation message to opponent");
sendHitAnimationToOpponent("slice");
}
}
if (isDamagingLocalPlayer) {
// We're being damaged by opponent - apply damage locally
// print("Local player taking damage");
applyLocalDamage(damageAmount, objectName, impactVelocity);
} else {
// We're attacking the opponent - reduce their health locally, then send the new health
// print("Local player attacking opponent - reducing opponent health locally");
applyDamageToOpponent(damageAmount, objectName, impactVelocity);
}
}
// Function to send hit animation message to opponent
function sendHitAnimationToOpponent(animationType) {
// print("🚀 SENDING HIT ANIMATION TO OPPONENT: " + animationType);
// print("🔍 Local Player: " + localPlayerNumber);
var animationData = {
type: animationType,
targetPlayer: localPlayerNumber === "player1" ? "player2" : "player1" // Explicitly specify target
};
// print("📦 Animation Data: " + JSON.stringify(animationData));
// Send animation event based on local player - use direct event sending
if (localPlayerNumber === "player1" && syncEntityPlayer1) {
// If we're player1, send to player2 to play animation
syncEntityPlayer1.sendEvent('PlayHitAnimationOnOpponent', animationData, true);
// print("✅ Sent hit animation event via Player1 sync to player2");
} else if (localPlayerNumber === "player2" && syncEntityPlayer2) {
// If we're player2, send to player1 to play animation
syncEntityPlayer2.sendEvent('PlayHitAnimationOnOpponent', animationData, true);
// print("✅ Sent hit animation event via Player2 sync to player1");
} else {
// print("❌ Error: Could not send hit animation event - incorrect sync entity configuration");
// print(" Local player: " + localPlayerNumber);
// print(" Player1 sync available: " + (syncEntityPlayer1 ? "YES" : "NO"));
// print(" Player2 sync available: " + (syncEntityPlayer2 ? "YES" : "NO"));
}
}
// Apply damage to the local player
function applyLocalDamage(damageAmount, objectName, impactVelocity) {
// If character is invulnerable, ignore damage
if (isInvulnerable) {
// print("Local player is invulnerable - ignoring damage");
return;
}
// Use the passed damage amount directly (already calculated)
var finalDamageAmount = damageAmount;
// Factor in velocity to increase damage for faster moving objects (optional)
var velocityFactor = impactVelocity / 10; // Adjust divisor to balance impact
velocityFactor = Math.min(Math.max(velocityFactor, 0.5), 2.0); // Clamp between 0.5x and 2x
// Calculate final damage
var finalDamage = finalDamageAmount * velocityFactor;
// Store previous health to check if anything changed
var previousHealth = currentHealth;
// Apply damage
currentHealth = Math.max(0, currentHealth - finalDamage);
// Only proceed if damage actually changed the health
if (currentHealth < previousHealth) {
// print("Local player took " + finalDamage.toFixed(1) + " damage from " + objectName);
// print("Health: " + previousHealth + " -> " + currentHealth);
// Force immediate health display update since damage was taken
updateHealthDisplay(true);
// Play damage sound effect
if (script.damageSoundEffect) {
script.damageSoundEffect.play(1);
}
// Apply screen shake or visual feedback
applyDamageVisualEffects(finalDamage);
// Reset health regeneration timer when taking damage
stopRegeneration();
// Schedule regeneration if enabled
if (script.enableHealthRegeneration && currentHealth > 0 && currentHealth < script.maxHealth) {
scheduleRegeneration();
}
// Check if character died
if (currentHealth <= 0) {
handleCharacterDeath();
} else if (script.enableInvulnerabilityFrames) {
// Apply invulnerability frames
setInvulnerable(true);
var invulnerabilityEvent = script.createEvent("DelayedCallbackEvent");
invulnerabilityEvent.bind(function() {
setInvulnerable(false);
});
invulnerabilityEvent.reset(script.invulnerabilityDuration);
}
} else {
// print("No health change - damage may have been blocked or health already at minimum");
}
}
// Apply damage to opponent locally and send the new health value
function applyDamageToOpponent(damageAmount, objectName, impactVelocity) {
// print("Applying damage to opponent locally");
// Factor in velocity to increase damage for faster moving objects (optional)
var velocityFactor = impactVelocity / 10; // Adjust divisor to balance impact
velocityFactor = Math.min(Math.max(velocityFactor, 0.5), 2.0); // Clamp between 0.5x and 2x
// Calculate final damage
var finalDamage = damageAmount * velocityFactor;
// Store previous opponent health
var previousOpponentHealth = opponentHealth;
// Apply damage to opponent health (tracked locally)
opponentHealth = Math.max(0, opponentHealth - finalDamage);
// print("Opponent health: " + previousOpponentHealth + " -> " + opponentHealth + " (damage: " + finalDamage.toFixed(1) + ")");
// Update opponent's health display locally
updateOpponentHealthDisplay();
// Send the new health value to the opponent
sendHealthToOpponent(opponentHealth);
// Check if opponent died
if (opponentHealth <= 0) {
// print("Opponent has been defeated!");
handleOpponentDeath();
}
}
// Send opponent's new health value to them
function sendHealthToOpponent(newHealth) {
// print("Sending opponent's new health: " + newHealth);
var healthData = {
newHealth: newHealth,
isDirectHealthSet: true
};
// Send health update based on local player
if (localPlayerNumber === "player1" && syncEntityPlayer1) {
syncEntityPlayer1.sendEvent('HealthSetEvent', healthData, true);
// print("Sent health set event via Player1 sync");
} else if (localPlayerNumber === "player2" && syncEntityPlayer2) {
syncEntityPlayer2.sendEvent('HealthSetEvent', healthData, true);
// print("Sent health set event via Player2 sync");
} else {
// print("Error: Could not send health set event - sync entity not available");
}
}
// Send regenerated health to opponent (different from damage-based health updates)
function sendRegeneratedHealthToOpponent(newHealth) {
// print("🔋 Sending regenerated health to opponent: " + newHealth);
var healthData = {
newHealth: newHealth,
isDirectHealthSet: true,
isRegeneration: true // Flag to indicate this is regeneration, not damage
};
// Send health update based on local player
if (localPlayerNumber === "player1" && syncEntityPlayer1) {
syncEntityPlayer1.sendEvent('HealthSetEvent', healthData, true);
// print("🔋 Sent regeneration health event via Player1 sync");
} else if (localPlayerNumber === "player2" && syncEntityPlayer2) {
syncEntityPlayer2.sendEvent('HealthSetEvent', healthData, true);
// print("🔋 Sent regeneration health event via Player2 sync");
} else {
// print("❌ Error: Could not send regeneration health event - sync entity not available");
// print(" Local player: " + localPlayerNumber);
// print(" Player1 sync available: " + (syncEntityPlayer1 ? "YES" : "NO"));
// print(" Player2 sync available: " + (syncEntityPlayer2 ? "YES" : "NO"));
}
}
// Set local health directly (when opponent has reduced our health)
function setLocalHealthDirectly(newHealth) {
var previousHealth = currentHealth;
currentHealth = Math.max(0, Math.min(script.maxHealth, newHealth));
// print("Health set directly: " + previousHealth + " -> " + currentHealth);
// Update local health display
updateHealthDisplay(true);
// Apply visual effects for taking damage
if (currentHealth < previousHealth) {
var damageAmount = previousHealth - currentHealth;
applyDamageVisualEffects(damageAmount);
// Play damage sound effect
if (script.damageSoundEffect) {
script.damageSoundEffect.play(1);
}
// Reset health regeneration timer when taking damage
stopRegeneration();
// Schedule regeneration if enabled
if (script.enableHealthRegeneration && currentHealth > 0 && currentHealth < script.maxHealth) {
scheduleRegeneration();
}
// Apply invulnerability frames
if (script.enableInvulnerabilityFrames && currentHealth > 0) {
setInvulnerable(true);
var invulnerabilityEvent = script.createEvent("DelayedCallbackEvent");
invulnerabilityEvent.bind(function() {
setInvulnerable(false);
});
invulnerabilityEvent.reset(script.invulnerabilityDuration);
}
}
// Check if we died
if (currentHealth <= 0) {
handleLocalPlayerDeath();
}
}
// Handle local player death
function handleLocalPlayerDeath() {
// print("Local player has died!");
// Stop regeneration if it's running
stopRegeneration();
// Play death sound
if (script.deathSoundEffect) {
script.deathSoundEffect.play(1);
}
// Show defeat message
if (script.EndText) {
script.EndText.text = "You Lost";
}
// Show game over screen
if (script.gameOverScreen) {
script.gameOverScreen.enabled = true;
}
}
// Update opponent health display locally
function updateOpponentHealthDisplay() {
var displayHealth = Math.max(0, Math.floor(opponentHealth));
// print("=== updateOpponentHealthDisplay ===");
// print("displayHealth: " + displayHealth);
// print("localPlayerNumber: " + localPlayerNumber);
// print("lastDisplayedHealth.opp: " + lastDisplayedHealth.opp);
// print("lastDisplayedHealth.opp2: " + lastDisplayedHealth.opp2);
// print("script.healthTextOpp exists: " + (script.healthTextOpp ? "yes" : "no"));
// print("script.healthTextOpp2 exists: " + (script.healthTextOpp2 ? "yes" : "no"));
// BOTH players should update BOTH text components to ensure visibility
// Update primary opponent text (healthTextOpp)
if (lastDisplayedHealth.opp !== displayHealth) {
// print("Updating primary opponent text to: Health: " + displayHealth + "/" + script.maxHealth);
if (script.healthTextOpp) {
script.healthTextOpp.text = "Health: " + displayHealth + "/" + script.maxHealth;
// print("Primary opponent text updated successfully");
} else {
// print("ERROR: script.healthTextOpp is null/undefined!");
}
var frameIndex = Math.max(0, displayHealth - 1);
textureControlOppHealth.pauseAtFrame(frameIndex);
lastDisplayedHealth.opp = displayHealth;
}
// Update secondary opponent text (healthTextOpp2)
if (lastDisplayedHealth.opp2 !== displayHealth) {
// print("Updating secondary opponent text to: Health: " + displayHealth + "/" + script.maxHealth);
if (script.healthTextOpp2) {
script.healthTextOpp2.text = "Health: " + displayHealth + "/" + script.maxHealth;
// print("Secondary opponent text updated successfully");
} else {
// print("ERROR: script.healthTextOpp2 is null/undefined!");
}
var frameIndex2 = Math.max(0, displayHealth - 1);
textureControlOpp2Health.pauseAtFrame(frameIndex2);
lastDisplayedHealth.opp2 = displayHealth;
}
// print("Updated opponent health display: " + displayHealth + " (Player: " + localPlayerNumber + ")");
}
// Handle opponent death
function handleOpponentDeath() {
// print("Opponent has died - local player wins!");
// Play victory sound or effects here
if (script.deathSoundEffect) {
script.deathSoundEffect.play(1);
}
// Show victory message
if (script.EndText) {
script.EndText.text = "You Won";
}
// Show game over screen
if (script.gameOverScreen) {
script.gameOverScreen.enabled = true;
}
}
// Set character invulnerability state
function setInvulnerable(state) {
isInvulnerable = state;
}
// Schedule health regeneration
function scheduleRegeneration() {
// Cancel any existing regeneration
stopRegeneration();
// Schedule regeneration to start after delay
regenerationEvent = script.createEvent("DelayedCallbackEvent");
regenerationEvent.bind(function() {
startRegeneration();
});
regenerationEvent.reset(script.regenerationDelay);
}
// Start the actual health regeneration
function startRegeneration() {
if (isRegenerating || currentHealth >= script.maxHealth || currentHealth <= 0) {
return;
}
isRegenerating = true;
// Play regeneration sound if available
if (script.regenerationSoundEffect) {
script.regenerationSoundEffect.play(1);
}
// Visual indicator that regeneration has started
// print("Health regeneration started");
// Create an update event to regenerate health gradually
regenerationUpdateEvent = script.createEvent("UpdateEvent");
regenerationUpdateEvent.bind(function(eventData) {
// Calculate how much health to add this frame
var healthToAdd = script.regenerationRate * eventData.getDeltaTime();
// Previous health value before adding
var previousHealth = currentHealth;
// Add health
currentHealth = Math.min(script.maxHealth, currentHealth + healthToAdd);
// Only update if health changed by at least 1 point
if (Math.floor(previousHealth) < Math.floor(currentHealth)) {
// print("🔋 HEALTH REGENERATION UPDATE:");
// print(" Previous: " + Math.floor(previousHealth));
// print(" Current: " + Math.floor(currentHealth));
// print(" Player: " + localPlayerNumber);
// Force an immediate health display update
updateHealthDisplay(true);
// Send regenerated health to opponent so they can see the update
sendRegeneratedHealthToOpponent(Math.floor(currentHealth));
}
// Check if we've reached max health
if (previousHealth < script.maxHealth && currentHealth >= script.maxHealth) {
// print("🔋 HEALTH REGENERATION COMPLETE - Full health reached");
// Force immediate update when reaching max health
updateHealthDisplay(true);
// Send final health value to opponent
sendRegeneratedHealthToOpponent(script.maxHealth);
stopRegeneration();
} else if (currentHealth >= script.maxHealth) {
// Stop regeneration if health is full
stopRegeneration();
}
});
}
// Stop health regeneration
function stopRegeneration() {
isRegenerating = false;
// Cancel the scheduled regeneration if it exists
if (regenerationEvent) {
regenerationEvent.enabled = false;
regenerationEvent = null;
}
// Cancel the update event if it exists
if (regenerationUpdateEvent) {
regenerationUpdateEvent.enabled = false;
regenerationUpdateEvent = null;
}
}
// Handle character death with proper player-specific logic
function handleCharacterDeath() {
// print("Character has died!");
// Stop regeneration if it's running
stopRegeneration();
// Play death sound
if (script.deathSoundEffect) {
script.deathSoundEffect.play(1);
}
// Show game over screen
if (script.gameOverScreen) {
script.gameOverScreen.enabled = true;
}
// Get correct health values, ensuring they're numeric
var ourHealthValue = parseInt(ReceivedHealth) || 0;
var oppHealthValue = Math.floor(currentHealth) || 0;
var opp2HealthValue = parseInt(ReceivedHealthOpp2) || 0;
// Prevent multiple executions
if (typeof handleCharacterDeath.hasExecuted === 'undefined') {
handleCharacterDeath.hasExecuted = true;
// Debug output to help understand the state
// print("Death handler - Our health: " + ourHealthValue +
// ", Opp health: " + oppHealthValue +
// ", Opp2 health: " + opp2HealthValue +
// ", Player1State: " + player1State +
// ", Player2State: " + player2State);
// Player-specific win/loss determination
if (player1State === true) {
// Player 1 specific logic
if (oppHealthValue <= 0) {
// Player 1's opponent (Player 2) has zero health, Player 1 wins
script.EndText.text = "You Won";
// print("Player 1 wins - opponent health is " + oppHealthValue);
} else if (ourHealthValue <= 0) {
// Player 1's health is zero, Player 1 loses
script.EndText.text = "You Lost";
// print("Player 1 loses - own health is " + ourHealthValue);
}
} else if (player2State === true) {
// Player 2 specific logic
if (oppHealthValue <= 0) {
// Player 2's opponent (Player 1) has zero health, Player 2 wins
script.EndText.text = "You Won";
// print("Player 2 wins - opponent health is " + oppHealthValue);
} else if (ourHealthValue <= 0) {
// Player 2's health is zero, Player 2 loses
script.EndText.text = "You Lost";
// print("Player 2 loses - own health is " + ourHealthValue);
}
}
// Always send death event regardless of who died
syncEntityDeath.sendEvent('Death', {
player1Health: player1State ? ourHealthValue : oppHealthValue,
player2Health: player2State ? ourHealthValue : oppHealthValue
}, true);
// Disable game elements
script.MainScene.enabled = false;
script.Hands.enabled = false;
script.Bodys.enabled = false;
script.Saber1.enabled = false;
script.Saber2.enabled = false;
script.Saber3.enabled = false;
script.Saber4.enabled = false;
}
}
// Apply visual effects when taking damage
function applyDamageVisualEffects(damageAmount) {
// Implement screen shake, flash, or other effects
// This is just a stub - implement specific effects as needed
var damageEffect = script.createEvent("DelayedCallbackEvent");
damageEffect.bind(function() {
// Reset any visual effects
});
damageEffect.reset(0.3); // Brief effect duration
}
// Handle collision event - could be connected to a collision event or called from other scripts
function onCollisionHandler(eventData) {
// print("=== COLLISION HANDLER CALLED ===");
// print("Event data structure: " + JSON.stringify(Object.keys(eventData)));
// Debug: Print all available properties
for (var key in eventData) {
// print("EventData." + key + ": " + (typeof eventData[key]));
}
// Handle different collision event structures
var collidingObject = null;
var hitTarget = null;
// Try different collision event formats
if (eventData.collision) {
// print("Using eventData.collision format");
collidingObject = eventData.collision.collider;
hitTarget = eventData.collision.otherCollider;
} else if (eventData.collider) {
// print("Using direct eventData.collider format");
collidingObject = eventData.collider;
hitTarget = eventData.otherCollider;
} else if (eventData.other) {
// print("Using eventData.other format");
collidingObject = eventData.collider;
hitTarget = eventData.other;
}
// print("Colliding object exists: " + (collidingObject ? "YES" : "NO"));
// print("Hit target exists: " + (hitTarget ? "YES" : "NO"));
// If we still don't have hitTarget, try alternative approaches
if (collidingObject && !hitTarget) {
// print("Trying alternative collision detection...");
// In some collision systems, we might need to determine the hit target differently
// For now, let's assume any collision with a player object is valid
var objectName = collidingObject.getSceneObject().name;
// print("Colliding object name: " + objectName);
// If this is a saber hitting something, we need to determine what it hit
if (isSaberObject(objectName)) {
// print("Saber collision detected, but hit target unknown");
// For now, let's skip this collision since we can't determine what was hit
return;
}
}
if (collidingObject && hitTarget) {
var objectName = collidingObject.getSceneObject().name;
var hitTargetName = hitTarget.getSceneObject().name;
// print("==================== COLLISION DETAILS ====================");
// print("🎯 COLLISION: " + objectName + " hit " + hitTargetName);
// print("📍 Local player: " + localPlayerNumber);
// print("⚔️ Colliding object type: " + (isSaberObject(objectName) ? "SABER" : "ENVIRONMENTAL"));
// print("🎭 Hit target type: " + (hitTargetName.indexOf("player") !== -1 ? "PLAYER" : "OTHER"));
if (isSaberObject(objectName)) {
var saberOwner = getSaberOwner(objectName);
// print("👤 Saber owner: " + saberOwner);
// print("🏠 Is local saber: " + isLocalPlayerSaber(objectName));
}
// print("========================================================");
// Get collision velocity
var collisionVelocity = 10.0; // Default if not available
var sceneObject = collidingObject.getSceneObject();
var physicsBody = sceneObject.getComponent("Physics.BodyComponent");
if (physicsBody) {
// collisionVelocity = physicsBody.velocity.length();
}
// Determine which player was hit
var hitPlayer = null;
// print("🔍 ANALYZING HIT TARGET:");
// print(" Target name: " + hitTargetName);
// print(" Player1 object: " + (script.player1 ? script.player1.name : "NOT ASSIGNED"));
// print(" Player2 object: " + (script.player2 ? script.player2.name : "NOT ASSIGNED"));
if (script.player1 && (hitTargetName === script.player1.name || hitTargetName.indexOf("player1") === 0)) {
hitPlayer = "player1";
// print("✅ Hit target identified as: PLAYER1");
} else if (script.player2 && (hitTargetName === script.player2.name || hitTargetName.indexOf("player2") === 0)) {
hitPlayer = "player2";
// print("✅ Hit target identified as: PLAYER2");
}
if (!hitPlayer) {
// print("❌ Hit target is NOT a player object: " + hitTargetName);
// print(" Available players: " + (script.player1 ? script.player1.name : "none") + ", " + (script.player2 ? script.player2.name : "none"));
// print(" This collision will be IGNORED");
return;
}
// print("🎯 FINAL RESULT: " + objectName + " hit " + hitPlayer);
// print(" Local player: " + localPlayerNumber);
// print(" Is hitting opponent: " + (hitPlayer !== localPlayerNumber));
// Check if the colliding object is a saber or environmental object
var damageAmount = 0;
var shouldApplyDamage = false;
if (isSaberObject(objectName)) {
// print("⚔️ SABER COLLISION ANALYSIS:");
// print(" Saber: " + objectName + " hit " + hitPlayer);
// Additional safety check: prevent collision if saber belongs to same player as target
var saberOwner = getSaberOwner(objectName);
// print(" 👤 Saber owner: " + saberOwner);
// print(" 🎯 Target player: " + hitPlayer);
// Prevent self-collision (saber hitting its own owner)
if (saberOwner === hitPlayer) {
// print(" 🛡️ SELF-COLLISION DETECTED - BLOCKING!");
// print(" ❌ " + objectName + " belongs to " + hitPlayer + " - preventing friendly fire");
return;
}
// Check if this saber belongs to the local player
var isLocalSaber = isLocalPlayerSaber(objectName);
// print(" 🏠 Is local player's saber: " + isLocalSaber);
// print(" 📱 Local player: " + localPlayerNumber);
// print(" 🎯 Target is opponent: " + (hitPlayer !== localPlayerNumber));
var shouldApplyDamageCheck = (isLocalSaber && hitPlayer !== localPlayerNumber);
// print(" 💥 Should apply damage: " + shouldApplyDamageCheck);
// Only apply damage if local player's saber hits opponent
if (isLocalSaber && hitPlayer !== localPlayerNumber) {
damageAmount = objectDamageAmounts[objectName] || 10;
shouldApplyDamage = true;
// print(" ✅ LOCAL SABER HIT OPPONENT - applying " + damageAmount + " damage");
} else if (isLocalSaber && hitPlayer === localPlayerNumber) {
// print(" ❌ LOCAL SABER HIT LOCAL PLAYER - no friendly fire damage");
} else if (!isLocalSaber) {
// print(" ⏸️ REMOTE SABER COLLISION - damage handled by remote player");
}
}
// Environmental objects (crates, etc.) can damage any player they hit
else if (objectDamageAmounts[objectName]) {
// print("🏗️ ENVIRONMENTAL COLLISION:");
// print(" Object: " + objectName);
// print(" Hit player: " + hitPlayer);
damageAmount = objectDamageAmounts[objectName];
shouldApplyDamage = true;
// print(" ✅ ENVIRONMENTAL DAMAGE - applying " + damageAmount + " damage");
} else {
// print("🤷 UNKNOWN OBJECT COLLISION:");
// print(" Object: " + objectName + " (not in damage list)");
// print(" Hit player: " + hitPlayer);
// print(" ❌ NO DAMAGE - object not configured for damage");
}
// Apply damage if conditions are met
if (shouldApplyDamage && damageAmount > 0) {
// Check collision cooldown to prevent spam damage from movement lag
var currentTime = getTime();
var collisionKey = objectName + "_" + hitPlayer;
if (lastCollisionTime[collisionKey] &&
(currentTime - lastCollisionTime[collisionKey]) < collisionCooldown) {
// print("⏰ COLLISION COOLDOWN ACTIVE:");
// print(" 🔄 Ignoring damage from " + objectName + " to " + hitPlayer);
// print(" ⏱️ Last collision: " + (currentTime - lastCollisionTime[collisionKey]).toFixed(2) + "s ago");
// print(" ⏳ Cooldown time: " + collisionCooldown + "s");
// print(" ❌ DAMAGE BLOCKED BY COOLDOWN");
return;
}
// Update last collision time
lastCollisionTime[collisionKey] = currentTime;
// print("💥 DAMAGE BEING APPLIED:");
// print(" 🎯 Object: " + objectName);
// print(" 👤 Target: " + hitPlayer);
// print(" 💔 Damage: " + damageAmount);
// print(" 🏃 Velocity: " + collisionVelocity);
// print(" ⏰ Time: " + currentTime.toFixed(2) + "s");
// print(" ✅ ALL CONDITIONS MET - APPLYING DAMAGE!");
// Apply damage to the hit player
applyDamageToPlayer(hitPlayer, damageAmount, objectName, collisionVelocity);
// Play damage sound effect
if (script.damageSoundEffect) {
script.damageSoundEffect.play(1);
}
} else {
// print("❌ CONDITIONS NOT MET - NO DAMAGE APPLIED:");
// print(" 🎯 Object: " + objectName);
// print(" 👤 Target: " + hitPlayer);
// print(" ✅ Should apply: " + shouldApplyDamage);
// print(" 💔 Damage amount: " + damageAmount);
// print(" 🚫 COLLISION IGNORED");
}
// print("================== END COLLISION ==================");
}
}
// Initialize
script.initialize = function(player1Statein, player2Statein, localPlayerNumberIn) {
print("=== HEALTH MANAGER INITIALIZATION ===");
print("Player1 State: " + player1Statein);
print("Player2 State: " + player2Statein);
print("Local Player Number: " + localPlayerNumberIn);
// Initialize health bars to full health
textureControlOurHealth.pauseAtFrame(100-1);
script.healthTextOur.text = "Health: " + "100" + "/" + script.maxHealth;
textureControlOppHealth.pauseAtFrame(100-1);
script.healthTextOpp.text = "Health: " + "100" + "/" + script.maxHealth;
textureControlOpp2Health.pauseAtFrame(100-1);
script.healthTextOpp2.text = "Health: " + "100" + "/" + script.maxHealth;
// Set last displayed health values to initial values
lastDisplayedHealth.our = 100;
lastDisplayedHealth.opp = 100;
lastDisplayedHealth.opp2 = 100;
print("Health UI initialized");
function onUpdate() {
// Check if we have enough players
if (global.sessionController.getUsers().length == 2) {
player1State = player1Statein;
player2State = player2Statein;
localPlayerNumber = localPlayerNumberIn;
print("Health Manager initialized with local player: " + localPlayerNumber);
// Create the SyncEntities if they don't exist yet
if (!syncEntityPlayer1) {
syncEntityPlayer1 = new SyncEntity(script, null, true);
syncEntityPlayer1.notifyOnReady(function() {
print("SyncEntity health player1 is ready");
});
// Legacy event handlers - replaced by new health system
syncEntityPlayer1.onEventReceived.add('Player1OppHealth', function(messageInfo) {
print("Legacy Player1OppHealth event received - using new health system instead");
});
syncEntityPlayer1.onEventReceived.add('Player1Opp2Health', function(messageInfo) {
print("Legacy Player1Opp2Health event received - using new health system instead");
});
// Handle incoming health set events (when opponent reduces our health or regenerates)
syncEntityPlayer1.onEventReceived.add('HealthSetEvent', function(messageInfo) {
var healthData = messageInfo.data;
print("Received health set event: " + JSON.stringify(healthData));
if (healthData.isDirectHealthSet) {
if (healthData.isRegeneration) {
// print("🔋 Received opponent's regenerated health: " + healthData.newHealth);
// Update the opponent's health display locally
opponentHealth = healthData.newHealth;
updateOpponentHealthDisplay();
} else {
// print("💔 Setting local health directly to: " + healthData.newHealth);
setLocalHealthDirectly(healthData.newHealth);
}
}
});
// Legacy health update handler - no longer used in new system
syncEntityPlayer1.onEventReceived.add('Player2HealthUpdate', function(messageInfo) {
print("Legacy Player2HealthUpdate received - using new health system instead");
});
// Handle hit animation events from opponent
syncEntityPlayer1.onEventReceived.add('PlayHitAnimationOnOpponent', function(messageInfo) {
var animationData = messageInfo.data;
print("⚡ Received PlayHitAnimationOnOpponent event on Player1 sync: " + JSON.stringify(animationData));
// Only play the animation if we're player2 (the target)
if (localPlayerNumber === "player2") {
print("✅ Playing hit animation on player2 (target)");
if (animationData.type === "balloon") {
if (global.playBalloonHitAnimation) {
print("🎬 Playing balloon hit animation on player2");
global.playBalloonHitAnimation();
} else {
print("⚠️ Warning: playBalloonHitAnimation function not found in global scope");
}
} else if (animationData.type === "slice") {
if (global.playSliceHitAnimation) {
print("🎬 Playing slice hit animation on player2");
global.playSliceHitAnimation();
} else {
print("⚠️ Warning: playSliceHitAnimation function not found in global scope");
}
}
} else {
print("⏭️ Not playing animation - we are not the target (we are " + localPlayerNumber + ")");
}
});
// Remove old event handler
syncEntityPlayer1.onEventReceived.remove('HitAnimationEvent');
}
if (!syncEntityPlayer2) {
syncEntityPlayer2 = new SyncEntity(script, null, true);
syncEntityPlayer2.notifyOnReady(function() {
print("SyncEntity health player2 is ready");
});
// Legacy event handlers - replaced by new health system
syncEntityPlayer2.onEventReceived.add('Player2OppHealth', function(messageInfo) {
print("Legacy Player2OppHealth event received - using new health system instead");
});
syncEntityPlayer2.onEventReceived.add('Player2Opp2Health', function(messageInfo) {
print("Legacy Player2Opp2Health event received - using new health system instead");
});
// Handle incoming health set events (when opponent reduces our health or regenerates)
syncEntityPlayer2.onEventReceived.add('HealthSetEvent', function(messageInfo) {
var healthData = messageInfo.data;
print("Received health set event: " + JSON.stringify(healthData));
if (healthData.isDirectHealthSet) {
if (healthData.isRegeneration) {
// print("🔋 Received opponent's regenerated health: " + healthData.newHealth);
// Update the opponent's health display locally
opponentHealth = healthData.newHealth;
updateOpponentHealthDisplay();
} else {
// print("💔 Setting local health directly to: " + healthData.newHealth);
setLocalHealthDirectly(healthData.newHealth);
}
}
});
// Legacy health update handler - no longer used in new system
syncEntityPlayer2.onEventReceived.add('Player1HealthUpdate', function(messageInfo) {
print("Legacy Player1HealthUpdate received - using new health system instead");
});
// Handle hit animation events from opponent
syncEntityPlayer2.onEventReceived.add('PlayHitAnimationOnOpponent', function(messageInfo) {
var animationData = messageInfo.data;
print("⚡ Received PlayHitAnimationOnOpponent event on Player2 sync: " + JSON.stringify(animationData));
// Only play the animation if we're player1 (the target)
if (localPlayerNumber === "player1") {
print("✅ Playing hit animation on player1 (target)");
if (animationData.type === "balloon") {
if (global.playBalloonHitAnimation) {
print("🎬 Playing balloon hit animation on player1");
global.playBalloonHitAnimation();
} else {
print("⚠️ Warning: playBalloonHitAnimation function not found in global scope");
}
} else if (animationData.type === "slice") {
if (global.playSliceHitAnimation) {
print("🎬 Playing slice hit animation on player1");
global.playSliceHitAnimation();
} else {
print("⚠️ Warning: playSliceHitAnimation function not found in global scope");
}
}
} else {
print("⏭️ Not playing animation - we are not the target (we are " + localPlayerNumber + ")");
}
});
// Remove old event handler
syncEntityPlayer2.onEventReceived.remove('HitAnimationEvent');
}
if (!syncEntityDeath) {
syncEntityDeath = new SyncEntity(script, null, true);
// Set up death event handler
syncEntityDeath.onEventReceived.add('Death', function(messageInfo) {
print("Received Opponent death event");
handleCharacterDeath();
});
}
// Update health display on initialization
updateHealthDisplay(true);
// Initialize opponent health display
updateOpponentHealthDisplay();
// Set up collision handlers for both player objects
setupPlayerCollisionDetection();
// Set up collision handlers for saber objects
setupSaberCollisionDetection();
// Disable the update event once everything is set up
updateEvent.enabled = false;
}
}
// Create the update event and bind it to the onUpdate function
var updateEvent = script.createEvent('UpdateEvent');
updateEvent.bind(onUpdate);
};
// Set up collision detection for opponent player only
function setupPlayerCollisionDetection() {
print("=== SETTING UP OPPONENT COLLISION DETECTION ===");
print("Local player: " + localPlayerNumber);
// Hide local player's visual components to prevent self-collision
hideLocalPlayerVisuals();
// IMPORTANT: Only set up collision detection for the OPPONENT player
// Local player will NEVER have collision events - only the opponent can be hit
var opponentPlayer = null;
var opponentName = "";
if (localPlayerNumber === "player1") {
// Player 1 is local - set up collision ONLY for Player 2 (opponent)
opponentPlayer = script.player2;
opponentName = "player2";
print("Player 1 is local - setting up collision detection for Player 2 only");
} else if (localPlayerNumber === "player2") {
// Player 2 is local - set up collision ONLY for Player 1 (opponent)
opponentPlayer = script.player1;
opponentName = "player1";
print("Player 2 is local - setting up collision detection for Player 1 only");
}
if (opponentPlayer) {
var opponentCollider = opponentPlayer.getComponent("Physics.ColliderComponent");
if (opponentCollider) {
// This collision event will ONLY fire when the OPPONENT gets hit
// The local player will never trigger collision events
opponentCollider.onCollisionEnter.add(function (e) {
print(">>> OPPONENT " + opponentName + " got hit (NOT local player) <<<");
// Handle opponent taking damage
onOpponentHitHandler(opponentName, e.collision);
});
print("✓ Collision detection set up ONLY for opponent: " + opponentName);
print("✓ Local player (" + localPlayerNumber + ") will NOT trigger collision events");
} else {
print("✗ Warning: Opponent has no ColliderComponent");
}
} else {
print("✗ Warning: No opponent player object found");
}
}
// Hide local player's visual mesh and physics collider to prevent self-collision
function hideLocalPlayerVisuals() {
print("=== HIDING LOCAL PLAYER VISUALS ===");
print("Local player: " + localPlayerNumber);
var localPlayer = null;
if (localPlayerNumber === "player1") {
localPlayer = script.player1;
} else if (localPlayerNumber === "player2") {
localPlayer = script.player2;
}
if (localPlayer) {
print("Hiding visuals for local player: " + localPlayer.name);
// Hide all RenderMeshVisual components on the local player and its children
hideRenderMeshVisuals(localPlayer);
// Disable physics collider for the local player to prevent self-collision
var localCollider = localPlayer.getComponent("Physics.ColliderComponent");
if (localCollider) {
localCollider.enabled = false;
print("✓ Disabled physics collider for local player: " + localPlayer.name);
} else {
print("⚠ No physics collider found on local player: " + localPlayer.name);
}
print("✓ Local player visuals hidden successfully");
} else {
print("✗ Warning: Local player object not found");
}
}
// Recursively hide RenderMeshVisual components on an object and its children
function hideRenderMeshVisuals(sceneObject) {
if (!sceneObject) return;
// Hide RenderMeshVisual components on this object
var renderMeshVisuals = sceneObject.getComponents("Component.RenderMeshVisual");
for (var i = 0; i < renderMeshVisuals.length; i++) {
renderMeshVisuals[i].enabled = false;
print("✓ Disabled RenderMeshVisual on: " + sceneObject.name);
}
// Recursively hide visuals on child objects
var childCount = sceneObject.getChildrenCount();
for (var j = 0; j < childCount; j++) {
var child = sceneObject.getChild(j);
hideRenderMeshVisuals(child);
}
}
// Function to show local player visuals (for debugging or special cases)
function showLocalPlayerVisuals() {
print("=== SHOWING LOCAL PLAYER VISUALS ===");
print("Local player: " + localPlayerNumber);
var localPlayer = null;
if (localPlayerNumber === "player1") {
localPlayer = script.player1;
} else if (localPlayerNumber === "player2") {
localPlayer = script.player2;
}
if (localPlayer) {
print("Showing visuals for local player: " + localPlayer.name);
// Show all RenderMeshVisual components on the local player and its children
showRenderMeshVisuals(localPlayer);
// Re-enable physics collider for the local player
var localCollider = localPlayer.getComponent("Physics.ColliderComponent");
if (localCollider) {
localCollider.enabled = true;
print("✓ Enabled physics collider for local player: " + localPlayer.name);
}
print("✓ Local player visuals shown successfully");
} else {
print("✗ Warning: Local player object not found");
}
}
// Recursively show RenderMeshVisual components on an object and its children
function showRenderMeshVisuals(sceneObject) {
if (!sceneObject) return;
// Show RenderMeshVisual components on this object
var renderMeshVisuals = sceneObject.getComponents("Component.RenderMeshVisual");
for (var i = 0; i < renderMeshVisuals.length; i++) {
renderMeshVisuals[i].enabled = true;
print("✓ Enabled RenderMeshVisual on: " + sceneObject.name);
}
// Recursively show visuals on child objects
var childCount = sceneObject.getChildrenCount();
for (var j = 0; j < childCount; j++) {
var child = sceneObject.getChild(j);
showRenderMeshVisuals(child);
}
}
// Simple handler when opponent gets hit - reduce their health locally and send update
function onOpponentHitHandler(opponentName, eventData) {
print("=== OPPONENT HIT ===");
print("Opponent: " + opponentName + " got hit");
// Use simple collision detection like old health manager
var collidingObject = eventData.collider;
if (collidingObject) {
var objectName = collidingObject.getSceneObject().name;
print("Hit by object: " + objectName);
// Get damage amount
var damageAmount = objectDamageAmounts[objectName] || defaultDamage;
print("Applying " + damageAmount + " damage to opponent");
// Reduce opponent's health locally
applyDamageToOpponent(damageAmount, objectName, 10.0);
// Play damage sound effect
if (script.damageSoundEffect) {
script.damageSoundEffect.play(1);
}
// Send appropriate hit animation to opponent based on object type
if (objectName.toLowerCase().includes("ballon")) {
// Send balloon hit animation message to opponent instead of playing locally
sendHitAnimationToOpponent("balloon");
} else if (objectName.toLowerCase().includes("slice")) {
// Send slice hit animation message to opponent instead of playing locally
sendHitAnimationToOpponent("slice");
}
} else {
print("✗ No colliding object found");
}
}
// Set up collision detection for saber objects
function setupSaberCollisionDetection() {
print("=== SETTING UP SABER COLLISION DETECTION ===");
print("Sabers array exists: " + (script.sabers ? "YES" : "NO"));
print("Sabers array length: " + (script.sabers ? script.sabers.length : "N/A"));
if (script.sabers && script.sabers.length > 0) {
for (var i = 0; i < script.sabers.length; i++) {
var saber = script.sabers[i];
if (saber) {
print("Setting up collision for saber: " + saber.name);
var saberCollider = saber.getComponent("Physics.ColliderComponent");
if (saberCollider) {
// Create a closure to capture the saber name
(function(saberName) {
saberCollider.onCollisionEnter.add(function (e) {
print(">>> Saber collision detected: " + saberName + " <<<");
onCollisionHandler(e);
});
})(saber.name);
print("✓ Saber collision handler set up for: " + saber.name);
} else {
print("✗ Warning: Saber " + saber.name + " has no ColliderComponent");
}
} else {
print("✗ Warning: Saber at index " + i + " is null");
}
}
} else {
print("✗ Warning: No saber objects assigned or empty array");
}
}
// Function to set the local player number (can be called externally)
script.setLocalPlayerNumber = function(playerNumber) {
localPlayerNumber = playerNumber;
print("Local player number set to: " + localPlayerNumber);
};
// DEBUG: Test function to manually trigger damage
script.testDamage = function() {
print("=== MANUAL DAMAGE TEST ===");
print("Current health: " + currentHealth);
print("Local player: " + localPlayerNumber);
// Test local damage
print("Testing local damage...");
applyLocalDamage(10, "TEST_OBJECT", 5);
// Test opponent damage (if we have an opponent)
if (localPlayerNumber === "player1") {
print("Testing opponent damage to player2...");
applyDamageToPlayer("player2", 15, "TEST_SABER", 8);
} else if (localPlayerNumber === "player2") {
print("Testing opponent damage to player1...");
applyDamageToPlayer("player1", 15, "TEST_SABER", 8);
}
};
// DEBUG: Test function to simulate collision
script.testCollision = function() {
print("=== MANUAL COLLISION TEST ===");
// Create a mock collision event
var mockEvent = {
collision: {
collider: {
getSceneObject: function() {
return {
name: localPlayerNumber === "player1" ? "player1saber1" : "player2saber1"
};
}
},
otherCollider: {
getSceneObject: function() {
return {
name: localPlayerNumber === "player1" ? script.player2.name : script.player1.name
};
}
}
}
};
print("Simulating collision: " + mockEvent.collision.collider.getSceneObject().name + " hits " + mockEvent.collision.otherCollider.getSceneObject().name);
onCollisionHandler(mockEvent);
};
// Make functions available to other scripts
script.applyDamageToPlayer = applyDamageToPlayer;
script.getCurrentHealth = function() { return Math.max(0, currentHealth); }; // Ensure health is never reported as below 0
script.setCurrentHealth = function(value) {
var previousHealth = currentHealth;
currentHealth = Math.max(0, Math.min(script.maxHealth, value));
// Only update if the health actually changed
if (previousHealth !== currentHealth) {
updateHealthDisplay(true);
// Check if health regeneration should be scheduled or stopped
if (script.enableHealthRegeneration && currentHealth < script.maxHealth && currentHealth > 0) {
scheduleRegeneration();
} else if (currentHealth >= script.maxHealth) {
stopRegeneration();
}
}
};
script.restoreHealth = function(amount) {
var previousHealth = currentHealth;
currentHealth = Math.min(script.maxHealth, currentHealth + amount);
// Only update if health actually changed
if (previousHealth !== currentHealth) {
updateHealthDisplay(true);
// Stop regeneration if health is full
if (currentHealth >= script.maxHealth) {
function delayedFunction(eventData) {
currentHealth = script.maxHealth;
updateHealthDisplay(true);
stopRegeneration();
}
// Create a DelayedCallbackEvent
var delayedEvent = script.createEvent('DelayedCallbackEvent');
// Set the delay time in seconds
delayedEvent.bind(delayedFunction);
delayedEvent.reset(2.0);
}
}
};
script.resetHealth = function() {
var previousHealth = currentHealth;
currentHealth = script.maxHealth;
// Only update if health actually changed
if (previousHealth !== currentHealth) {
updateHealthDisplay(true);
stopRegeneration();
}
};
// New functions to control regeneration from outside
script.enableRegeneration = function(enable) {
script.enableHealthRegeneration = enable;
if (!enable) {
stopRegeneration();
} else if (currentHealth < script.maxHealth && currentHealth > 0) {
scheduleRegeneration();
}
};
script.setRegenerationRate = function(rate) {
script.regenerationRate = rate;
};
script.setRegenerationDelay = function(delay) {
script.regenerationDelay = delay;
};
// Public functions to control player visibility
script.hideLocalPlayerVisuals = function() {
hideLocalPlayerVisuals();
};
script.showLocalPlayerVisuals = function() {
showLocalPlayerVisuals();
};
// Function to get current local player number
script.getLocalPlayerNumber = function() {
return localPlayerNumber;
};
// NATIVE LOGGER
import LogLevelProvider from "../Providers/InteractionConfigurationProvider/LogLevelProvider"
import SIKLogLevelProvider from "../Providers/InteractionConfigurationProvider/SIKLogLevelProvider"
import {logWithTag} from "./logger"
import {LogLevel} from "./LogLevel"
export default class NativeLogger {
private sikLogLevelProvider = SIKLogLevelProvider.getInstance()
private tag: string
private logger: (...args: any[]) => void
private logLevelFilter: LogLevel
private logLevelProvider: LogLevelProvider
constructor(tag: string, logLevelProvider?: LogLevelProvider) {
this.tag = tag
this.logger = logWithTag(tag)
this.logLevelProvider = logLevelProvider ?? this.sikLogLevelProvider
this.logLevelFilter = this.logLevelProvider.logLevel
// Add error handling around event subscription
try {
if (this.logLevelProvider && this.logLevelProvider.onLogLevelChanged && typeof this.logLevelProvider.onLogLevelChanged.add === 'function') {
this.logLevelProvider.onLogLevelChanged.add(this.updateLogLevel.bind(this))
}
} catch (error) {
// Silently handle the error to prevent crashes during prefab destruction
print(`Failed to subscribe to log level changes for tag: ${tag}`)
}
}
i(message: string): void {
if (!this.shouldLog(LogLevel.Info)) {
return
}
this.logger(this.tag, message)
}
d(message: string): void {
if (!this.shouldLog(LogLevel.Debug)) {
return
}
this.logger(this.tag, message)
}
e(message: string): void {
if (!this.shouldLog(LogLevel.Error)) {
return
}
this.logger(this.tag, message)
}
w(message: string): void {
if (!this.shouldLog(LogLevel.Warning)) {
return
}
this.logger(this.tag, message)
}
v(message: string): void {
if (!this.shouldLog(LogLevel.Verbose)) {
return
}
this.logger(this.tag, message)
}
private shouldLog(logLevel: LogLevel): boolean {
return logLevel <= this.logLevelFilter
}
private updateLogLevel(logLevel: LogLevel): void {
this.logLevelFilter = logLevel
}
}
// EVENT WRAPPER
import {SyncKitLogger} from "../Utils/SyncKitLogger"
const TAG = "EventWrapper"
/**
* Simple implementation of an event class. Add callbacks to be notified when the event is triggered.
* @class
* @template T
*/
export class EventWrapper<T extends any[]> {
private log = new SyncKitLogger(TAG)
private _callbacks: ((...args: T) => void)[] = []
/**
* Add a callback function to this event. The callback function will be executed when this event is triggered.
* @param {(...args: T) => void} callback Callback function to execute
*/
add(callback: (...args: T) => void): (...args: T) => void {
if (typeof callback === "function") {
this._callbacks.push(callback)
return callback
} else {
throw "Trying to add invalid callback type to EventWrapper. You must add a function."
}
}
/**
* Remove a callback function from this event.
* @param {(...args:T) => void} callback Callback function to remove
*/
remove(callback: (...args: T) => void): void {
const ind = this._callbacks.indexOf(callback)
if (ind > -1) {
this._callbacks.splice(ind, 1)
} else {
this.log.e(
"Trying to remove callback from EventWrapper, but the callback hasn't been added."
)
}
}
/**
* Trigger the event so that all callbacks are executed.
* All arguments given will be passed to the callbacks.
* @param {T} args Arguments to pass to callbacks
*/
trigger(...args: T): void {
const callbacks = this._callbacks.slice()
for (let i = 0; i < callbacks.length; i++) {
try {
if (typeof callbacks[i] === 'function') {
callbacks[i](...args)
}
} catch (error) {
// Handle errors during callback execution to prevent crashes
this.log.e(`Error in event callback: ${error}`)
}
}
}
}
// These exports exist for javascript compatibility, and should not be used from typescript code.
;(global as any).EventWrapper = EventWrapper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment