Added support for scripted maps (Map.c) and documentation.

scancodes-fix
Sven Eberhardt 2013-03-19 00:35:00 +01:00
parent b0a0d2a83c
commit 57e63a5275
24 changed files with 2115 additions and 20 deletions

View File

@ -313,6 +313,9 @@ set(OC_CLONK_SOURCES
src/landscape/C4MapCreatorS2.cpp
src/landscape/C4MapCreatorS2.h
src/landscape/C4Map.h
src/landscape/C4MapScript.cpp
src/landscape/C4MapScriptAlgo.cpp
src/landscape/C4MapScript.h
src/landscape/C4MassMover.cpp
src/landscape/C4MassMover.h
src/landscape/C4Material.cpp

View File

@ -25,6 +25,10 @@
<dt id="Landscapetxt"><img height="16" src="../../images/icon_text.png" width="16"/><emlink href="scenario/MapCreatorS2.html">Landscape.txt</emlink></dt>
<dd>
<text>Advanced scenario designers can use this component to define highly complex, fully random generated dynamic landscapes. This does require certain mathematical skill and some patience, however.</text>
</dd>
<dt id="Scriptc"><img height="16" src="../../images/icon_text.png" width="16"/><emlink href="script/MapScript.html">Map.c</emlink></dt>
<dd>
<text>Script for dynamic generation of map. See <emlink href="script/MapScript.html">map script documentation</emlink>.</text>
</dd>
</dl>

View File

@ -0,0 +1,474 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE doc
SYSTEM '../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../clonk.xsl"?>
<doc>
<title>Map script</title>
<h>Map script</h>
<part>
<text>Map scripts provide a powerful method to generate diverse, dynamic maps with just a few lines of simple script code. Map scripts can be used to generate new maps, as well as modify existing maps defined as static Map.bmp or dynamic Landscape.txt.</text>
<h>Introduction</h>
<text>A map script is simply a script file called Map.c placed in a scenario. On scenario initialization, the engine calls the local function called InitializeMap in this script. If the function returns true, the map will be used by the engine. If false is returned, the map is discarded and the regular fallback map is created.</text>
<text>Here an example of a simple map script:</text>
<code>/* A simple map */
#include Library_Map
func InitializeMap(proplist map)
{
// Create a big map
Resize(150,150);
// Draw earth
DrawRegularGround();
// Draw some resources into the ground
DrawWaterVeins(3, [0,map.Hgt/3,map.Wdt,map.Hgt*2/3]);
DrawCoal(6);
DrawSulphur(4);
DrawRock(15);
DrawOre(4);
DrawGold(2*GetStartupPlayerCount()); // amount of gold depends on player count!
// Make sure liquids don't border tunnel or sky sideways
FixLiquidBorders();
// Done. Use this map.
return true;
}</code>
<text>This draws a pretty boring standard map with basic resources. It makes use of some high-level helper functions such as DrawRegularGround or DrawCoal. which are included in the definition named Library_Map in Objects.ocd/Libraries.ocd. All map scripts should include this definition.</text>
<h>Layers</h>
<part>
<text>All map draw functions work on layers, which are simply 8 bit image surfaces. The map itself is a layer, but additional layers can be created as temporary buffers using the CreateLayer or Duplicate script functions. Additional layers are bound to the map they were created from and destroyed alongside with it when map drawing is complete.</text>
<text>In C4Script, map layers are represented as prop lists. They have the implicit properties Wdt and Hgt, which contain the width and height of the surface respectively. To resize a map or layer, use the Resize() function. Do not modify Wdt or Hgt directly.</text>
<text>For example, the following code:</text>
<code>var layer = CreateLayer("Earth");
layer->DrawCoal();
Blit(layer, [0,0,layer.Wdt/2,layer.Hgt]);</code>
<text>would create a secondary layer filled with earth. It would then draw coal onto the layer and finally copy only the left half of its contents to the main map.</text>
</part>
<h>Algorithms</h>
<part>
<text>Algorithms are the core concept of dynamic map creation to point drawing operations to specific subregions of the map only. An algorithm is a function that maps a position (int x, int y) to either a pixel color (int) or a mask value (bool).</text>
<text>Algorithms are defined as prop list with the Algo property set to one of the MAPALGO_* constants and additional algorithm parameters set as properties. They can then be passed to one of the drawing functions (Draw or Blit), which will evaluate the algorithm at all positions and draw pixel values accordingly.</text>
<text>For example, the following code would draw rectangles of earth in a checkerboard pattern:</text>
<code>Draw("Earth", {Algo=MAPALGO_RndChecker, Wdt=5, Hgt=5});</code>
<text>In addition to pattern-generating algorithms, there are also modifier algorithms that take other algorithms as parameters. For example, the Turbulence algorithm jumbles all pixels of the underlying algorithm around to create a noisy pattern:</text>
<code>var checkerboard = {Algo=MAPALGO_RndChecker, Wdt=10, Hgt=10};
var jumbled_checkerboard = {Algo=MAPALGO_Turbulence, Amplitude=10, Scale=10};
Draw("Earth", jumbled_checkerboard);</code>
<text>Modifier algorithms can also be applied to layer contents directly. For example, to flip the contents of the current map, one could write:</text>
<code>// Backup contents of current map
var copy_layer = Duplicate();
// Redraw flipped horizontally
Blit({Algo=MAPALGO_Scale, OffX=Wdt/2, X=-100, Op=copy_layer});
</code>
<text>Note: If you are using the target layer in a drawing command, always draw from a copy. Otherwise, the result is undefined.</text>
<h>SCRIPTALGO_Layer</h><part>
<text>Returns the pixel value at the x,y position of the given layer. Instead of passing a SCRIPTALGO_Layer prop list, layers can also be passed directly as algorithms.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Layer</col>
<col></col>
<col>The layer from which pixel values are taken.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_RndChecker</h><part>
<text>Returns values from a checkerboard pattern of rectangles that are filled with ones or zeros.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Seed</col>
<col>Random(65536)</col>
<col>If nonzero, the checkerboard pattern is generated from a fixed seed.</col>
</row><row>
<col>Ratio</col>
<col>50</col>
<col>Percentage of checkerboard fields that are one.</col>
</row><row>
<col>Wdt</col>
<col>10</col>
<col>Width of rectangles.</col>
</row><row>
<col>Hgt</col>
<col>10</col>
<col>Height of rectangles</col>
</row><row>
<col>FixedOffset</col>
<col>false</col>
<col>If true, the pattern always starts at position (0,0). Otherwise, it is offset by a random phase.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Rect</h><part>
<text>Returns one if the position is in a given rectangle and zero otherwise.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>X</col>
<col>0</col>
<col>Left side of rectangle (pixel is included).</col>
</row><row>
<col>Y</col>
<col>0</col>
<col>Top side of rectangle (pixel is included).</col>
</row><row>
<col>Wdt</col>
<col>0</col>
<col>Width of rectangle.</col>
</row><row>
<col>Hgt</col>
<col>0</col>
<col>Height of rectangle.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Ellipsis</h><part>
<text>Returns one if the position is in a given ellipsis and zero otherwise.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>X</col>
<col>0</col>
<col>Horizontal center of ellipsis.</col>
</row><row>
<col>Y</col>
<col>0</col>
<col>Vertical center of ellipsis.</col>
</row><row>
<col>Wdt</col>
<col>10</col>
<col>Horizontal radius of ellipsis.</col>
</row><row>
<col>Hgt</col>
<col>10</col>
<col>Vertical radius of ellipsis</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Polygon</h><part>
<text>Returns one if the position is in a given polygon or on its border and zero otherwise.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>X</col>
<col></col>
<col>Array of x coordinates of polygon points.</col>
</row><row>
<col>Y</col>
<col></col>
<col>Array of y coordinates of polygon points.</col>
</row><row>
<col>Wdt</col>
<col>1</col>
<col>Width of border lines of polygon.</col>
</row><row>
<col>Empty</col>
<col>false</col>
<col>If true, the polygon is not filled and only the border is drawn.</col>
</row><row>
<col>Open</col>
<col>false</col>
<col>If true, the last segment of the polygon is not drawn. Useful to draw lines. Only valid if Empty is true.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_And</h><part>
<text>Returns zero if any of the operands is zero. Otherwise, returns the value of the last operand. If there are zero operands, always returns zero.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Array of algorithms that are tested.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Or</h><part>
<text>Returns the first operand that is nonzero. If all operands are zero, returns zero. If there are zero operands, always returns zero.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Array of algorithms that are tested.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Not</h><part>
<text>Returns one if the operand is zero. Returns zero otherwise.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Algorithms that is negated.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Xor</h><part>
<text>If exactly one of the two operands is nonzero, returns that operand. Otherwise, returns zero.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Array of two algorithms that are tested.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Offset</h><part>
<text>Moves its operand by an offset.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Algorithms that is being manipulated.</col>
</row><row>
<col>OffX</col>
<col>0</col>
<col>Horizontal offset to the right.</col>
</row><row>
<col>OffY</col>
<col></col>
<col>Vertical offset downwards.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Scale</h><part>
<text>Scales its operand by a point.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Algorithms that is being manipulated.</col>
</row><row>
<col>X</col>
<col>100</col>
<col>Horizontal scaling in percent. Values smaller than zero flip the operand horizontally.</col>
</row><row>
<col>Y</col>
<col>100</col>
<col>Vertical scaling in percent. Values smaller than zero flip the operand vertically.</col>
</row><row>
<col>OffX</col>
<col>0</col>
<col>X position of fixed point that remains in position.</col>
</row><row>
<col>OffY</col>
<col>0</col>
<col>Y position of fixed point that remains in position.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Rotate</h><part>
<text>Rotates its operand around a point.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Algorithms that is being manipulated.</col>
</row><row>
<col>R</col>
<col>0</col>
<col>Rotation angle in degrees (0 to 360). Positive values rotate counter-clockwise.</col>
</row><row>
<col>OffX</col>
<col>0</col>
<col>X position of fixed point that remains in position.</col>
</row><row>
<col>OffY</col>
<col>0</col>
<col>Y position of fixed point that remains in position.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Turbulence</h><part>
<text>Jumbles its operand around by moving points by a randomized offset.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Seed</col>
<col>Random(65536)</col>
<col>If nonzero, the offset map is generated from a fixed seed.</col>
</row><row>
<col>Amplitude</col>
<col>10</col>
<col>Maximum range by which pixels may be moved in a single step. Movement in any direction is half of the amplitude. Can be an single integer for equal movement in both dimensions or an array of two integers for separate amplitudes for horizontal and vertical movement.</col>
</row><row>
<col>Scale</col>
<col>10</col>
<col>Distance of points for which the amplitude is randomized. A large scale relative to the amplitude creates more broadly scaled, regular turbulence, while a small scale can cause borders to look more jumpey. Can be an single integer for equal scale in both dimensions or an array of two integers for separate scales horizontally and vertically.</col>
</row><row>
<col>Iterations</col>
<col>2</col>
<col>Number of times each point is pushed around. The amplitude of the n'th successive push is reduced by 1/n.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Border</h><part>
<text>Returns true for positions that lie on an inner or outer border of an operand. An inner border is defined as a position where the operand is nonzero and a position where it is zero lies within inner border width range. An outer border is defined as a position where the operand is zero and a position where it is nonzero lies within outer border width range. Note that borders are only searched in four directions (left, right, upwards, downwards) and not diagonally. This means that for a square, outer borders to not catch the corners.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Algorithm of which the border is to be determined.</col>
</row><row>
<col>Wdt</col>
<col>1</col>
<col>Border width in all directions. Positive integer for inner border; negative integer for outer border. Can also be an array of two integers of opposing signs for inner and outer borders.</col>
</row><row>
<col>Left</col>
<col></col>
<col>Border width to the left side. Definition like Wdt. Falls back to Wdt if not specified.</col>
</row><row>
<col>Top</col>
<col></col>
<col>Border width upwards. Definition like Wdt. Falls back to Wdt if not specified.</col>
</row><row>
<col>Right</col>
<col></col>
<col>Border width to the right side. Definition like Wdt. Falls back to Wdt if not specified.</col>
</row><row>
<col>Bottom</col>
<col></col>
<col>Border width downwards. Definition like Wdt. Falls back to Wdt if not specified.</col>
</row>
</table></text></part>
<h>SCRIPTALGO_Filter</h><part>
<text>Return only pixel values of the operand that match the mask specification. Returns zero for other pixels.</text>
<text><table><rowh><col>Parameter</col><col>Default</col><col>Meaning</col></rowh>
<row>
<col>Op</col>
<col></col>
<col>Operand algorithm that is being filtered.</col>
</row><row>
<col>Filter</col>
<col></col>
<col>Mask specification (see section "Material-texture masks" below)</col>
</row>
</table></text></part>
</part>
<h>Script function parameters</h>
<part>
Map drawing functions follow a common syntax for passing certain structures:
<h>Rectangles (array rect)</h>
<text>All rectangles are given in the format <code>[left, top, width, height]</code>, where the left and top pixel rows are included and left+width and top+height pixel rows are excluded. Unless otherwise specified, rect can always be nil, in which case the area defaults to the whole map or layer (<code>[0,0,this.Wdt,this.Hgt]</code>).</text>
<h>Material-texture definitions (string mattex)</h>
<text>When a material is specified for the drawing functions, the following definitions are valid:</text>
<text>
<table>
<rowh>
<col>String</col>
<col>Example</col>
<col>Meaning</col>
</rowh>
<row>
<col>Material</col>
<col>Earth</col>
<col>Draws the given material in its default texture as underground (tunnel background) material.</col>
</row>
<row>
<col>Material-Texture</col>
<col>Earth-earth_topSoil</col>
<col>Draws the given material with the given texture as underground material.</col>
</row>
<row>
<col>^Material</col>
<col>^Water</col>
<col>Draws the given material with its default texture as overground (sky background) material.</col>
</row>
<row>
<col>^Material-Texture</col>
<col>^Earth-earth_rough</col>
<col>Draws the given material with the given texture as overground material.</col>
</row>
<row>
<col>Sky</col>
<col>Sky</col>
<col>Draws a sky material. Within the map generator, explicit sky is drawn as IFT (0x80), which is converted to index zero on map drawing. That way, sky can be blitted to other layers without being transparent.</col>
</row>
<row>
<col>Transparent</col>
<col>Transparent</col>
<col>Draws with index 0.</col>
</row>
</table>
</text>
<h>Material-texture masks (string mask_spec)</h>
<text>When a material is specified as a masking function, the following definitions are valid:</text>
<text>
<table>
<rowh>
<col>String</col>
<col>Example</col>
<col>Meaning</col>
</rowh>
<row>
<col>Material</col>
<col>Earth</col>
<col>True for given material with any texture and any (sky or tunnel) background.</col>
</row>
<row>
<col>Material-Texture</col>
<col>Earth-earth_topSoil</col>
<col>True for the given material with the given texture and any (sky or tunnel) background.</col>
</row>
<row>
<col>Sky</col>
<col>Sky</col>
<col>True for explicit sky material (0x80) only. Not true for transaprent (0) pixels.</col>
</row>
<row>
<col>Transparent</col>
<col>Transparent</col>
<col>True for transparent pixels (index 0) only.</col>
</row>
<row>
<col>Background</col>
<col>Background</col>
<col>True for all background materials (e.g. Tunnel, BrickBack and Sky).</col>
</row>
<row>
<col>Liquid</col>
<col>Liquid</col>
<col>True for all liquids (e.g. Water, Acid, Lava and DuroLava).</col>
</row>
<row>
<col>Solid</col>
<col>Solid</col>
<col>True for solid materials (e.g. Earth, Rock, Brick, etc.).</col>
</row>
<row>
<col>*</col>
<col>*</col>
<col>True for all materials.</col>
</row>
<row>
<col>^Definition</col>
<col>^Rock-rock_cracked</col>
<col>True for the definition if overground (sky background) only.</col>
</row>
<row>
<col>&amp;Definition</col>
<col>&amp;Liquid</col>
<col>True for the definition if underground (tunnel background) only. The example would match all underground liquids.</col>
</row>
<row>
<col>~Definition</col>
<col>~^*</col>
<col>Inverts the definition, i.e. true only if the definition would originally be false. The example would match all underground materials.</col>
</row>
</table>
</text>
</part>
<h>Script functions</h>
<part>
<text>All drawing functions are defined in the MapLayer static prop list. Because the Map.c script file is also evaluated in this context with the current map as this pointer, all drawing functions can be called directly by name in that script (e.g.: Resize(150,150)). In other script contexts or if the function is to be executed on a layer instead of on the main map, the base object must be given explicitely (e.g.: map->Resize(150,150), where map is the parameter passed to InitializeMap).</text>
<text>Because layers derive from the MapLayer prop list, all script functions defined in the Map.c and included script files are also available on any layer.</text>
<h>Internal engine functions</h>
<part>
<text><code>bool Draw(string mattex, proplist mask_algo, array rect);</code></text>
<text>Draws the material given by mattex on all pixels within rect if the algorithm given by mask_algo returns a value other than zero. Returns true on success.</text>
<text><code>bool Blit(proplist mask_algo, array rect);</code></text>
<text>Same as draw, but draws the result of evaluation of mask_algo directly instead of a material given by mattex. Because mask_algo can also be a layer, this function can be used to copy layer contents onto other layers or the map. If mask_algo evaluates to zero, nothing is drawn and the original pixel value is kept.</text>
<text><code>proplist CreateLayer(string mattex_fill, int width, int height);</code></text>
<text>Creates a new layer of size width,height. If no size is given, the layer is created in the same size as the calling context layer or map. The new layer is filled with the pixel color given by mattex_fill, or with zeroes if mattex_fill is nil. Returns the newly created layer.</text>
<text><code>bool Resize(int new_width, int new_height);</code></text>
<text>Recreates the calling layer or map surface in the given size. All contents are deleted and the layer is filled with zeroes. Use functions Duplicate and Blit to backup and restore any old layer contents if you want to extent the map without losing its contents. Returns true on success.</text>
<text><code>proplist Duplicate(any mask_spec, array rect);</code></text>
<text>Creates a new layer with the same size and surface contents as this layer. If a rect is given, the new layer is smaller and contains only the portion included in rect. If mask_spec is given, only pixels passing the mask are set and all other pixels in the new layer are zero.</text>
<text><code>int GetPixel(int x, int y);</code></text>
<text>Gets the pixel color at the given position in this layer. If x,y is outside the layer, zero is returned.</text>
<text><code>bool SetPixel(int x, int y, int new_color);</code></text>
<text>Sets the pixel at position x,y in this layer to new_color. Returns true on success.</text>
<text><code>int GetPixelCount(any mask_spec, array rect);</code></text>
<text>Returns number of pixels on this layer or map within rect that fulfill mask_spec.</text>
<text><code>bool FindPosition(proplist out_pos, mask_spec, array rect, int max_tries);</code></text>
<text>Tries to find a position on this layer for which the pixel color matches mask_spec. If a position is found, true is returned and the position is set as X and Y parameters in the out_pos prop list. If no position is found after max_tries, the function will walk through all pixels of the layer starting from a random starting position to find a point. If still no position is found, false is returned and out_pos is not changed. max_tries defaults to 500.</text>
</part>
</part>
</part>
<author>Sven2</author><date>2013-03</date>
</doc>

View File

@ -0,0 +1,4 @@
[DefCore]
id=Library_Map
Version=5,2,0,1
Category=C4D_StaticBack

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

View File

@ -0,0 +1,125 @@
/*--
Library_Map
Authors: Sven2
Utility functions for map drawing in InitializeMap().
--*/
// Returns p if p is an int. If p is an array of two ints, returns a
// random value between both (assuming p[1]>p[0])
func EvalIntPar(p) { if (GetType(p) == C4V_Array) return p[0]+Random(p[1]-p[0]); return p; }
// Returns p if p is an int. If p is an array of two ints, returns the
// medium value of both
func EvalIntParM(p) { if (GetType(p) == C4V_Array) return (p[0]+p[1])/2; return p; }
func DrawVaried(string mat, proplist algo, array rect, vary_mats)
{
// Draw material and put rndchecker variations of other materials on it
if (GetType(vary_mats) != C4V_Array || (GetLength(vary_mats)==3 && GetType(vary_mats[1])==C4V_Int)) vary_mats = [vary_mats];
var ratio = 100/(GetLength(vary_mats)+1);
var main_shape = this->CreateLayer();
main_shape->Draw(mat, algo, rect);
this->Blit(main_shape, rect);
for (var vary_def in vary_mats)
{
var sx=3,sy=3;
if (GetType(vary_def) == C4V_Array)
{
sx=vary_def[1]; sy = vary_def[2];
vary_def = vary_def[0];
}
var rand_algo = {Algo=MAPALGO_RndChecker, Ratio=ratio, Wdt=sx, Hgt=sy};
var turb_algo = {Algo=MAPALGO_Turbulence, Amplitude=12, Scale=8, Op=rand_algo};
this->Draw(vary_def, {Algo=MAPALGO_And, Op=[main_shape, turb_algo]});
}
return main_shape;
}
func DrawSpots(string mat, int num, sizex, sizey, array rect, inmat, vary_mats)
{
// Default parameters
if (!inmat) inmat="Earth";
if (!sizex) sizex = [5, 20];
if (!sizey) sizey = [5, 7];
if (!num) num=Max(this->GetPixelCount(inmat, rect) / (EvalIntParM(sizex)*EvalIntParM(sizey)*10), 1);
// Draw num spots
var spot = {Algo=MAPALGO_Ellipsis};
while (num--)
{
if (!this->FindPosition(spot, inmat, rect)) break;
var mask = this->Duplicate(inmat);
spot.Wdt = EvalIntPar(sizex)/2;
spot.Hgt = EvalIntPar(sizey)/2;
var algo = {Algo=MAPALGO_And, Op=[mask, {Algo=MAPALGO_Turbulence, Amplitude=Max(spot.Wdt, spot.Hgt), Scale=Max(spot.Wdt, spot.Hgt), Op=spot}]};
if (vary_mats)
DrawVaried(mat, algo, rect, vary_mats);
else
this->Draw(mat, algo, rect);
}
return true;
}
func DrawCoal(int num, array rect) { return DrawSpots("Coal", num, [20,60], [4,8], rect, nil); }
func DrawSulphur(int num, array rect) { return DrawSpots("Sulphur", num, [20,60], [8,12], rect, nil); }
func DrawOre(int num, array rect) { return DrawSpots("Ore", num, [8,12], [14,20], rect, nil); }
func DrawGold(int num, array rect) { return DrawSpots("Gold", num, [10,14], [10,14], rect, nil); }
func DrawRock(int num, array rect) { return DrawSpots("Rock-rock", num, [20,80], [6,8], rect, nil, [["Rock-rock_cracked", 3,10], ["Granite", 6,2]]); }
func DrawWaterVeins(int num, array rect)
{
while (num--) DrawLiquidVein("Water", 3, 5, rect);
return true;
}
func DrawLiquidVein(string mat, int wdt, int spread, array rect, inmat)
{
if (!rect) rect = [0,0,this.Wdt, this.Hgt];
if (!inmat) inmat="Earth";
var mask = this->Duplicate(inmat);
var x1 = rect[0]-rect[2]+Random(rect[2]*3);
var y1 = rect[1]-rect[3]+Random(rect[3]*3);
var x2 = rect[0]+Random(rect[2]);
var y2 = rect[1]+Random(rect[3]);
var water = {Algo=MAPALGO_Polygon, X=[x1,x2], Y=[y1,y2], Wdt=wdt};
var water_rand = {Algo=MAPALGO_Turbulence, Amplitude=30, Scale=30, Op=water};
var water_rand2 = {Algo=MAPALGO_Turbulence, Amplitude=spread*2, Scale=2, Op=water_rand};
this->Draw(mat, {Algo=MAPALGO_And, Op=[mask, water_rand2]});
return true;
}
func DrawRegularGround(array rect)
{
// Draw regular (boring) ground level
if (!rect) rect = [0,0,this.Wdt, this.Hgt];
var ground_rect = {Algo=MAPALGO_Rect, X=-100, Y=rect[1]+rect[3]/4, Wdt=rect[0]+rect[2]+200, Hgt=rect[3]+100};
var ground_algo = {Algo=MAPALGO_Turbulence, Amplitude=30, Scale=30, Op=ground_rect};
var earth_shape = DrawVaried("Earth-earth", ground_algo, rect, [["Earth-earth_dry", 3,10], ["Earth-earth_rough", 6,2]]);
var top1 = {Algo=MAPALGO_Border, Top=[-10, 3], Op=earth_shape};
var top2 = {Algo=MAPALGO_Border, Top=[-10, 1], Op=earth_shape};
this->Draw("Earth-earth_midSoil", {Algo=MAPALGO_And, Op=[earth_shape, {Algo=MAPALGO_Turbulence, Amplitude=4, Scale=10, Op=top1}]});
this->Draw("Earth-earth_topSoil", {Algo=MAPALGO_And, Op=[earth_shape, {Algo=MAPALGO_Turbulence, Amplitude=4, Scale=10, Op=top2}]});
return true;
}
func FixLiquidBorders(border_material, lava_border_material)
{
// Makes sure liquids bordering other liquids as well as liquids
// bordering background materials are surrounded by fix_material
// Default border materials
if (!border_material) border_material = "Earth-earth_topSoil";
if (!lava_border_material) lava_border_material = "Rock";
// Find liquid-to-background borders
var liquids = this->Duplicate("Liquid");
var liquid_borders = {Algo=MAPALGO_Border, Op=liquids, Wdt=-1, Top=0 };
var background = this->Duplicate("Background");
background->Draw("Sky", {Algo=MAPALGO_Not, Op=this});
this->Draw(border_material, {Algo=MAPALGO_And, Op=[liquid_borders, background]});
// Put lava on top of other liquids
var lava_borders = {Algo=MAPALGO_Border, Op=this->Duplicate(["Lava", "DuroLava"])};
this->Draw(lava_border_material, {Algo=MAPALGO_And, Op=[lava_borders, liquids]});
// Put acid on top of water
var acid_borders = {Algo=MAPALGO_Border, Op=this->Duplicate("Acid")};
this->Draw(border_material, {Algo=MAPALGO_And, Op=[acid_borders, liquids]});
return true;
}

View File

@ -63,6 +63,7 @@
#define C4CFN_DiffLandscape "DiffLandscape.bmp"
#define C4CFN_Sky "Sky"
#define C4CFN_Script "Script.c|Script%s.c|C4Script%s.c"
#define C4CFN_MapScript "Map.c"
#define C4CFN_ScriptStringTbl "StringTbl.txt|StringTbl%s.txt"
#define C4CFN_Info "Info.txt"
#define C4CFN_Author "Author.txt"
@ -170,7 +171,7 @@
// TODO: proper sorting of scaled def graphics (once we know what order we might load them in...)
#define C4FLS_Scenario "Loader*.bmp|Loader*.png|Loader*.jpeg|Loader*.jpg|Fonts.txt|Scenario.txt|Title*.txt|Info.txt|Desc*.rtf|Icon.png|Icon.bmp|Game.txt|StringTbl*.txt|Teams.txt|Parameters.txt|Info.txt|Sect*.ocg|Music.ocg|*.mid|*.wav|Desc*.rtf|Title.bmp|Title.png|*.ocd|Material.ocg|MatMap.txt|Landscape.bmp|Landscape.png|" C4CFN_DiffLandscape "|Sky.bmp|Sky.png|Sky.jpeg|Sky.jpg|PXS.ocb|MassMover.ocb|CtrlRec.ocb|Strings.txt|Objects.txt|RoundResults.txt|Author.txt|Version.txt|Names.txt|*.ocd|Script.c|Script*.c|System.ocg"
#define C4FLS_Scenario "Loader*.bmp|Loader*.png|Loader*.jpeg|Loader*.jpg|Fonts.txt|Scenario.txt|Title*.txt|Info.txt|Desc*.rtf|Icon.png|Icon.bmp|Game.txt|StringTbl*.txt|Teams.txt|Parameters.txt|Info.txt|Sect*.ocg|Music.ocg|*.mid|*.wav|Desc*.rtf|Title.bmp|Title.png|*.ocd|Material.ocg|MatMap.txt|Landscape.bmp|Landscape.png|" C4CFN_DiffLandscape "|Sky.bmp|Sky.png|Sky.jpeg|Sky.jpg|PXS.ocb|MassMover.ocb|CtrlRec.ocb|Strings.txt|Objects.txt|RoundResults.txt|Author.txt|Version.txt|Names.txt|*.ocd|Script.c|Script*.c|Map.c|System.ocg"
#define C4FLS_Section "Scenario.txt|Game.txt|Landscape.bmp|Landscape.png|Sky.bmp|Sky.png|Sky.jpeg|Sky.jpg|PXS.ocb|MassMover.ocb|CtrlRec.ocb|Strings.txt|Objects.txt"
#define C4FLS_SectionLandscape "Scenario.txt|Landscape.bmp|Landscape.png|PXS.ocb|MassMover.ocb"
#define C4FLS_SectionObjects "Strings.txt|Objects.txt"

View File

@ -82,6 +82,7 @@
#include <C4Version.h>
#include <C4AulExec.h>
#include <StdFile.h>
#include <C4MapScript.h>
class C4GameSec1Timer : public C4ApplicationSec1Timer
{
@ -601,6 +602,7 @@ void C4Game::Clear()
MessageInput.Clear();
Info.Clear();
Title.Clear();
::MapScript.Clear();
::GameScript.Clear();
Names.Clear();
GameText.Clear();
@ -2010,6 +2012,8 @@ bool C4Game::InitGame(C4Group &hGroup, bool fLoadSection, bool fLoadSky, C4Value
// Scenario scripts (and local system.ocg)
GameScript.Load(ScenarioFile, C4CFN_Script, Config.General.LanguageEx, &ScenarioLangStringTable);
// Map scripts
MapScript.Load(ScenarioFile, C4CFN_MapScript, Config.General.LanguageEx, &ScenarioLangStringTable);
// After defs to get overloading priority
if (!LoadAdditionalSystemGroup(ScenarioFile))
{ LogFatal(LoadResStr("IDS_PRC_FAIL")); return false; }
@ -2195,6 +2199,7 @@ bool C4Game::InitScriptEngine()
InitCoreFunctionMap(&ScriptEngine);
InitObjectFunctionMap(&ScriptEngine);
InitGameFunctionMap(&ScriptEngine);
::MapScript.InitFunctionMap(&ScriptEngine);
// system functions: check if system group is open
if (!Application.OpenSystemGroup())

View File

@ -60,6 +60,7 @@ bool C4ValueToMatrix(const C4ValueArray& array, StdMeshMatrix* matrix);
#define PSF_InitializeScriptPlayer "~InitializeScriptPlayer" // iPlayer, idTeam
#define PSF_PreInitializePlayer "~PreInitializePlayer" // iPlayer
#define PSF_InitializePlayerControl "~InitializePlayerControl" // iPlayer, szControlSet, hasKeyboard, hasMouse, hasGamepad
#define PSF_InitializeMap "~InitializeMap" // map
#define PSF_RemovePlayer "~RemovePlayer" // iPlayer
#define PSF_RelaunchPlayer "~RelaunchPlayer" // iPlayer, iKilledBy
#define PSF_Time1 "~Time1"

View File

@ -45,6 +45,11 @@ public:
// set pix in local copy...
if (Bits) Bits[iY*Pitch+iX]=byCol;
}
void _SetPix(int iX, int iY, BYTE byCol)
{
// set pix in local copy without bounds or surface checks
Bits[iY*Pitch+iX]=byCol;
}
BYTE GetPix(int iX, int iY) // get pixel
{
if (iX<0 || iY<0 || iX>=Wdt || iY>=Hgt) return 0;

View File

@ -59,6 +59,7 @@
#include <C4MaterialList.h>
#include <C4FindObject.h>
#include <C4GameObjects.h>
#include <C4MapScript.h>
C4Landscape::C4Landscape()
{
@ -1165,17 +1166,20 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
if ((sfcMap=GroupReadSurface8(hGroup, C4CFN_Map)))
if (!fLandscapeModeSet) Mode=C4LSC_Static;
// dynamic map from file
// dynamic map from Landscape.txt
if (!sfcMap)
if ((sfcMap=CreateMapS2(hGroup)))
if (!fLandscapeModeSet) Mode=C4LSC_Dynamic;
// script may create or edit map
if (MapScript.InitializeMap(hGroup, &sfcMap))
if (!fLandscapeModeSet) Mode=C4LSC_Dynamic;
// Dynamic map by scenario
if (!sfcMap && !fOverloadCurrent)
if ((sfcMap=CreateMap()))
if (!fLandscapeModeSet) Mode=C4LSC_Dynamic;
// No map failure
if (!sfcMap)
{

View File

@ -709,12 +709,7 @@ void C4MCMap::Default()
// inherited
C4MCOverlay::Default();
// size by landscape def
Wdt=MapCreator->Landscape->MapWdt.Evaluate();
Hgt=MapCreator->Landscape->MapHgt.Evaluate();
// map player extend
MapCreator->PlayerCount = Max(MapCreator->PlayerCount, 1);
if (MapCreator->Landscape->MapPlayerExtend)
Wdt = Min(Wdt * Min(MapCreator->PlayerCount, (int) C4S_MaxMapPlayerExtend), (int) MapCreator->Landscape->MapWdt.Max);
MapCreator->Landscape->GetMapSize(Wdt, Hgt, MapCreator->PlayerCount);
}
bool C4MCMap::RenderTo(BYTE *pToBuf, int32_t iPitch)

View File

@ -0,0 +1,586 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000 Matthes Bender
* Copyright (c) 2013 Sven Eberhardt
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* Handles scripted map creation */
#include <C4Include.h>
#include <C4MapScript.h>
#include <C4AulDefFunc.h>
#include <C4Landscape.h>
#include <C4Texture.h>
#include <C4Random.h>
C4MapScriptAlgo *FnParAlgo(C4PropList *algo_par);
static const char *DrawFn_Transparent_Name = "Transparent";
static const char *DrawFn_Sky_Name = "Sky";
static const char *DrawFn_Background_Name = "Background";
static const char *DrawFn_Liquid_Name = "Liquid";
static const char *DrawFn_Solid_Name = "Solid";
int32_t FnParTexCol(C4String *mattex, int32_t default_col = -1)
{
// Return index of material-texture definition for a single color
// Defaults to underground (tunnel background) color. Prefix material with ^ to get overground (sky background) color.
if (!mattex || !mattex->GetCStr()) return default_col;
if (mattex->GetData() == DrawFn_Transparent_Name) return 0;
if (mattex->GetData() == DrawFn_Sky_Name) return IFT;
const char *cmattex = mattex->GetCStr();
int32_t ift = IFT;
if (*cmattex == '^') { ift=0; ++cmattex; }
int32_t col = ::TextureMap.GetIndexMatTex(cmattex);
return col ? col|ift : default_col;
}
void C4MapScriptMatTexMask::UnmaskSpec(C4String *spec)
{
// Mask all indices of material-texture definitions
// Possible definitions:
// Material-Texture - Given material-texture combination (both sky and tunnel background)
// Material - All defined default textures of given material
// * - All materials
// Sky - Index IFT
// Transparent - Index 0
// Background - All tunnel materials plus sky
// Liquid - All liquid materials
// Solid - All solid materials
// Possible modifiers:
// ^Material - Given material only with sky background
// &Material - Given material only with tunnel background
// ~Material - Inverse of given definition; i.e. everything except Material
if (!spec || !spec->GetCStr()) return;
const char *cspec = spec->GetCStr();
bool invert=false, bgsky=false, bgtunnel=false, prefix_done=false;
while (*cspec)
{
switch (*cspec)
{
case '~': invert=!invert; break;
case '^': bgsky=true; break;
case '&': bgtunnel=true; break;
default: prefix_done=true; break;
}
if (prefix_done) break;
++cspec;
}
std::vector<bool> mat_mask(IFT, false);
if (SEqual(cspec, DrawFn_Transparent_Name))
{
// "Transparent" is zero index. Force to non-IFT
mat_mask[0] = true;
bgsky = true; bgtunnel = false;
}
else if (SEqual(cspec, DrawFn_Sky_Name))
{
// Sky material: Force to IFT
mat_mask[0] = true;
bgsky = false; bgtunnel = true;
}
else if (SEqual(cspec, DrawFn_Background_Name))
{
// All background materials
for (int32_t i=0; i<IFT; ++i) if (!DensitySemiSolid(Landscape.GetPixDensity(i))) mat_mask[i] = true;
// Background includes sky
mat_mask[0] = true;
}
else if (SEqual(cspec, DrawFn_Liquid_Name))
{
// All liquid materials
for (int32_t i=0; i<IFT; ++i) if (DensityLiquid(Landscape.GetPixDensity(i))) mat_mask[i] = true;
}
else if (SEqual(cspec, DrawFn_Solid_Name))
{
// All solid materials
for (int32_t i=0; i<IFT; ++i) if (DensitySolid(Landscape.GetPixDensity(i))) mat_mask[i] = true;
}
else if (SEqual(cspec, "*"))
{
// All materials
for (int32_t i=0; i<IFT; ++i) mat_mask[i] = true;
}
else
{
// Specified material
if (SCharCount('-', cspec))
{
// Material+Texture
int32_t col = ::TextureMap.GetIndexMatTex(cspec, NULL, false);
if (col) mat_mask[col] = true;
}
else
{
// Only material: Mask all textures of this material
int32_t mat = ::MaterialMap.Get(cspec);
if (mat!=MNone)
{
const char *tex_name;
int32_t col;
for (int32_t itex=0; (tex_name=::TextureMap.GetTexture(itex)); itex++)
if (col = ::TextureMap.GetIndex(cspec,tex_name,false))
mat_mask[col] = true;
}
}
}
// 'OR' spec onto this->mask. Apply bgsky, bgtunnel and invert.
for (int32_t i=0; i<IFT; ++i)
if ((mat_mask[i] && (bgsky || !bgtunnel)) != invert)
mask[i] = true;
for (int32_t i=0; i<IFT; ++i)
if ((mat_mask[i] && (!bgsky || bgtunnel)) != invert)
mask[i+IFT] = true;
}
void C4MapScriptMatTexMask::Init(const C4Value &spec)
{
// Mask may be initialized by a simple string or by an array of strings, of which the effects are OR'ed
const C4ValueArray *arr = spec.getArray();
if (arr)
{
// Init by array
for (int32_t i=0; i<arr->GetSize(); ++i)
{
C4String *smask = arr->GetItem(i).getStr();
if (!smask) throw new C4AulExecError(FormatString("MatTexMask expected string as %dth element in array.", (int)i).getData());
UnmaskSpec(smask);
}
}
else
{
// Init by string
C4String *smask = spec.getStr();
if (smask)
UnmaskSpec(smask);
else
{
if (spec) throw new C4AulExecError("MatTexMask expected string or array of strings.");
// nil defaults to everything except index zero unmasked
mask = std::vector<bool>(256, true);
mask[0] = false;
}
}
}
bool FnParRect(C4MapScriptLayer *layer, C4ValueArray *rect, C4Rect *rc_bounds)
{
// Convert rect parameter passed to script function to C4Rect structure
// and makes sure it is completely contained in bounding rectangle of layer
// rect==NULL defaults to bounding rectangle of layer
*rc_bounds = layer->GetBounds();
if (!rect) return true; // nil is OK for rect parameter. Defaults to bounds rectangle
if (rect->GetSize() != 4) return false;
rc_bounds->Intersect(C4Rect(rect->GetItem(0).getInt(), rect->GetItem(1).getInt(), rect->GetItem(2).getInt(), rect->GetItem(3).getInt()));
return true;
}
static bool FnLayerDraw(C4PropList * _this, C4String *mattex, C4PropList *mask_algo, C4ValueArray *rect)
{
// Layer script function: Draw material mattex in shape of mask_algo in _this layer within bounds given by rect
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
int32_t icol = FnParTexCol(mattex);
if (!layer || icol<0) return false;
C4Rect rcBounds;
if (!FnParRect(layer, rect, &rcBounds)) return false;
std::auto_ptr<C4MapScriptAlgo> algo(FnParAlgo(mask_algo));
return layer->Fill(icol, rcBounds, algo.get());
}
static bool FnLayerBlit(C4PropList * _this, C4PropList *mask_algo, C4ValueArray *rect)
{
// Layer script function: Blit mask_algo onto surface of _this within bounds given by rect
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
if (!layer) return false;
C4Rect rcBounds;
if (!FnParRect(layer, rect, &rcBounds)) return false;
std::auto_ptr<C4MapScriptAlgo> algo(FnParAlgo(mask_algo));
if (!algo.get()) return false;
return layer->Blit(rcBounds, algo.get());
}
static C4PropList *FnCreateLayer(C4PropList * _this, C4String *mattex_fill, int32_t width, int32_t height)
{
// Layer script function: Create new layer filled by mattex_fill of size width,height as sub layer of _this map
// Size defaults to _this layer size
int32_t icol = FnParTexCol(mattex_fill, 0);
if (icol<0) throw new C4AulExecError(FormatString("CreateLayer: Invalid fill material.").getData());
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
if (!layer) return NULL;
if (!width && !height)
{
width = layer->GetWdt();
height = layer->GetHgt();
}
if (width<=0 || height<=0) throw new C4AulExecError(FormatString("CreateLayer: Invalid size (%d*%d).", (int)width, (int)height).getData());
C4MapScriptMap *map = layer->GetMap();
if (!map) return NULL;
layer = map->CreateLayer(width, height);
if (icol) layer->Fill(icol, layer->GetBounds(), NULL);
return layer;
}
static C4PropList *FnLayerDuplicate(C4PropList * _this, const C4Value &mask_spec, C4ValueArray *rect)
{
// Layer script function: Create a copy of _this layer within bounds. If mask_spec is specified, copy only materials selected by mask spec
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
if (!layer) return NULL;
C4MapScriptMap *map = layer->GetMap();
if (!map) return NULL;
C4MapScriptMatTexMask mat_mask(mask_spec);
C4Rect src_rect;
if (!FnParRect(layer, rect, &src_rect)) return NULL;
if (!src_rect.Wdt || !src_rect.Hgt) return NULL;
C4MapScriptLayer *new_layer = map->CreateLayer(src_rect.Wdt, src_rect.Hgt);
new_layer->Blit(layer, src_rect, mat_mask, 0,0);
return new_layer;
}
static int32_t FnLayerGetPixel(C4PropList * _this, int32_t x, int32_t y)
{
// Layer script function: Query pixel at position x,y from _this layer
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
if (!layer) return 0;
return layer->GetPix(x,y,0);
}
static bool FnLayerSetPixel(C4PropList * _this, int32_t x, int32_t y, int32_t to_value)
{
// Layer script function: Set pixel at position x,y to to_value in _this layer
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
if (!layer) return false;
if (!Inside(to_value, 0, 255)) throw new C4AulExecError(FormatString("MapLayer::SetPixel: TRying to set invalid pixel value %d.", (int)to_value).getData());
return layer->SetPix(x,y,to_value);
}
static int32_t FnLayerGetPixelCount(C4PropList * _this, const C4Value &mask_spec, C4ValueArray *rect)
{
// Layer script function: Count all pixels within rect that match mask_spec specification
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
if (!layer) return -1;
C4MapScriptMatTexMask mat_mask(mask_spec);
C4Rect check_rect;
if (!FnParRect(layer, rect, &check_rect)) return -1;
return layer->GetPixCount(check_rect, mask_spec);
}
static bool FnLayerResize(C4PropList * _this, int32_t new_wdt, int32_t new_hgt)
{
// Layer script function: Recreate layer in new size. Resulting layer is empty (color 0)
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
// safety
if (!layer || new_wdt<=0 || new_hgt<=0) return false;
// recreate surface in new size
layer->ClearSurface();
return layer->CreateSurface(new_wdt, new_hgt);
}
static bool FnLayerFindPosition(C4PropList * _this, C4PropList *out_pos, const C4Value &mask_spec, C4ValueArray *rect, int32_t max_tries)
{
// Layer script function: Find a position (x,y) that has a color matching mask_spec. Set resulting position as X,Y properties in out_pos prop list
C4MapScriptLayer *layer = _this->GetMapScriptLayer();
if (!layer) return NULL;
C4MapScriptMatTexMask mat_mask(mask_spec);
C4Rect search_rect;
if (!FnParRect(layer, rect, &search_rect)) return NULL;
int32_t x,y; bool result;
if (!max_tries) max_tries = 500;
if (result = layer->FindPos(search_rect, mat_mask, &x, &y, max_tries))
{
if (out_pos && !out_pos->IsFrozen())
{
out_pos->SetProperty(P_X, C4VInt(x));
out_pos->SetProperty(P_Y, C4VInt(y));
}
}
return result;
}
C4MapScriptLayer::C4MapScriptLayer(C4PropList *prototype, C4MapScriptMap *map) : C4PropListNumbered(prototype), surface(NULL), surface_owned(false), map(map)
{
// It seems like numbered PropLists need a number. I don't know why.
AcquireNumber();
}
bool C4MapScriptLayer::CreateSurface(int32_t wdt, int32_t hgt)
{
// Create new surface of given size. Surface is filled with color 0
ClearSurface();
if (wdt<=0 || hgt<=0) return false;
surface = new CSurface8;
surface_owned = true;
if (!surface->Create(wdt, hgt))
{
ClearSurface();
return false;
}
UpdateSurfaceSize();
return true;
}
void C4MapScriptLayer::ClearSurface()
{
// Delete surface if owned or just set to zero if unowned
if (surface_owned) delete surface;
surface=NULL; surface_owned=false;
// if there is no surface, width and height parameters are undefined. no need to update them.
}
void C4MapScriptLayer::UpdateSurfaceSize()
{
// Called when surface size changes: Update internal property values
if (surface)
{
SetProperty(P_Wdt, C4VInt(surface->Wdt));
SetProperty(P_Hgt, C4VInt(surface->Hgt));
}
}
void C4MapScriptLayer::ConvertSkyToTransparent()
{
// Convert all sky (color==IFT) pixels to transparent (color==0)
// Needed because C4Landscape map zoom assumes sky to be 0
if (!HasSurface()) return;
for (int32_t y=0; y<surface->Hgt; ++y)
for (int32_t x=0; x<surface->Wdt; ++x)
if (surface->_GetPix(x,y) == IFT)
surface->_SetPix(x,y, 0);
}
C4Rect C4MapScriptLayer::GetBounds() const
{
// Return bounding rectangle of surface. Surface always starts at 0,0.
return surface ? C4Rect(0,0,surface->Wdt,surface->Hgt) : C4Rect();
}
bool C4MapScriptLayer::Fill(int col, const C4Rect &rcBounds, const C4MapScriptAlgo *algo)
{
// safety
if (!HasSurface()) return false;
assert(rcBounds.x>=0 && rcBounds.y>=0 && rcBounds.x+rcBounds.Wdt<=surface->Wdt && rcBounds.y+rcBounds.Hgt<=surface->Hgt);
// set all non-masked pixels within bounds that fulfill algo
for (int32_t y=rcBounds.y; y<rcBounds.y+rcBounds.Hgt; ++y)
for (int32_t x=rcBounds.x; x<rcBounds.x+rcBounds.Wdt; ++x)
if (!algo || (*algo)(x,y))
surface->_SetPix(x,y,col);
return true;
}
bool C4MapScriptLayer::Blit(const C4Rect &rcBounds, const C4MapScriptAlgo *algo)
{
// safety
if (!HasSurface()) return false;
assert(rcBounds.x>=0 && rcBounds.y>=0 && rcBounds.x+rcBounds.Wdt<=surface->Wdt && rcBounds.y+rcBounds.Hgt<=surface->Hgt);
assert(algo);
// set all pixels within bounds by algo, if algo is not transparent
uint8_t col;
for (int32_t y=rcBounds.y; y<rcBounds.y+rcBounds.Hgt; ++y)
for (int32_t x=rcBounds.x; x<rcBounds.x+rcBounds.Wdt; ++x)
if (col=(*algo)(x,y))
surface->_SetPix(x,y,col);
return true;
}
bool C4MapScriptLayer::Blit(const C4MapScriptLayer *src, const C4Rect &src_rect, const C4MapScriptMatTexMask &col_mask, int32_t tx, int32_t ty)
{
// safety
assert(src);
if (!HasSurface() || !src->HasSurface()) return false;
// cannot assert this, because C4Rect::Contains(C4Rect &) has an off-by-one-error which I don't dare to fix right now
// TODO: Fix C4Rect::Contains and check if the sector code still works
// assert(src->GetBounds().Contains(src_rect));
// copy all pixels that aren't masked
uint8_t col;
for (int32_t y=src_rect.y; y<src_rect.y+src_rect.Hgt; ++y)
for (int32_t x=src_rect.x; x<src_rect.x+src_rect.Wdt; ++x)
if (col_mask(col=src->surface->_GetPix(x,y)))
surface->_SetPix(x-src_rect.x+tx,y-src_rect.y+ty,col);
return true;
}
int32_t C4MapScriptLayer::GetPixCount(const C4Rect &rcBounds, const C4MapScriptMatTexMask &col_mask)
{
// safety
if (!HasSurface()) return 0;
assert(rcBounds.x>=0 && rcBounds.y>=0 && rcBounds.x+rcBounds.Wdt<=surface->Wdt && rcBounds.y+rcBounds.Hgt<=surface->Hgt);
// count matching pixels in rect
int32_t count = 0;
for (int32_t y=rcBounds.y; y<rcBounds.y+rcBounds.Hgt; ++y)
for (int32_t x=rcBounds.x; x<rcBounds.x+rcBounds.Wdt; ++x)
count += col_mask(surface->_GetPix(x,y));
return count;
}
bool C4MapScriptLayer::FindPos(const C4Rect &search_rect, const C4MapScriptMatTexMask &col_mask, int32_t *out_x, int32_t *out_y, int32_t max_tries)
{
// safety
if (!HasSurface() || search_rect.Wdt<=0 || search_rect.Hgt<=0) return false;
// Search random positions
for (int32_t i=0; i<max_tries; ++i)
{
int32_t x=search_rect.x + Random(search_rect.Wdt);
int32_t y=search_rect.y + Random(search_rect.Hgt);
if (col_mask(surface->_GetPix(x,y))) { *out_x=x; *out_y=y; return true; }
}
// Nothing found yet: Start at a random position and search systemically
// (this guantuess to find a pixel if there is one, but favours border pixels)
int32_t sx=search_rect.x + Random(search_rect.Wdt);
int32_t sy=search_rect.y + Random(search_rect.Hgt);
for (int32_t x=sx; x<surface->Wdt; ++x)
if (col_mask(surface->_GetPix(x,sy))) { *out_x=x; *out_y=sy; return true; }
for (int32_t y=sy+1; y<surface->Hgt; ++y)
for (int32_t x=0; x<surface->Wdt; ++x)
if (col_mask(surface->_GetPix(x,y))) { *out_x=x; *out_y=y; return true; }
for (int32_t y=0; y<sy; ++y)
for (int32_t x=0; x<surface->Wdt; ++x)
if (col_mask(surface->_GetPix(x,y))) { *out_x=x; *out_y=y; return true; }
for (int32_t x=0; x<sx; ++x)
if (col_mask(surface->_GetPix(x,sy))) { *out_x=x; *out_y=sy; return true; }
// Nothing found
return false;
}
void C4MapScriptMap::Clear()
{
// Layers are owned by map. Free them.
for (std::list<C4MapScriptLayer *>::iterator i=layers.begin(); i!=layers.end(); ++i) delete *i;
layers.clear();
}
C4MapScriptLayer *C4MapScriptMap::CreateLayer(int32_t wdt, int32_t hgt)
{
// Create layer and register to map. Layer's created by a map are freed when the map is freed.
C4MapScriptLayer *new_layer = new C4MapScriptLayer(MapScript.GetLayerPrototype(), this);
layers.push_back(new_layer); // push before CreateSurface for exception safety
if (!new_layer->CreateSurface(wdt, hgt))
{
layers.remove(new_layer);
delete new_layer;
return NULL;
}
return new_layer;
}
C4MapScriptHost::C4MapScriptHost(): LayerPrototype(NULL), MapPrototype(NULL) { }
C4MapScriptHost::~C4MapScriptHost() { Clear(); }
void C4MapScriptHost::InitFunctionMap(C4AulScriptEngine *pEngine)
{
// Register script host. Add Map and MapLayer prototypes, related constants and engine functions
assert(pEngine && pEngine->GetPropList());
Clear();
LayerPrototype = C4PropList::NewStatic(NULL, NULL, ::Strings.RegString("MapLayer"));
MapPrototype = C4PropList::NewStatic(LayerPrototype, NULL, ::Strings.RegString("Map"));
LayerPrototype->SetName("MapLayer");
MapPrototype->SetName("Map");
::ScriptEngine.RegisterGlobalConstant("MapLayer", C4VPropList(LayerPrototype));
::ScriptEngine.RegisterGlobalConstant("Map", C4VPropList(MapPrototype));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Layer", C4VInt(MAPALGO_Layer));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_RndChecker", C4VInt(MAPALGO_RndChecker));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_And", C4VInt(MAPALGO_And));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Or", C4VInt(MAPALGO_Or));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Xor", C4VInt(MAPALGO_Xor));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Not", C4VInt(MAPALGO_Not));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Scale", C4VInt(MAPALGO_Scale));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Offset", C4VInt(MAPALGO_Offset));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Rect", C4VInt(MAPALGO_Rect));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Ellipsis", C4VInt(MAPALGO_Ellipsis));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Polygon", C4VInt(MAPALGO_Polygon));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Turbulence", C4VInt(MAPALGO_Turbulence));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Border", C4VInt(MAPALGO_Border));
::ScriptEngine.RegisterGlobalConstant("MAPALGO_Filter", C4VInt(MAPALGO_Filter));
Reg2List(pEngine);
AddEngineFunctions();
}
void C4MapScriptHost::AddEngineFunctions()
{
// adds all engine functions to the MapLayer context
::AddFunc(this, "Draw", FnLayerDraw);
::AddFunc(this, "Blit", FnLayerBlit);
::AddFunc(this, "CreateLayer", FnCreateLayer);
::AddFunc(this, "Duplicate", FnLayerDuplicate);
::AddFunc(this, "GetPixel", FnLayerGetPixel);
::AddFunc(this, "SetPixel", FnLayerSetPixel);
::AddFunc(this, "GetPixelCount", FnLayerGetPixelCount);
::AddFunc(this, "Resize", FnLayerResize);
::AddFunc(this, "FindPosition", FnLayerFindPosition);
}
bool C4MapScriptHost::Load(C4Group & g, const char * f, const char * l, C4LangStringTable * t)
{
assert(LayerPrototype && MapPrototype);
return C4ScriptHost::Load(g, f, l, t);
}
void C4MapScriptHost::Clear()
{
delete LayerPrototype; delete MapPrototype;
LayerPrototype = MapPrototype = NULL;
C4ScriptHost::Clear();
}
C4PropListStatic * C4MapScriptHost::GetPropList()
{
// Scripts are compiled in the MapLayer context so it's possible to use all map drawing functions directly without "map->" prefix
return LayerPrototype;
}
C4MapScriptMap *C4MapScriptHost::CreateMap()
{
return new C4MapScriptMap(MapPrototype);
}
bool C4MapScriptHost::InitializeMap(C4Group &group, CSurface8 **pmap_surface)
{
// Init scripted map by calling InitializeMap in the proper scripts. If *pmap_surface is given, it will pass the existing map to be modified by script.
assert(pmap_surface);
// Don't bother creating surfaces if the functions aren't defined
if (!LayerPrototype->GetFunc(PSF_InitializeMap))
{
C4PropList *scen_proplist = ::GameScript.ScenPropList._getPropList();
if (!scen_proplist || !scen_proplist->GetFunc(PSF_InitializeMap)) return false;
}
// Create proplist as script context
std::auto_ptr<C4MapScriptMap> map(CreateMap());
// Drawing on existing map or create new?
if (*pmap_surface)
{
// Existing map
map->SetSurface(*pmap_surface);
}
else
{
// No existing map. Create new.
int32_t map_wdt,map_hgt;
::Game.C4S.Landscape.GetMapSize(map_wdt, map_hgt, ::Game.StartupPlayerCount);
if (!map->CreateSurface(map_wdt, map_hgt)) return false;
}
C4AulParSet Pars(C4VPropList(map.get()));
C4Value result = map->Call(PSF_InitializeMap, &Pars);
if (!result) result = ::GameScript.Call(PSF_InitializeMap, &Pars);
// Map creation done.
if (result)
{
map->ConvertSkyToTransparent();
*pmap_surface = map->ReleaseSurface();
}
return !!result;
}
C4MapScriptHost MapScript;

