OpenWorldVGDL/javascript/core/ontology/effect.js
2025-01-17 21:42:05 +08:00

566 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { colorDict, DOWN } from "./constants.js";
import { OrientedSprite } from "./vgdl-sprite.js";
import * as tools from "../tools.js";
import { oncePerStep, unitVector, vectNorm } from "../tools.js";
import { Resource } from "./resource.js";
import { ContinuousPhysics } from "./physics.js";
export function getColor(sprite) {
try {
let color = sprite.color;
try {
return colorDict[color];
} catch (e) {
console.log(e);
return color;
}
} catch (e) {
console.log(e);
return null;
}
}
export function scoreChange(sprite, partner, game, kwargs) {
// console.log(`Before Score Change: ${kwargs.score}/${game.score}`)
game.score += kwargs.score ?? kwargs.value;
// console.log(`Score Change: ${kwargs.score}/${game.score}`)
return ["scoreChange", sprite.ID || sprite, partner.ID || partner];
}
export let changeScore = scoreChange;
export function nothing(sprite, partner, game, kwargs) {
return ["nothing", sprite.ID || sprite, partner.ID || partner];
}
export function killSprite(sprite, partner, game, kwargs) {
game.kill_list.push(sprite);
// console.log("kill sprite", sprite.name, partner.name, game.collision_set)
return [
"killSprite",
sprite.ID || sprite,
partner ? partner.ID || partner : null,
];
}
//正好与killsprite效果相反专门为大预言模型设计
export function removeSprite(sprite, partner, game, kwargs) {
game.kill_list.push(partner);
return [
"removeSprite",
sprite.ID || sprite,
partner ? partner.ID || partner : null,
];
}
export function cloneSprite(sprite, partner, game, kwargs) {
game._createSprite([sprite.name], [sprite.location.x, sprite.location.y]);
return ["cloneSprite", sprite.ID || sprite, partner.ID || partner];
}
export function transformTo(sprite, partner, game, kwargs) {
// let hasID = partner.ID in sprite.transformedBy;
// let validTime;
// if (hasID) {
// if (game.time > sprite.transformedBy[partner.ID] + 3) {
// validTime = true;
// } else {
// validTime = false;
// }
// }
// if (!(hasID) || validTime) {
//(sprite.transformedBy.has(partner.ID)) {
//sprite.transformedBy.add(partner.ID);
sprite.transformedBy[partner.ID] = game.time;
let stype = kwargs.stype;
const forceOrientation = kwargs.forceOrientation;
let newones = game._createSprite(
[stype],
[sprite.location.x, sprite.location.y],
);
if (newones.length > 0) {
if (
forceOrientation ||
(sprite instanceof OrientedSprite &&
newones[0] instanceof OrientedSprite &&
vectNorm(newones[0].orientation) === 0)
)
newones[0].orientation = sprite.orientation;
newones[0].transformedBy = sprite.transformedBy;
newones[0].lastlocation = { ...sprite.lastlocation };
killSprite(sprite, partner, game, kwargs);
}
// if(kwargs['killSecond'] && kwargs['killSecond'] === true){
// game.kill_list.push(partner)
// }
return ["transformTo", sprite.ID || sprite, partner.ID || partner];
// } else {
// return;
// }
}
export function transformToAll(sprite, partner, game, kwargs) {
//(sprite.transformedBy.has(partner.ID)) {
//sprite.transformedBy.add(partner.ID);
sprite.transformedBy[partner.ID] = game.time;
let stype = kwargs.stype;
const sprites = game.getSprites(stype);
sprites.forEach((s) => {
transformTo(s, partner, game, { stype: kwargs.stypeTo });
});
return ["transforrmToAll", sprite.ID || sprite, partner.ID || partner];
}
export function stepBack(sprite, partner, game, kwargs) {
// console.log(`[Step Back] ${sprite.name} last location: ${JSON.stringify(sprite.lastlocation)}`)
sprite.location = { x: sprite.lastlocation.x, y: sprite.lastlocation.y };
return ["stepBack", sprite.ID || sprite, partner.ID || partner];
}
export function bounceForward(sprite, partner, game, kwargs) {
// console.log("bounce forward", sprite.name, partner.name, JSON.stringify(partner.lastdirection()))
// detect if the partner is moving toward the sprite
if(game.use_frame){
const partner2spriteVector = [sprite.location.x - partner.location.x, sprite.location.y - partner.location.y]
const dotResult = tools.dot(partner2spriteVector, partner.orientation)
if(dotResult <= 0.2) {return;}
}
sprite.physics.activeMovement(
sprite,
tools.unitVector(partner.lastdirection()),
partner.speed,
);
game._updateCollisionDict(sprite);
return ["bounceForward", sprite.ID || sprite, partner.ID || partner];
}
export function conveySprite(sprite, partner, game, kwargs) {
let sprite_lastlocation = { ...sprite.lastlocation };
let vect = tools.unitVector(partner.orientation);
sprite.physics.activeMovement(sprite, vect, partner.strength);
sprite.lastlocation = sprite_lastlocation;
game._updateCollisionDict(sprite);
return ["conveySprite", sprite.ID || sprite, partner.ID || partner];
}
export function windGust(sprite, partner, game, kwargs) {
let s = partner.strength - [0, 1, -1].randomElement();
if (s !== 0) {
let sprite_lastlocation = { ...sprite.lastlocation };
let vect = tools.unitVector(partner.orientation);
sprite.physics.activeMovement(sprite, vect, s);
sprite.lastlocation = sprite_lastlocation;
game._updateCollisionDict(sprite);
}
return ["windGust", sprite.ID || sprite, partner.ID || partner];
}
export function slipForward(sprite, partner, game, kwargs) {
if (kwargs.prob > Math.random()) {
let sprite_lastlocation = { ...sprite.lastlocation };
let vect = tools.unitVector(partner.orientation);
sprite.physics.activeMovement(sprite, vect, 1);
sprite.lastlocation = sprite_lastlocation;
game._updateCollisionDict(sprite);
}
return ["slipForward", sprite.ID || sprite, partner.ID || partner];
}
export function undoAll(sprite, partner, game, kwargs) {
const notStypeStr = kwargs["nontStype"] ?? "";
const notStype = notStypeStr.split(",").map((s) => s.trim());
game._iterAllExcept(notStype).forEach((s) => {
s.location = { ...s.lastlocation };
});
return ["undoAll", sprite.ID || sprite, partner.ID || partner];
}
export function attractGaze(sprite, partner, game, kwargs) {
if (kwargs.prob > Math.random()) {
sprite.orientation = partner.orientation;
}
return ["attractGaze", sprite.ID || sprite, partner.ID || partner];
}
export function turnAround(sprite, partner, game, kwargs) {
sprite.location = sprite.lastlocation;
sprite.lastmove = sprite.cooldown;
sprite.physics.activeMovement(sprite, DOWN);
sprite.lastmove = sprite.cooldown;
sprite.physics.activeMovement(sprite, DOWN);
reverseDirection(sprite, partner, game, kwargs);
game._updateCollisionDict(sprite);
return ["turnAround", sprite.ID || sprite, partner.ID || partner];
}
export function reverseDirection(sprite, partner, game, kwargs) {
sprite.orientation = [-sprite.orientation[0], -sprite.orientation[1]];
return ["reverseDirection", sprite.ID || sprite, partner.ID || partner];
}
export function bounceDirection(sprite, partner, game, kwargs) {
let friction = kwargs.friction || 0;
stepBack(sprite, partner, game);
let inc = sprite.orientation;
let snorm = unitVector([
-sprite.location.x + partner.location.y,
-sprite.location.y + partner.location.x,
]);
let dp = snorm[0] * inc[0] + snorm[1] * inc[1];
sprite.orientation = [
-2 * dp * snorm[0] + inc[0],
-2 * dp * snorm[1] + inc[1],
];
sprite.speed *= 1 - friction;
return ["bounceDirection", sprite.ID || sprite, partner.ID || partner];
}
export function wallBounce(sprite, partner, game, kwargs) {
let friction = kwargs.friction || 0;
if (!oncePerStep(sprite, game, "lastbounce")) return;
sprite.speed *= 1 - friction;
stepBack(sprite, partner, game);
if (
Math.abs(sprite.location.x - partner.location.x) >
Math.abs(sprite.location.y - partner.location.y)
)
sprite.orientation = (-sprite.orientation[0], sprite.orientation[1]);
else sprite.orientation = (sprite.orientation[0], -sprite.orientation[1]);
// return ('wallBounce', colorDict[str(partner.color)], colorDict[str(sprite.color)])
// TODO: Not printing for now
// return ('wallBounce', sprite, partner)
return ["wallBounce", sprite.ID || sprite, partner.ID || partner];
}
export function wallStop(sprite, partner, game, kwargs) {
if (!tools.oncePerStep(sprite, game, "laststop")) return;
stepBack(sprite, partner, game, kwargs);
let x_dist = Math.abs(sprite.location.x - partner.location.x);
let y_dist = Math.abs(sprite.location.y - partner.location.y);
let y_orient = sprite.orientation[1] * (1 - kwargs.friction);
let x_orient = sprite.orientation[0] * (1 - kwargs.friction);
if (x_dist > y_dist) sprite.orientation = [0, y_orient];
else sprite.orientation = [x_orient, 0];
sprite.speed = tools.vectNorm(sprite.orientation) * sprite.speed;
sprite.orientation = tools.unitVector(sprite.orientation);
return ["wallStop", sprite.ID || sprite, partner.ID || partner];
}
export function killIfSlow(sprite, partner, game, kwargs) {
let limitspeed = kwargs.limitspeed ?? 1000;
let relspeed = 0;
if (sprite.is_static) relspeed = partner.speed;
else if (partner.is_static) relspeed = sprite.speed;
else
relspeed = tools.vectNorm([
sprite._velocity()[0] - partner._velocity()[0],
sprite._velocity()[1] - partner._velocity()[1],
]);
if (relspeed < limitspeed) {
killSprite(sprite, partner, game);
return ["killIfSlow", sprite.ID || sprite, partner.ID || partner];
}
}
export function killIfFromAbove(sprite, partner, game, kwargs) {
if (
sprite.lastlocation.y > partner.lastlocation.y &&
partner.location.y > partner.lastlocation.y
) {
killSprite(sprite, partner, game);
return ["killIfFromAbove", sprite.ID || sprite, partner.ID || partner];
}
}
export function killIfAlive(sprite, partner, game, kwargs) {
if (!game.kill_list.contains(partner)) {
killSprite(sprite, partner, game);
return ["killIfAlive", sprite.ID || sprite, partner.ID || partner];
}
}
export function killBoth(sprite, partner, game, kwargs) {
if (!game.kill_list.contains(partner)) {
killSprite(sprite, partner, game);
killSprite(partner, sprite, game);
return ["killBoth", sprite.ID || sprite, partner.ID || partner];
}
}
export function killAll(sprite, partner, game, kwargs) {
const stype = kwargs.stype;
const sprites = game.getSprites(stype);
sprites.forEach((s) => {
killSprite(s, partner, game, kwargs);
});
}
export function collectResource(sprite, partner, game, kwargs) {
// console.assert(sprite instanceof Resource);
let resource_type = sprite.name;
partner.resources[resource_type] = Math.max(
-1,
Math.min(
partner.resources.get(resource_type) + sprite.value,
game.resources_limits.get(resource_type),
),
);
killSprite(sprite, partner, game, kwargs);
return ["collectResource", sprite.ID || sprite, partner.ID || partner];
}
export function changeResource(sprite, partner, game, kwargs) {
let resource = kwargs.resource;
let value = kwargs.value || 1;
let sprite_resource = sprite.resources[resource] || 0;
let resource_limit = game.resources_limits[resource] || Infinity;
sprite.resources[resource] = Math.max(
-1,
Math.min(sprite_resource + value, resource_limit),
);
if (kwargs.killResource) {
killSprite(partner, undefined, game, {});
}
return ["changeResource", sprite.ID || sprite, partner.ID || partner];
}
export function spawnIfHasMore(sprite, partner, game, kwargs) {
const resource = kwargs.resource;
const stype = kwargs.stype;
let limit = kwargs.limit || 1;
if (sprite.resources[resource] >= limit) {
game._createSprite([stype], [sprite.location.x, sprite.location.y]);
return ["spawnIfHasMore", sprite.ID || sprite, partner.ID || partner];
}
}
export function spawnIfHasLess(sprite, partner, game, kwargs) {
const resource = kwargs.resource;
const stype = kwargs.stype;
let limit = kwargs.limit || 1;
if (sprite.resources[resource] < limit) {
game._createSprite([stype], [sprite.location.x, sprite.location.y]);
return ["spawnIfHasLess", sprite.ID || sprite, partner.ID || partner];
}
}
export function killIfHasMore(sprite, partner, game, kwargs) {
let limit = kwargs.limit;
let resource = kwargs.resource;
if (sprite.resources[resource] === undefined) sprite.resources[resource] = 0;
if (sprite.resources[resource] >= limit) {
killSprite(sprite, partner, game, kwargs);
return ["killIfHasMore", sprite.ID || sprite, partner.ID || partner];
}
}
export function killIfHasLess(sprite, partner, game, kwargs) {
let resource = kwargs.resource;
let limit = kwargs.limit;
if (sprite.resources[resource] === undefined) sprite.resources[resource] = 0;
if (sprite.resources[resource] <= limit) {
killSprite(sprite, partner, game, kwargs);
return ["killIfHasLess", sprite.ID || sprite, partner.ID || partner];
}
}
export function killIfOtherHasMore(sprite, partner, game, kwargs) {
let resource = kwargs.resource;
let limit = kwargs.limit;
if (sprite.resources[resource] === undefined) {
sprite.resources[resource] = 0;
}
if (partner.resources[resource] >= limit) {
killSprite(sprite, partner, game, kwargs);
return ["killIfOtherHasMore", sprite.ID || sprite, partner.ID || partner];
}
}
export function wrapAround(sprite, partner, game, kwargs) {
let offset = kwargs.offset || 0;
if (sprite.orientation[0] > 0) sprite.location.x = offset * 1;
else if (sprite.orientation[0] < 0)
sprite.location.x = game.screensize[0] - 1 * (1 + offset);
if (sprite.orientation[1] > 0) sprite.location.y = offset * 1;
else if (sprite.orientation[1] < 0)
sprite.location.y = game.screensize[1] - 1 * (1 + offset);
sprite.lastmove = 0;
return ["wrapAround", sprite.ID || sprite, partner.ID || partner];
}
export function pullWithIt(sprite, partner, game, kwargs) {
if (!tools.oncePerStep(sprite, game, "lastpull")){
// console.log("once per step o!", sprite.name, sprite["_lastpull"], game.time)
return;
}
let tmp = { ...sprite.lastlocation };
let v = tools.unitVector(partner.lastdirection());
// console.log("Pull with it!", sprite.name, sprite["_lastpull"], game.time, sprite.physics.gridsize[0], partner.speed);
sprite._updatePos(v, partner.speed * sprite.physics.gridsize[0]);
if (sprite.physics instanceof ContinuousPhysics) {
sprite.speed = partner.speed;
sprite.orientation = partner.lastdirection;
}
sprite.lastlocation = { ...tmp };
}
export function teleportToExit(sprite, partner, game, kwargs) {
let rand_sprite;
try {
rand_sprite = game.sprite_groups[partner.stype].randomElement();
} catch (error) {
rand_sprite = game.sprite_groups["goal"].randomElement();
}
sprite.location = { ...rand_sprite.location };
sprite.lastmove = 0;
return ["teleportToExit", sprite.ID || sprite, partner.ID || partner];
}
export const stochastic_effects = [
teleportToExit,
windGust,
slipForward,
attractGaze,
];
export const kill_effects = [
killSprite,
killIfSlow,
transformTo,
transformToAll,
killIfOtherHasMore,
killIfHasMore,
killIfHasLess,
killIfFromAbove,
killIfAlive,
];
export function spawn(sprite, partner, game, kwargs) {
const stype = kwargs["stype"];
game._createSprite([stype], [sprite.location.x, sprite.location.y]);
}
export function setSpeedForAll(sprite, partner, game, kwargs) {
const value = kwargs["value"] ?? 1.0;
const stype = kwargs["stype"];
const sprites = game.getSprites(stype);
sprites.forEach((s) => (s.speed = value));
return ["setSpeedForAll", sprite.ID || sprite, partner.ID || partner];
}
export function align(sprite, partner, game, kwargs) {
const orient = kwargs["orient"];
if (orient) sprite.orient = Object.copy(orient);
sprite.location = Object.copy(partner.location);
return ["align", sprite.ID || sprite, partner.ID || partner];
}
export function addHealthPoints(sprite, partner, game, kwargs) {
const value = kwargs.value;
if (sprite.healthPoints + value < sprite.limitHealthPoints) {
sprite.healthPoints += value;
sprite.healthPoints = Math.min(sprite.healthPoints, sprite.maxHealthPoints);
}
return ["addHealthPoints", sprite.ID || sprite, partner.ID || partner];
}
export function addHealthPointsToMax(sprite, partner, game, kwargs) {
const value = kwargs.value;
if (sprite.healthPoints + value < sprite.limitHealthPoints) {
sprite.healthPoints += value;
} else {
sprite.healthPoints = sprite.limitHealthPoints;
}
if (sprite.healthPoints > sprite.maxHealthPoints)
sprite.maxHealthPoints = sprite.healthPoints;
return ["addHealthPointsToMax", sprite.ID || sprite, partner.ID || partner];
}
export function subtractHealthPoints(sprite, partner, game, kwargs) {
const value = kwargs.value || 0;
const stype = kwargs.stype;
const limit = kwargs.limit || 0;
if (stype) {
const sprites = game.getSprites(stype);
sprites.forEach((s) => {
s.healthPoints -= value;
if (s.healthPoints <= limit) {
killSprite(s, partner, game, kwargs);
}
});
} else {
sprite.healthPoints -= value;
if (sprite.healthPoints <= limit) {
killSprite(sprite, partner, game, kwargs);
}
}
return ["subtractHealthPoints", sprite.ID || sprite, partner.ID || partner];
}
export function transformToRandomChild(sprite, partner, game, kwargs) {
const stype = kwargs.stype;
if (stype) {
console.log();
const types = Object.keys(game.getSubTypes(stype));
transformTo(sprite, partner, game, { stype: types.randomElement() });
}
return ["transformToRandomChild", sprite.ID || sprite, partner.ID || partner];
}
export function shieldFrom(sprite, partner, game, kwargs) {
const stype = kwargs.stype;
const ftype = kwargs.ftype;
game.addShield(sprite.stypes, stype, ftype);
return ["shieldFrom", sprite.ID || sprite, partner.ID || partner];
}
export function killIfFrontal(sprite, partner, game, kwargs) {
const direction1 = unitVector(sprite.lastdirection());
const direction2 = unitVector(partner.lastdirection());
// console.log("kill if frontal")
const dirsum = [direction1[0] + direction2[0], direction1[1] + direction2[1]];
if (vectNorm(dirsum) === 0 || vectNorm(direction1) === 0) {
killSprite(sprite, partner, game, kwargs);
}
}
export function killIfNotFrontal(sprite, partner, game, kwargs) {
const direction1 = unitVector(sprite.lastdirection());
const direction2 = unitVector(partner.lastdirection());
const dirsum = [direction1[0] + direction2[0], direction1[1] + direction2[1]];
// console.log("kill not frontal", dirsum, direction1)
if (vectNorm(dirsum) !== 0 || vectNorm(direction1) === 0) {
killSprite(sprite, partner, game, kwargs);
}
}
export function increaseSpeedToAll(sprite, partner, game, kwargs) {
const value = kwargs.value || 0.1;
const stype = kwargs.stype;
if(!stype) return;
const sprites = game.getSprites(stype);
sprites.forEach((s) => (s.speed += value));
return ["increaseSpeedToAll", sprite.ID || sprite, partner.ID || partner];
}