360 lines
9.6 KiB
JavaScript
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);
|
|
}
|
|
}
|