home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 109
/
EnigmaAmiga109CD.iso
/
software
/
giochi
/
wormwars
/
source
/
engine.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-01-05
|
118KB
|
3,632 lines
/* $Filename: WormWars/Source/engine.c $
* $VER: WormWars 5.21 $
* $Description: Game engine $
*
* © Copyright 2000 James R. Jacobs.
*/
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
#include "stdafx.h"
#include "diff.h"
#include "same.h"
#include "engine.h"
// PRIVATE STRUCTURES -----------------------------------------------------
struct
{ SBYTE x, y, oldx, oldy;
ABOOL alive, moved, teleported, visible, reflected;
} bullet[7];
struct
{ UWORD frequency;
SLONG score;
} object[LASTOBJECT + 1] =
{ {1280, 60}, // AFFIXER
{ 80, 20}, // AMMO
{ 110, 20}, // ARMOUR
{ 70, 50}, // BIAS
{ 190, 30}, // BOMB
{ 80, 10}, // BONUS
{1020, 60}, // CLOCK
{ 330, 50}, // GROWER
{1900, 90}, // HEALER
{1360, 60}, // ICE
{ 140, 60}, // LIFE
{ 130, 80}, // LIGHTNING
{ 240, 40}, // MISSILE
{ 640, 50}, // MULTIPLIER
{ 400, 10}, // NITRO
{ 240, 30}, // POWER
{ 480, 50}, // PROTECTOR
{ 210, 40}, // PULSE
{ 400, 40}, // REMNANTS
{ 600, 40}, // SLAYER
{ 980, 40}, // SLOWER
{ 730, 70}, // SWITCHER
{ 320, 20}, // TONGUE
{1200, 120}, // TREASURE
{3900, 140} // UMBRELLA
};
/* -200 common
220-400 uncommon
420-980 rare
1000+ very rare */
struct
{ SBYTE x, y, deltax, deltay, relx, rely;
ABOOL alive, last, visible;
} protector[4][PROTECTORS + 1];
struct
{ SBYTE x, y, time;
ABOOL alive;
} timebomb[TIMEBOMBS + 1];
struct
{ SBYTE deltax;
SBYTE deltay;
} thequeue[4][QUEUELIMIT + 1];
SBYTE eachworm[4][2][9] =
{ { { GREENHEADUP, GREENHEADUP, GREENHEADUP,
GREENHEADLEFT, ANYTHING, GREENHEADRIGHT,
GREENHEADDOWN, GREENHEADDOWN, GREENHEADDOWN
},
{ GREENMODEUP, GREENMODEUP, GREENMODEUP,
GREENMODELEFT, ANYTHING, GREENMODERIGHT,
GREENMODEDOWN, GREENMODEDOWN, GREENMODEDOWN
} },
{ { REDHEADUP, REDHEADUP, REDHEADUP,
REDHEADLEFT, ANYTHING, REDHEADRIGHT,
REDHEADDOWN, REDHEADDOWN, REDHEADDOWN
},
{ REDMODEUP, REDMODEUP, REDMODEUP,
REDMODELEFT, ANYTHING, REDMODERIGHT,
REDMODEDOWN, REDMODEDOWN, REDMODEDOWN
} },
{ { BLUEHEADUP, BLUEHEADUP, BLUEHEADUP,
BLUEHEADLEFT, ANYTHING, BLUEHEADRIGHT,
BLUEHEADDOWN, BLUEHEADDOWN, BLUEHEADDOWN
},
{ BLUEMODEUP, BLUEMODEUP, BLUEMODEUP,
BLUEMODELEFT, ANYTHING, BLUEMODERIGHT,
BLUEMODEDOWN, BLUEMODEDOWN, BLUEMODEDOWN
} },
{ { YELLOWHEADUP, YELLOWHEADUP, YELLOWHEADUP,
YELLOWHEADLEFT, ANYTHING, YELLOWHEADRIGHT,
YELLOWHEADDOWN, YELLOWHEADDOWN, YELLOWHEADDOWN
},
{ YELLOWMODEUP, YELLOWMODEUP, YELLOWMODEUP,
YELLOWMODELEFT, ANYTHING, YELLOWMODERIGHT,
YELLOWMODEDOWN, YELLOWMODEDOWN, YELLOWMODEDOWN
} } };
struct
{ SBYTE speed;
SWORD freq;
UBYTE species;
UBYTE type; // owning worm, 0-3 (used for drips and missiles)
SBYTE x, y;
SBYTE deltax, deltay;
UBYTE last, oldlast;
ABOOL alive, explode, visible;
UBYTE mode;
SWORD armour, tongue;
ULONG score;
UBYTE multi;
} creature[CREATURES + 1];
// MODULE VARIABLES (used only within engine.c) ---------------------------
MODULE ABOOL letters[4][LETTERS + 1], trainer;
MODULE SBYTE freq, ice, lettertype, letterx, lettery, leveltype,
noletter, treasurer;
// GLOBAL VARIABLES (owned by engine.c, imported by system.c) -------------
AGLOBAL ABOOL clearthem = FALSE,
modified = FALSE;
AGLOBAL SBYTE a = GAMEOVER,
board[MAXLEVELS + 1][FIELDX + 1][FIELDY + 1],
brush = STONE, players,
field[FIELDX + 1][FIELDY + 1],
level = 1, levels, reallevel,
startx[MAXLEVELS + 1], starty[MAXLEVELS + 1];
AGLOBAL SWORD secondsleft, secondsperlevel;
AGLOBAL STRPTR pathname = (STRPTR) DEFAULTSET;
AGLOBAL ULONG delay, r;
AGLOBAL TEXT date[DATELENGTH + 1], times[TIMELENGTH + 1];
AGLOBAL struct HiScoreStruct hiscore[HISCORES + 1];
AGLOBAL struct TeleportStruct teleport[MAXLEVELS + 1][4];
AGLOBAL struct WormStruct worm[4];
/* FUNCTIONS --------------------------------------------------------------
NAME align -- right-justify a string within another string
SYNOPSIS align(STRPTR, SBYTE, TEXT);
FUNCTION Moves all text in a string to the right, padding with
spaces. Does not itself add a null terminator.
INPUTS string - pointer to the string of text
size - size in characters of the containing string
filler - what to pad the left of the string with
NOTE Null terminators are written over by thissy function, but that
does not matter, because calling functions use Text() with an
explicit length. This function only works with monospaced
fonts.
MODULE engine.c */
void align(STRPTR string, SBYTE size, TEXT filler)
{ SBYTE i, shift, length;
length = strlen((const char*) string);
shift = size - length;
for (i = 1; i <= length; i++)
*(string + size - i) = *(string + size - i - shift);
for (i = 0; i <= shift - 1; i++)
*(string + i) = filler;
}
ABOOL blocked(SBYTE which, SBYTE deltax, SBYTE deltay)
{ SBYTE thissy;
thissy = field[xwrap(teleport[level][partner(which)].x + deltax)][ywrap(teleport[level][partner(which)].y + deltay)];
if ((thissy < STONE || thissy > GOAT) && thissy != METAL)
return FALSE;
else return TRUE;
}
void bombblast(SBYTE triggerer, SBYTE player, SBYTE centrex, SBYTE centrey)
{ SBYTE counter, downy, downymax, leftx, leftxmax, rightx, rightxmax, strength, uppy, uppymax, x, y;
SLONG score = 0L;
effect(FXBOMBBLAST);
strength = BOMBADD + (rand() % BOMBRAND);
leftxmax = centrex - strength;
if (leftxmax < 0)
leftxmax = 0;
rightxmax = centrex + strength;
if (rightxmax > FIELDX)
rightxmax = FIELDX;
uppymax = centrey - strength;
if (uppymax < 0)
uppymax = 0;
downymax = centrey + strength;
if (downymax > FIELDY)
downymax = FIELDY;
leftx = centrex;
rightx = centrex;
uppy = centrey;
downy = centrey;
for (counter = 1; counter <= strength; counter++)
{ if (leftx > leftxmax)
{ leftx--;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[leftx][y], leftx, y);
}
if (rightx < rightxmax)
{ rightx++;
for (y = uppy; y <= downy; y++)
score += squareblast(triggerer, player, field[rightx][y], rightx, y);
}
if (uppy > uppymax)
{ uppy--;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][uppy], x, uppy);
}
if (downy < downymax)
{ downy++;
for (x = leftx; x <= rightx; x++)
score += squareblast(triggerer, player, field[x][downy], x, downy);
} }
if (triggerer == HEAD)
{ wormscore(player, score);
if (worm[player].bias)
stat(player, LIFE);
} else
orbscore(player, score);
}
void bouncegoat(SBYTE which, SBYTE x, SBYTE y)
{ if (field[x][y] == GOAT)
{ creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
orbscore(which, KILLGOAT);
change(x, y, BONUS);
} }
ABOOL bounceorb(SBYTE which, SBYTE x, SBYTE y)
{ if (field[x][y] == METAL)
return TRUE;
elif (creature[which].mode == NONE)
{ if (field[x][y] >= FIRSTNONE && field[x][y] <= LASTNONE)
return TRUE;
else return FALSE;
} elif (creature[which].mode == TONGUE)
{ if (field[x][y] >= FIRSTTONGUE && field[x][y] <= LASTTONGUE)
return TRUE;
else return FALSE;
} else // assumes creature[which].mode == ARMOUR
{ if (field[x][y] >= FIRSTARMOUR && field[x][y] <= LASTARMOUR)
return TRUE;
else return FALSE;
} }
SBYTE bsign(SBYTE value)
{ if (value < 0)
return (-1);
elif (value > 0)
return (1);
else
return (0);
}
void changefield(void)
{ SBYTE x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
field[x][y] = board[level][x][y];
}
void clearhiscores(void)
{ SBYTE i;
clearthem = FALSE;
for (i = 0; i <= HISCORES; i++)
{ hiscore[i].player = -1;
hiscore[i].level = 0;
hiscore[i].score = 0L;
hiscore[i].fresh = FALSE;
hiscore[i].name[0] = 0;
hiscore[i].time[0] = 0;
hiscore[i].date[0] = 0;
} }
void clearletters(void)
{ SBYTE player, which;
for (player = 0; player <= 3; player++)
for (which = 0; which <= LETTERS; which++)
{ letters[player][which] = FALSE;
drawletter(player, FIRSTLETTER + which, BLACK);
} }
void copyfield(SBYTE source, SBYTE destination)
{ SBYTE which, x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[destination][x][y] = board[source][x][y];
startx[destination] = startx[source];
starty[destination] = starty[source];
for (which = 0; which <= 1; which++)
{ teleport[destination][which].alive = teleport[source][which].alive;
teleport[destination][which].x = teleport[source][which].x;
teleport[destination][which].y = teleport[source][which].y;
} }
void death(void)
{ SBYTE pain, player, which;
ABOOL slow;
for (player = 0; player <= 3; player++)
{ if (worm[player].lives)
{ if (!worm[player].alive)
{ slow = FALSE;
pain = 0;
if (worm[player].cause >= FIRSTTAIL && worm[player].cause <= LASTTAIL)
{ if (player == worm[player].cause - FIRSTTAIL)
pain = TAILPAIN;
else pain = OTHERTAILPAIN;
slow = TRUE;
} elif (worm[player].cause >= FIRSTFIRE && worm[player].cause <= LASTFIRE)
pain = WORMFIREPAIN;
elif (worm[player].cause >= FIRSTHEAD && worm[player].cause <= LASTHEAD)
pain = HEADPAIN;
elif (worm[player].cause >= FIRSTPROTECTOR && worm[player].cause <= LASTPROTECTOR)
pain = PROTECTORPAIN;
elif (worm[player].cause >= FIRSTMISSILE && worm[player].cause <= LASTMISSILE)
pain = MISSILEPAIN;
elif (worm[player].cause >= FIRSTDRIP && worm[player].cause <= LASTDRIP)
pain = DRIPPAIN;
else switch (worm[player].cause)
{
case BOMB:
pain = BOMBPAIN;
break;
case WOOD:
pain = WOODPAIN;
slow = TRUE;
break;
case FRAGMENT:
pain = FRAGMENTPAIN;
break;
case GOAT:
pain = GOATPAIN;
slow = TRUE;
break;
case SLAYER:
pain = SLAYERPAIN;
break;
case STONE:
pain = STONEPAIN;
slow = TRUE;
break;
case TELEPORT:
pain = TELEPORTPAIN;
slow = TRUE;
break;
case SLIME:
pain = SLIMEPAIN;
slow = TRUE;
break;
case METAL:
pain = METALPAIN;
slow = TRUE;
break;
case REMNANTS:
pain = REMNANTPAIN;
slow = TRUE;
break;
case LIGHTNING:
pain = LIGHTNINGPAIN;
break;
case PENGUIN:
pain = PENGUINPAIN;
break;
default:
break;
}
if (worm[player].victor >= 0 && worm[player].victor != player)
{ wormscore(worm[player].victor, KILLWORM);
if (worm[worm[player].victor].bias)
{ worm[worm[player].victor].lives += pain;
stat(worm[player].victor, LIFE);
} }
if (slow)
{ worm[player].speed = slowdown(worm[player].speed);
stat(player, NITRO);
}
if (pain > worm[player].lives)
worm[player].lives = 0;
else worm[player].lives -= pain;
draw(worm[player].x, worm[player].y, SKULL);
drawcause(player, NORMAL);
stat(player, LIFE);
if (level)
worm[player].levelreached = level;
else worm[player].levelreached = reallevel;
if (worm[player].lives)
{ effect(FXPAIN + player);
worm[player].alive = TRUE;
worm[player].causewait = r + CAUSEWAIT;
} else
{ // kill worm
effect(FXWORMDEATH);
if (ice == player)
ice = -1;
field[worm[player].x][worm[player].y] = SKULL;
for (which = 0; which <= PROTECTORS; which++)
if (protector[player][which].alive && protector[player][which].visible)
change(protector[player][which].x, protector[player][which].y, EMPTY);
if (worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
} } } }
if (!worm[0].lives && !worm[1].lives && !worm[2].lives && !worm[3].lives)
{ // End of game
for (player = 0; player <= 3; player++)
if (worm[player].control != NONE && worm[player].score >= worm[player].hiscore)
worm[player].hiscore = worm[player].score;
newhiscores();
effect(FXDEFEAT);
a = GAMEOVER;
if (players == 1)
say((STRPTR) "Game over!", worm[onlyworm(FALSE)].colour);
elif (worm[0].control && ((!worm[1].control) || worm[1].score < worm[0].score) && ((!worm[2].control) || worm[2].score < worm[0].score) && ((!worm[3].control) || worm[3].score < worm[0].score))
say((STRPTR) "Green wins!", GREEN);
elif (worm[1].control && ((!worm[0].control) || worm[0].score < worm[1].score) && ((!worm[2].control) || worm[2].score < worm[1].score) && ((!worm[3].control) || worm[3].score < worm[1].score))
say((STRPTR) "Red wins!", RED);
elif (worm[2].control && ((!worm[0].control) || worm[0].score < worm[2].score) && ((!worm[1].control) || worm[1].score < worm[2].score) && ((!worm[3].control) || worm[3].score < worm[2].score))
say((STRPTR) "Blue wins!", BLUE);
elif (worm[3].control && ((!worm[0].control) || worm[0].score < worm[3].score) && ((!worm[1].control) || worm[1].score < worm[3].score) && ((!worm[2].control) || worm[2].score < worm[3].score))
say((STRPTR) "Yellow wins!", YELLOW);
else say((STRPTR) "A draw!", WHITE);
waitasec();
anykey(FALSE);
} }
void drawcause(SBYTE player, SBYTE state)
{ if (state == BLACK)
draw(-2 + ((FIELDX + 4) * worm[player].statx), (FIELDY / 2) - CAUSEYDISTANCE + (worm[player].staty * CAUSEYDISTANCE * 2), BLACKENED);
else draw(-2 + ((FIELDX + 4) * worm[player].statx), (FIELDY / 2) - CAUSEYDISTANCE + (worm[player].staty * CAUSEYDISTANCE * 2), worm[player].cause);
}
void drawletter(SBYTE player, SBYTE letter, SBYTE state)
{ SBYTE thissy;
if (state == BLACK)
thissy = BLACKENED;
else thissy = letter;
if (!worm[player].statx)
if (!worm[player].staty)
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
thissy);
else
draw(-7 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
thissy);
else
if (!worm[player].staty)
draw(FIELDX + 4 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) - 2 + ((letter - FIRSTLETTER) / 4),
thissy);
else
draw(FIELDX + 4 + ((letter - FIRSTLETTER) % 4),
(FIELDY / 2) + 1 + ((letter - FIRSTLETTER) / 4),
thissy);
}
/* NAME enginesetup -- once-only initialization of engine variables
SYNOPSIS enginesetup(void);
FUNCTION Sets up the unchanging worm variables.
MODULE engine.c */
void enginesetup(void)
{ worm[0].statx = worm[0].staty = worm[1].staty = worm[2].statx = 0;
worm[1].statx = worm[2].staty = worm[3].statx = worm[3].staty = 1;
worm[0].colour = GREEN;
worm[1].colour = RED;
worm[2].colour = BLUE;
worm[3].colour = YELLOW;
worm[0].name[0] = worm[1].name[0] = worm[2].name[0] = worm[3].name[0] = 0;
systemsetup();
}
/* NAME fastloop -- things done often
SYNOPSIS fastloop(void);
FUNCTION Checks for and handles level completion.
MODULE engine.c */
void fastloop(void)
{ ABOOL complete;
SBYTE advancer = -1, player, which;
// update joystick `1'
joy0();
// flash letter
if (level)
if (r % 16 == 1)
draw(letterx, lettery, WHITENED);
elif (r % 16 == 2)
draw(letterx, lettery, lettertype);
// handle level completion
for (player = 0; player <= 3; player++)
{ complete = TRUE;
for (which = 0; which <= LETTERS; which++)
if (!letters[player][which])
complete = FALSE;
if (complete)
advancer = player;
}
if (advancer != -1)
{ if (level++ == 0)
{ level = reallevel + 1;
reallevel = 0;
}
stopfx(0L);
if (level > levels)
effect(FXVICTORY);
newlevel(advancer);
} }
void fillfield(SBYTE which)
{ SBYTE x, y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
{ board[level][x][y] = which;
draw(x, y, which);
}
board[level][startx[level]][starty[level]] = EMPTY;
draw(startx[level], starty[level], START);
if (teleport[level][0].alive)
{ board[level][teleport[level][0].x][teleport[level][0].y] = TELEPORT;
draw(teleport[level][0].x, teleport[level][0].y, ONE);
}
if (teleport[level][1].alive)
{ board[level][teleport[level][1].x][teleport[level][1].y] = TELEPORT;
draw(teleport[level][1].x, teleport[level][1].y, TWO);
} }
ABOOL findempty(SBYTE* x, SBYTE* y, SBYTE first, SBYTE last)
{ SBYTE count = 0, xx, yy;
do
{ xx = rand() % (FIELDX + 1);
yy = rand() % (FIELDY + 1);
} while ((field[xx][yy] < first || field[xx][yy] > last) && ++count < PATIENCE);
if (count < PATIENCE)
{ *x = xx;
*y = yy;
return(TRUE);
} else return(FALSE);
}
void gameloop(void)
{ SBYTE player, which;
if (a == PLAYGAME)
{ fastloop();
gameinput();
}
if (a == PLAYGAME)
for (player = 0; player <= 3; player++)
if (worm[player].lives && (!(r % worm[player].speed)) && (ice == -1 || ice == player))
wormloop(player);
if (a == PLAYGAME)
for (which = 0; which <= CREATURES; which++)
if (creature[which].alive && (!(r % creature[which].speed)) && (ice == -1 || (creature[which].species == MISSILE && ice == creature[which].type)))
creatureloop(which);
if (a == PLAYGAME)
death();
if (a == PLAYGAME)
if (ice == -1 && (!(r % VERYSLOW)))
slowloop();
timing();
}
void killall(void)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
creature[i].alive = FALSE;
for (i = 0; i <= TIMEBOMBS; i++)
timebomb[i].alive = FALSE;
teleport[level][2].alive = FALSE;
teleport[level][3].alive = FALSE;
}
void levelappend(void)
{ UBYTE oldlevel;
if (levels < MAXLEVELS)
{ oldlevel = level;
level = ++levels;
newfield();
level = oldlevel;
saylevel(WHITE);
} }
void leveldelete(void)
{ SBYTE i;
// pull boards
if (levels > 1)
{ if (level < levels)
for (i = level; i < levels; i++)
copyfield(i + 1, i);
else
level--;
levels--;
saylevel(WHITE);
turborender();
} }
void levelerase(void)
{ newfield();
turborender();
}
void levelinsert(void)
{ SBYTE i;
// push boards
if (levels < MAXLEVELS)
{ for (i = levels; i >= level; i--)
copyfield(i, i + 1);
levels++;
saylevel(WHITE);
newfield();
turborender();
} }
SBYTE loadfields(STRPTR fieldname)
{ SBYTE i, j, x, y;
TEXT IOBuffer[NAMELENGTH + 1];
UBYTE ver;
/* This routine is not entirely robust, especially regarding
failures part way through reading. Also, field data values must be
those supported by the field editor (ie. objects, and the squares
represented by F1-F8), or undefined behaviour may result. None of
this is currently checked for. Provided that the fieldset was
created with the field editor (4.4+), and the file is not corrupt,
these failures should never happen anyway.
open file */
if (!ZOpen(fieldname, FALSE))
return 1; // no harm done
// read header
if (!ZRead(IOBuffer, 10))
{ ZClose();
return 2; // no harm done
}
if (!strcmp(IOBuffer, "FSET 5.1"))
ver = 51;
elif (!strcmp(IOBuffer, "FSET 5.0"))
ver = 50;
elif (!strcmp(IOBuffer, "FSET 4.4"))
ver = 44;
else
{ ZClose();
return 3; // no harm done
}
levels = IOBuffer[9];
// read high score table
for (i = 0; i <= HISCORES; i++)
{ if (!ZRead(IOBuffer, 6))
{ ZClose();
return 4; // incorrect levels
}
hiscore[i].fresh = FALSE;
hiscore[i].player = IOBuffer[0];
hiscore[i].level = IOBuffer[1];
hiscore[i].score = (IOBuffer[3] * 65536)
+ (IOBuffer[4] * 256)
+ IOBuffer[5];
if (!ZRead(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return 5; // incorrect levels, corrupted high scores
}
for (j = 0; j <= NAMELENGTH; j++)
hiscore[i].name[j] = IOBuffer[j];
if (ver >= 50)
{ if (!ZRead(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return 6; // incorrect levels, corrupted high scores
}
for (j = 0; j <= TIMELENGTH; j++)
hiscore[i].time[j] = IOBuffer[j];
if (!ZRead(IOBuffer, DATELENGTH + 1))
{ ZClose();
return 7; // incorrect levels, corrupted high scores
}
for (j = 0; j <= DATELENGTH; j++)
hiscore[i].date[j] = IOBuffer[j];
} else
{ // skip extra name character
if (!ZRead(IOBuffer, 1))
{ ZClose();
return 8; // incorrect levels, corrupted high scores
} else
{ if (hiscore[i].name[0])
{ strcpy(hiscore[i].time, "??:??");
strcpy(hiscore[i].date, "??/??/??");
} else
{ hiscore[i].time[0] = 0;
hiscore[i].date[0] = 0;
} } } }
// read level data
for (i = 0; i <= levels; i++)
{ if (!ZRead(IOBuffer, 8))
{ ZClose();
return 9;
/* incorrect levels, corrupted high scores,
incorrect startx, teleports, field data */
}
startx[i] = IOBuffer[0];
starty[i] = IOBuffer[1];
teleport[i][0].alive = IOBuffer[2];
teleport[i][0].x = IOBuffer[3];
teleport[i][0].y = IOBuffer[4];
teleport[i][1].alive = IOBuffer[5];
teleport[i][1].x = IOBuffer[6];
teleport[i][1].y = IOBuffer[7];
if (!ZRead((char *) &board[i][0][0], (FIELDX + 1) * (FIELDY + 1)))
{ ZClose();
return 10;
/* incorrect levels, corrupted high scores,
incorrect startx, teleports, field data */
} else
{ if (ver <= 44)
{ // convert from FSET 4.4 to FSET 5.0 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= 16)
board[i][x][y] += 2;
}
if (ver <= 50)
{ // convert from FSET 5.0 to FSET 5.1 format
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (board[i][x][y] >= LIGHTNING)
board[i][x][y]++;
} } }
// no need to read version string
ZClose();
modified = FALSE;
return 0;
}
void matchteleports(void)
{ SBYTE which;
for (which = 0; which <= levels; which++)
if (teleport[which][0].alive == TRUE && teleport[which][1].alive == FALSE)
{ board[which][teleport[which][0].x][teleport[which][0].y] = EMPTY;
teleport[which][0].alive = FALSE;
if (level == which && a == FIELDEDIT)
draw(teleport[which][0].x, teleport[which][0].y, EMPTY);
} elif (teleport[which][0].alive == FALSE && teleport[which][1].alive == TRUE)
{ board[which][teleport[which][1].x][teleport[which][1].y] = EMPTY;
teleport[which][1].alive = FALSE;
if (level == which && a == FIELDEDIT)
draw(teleport[which][1].x, teleport[which][1].y, EMPTY);
} }
void newfield(void)
{ int x, y;
teleport[level][0].alive = FALSE;
teleport[level][1].alive = FALSE;
startx[level] = FIELDX / 2;
starty[level] = FIELDY / 2;
if (level)
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[level][x][y] = EMPTY;
else for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
board[0][x][y] = SILVER;
}
void newfields(void)
{ if (verify())
{ pathname = (STRPTR) DEFAULTSET;
levels = DEFAULTLEVELS;
modified = FALSE;
for (level = 0; level <= levels; level++)
newfield();
clearhiscores();
level = 1;
if (a == FIELDEDIT)
{ turborender();
saylevel(WHITE);
} else
hiscores();
} }
void newgame(void)
{ SBYTE player;
players = 0;
for (player = 0; player <= 3; player++)
if (worm[player].control != NONE)
players++;
for (player = 0; player <= 3; player++)
{ worm[player].lives = 0;
worm[player].speed = NORMAL;
worm[player].hiscore = 0;
}
r = -1;
trainer = FALSE;
ice = -1;
reallevel = 0;
level = 1;
a = PLAYGAME;
clearscreen();
newlevel(rand() % 4);
timing();
}
void newhiscores(void)
{ PERSIST TEXT amiganame[4][NAMELENGTH + 1] = {"Jay Miner", "Carl Sassenrath", "R. J. Mical", "Dave Morse"};
SBYTE i, j, player;
datestamp();
for (player = 0; player <= 3; player++)
for (i = 0; i <= HISCORES; i++)
if (worm[player].control != NONE && worm[player].score >= hiscore[i].score)
{ // push all worse hiscores down
if (i < HISCORES)
for (j = HISCORES; j >= i + 1; j--)
{ hiscore[j].player = hiscore[j - 1].player;
hiscore[j].score = hiscore[j - 1].score;
hiscore[j].level = hiscore[j - 1].level;
hiscore[j].fresh = hiscore[j - 1].fresh;
strcpy(hiscore[j].name, hiscore[j - 1].name);
strcpy(hiscore[j].date, hiscore[j - 1].date);
strcpy(hiscore[j].time, hiscore[j - 1].time);
}
modified = TRUE;
hiscore[i].player = player;
hiscore[i].score = worm[player].hiscore;
hiscore[i].level = worm[player].levelreached;
if (worm[player].control == AMIGA)
{ strcpy(hiscore[i].name, amiganame[player]);
hiscore[i].fresh = FALSE;
} else
{ strcpy(hiscore[i].name, "(New)");
hiscore[i].fresh = TRUE;
}
strcpy(hiscore[i].time, times);
strcpy(hiscore[i].date, date);
break; // vital
} }
void newlevel(SBYTE player)
{ SBYTE iwhich, which;
if (level >= 2 && worm[player].lives)
rundown(player);
if (a == PLAYGAME)
{ if (level > levels)
{ for (which = 0; which <= 3; which++)
if (worm[which].lives)
worm[which].levelreached = -1;
celebrate();
newhiscores();
titlescreen();
} else
{ saylevel(WHITE);
for (which = 0; which <= 3; which++)
worm[which].multi = (SBYTE) atleast(worm[which].multi / 2, 1);
killall();
clearletters();
orientworms();
changefield();
turborender();
delay = atleast(DELAY_MAX - (level * DELAY_DEC), DELAY_MIN);
if (level)
{ secondsperlevel = SECONDSPERLEVEL;
putletter(-1);
freq = (SBYTE) atleast(FREQ_MAX - ((level - 1) * FREQ_DEC), FREQ_MIN);
for (which = 0; which <= 3; which++)
{ if (!worm[which].lives && worm[which].control != NONE)
{ // create (or resurrect) a worm
worm[which].lives = STARTLIVES;
worm[which].score = 0;
worm[which].alive = TRUE;
worm[which].oldscore = 0;
worm[which].armour = 0;
worm[which].tongue = 0;
worm[which].nitro = FALSE;
worm[which].mode = NULL;
worm[which].power = 0;
worm[which].bias = 0;
worm[which].multi = 1;
worm[which].ice = 0;
worm[which].victor = -1;
worm[which].ammo = 0;
worm[which].remnants = 0;
worm[which].affixer = FALSE;
worm[which].causewait = (ULONG) -1;
worm[which].last = FIRSTTAIL + which;
worm[which].pos = -1;
for (iwhich = 0; iwhich <= PROTECTORS; iwhich++)
protector[which][iwhich].alive = FALSE;
for (iwhich = 0; iwhich <= LASTOBJECT; iwhich++)
stat(which, iwhich);
} } } } }
clearjoystick();
clearkybd();
resettime();
}
void explosion(SBYTE x, SBYTE y, SBYTE exceptionx, SBYTE exceptiony)
{ UBYTE i, generated = 0;
/* You wouldn't think this would work properly for pulse-explosions,
because the worm's head is obliterated. However, it is refreshed (as
tail) the next time wormloop() is called for that worm. */
effect(FXEXPLODE);
for (i = 0; i <= CREATURES; i++)
if ((!(creature[i].alive)) && generated <= 7)
{ creature[i].last = EMPTY;
creature[i].x = x;
creature[i].y = y;
if (level)
creature[i].speed = (SBYTE) atleast(FRAGSPEED - level, 1);
else creature[i].speed = BONUSFRAGSPEED;
creature[i].species = FRAGMENT;
creature[i].visible = TRUE;
switch (generated)
{
case 0:
creature[i].deltax = 0;
creature[i].deltay = -1;
break;
case 1:
creature[i].deltax = 1;
creature[i].deltay = -1;
break;
case 2:
creature[i].deltax = 1;
creature[i].deltay = 0;
break;
case 3:
creature[i].deltax = 1;
creature[i].deltay = 1;
break;
case 4:
creature[i].deltax = 0;
creature[i].deltay = 1;
break;
case 5:
creature[i].deltax = -1;
creature[i].deltay = 1;
break;
case 6:
creature[i].deltax = -1;
creature[i].deltay = 0;
break;
case 7:
creature[i].deltax = -1;
creature[i].deltay = -1;
break;
default:
break;
}
generated++;
if (creature[i].deltax != exceptionx || creature[i].deltay != exceptiony)
{ creature[i].alive = TRUE;
if (generated == 1)
change(x, y, FRAGMENT);
} } }
/* Many creatures have been reorganized, to take advantage of shared
characteristics. 6 creature types (orbs, goats, drips, fragments, missiles
and penguins) use the creature structure. Not encapsulated are worms,
protectors, worm bullets, timebombs and teleports. */
void creatureloop(SBYTE which)
{ ABOOL happy = FALSE;
UBYTE bestdistance = 255, distance, player, thissy, i;
SBYTE x, y, xx, yy, xxx, yyy, frontx, fronty, rearx, reary;
x = creature[which].x;
y = creature[which].y;
if (creature[which].species == ORB && creature[which].explode)
{ creature[which].alive = FALSE;
explosion(x, y, -2, -2);
return;
}
// decide whether and where to move
switch(creature[which].species)
{
case PENGUIN:
do
{ xx = (rand() % 3) - 1;
yy = (rand() % 3) - 1;
} while (!valid(x + xx, y + yy));
thissy = field[x + xx][y + yy];
if (thissy >= FIRSTEMPTY && thissy <= LASTEMPTY)
{ creature[which].deltax = xx;
creature[which].deltay = yy;
} else
{ creature[which].deltax = 0;
creature[which].deltay = 0;
}
break;
case MISSILE:
for (player = 0; player <= 3; player++)
if (creature[which].type != player && worm[player].lives && !worm[player].bias)
{ xx = abs(worm[player].x - x);
yy = abs(worm[player].y - y);
if (xx < yy)
distance = xx;
else distance = yy;
if (distance <= bestdistance)
{ bestdistance = distance;
creature[which].deltax = bsign(worm[player].x - x);
creature[which].deltay = bsign(worm[player].y - y);
} }
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && which != i)
{ if
( creature[i].species == ORB
|| (creature[i].species == MISSILE && creature[i].type != creature[which].type)
|| creature[i].species == GOAT
)
{ xx = abs(creature[i].x - x);
yy = abs(creature[i].y - y);
if (xx < yy)
distance = xx;
else distance = yy;
if (distance <= bestdistance)
{ bestdistance = distance;
creature[which].deltax = bsign(creature[i].x - x);
creature[which].deltay = bsign(creature[i].y - y);
} } }
if (bestdistance == 255)
creature[which].alive = FALSE;
break;
case ORB:
frontx = xwrap(x + creature[which].deltax); // look in front
fronty = ywrap(y + creature[which].deltay);
rearx = xwrap(x - creature[which].deltax); // look behind
reary = ywrap(y - creature[which].deltay);
if (bounceorb(which, frontx, fronty))
{ bouncegoat(which, frontx, fronty);
xx = -creature[which].deltax; // default bounce angle is 180°
yy = -creature[which].deltay;
if (!bounceorb(which, frontx, reary))
{ if (bounceorb(which, rearx, fronty))
{ bouncegoat(which, rearx, fronty);
xx = creature[which].deltax;
} }
elif (!bounceorb(which, rearx, fronty))
{ bouncegoat(which, rearx, fronty);
yy = creature[which].deltay;
}
creature[which].deltax = xx;
creature[which].deltay = yy;
}
break;
case GOAT:
// decide whether to move
if (!(rand() % GOATMOVE))
{ for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] >= FIRSTEMPTY && field[xx][yy] <= LASTEMPTY)
{ happy = TRUE;
break;
} }
creature[which].deltax = 0;
creature[which].deltay = 0;
if (!happy)
{ xx = (rand() % 3) - 1;
yy = (rand() % 3) - 1;
if (valid(x + xx, y + yy) && (xx || yy))
{ thissy = field[x + xx][y + yy];
if (thissy >= FIRSTGOAT && thissy <= LASTGOAT && thissy != GOAT)
{ creature[which].last = creature[which].oldlast;
creature[which].oldlast = thissy;
creature[which].deltax = xx;
creature[which].deltay = yy;
} } }
break;
default:
break;
}
// now move
if (creature[which].deltax || creature[which].deltay)
{ if (creature[which].visible)
{ // erase previous image
change(x, y, creature[which].last);
if (creature[which].species != FRAGMENT)
creature[which].last = EMPTY;
}
if (creature[which].alive)
{ creature[which].x += creature[which].deltax;
creature[which].y += creature[which].deltay;
if (creature[which].species == ORB)
{ creature[which].x = xwrap(creature[which].x);
creature[which].y = ywrap(creature[which].y);
} elif (creature[which].species == FRAGMENT || creature[which].species == DRIP)
{ if (!valid(creature[which].x, creature[which].y))
creature[which].alive = FALSE;
} } }
creature[which].visible = TRUE;
x = creature[which].x;
y = creature[which].y;
// collision detection
if
( creature[which].alive
&& creature[which].species != GOAT
&& creature[which].species != PENGUIN
&& (creature[which].deltax || creature[which].deltay)
)
{ thissy = field[x][y];
if (thissy <= LASTOBJECT)
{ if (creature[which].species == ORB)
{ orbscore(which, object[thissy].score);
if (thissy != SLAYER && thissy != BOMB)
effect(FXGETOBJECT);
}
if (thissy == BOMB)
{ i = whichtimebomb(x, y);
if (creature[which].species == FRAGMENT || creature[which].species == MISSILE)
{ if (i != -1)
{ creature[which].alive = FALSE;
timebomb[i].alive = FALSE;
if (creature[which].species == MISSILE)
draw(x, y, FIRSTMISSILE + creature[which].type);
else draw(x, y, FRAGMENT);
bombblast(BOMB, 0, x, y);
change(x, y, EMPTY);
} }
elif (creature[which].species == ORB)
{ if (i != -1)
{ timebomb[i].alive = FALSE;
draw(x, y, ORB);
/* so the user understands what is happening
BEFORE the s-l-o-w bombblast() occurs */
}
bombblast(ORB, which, x, y);
} } }
elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ if (creature[which].species == MISSILE)
creature[which].alive = FALSE;
elif (creature[which].species == FRAGMENT)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
creature[which].alive = FALSE;
change(x, y, BONUS);
} elif (creature[which].species == ORB)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
if (creature[which].mode != ARMOUR)
{ creature[which].explode = TRUE;
draw(x, y, EMPTY);
} } }
elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ // Drips, orbs, fragments, missiles
creature[whichcreature(x, y, MISSILE, which)].alive = FALSE;
if (creature[which].species == FRAGMENT)
{ creature[which].alive = FALSE;
change(x, y, EMPTY);
} elif (creature[which].species == MISSILE)
{ creature[which].alive = FALSE;
change(x, y, BONUS);
} elif (creature[which].species == ORB)
{ if (creature[which].mode != ARMOUR)
{ effect(FXORBDEATH);
wormscore(thissy - FIRSTMISSILE, creature[which].score);
if (worm[thissy - FIRSTMISSILE].bias)
{ worm[thissy - FIRSTMISSILE].lives += ORBBLOOD;
stat(thissy - FIRSTMISSILE, LIFE);
}
creature[which].alive = FALSE;
change(x, y, BONUS);
} else effect(FXUSEARMOUR);
} }
elif (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (creature[which].species == MISSILE)
{ if (creature[which].type == thissy - FIRSTHEAD)
creature[which].visible = FALSE;
else
{ creature[which].alive = FALSE;
if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FIRSTMISSILE + creature[which].type;
worm[thissy - FIRSTHEAD].victor = creature[which].type;
worm[thissy - FIRSTHEAD].alive = FALSE;
} else effect(FXUSEARMOUR);
} }
elif (creature[which].species == FRAGMENT)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FRAGMENT;
worm[thissy - FIRSTHEAD].victor = -1;
worm[thissy - FIRSTHEAD].alive = FALSE;
creature[which].alive = FALSE;
} else
{ effect(FXUSEARMOUR);
reflect(which);
} }
elif (creature[which].species == DRIP)
{ creature[which].alive = FALSE;
if (creature[which].type == thissy - FIRSTHEAD || worm[thissy - FIRSTHEAD].bias)
{ effect(FXDRIP);
wormscore(thissy - FIRSTHEAD, DRIPBONUS);
} else
{ worm[thissy - FIRSTHEAD].alive = FALSE;
worm[thissy - FIRSTHEAD].cause = FIRSTDRIP + creature[which].type;
worm[thissy - FIRSTHEAD].victor = -1;
} }
elif (creature[which].species == ORB)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = ORB;
worm[thissy - FIRSTHEAD].victor = -1;
worm[thissy - FIRSTHEAD].alive = FALSE;
if (creature[which].mode == ARMOUR)
{ effect(FXUSEARMOUR);
orbscore(which, KILLWORM);
} else
{ effect(FXORBDEATH);
creature[which].alive = FALSE;
wormscore(thissy - FIRSTHEAD, creature[which].score);
if (worm[thissy - FIRSTHEAD].bias)
{ worm[thissy - FIRSTHEAD].lives += ORBBLOOD;
stat(thissy - FIRSTHEAD, LIFE);
} } }
else
{ effect(FXUSEARMOUR);
effect(FXORBDEATH);
creature[which].alive = FALSE;
wormscore(thissy - FIRSTHEAD, creature[which].score);
if (worm[thissy - FIRSTHEAD].bias)
{ worm[thissy - FIRSTHEAD].lives += ORBBLOOD;
stat(thissy - FIRSTHEAD, LIFE);
} }
creature[which].last = thissy - FIRSTHEAD + FIRSTTAIL; // note sign issues
} }
elif (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (creature[which].species == ORB)
{ if (creature[which].mode == TONGUE)
effect(FXUSETONGUE);
else
{ if (worm[thissy - FIRSTTAIL].alive)
{ effect(FXORBDEATH);
wormscore(thissy - FIRSTTAIL, creature[which].score);
if (worm[thissy - FIRSTTAIL].bias)
{ worm[thissy - FIRSTTAIL].lives += ORBBLOOD;
stat(thissy - FIRSTTAIL, LIFE);
} }
creature[which].alive = FALSE;
change(x, y, BONUS);
} } }
elif (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ if (creature[which].species == MISSILE)
{ if (creature[which].type != thissy - FIRSTPROTECTOR)
{ effect(FXUSEPROTECTOR);
creature[which].alive = FALSE;
} else creature[which].visible = FALSE;
} elif (creature[which].species == FRAGMENT)
{ effect(FXUSEPROTECTOR);
reflect(which);
} elif (creature[which].species == DRIP)
{ creature[which].alive = FALSE;
if (creature[which].type == thissy - FIRSTPROTECTOR || worm[thissy - FIRSTPROTECTOR].bias)
{ wormscore(thissy - FIRSTPROTECTOR, DRIPBONUS);
effect(FXDRIP);
} }
elif (creature[which].species == ORB)
{ effect(FXUSEPROTECTOR);
effect(FXORBDEATH);
creature[which].alive = FALSE;
wormscore(thissy - FIRSTPROTECTOR, creature[which].score);
if (worm[thissy - FIRSTPROTECTOR].bias)
{ worm[thissy - FIRSTPROTECTOR].lives += ORBBLOOD;
stat(thissy - FIRSTPROTECTOR, LIFE);
} } }
elif (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} elif (creature[which].species == ORB)
{ for (player = 0; player <= 3; player++)
{ letters[player][thissy - FIRSTLETTER] = FALSE;
drawletter(player, thissy, BLACK);
}
putletter(-1);
} }
elif (thissy == PENGUIN)
{ creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
if (creature[which].species == ORB && creature[which].mode == ARMOUR)
orbscore(which, KILLPENGUIN);
else
{ creature[which].alive = FALSE;
change(x, y, EMPTY);
} }
elif (thissy == FRAGMENT)
{ creature[whichcreature(x, y, FRAGMENT, which)].alive = FALSE;
if (creature[which].species == DRIP)
{ creature[which].alive = FALSE;
change(x, y, BONUS);
} elif (creature[which].species == PENGUIN)
{ creature[which].alive = FALSE;
change(x, y, EMPTY);
} elif (creature[which].species == MISSILE)
{ creature[which].alive = FALSE;
change(x, y, EMPTY);
} elif (creature[which].species == ORB)
{ if (creature[which].mode != ARMOUR)
creature[which].explode = TRUE;
else effect(FXUSEARMOUR);
} elif (creature[which].species == FRAGMENT)
{ effect(FXTHUD);
draw(x, y, EMPTY);
creature[which].alive = FALSE;
} }
elif (thissy == METAL)
{ if (creature[which].species == DRIP || creature[which].species == MISSILE)
creature[which].alive = FALSE;
elif (creature[which].species == FRAGMENT)
reflect(which);
} elif (thissy == STONE)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} }
elif (thissy == WOOD)
{ if (creature[which].species == FRAGMENT || creature[which].species == MISSILE)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} }
elif (thissy == GOAT)
{ // Drips, fragments and missiles
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
effect(FXGOATDEATH);
change(x, y, BONUS);
creature[which].alive = FALSE;
if (creature[which].species == MISSILE)
{ wormscore(creature[which].type, KILLGOAT);
if (worm[creature[which].type].bias)
{ worm[creature[which].type].lives += GOATBLOOD;
stat(creature[which].type, LIFE);
} } }
elif (thissy == SLIME)
{ if (creature[which].species == DRIP || creature[which].species == FRAGMENT || creature[which].species == MISSILE)
{ creature[which].alive = FALSE;
change(x, y, EMPTY);
} elif (creature[which].species == ORB)
{ if (creature[which].mode != TONGUE)
creature[which].alive = FALSE;
} }
elif (thissy == SKULL)
{ if (creature[which].species == ORB)
{ effect(FXGETSKULL);
orbscore(which, SKULLPOINT);
} elif (creature[which].species == FRAGMENT || creature[which].species == DRIP || creature[which].species == MISSILE)
{ effect(FXTHUD);
creature[which].alive = FALSE;
} }
elif (thissy == TELEPORT)
{ // Drips, fragments, missiles, orbs
i = whichteleport(x, y);
if (blocked(i, creature[which].deltax, creature[which].deltay))
creature[which].alive = FALSE;
else
{ effect(FXUSETELEPORT);
creature[which].x = teleport[level][partner(i)].x + creature[which].deltax;
creature[which].y = teleport[level][partner(i)].y + creature[which].deltay;
if (creature[which].species == DRIP)
{ if (!(valid(creature[which].x, creature[which].y)))
creature[which].alive = FALSE;
} elif (creature[which].species == ORB)
{ orbscore(which, TELPOINT);
creature[which].x = xwrap(creature[which].x);
creature[which].y = ywrap(creature[which].y);
} elif (creature[which].species == FRAGMENT)
creature[which].last = SILVER;
} }
elif (thissy == ORB)
{ i = whichcreature(x, y, ORB, which);
creature[which].alive = FALSE;
if (creature[which].species == MISSILE)
{ if (creature[i].mode != ARMOUR)
{ effect(FXORBDEATH);
creature[i].alive = FALSE;
change(x, y, BONUS);
wormscore(creature[which].type, creature[i].score);
if (worm[creature[which].type].bias)
{ worm[creature[which].type].lives += ORBBLOOD;
stat(creature[which].type, LIFE);
} }
else effect(FXUSEARMOUR);
} elif (creature[which].species == FRAGMENT || creature[which].species == DRIP)
{ if (creature[i].mode != ARMOUR)
{ creature[i].explode = TRUE;
draw(x, y, EMPTY);
} else effect(FXUSEARMOUR);
} elif (creature[which].species == ORB)
{ effect(FXORBDEATH);
creature[i].alive = FALSE;
change(x, y, BONUS);
} }
elif (creature[which].species == ORB)
{ if (thissy == EMPTY)
orbscore(which, EMPTYPOINT);
elif (thissy == SILVER)
orbscore(which, SILVERPOINT);
elif (thissy == GOLD)
orbscore(which, GOLDPOINT);
elif (thissy == AMMO || thissy == PULSE || thissy == SLAYER || thissy == LIGHTNING)
{ if (creature[which].mode != ARMOUR)
creature[which].explode = TRUE;
else effect(FXUSEARMOUR);
} elif (thissy == NITRO || thissy == POWER || thissy == SLOWER || thissy == REMNANTS)
{ effect(FXGETNITRO);
creature[which].speed = speedup(creature[which].speed, TRUE);
} elif (thissy == HEALER || thissy == LIFE || thissy == ICE || thissy == TREASURE || thissy == UMBRELLA)
orbsplit(which);
elif (thissy == ARMOUR)
{ creature[which].armour += MODEADD + (rand() % MODERAND);
creature[which].mode = ARMOUR;
} elif (thissy == TONGUE)
{ creature[which].tongue += MODEADD + (rand() % MODERAND);
creature[which].mode = TONGUE;
} elif (thissy == PROTECTOR)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives)
for (i = 0; i <= PROTECTORS; i++)
if (protector[player][i].alive)
{ protector[player][i].alive = FALSE;
if (protector[player][i].visible)
change(protector[player][i].x, protector[player][i].y, EMPTY);
} }
elif (thissy == MISSILE)
{ for (i = 0; i <= CREATURES; i++)
{ if (creature[i].alive && creature[i].species == MISSILE)
{ creature[i].alive = FALSE;
change(x, y, EMPTY);
} } }
elif (thissy == MULTIPLIER)
{ creature[which].multi *= 2;
if (creature[which].multi > MULTILIMIT)
creature[which].multi = MULTILIMIT;
} elif (thissy == BIAS)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives && worm[player].bias)
{ worm[player].bias = 0;
stat(player, BIAS);
} }
elif (thissy == AFFIXER)
{ for (player = 0; player <= 3; player++)
if (worm[player].lives)
worm[player].affixer = FALSE;
} elif (thissy == SWITCHER)
{ for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] >= FIRSTTAIL && field[xx][yy] <= LASTTAIL)
change(xx, yy, WOOD);
} elif (thissy == GROWER)
{ effect(FXGETGROWER);
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == WOOD)
for (xxx = xx - 1; xxx <= xx + 1; xxx++)
for (yyy = yy - 1; yyy <= yy + 1; yyy++)
if (valid(xxx, yyy) && field[xxx][yyy] == EMPTY)
field[xxx][yyy] = TEMPWOOD;
for (xx = 0; xx <= FIELDX; xx++)
for (yy = 0; yy <= FIELDY; yy++)
if (field[xx][yy] == TEMPWOOD)
change(xx, yy, WOOD);
} elif (thissy == CLOCK)
secondsperlevel -= rand() % CLOCKRAND;
} }
if (creature[which].alive && creature[which].visible && (creature[which].deltax || creature[which].deltay))
{ if (creature[which].species == MISSILE)
change(x, y, FIRSTMISSILE + creature[which].type);
elif (creature[which].species == DRIP)
change(x, y, FIRSTDRIP + creature[which].type);
elif (creature[which].species == FRAGMENT)
change(x, y, FRAGMENT);
elif (creature[which].species == GOAT)
change(x, y, GOAT);
elif (creature[which].species == PENGUIN)
change(x, y, PENGUIN);
elif (creature[which].species == ORB)
{ field[x][y] = ORB;
if (creature[which].mode == TONGUE)
draw(x, y, ORBTONGUE);
elif (creature[which].mode == ARMOUR)
draw(x, y, ORBARMOUR);
else draw(x, y, ORB);
} }
if (creature[which].alive && creature[which].species == GOAT)
{ // decide whether to fire
if (!(rand() % 10))
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].deltax = (rand() % 3) - 1;
creature[i].deltay = (rand() % 3) - 1;
if
( valid(x + creature[i].deltax, y + creature[i].deltay)
&& (creature[i].deltax || creature[i].deltay)
)
{ thissy = field[x + creature[i].deltax][y + creature[i].deltay];
if
( (thissy >= FIRSTEMPTY && thissy <= LASTEMPTY)
|| (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
)
{ effect(FXGOATFIRE);
creature[i].alive = TRUE;
creature[i].species = FRAGMENT;
creature[i].x = x + creature[i].deltax;
creature[i].y = y + creature[i].deltay;
creature[i].visible = TRUE;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(FRAGSPEED - level, 1);
} }
break;
} } } }
}
void orbsplit(SBYTE which)
{ SBYTE copy = 0, i;
effect(FXORBSPLIT);
for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ creature[i].x = creature[which].x;
creature[i].y = creature[which].y;
creature[i].score = creature[which].score;
creature[i].armour = creature[which].armour;
creature[i].tongue = creature[which].tongue;
creature[i].mode = creature[which].mode;
creature[i].speed = creature[which].speed;
creature[i].explode = FALSE;
creature[i].multi = creature[which].multi;
creature[i].last = EMPTY;
switch (copy)
{
case 0:
if (creature[which].deltax != -1 || creature[which].deltay != -1)
{ creature[i].deltax = -1;
creature[i].deltay = -1;
creature[i].alive = TRUE;
}
break;
case 1:
if (creature[which].deltax != 1 || creature[which].deltay != 1)
{ creature[i].deltax = 1;
creature[i].deltay = 1;
creature[i].alive = TRUE;
}
break;
case 2:
if (creature[which].deltax != 1 || creature[which].deltay != -1)
{ creature[i].deltax = 1;
creature[i].deltay = -1;
creature[i].alive = TRUE;
}
break;
case 3:
if (creature[which].deltax != -1 || creature[which].deltay != 1)
{ creature[i].deltax = -1;
creature[i].deltay = 1;
creature[i].alive = TRUE;
}
break;
default:
break;
}
if (++copy >= 4)
return;
} } }
void orientworms(void)
{ SBYTE player;
for (player = 0; player <= 3; player++)
{ worm[player].speed = NORMAL;
if (worm[player].lives)
stat(player, NITRO);
worm[player].moved = FALSE;
worm[player].x = startx[level];
worm[player].y = starty[level];
switch (player)
{
case 0:
worm[0].deltax = -1;
worm[0].deltay = 0;
break;
case 1:
worm[1].deltax = 1;
worm[1].deltay = 0;
break;
case 2:
worm[2].deltax = 0;
worm[2].deltay = -1;
break;
case 3:
worm[3].deltax = 0;
worm[3].deltay = 1;
break;
default:
break;
} } }
SBYTE partner(SBYTE which)
{ if (which % 2 == 0)
return((SBYTE) (which + 1));
else return((SBYTE) (which - 1));
}
/* NAME putletter -- Put a letter onto the field
SYNOPSIS void putletter(SBYTE player);
INPUTS SBYTE player -
0-3: player on whose behalf the letter is put on for
-1: any letter
RESULT none
NOTES noletter -
-2: if we found somewhere to put the letter
player: if we didn't */
void putletter(SBYTE player)
{ SBYTE count = 0, letter;
SBYTE i, x, y;
do
{ x = rand() % (FIELDX + 1);
y = rand() % (FIELDY + 1);
} while ((field[x][y] < FIRSTEMPTY || field[x][y] > LASTEMPTY) && (++count < PATIENCE));
if (count < PATIENCE)
{ noletter = -2;
if (player != -1)
{ for (i = 0; i <= LETTERS; i++)
if (!(letters[player][i]))
{ break;
}
if (i > LETTERS)
letter = rand() % (LETTERS + 1);
else
{ do
letter = rand() % (LETTERS + 1);
while (letters[player][letter]);
}
} else
letter = rand() % (LETTERS + 1);
change(x, y, letter + FIRSTLETTER);
letterx = x;
lettery = y;
lettertype = letter + FIRSTLETTER;
} else noletter = player;
}
/* NAME queue -- adds a keystroke to the key queue
SYNOPSIS name(SBYTE, SBYTE, SBYTE);
FUNCTION Adds a keystroke to the in-game key queue.
INPUTS player - player that pressed the key
deltax - the deltax of the key
deltay - the deltay of the key
IMPLEMENTATION
thequeue[] is an array, with QUEUELIMIT as its last index.
It is implemented as a FIFO stack rather than LIFO so that
the keystrokes are processed in the correct order (that is,
the order in which they were pressed). The oldest keystroke
is always at index [0], the next oldest at [1], and so on
upwards to the newest keystroke, at [worm[player].pos].
Keystrokes are removed from the bottom of the array ([0]),
and the rest of the array is shuffled down to fill the gap,
so that the contents of [1] go to [0], the contents of [2]
go to [1], etc. worm[player].pos is adjusted to always point
to the newest entry, which is the 'end' of the queue.
MODULE engine.c */
void queue(SBYTE player, SBYTE deltax, SBYTE deltay)
{ if (worm[player].pos < QUEUELIMIT)
{ worm[player].pos++;
thequeue[player][worm[player].pos].deltax = deltax;
thequeue[player][worm[player].pos].deltay = deltay;
} }
void reflect(UBYTE which)
{ creature[which].deltax = -creature[which].deltax;
creature[which].deltay = -creature[which].deltay;
creature[which].x += creature[which].deltax * 2;
creature[which].y += creature[which].deltay * 2;
}
ABOOL savefields(STRPTR fieldname)
{ SBYTE i, j;
TEXT IOBuffer[NAMELENGTH + 1];
matchteleports();
if (!ZOpen(fieldname, TRUE))
return FALSE;
// write header
strcpy(IOBuffer, "FSET 5.1");
IOBuffer[9] = levels;
if (!ZWrite(IOBuffer, 10))
{ ZClose();
return FALSE;
}
// write high score table
for (i = 0; i <= HISCORES; i++)
{ IOBuffer[0] = hiscore[i].player;
IOBuffer[1] = hiscore[i].level;
IOBuffer[2] = 0;
IOBuffer[3] = hiscore[i].score / 65536;
IOBuffer[4] = (hiscore[i].score % 65536) / 256;
IOBuffer[5] = (hiscore[i].score % 65536) % 256;
if (!ZWrite(IOBuffer, 6))
{ ZClose();
return FALSE;
}
for (j = 0; j <= NAMELENGTH; j++)
IOBuffer[j] = hiscore[i].name[j];
if (!ZWrite(IOBuffer, NAMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= TIMELENGTH; j++)
IOBuffer[j] = hiscore[i].time[j];
if (!ZWrite(IOBuffer, TIMELENGTH + 1))
{ ZClose();
return FALSE;
}
for (j = 0; j <= DATELENGTH; j++)
IOBuffer[j] = hiscore[i].date[j];
if (!ZWrite(IOBuffer, DATELENGTH + 1))
{ ZClose();
return FALSE;
} }
// write level data
for (i = 0; i <= levels; i++)
{ IOBuffer[0] = startx[i];
IOBuffer[1] = starty[i];
IOBuffer[2] = teleport[i][0].alive;
IOBuffer[3] = teleport[i][0].x;
IOBuffer[4] = teleport[i][0].y;
IOBuffer[5] = teleport[i][1].alive;
IOBuffer[6] = teleport[i][1].x;
IOBuffer[7] = teleport[i][1].y;
if (!ZWrite(IOBuffer, 8))
{ ZClose();
return FALSE;
}
if (!ZWrite((char *) &board[i][0][0], (FIELDX + 1) * (FIELDY + 1)))
{ ZClose();
return FALSE;
} }
// write version string
if (!ZWrite(VERSION, strlen(VERSION)))
{ ZClose();
return FALSE;
}
ZClose();
if (clearthem)
clearhiscores();
modified = FALSE;
return TRUE;
}
void saylevel(COLOUR colour)
{ TEXT mainstring[15] = "Level ", tempstring[4];
if (level > 0)
{ stci_d(&mainstring[6], level);
strcat((char*) mainstring, " of ");
stci_d(tempstring, levels);
strcat((char*) mainstring, (char*) tempstring);
say(mainstring, colour);
} else
{ if (a == FIELDEDIT)
say("Bonus Level", colour);
else
{ if (leveltype == TREASURE)
say("Bonus Level: Treasury!", colour);
else say("Bonus Level: Drips!", colour);
} } }
void setbrush(SBYTE newbrush)
{ brush = newbrush;
setpointer(brush);
underline(brush);
}
SBYTE slowdown(SBYTE speed)
{ speed *= 2;
if (speed > SLOW)
speed = SLOW;
return(speed);
}
/* NAME slowloop -- things done rarely
SYNOPSIS slowloop(void);
FUNCTION Decrements strength; creates goats, orbs, objects,
teleports, slime; controls timebombs; puts letters if
neccessary; blanks out old causes.
MODULE engine.c */
void slowloop(void)
{ ABOOL done;
SBYTE i, player, which, x, xx, y, yy;
UBYTE thissy;
// decrement worm strength
for (player = 0; player <= 3; player++)
if (worm[player].lives > 0 && ice == -1 || ice == player)
{ if (worm[player].bias > 0)
{ worm[player].bias--;
stat(player, BIAS);
}
if (worm[player].ice > 0 && --worm[player].ice == 0)
{ for (which = 0; which <= 3; which++)
if (player != which)
worm[player].pos = -1;
ice = -1;
}
if (worm[player].mode == ARMOUR)
{ if (--worm[player].armour == 0)
{ if (worm[player].tongue > 0)
worm[player].mode = TONGUE;
else worm[player].mode = NULL;
}
stat(player, ARMOUR);
} elif (worm[player].mode == TONGUE)
{ if (--worm[player].tongue == 0)
{ if (worm[player].armour > 0)
worm[player].mode = ARMOUR;
else worm[player].mode = NULL;
}
stat(player, TONGUE);
} }
// blank out old causes
for (player = 0; player <= 3; player++)
{ if (worm[player].lives > 0 && r > worm[player].causewait)
{ drawcause(player, BLACK);
worm[player].causewait = (ULONG) -1; // most future time possible
} }
if (ice == -1)
{ if (level)
{ // decrement orb strength
for (which = 0; which <= CREATURES; which++)
if (creature[which].alive && creature[which].species == ORB)
if (creature[which].mode == ARMOUR)
{ if (--creature[which].armour == 0)
if (creature[which].tongue > 0)
creature[which].mode = TONGUE;
else creature[which].mode = NULL;
} elif (creature[which].mode == TONGUE)
{ if (--creature[which].tongue == 0)
if (creature[which].armour > 0)
creature[which].mode = ARMOUR;
else creature[which].mode = NULL;
}
// create goats
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTGOAT, LASTGOAT)
&& field[x][y] != GOAT
)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXGOATBORN);
creature[i].x = x;
creature[i].y = y;
creature[i].alive = TRUE;
creature[i].species = GOAT;
creature[i].last = creature[i].oldlast = field[x][y];
creature[i].speed = (SBYTE) atleast(GOATSPEED - level, 2);
creature[i].visible = TRUE;
change(x, y, GOAT);
break;
} } }
// create orbs
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTEMPTY, LASTEMPTY)
)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXORBBORN);
creature[i].deltax = (rand() % 2) * 2 - 1;
creature[i].deltay = (rand() % 2) * 2 - 1;
creature[i].score = 0;
creature[i].alive = TRUE;
creature[i].armour = 0;
creature[i].tongue = 0;
creature[i].mode = NULL;
creature[i].speed = (SBYTE) atleast(ORBSPEED - level, 2);
creature[i].species = ORB;
creature[i].explode = FALSE;
creature[i].multi = 1;
creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].visible = TRUE;
change(x, y, ORB);
break;
} } }
// create penguins
if
( (!(rand() % freq))
&& findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
{ for (i = 0; i <= CREATURES; i++)
if (!(creature[i].alive))
{ effect(FXPENGUINBORN);
creature[i].x = x;
creature[i].y = y;
creature[i].last = EMPTY;
creature[i].speed = (SBYTE) atleast(PENGUINSPEED - level, 2);
creature[i].visible = TRUE;
creature[i].deltax = 0;
creature[i].deltay = 0;
creature[i].species = PENGUIN;
creature[i].alive = TRUE;
change(x, y, PENGUIN);
break;
} }
// create slime
if (!(rand() % (freq * 2)))
if (findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, SLIME);
// grow slime
if (!(rand() % (freq * 2)))
{ for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy) && field[xx][yy] == EMPTY && !(rand() % 2))
field[xx][yy] = TEMPSLIME;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == TEMPSLIME)
change(x, y, SLIME);
}
// retry putting letters if necessary
if (noletter != -2)
putletter(noletter);
}
// create drips
if
( (level && (!(rand() % freq)))
|| ((!level) && leveltype == DRIP && (!(rand() % FREQ_BONUS)))
)
{ x = (rand() % (FIELDX + 1));
y = (rand() % 3);
thissy = field[x][y];
if (thissy >= FIRSTEMPTY && thissy <= LASTEMPTY)
{ for (i = 0; i <= CREATURES; i++)
{ if (!creature[i].alive)
{ effect(FXDRIPBORN);
creature[i].alive = TRUE;
creature[i].last = EMPTY;
creature[i].species = DRIP;
creature[i].type = rand() % 4;
creature[i].x = x;
creature[i].y = y;
creature[i].deltax = 0;
creature[i].deltay = 1;
creature[i].visible = TRUE;
if (level)
creature[i].speed = (SBYTE) atleast(DRIPSPEED - level, 2);
else creature[i].speed = BONUSDRIPSPEED;
change(x, y, FIRSTDRIP + creature[which].type);
break;
} } } }
// create objects
for (which = 0; which <= LASTOBJECT; which++)
if (level || leveltype != TREASURE || which == TREASURE)
{ if (!(rand() % object[which].frequency) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, which);
} elif (!(rand() % (object[which].frequency / 10)) && findempty(&x, &y, FIRSTEMPTY, LASTEMPTY))
change(x, y, which);
// create teleports
if (!(rand() % TELFREQ)
&& !teleport[level][2].alive
&& findempty(&(teleport[level][2].x), &(teleport[level][2].y), FIRSTEMPTY, LASTEMPTY)
&& findempty(&(teleport[level][3].x), &(teleport[level][3].y), FIRSTEMPTY, LASTEMPTY)
&& (teleport[level][2].x != teleport[level][3].x || teleport[level][2].y != teleport[level][3].y))
{ teleport[level][2].alive = TRUE;
teleport[level][3].alive = TRUE;
change(teleport[level][2].x, teleport[level][2].y, TELEPORT);
change(teleport[level][3].x, teleport[level][3].y, TELEPORT);
}
// create timebombs
for (i = 0; i <= level; i++)
{ x = rand() % (FIELDX + 1);
y = rand() % (FIELDY + 1);
if (field[x][y] == BOMB)
{ done = FALSE;
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive == FALSE && !done)
{ timebomb[which].alive = TRUE;
timebomb[which].x = x;
timebomb[which].y = y;
timebomb[which].time = 10;
done = TRUE;
} } }
// decrement and explode timebombs
if (!(r % (VERYSLOW * 4)))
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive)
{ if (field[timebomb[which].x][timebomb[which].y] != BOMB)
timebomb[which].alive = FALSE;
else
{ effect(FXTIMEBOMBTICK);
timebomb[which].time--;
if (timebomb[which].time < 0)
{ timebomb[which].alive = FALSE;
change(timebomb[which].x, timebomb[which].y, EMPTY);
bombblast(BOMB, 0, timebomb[which].x, timebomb[which].y);
} else draw(timebomb[which].x, timebomb[which].y, ZERO + timebomb[which].time);
} } }
}
SBYTE speedup(SBYTE speed, ABOOL nitro)
{ speed /= 2;
if (speed < FAST)
speed = FAST;
return(speed);
}
ULONG squareblast(SBYTE type, SBYTE player, SBYTE thissy, SBYTE x, SBYTE y)
{ SBYTE which;
ULONG score = 0L;
SBYTE filler;
if (type == HEAD && worm[player].bias)
filler = SILVER;
else filler = EMPTY;
if (thissy <= LASTOBJECT)
{ change(x, y, filler);
if (thissy == BOMB)
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive && timebomb[which].x == x && timebomb[which].y == y)
timebomb[which].alive = FALSE;
} elif (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
change(x, y, filler);
elif (thissy == ORB)
{ which = whichcreature(x, y, ORB, 255);
if (creature[which].mode != ARMOUR)
{ effect(FXORBDEATH);
creature[which].alive = FALSE;
score = creature[which].score;
if (type == HEAD && worm[player].bias)
worm[player].lives += ORBBLOOD;
change(x, y, BONUS);
} else effect(FXUSEARMOUR);
} elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (type != HEAD || player != thissy - FIRSTHEAD)
if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = BOMB;
worm[thissy - FIRSTHEAD].alive = FALSE;
stat(thissy - FIRSTHEAD, LIFE);
if (type == HEAD)
worm[thissy - FIRSTHEAD].victor = player;
else
{ worm[thissy - FIRSTHEAD].victor = -1;
score = KILLWORM; // worms will get thissy bonus from death(), so it is not given for them here.
}
} else effect(FXUSEARMOUR);
} elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (type != HEAD || player != thissy - FIRSTMISSILE)
{ creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
change(x, y, filler);
} }
elif (thissy == PENGUIN)
{ creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy == FRAGMENT)
{ creature[whichcreature(x, y, FRAGMENT, 255)].alive = FALSE;
change(x, y, filler);
} elif (thissy == GOAT)
{ creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
change(x, y, BONUS);
}
return(score);
}
void timeloop(void)
{ TEXT timedisplay[5] = {"#:##"};
PERSIST ABOOL outoftime = FALSE;
UBYTE i;
secondsleft = atleast(secondsleft, 0);
timedisplay[0] = 48 + (secondsleft / 60);
timedisplay[2] = 48 + ((secondsleft % 60) / 10);
timedisplay[3] = 48 + ((secondsleft % 60) % 10);
if (!level)
{ say(timedisplay, worm[treasurer].colour);
if (!secondsleft)
{ level = reallevel + 1;
secondsleft = SECONDSPERLEVEL;
newlevel(treasurer);
} }
elif (!secondsleft)
{ say("Out of time!", WHITE);
if (!outoftime)
{ effect(FXTIMEALERT);
outoftime = TRUE;
freq /= 2;
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive)
creature[i].speed = speedup(creature[i].speed, TRUE);
} }
else
{ outoftime = FALSE;
say(timedisplay, WHITE);
} }
void train(SCANCODE scancode)
{ SBYTE i, x, y;
UBYTE thissy;
switch(scancode) {
case HELP:
trainer = !trainer;
break;
case NUMERICSLASH:
// Complete the level.
if (trainer)
{ trainer = FALSE;
if (worm[1].lives > 0)
for (i = 0; i <= LETTERS; i++)
{ letters[1][i] = TRUE;
drawletter(1, FIRSTLETTER + i, NORMAL);
} }
break;
case NUMERICASTERISK:
// Change most squares to gold.
if (trainer)
{ trainer = FALSE;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == FIRSTTAIL + 1)
change(x, y, METAL);
else
{ thissy = field[x][y];
if
( thissy != STONE
&& thissy != GOAT
&& thissy != ORB
&& thissy != SKULL
&& (thissy < FIRSTHEAD || thissy > LASTHEAD)
&& (thissy < FIRSTLETTER || thissy > LASTLETTER)
)
change(x, y, GOLD);
} }
break;
case NUMERICPLUS:
/* Full lives, tongue, bias, ammo, power, nitro, affixer
and remnants. */
if (trainer)
{ trainer = FALSE;
if (worm[1].lives > 0)
{ worm[1].lives = LIVESLIMIT;
stat(1, LIFE);
worm[1].tongue = MODELIMIT;
worm[1].mode = TONGUE;
stat(1, TONGUE);
worm[1].bias = BIASLIMIT;
stat(1, BIAS);
worm[1].ammo = AMMOLIMIT;
stat(1, AMMO);
worm[1].power = POWERLIMIT;
stat(1, POWER);
worm[1].nitro = TRUE;
stat(1, NITRO);
worm[1].affixer = TRUE;
worm[1].remnants = 100;
trainer = FALSE;
} }
break;
default:
break;
} }
void turnworm(SBYTE player, SBYTE deltax, SBYTE deltay)
{
if (worm[player].nitro || !deltax || !deltay)
{ if (worm[player].deltax == deltax && worm[player].deltay == deltay)
{ worm[player].speed = speedup(worm[player].speed, worm[player].nitro);
stat(player, NITRO);
} elif (worm[player].deltax == -deltax && worm[player].deltay == -deltay)
{ worm[player].speed = slowdown(worm[player].speed);
stat(player, NITRO);
} else
{ worm[player].deltax = deltax;
worm[player].deltay = deltay;
} }
}
void updatesquare(SBYTE x, SBYTE y)
{ SBYTE which;
if (startx[level] == x && starty[level] == y)
draw(x, y, START);
elif (board[level][x][y] == TELEPORT)
{ for (which = 0; which <= 1; which++)
if (teleport[level][which].alive && teleport[level][which].x == x && teleport[level][which].y == y)
draw(x, y, ONE + which);
} else draw(x, y, board[level][x][y]);
}
SBYTE valid(SBYTE x, SBYTE y)
{ if (x >= 0 && x <= FIELDX && y >= 0 && y <= FIELDY)
return(TRUE);
else
return(FALSE);
}
UBYTE whichcreature(SBYTE x, SBYTE y, UBYTE species, UBYTE exception)
{ UBYTE i;
for (i = 0; i <= CREATURES; i++)
if
( creature[i].alive
&& creature[i].x == x
&& creature[i].y == y
&& creature[i].species == species
&& i != exception
)
return i;
return 255; // error code
}
SBYTE whichteleport(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= TIMEBOMBS; which++)
if (teleport[level][which].alive && teleport[level][which].x == x && teleport[level][which].y == y)
return((SBYTE) which);
return((SBYTE) -1); // error code
}
SBYTE whichtimebomb(SBYTE x, SBYTE y)
{ SBYTE which;
for (which = 0; which <= TIMEBOMBS; which++)
if (timebomb[which].alive && timebomb[which].x == x && timebomb[which].y == y)
return(which);
return(-1); // error code
}
void wormbullet(SBYTE player)
{ ABOOL finished,
flag = FALSE,
remnants = FALSE,
ok = FALSE,
lettered = FALSE;
LONG score;
SBYTE distance,
thissy,
i, j,
x, y;
if (!worm[player].ammo)
{ stat(player, BONUS);
if (worm[player].speed == FAST)
distance = FASTDISTANCE;
elif (worm[player].speed == NORMAL)
distance = NORMALDISTANCE;
else
{ // assert(worm[player].speed == SLOW);
distance = SLOWDISTANCE;
}
// check for metal
for (i = 1; i < distance; i++)
{ x = xwrap(worm[player].x + (i * worm[player].deltax));
y = ywrap(worm[player].y + (i * worm[player].deltay));
if (field[x][y] == METAL)
flag = TRUE;
}
if (!flag)
{ // assert(abs(worm[player].deltax) == 1 && abs(worm[player].deltay) == 1);
x = xwrap(worm[player].x + worm[player].deltax * distance);
y = ywrap(worm[player].y + worm[player].deltay * distance);
thissy = field[x][y];
if (thissy == TELEPORT)
{ i = whichteleport(x, y);
if (!blocked(i, worm[player].deltax, worm[player].deltay))
ok = TRUE;
}
if (ok || ((thissy < STONE || thissy > GOAT) && thissy != METAL))
{ // Amiga-worms only make jumping sounds in demo mode
flag = FALSE;
for (i = 0; i <= 3; i++)
if (worm[i].control == HUMAN)
flag = TRUE;
if (!flag || worm[player].control == HUMAN)
effect(FXJUMP);
worm[player].deltax *= distance;
worm[player].deltay *= distance;
} } }
else
{ effect(FXSHOOT);
worm[player].ammo--;
stat(player, AMMO);
if (worm[player].remnants)
{ worm[player].remnants--;
remnants = TRUE;
}
if (worm[player].bias)
createmissile(player);
for (i = 0; i <= worm[player].power; i++)
{ bullet[i].alive = TRUE;
bullet[i].teleported = 0;
bullet[i].visible = TRUE;
bullet[i].reflected = FALSE;
if (i % 2 == 0)
distance = i / 2;
else
distance = -((i + 1) / 2);
if (worm[player].deltax == 0)
{ bullet[i].x = worm[player].x + distance;
bullet[i].y = worm[player].y;
} elif (worm[player].deltay == 0)
{ bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + distance;
} else
{ switch (i) {
case 0:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 1:
bullet[i].x = worm[player].x + worm[player].deltax;
bullet[i].y = worm[player].y;
break;
case 2:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay;
break;
case 3:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y;
break;
case 4:
bullet[i].x = worm[player].x;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
case 5:
bullet[i].x = worm[player].x + worm[player].deltax * 2;
bullet[i].y = worm[player].y - worm[player].deltay;
break;
case 6:
bullet[i].x = worm[player].x - worm[player].deltax;
bullet[i].y = worm[player].y + worm[player].deltay * 2;
break;
default:
break;
} }
if
( (!(valid(bullet[i].x, bullet[i].y)))
|| field[bullet[i].x][bullet[i].y] == METAL
|| field[bullet[i].x][bullet[i].y] == STONE
|| (field[bullet[i].x][bullet[i].y] > FIRSTLETTER && field[bullet[i].x][bullet[i].y] < LASTLETTER)
)
bullet[i].alive = FALSE;
/* That is somewhat kludgy. It is designed to prevent
players from being able to destroy metal, etc. by
firing at point-blank range. What really should be
done, however, is to organize wormbullet() so that
things are done in the same order as most of the
other loops are. Note that theoretically the bullet
would bounce back from metal and strike the firer.
This is not handled by this kludge code. */
}
score = 0L;
finished = FALSE;
while (!finished)
{ finished = TRUE;
for (i = 0; i <= worm[player].power; i++)
{ if (bullet[i].alive)
{ finished = FALSE;
if (bullet[i].visible && (!remnants))
{ if (bullet[i].teleported)
change(bullet[i].x, bullet[i].y, GOLD);
else change(bullet[i].x, bullet[i].y, EMPTY);
}
else bullet[i].visible = TRUE;
if (bullet[i].reflected)
{ bullet[i].x -= worm[player].deltax;
bullet[i].y -= worm[player].deltay;
} else
{ bullet[i].x += worm[player].deltax;
bullet[i].y += worm[player].deltay;
}
if (!(valid(bullet[i].x, bullet[i].y)))
bullet[i].alive = FALSE;
else if (bullet[i].x == worm[player].x && bullet[i].y == worm[player].y)
{ // hit by own bullet
bullet[i].alive = FALSE;
if (worm[player].mode != ARMOUR)
{ worm[player].cause = FIRSTFIRE + player;
worm[player].victor = -1;
worm[player].alive = FALSE;
} }
else
{ x = bullet[i].x;
y = bullet[i].y;
thissy = field[x][y];
if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ worm[thissy - FIRSTHEAD].cause = FIRSTFIRE + player;
worm[thissy - FIRSTHEAD].victor = player;
worm[thissy - FIRSTHEAD].alive = FALSE;
if (player != thissy - FIRSTHEAD)
score += KILLWORM + HITSHOT;
} else effect(FXUSEARMOUR);
bullet[i].alive = FALSE;
} elif (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ if (player != thissy - FIRSTPROTECTOR)
{ effect(FXUSEPROTECTOR);
bullet[i].alive = FALSE;
} else
bullet[i].visible = FALSE;
} elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
else bullet[i].visible = FALSE;
} elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ j = whichcreature(x, y, DRIP, 255);
creature[j].alive = FALSE;
} elif (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ wormletter(player, thissy);
lettered = TRUE;
} else
{ switch(thissy) {
case METAL:
if (bullet[i].reflected)
bullet[i].alive = FALSE;
else
{ bullet[i].reflected = TRUE;
bullet[i].x -= worm[player].deltax;
bullet[i].y -= worm[player].deltay;
}
break;
case STONE:
bullet[i].alive = FALSE;
break;
case ORB:
bullet[i].alive = FALSE;
j = whichcreature(x, y, ORB, 255);
if (creature[j].mode != ARMOUR)
{ creature[j].explode = TRUE;
score += creature[j].score + HITSHOT;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
} else effect(FXUSEARMOUR);
break;
case TELEPORT:
j = whichteleport(bullet[i].x, bullet[i].y);
if (bullet[i].teleported == 2)
bullet[i].alive = FALSE;
else
{ effect(FXUSETELEPORT);
bullet[i].visible = FALSE;
bullet[i].teleported++;
bullet[i].x = teleport[level][partner(j)].x;
bullet[i].y = teleport[level][partner(j)].y;
score += TELPOINT;
}
break;
case WOOD:
if (!worm[player].bias)
bullet[i].alive = FALSE;
break;
case GOAT:
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
change(x, y, BONUS);
score += KILLGOAT + HITSHOT;
if (worm[player].bias)
worm[player].lives += GOATBLOOD;
bullet[i].alive = FALSE;
break;
case SKULL:
bullet[i].alive = FALSE;
break;
case FRAGMENT:
bullet[i].alive = FALSE;
creature[whichcreature(x, y, FRAGMENT, 255)].alive = FALSE;
change(x, y, EMPTY);
break;
case BOMB:
j = whichtimebomb(x, y);
if (j != -1)
{ bullet[i].alive = FALSE;
timebomb[j].alive = FALSE;
change(timebomb[j].x, timebomb[j].y, EMPTY);
bombblast(HEAD, player, timebomb[j].x, timebomb[j].y);
}
break;
case PENGUIN:
bullet[i].alive = FALSE;
creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
change(x, y, EMPTY);
score += KILLPENGUIN + HITSHOT;
if (worm[player].bias)
worm[player].lives += PENGUINBLOOD;
break;
default:
break;
}
if (bullet[i].alive && bullet[i].visible)
{ draw(x, y, FIRSTFIRE + player);
if (remnants)
field[x][y] = FIRSTFIRE + player;
} } } } } }
if (lettered)
putletter(player);
wormscore(player, score);
if (worm[player].bias)
stat(player, LIFE);
clearkybd();
} }
void wormletter(SBYTE player, SBYTE thissy)
{ if (thissy == GREEN_C)
effect(FX_C);
elif (thissy == RED_O)
effect(FX_O);
elif (thissy == BLUE_M)
effect(FX_M);
elif (thissy == YELLOW_P)
effect(FX_P);
elif (thissy == GREEN_L)
effect(FX_L);
elif (thissy == BLUE_T)
effect(FX_T);
else // assumes RED_E or YELLOW_E
effect(FX_E);
letters[player][thissy - FIRSTLETTER] = TRUE;
drawletter(player, thissy, NORMAL);
if (player == (thissy - FIRSTLETTER) % 4)
wormscore(player, MYLETTER);
else wormscore(player, YOURLETTER);
}
/* NAME wormloop -- controls worms and protectors
SYNOPSIS wormloop(SBYTE);
FUNCTION Amiga-worm thinking, processing one keystroke from the
worm's queue, all the worm's protectors' control, the
worm's control.
MODULE engine.c */
void wormloop(SBYTE player)
{ ABOOL done, flag;
SBYTE bestgood, bestx, besty, animal, dirx, diry,
good, i, ithis, iwhich, thissy, thisletter,
thisprot = -1, which, x, xx, y, yy;
LONG score = 0L;
PERSIST SBYTE otherfield[FIELDX + 1][FIELDY + 1];
/* AI: Amiga worm control
Worm checks ahead, left and right one square. Assigns opinions to those
three choices and then takes the appropriate one.
Possible enhancements:
recognition of possession of objects
(esp. ammo, nitro, tongue, armour)
longer lookahead */
if (worm[player].control == AMIGA)
{ if (!(rand() % 50))
queue(player, (rand() % 3) - 1, (rand() % 3) - 1);
else
{ bestgood = -128;
for (i = 0; i <= 2; i++)
{ switch(i) {
case 0:
dirx = worm[player].deltax;
diry = worm[player].deltay;
break;
case 1:
if (worm[player].deltax == 0) // if going north or south
{ dirx = -1; // then look west
diry = 0;
} else // if going east or west
{ dirx = 0; // then look north
diry = -1;
}
break;
case 2:
if (worm[player].deltax == 0) // if going north or south
{ dirx = 1; // then look east
diry = 0;
} else // if going east or west
{ dirx = 0; // then look south
diry = 1;
}
break;
default:
break;
}
thissy = field[xwrap(worm[player].x + dirx)][ywrap(worm[player].y + diry)];
if (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
good = 100;
elif (thissy <= LASTOBJECT)
good = (SBYTE) object[thissy].score;
elif (thissy == FIRSTPROTECTOR + player)
good = 6;
elif (thissy == FIRSTFIRE + player)
good = 5;
elif (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (player == thissy - FIRSTTAIL)
good = -30;
else good = -60;
} else switch(thissy) {
case SKULL:
good = 70;
break;
case GOLD:
good = 50;
break;
case SILVER:
good = 40;
break;
case EMPTY:
good = 10;
break;
case WOOD:
good = -80;
break;
case STONE:
good = -90;
break;
case METAL:
good = -90;
break;
case GOAT:
good = -100;
break;
default:
good = -50; // slime, heads, orbs, etc.
break;
}
if (good > bestgood)
{ bestx = dirx;
besty = diry;
bestgood = good;
} }
if (bestgood <= -60)
queue(player, 0, 0);
elif (bestgood < 0 && (!(rand() % 2)))
queue(player, 0, 0);
elif (bestx != worm[player].deltax || besty != worm[player].deltay)
queue(player, bestx, besty);
} }
// remove a keystroke from the queue
if (worm[player].pos != -1)
{ if (thequeue[player][0].deltax == 0 && thequeue[player][0].deltay == 0)
wormbullet(player);
else turnworm(player, thequeue[player][0].deltax, thequeue[player][0].deltay);
if (--worm[player].pos != -1)
for (i = 0; i <= worm[player].pos; i++)
{ thequeue[player][i].deltax = thequeue[player][i + 1].deltax;
thequeue[player][i].deltay = thequeue[player][i + 1].deltay;
} }
// move worm
change(worm[player].x, worm[player].y, worm[player].last);
worm[player].x = xwrap(worm[player].x + worm[player].deltax);
worm[player].y = ywrap(worm[player].y + worm[player].deltay);
worm[player].deltax = bsign(worm[player].deltax);
worm[player].deltay = bsign(worm[player].deltay);
worm[player].last = FIRSTTAIL + player;
// move protectors
for (which = 0; which <= PROTECTORS; which++)
{ if (protector[player][which].alive)
{ if (protector[player][which].visible)
change(protector[player][which].x, protector[player][which].y, protector[player][which].last);
else protector[player][which].visible = TRUE;
protector[player][which].last = EMPTY;
if (which == NOSE)
{ protector[player][which].relx = worm[player].deltax * NOSEDISTANCE;
protector[player][which].rely = worm[player].deltay * NOSEDISTANCE;
if (!worm[player].affixer)
{ worm[player].position = 0;
if (worm[player].deltax == 0)
protector[player][which].relx = worm[player].position;
elif (worm[player].deltay == 0)
protector[player][which].rely = worm[player].position;
elif (worm[player].position == -1)
protector[player][which].relx = worm[player].deltax * (NOSEDISTANCE - 1);
elif (worm[player].position == 1)
protector[player][which].rely = worm[player].deltay * (NOSEDISTANCE - 1);
} }
elif (!worm[player].affixer)
{ if (protector[player][which].relx == 1 && protector[player][which].rely == -1)
{ protector[player][which].deltax = 0;
protector[player][which].deltay = 1;
} elif (protector[player][which].relx == 1 && protector[player][which].rely == 1)
{ protector[player][which].deltax = -1;
protector[player][which].deltay = 0;
} elif (protector[player][which].relx == -1 && protector[player][which].rely == 1)
{ protector[player][which].deltax = 0;
protector[player][which].deltay = -1;
} elif (protector[player][which].relx == -1 && protector[player][which].rely == -1)
{ protector[player][which].deltax = 1;
protector[player][which].deltay = 0;
}
protector[player][which].relx += protector[player][which].deltax;
protector[player][which].rely += protector[player][which].deltay;
}
protector[player][which].x = worm[player].x + protector[player][which].relx;
protector[player][which].y = worm[player].y + protector[player][which].rely;
if (!valid(protector[player][which].x, protector[player][which].y))
protector[player][which].visible = FALSE;
} }
// collision detection
animal = HEAD;
while (animal != NULL)
{ if (animal == HEAD)
{ x = worm[player].x;
y = worm[player].y;
thissy = field[x][y];
if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (worm[player].mode != TONGUE)
if (worm[thissy - FIRSTHEAD].mode != TONGUE)
{ // both worms die
worm[player].cause = thissy;
worm[player].alive = FALSE;
worm[player].victor = -1;
worm[thissy - FIRSTHEAD].cause = FIRSTHEAD + player;
worm[thissy - FIRSTHEAD].alive = FALSE;
worm[thissy - FIRSTHEAD].victor = -1;
} else
{ // this worm dies
worm[player].cause = thissy;
worm[player].alive = FALSE;
worm[player].victor = thissy - FIRSTHEAD;
}
elif (worm[thissy - FIRSTHEAD].mode != TONGUE)
{ // other worm dies
worm[thissy - FIRSTHEAD].cause = FIRSTHEAD + player;
worm[thissy - FIRSTHEAD].alive = FALSE;
worm[thissy - FIRSTHEAD].victor = player;
} }
elif (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (worm[player].mode == TONGUE)
{ effect(FXUSETONGUE);
if (players == 1)
if (player == thissy - FIRSTTAIL)
{ score += TURNTOSILVER;
worm[player].last = SILVER;
} else
{ score += TURNTOGOLD;
worm[player].last = GOLD;
} }
else
{ worm[player].cause = thissy;
worm[player].alive = FALSE;
worm[player].victor = thissy - FIRSTTAIL;
} }
elif (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
if (worm[player].mode != ARMOUR)
{ worm[player].cause = thissy;
worm[player].victor = thissy - FIRSTPROTECTOR;
worm[player].alive = FALSE;
} else effect(FXUSEARMOUR);
elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
{ creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
if (worm[player].mode != ARMOUR)
{ worm[player].alive = FALSE;
worm[player].cause = thissy;
worm[player].victor = thissy - FIRSTMISSILE;
} else effect(FXUSEARMOUR);
} }
elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
if (player == thissy - FIRSTDRIP)
{ effect(FXDRIP);
wormscore(player, DRIPBONUS);
} else
{ worm[player].alive = FALSE;
worm[player].cause = thissy;
worm[player].victor = -1;
}
} elif (thissy == STONE || thissy == GOAT || thissy == METAL)
{ worm[player].cause = thissy;
worm[player].victor = -1;
worm[player].alive = FALSE;
ramming(player);
} elif (thissy == WOOD)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = WOOD;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
elif (thissy == SLIME)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = SLIME;
worm[player].alive = FALSE;
worm[player].victor = -1;
} }
elif (thissy == PENGUIN)
{ effect(FXPENGUINDEATH);
creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
if (worm[player].armour)
{ score += KILLPENGUIN;
if (worm[player].bias)
{ worm[player].lives += PENGUINBLOOD;
stat(player, LIFE);
} }
else
{ worm[player].alive = FALSE;
worm[player].cause = PENGUIN;
worm[player].victor = 1;
} }
elif (thissy == TELEPORT)
{ which = whichteleport(x, y);
if (blocked(which, worm[player].deltax, worm[player].deltay))
{ worm[player].cause = TELEPORT;
worm[player].victor = -1;
worm[player].alive = FALSE;
ramming(player);
} else
{ effect(FXUSETELEPORT);
score += TELPOINT;
worm[player].x = xwrap(teleport[level][partner(which)].x + worm[player].deltax);
worm[player].y = ywrap(teleport[level][partner(which)].y + worm[player].deltay);
} }
elif (thissy >= FIRSTFIRE && thissy <= LASTFIRE)
{ if (player != thissy - FIRSTFIRE && worm[player].mode != ARMOUR)
{ worm[player].cause = REMNANTS;
worm[player].victor = thissy - FIRSTFIRE;
worm[player].alive = FALSE;
} } }
else // animal == PROTECTOR
{ x = protector[player][thisprot].x;
y = protector[player][thisprot].y;
thissy = field[x][y];
if (thissy >= FIRSTHEAD && thissy <= LASTHEAD)
{ if (player != thissy - FIRSTHEAD)
{ if (worm[thissy - FIRSTHEAD].mode != ARMOUR)
{ effect(FXUSEPROTECTOR);
worm[thissy - FIRSTHEAD].cause = FIRSTPROTECTOR + player;
worm[thissy - FIRSTHEAD].alive = FALSE;
worm[thissy - FIRSTHEAD].victor = player;
} else
{ effect(FXUSEARMOUR);
protector[player][thisprot].visible = FALSE;
} }
else // protector is over worm's own head; caused by ramming
protector[player][thisprot].visible = FALSE;
} elif (thissy >= FIRSTTAIL && thissy <= LASTTAIL)
{ if (player == thissy - FIRSTTAIL || worm[player].mode == TONGUE)
protector[player][thisprot].visible = FALSE;
} elif (thissy >= FIRSTPROTECTOR && thissy <= LASTPROTECTOR)
{ protector[player][thisprot].alive = FALSE;
for (which = 0; which <= PROTECTORS; which++)
if (protector[thissy - FIRSTPROTECTOR][which].alive && protector[thissy - FIRSTPROTECTOR][which].x == x && protector[thissy - FIRSTPROTECTOR][which].y == y)
{ protector[thissy - FIRSTPROTECTOR][which].alive = FALSE;
break;
}
change(x, y, EMPTY);
} elif (thissy == STONE || thissy == WOOD || thissy == METAL)
protector[player][thisprot].visible = FALSE;
elif (thissy >= FIRSTMISSILE && thissy <= LASTMISSILE)
{ if (player != thissy - FIRSTMISSILE)
{ effect(FXUSEPROTECTOR);
creature[whichcreature(x, y, MISSILE, 255)].alive = FALSE;
} else protector[player][thisprot].visible = FALSE;
} elif (thissy == TELEPORT)
protector[player][thisprot].visible = FALSE;
elif (thissy >= FIRSTDRIP && thissy <= LASTDRIP)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
if (player == thissy - FIRSTDRIP)
{ effect(FXDRIP);
score += DRIPBONUS;
} }
elif (thissy == PENGUIN)
{ effect(FXUSEPROTECTOR);
effect(FXPENGUINDEATH);
creature[whichcreature(x, y, PENGUIN, 255)].alive = FALSE;
score += KILLPENGUIN;
if (worm[player].bias)
{ worm[player].lives += PENGUINBLOOD;
stat(player, LIFE);
} }
elif (thissy == GOAT)
{ effect(FXUSEPROTECTOR);
effect(FXGOATDEATH);
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
protector[player][thisprot].last = BONUS;
score += KILLGOAT;
if (worm[player].bias)
{ worm[player].lives += GOATBLOOD;
stat(player, LIFE);
} } }
if (thissy >= FIRSTLETTER && thissy <= LASTLETTER)
{ wormletter(player, thissy);
putletter(player);
} elif (thissy <= LASTOBJECT)
{ score += object[thissy].score;
if (thissy != SLAYER
&& thissy != BOMB
&& thissy != MISSILE
&& thissy != NITRO
&& thissy != POWER
&& thissy != AMMO)
effect(FXGETOBJECT);
switch(thissy)
{
case BONUS:
thisletter = rand() % (LETTERS + 1);
letters[player][thisletter] = TRUE;
drawletter(player, FIRSTLETTER + thisletter, NORMAL);
break;
case AMMO:
effect(FXGETAMMO);
worm[player].ammo += (rand() % 5) + 2; // 2-6 bullets
stat(player, AMMO);
break;
case ARMOUR:
/* FXUSEDRILL sample will automatically stop, because
a square with ARMOUR is not a square with TAIL. */
worm[player].armour += MODEADD + (rand() % MODERAND);
worm[player].mode = ARMOUR;
stat(player, ARMOUR);
break;
case TONGUE:
worm[player].tongue += MODEADD + (rand() % MODERAND);
worm[player].mode = TONGUE;
stat(player, TONGUE);
worm[player].last = FIRSTTAIL + player;
break;
case NITRO:
effect(FXGETNITRO);
worm[player].nitro = TRUE;
stat(player, NITRO);
break;
case BOMB:
flag = FALSE;
which = whichtimebomb(x, y);
if (which != -1)
{ flag = TRUE;
if (animal == HEAD)
{ // push timebomb
if (valid(x + worm[player].deltax, y + worm[player].deltay))
{ ithis = field[x + worm[player].deltax][y + worm[player].deltay];
if (ithis == TELEPORT)
score += BOMBOVEREDGE;
elif (ithis == SKULL)
{ effect(FXGETSKULL);
score += SKULLPOINT;
} elif (ithis <= LASTEMPTY)
{ if (ithis <= LASTOBJECT)
{ score += object[thissy].score;
if (ithis == BOMB)
{ iwhich = whichtimebomb(x + worm[player].deltax, y + worm[player].deltay);
if (iwhich != -1)
timebomb[iwhich].alive = FALSE;
} }
timebomb[which].x += worm[player].deltax;
timebomb[which].y += worm[player].deltay;
field[timebomb[which].x][timebomb[which].y] = BOMB;
draw(timebomb[which].x, timebomb[which].y, ZERO + timebomb[which].time);
} else flag = FALSE;
} else score += BOMBOVEREDGE;
} else protector[player][thisprot].visible = FALSE;
}
if (!flag)
{ if (worm[player].mode == NULL)
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
bombblast(HEAD, player, worm[player].x, worm[player].y);
}
break;
case POWER:
effect(FXGETPOWERUP);
if (worm[player].power < POWERLIMIT)
{ worm[player].power += 2;
stat(player, POWER);
}
break;
case SLAYER:
for (which = 0; which <= CREATURES; which++)
if (creature[which].alive)
{ if (creature[which].species == ORB)
{ effect(FXORBDEATH);
score += creature[which].score;
creature[which].explode = TRUE;
if (worm[player].bias)
worm[player].lives += ORBBLOOD;
} elif (creature[which].species == GOAT)
{ effect(FXGOATDEATH);
creature[which].alive = FALSE;
score += KILLGOAT;
if (worm[player].bias)
worm[player].lives += GOATBLOOD;
change(creature[which].x, creature[which].y, BONUS);
} elif (creature[which].species == DRIP)
{ creature[which].alive = FALSE;
change(creature[which].x, creature[which].y, EMPTY);
} elif (creature[which].species == MISSILE && creature[which].type != player)
{ creature[which].alive = FALSE;
change(creature[which].x, creature[which].y, EMPTY);
} }
for (which = 0; which <= 3; which++)
if (player != which && worm[which].mode != ARMOUR)
{ worm[which].alive = FALSE;
worm[which].cause = SLAYER;
worm[which].victor = player;
}
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SLIME)
change(x, y, EMPTY);
if (worm[player].bias)
stat(player, LIFE);
break;
case PROTECTOR:
done = FALSE;
for (which = 0; which <= PROTECTORS; which++)
if (!protector[player][which].alive && !done)
{ do
{ protector[player][which].relx = ((rand() % 2) * 2) - 1;
protector[player][which].rely = ((rand() % 2) * 2) - 1;
for (iwhich = 0; iwhich <= PROTECTORS; iwhich++)
if (which == NOSE || !protector[player][iwhich].alive || protector[player][iwhich].x != xwrap(worm[player].x + protector[player][which].relx) || protector[player][iwhich].y != ywrap(worm[player].y + protector[player][which].rely))
{ effect(FXPROTECTORBORN);
done = TRUE;
protector[player][which].alive = TRUE;
protector[player][which].visible = FALSE;
protector[player][which].last = EMPTY;
if (which == NOSE)
worm[player].position = -1;
}
} while (!done);
}
break;
case MISSILE:
createmissile(player);
break;
case LIFE:
worm[player].lives += (rand() % 5) + 2; // 2-6 lives
stat(player, LIFE);
break;
case MULTIPLIER:
if (worm[player].multi < MULTILIMIT)
worm[player].multi *= 2;
break;
case BIAS:
worm[player].bias += MODEADD + (rand() % MODERAND);
stat(player, BIAS);
break;
case ICE:
worm[player].ice += ICEADD + (rand() % ICERAND);
ice = player;
break;
case GROWER:
effect(FXGETGROWER);
// grow silver
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == SILVER)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPTAIL)
field[xx][yy] = TEMPSILVER;
// grow gold
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == GOLD)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (valid(xx, yy))
if (field[xx][yy] == EMPTY || field[xx][yy] == TEMPTAIL || field[xx][yy] == TEMPSILVER)
field[xx][yy] = TEMPGOLD;
// update
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
switch (field[x][y])
{
case TEMPGOLD:
change(x, y, GOLD);
break;
case TEMPSILVER:
change(x, y, SILVER);
break;
case TEMPTAIL:
change(x, y, FIRSTTAIL + player);
break;
default:
break;
}
break;
case TREASURE:
treasurer = player;
if (level)
{ secondsperlevel = 0;
leveltype = rand() % 2;
if (leveltype == 0 || worm[player].bias)
{ say("Bonus Level: Treasury!", worm[treasurer].colour);
leveltype = TREASURE;
} else
{ say("Bonus Level: Drips!", worm[treasurer].colour);
leveltype = DRIP;
} }
secondsperlevel += TREASUREADD + (rand() % TREASURERAND);
if (level)
{ stat(player, BONUS);
reallevel = level;
level = 0;
newlevel(player);
}
break;
case AFFIXER:
worm[player].affixer = TRUE;
break;
case SWITCHER:
if (players >= 2)
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] >= FIRSTTAIL && field[x][y] <= LASTTAIL && field[x][y] != FIRSTTAIL + player)
change(x, y, FIRSTTAIL + player);
elif (worm[player].bias && field[x][y] >= FIRSTFIRE && field[x][y] <= LASTFIRE && field[x][y] != FIRSTFIRE + player)
change(x, y, FIRSTFIRE + player);
break;
case HEALER:
if (worm[player].lives < 100)
worm[player].lives = 100;
else worm[player].lives = LIVESLIMIT;
stat(player, LIFE);
break;
case UMBRELLA:
level += (rand() % 2) + 1;
if (level >= levels)
level = level - 1;
for (i = 0; i <= LETTERS; i++)
letters[player][i] = TRUE;
break;
case CLOCK:
if (secondsleft)
secondsperlevel += (rand() % CLOCKRAND) + CLOCKADD;
break;
case SLOWER:
for (which = 0; which <= CREATURES; which++)
if (creature[which].alive && creature[which].species != MISSILE)
creature[which].speed = (SBYTE) atleast(creature[which].speed * 2, VERYFAST);
break;
case PULSE:
explosion( xwrap(worm[player].x - worm[player].deltax),
ywrap(worm[player].y - worm[player].deltay),
worm[player].deltax,
worm[player].deltay
);
break;
case REMNANTS:
worm[player].remnants += (rand() % 5) + 2; // 2-6 remnants
break;
case LIGHTNING:
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
otherfield[x][y] = EMPTY;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
if (field[x][y] == FIRSTTAIL + player)
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if
( valid(xx, yy)
&& (field[xx][yy] < FIRSTLETTER || field[xx][yy] > LASTLETTER)
&& field[xx][yy] != METAL
&& field[xx][yy] != STONE
&& field[xx][yy] != WOOD
&& field[xx][yy] != SKULL
&& (field[xx][yy] != FIRSTTAIL + player)
)
{ otherfield[xx][yy] = TEMPTAIL; // lightning actually
draw(xx, yy, LIGHTNING);
}
for (x = 0; x <= FIELDX; x++)
{ for (y = 0; y <= FIELDY; y++)
{ if (otherfield[x][y] == TEMPTAIL)
{ switch(field[x][y])
{
case ORB:
which = whichcreature(x, y, ORB, 255);
if (creature[which].mode == ARMOUR)
draw(x, y, ORBARMOUR);
else
{ creature[which].alive = FALSE;
wormscore(player, creature[which].score);
change(x, y, BONUS);
}
break;
case GOAT:
creature[whichcreature(x, y, GOAT, 255)].alive = FALSE;
wormscore(player, KILLGOAT);
change(x, y, BONUS);
break;
case MISSILE:
i = whichcreature(x, y, MISSILE, 255);
if (player != creature[i].type)
{ creature[i].alive = FALSE;
change(x, y, EMPTY);
} else draw(x, y, FIRSTMISSILE + player);
break;
case PENGUIN:
wormscore(player, KILLPENGUIN);
// note no break here
case FRAGMENT:
creature[whichcreature(x, y, field[x][y], 255)].alive = FALSE;
change(x, y, EMPTY);
break;
default:
if (field[x][y] >= FIRSTDRIP && field[x][y] <= LASTDRIP)
{ creature[whichcreature(x, y, DRIP, 255)].alive = FALSE;
wormscore(player, DRIPBONUS);
}
if (field[x][y] >= FIRSTHEAD && field[x][y] <= LASTHEAD)
{ if (player != field[x][y] - FIRSTHEAD && worm[field[x][y] - FIRSTHEAD].mode != ARMOUR)
{ worm[field[x][y] - FIRSTHEAD].alive = FALSE;
worm[field[x][y] - FIRSTHEAD].cause = LIGHTNING;
worm[field[x][y] - FIRSTHEAD].victor = player;
change(x, y, EMPTY);
} }
else // eg. tail
change(x, y, EMPTY);
break;
} } } }
break;
default:
break;
} }
else
{ switch (thissy)
{
case EMPTY:
score += EMPTYPOINT;
break;
case SILVER:
score += SILVERPOINT;
break;
case GOLD:
score += GOLDPOINT;
break;
case ORB:
iwhich = whichcreature(x, y, ORB, 255);
if (worm[player].mode == ARMOUR || animal == PROTECTOR)
{ if (animal == HEAD)
effect(FXUSEARMOUR);
else effect(FXUSEPROTECTOR);
effect(FXORBDEATH);
wormscore(player, creature[iwhich].score);
creature[iwhich].alive = FALSE;
if (worm[player].bias)
{ worm[player].lives += ORBBLOOD;
stat(player, LIFE);
} }
else
{ if (creature[iwhich].mode == ARMOUR)
{ effect(FXUSEARMOUR);
orbscore(iwhich, KILLWORM);
} else creature[iwhich].alive = FALSE;
worm[player].cause = ORB;
worm[player].victor = -1;
worm[player].alive = FALSE;
}
break;
case FRAGMENT:
which = whichcreature(x, y, FRAGMENT, 255);
if (animal == HEAD)
{ if (worm[player].mode != ARMOUR)
{ worm[player].cause = FRAGMENT;
worm[player].victor = -1;
worm[player].alive = FALSE;
creature[which].alive = FALSE;
} else
{ effect(FXUSEARMOUR);
reflect(which);
} }
else
{ effect(FXUSEPROTECTOR);
reflect(which);
}
break;
case SKULL:
effect(FXGETSKULL);
score += SKULLPOINT;
for (which = 0; which <= 3; which++)
{ if (worm[which].alive == FALSE && worm[which].x == worm[player].x && worm[which].y == worm[player].y)
iwhich = which;
}
worm[player].bias += worm[iwhich].bias;
if (worm[player].bias > 0)
{ stat(player, BIAS);
worm[iwhich].bias = 0;
stat(iwhich, BIAS);
}
worm[player].multi *= worm[iwhich].multi;
if (worm[player].multi > 1)
{ if (worm[player].multi > MULTILIMIT)
worm[player].multi = MULTILIMIT;
}
worm[player].power += worm[iwhich].power;
if (worm[player].power > 1)
{ if (worm[player].power > POWERLIMIT)
worm[player].power = POWERLIMIT;
stat(player, POWER);
worm[iwhich].power = 0;
stat(iwhich, POWER);
}
worm[player].ammo += worm[iwhich].ammo;
if (worm[player].ammo > 0)
{ stat(player, AMMO);
worm[iwhich].ammo = 0;
stat(iwhich, AMMO);
}
worm[player].armour += worm[iwhich].armour;
if (worm[player].armour > 0)
{ stat(player, ARMOUR);
worm[iwhich].armour = 0;
stat(iwhich, ARMOUR);
}
worm[player].tongue += worm[iwhich].tongue;
if (worm[player].tongue > 0)
{ stat(player, TONGUE);
worm[iwhich].tongue = 0;
stat(iwhich, TONGUE);
}
if (worm[player].armour > 0 || worm[player].tongue > 0)
{ if (worm[player].armour >= worm[player].tongue)
worm[player].mode = ARMOUR;
else worm[player].mode = TONGUE;
}
if (worm[iwhich].nitro)
{ worm[player].nitro = TRUE;
stat(player, NITRO);
worm[iwhich].nitro = FALSE;
worm[iwhich].speed = NORMAL;
stat(iwhich, NITRO);
}
for (which = 0; which <= LETTERS; which++)
if (letters[iwhich][which])
{ drawletter(iwhich, FIRSTLETTER + which, BLACK);
if (!letters[player][which])
{ letters[player][which] = TRUE;
drawletter(player, FIRSTLETTER + which, NORMAL);
} }
break;
default:
break;
} }
if (animal == HEAD)
// it is important that HEAD is done before PROTECTORs
{ field[worm[player].x][worm[player].y] = FIRSTHEAD + player;
if (worm[player].alive)
{ switch (worm[player].mode)
{
case NULL:
draw(worm[player].x, worm[player].y, eachworm[player][0][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
break;
case TONGUE:
if (worm[player].tongue < 10 && (r / SLOW) % 2 == 0)
draw(worm[player].x, worm[player].y, WHITENED);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
break;
case ARMOUR:
if (worm[player].armour < 10 && (r / SLOW) % 2 == 0)
draw(worm[player].x, worm[player].y, WHITENED);
else draw(worm[player].x, worm[player].y, eachworm[player][1][worm[player].deltax + 1 + (worm[player].deltay + 1) * 3]);
break;
default:
break;
} }
else draw(worm[player].x, worm[player].y, SKULL);
} else // assumes animal == PROTECTOR
if (protector[player][thisprot].alive && protector[player][thisprot].visible)
change(x, y, FIRSTPROTECTOR + player);
while (++thisprot <= PROTECTORS)
if (protector[player][thisprot].alive && valid(protector[player][thisprot].x, protector[player][thisprot].y))
break;
if (thisprot > PROTECTORS)
animal = NULL;
else animal = PROTECTOR;
}
wormscore(player, score);
}
void wormscore(SBYTE player, LONG score)
{ worm[player].score += score * worm[player].multi * players;
stat(player, BONUS);
}
SWORD wsign(SWORD value)
{ if (value < 0)
return (-1);
elif (value > 0)
return (1);
else
return (0);
}
SBYTE xwrap(SBYTE x)
{ if (x < 0)
x += FIELDX + 1;
elif (x > FIELDX)
x -= FIELDX + 1;
return(x);
}
SBYTE ywrap(SBYTE y)
{ if (y < 0)
y += FIELDY + 1;
elif (y > FIELDY)
y -= FIELDY + 1;
return(y);
}
void ramming(SBYTE player)
{ SBYTE i;
worm[player].x = xwrap(worm[player].x - worm[player].deltax);
worm[player].y = ywrap(worm[player].y - worm[player].deltay);
for (i = 0; i <= PROTECTORS; i++)
{ // no point checking whether the protectors are alive or dead
protector[player][i].x -= worm[player].deltax;
protector[player][i].y -= worm[player].deltay;
} }
SWORD atleast(SWORD value, SWORD minimum)
{ if (value < minimum)
return(minimum);
else return(value);
}
void orbscore(SBYTE which, ULONG score)
{ creature[which].score += score * creature[which].multi;
}
void __inline change(SBYTE x, SBYTE y, UBYTE image)
{ field[x][y] = image;
draw(x, y, image);
}
void createmissile(UBYTE player)
{ UBYTE i, j = 0;
for (i = 0; i <= CREATURES; i++)
if (creature[i].alive && creature[i].species == MISSILE && creature[i].type == player)
if (++j >= 2)
return;
for (i = 0; i <= CREATURES; i++)
if (!creature[i].alive)
{ effect(FXMISSILEACTIVE);
creature[i].alive = TRUE;
creature[i].x = worm[player].x;
creature[i].y = worm[player].y;
creature[i].species = MISSILE;
creature[i].type = player;
creature[i].last = EMPTY;
creature[i].visible = FALSE;
if (level)
creature[i].speed = (SBYTE) atleast(MISSILESPEED - level, 1);
else creature[i].speed = BONUSMISSILESPEED;
break;
} }
/* WormWars FSET 5.1 format for fieldset contents and high
score table (Amiga and IBM-PC), as follows:
header
TEXT[] "FSET 5.1" (NULL-terminated)
SBYTE levels;
high score table
for (slot = 0; slot <= HISCORES; slot++)
{ SBYTE hiscore[slot].player,
hiscore[slot].level;
SLONG hiscore[slot].score;
TEXT[] hiscore[slot].name (NULL-terminated)
TEXT[] hiscore[slot].time (NULL-terminated)
TEXT[] hiscore[slot].date (NULL-terminated)
}
level data
for (level = 0; level <= levels; level++)
{ SBYTE startx[level],
starty[level];
ABOOL teleport[level][0].alive;
SBYTE teleport[level][0].x,
teleport[level][0].y;
ABOOL teleport[level][1].alive;
SBYTE teleport[level][1].x,
teleport[level][1].y;
for (x = 0; x <= FIELDX; x++)
for (y = 0; y <= FIELDY; y++)
SBYTE board[level][x][y];
}
version string
TEXT[] "$VER: Worm Wars 5.1 (dd.mm.yy) $" (NULL-terminated) */
SBYTE onlyworm(ABOOL alive)
{ UBYTE i, theworm, worms = 0;
for (i = 0; i <= 3; i++)
if (worm[i].control != NONE && ((!alive) || worm[i].lives))
{ theworm = i;
worms++;
}
if (worms == 1)
return (SBYTE) theworm;
else return -1;
}
// Must have blank line at EOF.