This is another addictive and time-consuming, video game. Play this game at your own risk. This article shares the game's code structure, graphics rendering, and virus behavior in the code, highlighting the challenges in drawing the game's borders and simulating virus spread and damage to living cells.
Introduction
After many gruelling hours of contemplation, struggling with the thought of afflicting the world with yet another addictive and time-consuming, video game that will likely be the cause of work related injuries, strained beer-nut relationships and childlike mental regression, I decided it was in the best interest of humanity to waste their afternoon gaming. Please, use responsibly.
Background
Built on a previous game Hex 2048.
Using the Code
The playing field is limited to a fixed max size.
public static Size szGame_MaxSize = new Size(35, 19);
The developer can edit the 'basic' game board to any size, or use multiple designs for the default game-board. Currently set to the default 'basic' design that appears rectangular in shape near the center of the szGame_MaxSize
.
Tiles from the original Hex2048 game are now called 'cells' and can be infected with one of two 'viruses': Green-Worm & Red-Fire. Viruses destroy living-cells, then reproduce and spread. The user's actions in 'gathering' pieces nurtures the cells around where the pieces are gathered, this heals the cell's 'health' and hurts any virus present nearby. The Green-Worm is always alone (there is only one living Green-Worm virus at a time) and tends to gnaw at the gameboard in generally the same direction (slightly to the left or right) unless the user 'nurtures' the cells to the right/left of the Worm's head. While Red-Fire consumes living cells more slowly, it multiplies and grows as it spreads, the GreenWorm tends to 'gnaw on a leaf' and can be steered using the mouse-wheel as a 'fire-break'. Double-click will move the worm in/out of its egg.
Most of what's different from the original post is found in classVirus
which 'eats' away at the board. Also, the main form's DrawBorder()
has to gather the 'islands' and draw a ribbon around them.
Most of the graphics start in the main form's DrawGame()
method which first draws the visible playable area's separate 'islands' (and border trim) of playing area in DrawBorder()
then draws the actual pieces, their values, state of health(static) and/or viruses on top of the islands. eFSM is an enum
property that governs the code's logic and behavior through a 'Finite-State-Machine'. DrawGame()
adds 'flash' appearance to tiles being gathered (when the user scores points), then it finishes up by drawing the extras like: new tiles being added to the board (they fly across from some never, neverland near the top-left of the screen), score, timer & 'no path' sign warning the player of an invalid move.
void DrawGame()
{
int intBorderWidth = 10;
int intGameHeight =(int)( szForm.Height - 2 * intBorderWidth);
int intCellHeight = (int)((float)intGameHeight / (float)(szGame_MaxSize.Height + 1));
szCell = new Size(intCellHeight ,intCellHeight);
int intGameWidth = szCell.Width * szGame_MaxSize.Width;
recGame = new RectangleF((szForm.Width - intGameWidth) / 2,
(szForm.Height - intGameHeight) / 2,
intGameWidth,
intGameHeight);
lstCellsContainedWithinAllRibbons.Clear();
Color clrTransparent = Color.Azure;
Graphics g = Graphics.FromImage(bmpDrawGame);
{
DrawBorder(ref g);
DrawGameBoard(ref g);
switch (eFSM)
{
case enuHex2048_FSM.Cells_GatherLike_Flash:
{
if (intTimerCounter % 3 == 0)
bolHighlight = !bolHighlight;
for (int intCellCounter = 0;
intCellCounter < lstCellsGathered.Count;
intCellCounter++)
Highlight(ref g, lstCellsGathered[intCellCounter], bolHighlight);
intCounter_FlashCells--;
if (intCounter_FlashCells <= 0)
eFSM = enuHex2048_FSM.Cells_RemoveLike;
}
break;
}
Highlight(ref g, ptCellHighLight, bolHighlight);
Highlight(ref g, ptCellSelected, bolHighlight);
Size szGrab = new Size(95, 20);
recGrab = new RectangleF((szForm_default.Width - szGrab.Width) / 2,
12,
szGrab.Width,
szGrab.Height);
if (bolMouseOverGrab)
g.FillRectangle(Brushes.Gray, recGrab);
if (intCounter_AddCell > 0)
DrawAddNewCells(ref g);
DrawScore(ref g);
if (eFSM == enuHex2048_FSM.Game_Over)
DrawHighScore(ref g);
DrawNoPath(ref g);
DrawTimer(ref g);
classFlare.Animate();
classSparkle.Animate(ref g);
classAddScore.Animate(ref g);
}
g.Dispose();
bmpDrawGame.MakeTransparent(clrDrawBorderTransparent);
bmpForm = bmpDrawGame;
}
The classVirus
can be of two types (for now...) Green_Worm
or Red_Fire
as described above. They each have properties that govern their behavior but the Spread()
method governs how they spread (the green-worm is particular). Their Growth_Rate
is the amount of hurt they cause a living cell every growth cycle. Since each virus has its own tmrSpread_Tick()
event handlers, they will cause damage on their own time and the game's DrawGame()
method (on its own timer) will reflect the changes as they happen. The classVirus
's Growth_Rate
& Growth_Period
are the average amount of damage caused to a living cell each Growth_Period
. When the cell dies, the virus spreads to different cell(s) and the previously living cell is left a decaying virus carcass that rots and spoils over time (usually less than a minute) making it inaccessible to the player.
enuVirusTypes _eType = enuVirusTypes.nil;
public enuVirusTypes eType
{
get { return _eType; }
set
{
_eType = value;
switch (eType)
{
case enuVirusTypes.Worm_green:
{
Growth_Rate = 20;
Growth_Period = 2000;
rateSpread = 1;
rateSpread_PCRndSuccess = 0.5f;
DamageCaused = 0;
}
break;
case enuVirusTypes.Fire_red:
{
Growth_Rate = 20;
Growth_Period = 4000;
rateSpread = 2;
rateSpread_PCRndSuccess = 0.5f;
DamageCaused = 0;
}
break;
case enuVirusTypes.nil:
{
Growth_Rate = 25;
Growth_Period = 4000;
rateSpread = 0;
rateSpread_PCRndSuccess = 0f;
DamageCaused = 0;
}
break;
default:
{
}
break;
}
}
}
Static
The living cells have a 'health
' property which ranges from 0-255. When the cells are in poor health(<255), they will appear 'staticky'. There are 10x static plates. One original image was sourced from the internet. The original's whitish pixels (those whose RGB values are closest to 255) were set to (255,255,255) white pixels, the remaining stayed variable. Then that image was processed 10x times by adding 25 points to each pixel's RGB values. Those values that exceeded 255 were set to 255(max RGB values) such that those that were almost white are now white and there are relatively more white-to-black areas in each successive Static image used. The cells then have their health condition 'drawn' with a static shield's white area made transparent and only the 'blackish' pixels taint the cell's appearance.
History
- 23rd September, 2023 9h45
- 26th September, 2023 08h43
- Got around to actually writing the article
- Updated source code
- Reduced the number of new-cells being added to the game per turn pass
- 13th October, 2023 07h29
- The green-worm wasn't staying 'turned' after user action
- 21st October, 2023 07h54
- Made virus/fire aggression scale up slower
- 28th October, 2023 14h08
- Increased gamespace from 38x19 to 150x100
- Reduced the virus/fire aggression scale even slower
- Reduced the number of worms to 1per 100 000 pts