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

360 lines
9.6 KiB
JavaScript

import { BASEDIRS, DOWN, GREEN, LEFT, RIGHT, UP, WHITE } from "./constants.js";
import { OrientedSprite, SpriteProducer, VGDLSprite } from "./vgdl-sprite.js";
import { unitVector, vectNorm, getAbsoluteDirection, UniformVectorToDegree, DegreeToUniformVector } from "../tools.js";
const ActionMapping = {
UP: UP,
DOWN: DOWN,
LEFT: LEFT,
RIGHT: RIGHT,
SPACE: 42,
};
// export class Avatar extends VGDLSprite {
// actions = [];
// constructor(pos, size, args) {
// super(pos, size, args);
// this.actions = this.declare_possible_actions();
// // this.shrinkfactor = 0.15
// }
// update(game, delta = 0) {
// super.update(game, delta);
// }
// declare_possible_actions() {
// return [];
// }
// _readMultiActions(game) {
// const res = [];
// for (const action of this.declare_possible_actions()) {
// if (game.keystate[action]) res.push(ActionMapping[action]);
// }
// return res;
// }
// _readAction(game) {
// const actions = this._readMultiActions(game);
// if (actions.length) return actions[0];
// else return null;
// }
// }
export class MovingAvatar extends VGDLSprite {
constructor(pos, size, args) {
// args.color = args.color || WHITE
super(pos, size, args);
// this.actions = this.declare_possible_actions();
this.speed = args.speed ?? 1;
this.is_avatar = true;
this.playerID = 0;
}
declare_possible_actions() {
return [`UP${this.playerID}`, `DOWN${this.playerID}`, `LEFT${this.playerID}`, `RIGHT${this.playerID}`];
}
_readMultiActions(game) {
const res = [];
for (const action of this.declare_possible_actions()) {
if (game.keystate[action]) res.push(ActionMapping[action.substring(0, action.length - 1)]);
}
return res;
}
_readAction(game) {
const actions = this._readMultiActions(game);
if (actions.length) return actions[0];
else return null;
}
update(game, delta = 0) {
super.update(game, delta);
const action = this._readAction(game);
if (
action !== null &&
action !== undefined &&
action !== ActionMapping.SPACE
)
this.physics.activeMovement(this, action, this.speed);
}
}
export class HorizontalAvatar extends MovingAvatar {
constructor(pos, size, args) {
super(pos, size, args);
// this.actions = this.declare_possible_actions();
}
declare_possible_actions() {
return [`LEFT${this.playerID}`, `RIGHT${this.playerID}`];
}
update(game, delta = 0) {
super.update(game, delta);
// const action = this._readAction(game);
// if (action === RIGHT || action === LEFT) {
// this.physics.activeMovement(this, action, this.speed);
// }
}
}
export class VerticalAvatar extends MovingAvatar {
constructor(pos, size, args) {
super(pos, size, args);
// this.actions = this.declare_possible_actions();
}
declare_possible_actions() {
return [`UP${this.playerID}`, `DOWN${this.playerID}`];
}
update(game, delta = 0) {
super.update(game, delta);
// const action = this._readAction(game);
// if (action === UP || action === DOWN) {
// this.physics.activeMovement(this, action);
// }
}
}
export class FlakAvatar extends HorizontalAvatar {
constructor(pos, size, args) {
// args.color = args.color || GREEN;
super(pos, size, args);
}
declare_possible_actions() {
return [...super.declare_possible_actions(), `SPACE${this.playerID}`];
}
update(game, delta = 0) {
super.update(game, delta);
this._shoot(game);
}
_shoot(game) {
if (this.stype && game.keystate[`SPACE${this.playerID}`]) {
const spawn = game._createSprite(
[this.stype],
[this.location.x, this.location.y],
);
}
}
}
export class OrientedAvatar extends MovingAvatar {
constructor(pos, size, args) {
args.draw_arrow = args.draw_arrow || true;
super(pos, size, args);
}
declare_possible_actions() {
return super.declare_possible_actions(this);
}
update(game, delta = 0) {
this.lastmove++;
const tmp = this.orientation.slice();
this.orientation = [0, 0];
const action = this._readAction(game);
// console.log(`[Avatar] action: ${action}`)
if (action !== null && action !== undefined) {
// console.log(`[Avatar] movement`)
this.physics.activeMovement(this, action);
}
const d = this.lastdirection();
if (d[0] !== 0 || d[1] !== 0) {
this.orientation = d;
} else {
this.orientation = tmp;
}
}
}
export class RotatingAvatar extends OrientedAvatar {
constructor(pos, size, args) {
args.draw_arrow = args.draw_arrow || true;
super(pos, size, args);
this.speed = 0;
}
update(game, delta = 0) {
this.lastmove++;
const actions = this._readMultiActions(game);
if (UP in actions) this.speed = 1;
else if (DOWN in actions) this.speed = -1;
if (LEFT in actions) {
const i = BASEDIRS.indexOf(this.orientation);
this.orientation = BASEDIRS[(i + 1) % BASEDIRS.length];
} else if (RIGHT in actions) {
const i = BASEDIRS.indexOf(this.orientation);
this.orientation = BASEDIRS[(i - 1) % BASEDIRS.length];
}
this.physics.passiveMovement(this);
this.speed = 0;
}
}
export class RotatingFlippingAvatar extends RotatingAvatar {
constructor(pos, size, args) {
super(pos, size, args);
this.noiseLevel = args.noiseLevel || 0;
}
update(game, delta = 0) {
this.lastmove++;
let actions = this._readMultiActions(game);
if (actions.length > 0 && this.noiseLevel > 0) {
// pick a random one instead
if (Math.random() < this.noiseLevel * 4)
actions = [[UP, LEFT, DOWN, RIGHT].randomElement()];
}
if (actions.contains(UP)) this.speed = 1;
else if (actions.contains(DOWN)) {
const i = BASEDIRS.indexOf(this.orientation);
this.orientation = BASEDIRS[(i + 2) % BASEDIRS.length];
} else if (actions.contains(LEFT)) {
const i = BASEDIRS.index(this.orientation);
this.orientation = BASEDIRS[(i + 1) % BASEDIRS.length];
} else if (actions.contains(RIGHT)) {
const i = BASEDIRS.index(this.orientation);
this.orientation = BASEDIRS[(i - 1) % BASEDIRS.length];
}
this.physics.passiveMovement(this);
this.speed = 0;
}
s_stochastic() {
return this.noiseLevel > 0;
}
}
export class NoisyRotatingFlippingAvatar extends RotatingFlippingAvatar {
constructor(pos, size, args) {
super(pos, size, args);
this.noiseLevel = args.noiseLevel || 0.1;
}
}
export class ShootAvatar extends OrientedAvatar {
constructor(pos, size, args) {
super(pos, size, args);
this.ammo = args.ammo?.split(",");
this.stype = args.stype.split(",");
}
update(game, delta = 0) {
// console.trace(`[Shoot Avatar] Update`)
super.update(game, delta);
for (let i = 0; i < this.stype.length; i++) {
if (this._hasAmmo(i)) {
this._shoot(game, i);
}
}
}
_hasAmmo(idx) {
// console.log('resources', this.resources)
if (!this.ammo) return true;
if (this.ammo[idx] in this.resources)
return this.resources[this.ammo[idx]] > 0;
return false;
}
_reduceAmmo(idx) {
if (this.ammo && this.ammo[idx] && this.ammo[idx] in this.resources)
this.resources[this.ammo[idx]]--;
}
_shoot(game, idx) {
if (this.stype && game.keystate["SPACE"]) {
const u = unitVector(this.orientation);
const newones = game._createSprite(
[this.stype[idx]],
[this.location.x + u[0], this.location.y + u[1]],
);
if (newones.length > 0 && newones[0] instanceof OrientedSprite)
newones[0].orientation = unitVector(this.orientation);
this._reduceAmmo();
}
}
}
export class OngoingShootAvatar extends ShootAvatar {
constructor(pos, size, args) {
super(pos, size, args);
this.speed = 1;
this.is_oriented = true;
this.orientation = [0, 0];
}
update(game, delta = 0) {
this.lastmove++;
const action = this._readAction(game);
// this.physics.passiveMovement(this)
if (action === null || action === 42) {
this._shoot(game, 0);
} else {
this.physics.activeMovement(this, action, this.speed);
}
const d = this.lastdirection();
if (d[0] !== 0 || d[1] !== 0) {
this.orientation = d;
}
}
}
export class MissileAvatar extends OrientedAvatar {
constructor(pos, size, args) {
super(pos, size, args);
this.speed = 1;
this.is_oriented = true;
}
update(game, delta = 0) {
this.physics.activeMovement(this, this.orientation, this.speed);
}
declare_possible_actions() {
return [];
}
}
export class FPSAvatar extends MovingAvatar{
constructor(pos, size, args) {
super(pos, size, args);
this.speed = args.speed ?? 1;
this.stored_speed = this.speed;
}
updateOrientation(ori){
this.orientation = unitVector([...ori]);
}
update(game, delta = 0) {
this.speed = 0;
super.update(game, delta)
this.speed = this.stored_speed;
const actions = this._readMultiActions(game)
const sum_action = actions.filter(a => a !== 42).reduce((a, b) => [a[0]+b[0], a[1]+b[1]], [0, 0])
if(vectNorm(sum_action)===0){
return;
}
const direction = unitVector(sum_action);
const current_degree = UniformVectorToDegree(this.orientation[0], this.orientation[1]);
const fixed_orientation = DegreeToUniformVector(current_degree + 180);
const absolute_direction = getAbsoluteDirection(fixed_orientation, direction)
// console.log(JSON.stringify(this.orientation), JSON.stringify(direction), current_degree, JSON.stringify(fixed_orientation), JSON.stringify(absolute_direction))
this.physics.activeMovement(this, absolute_direction, this.speed);
}
}