Arcade Village Blog

The Island: Detailed view of base classes

The classes are in javascript. I assume that you have the knowledge required to understand them.
I will only develop the functions that are useful for understanding the mechanism of the class.

The Bloc class



class Bloc
{
constructor( btype, cfond )
{
this.btype = btype;
this.xc = 0;
this.yc = 0;
this.x = 0;
this.y = 0;
this.width = WCASE;
this.height = HCASE;
this.cfond = cfond;
this.rf = null;
this.recx = new Array(4);
this.recy = new Array(4);
this.irec = 0;
this.dcout = 1; // Coût d'un déplacement sur le bloc
}

setTabPosition(xc, yc)
{
this.xc = xc;
this.yc = yc;
this.x = this.xc * this.width;
this.y = this.yc * this.height;
this.recy[0] = this.y -2;
this.recx[0] = this.x + Math.trunc( Math.random() * this.width );

this.recy[1] = this.y + this.height;
this.recx[1] = this.x + Math.trunc( Math.random() * this.width );

this.recx[2] = this.x - 2;
this.recy[2] = this.y + Math.trunc( Math.random() * this.height );;

this.recx[3] = this.x + this.width;
this.recy[3] = this.y + Math.trunc( Math.random() * this.height );;
}

getType()
{
return this.btype;
}


getNutriment()
{
return 0;
}

getWater()
{
return 0;
}

draw(g, ombre )
{

}
}

The blocks are therefore the basic bricks of the terrain. Each block is built with a particular type and background color.

The setTabPosition function is used to place the block on a position of the game grid.
GetType() returns the type of the block. The types I have already defined are:

const BLOC_TYPE_SOIL = 0;
const BLOC_TYPE_SAND = 1;
const BLOC_TYPE_STONE = 2;
const BLOC_TYPE_WATER = 3;

getNutriment() and getWater() allow the vegetation on the block to feed and grow.

Draw() is called each time the block needs to be drawn. The shadow parameter is transparency. Indeed, the higher the block, the clearer it must be to give an effect of depth. Transparency with a gray background achieves this goal.

For each element, we have a derived Block class. So here is a part of the BlocSand class that represents sand.

class BlocSand extends Bloc
{
constructor()
{
super(BLOC_TYPE_SAND, "#ffffa5");
this.dcout = 3; // Coût d'un déplacement sur le bloc
}

getNutriment()
{
return 3;
}

getWater()
{
return 1;
}

}

And part of the BlocSoil class which represents soil.
class BlocSoil extends Bloc
{
constructor()
{
super(BLOC_TYPE_SOIL, "#856D4D");
}

getNutriment()
{
return 100;
}

getWater()
{
return 100;
}
}


