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; } } }
bool operator ()(const C4Value &v1, const C4Value &v2) { C4Value p1,p2; if (!v1._getPropList()->GetPropertyByS(prop_name, &p1)) p1.Set0(); if (!v2._getPropList()->GetPropertyByS(prop_name, &p2)) p2.Set0(); return value_sort(p1,p2); }
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); }
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; }
bool C4FindObjectProcedure::Check(C4Object *pObj) { assert(pObj); if (!pObj->GetAction()) return false; C4Value v; pObj->GetAction()->GetProperty(P_Procedure, &v); return v != C4VNull && v.getStr() == procedure; }
void C4ParticleValueProvider::Set(const C4Value &value) { C4ValueArray *valueArray= value.getArray(); if (valueArray != 0) Set(*valueArray); else Set((float)value.getInt()); }
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); }
int32_t C4Def::GetValue(C4Object *pInBase, int32_t iBuyPlayer) { C4Value r = Call(PSF_CalcDefValue, &C4AulParSet(pInBase, iBuyPlayer)); int32_t iValue = Value; if (r != C4VNull) iValue = r.getInt(); // do any adjustments based on where the item is bought if (pInBase) { r = pInBase->Call(PSF_CalcBuyValue, &C4AulParSet(this, iValue)); if (r != C4VNull) iValue = r.getInt(); } return iValue; }
void C4ParticleProperties::SetCollisionFunc(const C4Value &source) { C4ValueArray *valueArray; if (!(valueArray = source.getArray())) return; int arraySize = valueArray->GetSize(); if (arraySize < 1) return; int type = (*valueArray)[0].getInt(); switch (type) { case C4PC_Die: collisionCallback = &C4ParticleProperties::CollisionDie; break; case C4PC_Bounce: collisionCallback = &C4ParticleProperties::CollisionBounce; bouncyness = 1.f; if (arraySize >= 2) bouncyness = ((float)(*valueArray)[1].getInt()); break; case C4PC_Stop: collisionCallback = &C4ParticleProperties::CollisionStop; break; default: assert(false); break; } }
bool operator ()(const C4Value &v1, const C4Value &v2) { // sort by whatever type the values have if (v1.getStr() && v2.getStr()) return v1._getStr()->GetData() < v2._getStr()->GetData(); if (v1.CheckConversion(C4V_Int) && v2.CheckConversion(C4V_Int)) return v1._getInt() < v2._getInt(); return false; }
static int32_t FnLayerGetDefaultBackgroundIndex(C4PropList * _this, const C4Value &value) { uint8_t fg; C4String* str; if ((str = value.getStr())) { if (!TexColSingle(str->GetCStr(), fg)) return -1; } else { if (!Inside(value.getInt(), 0, 255)) return -1; fg = value.getInt(); } return ::MapScript.pTexMap->DefaultBkgMatTex(fg); }
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; } }
C4ConsoleQtLocalizeStringDlg::C4ConsoleQtLocalizeStringDlg(class QMainWindow *parent_window, const C4Value &translations) : QDialog(parent_window, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint) , translations(translations) { ui.setupUi(this); // Add language editors int32_t lang_index = 0; C4LanguageInfo *lang_info; while (lang_info = ::Languages.GetInfo(lang_index++)) { AddEditor(lang_info->Code, lang_info->Name); } // Fill in values C4PropList *translations_proplist = translations.getPropList(); assert(translations_proplist); for (C4String *lang_str : translations_proplist->GetSortedLocalProperties(false)) { if (lang_str->GetData().getLength() == 2) { C4Value text_val; if (translations_proplist->GetPropertyByS(lang_str, &text_val)) { C4String *text = text_val.getStr(); if (text) { QLineEdit *editor = GetEditorByLanguage(lang_str->GetCStr()); if (!editor) { // Unknown language. Just add an editor without language name. editor = AddEditor(lang_str->GetCStr(), nullptr); } editor->setText(QString(text->GetCStr())); } } } } // Size adjustSize(); setMinimumSize(size()); // Focus on first empty editor if (edited_languages.size()) { edited_languages.front().value_editor->setFocus(); // fallback to first editor for (const auto & langs : edited_languages) { if (!langs.value_editor->text().length()) { langs.value_editor->setFocus(); break; } } } }
C4PropList *C4Def::GetActionByName(C4String *actname) { assert(actname); // If we get the null string or ActIdle by name, return NULL action if (!actname || actname == &Strings.P[P_Idle]) return NULL; // otherwise, query actmap C4Value ActMap; GetProperty(P_ActMap, &ActMap); if (!ActMap.getPropList()) return NULL; C4Value Action; ActMap.getPropList()->GetPropertyByS(actname, &Action); if (!Action.getPropList()) return NULL; return Action.getPropList(); }
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)); } }
void C4ParticleValueProvider::SetParameterValue(int type, const C4Value &value, float C4ParticleValueProvider::*floatVal, int C4ParticleValueProvider::*intVal, size_t keyFrameIndex) { // just an atomic data type if (value.GetType() == C4V_Int) { if (type == VAL_TYPE_FLOAT) this->*floatVal = (float)value.getInt(); else if (type == VAL_TYPE_INT) this->*intVal = value.getInt(); else if (type == VAL_TYPE_KEYFRAMES) this->keyFrames[keyFrameIndex] = (float)value.getInt(); } else if (value.GetType() == C4V_Array) { // might be another value provider! C4ParticleValueProvider *child = new C4ParticleValueProvider(); childrenValueProviders.push_back(child); child->Set(*value.getArray()); child->typeOfValueToChange = type; if (type == VAL_TYPE_FLOAT) { child->floatValueToChange = floatVal; } else if (type == VAL_TYPE_INT) { child->intValueToChange = intVal; } else if (type == VAL_TYPE_KEYFRAMES) { child->keyFrameIndex = keyFrameIndex; } } else // invalid { if (type == VAL_TYPE_FLOAT) this->*floatVal = 0.f; else if (type == VAL_TYPE_INT) this->*intVal = 0; else if (type == VAL_TYPE_KEYFRAMES) this->keyFrames[keyFrameIndex] = 0.f; } }
void C4GraphicsOverlay::UpdateFacet() { // special: Nothing to update for object and pSourceGfx may be NULL // If there will ever be something to init here, UpdateFacet() will also need to be called when objects have been loaded if (eMode == MODE_Object) return; // otherwise, source graphics must be specified if (!pSourceGfx) return; C4Def *pDef = pSourceGfx->pDef; assert(pDef); fZoomToShape = false; // Clear old mesh instance, if any delete pMeshInstance; pMeshInstance = NULL; // update by mode switch (eMode) { case MODE_None: break; case MODE_Base: // def base graphics if (pSourceGfx->Type == C4DefGraphics::TYPE_Bitmap) fctBlit.Set(pSourceGfx->GetBitmap(), 0, 0, pDef->Shape.Wdt, pDef->Shape.Hgt, pDef->Shape.x+pDef->Shape.Wdt/2, pDef->Shape.y+pDef->Shape.Hgt/2); else if (pSourceGfx->Type == C4DefGraphics::TYPE_Mesh) pMeshInstance = new StdMeshInstance(*pSourceGfx->Mesh, 1.0f); break; case MODE_Action: // graphics of specified action { // Clear old facet fctBlit.Default(); // Ensure there is actually an action set if (!Action[0]) return; C4Value v; pDef->GetProperty(P_ActMap, &v); C4PropList *actmap = v.getPropList(); if (!actmap) return; actmap->GetPropertyByS(::Strings.RegString(Action), &v); C4PropList *action = v.getPropList(); if (!action) return; if (pSourceGfx->Type == C4DefGraphics::TYPE_Bitmap) { fctBlit.Set(pSourceGfx->GetBitmap(), action->GetPropertyInt(P_X), action->GetPropertyInt(P_Y), action->GetPropertyInt(P_Wdt), action->GetPropertyInt(P_Hgt)); // FIXME: fctBlit.TargetX has to be set here } else if (pSourceGfx->Type == C4DefGraphics::TYPE_Mesh) { C4String* AnimationName = action->GetPropertyStr(P_Animation); if (!AnimationName) return; pMeshInstance = new StdMeshInstance(*pSourceGfx->Mesh, 1.0f); const StdMeshAnimation* Animation = pSourceGfx->Mesh->GetSkeleton().GetAnimationByName(AnimationName->GetData()); if (!Animation) return; pMeshInstance->PlayAnimation(*Animation, 0, NULL, new C4ValueProviderRef<int32_t>(iPhase, ftofix(Animation->Length / action->GetPropertyInt(P_Length))), new C4ValueProviderConst(itofix(1)), true); } break; } case MODE_ObjectPicture: // ingame picture of object // calculated at runtime break; case MODE_IngamePicture: case MODE_Picture: // def picture fZoomToShape = true; // drawn at runtime break; case MODE_ExtraGraphics: // like ColorByOwner-sfc // calculated at runtime break; case MODE_Rank: // drawn at runtime break; case MODE_Object: // TODO break; } }
bool operator ()(const C4Value &v1, const C4Value &v2) { return rSorter.Compare(v1._getObj(), v2._getObj()) > 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; } }
bool operator ()(const C4Value &v1, const C4Value &v2) { return value_sort(v1._getArray()->GetItem(element_idx), v2._getArray()->GetItem(element_idx)); }
bool C4FindObjectProperty::Check(C4Object *pObj) { assert(Name); // checked in constructor C4Value value; return pObj->GetPropertyByS(Name, &value) && value.getBool(); }
bool operator ()(const C4Value &v1, const C4Value &v2) { return v1.getStr() && v2.getStr() && v1._getStr()->GetData() < v2._getStr()->GetData(); }
bool operator ()(const C4Value &v1, const C4Value &v2) { if (v1.getStr() && v2.getStr()) return std::strcmp(v1._getStr()->GetCStr(), v2._getStr()->GetCStr()) < 0; return v2.getStr(); }
C4AulDebug::ProcessLineResult C4AulDebug::ProcessLine(const StdStrBuf &Line) { // Get command StdStrBuf Cmd; Cmd.CopyUntil(Line.getData(), ' '); // Get data const char *szData = Line.getPtr(Cmd.getLength()); if (*szData) szData++; // Identify command const char *szCmd = Cmd.getData(); if (SEqualNoCase(szCmd, "HELP")) return ProcessLineResult(false, "Yeah, like I'm going to explain that /here/"); else if (SEqualNoCase(szCmd, "BYE") || SEqualNoCase(szCmd, "QUIT")) C4NetIOTCP::Close(PeerAddr); else if (SEqualNoCase(szCmd, "SAY")) ::Control.DoInput(CID_Message, new C4ControlMessage(C4CMT_Normal, szData), CDT_Direct); else if (SEqualNoCase(szCmd, "CMD")) ::MessageInput.ProcessCommand(szData); else if (SEqualNoCase(szCmd, "STP") || SEqualNoCase(szCmd, "S")) eState = DS_Step; else if (SEqualNoCase(szCmd, "GO") || SEqualNoCase(szCmd, "G")) eState = DS_Go; else if (SEqualNoCase(szCmd, "STO") || SEqualNoCase(szCmd, "O")) eState = DS_StepOver; else if (SEqualNoCase(szCmd, "STR") || SEqualNoCase(szCmd, "R")) eState = DS_StepOut; else if (SEqualNoCase(szCmd, "EXC") || SEqualNoCase(szCmd, "E")) { C4AulScriptContext* context = pExec->GetContext(pExec->GetContextDepth()-1); int32_t objectNum = C4ControlScript::SCOPE_Global; if (context && context->Obj && context->Obj->GetObject()) objectNum = context->Obj->GetObject()->Number; ::Control.DoInput(CID_Script, new C4ControlScript(szData, objectNum, true), CDT_Decide); } else if (SEqualNoCase(szCmd, "PSE")) if (Game.IsPaused()) { Game.Unpause(); return ProcessLineResult(true, "Game unpaused."); } else { Game.Pause(); return ProcessLineResult(true, "Game paused."); } else if (SEqualNoCase(szCmd, "LST")) { for (C4AulScript* script = ScriptEngine.Child0; script; script = script->Next) { SendLine(RelativePath(script->ScriptName)); } } // toggle breakpoint else if (SEqualNoCase(szCmd, "TBR")) { using namespace std; // FIXME: this doesn't find functions which were included/appended string scriptPath = szData; size_t colonPos = scriptPath.find(':'); if (colonPos == string::npos) return ProcessLineResult(false, "Missing line in breakpoint request"); int line = atoi(&scriptPath[colonPos+1]); scriptPath.erase(colonPos); C4AulScript *script; for (script = ScriptEngine.Child0; script; script = script->Next) { if (SEqualNoCase(RelativePath(script->ScriptName), scriptPath.c_str())) break; } auto sh = script ? script->GetScriptHost() : NULL; if (sh) { C4AulBCC * found = NULL; for (auto script = ::ScriptEngine.Child0; script; script = script->Next) for (C4PropList *props = script->GetPropList(); props; props = props->GetPrototype()) for (auto fname = props->EnumerateOwnFuncs(); fname; fname = props->EnumerateOwnFuncs(fname)) { C4Value val; if (!props->GetPropertyByS(fname, &val)) continue; auto func = val.getFunction(); if (!func) continue; auto sfunc = func->SFunc(); if (!sfunc) continue; if (sfunc->pOrgScript != sh) continue; for (auto chunk = sfunc->GetCode(); chunk->bccType != AB_EOFN; chunk++) { if (chunk->bccType == AB_DEBUG) { int lineOfThisOne = sfunc->GetLineOfCode(chunk); if (lineOfThisOne == line) { found = chunk; goto Found; } } } } Found: if (found) found->Par.i = !found->Par.i; // activate breakpoint else return ProcessLineResult(false, "Can't set breakpoint (wrong line?)"); } else return ProcessLineResult(false, "Can't find script"); } else if (SEqualNoCase(szCmd, "SST")) { std::list<StdStrBuf*>::iterator it = StackTrace.begin(); for (it++; it != StackTrace.end(); it++) { SendLine("AT", (*it)->getData()); } SendLine("EST"); } else if (SEqualNoCase(szCmd, "VAR")) { C4Value *val = NULL; int varIndex; C4AulScriptContext* pCtx = pExec->GetContext(pExec->GetContextDepth() - 1); if (pCtx) { if ((varIndex = pCtx->Func->ParNamed.GetItemNr(szData)) != -1) { val = &pCtx->Pars[varIndex]; } else if ((varIndex = pCtx->Func->VarNamed.GetItemNr(szData)) != -1) { val = &pCtx->Vars[varIndex]; } } const char* typeName = val ? GetC4VName(val->GetType()) : "any"; StdStrBuf output = FormatString("%s %s %s", szData, typeName, val ? val->GetDataString().getData() : "Unknown"); SendLine("VAR", output.getData()); } else return ProcessLineResult(false, "Can't do that"); return ProcessLineResult(true, ""); }