#strict 2 static const Object_Status = 0; static const Object_Name = 1; static const Object_Owner = 2; static const Object_Controller = 3; static const Object_Killer = 4; static const Object_Category = 5; static const Object_Position = 6; static const Object_Speed = 7; static const Object_Con = 8; static const Object_OwnMass = 9; static const Object_Damage = 10; static const Object_Energy = 11; static const Object_MagicEnergy = 12; static const Object_Alive = 13; static const Object_Breath = 14; static const Object_ColorDw = 15; static const Object_Shape = 16; static const Object_Vertices = 17; static const Object_SolidMask = 18; static const Object_Picture = 19; static const Object_Entrance = 20; static const Object_Physicals = 21; static const Object_Action = 22; static const Object_Components = 23; static const Object_Contents = 24; static const Object_PlrViewRange = 25; static const Object_Visibility = 26; static const Object_BlitMode = 27; static const Object_CrewEnabled = 28; static const Object_ObjectLayer = 29; static const Object_Locals = 30; static const Object_Graphics = 31; static const Object_Effects = 32; static const Object_ID = 33; static const Object_ContactDensity = 34; static const Object_Dir = 35; static const Object_ComDir = 36; static const Object_Origin = 37; static const Object_Custom = 38; static const Object_CrewPlayer = 39; static const Object_Count = 40; // DONT KNOW: OnFire, Commands global func& LocalX(name, object obj) { obj = obj || this; if(GetType(name) == C4V_String) { return LocalN(name, obj); } else { return Local(name, obj); } } global func GetEffectVarCount(object target, int effectNumber) // WARNING: this could eventually be wrong (with a very very small probability) when some EffectVars are set right coincidentally { // based on and inspired by C4Effect::CompileFunc var name = GetEffect(0, target, effectNumber, 1); var prio = GetEffect(0, target, effectNumber, 2); var timer = GetEffect(0, target, effectNumber, 3); var lifeTime = GetEffect(0, target, effectNumber, 6); if(!name) { return -1; } // determine offset for this effect within GetObjectVal for(var i = 0; ; ++i) { if(GetObjectVal("Effects", 0, target, i) == name && GetObjectVal("Effects", 0, target, i + 1) == effectNumber && GetObjectVal("Effects", 0, target, i + 2) == prio && GetObjectVal("Effects", 0, target, i + 3) == lifeTime && GetObjectVal("Effects", 0, target, i + 4) == timer) { var count = GetObjectVal("Effects", 0, target, i + 7); if(count == 1) { var nextEffectName = GetObjectVal("Effects", 0, target, i + 8); if(GetType(nextEffectName) == C4V_String) { var nextNumber = GetObjectVal("Effects", 0, target, i + 9); if(GetType(nextNumber) == C4V_Int && GetEffect(0, target, nextNumber, 1) == nextEffectName) { return 0; } } } return GetType(count) == C4V_Int && count; } } } global func GetObjectSaveData(object obj) { obj = obj || this; var id = GetID(obj); var owner = GetOwner(obj); var ret = CreateArray(Object_Count); ret[Object_ID] = id; ret[Object_Origin] = obj; ret[Object_Status] = GetObjectStatus(obj); if(GetName(obj) != GetName(0, id)) { ret[Object_Name] = GetName(obj); } ret[Object_Owner] = owner; ret[Object_Controller] = GetController(obj); ret[Object_Killer] = GetKiller(obj); ret[Object_Category] = GetCategory(obj); ret[Object_Position] = [GetX(obj), GetY(obj), GetR(obj)]; ret[Object_Speed] = [GetXDir(obj, 65536), GetYDir(obj, 65536), GetRDir(obj, 65536)]; ret[Object_Con] = GetCon(obj); ret[Object_OwnMass] = GetMass(obj) - GetMass(0, id); // maybe save complete Mass instead? ret[Object_Damage] = GetDamage(obj); ret[Object_Energy] = GetObjectVal("Energy", 0, obj); ret[Object_MagicEnergy] = GetMagicEnergy(obj); ret[Object_Alive] = GetAlive(obj); ret[Object_Breath] = GetBreath(obj); ret[Object_ColorDw] = GetColorDw(obj); ret[Object_Shape] = [GetObjectVal("Offset", 0, obj, 0), GetObjectVal("Offset", 0, obj, 1), GetObjectVal("Width", 0, obj), GetObjectVal("Height", 0, obj)]; if(GetObjectVal("OwnVertices", 0, obj)) { var vertexNum = GetVertexNum(obj); var vertices = CreateArray(vertexNum); for(var i = 0; i < vertexNum; ++i) { vertices[i] = [GetVertex(i, VTX_X, obj), GetVertex(i, VTX_Y, obj), GetVertex(i, VTX_CNAT, obj), GetVertex(i, VTX_Friction, obj)]; } ret[Object_Vertices] = vertices; } var crewPlayer = NO_OWNER; if(CrewMember(obj)) { for(var i = 0; i < GetCrewCount(owner); ++i) { if(GetCrew(owner, i) == obj) { crewPlayer = owner; break; } } } ret[Object_CrewPlayer] = crewPlayer; ret[Object_SolidMask] = GetSolidMask(obj); ret[Object_Picture] = [GetObjectVal("Picture", 0, obj, 0), GetObjectVal("Picture", 0, obj, 1), GetObjectVal("Picture", 0, obj, 2), GetObjectVal("Picture", 0, obj, 3)]; ret[Object_Entrance] = GetEntrance(obj); var action = GetAction(obj); ret[Object_Action] = [action, GetPhase(obj), GetActionTarget(0, obj), GetActionTarget(1, obj), GetObjectVal("ActionData", 0, obj)]; var defComponents = [], objComponents = []; for(var i = 0, componentID; componentID = GetComponent(0, i, 0, id); ++i) { var cnt = GetComponent(componentID, 0, 0, id); if(cnt != 0) { defComponents[] = [componentID, cnt]; } } for(var i = 0, componentID; componentID = GetComponent(0, i, obj); ++i) { var cnt = GetComponent(componentID, 0, obj); if(cnt != 0) { objComponents[] = [componentID, cnt]; } } var diffComponents = []; for(var i = 0; i < GetLength(defComponents); ++i) { var defID = defComponents[i][0], defCnt = defComponents[i][1]; var cnt = 0; for(var j = 0; j < GetLength(objComponents); ++j) { if(objComponents[j][0] == defID) { cnt = objComponents[j][1]; ArrayErase(objComponents, j); break; } } if(cnt != defCnt) { diffComponents[] = [defID, cnt]; } } ArrayAppendArray(diffComponents, objComponents); if(GetLength(diffComponents) != 0) { ret[Object_Components] = diffComponents; } var contents = []; for(var content in obj->GetContents()) { contents[] = GetObjectSaveData(content); } ret[Object_Contents] = contents; ret[Object_PlrViewRange] = GetObjectVal("PlrViewRange", 0, obj); ret[Object_Visibility] = GetVisibility(obj); ret[Object_BlitMode] = GetObjectBlitMode(obj); ret[Object_CrewEnabled] = GetCrewEnabled(obj); ret[Object_ObjectLayer] = GetObjectLayer(obj); ret[Object_Dir] = GetDir(obj); ret[Object_ComDir] = GetComDir(obj); ret[Object_ContactDensity] = GetObjectVal("ContactDensity", 0, obj); var locals = []; for(var f in [0, 1]) { var cnt = GetLocalCount(obj, f); for(var i = 0; i < cnt; ++i) { var name = GetLocalByIndex(i, obj, f); locals[] = [name, LocalX(name, obj)]; } } ret[Object_Locals] = locals; var overlays = [[GetGraphics(1, obj, 0), GetGraphics(0, obj, 0), 0, GetGraphics(2, obj, 0), GetGraphics(3, obj, 0), GetGraphics(4, obj, 0), 0, obj->GetObjDrawTransform(0), GetClrModulation(obj, 0)]]; var offset = 0, diffIndex = 0; while((offset = obj->GetOverlayValueByIndex(diffIndex, offset)) >= 0) { var overlayID = obj->GetOverlayValueByIndex(0, offset, 1); overlays[] = [obj->GetOverlayValueByIndex(0, offset, 4), obj->GetOverlayValueByIndex(0, offset, 3), overlayID, obj->GetOverlayValueByIndex(0, offset, 2), obj->GetOverlayValueByIndex(0, offset, 5), obj->GetOverlayValueByIndex(0, offset, 6), Object(obj->GetOverlayValueByIndex(0, offset, 8)), obj->GetObjDrawTransform(-1, offset), GetClrModulation(obj, overlayID)]; diffIndex = 1; } if(GetActMapVal("FlipDir", action, id) && GetDir(obj) == DIR_Right) { overlays[0][7][0] *= -1; } ret[Object_Graphics] = overlays; var physicals = []; // TODO: Think about StackTemporary? What about Effects? Maybe pop the whole stack while saving and restore afterwards for(var physical in ["Energy", "Breath", "Walk", "Jump", "Scale", "Hangle", "Dig", "Swim", "Throw", "Push", "Fight", "Magic", "Float", "CanScale", "CanHangle", "CanDig", "CanConstruct", "CanChop", "CanSwimDig", "CanFly", "CorrosionResist", "BreatheWater"]) { var value = GetPhysical(physical, PHYS_Current, obj); if(value != GetPhysical(physical, 0, 0, id)) { physicals[] = [physical, value]; } } if(GetLength(physicals) != 0) { ret[Object_Physicals] = physicals; } var effects = []; for(var i = 0, index; index = GetEffect("*", obj, i, 0); ++i) { var varCnt = GetEffectVarCount(obj, index); var effectVars = CreateArray(varCnt); for(var j = 0; j < varCnt; ++j) { effectVars[j] = EffectVar(j, obj, index); } effects[i] = [GetEffect(0, obj, index, 1), GetEffect(0, obj, index, 2), GetEffect(0, obj, index, 3), GetEffect(0, obj, index, 4), GetEffect(0, obj, index, 5), GetEffect(0, obj, index, 6), effectVars, index]; } if(GetLength(effects) != 0) { ret[Object_Effects] = effects; } // TODO: Custom return ret; } global func ApplyObjectSaveData(array data, object obj) { obj = obj || this; // set values which can trigger callbacks before changing def obj->SetObjectStatus(data[Object_Status]); obj->SetCon(data[Object_Con]); obj->SetOwner(data[Object_Owner]); obj->DoDamage(data[Object_Damage] - obj->GetDamage()); if(data[Object_Energy]) { if(GetPhysical("Energy", PHYS_Current, obj) < data[Object_Energy]) { SetPhysical("Energy", data[Object_Energy], PHYS_Temporary, obj); } obj->DoEnergy(data[Object_Energy] - GetObjectVal("Energy", 0, obj), obj, true); } for(var content in data[Object_Contents]) { var temp = obj->CreateContents(ROCK); temp->ApplyObjectSaveData(content); } obj->ChangeDef(data[Object_ID]); // the following changes should not call Callbacks anymore if(data[Object_CrewPlayer] != NO_OWNER) { MakeCrewMember(obj, data[Object_CrewPlayer]); } obj->SetName(data[Object_Name]); obj->SetController(data[Object_Controller]); obj->SetKiller(data[Object_Killer]); obj->SetCategory(data[Object_Category]); obj->SetPosition(data[Object_Position][0], data[Object_Position][1]); obj->SetR(data[Object_Position][2]); SetXDir(data[Object_Speed][0], obj, 65536); SetYDir(data[Object_Speed][1], obj, 65536); SetRDir(data[Object_Speed][2], obj, 65536); obj->SetMass(data[Object_OwnMass] - obj->GetMass()); obj->SetColorDw(data[Object_ColorDw]); obj->SetShape(data[Object_Shape][0], data[Object_Shape][1], data[Object_Shape][2], data[Object_Shape][3]); obj->SetSolidMask(data[Object_SolidMask][0], data[Object_SolidMask][1], data[Object_SolidMask][2], data[Object_SolidMask][3], data[Object_SolidMask][4], data[Object_SolidMask][5]); obj->SetPicture(data[Object_Picture][0], data[Object_Picture][1], data[Object_Picture][2], data[Object_Picture][3]); obj->SetAlive(data[Object_Alive]); obj->DoMagicEnergy(data[Object_MagicEnergy] - obj->GetMagicEnergy()); obj->DoBreath(data[Object_Breath] - obj->GetBreath()); obj->SetEntrance(data[Object_Entrance]); if(data[Object_Vertices]) { var cnt = GetLength(data[Object_Vertices]); while(obj->GetVertexNum() < cnt) { obj->AddVertex(); } var i = 0; for(var vertex in data[Object_Vertices]) { obj->SetVertex(i, VTX_X, vertex[0], VTX_SetPermanent); obj->SetVertex(i, VTX_Y, vertex[1], VTX_SetPermanent); obj->SetVertex(i, VTX_CNAT, vertex[2], VTX_SetPermanent); obj->SetVertex(i, VTX_Friction, vertex[3], (i == cnt - 1 && VTX_SetPermanentUpd) || VTX_SetPermanent); ++i; } } var id = data[Object_ID]; for(var physical in ["Energy", "Breath", "Walk", "Jump", "Scale", "Hangle", "Dig", "Swim", "Throw", "Push", "Fight", "Magic", "Float", "CanScale", "CanHangle", "CanDig", "CanConstruct", "CanChop", "CanSwimDig", "CanFly", "CorrosionResist", "BreatheWater"]) { obj->SetPhysical(physical, GetPhysical(physical, 0, 0, id), PHYS_Temporary); } if(data[Object_Physicals]) // TODO: Update for StackTemporary and try to represent right mode { for(var physical in data[Object_Physicals]) { obj->SetPhysical(physical[0], physical[1], PHYS_Temporary); } } for(var localVar in data[Object_Locals]) { LocalX(localVar[0]) = localVar[1]; } if(data[Object_Components]) { for(var component in data[Object_Components]) { obj->SetComponent(component[0], component[1]); } } SetPlrViewRange(data[Object_PlrViewRange], obj, true); obj->SetVisibility(data[Object_Visibility]); obj->SetObjectBlitMode(data[Object_BlitMode]); obj->SetCrewEnabled(data[Object_CrewEnabled]); obj->SetObjectLayer(data[Object_ObjectLayer]); obj->SetContactDensity(data[Object_ContactDensity]); obj->SetComDir(data[Object_ComDir]); // unfortunately there is no SetAction without triggering CallCallbacks // SetAction at last, so the important stuff is setup already obj->SetAction(data[Object_Action][0], data[Object_Action][2], data[Object_Action][3], true); obj->SetPhase(data[Object_Action][1]); obj->SetActionData(data[Object_Action][4]); obj->SetDir(data[Object_Dir]); // Set Dir after Action because Dirs are enabled per action for(var graphic in data[Object_Graphics]) { SetGraphics(graphic[0], obj, graphic[1], graphic[2], graphic[3], graphic[4], graphic[5], graphic[6]); var transform = graphic[7]; if(GetLength(transform) == 6) { SetObjDrawTransform(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5], obj, graphic[2]); } else { SetObjDrawTransform(1000, 0, 0, 0, 1000, 0, obj, graphic[2]); obj->SetObjDrawTransform2(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5], transform[6], transform[7], transform[8], graphic[2]); } SetClrModulation(graphic[8], obj, graphic[2]); } if(data[Object_Effects]) { // WARNING: Can't restore lifetime for(var effect in data[Object_Effects]) { var temp = AddEffect("ApplyEffectDummy", obj, effect[1], effect[2], effect[3], effect[4]); for(var i = 0; i < GetLength(effect[6]); ++i) { EffectVar(i, obj, temp) = effect[6][i]; } ChangeEffect(0, obj, temp, effect[0], -1); } } // TODO: Custom } global func InstantiateObjectSaveData(array data) { var ret = CreateObject(ROCK); ret->ApplyObjectSaveData(data); return ret; }