Merge branch 'develop' into walking-shortest-path

This commit is contained in:
Hanusiak Piotr 2022-01-18 12:37:27 +01:00
commit e0e1a7e76a
3 changed files with 159 additions and 36 deletions

View File

@ -213,13 +213,13 @@ export class GameRoom {
}
private updateUserGroup(user: User): void {
user.group?.updatePosition();
user.group?.searchForNearbyUsers();
if (user.silent) {
return;
}
const group = user.group;
const closestItem: User | Group | null = this.searchClosestAvailableUserOrGroup(user);
if (group === undefined) {
// If the user is not part of a group:
// should he join a group?
@ -229,12 +229,11 @@ export class GameRoom {
return;
}
const closestItem: User | Group | null = this.searchClosestAvailableUserOrGroup(user);
if (closestItem !== null) {
if (closestItem instanceof Group) {
// Let's join the group!
closestItem.join(user);
closestItem.setOutOfBounds(false);
} else {
const closestUser: User = closestItem;
const group: Group = new Group(
@ -249,32 +248,95 @@ export class GameRoom {
}
}
} else {
// If the user is part of a group:
// should he leave the group?
let noOneOutOfBounds = true;
group.getUsers().forEach((foreignUser: User) => {
if (foreignUser.group === undefined) {
return;
let hasKickOutSomeone = false;
let followingMembers: User[] = [];
const previewNewGroupPosition = group.previewGroupPosition();
if (!previewNewGroupPosition) {
this.leaveGroup(user);
return;
}
if (user.hasFollowers() || user.following) {
followingMembers = user.hasFollowers()
? group.getUsers().filter((currentUser) => currentUser.following === user)
: group.getUsers().filter((currentUser) => currentUser.following === user.following);
// If all group members are part of the same follow group
if (group.getUsers().length - 1 === followingMembers.length) {
let isOutOfBounds = false;
// If a follower is far away from the leader, "outOfBounds" is set to true
for (const member of followingMembers) {
const distance = GameRoom.computeDistanceBetweenPositions(
member.getPosition(),
previewNewGroupPosition
);
if (distance > this.groupRadius) {
isOutOfBounds = true;
break;
}
}
group.setOutOfBounds(isOutOfBounds);
}
const usrPos = foreignUser.getPosition();
const grpPos = foreignUser.group.getPosition();
const distance = GameRoom.computeDistanceBetweenPositions(usrPos, grpPos);
}
// Check if the moving user has kicked out another user
for (const headMember of group.getGroupHeads()) {
if (!headMember.group) {
this.leaveGroup(headMember);
continue;
}
const headPosition = headMember.getPosition();
const distance = GameRoom.computeDistanceBetweenPositions(headPosition, previewNewGroupPosition);
if (distance > this.groupRadius) {
if (foreignUser.hasFollowers() || foreignUser.following) {
// If one user is out of the group bounds BUT following, the group still exists... but should be hidden.
// We put it in 'outOfBounds' mode
group.setOutOfBounds(true);
noOneOutOfBounds = false;
} else {
this.leaveGroup(foreignUser);
}
hasKickOutSomeone = true;
break;
}
}
/**
* If the current moving user has kicked another user from the radius,
* the moving user leaves the group because he is too far away.
*/
const userDistance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), previewNewGroupPosition);
if (hasKickOutSomeone && userDistance > this.groupRadius) {
if (user.hasFollowers() && group.getUsers().length === 3 && followingMembers.length === 1) {
const other = group
.getUsers()
.find((currentUser) => !currentUser.hasFollowers() && !currentUser.following);
if (other) {
this.leaveGroup(other);
}
} else if (user.hasFollowers()) {
this.leaveGroup(user);
for (const member of followingMembers) {
this.leaveGroup(member);
}
// Re-create a group with the followers
const newGroup: Group = new Group(
this.roomUrl,
[user, ...followingMembers],
this.groupRadius,
this.connectCallback,
this.disconnectCallback,
this.positionNotifier
);
this.groups.add(newGroup);
} else {
this.leaveGroup(user);
}
});
if (noOneOutOfBounds && !user.group?.isEmpty()) {
group.setOutOfBounds(false);
}
}
user.group?.updatePosition();
user.group?.searchForNearbyUsers();
}
public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void {

View File

@ -59,6 +59,39 @@ export class Group implements Movable {
};
}
/**
* Returns the list of users of the group, ignoring any "followers".
* Useful to compute the position of the group if a follower is "trapped" far away from the the leader.
*/
getGroupHeads(): User[] {
return Array.from(this.users).filter((user) => user.group?.leader === user || !user.following);
}
/**
* Preview the position of the group but don't update it
*/
previewGroupPosition(): { x: number; y: number } | undefined {
const users = this.getGroupHeads();
let x = 0;
let y = 0;
if (users.length === 0) {
return undefined;
}
users.forEach((user: User) => {
const position = user.getPosition();
x += position.x;
y += position.y;
});
x /= users.length;
y /= users.length;
return { x, y };
}
/**
* Computes the barycenter of all users (i.e. the center of the group)
*/
@ -66,19 +99,15 @@ export class Group implements Movable {
const oldX = this.x;
const oldY = this.y;
let x = 0;
let y = 0;
// Let's compute the barycenter of all users.
this.users.forEach((user: User) => {
const position = user.getPosition();
x += position.x;
y += position.y;
});
x /= this.users.size;
y /= this.users.size;
if (this.users.size === 0) {
throw new Error("EMPTY GROUP FOUND!!!");
const newPosition = this.previewGroupPosition();
if (!newPosition) {
return;
}
const { x, y } = newPosition;
this.x = x;
this.y = y;
@ -97,10 +126,12 @@ export class Group implements Movable {
if (!this.currentZone) return;
for (const user of this.positionNotifier.getAllUsersInSquareAroundZone(this.currentZone)) {
// Todo: Merge two groups with a leader
if (user.group || this.isFull()) return; //we ignore users that are already in a group.
const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), this.getPosition());
if (distance < this.groupRadius) {
this.join(user);
this.setOutOfBounds(false);
this.updatePosition();
}
}
@ -176,4 +207,8 @@ export class Group implements Movable {
this.outOfBounds = true;
}
}
get getOutOfBounds() {
return this.outOfBounds;
}
}

View File

@ -1,6 +1,32 @@
{.section-title.accent.text-primary}
# API Camera functions Reference
### Start following player
```javascript
WA.camera.followPlayer(smooth: boolean): void
```
Set camera to follow the player. Set `smooth` to true for smooth transition.
### Set spot for camera to look at
```javascript
WA.camera.set(
x: number,
y: number,
width?: number,
height?: number,
lock: boolean = false,
smooth: boolean = false,
): void
```
Set camera to look at given spot.
Setting `width` and `height` will adjust zoom.
Set `lock` to true to lock camera in this position.
Set `smooth` to true for smooth transition.
### Listen to camera updates
```