Add tracking assimilate attacking behaviot #39
@ -1,14 +1,22 @@
 | 
				
			|||||||
import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior";
 | 
					import { BehaviorRecorder, IAnyBehaviorRecorder } from "@Model/Behavior";
 | 
				
			||||||
import { Template } from "@Behavior/Template";
 | 
					import { Template } from "@Behavior/Template";
 | 
				
			||||||
import { Dynamics } from "@Behavior/Dynamics";
 | 
					import { PhysicsDynamics } from "@Behavior/PhysicsDynamics";
 | 
				
			||||||
import { Brownian } from "@Behavior/Brownian";
 | 
					import { Brownian } from "@Behavior/Brownian";
 | 
				
			||||||
import { BoundaryConstraint } from "@Behavior/BoundaryConstraint";
 | 
					import { BoundaryConstraint } from "@Behavior/BoundaryConstraint";
 | 
				
			||||||
 | 
					import { Tracking } from "@Behavior/Tracking";
 | 
				
			||||||
 | 
					import { ContactAttacking } from "@Behavior/ContactAttacking";
 | 
				
			||||||
 | 
					import { ContactAssimilate } from "@Behavior/ContactAssimilate";
 | 
				
			||||||
 | 
					import { DelayAssimilate } from "@Behavior/DelayAssimilate";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AllBehaviors: IAnyBehaviorRecorder[] = [
 | 
					const AllBehaviors: IAnyBehaviorRecorder[] = [
 | 
				
			||||||
    new BehaviorRecorder(Template),
 | 
					    new BehaviorRecorder(Template),
 | 
				
			||||||
    new BehaviorRecorder(Dynamics),
 | 
					    new BehaviorRecorder(PhysicsDynamics),
 | 
				
			||||||
    new BehaviorRecorder(Brownian),
 | 
					    new BehaviorRecorder(Brownian),
 | 
				
			||||||
    new BehaviorRecorder(BoundaryConstraint),
 | 
					    new BehaviorRecorder(BoundaryConstraint),
 | 
				
			||||||
 | 
					    new BehaviorRecorder(Tracking),
 | 
				
			||||||
 | 
					    new BehaviorRecorder(ContactAttacking),
 | 
				
			||||||
 | 
					    new BehaviorRecorder(ContactAssimilate),
 | 
				
			||||||
 | 
					    new BehaviorRecorder(DelayAssimilate),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@ -54,4 +62,13 @@ function categoryBehaviors(behaviors: IAnyBehaviorRecorder[]): ICategory[] {
 | 
				
			|||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { AllBehaviors, AllBehaviorsWithCategory, ICategory as ICategoryBehavior };
 | 
					function getBehaviorById(id: string): IAnyBehaviorRecorder {
 | 
				
			||||||
 | 
					    for (let i = 0; i < AllBehaviors.length; i++) {
 | 
				
			||||||
 | 
					        if (AllBehaviors[i].behaviorId === id) {
 | 
				
			||||||
 | 
					            return AllBehaviors[i];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return getBehaviorById("Template");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { AllBehaviors, AllBehaviorsWithCategory, getBehaviorById, ICategory as ICategoryBehavior };
 | 
				
			||||||
@ -17,7 +17,7 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public override behaviorName: string = "$Title";
 | 
					    public override behaviorName: string = "$Title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override iconName: string = "Running";
 | 
					    public override iconName: string = "Quantity";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override describe: string = "$Intro";
 | 
					    public override describe: string = "$Intro";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -28,7 +28,7 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
 | 
				
			|||||||
		strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
 | 
							strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public effect(individual: Individual, group: Group, model: Model, t: number): void {
 | 
					    public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
        let rangeList: Range[] = this.parameter.range.objects;
 | 
					        let rangeList: Range[] = this.parameter.range.objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let fx = 0;
 | 
							let fx = 0;
 | 
				
			||||||
@ -57,7 +57,6 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
 | 
					 | 
				
			||||||
				fx = 0;
 | 
									fx = 0;
 | 
				
			||||||
				fy = 0;
 | 
									fy = 0;
 | 
				
			||||||
				fz = 0;
 | 
									fz = 0;
 | 
				
			||||||
@ -65,11 +64,13 @@ class BoundaryConstraint extends Behavior<IBoundaryConstraintBehaviorParameter,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		individual.applyForce(
 | 
					        if (fLen && fLen !== Infinity) {
 | 
				
			||||||
			fx * this.parameter.strength,
 | 
					            individual.applyForce(
 | 
				
			||||||
			fy * this.parameter.strength,
 | 
					                fx * this.parameter.strength / fLen,
 | 
				
			||||||
			fz * this.parameter.strength
 | 
					                fy * this.parameter.strength / fLen,
 | 
				
			||||||
		);
 | 
					                fz * this.parameter.strength / fLen
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public override terms: Record<string, Record<string, string>> = {
 | 
						public override terms: Record<string, Record<string, string>> = {
 | 
				
			||||||
 | 
				
			|||||||
@ -18,7 +18,7 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public override behaviorName: string = "$Title";
 | 
					    public override behaviorName: string = "$Title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override iconName: string = "Running";
 | 
					    public override iconName: string = "ScatterChart";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override describe: string = "$Intro";
 | 
					    public override describe: string = "$Intro";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -31,7 +31,7 @@ class Brownian extends Behavior<IBrownianBehaviorParameter, IBrownianBehaviorEve
 | 
				
			|||||||
		minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 }
 | 
							minStrength: { type: "number", name: "$Min.Strength", defaultValue: 0, numberStep: .01, numberMin: 0 }
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public effect(individual: Individual, group: Group, model: Model, t: number): void {
 | 
						public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const {maxFrequency, minFrequency, maxStrength, minStrength} = this.parameter;
 | 
							const {maxFrequency, minFrequency, maxStrength, minStrength} = this.parameter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										88
									
								
								source/Behavior/ContactAssimilate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								source/Behavior/ContactAssimilate.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					import { Behavior } from "@Model/Behavior";
 | 
				
			||||||
 | 
					import { Group } from "@Model/Group";
 | 
				
			||||||
 | 
					import { Individual } from "@Model/Individual";
 | 
				
			||||||
 | 
					import { Model } from "@Model/Model";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IContactAssimilateBehaviorParameter = {
 | 
				
			||||||
 | 
					    target: "CLG",
 | 
				
			||||||
 | 
					    assimilate: "CG",
 | 
				
			||||||
 | 
						success: "number",
 | 
				
			||||||
 | 
					    range: "number"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IContactAssimilateBehaviorEvent = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ContactAssimilate extends Behavior<IContactAssimilateBehaviorParameter, IContactAssimilateBehaviorEvent> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorId: string = "ContactAssimilate";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorName: string = "$Title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override iconName: string = "SyncStatus";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override describe: string = "$Intro";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override category: string = "$Interactive";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override parameterOption = {
 | 
				
			||||||
 | 
							target: { type: "CLG", name: "$Target" },
 | 
				
			||||||
 | 
					        assimilate: { type: "CG", name: "$Assimilate" },
 | 
				
			||||||
 | 
					        range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
 | 
				
			||||||
 | 
					        success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 }
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let assimilateGroup = this.parameter.assimilate.objects;
 | 
				
			||||||
 | 
					        if (!assimilateGroup) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (let i = 0; i < this.parameter.target.objects.length; i++) {
 | 
				
			||||||
 | 
								const targetGroup = this.parameter.target.objects[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								targetGroup.individuals.forEach((targetIndividual) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// 排除自己
 | 
				
			||||||
 | 
									if (targetIndividual === individual) return;
 | 
				
			||||||
 | 
									let dis = targetIndividual.distanceTo(individual);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 进入同化范围
 | 
				
			||||||
 | 
									if (dis <= this.parameter.range) {
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
					                    // 成功判定
 | 
				
			||||||
 | 
					                    if (Math.random() * 100 < this.parameter.success) {
 | 
				
			||||||
 | 
					                        targetIndividual.transfer(assimilateGroup!);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override terms: Record<string, Record<string, string>> = {
 | 
				
			||||||
 | 
					        "$Title": {
 | 
				
			||||||
 | 
					            "ZH_CN": "接触同化",
 | 
				
			||||||
 | 
					            "EN_US": "Contact Assimilate"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Target": {
 | 
				
			||||||
 | 
								"ZH_CN": "从哪个群...",
 | 
				
			||||||
 | 
								"EN_US": "From group..."
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					        "$Assimilate": {
 | 
				
			||||||
 | 
					            "ZH_CN": "到哪个群...",
 | 
				
			||||||
 | 
								"EN_US": "To group..."
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "$Range": {
 | 
				
			||||||
 | 
					            "ZH_CN": "同化范围 (m)",
 | 
				
			||||||
 | 
								"EN_US": "Assimilate range (m)"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Success": {
 | 
				
			||||||
 | 
								"ZH_CN": "成功率 (%)",
 | 
				
			||||||
 | 
								"EN_US": "Success rate (%)"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					        "$Intro": {
 | 
				
			||||||
 | 
					            "ZH_CN": "将进入同化范围内的个体同化至另一个群",
 | 
				
			||||||
 | 
					            "EN_US": "Assimilate individuals who enter the assimilation range to another group"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { ContactAssimilate };
 | 
				
			||||||
							
								
								
									
										79
									
								
								source/Behavior/ContactAttacking.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								source/Behavior/ContactAttacking.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,79 @@
 | 
				
			|||||||
 | 
					import { Behavior } from "@Model/Behavior";
 | 
				
			||||||
 | 
					import { Group } from "@Model/Group";
 | 
				
			||||||
 | 
					import { Individual } from "@Model/Individual";
 | 
				
			||||||
 | 
					import { Model } from "@Model/Model";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IContactAttackingBehaviorParameter = {
 | 
				
			||||||
 | 
					    target: "CLG",
 | 
				
			||||||
 | 
						success: "number",
 | 
				
			||||||
 | 
					    range: "number"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IContactAttackingBehaviorEvent = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ContactAttacking extends Behavior<IContactAttackingBehaviorParameter, IContactAttackingBehaviorEvent> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorId: string = "ContactAttacking";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorName: string = "$Title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override iconName: string = "DefenderTVM";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override describe: string = "$Intro";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override category: string = "$Interactive";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override parameterOption = {
 | 
				
			||||||
 | 
							target: { type: "CLG", name: "$Target" },
 | 
				
			||||||
 | 
					        range: { type: "number", name: "$Range", defaultValue: .05, numberMin: 0, numberStep: .01 },
 | 
				
			||||||
 | 
					        success: { type: "number", name: "$Success", defaultValue: 90, numberMin: 0, numberMax: 100, numberStep: 5 }
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (let i = 0; i < this.parameter.target.objects.length; i++) {
 | 
				
			||||||
 | 
								const targetGroup = this.parameter.target.objects[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								targetGroup.individuals.forEach((targetIndividual) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// 排除自己
 | 
				
			||||||
 | 
									if (targetIndividual === individual) return;
 | 
				
			||||||
 | 
									let dis = targetIndividual.distanceTo(individual);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 进入攻击范围
 | 
				
			||||||
 | 
									if (dis <= this.parameter.range) {
 | 
				
			||||||
 | 
										
 | 
				
			||||||
 | 
					                    // 成功判定
 | 
				
			||||||
 | 
					                    if (Math.random() * 100 < this.parameter.success) {
 | 
				
			||||||
 | 
					                        targetIndividual.die();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override terms: Record<string, Record<string, string>> = {
 | 
				
			||||||
 | 
					        "$Title": {
 | 
				
			||||||
 | 
					            "ZH_CN": "接触攻击",
 | 
				
			||||||
 | 
					            "EN_US": "Contact Attacking"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Target": {
 | 
				
			||||||
 | 
								"ZH_CN": "攻击目标",
 | 
				
			||||||
 | 
								"EN_US": "Attacking target"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					        "$Range": {
 | 
				
			||||||
 | 
					            "ZH_CN": "攻击范围 (m)",
 | 
				
			||||||
 | 
								"EN_US": "Attacking range (m)"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Success": {
 | 
				
			||||||
 | 
								"ZH_CN": "成功率 (%)",
 | 
				
			||||||
 | 
								"EN_US": "Success rate (%)"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					        "$Intro": {
 | 
				
			||||||
 | 
					            "ZH_CN": "攻击进入共进范围的目标群个体",
 | 
				
			||||||
 | 
					            "EN_US": "Attack the target group and individual entering the range"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { ContactAttacking };
 | 
				
			||||||
							
								
								
									
										96
									
								
								source/Behavior/DelayAssimilate.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								source/Behavior/DelayAssimilate.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
				
			|||||||
 | 
					import { Behavior } from "@Model/Behavior";
 | 
				
			||||||
 | 
					import { Group } from "@Model/Group";
 | 
				
			||||||
 | 
					import { Individual } from "@Model/Individual";
 | 
				
			||||||
 | 
					import { Model } from "@Model/Model";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IDelayAssimilateBehaviorParameter = {
 | 
				
			||||||
 | 
					    target: "CG",
 | 
				
			||||||
 | 
					    maxDelay: "number",
 | 
				
			||||||
 | 
						minDelay: "number",
 | 
				
			||||||
 | 
						success: "number"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type IDelayAssimilateBehaviorEvent = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DelayAssimilate extends Behavior<IDelayAssimilateBehaviorParameter, IDelayAssimilateBehaviorEvent> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorId: string = "DelayAssimilate";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorName: string = "$Title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override iconName: string = "FunctionalManagerDashboard";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override describe: string = "$Intro";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override category: string = "$Initiative";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override parameterOption = {
 | 
				
			||||||
 | 
					        target: { type: "CG", name: "$Target" },
 | 
				
			||||||
 | 
							maxDelay: { type: "number", name: "$Max.Delay", defaultValue: 20, numberStep: 1, numberMin: 0 },
 | 
				
			||||||
 | 
							minDelay: { type: "number", name: "$Min.Delay", defaultValue: 5, numberStep: 1, numberMin: 0 },
 | 
				
			||||||
 | 
							success: { type: "number", name: "$Success", defaultValue: 90, numberStep: 5, numberMin: 0 }
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let assimilateGroup = this.parameter.target.objects;
 | 
				
			||||||
 | 
					        if (!assimilateGroup) return;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const {maxDelay, minDelay, success} = this.parameter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let nextTime = individual.getData("DelayAssimilate.nextTime") ?? 
 | 
				
			||||||
 | 
							minDelay +  Math.random() * (maxDelay - minDelay);
 | 
				
			||||||
 | 
							let currentTime = individual.getData("DelayAssimilate.currentTime") ?? 0;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							currentTime += t;
 | 
				
			||||||
 | 
							if (currentTime > nextTime) {
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					            // 成功判定
 | 
				
			||||||
 | 
					            if (Math.random() * 100 < success) {
 | 
				
			||||||
 | 
					                individual.transfer(assimilateGroup);
 | 
				
			||||||
 | 
					                nextTime = undefined;
 | 
				
			||||||
 | 
					                currentTime = undefined;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                nextTime = minDelay +  Math.random() * (maxDelay - minDelay);
 | 
				
			||||||
 | 
								    currentTime = 0;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							individual.setData("DelayAssimilate.nextTime", nextTime);
 | 
				
			||||||
 | 
							individual.setData("DelayAssimilate.currentTime", currentTime);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override terms: Record<string, Record<string, string>> = {
 | 
				
			||||||
 | 
					        "$Title": {
 | 
				
			||||||
 | 
					            "ZH_CN": "延迟同化",
 | 
				
			||||||
 | 
					            "EN_US": "Delayed assimilation"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "$Intro": {
 | 
				
			||||||
 | 
					            "ZH_CN": "随着时间的推移,个体逐渐向另一个群同化。",
 | 
				
			||||||
 | 
					            "EN_US": "Over time, individuals gradually assimilate to another group."
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "$Target": {
 | 
				
			||||||
 | 
					            "ZH_CN": "同化目标",
 | 
				
			||||||
 | 
					            "EN_US": "Assimilation target"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Max.Delay": {
 | 
				
			||||||
 | 
					            "ZH_CN": "最长时间",
 | 
				
			||||||
 | 
					            "EN_US": "Longest time"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Min.Delay": {
 | 
				
			||||||
 | 
					            "ZH_CN": "最短时间",
 | 
				
			||||||
 | 
					            "EN_US": "Shortest time"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Success": {
 | 
				
			||||||
 | 
					            "ZH_CN": "成功率",
 | 
				
			||||||
 | 
					            "EN_US": "Minimum strength"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "$Initiative": {
 | 
				
			||||||
 | 
					            "ZH_CN": "主动",
 | 
				
			||||||
 | 
					            "EN_US": "Initiative"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { DelayAssimilate };
 | 
				
			||||||
@ -3,22 +3,23 @@ import Group from "@Model/Group";
 | 
				
			|||||||
import Individual from "@Model/Individual";
 | 
					import Individual from "@Model/Individual";
 | 
				
			||||||
import { Model } from "@Model/Model";
 | 
					import { Model } from "@Model/Model";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IDynamicsBehaviorParameter = {
 | 
					type IPhysicsDynamicsBehaviorParameter = {
 | 
				
			||||||
    mass: "number",
 | 
					    mass: "number",
 | 
				
			||||||
	maxAcceleration: "number",
 | 
						maxAcceleration: "number",
 | 
				
			||||||
	maxVelocity: "number",
 | 
						maxVelocity: "number",
 | 
				
			||||||
	resistance: "number"
 | 
						resistance: "number",
 | 
				
			||||||
 | 
						limit: "boolean"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IDynamicsBehaviorEvent = {}
 | 
					type IPhysicsDynamicsBehaviorEvent = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEvent> {
 | 
					class PhysicsDynamics extends Behavior<IPhysicsDynamicsBehaviorParameter, IPhysicsDynamicsBehaviorEvent> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override behaviorId: string = "Dynamics";
 | 
					    public override behaviorId: string = "PhysicsDynamics";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override behaviorName: string = "$Title";
 | 
					    public override behaviorName: string = "$Title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override iconName: string = "Running";
 | 
					    public override iconName: string = "SliderHandleSize";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public override describe: string = "$Intro";
 | 
					    public override describe: string = "$Intro";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,12 +27,19 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	public override parameterOption = {
 | 
						public override parameterOption = {
 | 
				
			||||||
		mass: { name: "$Mass", type: "number", defaultValue: 1, numberStep: .01, numberMin: .001 },
 | 
							mass: { name: "$Mass", type: "number", defaultValue: 1, numberStep: .01, numberMin: .001 },
 | 
				
			||||||
		maxAcceleration: { name: "$Max.Acceleration", type: "number", defaultValue: 5, numberStep: .1, numberMin: 0 },
 | 
							resistance: { name: "$Resistance", type: "number", defaultValue: 2.8, numberStep: .1, numberMin: 0 },
 | 
				
			||||||
		maxVelocity: { name: "$Max.Velocity", type: "number", defaultValue: 10, numberStep: .1, numberMin: 0 },
 | 
							limit: { name: "$Limit", type: "boolean", defaultValue: true },
 | 
				
			||||||
		resistance: { name: "$Resistance", type: "number", defaultValue: 0.5, numberStep: .1, numberMin: 0 }
 | 
							maxAcceleration: {
 | 
				
			||||||
 | 
								name: "$Max.Acceleration", type: "number", defaultValue: 6.25, 
 | 
				
			||||||
 | 
								numberStep: .1, numberMin: 0, condition: { key: "limit", value: true }
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							maxVelocity: {
 | 
				
			||||||
 | 
								name: "$Max.Velocity", type: "number", defaultValue: 12.5,
 | 
				
			||||||
 | 
								numberStep: .1, numberMin: 0, condition: { key: "limit", value: true }
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public override finalEffect(individual: Individual, group: Group, model: Model, t: number): void {
 | 
						public override finalEffect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 计算当前速度
 | 
							// 计算当前速度
 | 
				
			||||||
		const currentV = individual.vectorLength(individual.velocity);
 | 
							const currentV = individual.vectorLength(individual.velocity);
 | 
				
			||||||
@ -54,24 +62,28 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
 | 
				
			|||||||
		individual.acceleration[2] = individual.force[2] / this.parameter.mass;
 | 
							individual.acceleration[2] = individual.force[2] / this.parameter.mass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 加速度约束
 | 
							// 加速度约束
 | 
				
			||||||
		const lengthA = individual.vectorLength(individual.acceleration);
 | 
							if (this.parameter.limit) {
 | 
				
			||||||
		if (lengthA > this.parameter.maxAcceleration) {
 | 
								const lengthA = individual.vectorLength(individual.acceleration);
 | 
				
			||||||
			individual.acceleration[0] = individual.acceleration[0] * this.parameter.maxAcceleration / lengthA;
 | 
								if (lengthA > this.parameter.maxAcceleration) {
 | 
				
			||||||
			individual.acceleration[1] = individual.acceleration[1] * this.parameter.maxAcceleration / lengthA;
 | 
									individual.acceleration[0] = individual.acceleration[0] * this.parameter.maxAcceleration / lengthA;
 | 
				
			||||||
			individual.acceleration[2] = individual.acceleration[2] * this.parameter.maxAcceleration / lengthA;
 | 
									individual.acceleration[1] = individual.acceleration[1] * this.parameter.maxAcceleration / lengthA;
 | 
				
			||||||
 | 
									individual.acceleration[2] = individual.acceleration[2] * this.parameter.maxAcceleration / lengthA;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		// 计算速度
 | 
							// 计算速度
 | 
				
			||||||
		individual.velocity[0] = individual.velocity[0] + individual.acceleration[0] * t;
 | 
							individual.velocity[0] = individual.velocity[0] + individual.acceleration[0] * t;
 | 
				
			||||||
		individual.velocity[1] = individual.velocity[1] + individual.acceleration[1] * t;
 | 
							individual.velocity[1] = individual.velocity[1] + individual.acceleration[1] * t;
 | 
				
			||||||
		individual.velocity[2] = individual.velocity[2] + individual.acceleration[2] * t;
 | 
							individual.velocity[2] = individual.velocity[2] + individual.acceleration[2] * t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 速度约束
 | 
							// 速度约束
 | 
				
			||||||
		const lengthV = individual.vectorLength(individual.velocity);
 | 
							if (this.parameter.limit) {
 | 
				
			||||||
		if (lengthV > this.parameter.maxVelocity) {
 | 
								const lengthV = individual.vectorLength(individual.velocity);
 | 
				
			||||||
			individual.velocity[0] = individual.velocity[0] * this.parameter.maxVelocity / lengthV;
 | 
								if (lengthV > this.parameter.maxVelocity) {
 | 
				
			||||||
			individual.velocity[1] = individual.velocity[1] * this.parameter.maxVelocity / lengthV;
 | 
									individual.velocity[0] = individual.velocity[0] * this.parameter.maxVelocity / lengthV;
 | 
				
			||||||
			individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV;
 | 
									individual.velocity[1] = individual.velocity[1] * this.parameter.maxVelocity / lengthV;
 | 
				
			||||||
 | 
									individual.velocity[2] = individual.velocity[2] * this.parameter.maxVelocity / lengthV;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// 应用速度
 | 
							// 应用速度
 | 
				
			||||||
@ -87,13 +99,17 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
 | 
				
			|||||||
	
 | 
						
 | 
				
			||||||
    public override terms: Record<string, Record<string, string>> = {
 | 
					    public override terms: Record<string, Record<string, string>> = {
 | 
				
			||||||
        "$Title": {
 | 
					        "$Title": {
 | 
				
			||||||
            "ZH_CN": "动力学",
 | 
					            "ZH_CN": "物理动力学",
 | 
				
			||||||
            "EN_US": "Dynamics"
 | 
					            "EN_US": "Physics dynamics"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "$Intro": {
 | 
					        "$Intro": {
 | 
				
			||||||
            "ZH_CN": "一切可以运动物体的必要行为,执行物理法则。",
 | 
					            "ZH_CN": "一切按照物理规则运动物体的行为, 按照牛顿经典物理运动公式执行。",
 | 
				
			||||||
            "EN_US": "All necessary behaviors that can move objects and implement the laws of physics."
 | 
					            "EN_US": "The behavior of all moving objects according to physical rules is carried out according to Newton's classical physical motion formula."
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
							"$Limit": {
 | 
				
			||||||
 | 
								"ZH_CN": "开启限制",
 | 
				
			||||||
 | 
					            "EN_US": "Enable limit"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"$Mass": {
 | 
							"$Mass": {
 | 
				
			||||||
			"ZH_CN": "质量 (Kg)",
 | 
								"ZH_CN": "质量 (Kg)",
 | 
				
			||||||
            "EN_US": "Mass (Kg)"
 | 
					            "EN_US": "Mass (Kg)"
 | 
				
			||||||
@ -117,4 +133,4 @@ class Dynamics extends Behavior<IDynamicsBehaviorParameter, IDynamicsBehaviorEve
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export { Dynamics };
 | 
					export { PhysicsDynamics };
 | 
				
			||||||
@ -14,6 +14,8 @@ type ITemplateBehaviorParameter = {
 | 
				
			|||||||
    testLR: "LR";
 | 
					    testLR: "LR";
 | 
				
			||||||
    testLG: "LG";
 | 
					    testLG: "LG";
 | 
				
			||||||
    testVec: "vec";
 | 
					    testVec: "vec";
 | 
				
			||||||
 | 
					    testCG: "CG",
 | 
				
			||||||
 | 
					    testCLG: "CLG",
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ITemplateBehaviorEvent = {}
 | 
					type ITemplateBehaviorEvent = {}
 | 
				
			||||||
@ -42,10 +44,12 @@ class Template extends Behavior<ITemplateBehaviorParameter, ITemplateBehaviorEve
 | 
				
			|||||||
        testG: { name: "$Test", type: "G" },
 | 
					        testG: { name: "$Test", type: "G" },
 | 
				
			||||||
        testLR: { name: "$Test", type: "LR" },
 | 
					        testLR: { name: "$Test", type: "LR" },
 | 
				
			||||||
        testLG: { name: "$Test", type: "LG" },
 | 
					        testLG: { name: "$Test", type: "LG" },
 | 
				
			||||||
 | 
					        testCG: { name: "$Test", type: "CG" },
 | 
				
			||||||
 | 
					        testCLG: { name: "$Test", type: "CLG" },
 | 
				
			||||||
        testVec: { name: "$Test", type: "vec", defaultValue: [1, 2, 3], numberMax: 10, numberMin: 0, numberStep: 1 }
 | 
					        testVec: { name: "$Test", type: "vec", defaultValue: [1, 2, 3], numberMax: 10, numberMin: 0, numberStep: 1 }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public effect(individual: Individual, group: Group, model: Model, t: number): void {
 | 
					    public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										157
									
								
								source/Behavior/Tracking.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								source/Behavior/Tracking.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,157 @@
 | 
				
			|||||||
 | 
					import { Behavior } from "@Model/Behavior";
 | 
				
			||||||
 | 
					import { Group } from "@Model/Group";
 | 
				
			||||||
 | 
					import { Individual } from "@Model/Individual";
 | 
				
			||||||
 | 
					import { Model } from "@Model/Model";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ITrackingBehaviorParameter = {
 | 
				
			||||||
 | 
					    target: "CLG",
 | 
				
			||||||
 | 
						strength: "number",
 | 
				
			||||||
 | 
					    range: "number",
 | 
				
			||||||
 | 
					    lock: "boolean"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ITrackingBehaviorEvent = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Tracking extends Behavior<ITrackingBehaviorParameter, ITrackingBehaviorEvent> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorId: string = "Tracking";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override behaviorName: string = "$Title";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override iconName: string = "Bullseye";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override describe: string = "$Intro";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public override category: string = "$Interactive";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override parameterOption = {
 | 
				
			||||||
 | 
							target: { type: "CLG", name: "$Target" },
 | 
				
			||||||
 | 
					        lock: { type: "boolean", name: "$Lock", defaultValue: false },
 | 
				
			||||||
 | 
					        range: { type: "number", name: "$Range", defaultValue: 4, numberMin: 0, numberStep: .1 },
 | 
				
			||||||
 | 
							strength: { type: "number", name: "$Strength", defaultValue: 1, numberMin: 0, numberStep: .1 }
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private target: Individual | undefined = undefined;
 | 
				
			||||||
 | 
					    private currentDistant: number = Infinity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private searchTarget(individual: Individual) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (let i = 0; i < this.parameter.target.objects.length; i++) {
 | 
				
			||||||
 | 
								const targetGroup = this.parameter.target.objects[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								targetGroup.individuals.forEach((targetIndividual) => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// 排除自己
 | 
				
			||||||
 | 
									if (targetIndividual === individual) return;
 | 
				
			||||||
 | 
									let dis = targetIndividual.distanceTo(individual);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (dis < this.currentDistant && dis <= this.parameter.range) {
 | 
				
			||||||
 | 
										this.target = targetIndividual;
 | 
				
			||||||
 | 
										this.currentDistant = dis;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private clearTarget() {
 | 
				
			||||||
 | 
					        this.target = undefined as Individual | undefined;
 | 
				
			||||||
 | 
					        this.currentDistant = Infinity;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public effect = (individual: Individual, group: Group, model: Model, t: number): void => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.clearTarget();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.parameter.lock) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let isValidTarget = false;
 | 
				
			||||||
 | 
					            this.target = individual.getData("Tracking.lock.target");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (this.target) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 校验目标所在的群是否仍是目标
 | 
				
			||||||
 | 
					                let isInTarget = false;
 | 
				
			||||||
 | 
					                for (let i = 0; i < this.parameter.target.objects.length; i++) {
 | 
				
			||||||
 | 
					                    if (this.parameter.target.objects[i].equal(this.target.group)) {
 | 
				
			||||||
 | 
					                        isInTarget = true;
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 如果还在目标范围内,校验距离
 | 
				
			||||||
 | 
					                if (isInTarget) {
 | 
				
			||||||
 | 
					                    let dis = individual.distanceTo(this.target);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // 校验成功
 | 
				
			||||||
 | 
					                    if (dis <= this.parameter.range) {
 | 
				
			||||||
 | 
					                        this.currentDistant = dis;
 | 
				
			||||||
 | 
					                        isValidTarget = true;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 如果目标无效,尝试搜索新的目标
 | 
				
			||||||
 | 
					            if (!isValidTarget) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                this.clearTarget();
 | 
				
			||||||
 | 
					                this.searchTarget(individual);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 如果成功搜索,缓存目标
 | 
				
			||||||
 | 
					                if (this.target && this.currentDistant && this.currentDistant !== Infinity) {
 | 
				
			||||||
 | 
					                    individual.setData("Tracking.lock.target", this.target);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 搜索失败,清除目标
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    individual.setData("Tracking.lock.target", undefined);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            this.searchTarget(individual);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this.target && this.currentDistant && this.currentDistant !== Infinity) {
 | 
				
			||||||
 | 
								individual.applyForce(
 | 
				
			||||||
 | 
									(this.target.position[0] - individual.position[0]) * this.parameter.strength / this.currentDistant,
 | 
				
			||||||
 | 
									(this.target.position[1] - individual.position[1]) * this.parameter.strength / this.currentDistant,
 | 
				
			||||||
 | 
									(this.target.position[2] - individual.position[2]) * this.parameter.strength / this.currentDistant
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public override terms: Record<string, Record<string, string>> = {
 | 
				
			||||||
 | 
					        "$Title": {
 | 
				
			||||||
 | 
					            "ZH_CN": "追踪",
 | 
				
			||||||
 | 
					            "EN_US": "Tracking"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Target": {
 | 
				
			||||||
 | 
								"ZH_CN": "追踪目标",
 | 
				
			||||||
 | 
								"EN_US": "Tracking target"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					        "$Lock": {
 | 
				
			||||||
 | 
					            "ZH_CN": "追踪锁定",
 | 
				
			||||||
 | 
								"EN_US": "Tracking lock"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "$Range": {
 | 
				
			||||||
 | 
					            "ZH_CN": "追踪范围 (m)",
 | 
				
			||||||
 | 
								"EN_US": "Tracking range (m)"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Strength": {
 | 
				
			||||||
 | 
								"ZH_CN": "追踪强度系数",
 | 
				
			||||||
 | 
								"EN_US": "Tracking intensity coefficient"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					        "$Intro": {
 | 
				
			||||||
 | 
					            "ZH_CN": "个体将主动向最近的目标群个体发起追踪",
 | 
				
			||||||
 | 
					            "EN_US": "The individual will actively initiate tracking to the nearest target group individual"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
							"$Interactive": {
 | 
				
			||||||
 | 
								"ZH_CN": "交互",
 | 
				
			||||||
 | 
					            "EN_US": "Interactive"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export { Tracking };
 | 
				
			||||||
@ -94,8 +94,8 @@ div.behavior-list {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            div.behavior-content-view {
 | 
					            div.behavior-content-view {
 | 
				
			||||||
                width: calc( 100% - 50px );
 | 
					                width: calc( 100% - 55px );
 | 
				
			||||||
                padding-right: 5px;
 | 
					                padding-right: 10px;
 | 
				
			||||||
                max-width: 125px;
 | 
					                max-width: 125px;
 | 
				
			||||||
                height: $behavior-item-height;
 | 
					                height: $behavior-item-height;
 | 
				
			||||||
                display: flex;
 | 
					                display: flex;
 | 
				
			||||||
 | 
				
			|||||||
@ -119,6 +119,10 @@ div.app-container {
 | 
				
			|||||||
		flex-shrink: 1;
 | 
							flex-shrink: 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						div.app-panel.hide-scrollbar {
 | 
				
			||||||
 | 
							overflow: hidden;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	div.app-panel.hide-scrollbar::-webkit-scrollbar {
 | 
						div.app-panel.hide-scrollbar::-webkit-scrollbar {
 | 
				
			||||||
		width : 0;  /*高宽分别对应横竖滚动条的尺寸*/
 | 
							width : 0;  /*高宽分别对应横竖滚动条的尺寸*/
 | 
				
			||||||
		height: 0;
 | 
							height: 0;
 | 
				
			||||||
 | 
				
			|||||||
@ -59,6 +59,10 @@ class ObjectPicker extends Component<IObjectPickerProps & IMixinStatusProps, IOb
 | 
				
			|||||||
                    option.push(this.props.status.model.objectPool[j]);
 | 
					                    option.push(this.props.status.model.objectPool[j]);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (this.props.type.includes("C")) {
 | 
				
			||||||
 | 
					                option.push(this.props.status.model.currentGroupLabel);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return option;
 | 
					        return option;
 | 
				
			||||||
 | 
				
			|||||||
@ -32,9 +32,15 @@ class Parameter<P extends IParameter> extends Component<IParameterProps<P> & IMi
 | 
				
			|||||||
    private renderParameter<K extends keyof P>
 | 
					    private renderParameter<K extends keyof P>
 | 
				
			||||||
    (key: K, option: IParameterOptionItem<P[K]>, value: IParamValue<P[K]>): ReactNode {
 | 
					    (key: K, option: IParameterOptionItem<P[K]>, value: IParamValue<P[K]>): ReactNode {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const language = this.props.setting?.language ?? "EN_US";
 | 
					 | 
				
			||||||
        const indexKey = `${this.props.key}-${key}`;
 | 
					        const indexKey = `${this.props.key}-${key}`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 条件检测
 | 
				
			||||||
 | 
					        if (option.condition && this.props.value[option.condition.key] !==  option.condition.value) {
 | 
				
			||||||
 | 
					            return <Fragment key={indexKey}/>;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const type = option.type;
 | 
					        const type = option.type;
 | 
				
			||||||
 | 
					        const language = this.props.setting?.language ?? "EN_US";        
 | 
				
			||||||
        let keyI18n: string, keyI18nOption: Record<string, string> | undefined;
 | 
					        let keyI18n: string, keyI18nOption: Record<string, string> | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Custom I18N
 | 
					        // Custom I18N
 | 
				
			||||||
 | 
				
			|||||||
@ -51,16 +51,25 @@ function getObjectDisplayInfo(item?: IPickerListItem): IDisplayInfo {
 | 
				
			|||||||
	if (item instanceof Label) {
 | 
						if (item instanceof Label) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (item.isBuildIn) {
 | 
							if (item.isBuildIn) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            internal = true;
 | 
					            internal = true;
 | 
				
			||||||
            allLabel = true;
 | 
					            allLabel = true;
 | 
				
			||||||
            color = "transparent";
 | 
					            color = "transparent";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (item.id === "AllRange") {
 | 
					            if (item.id === "AllRange") {
 | 
				
			||||||
                icon = "ProductList";
 | 
					                icon = "ProductList";
 | 
				
			||||||
                name = "Build.In.Label.Name.All.Range";
 | 
					                name = "Build.In.Label.Name.All.Range";
 | 
				
			||||||
            } else if (item.id === "AllGroup") {
 | 
					            }
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								else if (item.id === "AllGroup") {
 | 
				
			||||||
                icon = "SizeLegacy";
 | 
					                icon = "SizeLegacy";
 | 
				
			||||||
                name = "Build.In.Label.Name.All.Group";
 | 
					                name = "Build.In.Label.Name.All.Group";
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								else if (item.id === "CurrentGroupLabel") {
 | 
				
			||||||
 | 
									icon = "TriangleShape";
 | 
				
			||||||
 | 
					                name = "Build.In.Label.Name.Current.Group";
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
        } 
 | 
					        } 
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        else {
 | 
					        else {
 | 
				
			||||||
 | 
				
			|||||||
@ -57,8 +57,11 @@ const EN_US = {
 | 
				
			|||||||
    "Popup.Action.No": "Cancel",
 | 
					    "Popup.Action.No": "Cancel",
 | 
				
			||||||
    "Popup.Action.Objects.Confirm.Title": "Confirm Delete",
 | 
					    "Popup.Action.Objects.Confirm.Title": "Confirm Delete",
 | 
				
			||||||
    "Popup.Action.Objects.Confirm.Delete": "Delete",
 | 
					    "Popup.Action.Objects.Confirm.Delete": "Delete",
 | 
				
			||||||
 | 
					    "Popup.Action.Objects.Confirm.Restore.Title": "Confirm Restore",
 | 
				
			||||||
 | 
					    "Popup.Action.Objects.Confirm.Restore": "Restore",
 | 
				
			||||||
    "Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.",
 | 
					    "Popup.Delete.Objects.Confirm": "Are you sure you want to delete this object(s)? The object is deleted and cannot be recalled.",
 | 
				
			||||||
    "Popup.Delete.Behavior.Confirm": "Are you sure you want to delete this behavior? The behavior is deleted and cannot be recalled.",
 | 
					    "Popup.Delete.Behavior.Confirm": "Are you sure you want to delete this behavior? The behavior is deleted and cannot be recalled.",
 | 
				
			||||||
 | 
					    "Popup.Restore.Behavior.Confirm": "Are you sure you want to reset all parameters of this behavior? This operation cannot be recalled.",
 | 
				
			||||||
    "Popup.Setting.Title": "Preferences setting",
 | 
					    "Popup.Setting.Title": "Preferences setting",
 | 
				
			||||||
    "Popup.Add.Behavior.Title": "Add behavior",
 | 
					    "Popup.Add.Behavior.Title": "Add behavior",
 | 
				
			||||||
    "Popup.Add.Behavior.Action.Add": "Add all select behavior",
 | 
					    "Popup.Add.Behavior.Action.Add": "Add all select behavior",
 | 
				
			||||||
@ -68,6 +71,7 @@ const EN_US = {
 | 
				
			|||||||
    "Popup.Behavior.Info.Confirm": "OK, I know it",
 | 
					    "Popup.Behavior.Info.Confirm": "OK, I know it",
 | 
				
			||||||
    "Build.In.Label.Name.All.Group": "All group",
 | 
					    "Build.In.Label.Name.All.Group": "All group",
 | 
				
			||||||
    "Build.In.Label.Name.All.Range": "All range",
 | 
					    "Build.In.Label.Name.All.Range": "All range",
 | 
				
			||||||
 | 
					    "Build.In.Label.Name.Current.Group": "Current group",
 | 
				
			||||||
    "Common.Search.Placeholder": "Search in here...",
 | 
					    "Common.Search.Placeholder": "Search in here...",
 | 
				
			||||||
    "Common.No.Data": "No Data",
 | 
					    "Common.No.Data": "No Data",
 | 
				
			||||||
    "Common.No.Unknown.Error": "Unknown error",
 | 
					    "Common.No.Unknown.Error": "Unknown error",
 | 
				
			||||||
@ -107,6 +111,7 @@ const EN_US = {
 | 
				
			|||||||
    "Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired",
 | 
					    "Common.Attr.Key.Generation.Error.Invalid.Label": "The specified label has expired",
 | 
				
			||||||
    "Common.Attr.Key.Kill.Random": "Random kill",
 | 
					    "Common.Attr.Key.Kill.Random": "Random kill",
 | 
				
			||||||
    "Common.Attr.Key.Kill.Count": "Kill count",
 | 
					    "Common.Attr.Key.Kill.Count": "Kill count",
 | 
				
			||||||
 | 
					    "Common.Attr.Key.Behavior.Restore": "Restore default parameters",
 | 
				
			||||||
    "Common.Render.Attr.Key.Display.Shape": "Display Shape",
 | 
					    "Common.Render.Attr.Key.Display.Shape": "Display Shape",
 | 
				
			||||||
    "Common.Render.Attr.Key.Display.Shape.Square": "Square",
 | 
					    "Common.Render.Attr.Key.Display.Shape.Square": "Square",
 | 
				
			||||||
    "Common.Render.Attr.Key.Display.Shape.Hollow.Square": "Hollow square",
 | 
					    "Common.Render.Attr.Key.Display.Shape.Hollow.Square": "Hollow square",
 | 
				
			||||||
 | 
				
			|||||||
@ -57,17 +57,21 @@ const ZH_CN = {
 | 
				
			|||||||
    "Popup.Action.No": "取消",
 | 
					    "Popup.Action.No": "取消",
 | 
				
			||||||
    "Popup.Action.Objects.Confirm.Title": "删除确认",
 | 
					    "Popup.Action.Objects.Confirm.Title": "删除确认",
 | 
				
			||||||
    "Popup.Action.Objects.Confirm.Delete": "删除",
 | 
					    "Popup.Action.Objects.Confirm.Delete": "删除",
 | 
				
			||||||
 | 
					    "Popup.Action.Objects.Confirm.Restore.Title": "重置确认",
 | 
				
			||||||
 | 
					    "Popup.Action.Objects.Confirm.Restore": "重置",
 | 
				
			||||||
    "Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。",
 | 
					    "Popup.Delete.Objects.Confirm": "你确定要删除这个(些)对象吗?对象被删除将无法撤回。",
 | 
				
			||||||
    "Popup.Delete.Behavior.Confirm": "你确定要删除这个行为吗?行为被删除将无法撤回。",
 | 
					    "Popup.Delete.Behavior.Confirm": "你确定要删除这个行为吗?行为被删除将无法撤回。",
 | 
				
			||||||
 | 
					    "Popup.Restore.Behavior.Confirm": "你确定要重置此行为的全部参数吗?此操作无法撤回。",
 | 
				
			||||||
    "Popup.Setting.Title": "首选项设置",
 | 
					    "Popup.Setting.Title": "首选项设置",
 | 
				
			||||||
    "Popup.Add.Behavior.Title": "添加行为",
 | 
					    "Popup.Add.Behavior.Title": "添加行为",
 | 
				
			||||||
    "Popup.Add.Behavior.Action.Add": "添加全部选中行为",
 | 
					    "Popup.Add.Behavior.Action.Add": "添加全部选中行为",
 | 
				
			||||||
    "Popup.Add.Behavior.Select.Counter": "找不到名为 \"{name}\" 的行为",
 | 
					    "Popup.Add.Behavior.Select.Counter": "已选择 {count} 个行为",
 | 
				
			||||||
    "Popup.Add.Behavior.Select.Nodata": "Could not find behavior named \"{name}\"",
 | 
					    "Popup.Add.Behavior.Select.Nodata": "找不到名为 \"{name}\" 的行为",
 | 
				
			||||||
    "Popup.Behavior.Info.Title": "行为详情: {behavior}",
 | 
					    "Popup.Behavior.Info.Title": "行为详情: {behavior}",
 | 
				
			||||||
    "Popup.Behavior.Info.Confirm": "好的, 我知道了",
 | 
					    "Popup.Behavior.Info.Confirm": "好的, 我知道了",
 | 
				
			||||||
    "Build.In.Label.Name.All.Group": "全部群",
 | 
					    "Build.In.Label.Name.All.Group": "全部群",
 | 
				
			||||||
    "Build.In.Label.Name.All.Range": "全部范围",
 | 
					    "Build.In.Label.Name.All.Range": "全部范围",
 | 
				
			||||||
 | 
					    "Build.In.Label.Name.Current.Group": "当前群",
 | 
				
			||||||
    "Common.Search.Placeholder": "在此处搜索...",
 | 
					    "Common.Search.Placeholder": "在此处搜索...",
 | 
				
			||||||
    "Common.No.Data": "暂无数据",
 | 
					    "Common.No.Data": "暂无数据",
 | 
				
			||||||
    "Common.No.Unknown.Error": "未知错误",
 | 
					    "Common.No.Unknown.Error": "未知错误",
 | 
				
			||||||
@ -107,6 +111,7 @@ const ZH_CN = {
 | 
				
			|||||||
    "Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效",
 | 
					    "Common.Attr.Key.Generation.Error.Invalid.Label": "指定的标签已失效",
 | 
				
			||||||
    "Common.Attr.Key.Kill.Random": "随机消除",
 | 
					    "Common.Attr.Key.Kill.Random": "随机消除",
 | 
				
			||||||
    "Common.Attr.Key.Kill.Count": "消除数量",
 | 
					    "Common.Attr.Key.Kill.Count": "消除数量",
 | 
				
			||||||
 | 
					    "Common.Attr.Key.Behavior.Restore": "还原默认参数",
 | 
				
			||||||
    "Common.Render.Attr.Key.Display.Shape": "显示形状",
 | 
					    "Common.Render.Attr.Key.Display.Shape": "显示形状",
 | 
				
			||||||
    "Common.Render.Attr.Key.Display.Shape.Square": "方形",
 | 
					    "Common.Render.Attr.Key.Display.Shape.Square": "方形",
 | 
				
			||||||
    "Common.Render.Attr.Key.Display.Shape.Hollow.Square": "空心方形",
 | 
					    "Common.Render.Attr.Key.Display.Shape.Hollow.Square": "空心方形",
 | 
				
			||||||
 | 
				
			|||||||
@ -158,6 +158,11 @@ class Behavior<
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public parameter: IParameterValue<P>;
 | 
					    public parameter: IParameterValue<P>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 指定当前群的 Key
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public currentGroupKey: Array<keyof P> = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 对象参数列表
 | 
					     * 对象参数列表
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -222,7 +227,7 @@ class Behavior<
 | 
				
			|||||||
     * @param model 模型
 | 
					     * @param model 模型
 | 
				
			||||||
     * @param t 经过时间
 | 
					     * @param t 经过时间
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public effect(individual: Individual, group: Group, model: Model, t: number): void {};
 | 
					    public effect?: (individual: Individual, group: Group, model: Model, t: number) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 作用影响于个体
 | 
					     * 作用影响于个体
 | 
				
			||||||
@ -231,7 +236,7 @@ class Behavior<
 | 
				
			|||||||
     * @param model 模型
 | 
					     * @param model 模型
 | 
				
			||||||
     * @param t 经过时间
 | 
					     * @param t 经过时间
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public afterEffect(individual: Individual, group: Group, model: Model, t: number): void {};
 | 
					    public afterEffect?: (individual: Individual, group: Group, model: Model, t: number) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 全部影响作用后
 | 
					     * 全部影响作用后
 | 
				
			||||||
@ -240,7 +245,7 @@ class Behavior<
 | 
				
			|||||||
     * @param model 模型
 | 
					     * @param model 模型
 | 
				
			||||||
     * @param t 经过时间
 | 
					     * @param t 经过时间
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public finalEffect(individual: Individual, group: Group, model: Model, t: number): void {};
 | 
					    public finalEffect?: (individual: Individual, group: Group, model: Model, t: number) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -61,8 +61,8 @@ class CtrlObject extends LabelObject {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 判断是否为相同对象
 | 
					     * 判断是否为相同对象
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public equal(obj: CtrlObject): boolean {
 | 
					    public equal(obj?: CtrlObject): boolean {
 | 
				
			||||||
        return this === obj || this.id === obj.id;
 | 
					        return this === obj || this.id === obj?.id;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
import { Individual } from "@Model/Individual";
 | 
					import { Individual } from "@Model/Individual";
 | 
				
			||||||
import { CtrlObject } from "@Model/CtrlObject";
 | 
					import { CtrlObject } from "@Model/CtrlObject";
 | 
				
			||||||
import type { Behavior } from "@Model/Behavior"; 
 | 
					import type { Behavior, IAnyBehavior } from "@Model/Behavior"; 
 | 
				
			||||||
import { Label } from "@Model/Label";
 | 
					import { Label } from "@Model/Label";
 | 
				
			||||||
import { Range } from "@Model/Range";
 | 
					import { Range } from "@Model/Range";
 | 
				
			||||||
import { Model, ObjectID } from "@Model/Model";
 | 
					import { Model, ObjectID } from "@Model/Model";
 | 
				
			||||||
@ -39,7 +39,7 @@ class Group extends CtrlObject {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 生成个数
 | 
					     * 生成个数
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public genCount: number = 1;
 | 
					    public genCount: number = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 生成错误信息
 | 
					     * 生成错误信息
 | 
				
			||||||
@ -54,7 +54,7 @@ class Group extends CtrlObject {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 删除个数
 | 
					     * 删除个数
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public killCount: number = 1;
 | 
					    public killCount: number = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private genInSingleRange(count: number, range: Range) {
 | 
					    private genInSingleRange(count: number, range: Range) {
 | 
				
			||||||
        for (let i = 0; i < count; i++) {
 | 
					        for (let i = 0; i < count; i++) {
 | 
				
			||||||
@ -272,9 +272,11 @@ class Group extends CtrlObject {
 | 
				
			|||||||
    public remove(individual: Individual[] | Individual): this {
 | 
					    public remove(individual: Individual[] | Individual): this {
 | 
				
			||||||
        if (Array.isArray(individual)) {
 | 
					        if (Array.isArray(individual)) {
 | 
				
			||||||
            for (let i = 0; i < individual.length; i++) {
 | 
					            for (let i = 0; i < individual.length; i++) {
 | 
				
			||||||
 | 
					                individual[i].group = undefined;
 | 
				
			||||||
                this.individuals.delete(individual[i]);
 | 
					                this.individuals.delete(individual[i]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					            individual.group = undefined;
 | 
				
			||||||
            this.individuals.delete(individual);
 | 
					            this.individuals.delete(individual);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
@ -308,7 +310,7 @@ class Group extends CtrlObject {
 | 
				
			|||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * 行为列表
 | 
						 * 行为列表
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public behaviors: Behavior[] = [];
 | 
						public behaviors: IAnyBehavior[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * 添加行为
 | 
						 * 添加行为
 | 
				
			||||||
@ -358,15 +360,37 @@ class Group extends CtrlObject {
 | 
				
			|||||||
	 * @param
 | 
						 * @param
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
	public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void {
 | 
						public runner(t: number, effectType: "finalEffect" | "effect" | "afterEffect" ): void {
 | 
				
			||||||
		this.individuals.forEach((individual) => {
 | 
					
 | 
				
			||||||
			for(let j = 0; j < this.behaviors.length; j++) {
 | 
					        for(let j = 0; j < this.behaviors.length; j++) {
 | 
				
			||||||
                if (this.behaviors[j].isDeleted()) {
 | 
					
 | 
				
			||||||
                    continue;
 | 
					            const behavior = this.behaviors[j];
 | 
				
			||||||
 | 
					            if (behavior.isDeleted()) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const runnerFunction = behavior[effectType];
 | 
				
			||||||
 | 
					            if (!runnerFunction) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            for (let k = 0; k < behavior.currentGroupKey.length; k++) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let parameterCache = behavior.parameter[
 | 
				
			||||||
 | 
					                    behavior.currentGroupKey[k] as string
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (Array.isArray(parameterCache?.objects)) {
 | 
				
			||||||
 | 
					                    parameterCache.objects = [this];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    this.behaviors[j][effectType](individual, this, this.model, t);
 | 
					                    parameterCache.objects = this;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
			}
 | 
					            }
 | 
				
			||||||
		});
 | 
					
 | 
				
			||||||
 | 
					            this.individuals.forEach((individual) => {
 | 
				
			||||||
 | 
					                runnerFunction(individual, this, this.model, t);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
@ -88,7 +88,7 @@ class Individual {
 | 
				
			|||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * 所属群组
 | 
						 * 所属群组
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public group: Group;
 | 
						public group: Group | undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * 初始化
 | 
						 * 初始化
 | 
				
			||||||
@ -97,11 +97,16 @@ class Individual {
 | 
				
			|||||||
		this.group = group;
 | 
							this.group = group;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public isDie(): boolean {
 | 
				
			||||||
 | 
					        return !!this.group;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 死亡
 | 
					     * 死亡
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public die(): this {
 | 
					    public die(): this {
 | 
				
			||||||
        this.group.remove(this);
 | 
					        this.group?.remove(this);
 | 
				
			||||||
 | 
					        this.group = undefined;
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -110,7 +115,7 @@ class Individual {
 | 
				
			|||||||
     * @param newGroup 新群体
 | 
					     * @param newGroup 新群体
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public transfer(newGroup: Group): this {
 | 
					    public transfer(newGroup: Group): this {
 | 
				
			||||||
        this.group.remove(this);
 | 
					        this.group?.remove(this);
 | 
				
			||||||
        newGroup.add(this);
 | 
					        newGroup.add(this);
 | 
				
			||||||
        this.group = newGroup;
 | 
					        this.group = newGroup;
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
 | 
				
			|||||||
@ -66,6 +66,11 @@ class Model extends Emitter<ModelEvent> {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public allGroupLabel = new Label(this, "AllGroup").setBuildInLabel();
 | 
					    public allGroupLabel = new Label(this, "AllGroup").setBuildInLabel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 内置标签-全部群标签
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public currentGroupLabel = new Label(this, "CurrentGroupLabel").setBuildInLabel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 添加标签
 | 
					     * 添加标签
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -223,6 +228,7 @@ class Model extends Emitter<ModelEvent> {
 | 
				
			|||||||
    public updateBehaviorParameter() {
 | 
					    public updateBehaviorParameter() {
 | 
				
			||||||
        for (let i = 0; i < this.behaviorPool.length; i++) {
 | 
					        for (let i = 0; i < this.behaviorPool.length; i++) {
 | 
				
			||||||
            const behavior = this.behaviorPool[i];
 | 
					            const behavior = this.behaviorPool[i];
 | 
				
			||||||
 | 
					            behavior.currentGroupKey = [];
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            for (let key in behavior.parameterOption) {
 | 
					            for (let key in behavior.parameterOption) {
 | 
				
			||||||
                switch (behavior.parameterOption[key].type) {
 | 
					                switch (behavior.parameterOption[key].type) {
 | 
				
			||||||
@ -236,11 +242,17 @@ class Model extends Emitter<ModelEvent> {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    case "CG":
 | 
				
			||||||
                    case "G":
 | 
					                    case "G":
 | 
				
			||||||
                        const dataG: IParamValue<"G"> = behavior.parameter[key];
 | 
					                        const dataG: IParamValue<"CG"> = behavior.parameter[key];
 | 
				
			||||||
                        dataG.objects = undefined;
 | 
					                        dataG.objects = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (dataG.picker instanceof Label && dataG.picker.id === this.currentGroupLabel.id) {
 | 
				
			||||||
 | 
					                            behavior.currentGroupKey.push(key);
 | 
				
			||||||
 | 
					                            dataG.objects = undefined;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
                        if (dataG.picker instanceof Group && !dataG.picker.isDeleted()) {
 | 
					                        else if (dataG.picker instanceof Group && !dataG.picker.isDeleted()) {
 | 
				
			||||||
                            dataG.objects = dataG.picker;
 | 
					                            dataG.objects = dataG.picker;
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
@ -260,8 +272,9 @@ class Model extends Emitter<ModelEvent> {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    case "CLG":
 | 
				
			||||||
                    case "LG":
 | 
					                    case "LG":
 | 
				
			||||||
                        const dataLG: IParamValue<"LG"> = behavior.parameter[key];
 | 
					                        const dataLG: IParamValue<"CLG"> = behavior.parameter[key];
 | 
				
			||||||
                        dataLG.objects = [];
 | 
					                        dataLG.objects = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (dataLG.picker instanceof Group && !dataLG.picker.isDeleted()) {
 | 
					                        if (dataLG.picker instanceof Group && !dataLG.picker.isDeleted()) {
 | 
				
			||||||
@ -269,9 +282,16 @@ class Model extends Emitter<ModelEvent> {
 | 
				
			|||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        if (dataLG.picker instanceof Label && !dataLG.picker.isDeleted()) {
 | 
					                        if (dataLG.picker instanceof Label && !dataLG.picker.isDeleted()) {
 | 
				
			||||||
                            dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => {
 | 
					
 | 
				
			||||||
                                return obj instanceof Group;
 | 
					                            if (dataLG.picker.id === this.currentGroupLabel.id) {
 | 
				
			||||||
                            }) as any;
 | 
					                                behavior.currentGroupKey.push(key);
 | 
				
			||||||
 | 
					                                dataLG.objects = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            } else {
 | 
				
			||||||
 | 
					                                dataLG.objects = this.getObjectByLabel(dataLG.picker).filter((obj) => {
 | 
				
			||||||
 | 
					                                    return obj instanceof Group;
 | 
				
			||||||
 | 
					                                }) as any;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -22,6 +22,8 @@ type IMapObjectParamTypeKeyToType = {
 | 
				
			|||||||
    "G": IObjectParamCacheType<Group | undefined>;
 | 
					    "G": IObjectParamCacheType<Group | undefined>;
 | 
				
			||||||
    "LR": IObjectParamCacheType<Label | Range | undefined, Range[]>;
 | 
					    "LR": IObjectParamCacheType<Label | Range | undefined, Range[]>;
 | 
				
			||||||
    "LG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
 | 
					    "LG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
 | 
				
			||||||
 | 
					    "CG": IObjectParamCacheType<Label | Group | undefined, Group | undefined>;
 | 
				
			||||||
 | 
					    "CLG": IObjectParamCacheType<Label | Group | undefined, Group[]>;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type IMapVectorParamTypeKeyToType = {
 | 
					type IMapVectorParamTypeKeyToType = {
 | 
				
			||||||
@ -41,7 +43,7 @@ type IParamValue<K extends IParamType> = AllMapType[K];
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * 特殊对象类型判定
 | 
					 * 特殊对象类型判定
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const objectTypeListEnumSet = new Set<string>(["R", "G", "LR", "LG"]);
 | 
					const objectTypeListEnumSet = new Set<string>(["R", "G", "LR", "LG", "CG", "CLG"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 /**
 | 
					 /**
 | 
				
			||||||
  * 对象断言表达式
 | 
					  * 对象断言表达式
 | 
				
			||||||
@ -113,6 +115,11 @@ interface IParameterOptionItem<T extends IParamType = IParamType> {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    colorNormal?: boolean;
 | 
					    colorNormal?: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 显示条件
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    condition?: {key: string, value: any};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 全部选项
 | 
					     * 全部选项
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -163,6 +170,7 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
 | 
				
			|||||||
                    defaultObj[key] = [0, 0, 0] as any;
 | 
					                    defaultObj[key] = [0, 0, 0] as any;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
 | 
					                case "CG":
 | 
				
			||||||
                case "G":
 | 
					                case "G":
 | 
				
			||||||
                case "R":
 | 
					                case "R":
 | 
				
			||||||
                    defaultObj[key] = {
 | 
					                    defaultObj[key] = {
 | 
				
			||||||
@ -171,6 +179,7 @@ function getDefaultValue<P extends IParameter> (option: IParameterOption<P>): IP
 | 
				
			|||||||
                    } as any;
 | 
					                    } as any;
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                case "CLG":
 | 
				
			||||||
                case "LR":
 | 
					                case "LR":
 | 
				
			||||||
                case "LG":
 | 
					                case "LG":
 | 
				
			||||||
                    defaultObj[key] = {
 | 
					                    defaultObj[key] = {
 | 
				
			||||||
 | 
				
			|||||||
@ -6,11 +6,12 @@ import { ClassicRenderer } from "@GLRender/ClassicRenderer";
 | 
				
			|||||||
import { initializeIcons } from '@fluentui/font-icons-mdl2';
 | 
					import { initializeIcons } from '@fluentui/font-icons-mdl2';
 | 
				
			||||||
import { RootContainer } from "@Component/Container/RootContainer";
 | 
					import { RootContainer } from "@Component/Container/RootContainer";
 | 
				
			||||||
import { LayoutDirection } from "@Context/Layout";
 | 
					import { LayoutDirection } from "@Context/Layout";
 | 
				
			||||||
import { AllBehaviors } from "@Behavior/Behavior";
 | 
					import { AllBehaviors, getBehaviorById } from "@Behavior/Behavior";
 | 
				
			||||||
import { CommandBar } from "@Component/CommandBar/CommandBar";
 | 
					import { CommandBar } from "@Component/CommandBar/CommandBar";
 | 
				
			||||||
import { HeaderBar } from "@Component/HeaderBar/HeaderBar";
 | 
					import { HeaderBar } from "@Component/HeaderBar/HeaderBar";
 | 
				
			||||||
import { Popup } from "@Component/Popup/Popup";
 | 
					import { Popup } from "@Component/Popup/Popup";
 | 
				
			||||||
import { Entry } from "../Entry/Entry";
 | 
					import { Entry } from "../Entry/Entry";
 | 
				
			||||||
 | 
					import { Group } from "@Model/Group";
 | 
				
			||||||
import "./SimulatorWeb.scss";
 | 
					import "./SimulatorWeb.scss";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/");
 | 
					initializeIcons("https://img.mrkbear.com/fabric-cdn-prod_20210407.001/");
 | 
				
			||||||
@ -40,18 +41,22 @@ class SimulatorWeb extends Component {
 | 
				
			|||||||
        this.status.bindRenderer(classicRender);
 | 
					        this.status.bindRenderer(classicRender);
 | 
				
			||||||
        this.status.setting = this.setting;
 | 
					        this.status.setting = this.setting;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 测试代码
 | 
					        const randomPosition = (group: Group) => {
 | 
				
			||||||
        if (true) {
 | 
					 | 
				
			||||||
            let group = this.status.newGroup();
 | 
					 | 
				
			||||||
            let range = this.status.newRange();
 | 
					 | 
				
			||||||
            range.color = [.1, .5, .9];
 | 
					 | 
				
			||||||
            group.new(100);
 | 
					 | 
				
			||||||
            group.color = [.8, .1, .6];
 | 
					 | 
				
			||||||
            group.individuals.forEach((individual) => {
 | 
					            group.individuals.forEach((individual) => {
 | 
				
			||||||
                individual.position[0] = (Math.random() - .5) * 2;
 | 
					                individual.position[0] = (Math.random() - .5) * 2;
 | 
				
			||||||
                individual.position[1] = (Math.random() - .5) * 2;
 | 
					                individual.position[1] = (Math.random() - .5) * 2;
 | 
				
			||||||
                individual.position[2] = (Math.random() - .5) * 2;
 | 
					                individual.position[2] = (Math.random() - .5) * 2;
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 测试代码
 | 
				
			||||||
 | 
					        if (false) {
 | 
				
			||||||
 | 
					            let group = this.status.newGroup();
 | 
				
			||||||
 | 
					            let range = this.status.newRange();
 | 
				
			||||||
 | 
					            range.color = [.1, .5, .9];
 | 
				
			||||||
 | 
					            group.new(100);
 | 
				
			||||||
 | 
					            group.color = [.8, .1, .6];
 | 
				
			||||||
 | 
					            randomPosition(group);
 | 
				
			||||||
            this.status.model.update(0);
 | 
					            this.status.model.update(0);
 | 
				
			||||||
            this.status.newLabel().name = "New Label";
 | 
					            this.status.newLabel().name = "New Label";
 | 
				
			||||||
            this.status.newLabel().name = "Test Label 01";
 | 
					            this.status.newLabel().name = "Test Label 01";
 | 
				
			||||||
@ -70,6 +75,84 @@ class SimulatorWeb extends Component {
 | 
				
			|||||||
            group.addBehavior(boundary);
 | 
					            group.addBehavior(boundary);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 鱼群模型测试
 | 
				
			||||||
 | 
					        if (true) {
 | 
				
			||||||
 | 
					            let fish1 = this.status.newGroup();
 | 
				
			||||||
 | 
					            let fish2 = this.status.newGroup();
 | 
				
			||||||
 | 
					            let shark = this.status.newGroup();
 | 
				
			||||||
 | 
					            let range = this.status.newRange();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            range.displayName = "Experimental site";
 | 
				
			||||||
 | 
					            range.color = [.8, .1, .6];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fish1.new(100);
 | 
				
			||||||
 | 
					            fish1.displayName = "Fish A";            
 | 
				
			||||||
 | 
					            fish1.color = [.1, .5, .9];
 | 
				
			||||||
 | 
					            randomPosition(fish1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fish2.new(50);
 | 
				
			||||||
 | 
					            fish2.displayName = "Fish B";            
 | 
				
			||||||
 | 
					            fish2.color = [.3, .2, .9];
 | 
				
			||||||
 | 
					            randomPosition(fish2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            shark.new(3);
 | 
				
			||||||
 | 
					            shark.displayName = "Shark";
 | 
				
			||||||
 | 
					            shark.color = [.8, .2, .3];
 | 
				
			||||||
 | 
					            shark.renderParameter.size = 100;
 | 
				
			||||||
 | 
					            shark.renderParameter.shape = "5";
 | 
				
			||||||
 | 
					            randomPosition(shark);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.status.model.update(0);
 | 
				
			||||||
 | 
					            let fishLabel = this.status.newLabel();
 | 
				
			||||||
 | 
					            fishLabel.name = "Fish";
 | 
				
			||||||
 | 
					            fish1.addLabel(fishLabel);
 | 
				
			||||||
 | 
					            fish2.addLabel(fishLabel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let template = this.status.model.addBehavior(getBehaviorById("Template"));
 | 
				
			||||||
 | 
					            template.name = "Template"; template.color = [150, 20, 220];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let dynamicFish = this.status.model.addBehavior(getBehaviorById("PhysicsDynamics"));
 | 
				
			||||||
 | 
					            dynamicFish.name = "Dynamic Fish"; dynamicFish.color = [250, 200, 80];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let dynamicShark = this.status.model.addBehavior(getBehaviorById("PhysicsDynamics"));
 | 
				
			||||||
 | 
					            dynamicShark.name = "Dynamic Shark"; dynamicShark.color = [250, 200, 80];
 | 
				
			||||||
 | 
					            dynamicShark.parameter.maxAcceleration = 8.5;
 | 
				
			||||||
 | 
					            dynamicShark.parameter.maxVelocity = 15.8;
 | 
				
			||||||
 | 
					            dynamicShark.parameter.resistance = 3.6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let brownian = this.status.model.addBehavior(getBehaviorById("Brownian"));
 | 
				
			||||||
 | 
					            brownian.name = "Brownian"; brownian.color = [200, 80, 250];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let boundary = this.status.model.addBehavior(getBehaviorById("BoundaryConstraint"));
 | 
				
			||||||
 | 
					            boundary.name = "Boundary"; boundary.color = [80, 200, 250];
 | 
				
			||||||
 | 
					            boundary.parameter.range.picker = this.status.model.allRangeLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let tracking = this.status.model.addBehavior(getBehaviorById("Tracking"));
 | 
				
			||||||
 | 
					            tracking.name = "Tracking"; tracking.color = [80, 200, 250];
 | 
				
			||||||
 | 
					            tracking.parameter.target.picker = fishLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let attacking = this.status.model.addBehavior(getBehaviorById("ContactAttacking"));
 | 
				
			||||||
 | 
					            attacking.name = "Contact Attacking"; attacking.color = [120, 100, 250];
 | 
				
			||||||
 | 
					            attacking.parameter.target.picker = fishLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fish1.addBehavior(dynamicFish);
 | 
				
			||||||
 | 
					            fish1.addBehavior(brownian);
 | 
				
			||||||
 | 
					            fish1.addBehavior(boundary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            fish2.addBehavior(dynamicFish);
 | 
				
			||||||
 | 
					            fish2.addBehavior(brownian);
 | 
				
			||||||
 | 
					            fish2.addBehavior(boundary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            shark.addBehavior(dynamicShark);
 | 
				
			||||||
 | 
					            shark.addBehavior(boundary);
 | 
				
			||||||
 | 
					            shark.addBehavior(tracking);
 | 
				
			||||||
 | 
					            shark.addBehavior(attacking);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            setTimeout(() => {
 | 
				
			||||||
 | 
					                this.status.model.updateBehaviorParameter();
 | 
				
			||||||
 | 
					            }, 200)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (window as any).s = this;
 | 
					        (window as any).s = this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,7 @@
 | 
				
			|||||||
import { Component, ReactNode} from "react";
 | 
					import { Component, ReactNode} from "react";
 | 
				
			||||||
import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting";
 | 
					import { useSettingWithEvent, IMixinSettingProps } from "@Context/Setting";
 | 
				
			||||||
import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
 | 
					import { useStatusWithEvent, IMixinStatusProps } from "@Context/Status";
 | 
				
			||||||
 | 
					import { getDefaultValue } from "@Model/Parameter";
 | 
				
			||||||
import { IAnyBehavior } from "@Model/Behavior";
 | 
					import { IAnyBehavior } from "@Model/Behavior";
 | 
				
			||||||
import { Message } from "@Input/Message/Message";
 | 
					import { Message } from "@Input/Message/Message";
 | 
				
			||||||
import { AttrInput } from "@Input/AttrInput/AttrInput";
 | 
					import { AttrInput } from "@Input/AttrInput/AttrInput";
 | 
				
			||||||
@ -12,9 +13,17 @@ import "./BehaviorDetails.scss";
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
interface IBehaviorDetailsProps {}
 | 
					interface IBehaviorDetailsProps {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface IBehaviorDetailsState {
 | 
				
			||||||
 | 
					    updateId: number;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@useSettingWithEvent("language")
 | 
					@useSettingWithEvent("language")
 | 
				
			||||||
@useStatusWithEvent("focusBehaviorChange", "behaviorAttrChange")
 | 
					@useStatusWithEvent("focusBehaviorChange", "behaviorAttrChange")
 | 
				
			||||||
class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProps & IMixinSettingProps> {
 | 
					class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProps & IMixinSettingProps, IBehaviorDetailsState> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public state: Readonly<IBehaviorDetailsState> = {
 | 
				
			||||||
 | 
					        updateId: 1
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private handelDeleteBehavior = (behavior: IAnyBehavior) => {
 | 
					    private handelDeleteBehavior = (behavior: IAnyBehavior) => {
 | 
				
			||||||
        if (this.props.status) {
 | 
					        if (this.props.status) {
 | 
				
			||||||
@ -32,6 +41,28 @@ class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProp
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private handelRestoreBehavior = (behavior: IAnyBehavior) => {
 | 
				
			||||||
 | 
					        if (this.props.status) {
 | 
				
			||||||
 | 
					            const status = this.props.status;
 | 
				
			||||||
 | 
					            status.popup.showPopup(ConfirmPopup, {
 | 
				
			||||||
 | 
					                infoI18n: "Popup.Restore.Behavior.Confirm",
 | 
				
			||||||
 | 
					                titleI18N: "Popup.Action.Objects.Confirm.Restore.Title",
 | 
				
			||||||
 | 
					                yesI18n: "Popup.Action.Objects.Confirm.Restore",
 | 
				
			||||||
 | 
					                red: "yes",
 | 
				
			||||||
 | 
					                yes: () => {
 | 
				
			||||||
 | 
					                    status.changeBehaviorAttrib(
 | 
				
			||||||
 | 
					                        behavior.id, "parameter",
 | 
				
			||||||
 | 
					                        getDefaultValue(behavior.parameterOption) as any,
 | 
				
			||||||
 | 
					                        true
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    this.setState({
 | 
				
			||||||
 | 
					                        updateId: this.state.updateId + 1
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private renderFrom(behavior: IAnyBehavior): ReactNode {
 | 
						private renderFrom(behavior: IAnyBehavior): ReactNode {
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
		return <>
 | 
							return <>
 | 
				
			||||||
@ -60,9 +91,17 @@ class BehaviorDetails extends Component<IBehaviorDetailsProps & IMixinStatusProp
 | 
				
			|||||||
					this.handelDeleteBehavior(behavior)
 | 
										this.handelDeleteBehavior(behavior)
 | 
				
			||||||
				}}
 | 
									}}
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <TogglesInput
 | 
				
			||||||
 | 
									keyI18n="Common.Attr.Key.Behavior.Restore" red
 | 
				
			||||||
 | 
									onIconName="ReplyAll" offIconName="ReplyAll"
 | 
				
			||||||
 | 
									valueChange={() => {
 | 
				
			||||||
 | 
										this.handelRestoreBehavior(behavior)
 | 
				
			||||||
 | 
									}}
 | 
				
			||||||
 | 
								/>
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            <Parameter
 | 
					            <Parameter
 | 
				
			||||||
                key={behavior.id}
 | 
					                key={`${behavior.id}-${this.state.updateId}`}
 | 
				
			||||||
                option={behavior.parameterOption}
 | 
					                option={behavior.parameterOption}
 | 
				
			||||||
                value={behavior.parameter}
 | 
					                value={behavior.parameter}
 | 
				
			||||||
                i18n={(name, language) => behavior.getTerms(name, language)}
 | 
					                i18n={(name, language) => behavior.getTerms(name, language)}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user