/// the central, big mass struct (struct _Mass_) #ifndef __MASS__ #define __MASS__ #include #include "math2d.h" /* only used for Uint8 etc... */ #include "SDL.h" /* We should be able to sum up the height values of all pixels of a mass * using an int (without overflow). HEIGHT_MAX is not a "hard" limit, but * the average height should be significant lower. */ /* This must be architecture-independant for network games to work */ /* it's equal to INT_MAX on a 32 bit machine */ #define HEIGHT_ABSOLOUTE_MAX (2147483647) #define HEIGHT_MAX (HEIGHT_ABSOLOUTE_MAX / 256) #if HEIGHT_MAX > INT_MAX #error int too small on this architecture #endif /* HEIGHT_NORM ist the height difference that will be shadowed as a * 45 degree slope (this means it is equal to the distance between two * neighbour pixels). */ #define HEIGHT_NORM (HEIGHT_MAX / 64) #define MAX_SHAPEPOINTS 16 typedef struct _Mass_ Mass; typedef struct _MassInterface_ MassInterface; /* Used in the mass below. See PreFlattening_ShapePoints() in mass.c for details */ typedef struct { Point pos; int radius; // 0 = not used, 5 = 5 pixels radius int strength; // 0 = not used, <0 = distract, >0 = attract the mass } ShapePoint; struct _MassInterface_ { /* the target mass, or NULL for the (last) interface to all empty points */ Mass * m; /* Rounds alive (relative to the parent masses's age (FIXME: did I mean with this increasing with parent speed?)) */ int age; /* number of pixels currently touching each other */ int contact, oldContact; /* 0: stay; 128 half-join; 256 join equal; -256 shrink equal */ int borderReaction; unsigned eatme : 1; }; typedef struct _DummyDisplayData_ DummyDisplayData; struct _DummyDisplayData_ { void (*freeFunc)(DummyDisplayData *); }; // This represents one "physical" mass on the screen. // A script will parse this in the build process: // Comments starting with % are members that will be saved/loaded/reset struct _Mass_ { // //%% Flattening Algorithm // // Order does matter for speed; OPTIMIZE: systematically search better order //% h_int % default=0.0 % height to be added to the mass as a whole int bufferMass; // the smallest rectangle that encloses the mass on the screen Rect maxRect; //% int % min=0,default=4 % parameter for move backward int weakSHR; //% int % min=0,default=3 % parameter for no movement int normalSHR; //% int % min=0,default=2 % parameter for move forward int strongSHR; // controls the strength [LEFT,UP...], calculated from speed + direction int flatteningSHR[4]; //% h_int % default=1.0 % minimal border height int minBorderHeight; //% h_int % default=5.0 % maximal border height int maxBorderHeight; // //%% Basics // // how many pixels? must always be accurate int numPixels; // field.c: pointer to where the mass is cached (avoid #include "cellitems.h") void * massCell; // sum of all height values; not accurate, auto-updated int volume; // not accurate, auto-updated int maxHeight; //% int % min=0,default=256 % how often the mass is processed per (engine) frame int speed; // time += speed int time256; // how the mass interacts with the neighbour masses (never NULL in game) MassInterface * interfaces; // was the mass changed? unsigned mustRedraw : 1; // set by RemoveMassFromField() while the mass has still references unsigned isDead : 1; // number of references (pointers) to this mass, 0 means mass destroyed int refcount; // mass class name this mass is/was originally copied from char * class; // pointer to frontend-specific data (OGL: OGLdisplayData) DummyDisplayData * displayData; //% int % min=-1,max=255,default=-1 % the direction (mass orientation), 128 means 180 degree, -1 random (choosen at creation) int heading; //% int % default=0 % m->speed is how often the mass is calculated, this is for movement int velocity; //% int % min=0,default=1 % number of pixels to look for neighbour mass (for interfaces) ; 0 means don't (faster but neighbours are all threated as empty space) int lookDistance; //% int % min=0,default=0 % how many rounds the mass will grow; masses are usually boren with one pixel, which will not last long int growRounds; //% int % min=0,default=0 % whenever the mass has fewer pixel than this count, grow int growIfSmallerThan; //% boolean % default=0 % grow much much faster (not linear) unsigned superGrow : 1; //% h_int % default=0.0 % shrink that much per round int shrink; //% int % default=0 % automatically increases by one with each step int age; //% int % default=0 % generic energy counter int energy; //% int % default=0 % generic activity counter int pulse; //% int % default=-64 % add that much energy each cycle (negative value means add one every -value cycles) int energyAdd; //% int % default=256 % don't add energy if it there is already more than this limit int maxEnergy; //% int % min=-256,max=255,default=0 % add this value to the heading each round (or faster, see slowHeadingAdd below); -100 means a random value between -50 and +50 int headingAdd; //% boolean % default=1 % consider headingAdd only every 32 rounds instead every round unsigned slowHeadingAdd : 1; //% boolean % default=0 % move according to m->heading and m->velocity, or just flatten? unsigned flatteningMovement : 1; //% boolean % default=1 % do flattening using the strong exponential algorithm unsigned expFlattening : 1; //% boolean % default=0 % do flattening using the convolution matrix algo unsigned convolveFlattening : 1; //% boolean % default=0 % do flattening using the old (fast but weak) algo unsigned oldFlattening : 1; // //%% Players // //% int % default=-1,min=-1 % player id controling this mass, 0 first player, -1 no player; FIXME: resets both velocity and heading each cycle int player; //% int % min=0,default=128 % if m->player is in range, attract his viewpoint int attractPlayer; //% enum % direct=PC_DIRECT,swarm=PC_SWARM,ignore=PC_IGNORE % react on player input (if player is set); direct (move as commanded), swarm (circle around viewpoint and follow commands freely), ignore (no direct control) enum { PC_IGNORE = 0, PC_DIRECT, PC_SWARM } playerControl; //% boolean % default=0 % the mass gets speed=256 the first time a player approximates unsigned wakeOnPlayer : 1; //% boolean % default=0 % the mass gets speed=256 the first time any mass with speed >50 approximates unsigned wakeOnTouch : 1; //% boolean % default=0 % can a player simply eat this mass? unsigned playerFood : 1; //% boolean % default=0 % throw a lightning on player button2 (using 50 energy) unsigned playerLightning : 1; //% boolean % default=0 % this mass counts as a player body: special action may be taken when a player owns no more body mass unsigned playerBody : 1; //% string % default= % if set to a non-empty string, this mass will catch the first player without a playerBody and transform into the given mass type (which probably has playerBody set, thus giving the player a new body); as long as all players have a playerBody mass, nothing happens char * transformOnBodylessPlayer; //% boolean % default=1 % when a transformation happens, all attributes are overwritten with the new ones, including player id; this is usually not wanted (except if the player should loose the mass) unsigned transformationSavesPlayerid : 1; //% boolean % default=0 % TODO attract players without a playerBody using attractPlayer unsigned attractBodylessPlayers : 1; //% boolean % default=0 % if a player dies (lost his last playerBody), all other masses from this player will have player set to -1; this option prevents this unsigned ignorePlayerDeath : 1; //% string % default= % transform into the given class on button1 (does not check playerControl) char * transformOnButton1; //% string % default= % transform into the given class on button2 (does not check playerControl) char * transformOnButton2; //% int % min=0,default=0 % when >0 and a mass with player != -1 is nearby, change the own player number; the value given is the number of cycles a player has to stay around exclusively; FIXME: how is "nearby" defined? int changePlayer; int lastPlayerSeen; int lastPlayerSeenSince; // //%% Visual Appearance // //% enum % shadow=DT_NORMAL,flat=DT_FLAT % how to display the mass (shadow or flat) enum { DT_NORMAL = 0, DT_FLAT } displayType; //% int[] % default=255 255 255 % basic mass color (RGB) Color baseColor; //% int[] % default=0 255 0 % second mass color, for some effects (RGB) Color secondColor; //% h_int % default=0.0 % draw points lower than this with secondColor int borderColorHeight; //% boolean % default=0 % draw a gray elipse that indicates the heading (must have a certein height before it becomes visible); SLOW! unsigned drawElipsoid : 1; //% boolean % default=0 % draw a spark effect from time to time (a bright point gleaming for a short time over the hightest mass pixel) unsigned drawSparks : 1; //% boolean % default=0 % mass generates fog clouds unsigned foggy : 1; // //%% Children and Transformations // //% string % default=ChildClassNotSet % what children masses this mass generates, self (lowercase) means clone, if more than one class is given (seperated with one space), one is picked randomly char * childClass; //% int % default=0 % if not zero, create a child mass all that many cycles; negative values, eg -80 mean a child is generated each turn with probability 1:80 int childTime; //% int % default=-1,min=-1 % number of children this mass can generate, -1 unlimited int childCount; //% int % default=50 % in which distance a child is born (0 mass center, -200 means 200 pixels in the oposite of the choosen direction) int childDistance; //% int % default=0 % if the parent heading is used to place the child, this value is added int childDirection; //% boolean % default=0 % if set, childDirection := -childDirection after the child is created int childDirectionAlternating; //% boolean % default=0 % if set, children are born by cutting a part of the parent mass; in this case childDistance is interpreted relative (0 center, 256 or -256 mass border) unsigned childInside : 1; //% boolean % default=0 % if set, childDistance is taken as a maximum and the actual distance is choosen randomly unsigned childRandomDistance : 1; //% boolean % default=0 % if set, children are born in a random direction, ignoring the parent's heading unsigned childRandomDirection : 1; //% boolean % default=1 % if true, the children's heading is set to point away from its parent mass after creation unsigned childSetHeading : 1; //% boolean % default=0 % if set, the parent does not get the initial child mass back (most relevant if childInside is set) unsigned childTakesMassFromParent : 1; //% boolean % default=0 % if true the parent gets the body of the child and vice versa (consequently the child will not have to grow but maybe the parent) unsigned childAndParentSwapped : 1; //% string % default=TransformClassNotSet % what mass to transform into if a transformation is tiggered, see childClass for allowed values; note that a transformation will reset most values (including age but not heading) char * transformClass; //% enum % never=TR_NEVER,age=TR_AGE,random=TR_RANDOM,energy=TR_ENERGY,size=TR_SIZE,touch=TR_TOUCH % when to transform into another class (never, age, random, energy, size, touch); see also transformArgument enum { TR_NEVER = 0, TR_AGE, TR_RANDOM, TR_ENERGY, TR_SIZE, TR_TOUCH } transformAt; //% int % default=0 % compare value for transformAt: age, random (probability 1:value), size (in pixels) or energy; ignored for: never, touch int transformArgument; // //%% Particles // //% boolean % default=0 % particle receptor 1; a particle can set this flag when it hits the mass unsigned particleReceptor1 : 1; //% boolean % default=0 % particle receptor 2; as above. unsigned particleReceptor2 : 1; //% boolean % default=0 % particle receptor 3; as above. unsigned particleReceptor3 : 1; //% int % default=0, min=0 % how many turns (age) pass until the particle receptors are reset; 0 means disable; FIXME: used only and always together with transformOnParticle_minAge? int particleResetTime; //% int % default=0, min=-3, max=3 % Transform if the given particle receptor number (1, 2, 3) is set. Negative numbers (-1, -2, -3) will transform if particle is not present. int transformOnParticle; //% string % default= % transform into the given class char * transformOnParticle_transformClass; //% int % default=5, min=0 % transform only if older than the given age int transformOnParticle_minAge; //% int % default=0, min=0 % transform only if younger than the given age (0 means no limit) int transformOnParticle_maxAge; // TODO: growOnParticle, removing it, and similar things - changing color, whatever //%% Algorithms // //% int % min=0,default=0 % how vivid the mass is; causes random movements in it int vivid; //% int % min=0,default=0 % same as vivid, but other algo (uses shapepoints, which is a bit slow, don't overuse) int vivid2; //% int % default=0 % if nonzero, the mass will shrink from random pixels taken away (no good idea while growing); FIXME: this has more complicated threshold constants, document them. int sickness; //% int % default=0 % how much mass it steals from its surrounding FIXME: does it work? how? int destructive; //% int % default=0 % decrease speed by that value each turn (value>0) or by one with probability 1:-value (value<0) int decreaseSpeed; //% int % min=0,default=1 % never decrease speed lower than this; speed 0 is possible but might cause stale references int minSpeed; //% boolean % default=0 % enables wall code (join other walls, adapt to speed of other walls) unsigned isWall : 1; //% boolean % default=0 % FIXME: what does this? unsigned isProjectil : 1; //% boolean % default=0 % FIXME: what does this? unsigned isMazeWall : 1; //% boolean % default=0 % FIXME: what does this? unsigned isCoinMass : 1; //% boolean % default=0 % make the mass throw lightnings on proximity (needs 80 energy) unsigned isLightningMass : 1; //% boolean % default=0 % lightning does not affect this mass; a small smoke cloud might be generated instead unsigned lightningResistance : 1; //% boolean % default=0 % FIXME: planned? unsigned isNetworkMass : 1; //% boolean % default=0 % randomly change the speed a bit (use this to avoid identical masses to grow up exactly simultanous; this property disables itself after some time) unsigned speedPerturb : 1; //% boolean % default=0 % experimental new faster border point algorithm unsigned experimentalBorderPoints : 1; // these points form the shape of the mass (if used) FIXME: remove? ShapePoint shapePoints[MAX_SHAPEPOINTS]; //% h_int % default=0.0 % grow mass around a line (through mass center, direction of heading) that much for each pixel int baseLineGrowth; //% h_int % default=0.0 % extra-height to add to startpoint int baseLineGrowth_start; //% h_int % default=0.0 % extra-height to add to endpoint int baseLineGrowth_end; //% int % default=256,min=1 % distance between the pixels of the line (256 = 1 pixel) int baseLineGrowth_step; //% int % default=0 % if set, the first step is not always in the mass center, but still on the line (probably useful with big step size) unsigned baseLineGrowth_randomStepStart : 1; //% int % default=0 % if set, the height is not added but re-set (if baseLineGrowth > 0) unsigned baseLineGrowth_setHeight : 1; //% boolean % default=0 % enable cell functions (rules similar to game of life, get input from nearest neighbours) unsigned cell : 1; //% int % default=1, min=-1, max=1 % cell activation status (0 or 1, or -1 for random; resets baseColor to hardcoded values for now) signed cell_activation : 2; //% boolean % default=0 % enable simple swarm algorithm (resets baseColor to hardcoded values for now) FIXME: needs tuning, not of much use yet unsigned swarm : 1; //% int % default=0,min=0 % used to call other swarm members (energy is spent to increase if something is happening, then decrease quickly back to zero) int swarm_pulse; // //%% Debugging // //% boolean % default=0 % does whatever I want it to (don't use) unsigned debugTest : 1; // mass color in debug mode, set automatically Color debugColor; // used for mass classes, to find out which mass type wastes most time int timeEngine, timeDisplay; }; /* * for main() */ extern void ContinueMasses(void); // create a new mass, not touching the field (if copy is set, use this as parent) extern Mass * AllocMass(Mass * copy); // free the memory (assuming everything else was cleaned up) extern void FreeMass(Mass * m); // place an already created mass (with no pixels yet), returns 0 if it fails extern int PlaceMassNearby(Mass * m, int x, int y, int radius); // for editing levels extern void PlaceMassAt(int x, int y, char * class); // removes all pixels first, then moves mass to the zombies extern void RemoveMassFromField(Mass * m); /* possible reactions to a border point conflict with another mass */ typedef enum { BP_ERROR = -1, BP_SHRINK = -2, BP_EXPAND = -3, BP_STAY = 0, BP_HALFJOIN = 128, /* any value between is OK */ BP_JOIN = 256 /* biger values are OK */ } BP_Reaction; /* get interface between 2 masses, create one if none */ extern MassInterface * CreateInterface(Mass * m1, Mass * m2); /* get interface between 2 masses, NULL if none */ extern MassInterface * GetInterface(Mass * m1, Mass * m2); /* delete it */ extern void DeleteInterface(MassInterface * mi); /* looks for a (p->owner == NULL) pixel at the border of the mass and adds it */ extern Point AddBorderPixel(Mass * m); /* sum up the height of all owned pixels, and add buffermass */ extern int CountMassVolume(Mass * m); extern void DebugCheckMass(Mass * m); extern void DebugPrintMass(Mass * m); #endif