forked from 0ad/0ad
New random map script "Flood". Created by _kali and niektb, refs #3455.
This was SVN commit r17844.
This commit is contained in:
parent
35a13c5080
commit
ea73e6b3b4
BIN
binaries/data/mods/public/art/textures/ui/session/icons/mappreview/flood.png
(Stored with Git LFS)
Normal file
BIN
binaries/data/mods/public/art/textures/ui/session/icons/mappreview/flood.png
(Stored with Git LFS)
Normal file
Binary file not shown.
455
binaries/data/mods/public/maps/random/flood.js
Normal file
455
binaries/data/mods/public/maps/random/flood.js
Normal file
@ -0,0 +1,455 @@
|
||||
RMS.LoadLibrary("rmgen");
|
||||
|
||||
let random_terrain;
|
||||
do
|
||||
random_terrain = randomizeBiome();
|
||||
while (random_terrain == 6);
|
||||
|
||||
const tMainTerrain = rBiomeT1();
|
||||
const tForestFloor1 = rBiomeT2();
|
||||
const tForestFloor2 = rBiomeT3();
|
||||
const tCliff = rBiomeT4();
|
||||
const tTier1Terrain = rBiomeT5();
|
||||
const tTier2Terrain = rBiomeT6();
|
||||
const tTier3Terrain = rBiomeT7();
|
||||
const tRoad = rBiomeT10();
|
||||
const tRoadWild = rBiomeT11();
|
||||
const tTier4Terrain = rBiomeT12();
|
||||
const tShore = rBiomeT14();
|
||||
const tWater = rBiomeT15();
|
||||
let tHill = rBiomeT8();
|
||||
let tDirt = rBiomeT9();
|
||||
|
||||
if (random_terrain == 1)
|
||||
{
|
||||
tDirt = ["medit_shrubs_a", "grass_field"];
|
||||
tHill = ["grass_field", "peat_temp"];
|
||||
}
|
||||
|
||||
// Gaia entities
|
||||
const oTree1 = rBiomeE1();
|
||||
const oTree2 = rBiomeE2();
|
||||
const oTree3 = rBiomeE3();
|
||||
const oTree4 = rBiomeE4();
|
||||
const oTree5 = rBiomeE5();
|
||||
const oFruitBush = rBiomeE6();
|
||||
const oChicken = rBiomeE7();
|
||||
const oMainHuntableAnimal = rBiomeE8();
|
||||
const oFish = rBiomeE9();
|
||||
const oSecondaryHuntableAnimal = rBiomeE10();
|
||||
const oStoneLarge = rBiomeE11();
|
||||
const oMetalLarge = rBiomeE13();
|
||||
|
||||
// Decorative props
|
||||
const aGrass = rBiomeA1();
|
||||
const aGrassShort = rBiomeA2();
|
||||
const aRockLarge = rBiomeA5();
|
||||
const aRockMedium = rBiomeA6();
|
||||
const aBushMedium = rBiomeA7();
|
||||
const aBushSmall = rBiomeA8();
|
||||
|
||||
const pForest1 = [tForestFloor2 + TERRAIN_SEPARATOR + oTree1, tForestFloor2 + TERRAIN_SEPARATOR + oTree2, tForestFloor2];
|
||||
const pForest2 = [tForestFloor1 + TERRAIN_SEPARATOR + oTree4, tForestFloor1 + TERRAIN_SEPARATOR + oTree5, tForestFloor1];
|
||||
const BUILDING_ANGlE = -PI/4;
|
||||
|
||||
log("Initializing map...");
|
||||
InitMap();
|
||||
|
||||
const radius = scaleByMapSize(15, 25);
|
||||
const elevation = 2;
|
||||
const shoreRadius = 6;
|
||||
const numPlayers = getNumPlayers();
|
||||
const mapSize = getMapSize();
|
||||
const mapArea = mapSize * mapSize;
|
||||
const centerOfMap = mapSize / 2;
|
||||
|
||||
// Create tile classes
|
||||
let clPlayer = createTileClass();
|
||||
let clHill = createTileClass();
|
||||
let clMountain = createTileClass();
|
||||
let clForest = createTileClass();
|
||||
let clWater = createTileClass();
|
||||
let clDirt = createTileClass();
|
||||
let clRock = createTileClass();
|
||||
let clMetal = createTileClass();
|
||||
let clFood = createTileClass();
|
||||
let clBaseResource = createTileClass();
|
||||
|
||||
for (let ix = 0; ix < mapSize; ++ix)
|
||||
for (let iz = 0; iz < mapSize; ++iz)
|
||||
placeTerrain(ix, iz, tWater);
|
||||
|
||||
// Randomize player order
|
||||
let playerIDs = [];
|
||||
for (let i = 0; i < numPlayers; ++i)
|
||||
playerIDs.push(i+1);
|
||||
playerIDs = sortPlayers(playerIDs);
|
||||
|
||||
// Place players
|
||||
let playerX = [];
|
||||
let playerZ = [];
|
||||
let playerAngle = [];
|
||||
|
||||
let startAngle = randFloat(0, TWO_PI);
|
||||
for (let i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
playerAngle[i] = startAngle + i * TWO_PI/numPlayers;
|
||||
playerX[i] = 0.5 + 0.38 * cos(playerAngle[i]);
|
||||
playerZ[i] = 0.5 + 0.38 * sin(playerAngle[i]);
|
||||
}
|
||||
|
||||
let fx = fractionToTiles(0.5);
|
||||
let fz = fractionToTiles(0.5);
|
||||
let ix = round(fx);
|
||||
let iz = round(fz);
|
||||
|
||||
// Create the water
|
||||
let placer = new ClumpPlacer(mapArea * 1, 1, 1, 1, ix, iz);
|
||||
let terrainPainter = new LayeredPainter(
|
||||
[tWater, tWater, tShore], // terrains
|
||||
[1, 4] // widths
|
||||
);
|
||||
let elevationPainter = new SmoothElevationPainter(
|
||||
ELEVATION_SET, // type
|
||||
getMapBaseHeight(), // elevation
|
||||
2 // blend radius
|
||||
);
|
||||
createArea(placer, [terrainPainter, elevationPainter, paintClass(clWater)], avoidClasses(clPlayer, 5));
|
||||
|
||||
for (let i = 0; i < numPlayers; ++i)
|
||||
{
|
||||
let id = playerIDs[i];
|
||||
log("Creating base for player " + id + "...");
|
||||
|
||||
// Get the x and z in tiles
|
||||
let fx = fractionToTiles(playerX[i]);
|
||||
let fz = fractionToTiles(playerZ[i]);
|
||||
let ix = round(fx);
|
||||
let iz = round(fz);
|
||||
|
||||
let hillSize = PI * radius * radius * 2;
|
||||
|
||||
// Create the hill
|
||||
let placer = new ClumpPlacer(hillSize, 0.80, 0.1, 10, ix, iz);
|
||||
let terrainPainter = new LayeredPainter(
|
||||
[tShore, tMainTerrain], // terrains
|
||||
[shoreRadius] // widths
|
||||
);
|
||||
let elevationPainter = new SmoothElevationPainter(
|
||||
ELEVATION_SET, // type
|
||||
elevation, // elevation
|
||||
shoreRadius // blend radius
|
||||
);
|
||||
createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill)], null);
|
||||
|
||||
// Mark a small area around the player's starting coördinates with the clPlayer class
|
||||
addToClass(ix, iz, clPlayer);
|
||||
addToClass(ix + 5, iz, clPlayer);
|
||||
addToClass(ix, iz + 5, clPlayer);
|
||||
addToClass(ix - 5, iz, clPlayer);
|
||||
addToClass(ix, iz - 5, clPlayer);
|
||||
|
||||
placeCivDefaultEntities(fx, fz, id, BUILDING_ANGlE, { "iberWall": false });
|
||||
|
||||
// Create the city patch
|
||||
let cityRadius = radius/3;
|
||||
placer = new ClumpPlacer(PI * cityRadius * cityRadius, 0.6, 0.3, 10, ix, iz);
|
||||
let painter = new LayeredPainter([tRoadWild, tRoad], [1]);
|
||||
createArea(placer, painter, null);
|
||||
|
||||
// Create animals
|
||||
for (let j = 0; j < 2; ++j)
|
||||
{
|
||||
let aAngle = randFloat(0, TWO_PI);
|
||||
let aDist = 7;
|
||||
let aX = round(fx + aDist * cos(aAngle));
|
||||
let aZ = round(fz + aDist * sin(aAngle));
|
||||
let group = new SimpleGroup(
|
||||
[new SimpleObject(oChicken, 5, 5, 0, 2)],
|
||||
true, clBaseResource, aX, aZ
|
||||
);
|
||||
createObjectGroup(group, 0);
|
||||
}
|
||||
|
||||
// Create berry bushes
|
||||
let bbAngle = randFloat(0, TWO_PI);
|
||||
let bbDist = 12;
|
||||
let bbX = round(fx + bbDist * cos(bbAngle));
|
||||
let bbZ = round(fz + bbDist * sin(bbAngle));
|
||||
let group = new SimpleGroup(
|
||||
[new SimpleObject(oFruitBush, 5, 5, 0, 3)],
|
||||
true, clBaseResource, bbX, bbZ
|
||||
);
|
||||
createObjectGroup(group, 0);
|
||||
|
||||
// Create metal mine
|
||||
let mAngle = bbAngle;
|
||||
while (abs(mAngle - bbAngle) < PI/3)
|
||||
mAngle = randFloat(0, TWO_PI);
|
||||
|
||||
let mDist = 12;
|
||||
let mX = round(fx + mDist * cos(mAngle));
|
||||
let mZ = round(fz + mDist * sin(mAngle));
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(oMetalLarge, 1, 1, 0, 0)],
|
||||
true, clBaseResource, mX, mZ
|
||||
);
|
||||
createObjectGroup(group, 0);
|
||||
|
||||
// Create stone mines
|
||||
mAngle += randFloat(PI/8, PI/4);
|
||||
mX = round(fx + mDist * cos(mAngle));
|
||||
mZ = round(fz + mDist * sin(mAngle));
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(oStoneLarge, 1, 1, 0, 2)],
|
||||
true, clBaseResource, mX, mZ
|
||||
);
|
||||
createObjectGroup(group, 0);
|
||||
|
||||
// Create starting trees, should avoid mines and bushes
|
||||
let tries = 50;
|
||||
let tDist = 16;
|
||||
let num = 50;
|
||||
for (let x = 0; x < tries; ++x)
|
||||
{
|
||||
let tAngle = randFloat(0, TWO_PI);
|
||||
let tX = round(fx + tDist * cos(tAngle));
|
||||
let tZ = round(fz + tDist * sin(tAngle));
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(oTree2, num, num, 0, 7)],
|
||||
true, clBaseResource, tX, tZ
|
||||
);
|
||||
if (createObjectGroup(group, 0, avoidClasses(clBaseResource, 5)))
|
||||
break;
|
||||
}
|
||||
|
||||
// Create grass tufts
|
||||
num = (PI * radius * radius) / 250;
|
||||
for (let j = 0; j < num; ++j)
|
||||
{
|
||||
let gAngle = randFloat(0, TWO_PI);
|
||||
let gDist = radius - (5 + randInt(7));
|
||||
let gX = round(fx + gDist * cos(gAngle));
|
||||
let gZ = round(fz + gDist * sin(gAngle));
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(aGrassShort, 2, 5, 0, 1, -PI/8, PI/8)],
|
||||
false, clBaseResource, gX, gZ
|
||||
);
|
||||
createObjectGroup(group, 0);
|
||||
}
|
||||
}
|
||||
|
||||
RMS.SetProgress(40);
|
||||
|
||||
// Create central island
|
||||
placer = new ChainPlacer(floor(scaleByMapSize(6, 6)), floor(scaleByMapSize(10, 15)), floor(scaleByMapSize(200, 300)), 1, centerOfMap, centerOfMap, 0, [floor(mapSize * 0.01)]);
|
||||
terrainPainter = new LayeredPainter(
|
||||
[tShore, tMainTerrain], // terrains
|
||||
[shoreRadius, 100] // widths
|
||||
);
|
||||
elevationPainter = new SmoothElevationPainter(
|
||||
ELEVATION_SET, // type
|
||||
elevation, // elevation
|
||||
shoreRadius // blend radius
|
||||
);
|
||||
createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill)], avoidClasses(clPlayer, 40));
|
||||
|
||||
let randMountains = 20 + randInt(15);
|
||||
for (let m = 0; m < randMountains; ++m)
|
||||
{
|
||||
let randX = randInt(mapSize);
|
||||
let randY = randInt(mapSize);
|
||||
let placer = new ChainPlacer(floor(scaleByMapSize(7, 7)), floor(scaleByMapSize(15, 15)), floor(scaleByMapSize(15, 20)), 1, randX, randY, 0, [floor(mapSize * 0.01)]);
|
||||
let elevRand = 6 + randInt(15);
|
||||
let terrainPainter = new LayeredPainter(
|
||||
[tDirt, tHill], // terrains
|
||||
[floor(elevRand / 3), 40] // widths
|
||||
);
|
||||
let elevationPainter = new SmoothElevationPainter(
|
||||
ELEVATION_SET, // type
|
||||
elevRand, // elevation
|
||||
floor(elevRand / 3) // blend radius
|
||||
);
|
||||
createArea(placer, [terrainPainter, elevationPainter, paintClass(clHill)], [avoidClasses(clBaseResource, 2, clPlayer, 40), stayClasses(clHill, 6)]);
|
||||
}
|
||||
|
||||
randMountains = 8 + randInt(10);
|
||||
for (let m = 0; m < randMountains; ++m)
|
||||
{
|
||||
let randX = randInt(mapSize);
|
||||
let randY = randInt(mapSize);
|
||||
let placer = new ChainPlacer(floor(scaleByMapSize(5, 5)), floor(scaleByMapSize(8, 8)), floor(scaleByMapSize(15, 20)), 1, randX, randY, 0, [floor(mapSize * 0.01)]);
|
||||
let elevRand = 15 + randInt(15);
|
||||
let terrainPainter = new LayeredPainter(
|
||||
[tCliff, tForestFloor2], // terrains
|
||||
[floor(elevRand / 3), 40] // widths
|
||||
);
|
||||
let elevationPainter = new SmoothElevationPainter(
|
||||
ELEVATION_MODIFY, // type
|
||||
elevRand, // elevation
|
||||
floor(elevRand / 3) // blend radius
|
||||
);
|
||||
createArea(placer, [terrainPainter, elevationPainter, paintClass(clMountain)], [avoidClasses(clBaseResource, 2, clPlayer, 40), stayClasses(clHill, 6)]);
|
||||
}
|
||||
|
||||
// Create center bounty
|
||||
let group = new SimpleGroup(
|
||||
[new SimpleObject(oMetalLarge, 3, 6, 25, floor(mapSize * 0.25))],
|
||||
true, clBaseResource, centerOfMap, centerOfMap
|
||||
);
|
||||
createObjectGroup(group, 0, [avoidClasses(clBaseResource, 20, clPlayer, 40, clMountain, 4), stayClasses(clHill, 10)]);
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(oStoneLarge, 3, 6, 25, floor(mapSize * 0.25))],
|
||||
true, clBaseResource, centerOfMap, centerOfMap
|
||||
);
|
||||
createObjectGroup(group, 0, [avoidClasses(clBaseResource, 20, clPlayer, 40, clMountain, 4), stayClasses(clHill, 10)]);
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(oMainHuntableAnimal, floor(6 * numPlayers), floor(6 * numPlayers), 2, floor(mapSize * 0.1))],
|
||||
true, clBaseResource, centerOfMap, centerOfMap
|
||||
);
|
||||
createObjectGroup(group, 0, [avoidClasses(clBaseResource, 2, clMountain, 4, clPlayer, 40, clWater, 2), stayClasses(clHill, 10)]);
|
||||
|
||||
log("Creating fish...");
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(oFish, 2, 3, 0, 2)],
|
||||
true, clFood
|
||||
);
|
||||
createObjectGroups(group, 0,
|
||||
avoidClasses(clHill, 10, clFood, 20),
|
||||
10 * numPlayers, 60
|
||||
);
|
||||
|
||||
createForests(
|
||||
[tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2],
|
||||
[avoidClasses(clPlayer, 25, clForest, 10, clBaseResource, 3, clMetal, 3, clRock, 3, clMountain, 2), stayClasses(clHill, 6)],
|
||||
clForest,
|
||||
0.7,
|
||||
random_terrain
|
||||
);
|
||||
|
||||
log("Creating straggeler trees...");
|
||||
let types = [oTree1, oTree2, oTree4, oTree3];
|
||||
createStragglerTrees(types, [avoidClasses(clBaseResource, 2, clMetal, 3, clRock, 3, clMountain, 2, clPlayer, 25), stayClasses(clHill, 6)]);
|
||||
|
||||
RMS.SetProgress(65);
|
||||
|
||||
log("Creating dirt patches...");
|
||||
let sizes = [scaleByMapSize(3, 6), scaleByMapSize(5, 10), scaleByMapSize(8, 21)];
|
||||
let numb = random_terrain == 6 ? 3 : 1;
|
||||
|
||||
for (let i = 0; i < sizes.length; ++i)
|
||||
{
|
||||
placer = new ChainPlacer(1, floor(scaleByMapSize(3, 5)), sizes[i], 0.5);
|
||||
let painter = new LayeredPainter(
|
||||
[[tMainTerrain, tTier1Terrain], [tTier1Terrain, tTier2Terrain], [tTier2Terrain, tTier3Terrain]], // terrains
|
||||
[1, 1] // widths
|
||||
);
|
||||
createAreas(
|
||||
placer,
|
||||
[painter, paintClass(clDirt)],
|
||||
avoidClasses(clForest, 0, clMountain, 0, clDirt, 5, clPlayer, 10),
|
||||
numb * scaleByMapSize(15, 45)
|
||||
);
|
||||
}
|
||||
|
||||
log("Painting shorelines...");
|
||||
paintTerrainBasedOnHeight(1, 2, 0, tMainTerrain);
|
||||
paintTerrainBasedOnHeight(getMapBaseHeight(), 1, 3, tTier1Terrain);
|
||||
|
||||
log("Creating grass patches...");
|
||||
sizes = [scaleByMapSize(2, 4), scaleByMapSize(3, 7), scaleByMapSize(5, 15)];
|
||||
for (let i = 0; i < sizes.length; ++i)
|
||||
{
|
||||
placer = new ChainPlacer(1, floor(scaleByMapSize(3, 5)), sizes[i], 0.5);
|
||||
let painter = new TerrainPainter(tTier4Terrain);
|
||||
createAreas(
|
||||
placer,
|
||||
painter,
|
||||
avoidClasses(clForest, 0, clMountain, 0, clDirt, 5, clPlayer, 10),
|
||||
numb * scaleByMapSize(15, 45)
|
||||
);
|
||||
}
|
||||
|
||||
log("Creating food...");
|
||||
createFood(
|
||||
[
|
||||
[new SimpleObject(oMainHuntableAnimal, 5, 7, 0, 4)],
|
||||
[new SimpleObject(oSecondaryHuntableAnimal, 2, 3, 0, 2)]
|
||||
],
|
||||
[3 * numPlayers, 3 * numPlayers],
|
||||
[avoidClasses(clForest, 0, clPlayer, 20, clMountain, 1, clFood, 4, clRock, 4, clMetal, 4), stayClasses(clHill, 2)]
|
||||
);
|
||||
|
||||
RMS.SetProgress(75);
|
||||
|
||||
createFood(
|
||||
[
|
||||
[new SimpleObject(oFruitBush, 5, 7, 0, 4)]
|
||||
],
|
||||
[3 * numPlayers],
|
||||
[avoidClasses(clForest, 0, clPlayer, 15, clMountain, 1, clFood, 4, clRock, 4, clMetal, 4), stayClasses(clHill, 2)]
|
||||
);
|
||||
|
||||
RMS.SetProgress(85);
|
||||
|
||||
log("Creating more straggeler trees...");
|
||||
createStragglerTrees(types, avoidClasses(clWater, 5, clForest, 7, clMountain, 1, clPlayer, 30, clMetal, 3, clRock, 3));
|
||||
|
||||
log("Creating decoration...");
|
||||
let planetm = random_terrain == 7 ? 8 : 1;
|
||||
createDecoration
|
||||
(
|
||||
[
|
||||
[new SimpleObject(aRockMedium, 1, 3, 0, 1)],
|
||||
[new SimpleObject(aRockLarge, 1, 2, 0, 1), new SimpleObject(aRockMedium, 1, 3, 0, 2)],
|
||||
[new SimpleObject(aGrassShort, 2, 15, 0, 1, -PI/8, PI/8)],
|
||||
[new SimpleObject(aGrass, 2, 10, 0, 1.8, -PI/8, PI/8), new SimpleObject(aGrassShort, 3, 10, 1.2, 2.5, -PI/8, PI/8)],
|
||||
[new SimpleObject(aBushMedium, 1, 5, 0, 2), new SimpleObject(aBushSmall, 2, 4, 0, 2)]
|
||||
],
|
||||
[
|
||||
scaleByMapSize(16, 262),
|
||||
scaleByMapSize(8, 131),
|
||||
planetm * scaleByMapSize(13, 200),
|
||||
planetm * scaleByMapSize(13, 200),
|
||||
planetm * scaleByMapSize(13, 200)
|
||||
],
|
||||
avoidClasses(clForest, 2, clPlayer, 20, clMountain, 5, clFood, 1, clBaseResource, 2)
|
||||
);
|
||||
|
||||
log("Creating water forests...");
|
||||
createForests(
|
||||
[tMainTerrain, tForestFloor1, tForestFloor2, pForest1, pForest2],
|
||||
avoidClasses(clPlayer, 30, clHill, 10, clFood, 5),
|
||||
clForest,
|
||||
0.1,
|
||||
random_terrain
|
||||
);
|
||||
|
||||
log("Creating grass tufts...");
|
||||
let num = (PI * radius * radius) / 250;
|
||||
for (let j = 0; j < num; ++j)
|
||||
{
|
||||
let gAngle = randFloat(0, TWO_PI);
|
||||
let gDist = radius - (5 + randInt(7));
|
||||
let gX = round(fx + gDist * cos(gAngle));
|
||||
let gZ = round(fz + gDist * sin(gAngle));
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(aGrassShort, 2, 5, 0, 1, -PI / 8, PI / 8)],
|
||||
false, clBaseResource, gX, gZ
|
||||
);
|
||||
createObjectGroup(group, 0, [avoidClasses(clMountain, 2, clPlayer, 2, clDirt, 0), stayClasses(clHill, 8)]);
|
||||
}
|
||||
|
||||
log("Creating small grass tufts...");
|
||||
group = new SimpleGroup(
|
||||
[new SimpleObject(aGrassShort, 1, 2, 0, 1, -PI / 8, PI / 8)]
|
||||
);
|
||||
createObjectGroups(group, 0,
|
||||
[avoidClasses(clMountain, 2, clPlayer, 2, clDirt, 0), stayClasses(clHill, 8)],
|
||||
planetm * scaleByMapSize(13, 200)
|
||||
);
|
||||
|
||||
setSkySet(shuffleArray(["cloudless", "cumulus", "overcast"])[0]);
|
||||
setWaterMurkiness(0.4);
|
||||
|
||||
ExportMap();
|
13
binaries/data/mods/public/maps/random/flood.json
Normal file
13
binaries/data/mods/public/maps/random/flood.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"settings" : {
|
||||
"Name" : "Flood",
|
||||
"Script" : "flood.js",
|
||||
"Description" : "A great flood has moved across the valley enabling ships and troops to battle in chest deep waters.",
|
||||
"BaseTerrain" : ["medit_sand_wet"],
|
||||
"BaseHeight" : -2,
|
||||
"Keywords": ["naval"],
|
||||
"Preview" : "flood.png",
|
||||
"CircularMap" : true,
|
||||
"XXXXXX" : "Optionally define other things here, like we would for a scenario"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user