Error
Turn your device sideways for a better view
Error
EKC Client
EKC Client is a simple electron client for kour.io aiming to provide a unique and user-friendly experience.
Suroi.io
Miss surviv.io and Surviv Reloaded? Suroi is an open-source 2D battle royale game inspired by surviv.io. Work in progress.
Kour Stats
Kour stats is a simple WIP discord bot focused on displaying accurate user stats.
generateBuilding(
definition: ReifiableDef,
position: Vector,
orientation?: Orientation
): Building {
definition = Buildings.reify(definition);
orientation ??= Map.getRandomBuildingOrientation(definition.rotationMode);
const building = new Building(this.game, definition, Vec.clone(position), orientation);
for (const obstacleData of definition.obstacles) {
const obstacleDef = Obstacles.fromString(getRandomIDString(obstacleData.idString));
let obstacleRotation = obstacleData.rotation ?? Map.getRandomRotation(obstacleDef.rotationMode);
if (obstacleDef.rotationMode === RotationMode.Limited) {
obstacleRotation = Numeric.addOrientations(orientation, obstacleRotation as Orientation);
}
let lootSpawnOffset: Vector | undefined;
if (obstacleData.lootSpawnOffset) lootSpawnOffset = Vec.addAdjust(Vec.create(0, 0), obstacleData.lootSpawnOffset, orientation);
const obstacle = this.generateObstacle(
obstacleDef,
Vec.addAdjust(position, obstacleData.position, orientation),
obstacleRotation,
obstacleData.scale ?? 1,
obstacleData.variation,
lootSpawnOffset,
building,
obstacleData.puzzlePiece
);
if (obstacleDef.role === ObstacleSpecialRoles.Activatable ||
obstacleDef.role === ObstacleSpecialRoles.Door) {
building.interactableObstacles.add(obstacle);
}
}
for (const lootData of definition.lootSpawners) {
const table = LootTables[lootData.table];
const drops = table.loot;
for (
const item of Array.from(
{ length: random(table.min, table.max) },
() => getLootTableLoot(drops as WeightedItem[]) // fixme This will break if multiple tables are specified
).flat()
) {
this.game.addLoot(
item.idString,
Vec.addAdjust(position, lootData.position, orientation),
item.count
);
}
}
for (const subBuilding of definition.subBuildings) {
const finalOrientation = Numeric.addOrientations(orientation, subBuilding.orientation ?? 0);
this.generateBuilding(
getRandomIDString(subBuilding.idString),
Vec.addAdjust(position, subBuilding.position, finalOrientation),
finalOrientation
);
}
for (const floor of definition.floors) {
this.terrain.addFloor(floor.type, floor.hitbox.transform(position, 1, orientation));
}
for (const decal of definition.decals) {
this.game.grid.addObject(new Decal(this.game, Decals.reify(decal.idString), Vec.addAdjust(position, decal.position, orientation), Numeric.addOrientations(orientation, decal.orientation ?? 0)));
}
if (!definition.hideOnMap) this.packet.objects.push(building);
this.game.grid.addObject(building);
return building;
}
advanceGasStage(): void {
if (Config.gas.mode === GasMode.Disabled) return;
const currentStage = GasStages[this.stage + 1];
if (currentStage === undefined) return;
const duration = Config.gas.mode === GasMode.Debug && Config.gas.overrideDuration !== undefined && currentStage.duration !== 0
? Config.gas.overrideDuration
: currentStage.duration;
this.stage++;
this.state = currentStage.state;
this.currentDuration = duration;
this.completionRatio = 1;
this.countdownStart = this.game.now;
if (currentStage.state === GasState.Waiting) {
this.oldPosition = Vec.clone(this.newPosition);
if (currentStage.newRadius !== 0) {
const { width, height } = this.game.map;
if (Config.gas.mode === GasMode.Debug && Config.gas.overridePosition) {
this.newPosition = Vec.create(width / 2, height / 2);
} else {
const maxDistance = (currentStage.oldRadius - currentStage.newRadius) * this.mapSize;
const maxDistanceSquared = maxDistance ** 2;
this.newPosition = randomPointInsideCircle(this.oldPosition, maxDistance);
let quadCoord = Gas._genQuadCoord(this.newPosition, width, height);
let foundPosition = false;
for (let attempts = 0; attempts < 100; attempts++) {
quadCoord = Gas._genQuadCoord(this.newPosition, width, height);
if (Geometry.distanceSquared(quadCoord, this.oldPosition) <= maxDistanceSquared) {
foundPosition = true;
break;
}
}
if (foundPosition) this.newPosition = quadCoord;
}
} else {
this.newPosition = Vec.clone(this.oldPosition);
}
this.currentPosition = Vec.clone(this.oldPosition);
this.currentRadius = currentStage.oldRadius * this.mapSize;
}
this.oldRadius = currentStage.oldRadius * this.mapSize;
this.newRadius = currentStage.newRadius * this.mapSize;
this.dps = currentStage.dps;
this.dirty = true;
this.completionRatioDirty = true;
if (currentStage.summonAirdrop) {
this.game.summonAirdrop(
this.game.map.getRandomPosition(
new CircleHitbox(15),
{
maxAttempts: 500,
spawnMode: MapObjectSpawnMode.GrassAndSand,
collides: position => Geometry.distanceSquared(position, this.currentPosition) >= this.newRadius ** 2
}
) ?? this.newPosition
);
}
// Start the next stage
if (duration !== 0) {
this.game.addTimeout(() => this.advanceGasStage(), duration * 1000);
}
}