mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-02-10 18:57:39 +07:00
Complete planet panning
This commit is contained in:
parent
99d3e9ae7c
commit
a5e469e96c
@ -6,12 +6,12 @@ varying vec4 v_col;
|
||||
|
||||
const vec3 ambientColor = vec3(1.0);
|
||||
const vec3 ambientDir = normalize(vec3(1.0, 1.0, 1.0));
|
||||
const vec3 diffuse = vec3(0.5);
|
||||
const vec3 diffuse = vec3(0);
|
||||
const vec3 v1 = vec3(1.0, 0.0, 1.0);
|
||||
const vec3 v2 = vec3(1.0, 0.5, 0.0);
|
||||
|
||||
void main(){
|
||||
vec3 norc = ambientColor * clamp((dot(a_normal, ambientDir) + 1.0) / 2.0, 0.0, 1.0);
|
||||
vec3 norc = ambientColor * lerp(vec3(1.0), diffuse, 1.0 - clamp((dot(a_normal, ambientDir) + 1.0) / 2.0, 0.0, 1.0));
|
||||
|
||||
v_col = a_color * vec4(norc, 1.0);
|
||||
gl_Position = u_projModelView * a_position;
|
||||
|
@ -4,7 +4,6 @@ import mindustry.ctype.*;
|
||||
import mindustry.game.Objectives.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.maps.generators.*;
|
||||
import mindustry.maps.zonegen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static arc.struct.Array.with;
|
||||
@ -31,7 +30,8 @@ public class Zones implements ContentList{
|
||||
resources = with(copper, scrap, lead);
|
||||
}};
|
||||
|
||||
desertWastes = new Zone("desertWastes", starter, new DesertWastesGenerator()){{
|
||||
//TODO remove
|
||||
desertWastes = new Zone("desertWastes", starter, new FileMapGenerator("groundZero")){{
|
||||
startingItems = list(copper, 120);
|
||||
conditionWave = 20;
|
||||
launchPeriod = 10;
|
||||
|
@ -5,6 +5,7 @@ import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.g3d.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.graphics.PlanetGrid.*;
|
||||
@ -16,9 +17,9 @@ import mindustry.world.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PlanetRenderer implements PlanetGenerator{
|
||||
private final Color outlineColor = Pal.accent.cpy().a(0.7f);
|
||||
private final float camLength = 4f, outlineRad = 1.15f;
|
||||
private final boolean drawRect = true;
|
||||
private static final Color outlineColor = Pal.accent.cpy().a(0.7f);
|
||||
private static final float camLength = 4f, outlineRad = 1.15f;
|
||||
private static final boolean drawRect = false;
|
||||
|
||||
private final PlanetMesh[] outlines = new PlanetMesh[10];
|
||||
private final Camera3D cam = new Camera3D();
|
||||
@ -103,8 +104,24 @@ public class PlanetRenderer implements PlanetGenerator{
|
||||
Vec3 v = Tmp.v33.set(Core.input.mouseX(), Core.input.mouseY(), 0);
|
||||
|
||||
if(Core.input.keyDown(KeyCode.MOUSE_LEFT)){
|
||||
cam.position.rotate(Vec3.Y, (v.x - lastX) / 10);
|
||||
float upV = cam.position.angle(Vec3.Y);
|
||||
float margin = 1;
|
||||
|
||||
//scale X speed depending on polar coordinate
|
||||
float speed = 1f - Math.abs(upV - 90) / 90f;
|
||||
|
||||
cam.position.rotate(cam.up, (v.x - lastX) / 10 * speed);
|
||||
|
||||
//prevent user from scrolling all the way up and glitching it out
|
||||
float amount = (v.y - lastY) / 10;
|
||||
amount = Mathf.clamp(upV + amount, margin, 180f - margin) - upV;
|
||||
|
||||
cam.position.rotate(Tmp.v31.set(cam.up).rotate(cam.direction, 90), amount);
|
||||
}
|
||||
|
||||
//lock to Y coordinate so planet rotation doesn't get confusing
|
||||
cam.up.set(Vec3.Y);
|
||||
|
||||
lastX = v.x;
|
||||
lastY = v.y;
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ public abstract class BasicGenerator implements WorldGenerator{
|
||||
|
||||
tiles.each((x, y) -> {
|
||||
int idx = y*tiles.width + x;
|
||||
float cx = x + noise(x, y, scl, mag) - mag / 2f, cy = y + noise(x, y + 152f, scl, mag) - mag / 2f;
|
||||
float cx = x + noise(x, y, scl, mag) - mag / 2f, cy = y + noise(x + 155f, y + 155f, scl, mag) - mag / 2f;
|
||||
Tile other = tiles.getn(Mathf.clamp((int)cx, 0, tiles.width-1), Mathf.clamp((int)cy, 0, tiles.height-1));
|
||||
blocks[idx] = other.block().id;
|
||||
floors[idx] = other.floor().id;
|
||||
|
@ -6,12 +6,13 @@ import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.maps.zonegen.*;
|
||||
import mindustry.maps.generators.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.schematics;
|
||||
|
||||
//TODO refactor into generic planet class
|
||||
public class TestPlanetGenerator implements PlanetGenerator{
|
||||
Simplex noise = new Simplex();
|
||||
@ -24,9 +25,9 @@ public class TestPlanetGenerator implements PlanetGenerator{
|
||||
Block[][] arr = {
|
||||
{Blocks.water, Blocks.darksandWater, Blocks.darksand, Blocks.darksand, Blocks.darksand, Blocks.darksand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.darksandTaintedWater, Blocks.snow, Blocks.ice},
|
||||
{Blocks.water, Blocks.darksandWater, Blocks.darksand, Blocks.darksand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.darksandTaintedWater, Blocks.snow, Blocks.snow, Blocks.ice},
|
||||
{Blocks.water, Blocks.darksandWater, Blocks.darksand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.darksandTaintedWater, Blocks.snow, Blocks.ice, Blocks.ice},
|
||||
{Blocks.water, Blocks.sandWater, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.iceSnow, Blocks.snow, Blocks.snow, Blocks.ice, Blocks.ice},
|
||||
{Blocks.deepwater, Blocks.water, Blocks.sandWater, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.moss, Blocks.snow, Blocks.snow, Blocks.snow, Blocks.snow, Blocks.ice},
|
||||
{Blocks.water, Blocks.darksandWater, Blocks.darksand, Blocks.sand, Blocks.salt, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.darksandTaintedWater, Blocks.snow, Blocks.ice, Blocks.ice},
|
||||
{Blocks.water, Blocks.sandWater, Blocks.sand, Blocks.salt, Blocks.salt, Blocks.salt, Blocks.sand, Blocks.sand, Blocks.iceSnow, Blocks.snow, Blocks.snow, Blocks.ice, Blocks.ice},
|
||||
{Blocks.deepwater, Blocks.water, Blocks.sandWater, Blocks.sand, Blocks.salt, Blocks.sand, Blocks.sand, Blocks.moss, Blocks.snow, Blocks.snow, Blocks.snow, Blocks.snow, Blocks.ice},
|
||||
{Blocks.deepwater, Blocks.water, Blocks.sandWater, Blocks.sand, Blocks.sand, Blocks.sand, Blocks.moss, Blocks.iceSnow, Blocks.snow, Blocks.snow, Blocks.ice, Blocks.snow, Blocks.ice},
|
||||
{Blocks.deepwater, Blocks.sandWater, Blocks.sand, Blocks.sand, Blocks.moss, Blocks.moss, Blocks.moss, Blocks.snow, Blocks.snow, Blocks.ice, Blocks.ice, Blocks.snow, Blocks.ice},
|
||||
{Blocks.taintedWater, Blocks.darksandTaintedWater, Blocks.darksand, Blocks.darksand, Blocks.darksandTaintedWater, Blocks.moss, Blocks.snow, Blocks.snow, Blocks.snow, Blocks.ice, Blocks.snow, Blocks.ice, Blocks.ice},
|
||||
@ -55,7 +56,9 @@ public class TestPlanetGenerator implements PlanetGenerator{
|
||||
|
||||
@Override
|
||||
public Color getColor(Vec3 position){
|
||||
return getBlock(position).color;
|
||||
Block block = getBlock(position);
|
||||
//replace salt with sand color
|
||||
return block == Blocks.salt ? Blocks.sand.color : block.color;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -77,77 +80,7 @@ public class TestPlanetGenerator implements PlanetGenerator{
|
||||
this.tiles = tiles;
|
||||
this.sector = sec;
|
||||
|
||||
//OvergrowthGenerator generator = new OvergrowthGenerator(tiles.width, tiles.height);
|
||||
//generator.init(Loadouts.basicNucleus);
|
||||
//generator.generate(tiles);
|
||||
GridBits write = new GridBits(tiles.width, tiles.height);
|
||||
GridBits read = new GridBits(tiles.width, tiles.height);
|
||||
|
||||
//double removalChance = 0.2;
|
||||
//remove random tiles
|
||||
//tiles.each((x, y) -> {
|
||||
|
||||
//});
|
||||
|
||||
tiles.each((x, y) -> read.set(x, y, !tiles.get(x, y).block().isAir()));
|
||||
|
||||
int iterations = 4, birthLimit = 16, deathLimit = 16, radius = 3;
|
||||
|
||||
for(int i = 0; i < iterations; i++){
|
||||
tiles.each((x, y) -> {
|
||||
int alive = 0;
|
||||
|
||||
for(int cx = -radius; cx <= radius; cx++){
|
||||
for(int cy = -radius; cy <= radius; cy++){
|
||||
if((cx == 0 && cy == 0) || !Mathf.within(cx, cy, radius)) continue;
|
||||
if(!Structs.inBounds(x + cx, y + cy, tiles.width, tiles.height) || read.get(x + cx, y + cy)){
|
||||
alive++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(read.get(x, y)){
|
||||
write.set(x, y, alive >= deathLimit);
|
||||
}else{
|
||||
write.set(x, y, alive > birthLimit);
|
||||
}
|
||||
});
|
||||
|
||||
//flush results
|
||||
read.set(write);
|
||||
}
|
||||
|
||||
tiles.each((x, y) -> tiles.get(x, y).setBlock(!read.get(x, y) ? Blocks.air : tiles.get(x, y).floor().wall));
|
||||
distort(0.009f, 12f);
|
||||
|
||||
OvergrowthGenerator gen = new OvergrowthGenerator();
|
||||
gen.generate(tiles);
|
||||
|
||||
//tiles.get(tiles.width /2, tiles.height /2).setBlock(Blocks.coreShard, Team.sharded);
|
||||
}
|
||||
|
||||
void distort(float scl, float mag){
|
||||
short[] blocks = new short[tiles.width * tiles.height];
|
||||
short[] floors = new short[blocks.length];
|
||||
|
||||
tiles.each((x, y) -> {
|
||||
int idx = y*tiles.width + x;
|
||||
float cx = x + noise(x, y, scl, mag) - mag / 2f, cy = y + noise(x, y + 152f, scl, mag) - mag / 2f;
|
||||
Tile other = tiles.getn(Mathf.clamp((int)cx, 0, tiles.width-1), Mathf.clamp((int)cy, 0, tiles.height-1));
|
||||
blocks[idx] = other.block().id;
|
||||
floors[idx] = other.floor().id;
|
||||
});
|
||||
|
||||
for(int i = 0; i < blocks.length; i++){
|
||||
Tile tile = tiles.geti(i);
|
||||
tile.setFloor(Vars.content.block(floors[i]).asFloor());
|
||||
tile.setBlock(Vars.content.block(blocks[i]));
|
||||
}
|
||||
}
|
||||
|
||||
protected float noise(float x, float y, float scl, float mag){
|
||||
Vec3 v = sector.rect.project(x / tiles.width, y / tiles.height);
|
||||
return (float)noise.octaveNoise3D(1f, 0f, 1f / scl, v.x, v.y, v.z) * mag;
|
||||
new Terrain().generate(tiles);
|
||||
}
|
||||
|
||||
Block getBlock(Vec3 position){
|
||||
@ -162,4 +95,96 @@ public class TestPlanetGenerator implements PlanetGenerator{
|
||||
|
||||
return arr[Mathf.clamp((int)(temp * arr.length), 0, arr.length - 1)][Mathf.clamp((int)(height * arr[0].length), 0, arr[0].length - 1)];
|
||||
}
|
||||
|
||||
class Terrain extends BasicGenerator{
|
||||
Array<Block> ores = Array.with(Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreCopper);
|
||||
|
||||
@Override
|
||||
protected void generate(){
|
||||
|
||||
cells(4);
|
||||
distort(20f, 12f);
|
||||
|
||||
float constraint = 1.3f;
|
||||
float radius = width / 2f / Mathf.sqrt3;
|
||||
int rooms = Mathf.random(2, 5);
|
||||
Array<Point3> array = new Array<>();
|
||||
|
||||
//TODO replace random calls with seed
|
||||
|
||||
for(int i = 0; i < rooms; i++){
|
||||
Tmp.v1.trns(Mathf.random(360f), Mathf.random(radius / constraint));
|
||||
float rx = (width/2f + Tmp.v1.x);
|
||||
float ry = (height/2f + Tmp.v1.y);
|
||||
float maxrad = radius - Tmp.v1.len();
|
||||
float rrad = Math.min(Mathf.random(9f, maxrad / 2f), 30f);
|
||||
array.add(new Point3((int)rx, (int)ry, (int)rrad));
|
||||
}
|
||||
|
||||
for(Point3 room : array){
|
||||
erase(room.x, room.y, room.z);
|
||||
}
|
||||
|
||||
int connections = Mathf.random(Math.max(rooms - 1, 1), rooms + 3);
|
||||
for(int i = 0; i < connections; i++){
|
||||
Point3 from = array.random();
|
||||
Point3 to = array.random();
|
||||
|
||||
float nscl = Mathf.random(20f, 60f);
|
||||
int stroke = Mathf.random(4, 12);
|
||||
brush(pathfind(from.x, from.y, to.x, to.y, tile -> (tile.solid() ? 5f : 0f) + (float)sim.octaveNoise2D(1, 1, 1f / nscl, tile.x, tile.y) * 50, manhattan), stroke);
|
||||
}
|
||||
|
||||
cells(1);
|
||||
distort(20f, 6f);
|
||||
|
||||
Point3 spawn = array.random();
|
||||
inverseFloodFill(tiles.getn(spawn.x, spawn.y));
|
||||
|
||||
ores(ores);
|
||||
|
||||
for(Point3 other : array){
|
||||
if(other != spawn){
|
||||
// tiles.getn(other.x, other.y).setOverlay(Blocks.spawn);
|
||||
}
|
||||
}
|
||||
|
||||
schematics.placeLoadout(Loadouts.advancedShard, spawn.x, spawn.y);
|
||||
}
|
||||
|
||||
void cells(int iterations){
|
||||
GridBits write = new GridBits(tiles.width, tiles.height);
|
||||
GridBits read = new GridBits(tiles.width, tiles.height);
|
||||
|
||||
tiles.each((x, y) -> read.set(x, y, !tiles.get(x, y).block().isAir()));
|
||||
|
||||
int birthLimit = 16, deathLimit = 16, cradius = 3;
|
||||
|
||||
for(int i = 0; i < iterations; i++){
|
||||
tiles.each((x, y) -> {
|
||||
int alive = 0;
|
||||
|
||||
for(int cx = -cradius; cx <= cradius; cx++){
|
||||
for(int cy = -cradius; cy <= cradius; cy++){
|
||||
if((cx == 0 && cy == 0) || !Mathf.within(cx, cy, cradius)) continue;
|
||||
if(!Structs.inBounds(x + cx, y + cy, tiles.width, tiles.height) || read.get(x + cx, y + cy)){
|
||||
alive++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(read.get(x, y)){
|
||||
write.set(x, y, alive >= deathLimit);
|
||||
}else{
|
||||
write.set(x, y, alive > birthLimit);
|
||||
}
|
||||
});
|
||||
|
||||
//flush results
|
||||
read.set(write);
|
||||
}
|
||||
|
||||
tiles.each((x, y) -> tiles.get(x, y).setBlock(!read.get(x, y) ? Blocks.air : tiles.get(x, y).floor().wall));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
package mindustry.maps.zonegen;
|
||||
|
||||
import mindustry.maps.generators.*;
|
||||
|
||||
//TODO remove
|
||||
public class DesertWastesGenerator extends BasicGenerator{
|
||||
|
||||
/*
|
||||
public DesertWastesGenerator(int width, int height){
|
||||
super(width, height, Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreCopper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(int x, int y){
|
||||
floor = Blocks.sand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decorate(Tiles tiles){
|
||||
ores(tiles);
|
||||
terrain(tiles, Blocks.sandRocks, 60f, 1.5f, 0.9f);
|
||||
|
||||
int rand = 40;
|
||||
int border = 25;
|
||||
int spawnX = Mathf.clamp(30 + Mathf.range(rand), border, width - border), spawnY = Mathf.clamp(30 + Mathf.range(rand), border, height - border);
|
||||
int endX = Mathf.clamp(width - 30 + Mathf.range(rand), border, width - border), endY = Mathf.clamp(height - 30 + Mathf.range(rand), border, height - border);
|
||||
|
||||
brush(tiles, pathfind(tiles, spawnX, spawnY, endX, endY, tile -> tile.solid() ? 5f : 0f, manhattan), 6);
|
||||
brush(tiles, pathfind(tiles, spawnX, spawnY, endX, endY, tile -> tile.solid() ? 4f : 0f + (float)sim.octaveNoise2D(1, 1, 1f / 40f, tile.x, tile.y) * 20, manhattan), 5);
|
||||
|
||||
erase(tiles, endX, endY, 10);
|
||||
erase(tiles, spawnX, spawnY, 20);
|
||||
distort(tiles, 20f, 4f);
|
||||
inverseFloodFill(tiles, tiles.getn(spawnX, spawnY));
|
||||
|
||||
noise(tiles, Blocks.salt, Blocks.saltRocks, 5, 0.6f, 200f, 0.55f);
|
||||
noise(tiles, Blocks.darksand, Blocks.duneRocks, 5, 0.7f, 120f, 0.5f);
|
||||
|
||||
tech(tiles);
|
||||
overlay(tiles, Blocks.sand, Blocks.pebbles, 0.15f, 5, 0.8f, 30f, 0.62f);
|
||||
//scatter(tiles, Blocks.sandRocks, Blocks.creeptree, 1f);
|
||||
|
||||
tiles.getn(endX, endY).setOverlay(Blocks.spawn);
|
||||
schematics.placeLoadout(loadout, spawnX, spawnY);
|
||||
}*/
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
package mindustry.maps.zonegen;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.maps.generators.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.schematics;
|
||||
|
||||
//TODO remove
|
||||
public class OvergrowthGenerator extends BasicGenerator{
|
||||
Array<Block> ores = Array.with(Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreCopper);
|
||||
|
||||
@Override
|
||||
protected void generate(){
|
||||
//terrain(tiles, Blocks.sporePine, 70f, 1.4f, 1f);
|
||||
|
||||
//int rand = 40;
|
||||
//int border = 25;
|
||||
//int spawnX = Mathf.clamp(30 + Mathf.range(rand), border, width - border), spawnY = Mathf.clamp(30 + Mathf.range(rand), border, height - border);
|
||||
//int endX = Mathf.clamp(width - 30 + Mathf.range(rand), border, width - border), endY = Mathf.clamp(height - 30 + Mathf.range(rand), border, height - border);
|
||||
|
||||
float constraint = 1.3f;
|
||||
float radius = width / 2f / Mathf.sqrt3;
|
||||
int rooms = Mathf.random(2, 5);
|
||||
Array<Point3> array = new Array<>();
|
||||
|
||||
//TODO replace random calls with seed
|
||||
|
||||
for(int i = 0; i < rooms; i++){
|
||||
Tmp.v1.trns(Mathf.random(360f), Mathf.random(radius / constraint));
|
||||
float rx = (width/2f + Tmp.v1.x);
|
||||
float ry = (height/2f + Tmp.v1.y);
|
||||
float maxrad = radius - Tmp.v1.len();
|
||||
float rrad = Math.min(Mathf.random(9f, maxrad), 40f);
|
||||
array.add(new Point3((int)rx, (int)ry, (int)rrad));
|
||||
}
|
||||
|
||||
for(Point3 room : array){
|
||||
erase(room.x, room.y, room.z);
|
||||
}
|
||||
|
||||
int connections = Mathf.random(Math.max(rooms - 1, 1), rooms + 3);
|
||||
for(int i = 0; i < connections; i++){
|
||||
Point3 from = array.random();
|
||||
Point3 to = array.random();
|
||||
|
||||
float nscl = Mathf.random(20f, 60f);
|
||||
int stroke = Mathf.random(4, 12);
|
||||
brush(pathfind(from.x, from.y, to.x, to.y, tile -> (tile.solid() ? 5f : 0f) + (float)sim.octaveNoise2D(1, 1, 1f / nscl, tile.x, tile.y) * 50, manhattan), stroke);
|
||||
}
|
||||
|
||||
//
|
||||
//brush(tiles, pathfind(tiles, spawnX, spawnY, endX, endY, tile -> (tile.solid() ? 4f : 0f) + (float)sim.octaveNoise2D(1, 1, 1f / 90f, tile.x+999, tile.y) * 70, manhattan), 5);
|
||||
|
||||
//
|
||||
//erase(tiles, spawnX, spawnY, 20);
|
||||
distort(20f, 6f);
|
||||
|
||||
Point3 spawn = array.random();
|
||||
inverseFloodFill(tiles.getn(spawn.x, spawn.y));
|
||||
|
||||
ores(ores);
|
||||
|
||||
for(Point3 other : array){
|
||||
if(other != spawn){
|
||||
// tiles.getn(other.x, other.y).setOverlay(Blocks.spawn);
|
||||
}
|
||||
}
|
||||
|
||||
//noise(tiles, Blocks.darksandTaintedWater, Blocks.duneRocks, 4, 0.7f, 120f, 0.64f);
|
||||
//scatter(tiles, Blocks.sporePine, Blocks.whiteTreeDead, 1f);
|
||||
|
||||
//tiles.getn(endX, endY).setOverlay(Blocks.spawn);
|
||||
schematics.placeLoadout(Loadouts.advancedShard, spawn.x, spawn.y);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user