Пример #1
0
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 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 C4AulExecError("MatTexMask expected string or array of strings.");
			// nil defaults to everything except index zero unmasked
			sky_mask = std::vector<bool>(256, true);
			tunnel_mask = std::vector<bool>(256, true);
			sky_mask[0] = false;
			tunnel_mask[0] = false;
		}
	}
}
Пример #2
0
void C4Effect::SetPropertyByS(C4String * k, const C4Value & to)
{
	if (k >= &Strings.P[0] && k < &Strings.P[P_LAST])
	{
		switch(k - &Strings.P[0])
		{
			case P_Name:
				if (!to.getStr() || !*to.getStr()->GetCStr())
					throw C4AulExecError("effect: Name has to be a nonempty string");
				C4PropListNumbered::SetPropertyByS(k, to);
				ReAssignCallbackFunctions();
				return;
			case P_Priority:
				throw C4AulExecError("effect: Priority is readonly");
			case P_Interval: iInterval = to.getInt(); return;
			case P_CommandTarget:
				throw C4AulExecError("effect: CommandTarget is readonly");
			case P_Target:
				throw C4AulExecError("effect: Target is readonly");
			case P_Time: iTime = to.getInt(); return;
			case P_Prototype:
				throw new C4AulExecError("effect: Prototype is readonly");
		}
	}
	C4PropListNumbered::SetPropertyByS(k, to);
}
Пример #3
0
static bool FnLayerSetPixel(C4PropList * _this, int32_t x, int32_t y, const C4Value& fg_value_c4v, const C4Value& bg_value_c4v)
{
	// Layer script function: Set pixel at position x,y to to_value in _this layer
	C4MapScriptLayer *layer = _this->GetMapScriptLayer();
	if (!layer) return false;
	uint8_t fg, bg;

	if (fg_value_c4v.GetType() == C4V_Nil)
	{
		fg = layer->GetPix(x,y,0);
	}
	else
	{
		const C4Value& val = fg_value_c4v;
		C4String *str = val.getStr();
		if (str != NULL)
		{
			if (!TexColSingle(str->GetCStr(), fg))
				throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
		}
		else
		{
			if (!Inside(val.getInt(), 0, 255))
				throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
			fg = val.getInt();
		}
	}

	if (bg_value_c4v.GetType() == C4V_Nil)
	{
		bg = layer->GetBackPix(x,y,0);
	}
	else
	{
		const C4Value& val = bg_value_c4v;
		C4String *str = val.getStr();
		if (str != NULL)
		{
			if (!TexColSingle(str->GetCStr(), bg))
				throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
		}
		else
		{
			if (!Inside(val.getInt(), 0, 255))
				throw C4AulExecError("MapLayer::SetPixel: Trying to set invalid pixel value.");
			bg = val.getInt();
		}
	}
			
	return layer->SetPix(x,y,fg,bg);
}
Пример #4
0
void C4ValueArray::SetItem(int32_t iElem, const C4Value &Value)
{
    // enlarge
    if (iElem < -iSize)
        throw C4AulExecError("array access: index out of range");
    else if (iElem < 0)
        iElem = iSize + iElem;
    else if (iElem >= iSize && iElem < MaxSize) this->SetSize(iElem + 1);
    // out-of-memory? This might not get caught, but it's better than a segfault
    if (iElem >= iSize)
        throw C4AulExecError("array access: index too large");
    // set
    pData[iElem]=Value;
}
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_Lines:       return new C4MapScriptAlgoLines(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 C4AulExecError(FormatString("got invalid algo: %d", algo_par->GetPropertyInt(P_Algo)).getData());
	}
	return NULL;
}
C4MapScriptAlgoLayer::C4MapScriptAlgoLayer(const C4PropList *props)
{
	// Get MAPALGO_Layer properties
	C4PropList *layer_pl = props->GetPropertyPropList(P_Layer);
	if (!layer_pl || !(layer = layer_pl->GetMapScriptLayer()))
		throw C4AulExecError("C4MapScriptAlgoLayer: Expected layer in \"Layer\" property.");
}
Пример #7
0
C4ValueArray * C4ValueArray::GetSlice(int32_t startIndex, int32_t endIndex)
{
    // adjust indices so that the default end index works and that negative numbers count backwards from the end of the string
    if (startIndex > iSize) startIndex = iSize;
    else if (startIndex < -iSize) throw C4AulExecError("array slice: start index out of range");
    else if (startIndex < 0) startIndex += iSize;

    if (endIndex > iSize) endIndex = iSize; // this also processes the MAX_INT default if no parameter is given in script
    else if (endIndex < -iSize) throw C4AulExecError("array slice: end index out of range");
    else if (endIndex < 0) endIndex += iSize;

    C4ValueArray* NewArray = new C4ValueArray(std::max(0, endIndex - startIndex));
    for (int i = startIndex; i < endIndex; ++i)
        NewArray->pData[i - startIndex] = pData[i];
    return NewArray;
}
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 C4AulExecError(FormatString("C4MapScriptAlgo: Expected either integer or array with two integer elements in property \"%s\".", Strings.P[k].GetCStr()).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;
}
C4MapScriptAlgoFilter::C4MapScriptAlgoFilter(const C4PropList *props) : C4MapScriptAlgoModifier(props,1,1)
{
	// Get MAPALGO_Filter properties
	C4Value spec;
	if (!props->GetProperty(P_Filter, &spec))
		throw C4AulExecError("MapScriptAlgoFilter without Filter property.");
	filter.Init(spec);
}
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 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 C4AulExecError("C4MapScriptAlgoPolygon: Only empty polygons may be open.");
}
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 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 C4AulExecError(FormatString("C4MapScriptAlgo: Operand %d in property \"Op\" not valid.", (int)i).getData());
			operands[i] = new_algo;
		}
	}
	catch (...)
	{
		Clear();
		throw;
	}
}
Пример #12
0
void C4Effect::ResetProperty(C4String * k)
{
	if (k >= &Strings.P[0] && k < &Strings.P[P_LAST])
	{
		switch(k - &Strings.P[0])
		{
			case P_Name:
				throw C4AulExecError("effect: Name has to be a nonempty string");
			case P_Priority:
				throw C4AulExecError("effect: Priority is readonly");
			case P_Interval: iInterval = 0; return;
			case P_CommandTarget:
				throw C4AulExecError("effect: CommandTarget is readonly");
			case P_Target:
				throw C4AulExecError("effect: Target is readonly");
			case P_Time: iTime = 0; return;
			case P_Prototype:
				throw new C4AulExecError("effect: Prototype is readonly");
		}
	}
	C4PropListNumbered::ResetProperty(k);
}
Пример #13
0
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
	uint8_t fg = 0, bg = 0;
	if (mattex_fill && mattex_fill->GetCStr())
		if (!FnParTexCol(mattex_fill, fg, bg))
			throw 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 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 (fg != 0 || bg != 0) layer->Fill(fg, bg, layer->GetBounds(), NULL);
	return layer;
}
C4MapScriptAlgoLines::C4MapScriptAlgoLines(const C4PropList *props)
{
	// Get MAPALGO_Lines properties
	lx = props->GetPropertyInt(P_X);
	ly = props->GetPropertyInt(P_Y);
	if (!lx && !ly) throw C4AulExecError("C4MapScriptAlgoLines: Invalid direction vector. Either \"X\" or \"Y\" must be nonzero!");
	ox = props->GetPropertyInt(P_OffX);
	oy = props->GetPropertyInt(P_OffY);
		// use sync-safe distance function to calculate line width
	int32_t l = Distance(0,0,lx,ly);
	// default distance: double line width, so lines and gaps have same width
	distance = props->GetPropertyInt(P_Distance);
	if (!distance) distance = l+l; // 1+1=2
	// cache for calculation
	ll = int64_t(lx)*lx+int64_t(ly)*ly;
	dl = int64_t(distance) * l;
}
Пример #15
0
void C4PropList::SetPropertyByS(C4String * k, const C4Value & to)
{
	assert(!constant);
	if (k == &Strings.P[P_Prototype])
	{
		C4PropList * newpt = to.getPropList();
		for(C4PropList * it = newpt; it; it = it->GetPrototype())
			if(it == this)
				throw C4AulExecError("Trying to create cyclic prototype structure");
		prototype.SetPropList(newpt);
	}
	else if (Properties.Has(k))
	{
		Properties.Get(k).Value = to;
	}
	else
	{
		Properties.Add(C4Property(k, to));
	}
}
Пример #16
0
void C4ValueArray::SetSlice(int32_t startIndex, int32_t endIndex, const C4Value &Val)
{
    // maximum bounds
    if(startIndex >= MaxSize) throw C4AulExecError("array slice: start index exceeds maximum capacity");

    // index from back
    if(startIndex < 0) startIndex += iSize;
    if(endIndex < 0) endIndex += iSize;

    // ensure relevant bounds
    if(startIndex < 0) throw C4AulExecError("array slice: start index out of range");
    if(endIndex < 0) throw C4AulExecError("array slice: end index out of range");
    if(endIndex < startIndex)
        endIndex = startIndex;

    // setting an array?
    if(Val.GetType() == C4V_Array)
    {
        const C4ValueArray &Other = *Val._getArray(); // Remember that &Other could be equal to this, carefull with modifying pData

        // Calculcate new size
        int32_t iNewEnd = std::min(startIndex + Other.GetSize(), (int32_t)MaxSize);
        int32_t iNewSize = iNewEnd;
        if(endIndex < iSize)
            iNewSize += iSize - endIndex;
        iNewSize = std::min(iNewSize, (int32_t)MaxSize);
        int32_t iOtherSize = Other.GetSize();

        if(iNewSize != iSize)
        {
            int32_t i,j;
            C4Value* pnData = pData;

            if(iNewSize > iCapacity)
            {
                pnData = new C4Value [iNewSize];

                // Copy first part of old array
                for(i = 0; i < startIndex && i < iSize; ++i)
                    pnData[i] = pData[i];
            }

            // Copy the second slice of the new array
            for(i = iNewEnd, j = endIndex; i < iNewSize; ++i, ++j)
            {
                assert(j < iSize);
                pnData[i] = pData[j];
            }

            // Copy the data
            // Since pnData and pData can be the same, we can not copy with
            //for(i = startIndex, j = 0; i < iNewEnd; ++i, ++j)
            // but have to start from the end of the copied sequence. That works, since j <= i
            for(i = iNewEnd - 1, j = iNewEnd - startIndex - 1; i >= startIndex; --i, --j)
            {
                assert(j < iOtherSize);
                pnData[i] = Other.pData[j];
            }

            // Other values should have been initialized to 0 by new
            if(pData != pnData)
            {
                delete [] pData;
                pData = pnData;
                iCapacity = iSize = iNewSize;
            }
            else
            {
                // "ignore" the now unused part
                for(i = iNewSize; i < iSize; ++i)
                    pData[i].Set0();
                iSize = iNewSize;
            }

        } else // slice has the same size as inserted array
            // Copy the data. changing pData does not break because if &Other == this and iNewSize == iSize, nothing happens at all
            for(int32_t i = startIndex, j = 0; j < iOtherSize; i++, j++)
                pData[i] = Other.pData[j];

    } else { /* if(Val.GetType() != C4V_Array) */
        if(endIndex > MaxSize) endIndex = iSize;

        // Need resize?
        if(endIndex > iSize) SetSize(endIndex);

        // Fill
        for(int32_t i = startIndex; i < endIndex; i++)
            pData[i] = Val;

    }

}
Пример #17
0
void C4ParticleValueProvider::Set(const C4ValueArray &fromArray)
{
	startValue = endValue = 1.0f;
	valueFunction = &C4ParticleValueProvider::Const;

	size_t arraySize = (size_t) fromArray.GetSize();
	if (arraySize < 2) return;

	int type = fromArray[0].getInt();

	switch (type)
	{
	case C4PV_Const:
		if (arraySize >= 2)
		{
			SetType(C4PV_Const);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
		}
		break;

	case C4PV_Linear:
		if (arraySize >= 3)
		{
			SetType(C4PV_Linear);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::endValue);
		}
		break;
	case C4PV_Random:
		if (arraySize >= 3)
		{
			SetType(C4PV_Random);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::endValue);
			if (arraySize >= 4)
				SetParameterValue(VAL_TYPE_INT, fromArray[3], 0, &C4ParticleValueProvider::rerollInterval);
			alreadyRolled = 0;
		}
		break;
	case C4PV_Direction:
		if (arraySize >= 2)
		{
			SetType(C4PV_Direction);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
		}
		break;
	case C4PV_Step:
		if (arraySize >= 4)
		{
			SetType(C4PV_Step);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::startValue);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::currentValue);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[3], &C4ParticleValueProvider::delay);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[4], &C4ParticleValueProvider::maxValue);
			if (delay == 0.f) delay = 1.f;
		}
		break;
	case C4PV_KeyFrames:
		if (arraySize >= 5)
		{
			SetType(C4PV_KeyFrames);
			SetParameterValue(VAL_TYPE_INT, fromArray[1], 0,  &C4ParticleValueProvider::smoothing);
			keyFrames.resize(arraySize + 4 - 1); // 2*2 additional information floats at the beginning and ending, offset the first array item, though

			keyFrameCount = 0;
			const size_t startingOffset = 2;
			size_t i = startingOffset;
			for (; i < arraySize; ++i)
			{
				SetParameterValue(VAL_TYPE_KEYFRAMES, fromArray[(int32_t)i], 0, 0, 2 + i - startingOffset);
			}
			keyFrameCount = (i - startingOffset) / 2 + 2;

			startValue = keyFrames[2 + 1];
			endValue = keyFrames[2 * keyFrameCount - 1];

			// add two points for easier interpolation at beginning and ending
			keyFrames[0] = -500.f;
			keyFrames[1] = keyFrames[2 + 1];
			keyFrames[2 * keyFrameCount - 2] = 1500.f;
			keyFrames[2 * keyFrameCount - 1] = keyFrames[keyFrameCount - 1 - 2];

		}
		break;
	case C4PV_Sin:
		if (arraySize >= 3)
		{
			SetType(C4PV_Sin); // Sin(parameterValue) * maxValue + startValue
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::parameterValue);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::maxValue);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[3], &C4ParticleValueProvider::startValue);
		}
		break;
	case C4PV_Speed:
		if (arraySize >= 3)
		{
			SetType(C4PV_Speed);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::speedFactor);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::startValue);
		}
		break;
	case C4PV_Wind:
		if (arraySize >= 3)
		{
			SetType(C4PV_Wind);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::speedFactor);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::startValue);
		}
		break;
	case C4PV_Gravity:
		if (arraySize >= 3)
		{
			SetType(C4PV_Gravity);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[1], &C4ParticleValueProvider::speedFactor);
			SetParameterValue(VAL_TYPE_FLOAT, fromArray[2], &C4ParticleValueProvider::startValue);
		}
		break;
	default:
		throw C4AulExecError("invalid particle value provider supplied");
		break;
	}
}