class Plot
{
constructor( maman )
{
this.maman = maman;
this.cx = 0;
this.cy = 0;
this.x = 0;
this.y = 0;
this.width = WCASE;
this.height = HCASE;
this.rf = null;
this.blocs = Array();
this.upbloc = -1;
this.hauteur = 0;
this.xd = 0;
this.yb = 0;
this.tid = 0; // id par rapport au terrain
}

addBloc( ibloc )
{
this.blocs.push( ibloc);
this.upbloc = this.blocs.length -1;
this.hauteur = this.blocs.length;
this.maman.redrawPlotBack( this );
}

eleve()
{
this.addBloc(BlocProvider.get(this.getBlocUp().getType()));
this.blocs[this.upbloc].setTabPosition(this.cx, this.cy);
}

eleveTo( hauteur )
{
while (hauteur > this.hauteur) this.eleve();
}

eleveBloc( ibloc )
{
this.addBloc( ibloc );
this.blocs[this.upbloc].setTabPosition(this.cx, this.cy);
this.maman.redrawPlotBack( this );
}

setBlocUp( ibloc )
{
this.blocs[this.upbloc] = ibloc;
this.blocs[this.upbloc].setTabPosition(this.cx, this.cy);
this.maman.redrawPlotBack( this );
}

getBlocUp()
{
return this.blocs[this.upbloc];
}

getType()
{
return this.blocs[this.upbloc].getType();
}

setTabPosition(xc, yc)
{
this.cx = xc;
this.cy = yc;
this.x = this.cx * this.width;
this.y = this.cy * this.height;
this.xd = this.x + this.width - 1;
this.yb = this.y + this.height - 1;
this.tid = this.cx + this.cy * this.maman.ncases_w;

for (var i = 0 ; i < this.blocs.length ; i++ )
this.blocs[i].setTabPosition(this.cx, this.cy);
}

essaime()
{
return this.maman.essaime( this );
}

getNutriment()
{
return this.blocs[this.upbloc].getNutriment();
}

getWater()
{
return this.blocs[this.upbloc].getWater();
}

attachProducer(fr)
{
this.fr = fr;
}

detachProducer()
{
this.fr = null;
}

hasProducer()
{
return (this.fr != null );
}

getProducer()
{
return this.fr;
}

getRessource( restype )
{
if ( this.hasProducer() )
return this.fr.getRessource( restype );
return 0;
}

preleveRessource( restype, amount )
{
if ( this.fr.rtype != restype )
{
return;
}
this.fr.decRessource( amount );
}

getCoutTraverse(creature)
{
return this.blocs[this.upbloc].getCoutDeplacement() + ( this.hasProducer() ? this.fr.getCoutDeplacement() : 0 );
}

This class must allow all interactions with the playing field. It contains in the block table all the stacked blocks, offers block addition and block modification mechanisms. Creatures and vegetation will only know it, so it offers the possibility of accessing water and nutrients, as well as the various resources found on the plot.

Resources ? What to say?
My goal being to create a survival game, the character must be able to access a certain number of resources. So far, the only resources I've created are
RESOURCE_FOOD, the food
RESOURCE_WATER, water.

Be careful, you must not confuse these resources with the block's getNutriment and getWater functions, which are resources inaccessible to the player or animals, just like the mineral salts contained in the earth are not accessible to animals. Obviously, like other video games, new resources will enrich the game (wood, stone, etc.).
The novelty is that the player will be competing with wildlife for access.
In these three functions,
detachProducer()
hasProducer()
getProducer(),
I am referring to a producer. I will introduce this class in the next chapter because it will allow us to manage vegetation or animals.

The Biome class.


The Biome class is called by Gaia to define the different parts of the terrain. It is a very simple class.

const BIOME_WIDTH = 10;
const BIOME_HEIGHT = 10;

class Biome
{
constructor( terrain, cx, cy )
{
this.terrain = terrain;
this.ae = this.terrain.ae; // Pour simplifier les écritures
this.cx = cx;
this.cy = cy;
}

create()
{

}

addProducers( nproducer, producer_type, bloc_type, max_hauteur)
{
...
}


addNCreatures( creature_type, bloc_type, n)
{
….
}
}

I previously recalled the definition of the Biome, for our game, we can say that a biome contains a number of homogeneous plots offering the same vegetation (vegetation providers) and the same animal species (creatures).

All biomes derive from the Biome class whose create() function they implement.

BiomePasture


This class represents a meadow.

class BiomePasture extends Biome
{
create()
{
this.addProducers(BIOME_WIDTH*BIOME_HEIGHT/2, PRODUCER_HERB, BLOC_TYPE_SOIL, 2); this.addNCreatures( "herbivore1",BLOC_TYPE_SOIL, BIOME_WIDTH*BIOME_HEIGHT/ 20 );
this.addNCreatures("predator1", BLOC_TYPE_SOIL, 4);
}

}

As you can imagine, this biome adds herb producers, herbivores ("herbivore1") and predator type "predator1".

BiomeMountain



class BiomeMountain extends Biome
{
create()
{
...
this.addProducers(Math.trunc(BIOME_WIDTH /4), PRODUCER_FRUIT, BLOC_TYPE_SOIL, 5);
}
}

Here we add some fruit trees.

The Gaia class


This class therefore contains all the plots and from biomes, creates the terrain. I only show you the most useful functions.

class Gaia
{

constructor( ae )
{
this.ae = ae;
this.ncases_w = 36;
this.ncases_h = 36;
//this.ncases_w = 36;
//this.ncases_h = 36;

this.ncases = this.ncases_w*this.ncases_h;
this.cases = new Array(this.ncases);
this.centrex = this.ncases_w / 2 - 1;
this.centrey = this.ncases_h / 2 - 1;

this.biomes = new Array();

...
}


// Fonction de création du terrain
genere()
{
var p = null;
var f = null;
var i = 0;
var xc = 0;
var yc = 0;
var center_x = Math.trunc(this.ncases_w / 2);
var center_y = Math.trunc(this.ncases_h / 2);
var d = 0;
var rayon = Math.max(center_x, center_y);
var biomes = ["pasture","hill_horizontal","pasture",
"hill_vertical", 'lake', 'pasture',
"desert", "pasture", "mountain"];

// Island creation
for ( yc = 0 ; yc < this.ncases_h ; yc++ )
{
for ( xc = 0 ; xc < this.ncases_w ; xc++ )
{
p = new Plot(this);
this.setCase(xc, yc, p);
p.eleveBloc( BlocProvider.get( BLOC_TYPE_STONE ) );
if ( yc == 0 || xc == 0 || xc == this.ncases_w -1 || yc == this.ncases_h -1 )
p.eleveBloc( BlocProvider.get( BLOC_TYPE_WATER ) );
else
{
if ( p.getBlocUp().getType() == BLOC_TYPE_STONE ) // On ajoute de la terre ou de l'eau
{
d = Math.trunc( Math.sqrt( (xc - center_x) * (xc - center_x) + (yc - center_y) * (yc - center_y) ) );
if ( (d > rayon ) || ( d == rayon && Math.random() > .25 ) || ( d == rayon-1 && Math.random() > .5 ))
p.eleveBloc( BlocProvider.get( BLOC_TYPE_WATER ) ); // C'est de l'eau
else
p.eleveBloc( BlocProvider.get( BLOC_TYPE_SOIL ) );
}

}
}
}
// Biomes
for ( yc = 3 ; yc < this.ncases_h - 3 ; yc+= BIOME_HEIGHT )
{
for ( xc = 3 ; xc < this.ncases_w - 3; xc+= BIOME_WIDTH )
{
f = BiomeProvider.get(this, xc, yc, biomes[i++]);
f.create();
this.biomes.push(f);
}
}

}

// Cette fonction est utilisée par un producteur pour essaimer sur les parcelles allentour. C'est pour cela que vous voyez l'herbe se propager
essaime( p )
{
var iadj = Math.trunc(Math.random() * Plot_adjacent_x.length);
var newp = this.getCase( p.cx + Plot_adjacent_x[iadj], p.cy + Plot_adjacent_y[iadj]);
if ( newp == null ) return false;
if ( newp.hasProducer() || newp.getBlocUp().getType() == BLOC_TYPE_WATER ) return false;
if ( newp.hauteur > p.hauteur && Math.random() > .5 ) return false; // Si en hauteur, moins de chance de monter
newp.attachProducer( ProducerProvider.get(this, newp, p.getProducer().getType()));
return true;
}


getCase(xc, yc)
{
if ( xc < 0 || xc >= this.ncases_w || yc < 0 || yc >= this.ncases_h ) return null;
return this.cases[xc+yc*this.ncases_w];
}

setCase(xc, yc, p)
{
this.cases[xc+yc*this.ncases_w] = p;
p.setTabPosition(xc, yc);
}

getHauteur( xc, yc)
{
if ( xc < 0 || xc >= this.ncases_w || yc < 0 || yc >= this.ncases_h ) return 0;
return this.getCase(xc, yc).hauteur;
}

getCX(px)
{
return Math.trunc( px / WCASE - this.xorigine);
}

getCY(py)
{
return Math.trunc( py / HCASE - this.yorigine);
}


// Dessine les parcelles

drawSimple(g)
{
if ( this.bpaint || this.topaint==false) return; // Pour
this.bpaint = true;
var i;
g.setColor("c0c0a0");
g.fillRect(0, 0, this.ae.width, this.ae.height);
for (i= 0 ; i < this.ncases ; i++ )
this.cases[i].draw(g);
this.bpaint = false;
this.topaint = false;
}

redrawPlotBack( p )
{
this.ptoredraw.push(p);
}

redrawBack( g )
{
while ( this.ptoredraw.length > 0 )
{
this.pdraw = this.ptoredraw.pop();
this.pdraw.draw(g);
}
}

//-----------------------------------------------------------------------------------------------
//
// Les ressources
//
//----------------------------------------------------------------------------------------------
drawProducers(g)
{
var i;
for (i= 0 ; i < this.ncases ; i++ )
{
if ( this.cases[i].hasProducer() )
this.cases[i].getProducer().draw(g);

}
}

updateRessources()
{
var i;
for (i= 0 ; i < this.ncases ; i++ )
{
if ( this.cases[i].hasProducer() )
this.cases[i].getProducer().grow();
}
}

getProducer(xc, yc)
{
if ( xc < 0 || yc < 0 || xc >= this.ncases_w || yc >= this.ncases_h ) return null;
return this.cases[xc+yc*this.ncases_w].getProducer();
}

}

I remind you that you can see the game running here

Next time we'll talk about vendors and the general architecture of the game.