View File

@ -0,0 +1,329 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000 Matthes Bender
* Copyright (c) 2013 Sven Eberhardt
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* Handles scripted map creation */
#ifndef INC_C4MapScript
#define INC_C4MapScript
#include <C4ComponentHost.h>
#include <C4Aul.h>
#include <C4ScriptHost.h>
#include <C4Rect.h>
#include <CSurface8.h>
#include <C4Landscape.h>
// mattex masks: Array of bools for each possible material-texture index
class C4MapScriptMatTexMask
{
std::vector<bool> mask; // vector of size 256: true means pixel color is let through; false means it is blocked
void UnmaskSpec(C4String *spec);
public:
C4MapScriptMatTexMask() : mask(256,false) { }
C4MapScriptMatTexMask(const C4Value &spec) : mask(256,false) { Init(spec); }
void Init(const C4Value &spec);
bool operator()(uint8_t mattex) const { return mask[mattex]; }
};
// algorithms may be either indicator functions (int,int)->bool that tell whether a map pixel should be
// set (to be used in C4MapScriptLayer::Fill) or functions (int,int)->int that tell which material should
// be set (to be used in Fill or C4MapScriptLayer::Blit).
class C4MapScriptAlgo
{
protected:
bool GetXYProps(const C4PropList *props, C4PropertyName k, int32_t *out_xy, bool zero_defaults);
public:
virtual uint8_t operator () (int32_t x, int32_t y) const = 0;
virtual ~C4MapScriptAlgo() {}
};
// List of possible algorithms. Also registered as script constants in void C4MapScriptHost::InitFunctionMap.
enum C4MapScriptAlgoType
{
MAPALGO_None = 0,
MAPALGO_Layer = 1,
MAPALGO_RndChecker = 10,
MAPALGO_Rect = 20,
MAPALGO_Ellipsis = 21,
MAPALGO_Polygon = 22,
MAPALGO_And = 30,
MAPALGO_Or = 31,
MAPALGO_Not = 32,
MAPALGO_Xor = 33,
MAPALGO_Offset = 40,
MAPALGO_Scale = 41,
MAPALGO_Rotate = 42,
MAPALGO_Turbulence = 43,
MAPALGO_Border = 50,
MAPALGO_Filter = 51,
};
// MAPALGO_Layer: Just query pixel in layer. Pixels outside the layer range are zero.
class C4MapScriptAlgoLayer : public C4MapScriptAlgo
{
const class C4MapScriptLayer *layer;
public:
C4MapScriptAlgoLayer(const C4MapScriptLayer *layer) : layer(layer) {}
C4MapScriptAlgoLayer(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_RndChecker: checkerboard on which areas are randomly set or unset
class C4MapScriptAlgoRndChecker : public C4MapScriptAlgo
{
int32_t seed, set_percentage, checker_wdt, checker_hgt;
bool is_fixed_offset;
public:
C4MapScriptAlgoRndChecker(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Rect: 1 for pixels contained in rect, 0 otherwise
class C4MapScriptAlgoRect : public C4MapScriptAlgo
{
C4Rect rect;
public:
C4MapScriptAlgoRect(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Ellipsis: 1 for pixels within ellipsis, 0 otherwise
class C4MapScriptAlgoEllipsis : public C4MapScriptAlgo
{
int32_t cx,cy;
uint32_t wdt,hgt;
public:
C4MapScriptAlgoEllipsis(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Polygon: 1 for pixels within polygon or on border, 0 otherwise
class C4MapScriptAlgoPolygon : public C4MapScriptAlgo
{
struct Pt { int32_t x,y; };
std::vector<Pt> poly;
int32_t wdt;
bool empty; // don't fill inside of polygon
bool open; // don't draw closing segment. only valid if empty=true
public:
C4MapScriptAlgoPolygon(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// base class for algo that takes one or more operands
class C4MapScriptAlgoModifier : public C4MapScriptAlgo
{
protected:
std::vector<C4MapScriptAlgo *> operands;
public:
C4MapScriptAlgoModifier(const C4PropList *props, int32_t min_ops=0, int32_t max_ops=0);
virtual ~C4MapScriptAlgoModifier() { Clear(); }
void Clear();
};
// MAPALGO_And: 0 if any of the operands is 0. Otherwise, returns value of last operand.
class C4MapScriptAlgoAnd : public C4MapScriptAlgoModifier
{
public:
C4MapScriptAlgoAnd(const C4PropList *props) : C4MapScriptAlgoModifier(props) { }
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Or: First nonzero operand
class C4MapScriptAlgoOr : public C4MapScriptAlgoModifier
{
public:
C4MapScriptAlgoOr(const C4PropList *props) : C4MapScriptAlgoModifier(props) { }
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Not: 1 if operand is 0, 0 otherwise.
class C4MapScriptAlgoNot : public C4MapScriptAlgoModifier
{
public:
C4MapScriptAlgoNot(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1) { }
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Xor: If exactly one of the two operands is nonzero, return it. Otherwise, return zero.
class C4MapScriptAlgoXor : public C4MapScriptAlgoModifier
{
public:
C4MapScriptAlgoXor(const C4PropList *props) : C4MapScriptAlgoModifier(props,2,2) { }
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Offset: Base layer shifted by ox,oy
class C4MapScriptAlgoOffset : public C4MapScriptAlgoModifier
{
int32_t ox,oy;
public:
C4MapScriptAlgoOffset(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Scale: Base layer scaled by sx,sy percent from fixed point cx,cy
class C4MapScriptAlgoScale : public C4MapScriptAlgoModifier
{
int32_t sx,sy,cx,cy;
public:
C4MapScriptAlgoScale(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Rotate: Base layer rotated by angle r around point ox,oy
class C4MapScriptAlgoRotate : public C4MapScriptAlgoModifier
{
int32_t sr,cr,ox,oy;
enum {Precision=1000};
public:
C4MapScriptAlgoRotate(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Turbulence: move by a random offset iterations times
class C4MapScriptAlgoTurbulence : public C4MapScriptAlgoModifier
{
int32_t amp[2], scale[2], seed, iterations;
public:
C4MapScriptAlgoTurbulence(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Border: Border of operand layer
class C4MapScriptAlgoBorder : public C4MapScriptAlgoModifier
{
int32_t left[2], top[2], right[2], bottom[2];
void ResolveBorderProps(int32_t *p);
public:
C4MapScriptAlgoBorder(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// MAPALGO_Filter: Original color of operand if it's marked to go through filter. 0 otherwise.
class C4MapScriptAlgoFilter : public C4MapScriptAlgoModifier
{
C4MapScriptMatTexMask filter;
public:
C4MapScriptAlgoFilter(const C4PropList *props);
virtual uint8_t operator () (int32_t x, int32_t y) const;
};
// layer of a script-controlled map
// IFT can be used to mark Sky
class C4MapScriptLayer : public C4PropListNumbered
{
CSurface8 *surface;
bool surface_owned;
protected:
class C4MapScriptMap *map;
public:
C4MapScriptLayer(C4PropList *prototype, C4MapScriptMap *map);
virtual ~C4MapScriptLayer() { ClearSurface(); }
virtual C4MapScriptLayer * GetMapScriptLayer() { return this; }
class C4MapScriptMap *GetMap() { return map; }
// Surface management
bool CreateSurface(int32_t wdt, int32_t hgt);
void ClearSurface();
CSurface8 *ReleaseSurface() { surface_owned=false; return surface; } // return surface and mark it as not owned by self
void SetSurface(CSurface8 *s) { ClearSurface(); surface=s; UpdateSurfaceSize(); }
bool HasSurface() const { return surface && surface->Bits; }
// Size management
void UpdateSurfaceSize(); // updates width and height properties by current surface
C4Rect GetBounds () const;
int32_t GetWdt() const { return surface ? surface->Wdt : 0; }
int32_t GetHgt() const { return surface ? surface->Hgt : 0; }
// Pixel functions
uint8_t GetPix(int32_t x, int32_t y, uint8_t outside_col) const { return (!HasSurface()||x<0||y<0||x>=surface->Wdt||y>=surface->Hgt) ? outside_col : surface->_GetPix(x,y); }
bool SetPix(int32_t x, int32_t y, uint8_t col) const { if (!HasSurface()||x<0||y<0||x>=surface->Wdt||y>=surface->Hgt) return false; surface->_SetPix(x,y,col); return true; }
bool IsPixMasked(int32_t x, int32_t y) const { return GetPix(x,y,0)!=0; } // masking: If pixel is inside surface and not transparent
void ConvertSkyToTransparent(); // change all pixels that are IFT to 0
int32_t GetPixCount(const C4Rect &rcBounds, const C4MapScriptMatTexMask &col_mask); // return number of pixels that match mask
// Drawing functions
bool Fill(int col, const C4Rect &rcBounds, const C4MapScriptAlgo *algo);
bool Blit(const C4Rect &rcBounds, const C4MapScriptAlgo *algo);
bool Blit(const C4MapScriptLayer *src, const C4Rect &src_rect, const C4MapScriptMatTexMask &col_mask, int32_t tx, int32_t ty);
// Search functions
bool FindPos(const C4Rect &search_rect, const C4MapScriptMatTexMask &col_mask, int32_t *out_x, int32_t *out_y, int32_t max_tries);
};
class C4MapScriptMap : public C4MapScriptLayer
{
std::list<C4MapScriptLayer *> layers;
public:
C4MapScriptMap(C4PropList *prototype) : C4MapScriptLayer(prototype, NULL) { map=this; }
~C4MapScriptMap() { Clear(); }
void Clear();
virtual C4MapScriptMap * GetMapScriptMap() { return this; }
C4MapScriptLayer *CreateLayer(int32_t wdt, int32_t hgt);
};
// Script host for scenario Map.c, parsed in static MapLayer prop list context
// Also hosts engine functions MapLayer prop list.
class C4MapScriptHost : public C4ScriptHost
{
private:
C4PropListStatic *LayerPrototype, *MapPrototype;
C4MapScriptMap *CreateMap();
public:
C4MapScriptHost();
~C4MapScriptHost();
void InitFunctionMap(C4AulScriptEngine *pEngine);
virtual void AddEngineFunctions();
virtual bool Load(C4Group &, const char *, const char *, C4LangStringTable *);
void Clear();
virtual C4PropListStatic * GetPropList();
bool InitializeMap(C4Group &group, CSurface8 **pmap_surface);
C4PropListStatic *GetLayerPrototype() { return LayerPrototype; }
};
extern C4MapScriptHost MapScript;
#endif

View File

@ -0,0 +1,482 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000 Matthes Bender
* Copyright (c) 2013 Sven Eberhardt
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* Handles scripted map creation */
#include <C4Include.h>
#include <C4MapScript.h>
#include <C4Random.h>
C4MapScriptAlgo *FnParAlgo(C4PropList *algo_par);
bool C4MapScriptAlgo::GetXYProps(const C4PropList *props, C4PropertyName k, int32_t *out_xy, bool zero_defaults)
{
// Evaluate property named "k" in proplist props to store two numbers in out_xy:
// If props->k is a single integer, fill both numbers in out_xy with it
// If props->k is an array, check that it contains two numbers and store them in out_xy
if (!props->HasProperty(&Strings.P[k]))
{
if (zero_defaults) out_xy[0] = out_xy[1] = 0;
return false;
}
C4Value val; C4ValueArray *arr;
props->GetProperty(k, &val);
if (arr = val.getArray())
{
if (arr->GetSize() != 2)
throw new C4AulExecError(FormatString("C4MapScriptAlgo: Expected either integer or array with two integer elements in property \"%s\".", Strings.P[k]).getData());
out_xy[0] = arr->GetItem(0).getInt();
out_xy[1] = arr->GetItem(1).getInt();
}
else
{
out_xy[0] = out_xy[1] = val.getInt();
}
return true;
}
C4MapScriptAlgoLayer::C4MapScriptAlgoLayer(const C4PropList *props)
{
// Get MAPALGO_Layer properties
C4PropList *layer_pl = props->GetPropertyPropList(P_Layer);
if (!layer_pl || !(layer = layer_pl->GetMapScriptLayer()))
throw new C4AulExecError("C4MapScriptAlgoLayer: Expected layer in \"Layer\" property.");
}
uint8_t C4MapScriptAlgoLayer::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Layer at x,y: Just query pixel in layer. Pixels outside the layer range are zero.
return layer->GetPix(x,y,0);
}
C4MapScriptAlgoRndChecker::C4MapScriptAlgoRndChecker(const C4PropList *props)
{
// Get MAPALGO_RndChecker properties
seed = props->GetPropertyInt(P_Seed);
if (!seed) seed = Random(65536);
set_percentage = BoundBy(props->GetPropertyInt(P_Ratio), 0, 100);
if (!set_percentage) set_percentage = 50;
checker_wdt = Abs(props->GetPropertyInt(P_Wdt));
if (!checker_wdt) checker_wdt = 10;
checker_hgt = Abs(props->GetPropertyInt(P_Hgt));
if (!checker_hgt) checker_hgt = 10;
C4Value is_fixed_offset_v;
if (props->GetProperty(P_FixedOffset, &is_fixed_offset_v))
is_fixed_offset = is_fixed_offset_v.getBool();
else
is_fixed_offset = false;
}
// Division and modulo operators that always round downwards
// Both assuming b>0
static int32_t divD(int32_t a, int32_t b) { return a/b-(a%b<0); }
static int32_t modD(int32_t a, int32_t b) { return (a>=0)?a%b:b-(-a)%b; }
// Creates a field of random numbers between 0 and scale-1. Returns the value of the field at position x,y
// Function should be deterministic for the same value of x,y, but should look somewhat random wrt neighbouring values of x,y
int32_t QuerySeededRandomField(int32_t seed, int32_t x, int32_t y, int32_t scale)
{
return modD((((seed ^ (x*214013))*214013) ^ (y*214013)), scale);
}
uint8_t C4MapScriptAlgoRndChecker::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_RndChecker at x,y: Query a seeded random field scaled by checker_wdt,checker_hgt
if (!is_fixed_offset) { x+=seed%checker_wdt; y+=((seed*214013)%checker_hgt); }
x = divD(x, checker_wdt); y = divD(y, checker_hgt);
return QuerySeededRandomField(seed, x,y, 100) < set_percentage;
}
C4MapScriptAlgoRect::C4MapScriptAlgoRect(const C4PropList *props)
{
// Get MAPALGO_Rect properties
rect = C4Rect(props->GetPropertyInt(P_X), props->GetPropertyInt(P_Y), props->GetPropertyInt(P_Wdt), props->GetPropertyInt(P_Hgt));
}
uint8_t C4MapScriptAlgoRect::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Rect at x,y: Return 1 for pixels contained in rect, 0 otherwise
return rect.Contains(x, y);
}
C4MapScriptAlgoEllipsis::C4MapScriptAlgoEllipsis(const C4PropList *props)
{
// Get MAPALGO_Ellipsis properties
cx = props->GetPropertyInt(P_X);
cy = props->GetPropertyInt(P_Y);
wdt = Abs(props->GetPropertyInt(P_Wdt));
hgt = Abs(props->GetPropertyInt(P_Hgt));
if (!wdt) wdt = 10;
if (!hgt) hgt = wdt;
}
uint8_t C4MapScriptAlgoEllipsis::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Ellipsis at x,y: Return 1 for pixels within ellipsis, 0 otherwise
// warning: overflows for large values (wdt or hgt >=256)
// but who would draw such large ellipsis anyway?
uint32_t dx = Abs((cx-x)*int32_t(hgt)), dy = Abs((cy-y)*int32_t(wdt));
return dx*dx+dy*dy < wdt*wdt*hgt*hgt;
}
C4MapScriptAlgoPolygon::C4MapScriptAlgoPolygon(const C4PropList *props)
{
// Get MAPALGO_Polygon properties
C4Value vptx, vpty;
props->GetProperty(P_X, &vptx); props->GetProperty(P_Y, &vpty);
C4ValueArray *ptx = vptx.getArray(), *pty = vpty.getArray();
if (!ptx || !pty || ptx->GetSize() != pty->GetSize())
throw new C4AulExecError("C4MapScriptAlgoPolygon: Expected two equally sized int arrays in properties \"X\" and \"Y\".");
poly.resize(ptx->GetSize());
for (int32_t i=0; i<ptx->GetSize(); ++i)
{
poly[i].x = ptx->GetItem(i).getInt();
poly[i].y = pty->GetItem(i).getInt();
}
wdt = props->GetPropertyInt(P_Wdt);
if (!wdt) wdt = 1;
empty = !!props->GetPropertyInt(P_Empty);
open = !!props->GetPropertyInt(P_Open);
if (open && !empty) throw new C4AulExecError("C4MapScriptAlgoPolygon: Only empty polygons may be open.");
}
uint8_t C4MapScriptAlgoPolygon::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Polygon at x,y: Return 1 for pixels within the polygon or its borders, 0 otherwise
int32_t crossings = 0;
for (size_t i=0; i<poly.size(); ++i)
{
Pt pt1 = poly[i];
Pt pt2 = poly[(i+1)%poly.size()];
// check border line distance
int32_t pdx = pt2.x-pt1.x, pdy = pt2.y-pt1.y, dx = x-pt1.x, dy = y-pt1.y;
if (i!=poly.size()-1 || !open)
{
int32_t d = dx*pdy-dy*pdx;
int32_t lsq = (pdx*pdx+pdy*pdy);
if (d*d < wdt*wdt*lsq) // check distance perpendicular to line
{
if (Inside(dx*pdx+dy*pdy, 0, lsq)) // check if point lies within pt1 and pt2
return true; // x/y lies on this line
}
}
// check point distance
if (dx*dx+dy*dy < wdt*wdt) return true; // x/y lies close to edge point
// filling of polygon: point is contained if it crosses an off number of borders
if (!empty && (pt1.y<=y) != (pt2.y<=y)) // crossing vertically?
{
// does line pt1-pt2 intersect line (x,y)-(inf,y)?
crossings += (dx>dy*pdx/pdy);
}
}
// x/y lies inside polygon
return (crossings % 2)==1;
}
C4MapScriptAlgoModifier::C4MapScriptAlgoModifier(const C4PropList *props, int32_t min_ops, int32_t max_ops)
{
// Evaluate "Op" property of all algos that take another algo or layer as an operand
// Op may be a proplist or an array of proplists
C4Value vops; int32_t n; C4ValueArray temp_ops;
props->GetProperty(P_Op, &vops);
C4ValueArray *ops = vops.getArray();
if (!ops)
{
C4PropList *op = vops.getPropList();
if (op)
{
temp_ops.SetItem(0, vops);
ops = &temp_ops;
n = 1;
}
}
else
{
n = ops->GetSize();
}
if (!ops || n<min_ops || (max_ops && n>max_ops))
throw new C4AulExecError(FormatString("C4MapScriptAlgo: Expected between %d and %d operands in property \"Op\".", (int)min_ops, (int)max_ops).getData());
operands.resize(n);
try
{
// can easily crash this by building a recursive prop list
// unfortunately, protecting against that is not trivial
for (int32_t i=0; i<n; ++i)
{
C4MapScriptAlgo *new_algo = FnParAlgo(ops->GetItem(i).getPropList());
if (!new_algo) throw new C4AulExecError(FormatString("C4MapScriptAlgo: Operand %d in property \"Op\" not valid.", (int)min_ops, (int)max_ops).getData());
operands[i] = new_algo;
}
}
catch (...)
{
Clear();
throw;
}
}
void C4MapScriptAlgoModifier::Clear()
{
// Child algos are owned by this algo, so delete them
for (std::vector<C4MapScriptAlgo *>::iterator i=operands.begin(); i != operands.end(); ++i) delete *i;
operands.clear();
}
uint8_t C4MapScriptAlgoAnd::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_And at x,y:
// Return 0 if any of the operands is 0. Otherwise, returns value of last operand.
uint8_t val=0;
for (std::vector<C4MapScriptAlgo *>::const_iterator i=operands.begin(); i != operands.end(); ++i)
if (!(val=(**i)(x,y))) return false;
return val;
}
uint8_t C4MapScriptAlgoOr::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Or at x,y:
// Return first nonzero operand
uint8_t val;
for (std::vector<C4MapScriptAlgo *>::const_iterator i=operands.begin(); i != operands.end(); ++i)
if (val=(**i)(x,y)) return val;
// If all operands are zero, return zero.
return 0;
}
uint8_t C4MapScriptAlgoNot::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Not at x,y:
assert(operands.size()==1);
// Return zero if operand is set and one otherwise
return !(*operands[0])(x,y);
}
uint8_t C4MapScriptAlgoXor::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Xor at x,y:
assert(operands.size()==2);
// If exactly one of the two operands is nonzero, return it. Otherwise, return zero.
uint8_t v1=(*operands[0])(x,y), v2=(*operands[1])(x,y);
return v1?v2 ?0: v1:v2;
}
C4MapScriptAlgoOffset::C4MapScriptAlgoOffset(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1)
{
// Get MAPALGO_Offset properties
ox = props->GetPropertyInt(P_OffX);
oy = props->GetPropertyInt(P_OffY);
}
uint8_t C4MapScriptAlgoOffset::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Offset at x,y:
assert(operands.size()==1);
// Return base layer shifted by ox,oy
return (*operands[0])(x-ox,y-oy);
}
C4MapScriptAlgoScale::C4MapScriptAlgoScale(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1)
{
// Get MAPALGO_Scale properties
sx = props->GetPropertyInt(P_X);
sy = props->GetPropertyInt(P_Y);
if (!sx) sx=100;
if (!sy) sy=100;
cx = props->GetPropertyInt(P_OffX);
cy = props->GetPropertyInt(P_OffY);
}
uint8_t C4MapScriptAlgoScale::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Scale at x,y:
assert(operands.size()==1);
// Return base layer scaled by sx,sy percent from fixed point cx,cy
return (*operands[0])((x-cx)*100/sx+cx,(y-cy)*100/sy+cy);
}
C4MapScriptAlgoRotate::C4MapScriptAlgoRotate(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1)
{
// Get MAPALGO_Rotate properties
int32_t r = props->GetPropertyInt(P_R);
sr=fixtoi(Sin(itofix(r)), Precision);
cr=fixtoi(Cos(itofix(r)), Precision);
ox = props->GetPropertyInt(P_OffX);
oy = props->GetPropertyInt(P_OffY);
}
uint8_t C4MapScriptAlgoRotate::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Rotate at x,y:
assert(operands.size()==1);
// Return base layer rotated by angle r around point ox,oy
x-=ox; y-=oy;
return (*operands[0])(x*cr/Precision-y*sr/Precision+ox,x*sr/Precision+y*cr/Precision+oy);
}
C4MapScriptAlgoTurbulence::C4MapScriptAlgoTurbulence(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1)
{
// Get MAPALGO_Turbulence properties
seed = props->GetPropertyInt(P_Seed);
if (!seed) seed = Random(65536);
GetXYProps(props, P_Amplitude, amp, true);
GetXYProps(props, P_Scale, scale, true);
if (!scale[0]) scale[0] = 10; if (!scale[1]) scale[1] = 10;
if (!amp[0] && !amp[1]) { amp[0] = amp[1] = 10; }
iterations = props->GetPropertyInt(P_Iterations);
if (!iterations) iterations = 2;
}
uint8_t C4MapScriptAlgoTurbulence::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Turbulence at x,y:
// move by a random offset iterations times
assert(operands.size()==1);
int32_t xy[] = {x, y};
for (int32_t iter=0; iter<iterations; ++iter)
{
int32_t s[2], p[2];
for (int dim=0; dim<2; ++dim)
{
s[dim] = divD(xy[dim], scale[dim]);
p[dim] = modD(xy[dim], scale[dim]);
}
int32_t a[2][2];
for (int dim=0; dim<2; ++dim)
{
int32_t aamp = amp[dim] / (iter+1);
if (!aamp) continue;
for (int dx=0; dx<2; ++dx) for (int dy=0; dy<2; ++dy) a[dx][dy] = QuerySeededRandomField(seed+dim, s[0]+dx, s[1]+dy, aamp) - aamp/2;
int32_t a_interp = a[0][0]*(scale[0]-p[0])*(scale[1]-p[1])
+ a[1][0]*( p[0])*(scale[1]-p[1])
+ a[0][1]*(scale[0]-p[0])*( p[1])
+ a[1][1]*( p[0])*( p[1]);
xy[dim] += a_interp / (scale[0]*scale[1]);
}
}
return (*operands[0])(xy[0],xy[1]);
}
void C4MapScriptAlgoBorder::ResolveBorderProps(int32_t *p)
{
// Converts arrays in MAPALGO_Border properties to array of [inner border, outer border]
// Input: Negative values mark outer borders; positive values mark inner borders
int32_t inner=0, outer=0;
for (int32_t i=0; i<2; ++i) if (p[i]>0) inner=p[i]; else if (p[i]<0) outer=-p[i];
p[0] = inner; p[1] = outer;
}
C4MapScriptAlgoBorder::C4MapScriptAlgoBorder(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1)
{
// Get MAPALGO_Border properties
int32_t wdt[2] = {0,0};
// Parameter Wdt fills all directions
int32_t n_borders = 0;
n_borders += GetXYProps(props, P_Wdt, wdt, false);
for (int32_t i=0; i<2; ++i) left[i]=top[i]=right[i]=bottom[i]=wdt[i];
// Individual direction parameters
n_borders += GetXYProps(props, P_Left, left, false);
n_borders += GetXYProps(props, P_Top, top, false);
n_borders += GetXYProps(props, P_Right, right, false);
n_borders += GetXYProps(props, P_Bottom, bottom, false);
// Resolve negative/positive values to inner/outer borders
ResolveBorderProps(left);
ResolveBorderProps(top);
ResolveBorderProps(right);
ResolveBorderProps(bottom);
// If nothing was specified, fill all directions with a default: Draw 1px of outer border
if (!n_borders)
{
left[1] = top[1] = right[1] = bottom[1] = 1;
}
}
uint8_t C4MapScriptAlgoBorder::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Border at x,y: Check if position is at a border of operand layer
// For borders inside operand layer, return the operand material. For outside borders, just return 1. For non-borders, return 0.
// Are we inside or outside?
const C4MapScriptAlgo &l = *operands[0];
uint8_t mat = l(x,y);
bool inside = !!mat;
if (!mat) mat=1; // return 1 for outside borders
// Check four sideways directions
const int32_t *ymove[] = { top, bottom }, *xmove [] ={ left, right };
const int32_t d[] = { -1, +1 };
for (int32_t dir=0; dir<2; ++dir)
{
int32_t hgt = ymove[inside!=!dir][!inside];
for (int32_t dy=0; dy<hgt; ++dy)
if (inside==!l(x,y+d[dir]*(dy+1)))
return mat;
int32_t wdt = xmove[inside!=!dir][!inside];
for (int32_t dx=0; dx<wdt; ++dx)
if (inside==!l(x+d[dir]*(dx+1),y))
return mat;
}
// Not on border
return 0;
}
C4MapScriptAlgoFilter::C4MapScriptAlgoFilter(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1)
{
// Get MAPALGO_Filter properties
C4Value spec;
if (!props->GetProperty(P_Filter, &spec))
throw new C4AulExecError("MapScriptAlgoFilter without Filter property.");
filter.Init(spec);
}
uint8_t C4MapScriptAlgoFilter::operator () (int32_t x, int32_t y) const
{
// Evaluate MAPALGO_Filter at x,y:
// Return original color if it's marked to go through filter
uint8_t col = (*operands[0])(x,y);
return filter(col) ? col : 0;
}
C4MapScriptAlgo *FnParAlgo(C4PropList *algo_par)
{
// Convert script function parameter to internal C4MapScriptAlgo class. Also resolve all parameters and nested child algos.
if (!algo_par) return NULL;
// if algo is a layer, take that directly
C4MapScriptLayer *algo_layer = algo_par->GetMapScriptLayer();
if (algo_layer) return new C4MapScriptAlgoLayer(algo_layer);
// otherwise, determine by proplist parameter "algo"
switch (algo_par->GetPropertyInt(P_Algo))
{
case MAPALGO_Layer: return new C4MapScriptAlgoLayer(algo_par);
case MAPALGO_RndChecker: return new C4MapScriptAlgoRndChecker(algo_par);
case MAPALGO_And: return new C4MapScriptAlgoAnd(algo_par);
case MAPALGO_Or: return new C4MapScriptAlgoOr(algo_par);
case MAPALGO_Xor: return new C4MapScriptAlgoXor(algo_par);
case MAPALGO_Not: return new C4MapScriptAlgoNot(algo_par);
case MAPALGO_Offset: return new C4MapScriptAlgoOffset(algo_par);
case MAPALGO_Scale: return new C4MapScriptAlgoScale(algo_par);
case MAPALGO_Rotate: return new C4MapScriptAlgoRotate(algo_par);
case MAPALGO_Rect: return new C4MapScriptAlgoRect(algo_par);
case MAPALGO_Ellipsis: return new C4MapScriptAlgoEllipsis(algo_par);
case MAPALGO_Polygon: return new C4MapScriptAlgoPolygon(algo_par);
case MAPALGO_Turbulence: return new C4MapScriptAlgoTurbulence(algo_par);
case MAPALGO_Border: return new C4MapScriptAlgoBorder(algo_par);
case MAPALGO_Filter: return new C4MapScriptAlgoFilter(algo_par);
default:
throw new C4AulExecError(FormatString("got invalid algo: %d", algo_par->GetPropertyInt(P_Algo)).getData());
}
return NULL;
}

View File

@ -43,11 +43,11 @@ public:
bool operator ==(const C4Rect &r2) { return !((x-r2.x) | (y-r2.y) | (Wdt-r2.Wdt) | (Hgt-r2.Hgt)); }
bool operator !=(const C4Rect &r2) { return 0 != ((x-r2.x) | (y-r2.y) | (Wdt-r2.Wdt) | (Hgt-r2.Hgt)); }
bool Contains(int32_t iX, int32_t iY)
bool Contains(int32_t iX, int32_t iY) const
{ return iX>=x && iX<x+Wdt && iY>=y && iY<y+Hgt; }
bool Contains(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt)
bool Contains(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt) const
{ return iX>=x && iX+iWdt<x+Wdt && iY>=y && iY+iHgt<y+Hgt; }
bool Contains(const C4Rect &rect)
bool Contains(const C4Rect &rect) const
{ return Contains(rect.x, rect.y, rect.Wdt, rect.Hgt); }
bool IntersectsLine(int32_t iX, int32_t iY, int32_t iX2, int32_t iY2);

View File

@ -21,6 +21,7 @@
#include <C4AulFunc.h>
#include <C4Aul.h>
#include <C4ScriptHost.h>
#include <C4MapScript.h>
C4AulFunc::C4AulFunc(C4AulScript *pOwner, const char *pName):
iRefCnt(0),
@ -56,6 +57,10 @@ StdStrBuf C4AulFunc::GetFullName()
{
r.Ref("Scenario.");
}
else if (Owner == &MapScript)
{
r.Ref("MapLayer.");
}
else if (Owner->Engine == Owner)
{
r.Ref("Global.");

View File

@ -771,6 +771,9 @@ bool C4ScriptHost::Preparse()
GetPropList()->SetProperty(P_Prototype, C4VPropList(Engine->GetPropList()));
LocalValues.Clear();
// Add any engine functions specific to this script
AddEngineFunctions();
C4AulParse state(this, C4AulParse::PREPARSER);
state.Parse_Script(this);
@ -2879,14 +2882,22 @@ void C4ScriptHost::CopyPropList(C4Set<C4Property> & from, C4PropListStatic * to)
case C4V_Function:
{
C4AulScriptFunc * sf = prop->Value.getFunction()->SFunc();
//assert(sf->pOrgScript == *s);
C4AulScriptFunc *sfc;
if (sf->pOrgScript != this)
sfc = new C4AulScriptFunc(this, *sf);
if (sf)
{
//assert(sf->pOrgScript == *s);
C4AulScriptFunc *sfc;
if (sf->pOrgScript != this)
sfc = new C4AulScriptFunc(this, *sf);
else
sfc = sf;
sfc->SetOverloaded(to->GetFunc(sf->Name));
to->SetPropertyByS(prop->Key, C4VFunction(sfc));
}
else
sfc = sf;
sfc->SetOverloaded(to->GetFunc(sf->Name));
to->SetPropertyByS(prop->Key, C4VFunction(sfc));
{
// engine function
to->SetPropertyByS(prop->Key, prop->Value);
}
}
break;
case C4V_PropList:

View File

@ -525,6 +525,20 @@ int32_t C4PropList::GetPropertyInt(C4PropertyName n) const
return 0;
}
C4PropList *C4PropList::GetPropertyPropList(C4PropertyName n) const
{
C4String * k = &Strings.P[n];
if (Properties.Has(k))
{
return Properties.Get(k).Value.getPropList();
}
if (prototype)
{
return prototype->GetPropertyPropList(n);
}
return NULL;
}
C4ValueArray * C4PropList::GetProperties() const
{
C4ValueArray * a;

View File

@ -83,6 +83,8 @@ public:
virtual C4Effect * GetEffect();
virtual C4PropListNumbered * GetPropListNumbered();
C4PropList * GetPrototype() const { return prototype; }
virtual class C4MapScriptLayer * GetMapScriptLayer() { return NULL; }
virtual class C4MapScriptMap * GetMapScriptMap() { return NULL; }
// saved as a reference to a global constant?
virtual class C4PropListStatic * IsStatic() { return NULL; }
@ -114,7 +116,8 @@ public:
C4Value Call(const char * k, C4AulParSet *pPars=0, bool fPassErrors=false);
C4PropertyName GetPropertyP(C4PropertyName k) const;
int32_t GetPropertyInt(C4PropertyName k) const;
bool HasProperty(C4String * k) { return Properties.Has(k); }
C4PropList *GetPropertyPropList(C4PropertyName k) const;
bool HasProperty(C4String * k) const { return Properties.Has(k); }
// not allowed on frozen proplists
void SetProperty(C4PropertyName k, const C4Value & to)
{ SetPropertyByS(&Strings.P[k], to); }

View File

@ -55,6 +55,7 @@ protected:
std::list<C4ID> Includes; // include list
std::list<C4ID> Appends; // append list
virtual void AddEngineFunctions() {}; // add any engine functions specific to this script host
void CopyPropList(C4Set<C4Property> & from, C4PropListStatic * to);
bool ResolveIncludes(C4DefList *rDefs); // resolve includes
bool ResolveAppends(C4DefList *rDefs); // resolve appends

View File

@ -30,6 +30,7 @@
#include <C4Material.h>
#include <C4Reloc.h>
#include <C4Record.h>
#include <C4MapScript.h>
#ifdef _DEBUG
C4Set<C4PropList *> C4PropList::PropLists;
@ -100,6 +101,14 @@ void C4Def::IncludeDefinition(C4Def*) {}
bool EraseItemSafe(const char *szFilename) {return false;}
void AddDbgRec(C4RecordChunkType, const void *, int) {}
C4MapScriptHost MapScript;
C4MapScriptHost::C4MapScriptHost() {}
C4MapScriptHost::~C4MapScriptHost() {}
void C4MapScriptHost::Clear() {}
C4PropListStatic *C4MapScriptHost::GetPropList() {return NULL;}
bool C4MapScriptHost::Load(C4Group &, const char *, const char *, C4LangStringTable *) { return false; }
void C4MapScriptHost::AddEngineFunctions() {}
int c4s_runscript(const char * filename)
{
InitCoreFunctionMap(&ScriptEngine);

View File

@ -150,6 +150,23 @@ C4StringTable::C4StringTable()
P[P_ContactIncinerate] = "ContactIncinerate";
P[P_Global] = "Global";
P[P_JumpSpeed] = "JumpSpeed";
P[P_Algo] = "Algo";
P[P_Layer] = "Layer";
P[P_Seed] = "Seed";
P[P_Ratio] = "Ratio";
P[P_FixedOffset] = "FixedOffset";
P[P_Op] = "Op";
P[P_R] = "R";
P[P_Scale] = "Scale";
P[P_Amplitude] = "Amplitude";
P[P_Iterations] = "Iterations";
P[P_Empty] = "Empty";
P[P_Open] = "Open";
P[P_Left] = "Left";
P[P_Top] = "Top";
P[P_Right] = "Right";
P[P_Bottom] = "Bottom";
P[P_Filter] = "Filter";
P[DFA_WALK] = "WALK";
P[DFA_FLIGHT] = "FLIGHT";
P[DFA_KNEEL] = "KNEEL";

View File

@ -354,6 +354,23 @@ enum C4PropertyName
P_ContactIncinerate,
P_Global,
P_JumpSpeed,
P_Algo,
P_Layer,
P_Seed,
P_Ratio,
P_FixedOffset,
P_Op,
P_R,
P_Scale,
P_Amplitude,
P_Iterations,
P_Empty,
P_Open,
P_Left,
P_Top,
P_Right,
P_Bottom,
P_Filter,
// Default Action Procedures
DFA_WALK,
DFA_FLIGHT,