Skip to content

Instantly share code, notes, and snippets.

@dominx99
Created August 25, 2023 21:18
Show Gist options
  • Save dominx99/0dc44e0a7946199d49c086d5afd4e73c to your computer and use it in GitHub Desktop.
Save dominx99/0dc44e0a7946199d49c086d5afd4e73c to your computer and use it in GitHub Desktop.
Player range attack
import { RpgEvent, RpgMap, RpgPlayer } from "@rpgjs/server"
import { takeUntil } from "rxjs";
import { VictimsDetector } from "../../combat/hitbox/VictimsDetector";
import { CombatReaction } from "../../combat/CombatReaction";
import { Direction, PositionXY } from "@rpgjs/types";
import { AbstractObject } from "@rpgjs/server";
import { Vector2d, Vector2dZero } from "@rpgjs/common/lib/Vector2d";
import { Observable, Subject, from, map, mergeMap, tap } from "rxjs";
declare module "@rpgjs/server" {
export interface AbstractObject {
movePlayerToPosition(position: Vector2d): Observable<Vector2d>
computeNextPosition(nextPosition: Vector2d, target: Vector2d): Promise<Vector2d>
changeDirectionByNextPosition(nextPosition: Vector2d): void
}
}
AbstractObject.prototype.changeDirectionByNextPosition = function (nextPosition: Vector2d): void {
const { x, y } = this.position
const { x: nx, y: ny } = nextPosition
const diff = Math.abs(x - nx) > Math.abs(y - ny)
if (diff) {
if (nx > x) {
this.changeDirection(Direction.Right)
}
else {
this.changeDirection(Direction.Left)
}
}
else {
if (ny > y) {
this.changeDirection(Direction.Down)
}
else {
this.changeDirection(Direction.Up)
}
}
}
AbstractObject.prototype.computeNextPosition = async function(nextPosition: Vector2d, target: Vector2d): Promise<Vector2d> {
const pullDistance = target.distanceWith(nextPosition)
if (pullDistance <= this.speed) {
return nextPosition.set(target)
}
const pull = (target.copy().subtract(nextPosition)).multiply((1 / pullDistance))
const totalPush = new Vector2dZero()
let contenders = 0
await this.isCollided(nextPosition);
pull
.multiply(Math.max(1, 4 * contenders))
.add(totalPush)
.normalize()
return nextPosition.add(pull.multiply(this.speed))
}
AbstractObject.prototype.movePlayerToPosition = function(targetPosition: Vector2d): Observable<Vector2d> {
const lastPositions: Vector2d[] = []
let i = 0
let count = 0
this.stopMoveTo()
this.destroyMove$ = new Subject()
return this.server.tick
.pipe(
takeUntil(this.destroyMove$),
takeUntil(this._destroy$),
mergeMap(() => from(this.computeNextPosition(this.position.copy(), targetPosition))),
map(newPosition => {
return this.position.set(newPosition);
}),
tap((position: Vector2d) => {
lastPositions[i] = position.copy()
i++
count++
if (i >= 3) {
i = 0
}
else if (this.position.isEqual(targetPosition)) {
this.stopMoveTo()
}
else {
count = 0
}
})
);
}
interface RangePhysicalAttackOptions {
event: typeof RpgEvent
}
export class RangePhysicalAttack {
options: RangePhysicalAttackOptions;
constructor(options: RangePhysicalAttackOptions) {
this.options = options;
}
attack(map: RpgMap, attacker: RpgPlayer, position: PositionXY) {
if (!map) {
return;
}
const targetVector = new Vector2d(position.x, position.y);
attacker.changeDirectionByNextPosition(targetVector)
const events = map.createDynamicEvent({
x: attacker.position.x,
y: attacker.position.y,
event: this.options.event,
});
const event = Object.values(events)[0];
event.changeDirectionByNextPosition(targetVector);
event
.movePlayerToPosition(new Vector2d(position.x, position.y))
.subscribe({
next: () => {
if (event.tilesCollision.length > 0 || event.shapesCollision.length > 0) {
event.remove();
}
VictimsDetector
.detectMob(event.otherPlayersCollision, attacker.id, map.id)
.forEach(victim => {
event.remove();
CombatReaction.onHit(attacker, victim);
})
},
complete: () => event.remove(),
error: () => event.remove(),
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment