-
Content count
110 -
Joined
-
Last visited
Community Reputation
25 Very GoodAbout Mjjstral
-
Rank
Specialist
Contact Methods
- Website URL
Profile Information
-
Location
Germany
-
Interests
philosophy, border science, science fiction
Command & Conquer Profile
-
Favourite C&C
Kanes Wrath
Recent Profile Visitors
-
Mjjstral started following Iron Curtain Harvester support?, CNC4 Engine Essential Patch, How does the ScriptTimer in metaMod work? and and 6 others
-
CNC4 Engine Essential Patch 1.05 by Mjstral aka MetaIdea - 60 FPS with proper gamespeed - Unlimited Camera Zoom - Much higher unit and structure build limit (500 command and ability points) - Fix for lua command ExecuteAction Usage: Copy the CNC4.game file to "Command and Conquer 4 Tiberian Twilight\Data\" and replace the file (make a backup before). Also works with any version (Steam/Origin). There is a optional version without higher command/ability points as well you can get on the discord below since I can't upload more than 8.52mb here. Sidenote: All of these changes and including support for all other SAGE games are also part of SageMetaTool engine extender (early access available for patreons). Discussion/questions and updates: Sage Modding Discord CNC4-EnginePatch-1.05-Mjstral.zip
-
Is there any way to save a global variable and its value to a SavedGame file and have them loaded later?
Mjjstral replied to StealthIsKey's topic in Tiberium Wars / Kane's Wrath Maps & Modding
http://lua-users.org/wiki/PersistentTables Usage as mentioned in the examples: make_persistent(globals(),"C:/Anywhere/") Also here is a prototype I made myself quickly that creates one lua savefile. You can then recreate the savestate with dofile(savefile). function SaveLuaState(savepath) local Save = "" for index,value in globals() do if type(value) == "number" then Save = Save .. index .. " = " .. value .."\n" elseif type(value) == "string" then if strfind(value,"\\") or strfind(value,"\n") then Save = Save .. index .. " = " .. strrep(strchar(91),2) .. value .. strrep(strchar(93),2) .. "\n" else Save = Save .. index .. " = " .. "'" .. value .. "'" .. "\n" end elseif type(value) == "table" and not strfind(index,"ObjID") then Save = Save .. index .. " = {" for t_index,t_value in value do if type(t_value) == "number" then if type(t_index) == "number" then Save = Save .. "\t[" .. t_index .. "]=" .. t_value .. "," elseif type(t_index) == "string" then Save = Save .. "\t['" .. t_index .. "']=" .. t_value .. "," end elseif type(t_value) == "string" then if type(t_index) == "number" then Save = Save .. "\t[" .. t_index .. "]='" .. t_value .. "'," elseif type(t_index) == "string" then Save = Save .. "\t['" .. t_index .. "']='" .. t_value .. "'," end else --Save = Save .. "\n\t['" .. t_index .. "']=" .. t_value .. "," end end Save = Save .. "}\n" end end local filehandle = openfile(savepath, 'w') if filehandle ~= nil then write(filehandle, Save) flush(filehandle) closefile(filehandle) end end Next step is a lua savefile to game savefile identifier function that then loads the lua save. It could simply be executed in the ususal scipts.lua environment. If your patient I will provide all that with the "Meta" dll framework. It hooks the save function and cares for everything automatically so modders doesn't have to care anymore about lost lua vars in saves.- 3 replies
-
- global variable
- coding
-
(and 1 more)
Tagged with:
-
How does the ScriptTimer in metaMod work?
Mjjstral replied to pengion's topic in Tiberium Wars / Kane's Wrath Maps & Modding
Nice ! Will probably put it on github at some point Yes see this question. In the long run extending the actual savegame routine would be the way to go I admit. As I have written this is how it will work in sage meta tool (unreleased yet). The game is not able to handle coord3d parameters from lua in any way so I had to write a new parameter parser for it. Until then use the old method but keep in mind it depends on objects and ocls (not pure lua). -
How does the ScriptTimer in metaMod work?
Mjjstral replied to pengion's topic in Tiberium Wars / Kane's Wrath Maps & Modding
You first need to initialize the global counters by using InitializeGCounters(). This will setup 10 counters with values 1 to 10. SetCounter will then do math with these global counters to get your desired counter value. This is because the regular counter commands for some reason don't work. So this is a workaround that works great. This is a compilcated monster that fortunately gets replaced in the sage meta tool project with a muuuch faster and more convenient solution. This old method works like that in short: Spawns ocl dummy anchors with positional offsets (defined in ocl) on itself again and again until the desired position is reached. Then it uses "CREATE_NAMED_ON_TEAM_AT_OBJECTTYPE" to spawn the actual object on the invisible anchor. It also uses a self made lua stack system to process multiple spawn requests because that whole method gets not executed between two frames. The new method will work like that with a COORD3D lua table {x,y,z}: ExecuteAction("UNIT_SPAWN_NAMED_LOCATION_ORIENTATION", ObjectName, ObjectType, Team, {PositionX, PositionY, PositionZ}, Orientation) Yes completely new UI and a parser to show infos about these gamemodes + update functionality (pastebin dl based). -
How does the ScriptTimer in metaMod work?
Mjjstral replied to pengion's topic in Tiberium Wars / Kane's Wrath Maps & Modding
Hi pengion, sorry for the late reply ! So I'm not sure how exactly you make the tower defense map and if you use the in the meta mod sdk pdf described procedure to make special meta mod tower defense maps or some variant of your own so I have to give you a general answer: First you set your function that gets executed every second and checks for the timer (assuming you have only one timer set). TIME_THRESHOLD = 120 function Coroutine() for counter,time in TimerTable do if time <= TIME_THRESHOLD then --your action here end end end Next step is to give it to the script timer so that this function above gets executed every second: SetScriptTimer(1,Coroutine,-1) The last argument is the number of executions. -1 means execute infinitely. The first argument is the time intervall between each execution. Note you can also create custom gamemode luas as descibed in the meta mod manual. Btw I'm working on a new successor project that will come as a dll mod and will in particular support new custom gamemodes by a new skirmish setup submenu (news on the sage modding discord). -
Is there any way to save a global variable and its value to a SavedGame file and have them loaded later?
Mjjstral replied to StealthIsKey's topic in Tiberium Wars / Kane's Wrath Maps & Modding
To answer your question, yes you can make custom savegames with lua file io commands. If you use a timestamp you can load exactly the state you need. I made custom savegames in many variations in metamod (base templates, auto start config, stats save) . The source code is completely open so have a look and report how that works out. The main function you will have to work with is WriteToFile and LoadFile (from meta mod).- 3 replies
-
- global variable
- coding
-
(and 1 more)
Tagged with:
-
Map-wide Random AI / Neutral spawner
Mjjstral replied to Nowaru's topic in Tiberium Wars / Kane's Wrath Maps & Modding
I've build a spawner system in meta mod with lua. You can incorporate it into your mod. For an example see the meta menu spawner submenu that has a random position option to spawn any object. Initial load mechanism is as follows: playertemplate invisible start object dummy OR spellbook -> OnCreate lua event -> lua random spawn function -
AISpecialPowerUpdate chance
Mjjstral replied to Nowaru's topic in Tiberium Wars / Kane's Wrath Maps & Modding
You need to know one thing: You can theoretically disable the AI and completely rebuild it in lua. That means you can also mimic only parts of it like the desire you mentioned above. https://pastebin.com/xHHGCVZ1 -
Kanes Wrath InGameUISettings Schemas
Mjjstral replied to TheHostileNegotiator's topic in Tiberium Wars / Kane's Wrath Maps & Modding
Hey really nice to see that file to be useable now. Do you or someone else know if we can change the text offset of "military captions" with that file. I need that for my metamod to hide the "missing:..." string in the meta command menu. Moving the text up so that it moves out of the visible screen would be great. -
WrathEd executable issue
Mjjstral replied to Nowaru's topic in Tiberium Wars / Kane's Wrath Maps & Modding
Clear the Compilation folder (WrathEDSDK_Folder/Compilation/Mods/YourMod). If it is path related, move the SDK folder to the root of C drive and try again. Maybe also delete the userdata WrathED creates (Documents\WrathEd). -
Iron Curtain Harvester support?
Mjjstral replied to Madin's topic in Tiberium Wars / Kane's Wrath Maps & Modding
This should fix it. It's now more error robust in general: IronCurtain = {} IronCurtain["Targets"] = {} IronCurtain["BuildingRef"] = {} IronCurtain["Ready"] = {} function OnIronCurtainBuildingCreated(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = self if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainBuildingDestroyed(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = nil IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainReady(self) IronCurtain["Ready"][ObjectTeamName(self)] = true if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end if IronCurtain["Targets"][ObjectTeamName(self)] and getn(IronCurtain["Targets"][ObjectTeamName(self)]) > 0 and not ObjectTestModelCondition(self,"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(IronCurtain["Targets"][1],"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(self),"SpecialPower_IronCurtain",GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1])) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25Percent(self) if IronCurtain["Targets"][ObjectTeamName(self)] == nil then IronCurtain["Targets"][ObjectTeamName(self)] = {} end tinsert(IronCurtain["Targets"][ObjectTeamName(self)],self) if IronCurtain["BuildingRef"][ObjectTeamName(self)] and IronCurtain["Ready"][ObjectTeamName(self)] and not ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(self,"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(IronCurtain["BuildingRef"][ObjectTeamName(self)]),"SpecialPower_IronCurtain",GetObj.String(self)) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead(self) local Index = GetIndexOfTableElement(IronCurtain["Targets"][ObjectTeamName(self)],self) if Index then tremove(IronCurtain["Targets"][ObjectTeamName(self)], Index) end end function GetIndexOfTableElement(table,element) if not table then return nil end for i=1,getn(table),1 do if table[i]==element then return i end end end Hm I'm also on win7... Shouldn't be system dependent acually. Why does the lua code pasted in these forum text boxes appear so whitish and unreadable ? -
Connecting Objects Problem / Logic Circuits Mod
Mjjstral posted a topic in Tiberium Wars / Kane's Wrath Maps & Modding
I'm currently trying to create a prototype of a logic circuits mod for Kane's Wrath. You will basically have different elements you can connect via cables. One Example: A "switch" and an "enemy detector" connected via an AND gate and the output cable leads to an "sound alarm" element. Functionally it will look like in games like Minecraft (redstone logic) or Starmade, see here: Now my problem: I need to connect the elements somehow via cables creating a hook up, latch or click in effect that somehow (likewise via modelcondition) triggers a lua event. I will handle the whole logic in lua then. Let's brainstorm ! Also what parts do you think should be added ? (so far on my list: lua computer terminal (like in ComputerCraft, each computer with own lua file), Gates, Structure Status Detector, AND, OR, NOT, switch, toggle switch, sound alarm, structure enabler/disabler, structure command giver (build commands...), unit command giver, laser trigger, team command giver, unit counter, seller, repairer, light (with rgb code connectors), text displayer) If someone wants to help me especially with the model creation and also xml coding wise (beside this forum), feel free to contact me (also consider joining the SAGE modding discord). -
Iron Curtain Harvester support?
Mjjstral replied to Madin's topic in Tiberium Wars / Kane's Wrath Maps & Modding
And here is an alternative I made earlier but you can disregard. It uses lua script timers (meta mod) and gets the harvester refs automatically. So it's a more lua self contained solution. IronCurtainBuildingList = {} function IronCurtainBuildingOnCreated(self) tinsert(IronCurtainBuildingList,self) SetScriptTimer(1,IronCurtainAttackManagement) end function IronCurtainBuildingOnDestroyed(self) local Index = GetIndexOfTableElement(IronCurtainBuildingList,self) if Index then tremove(IronCurtainBuildingList, Index) end DeleteScriptTimerAction(IronCurtainAttackManagement) end function IronCurtainBuildingOnPowerOutage(self) local Index = GetIndexOfTableElement(IronCurtainBuildingList,self) if Index then tremove(IronCurtainBuildingList, Index) end DeleteScriptTimerAction(IronCurtainAttackManagement) end function IronCurtainBuildingOnPowerRestore(self) tinsert(IronCurtainBuildingList,self) SetScriptTimer(1,IronCurtainAttackManagement) end IronCurtainSchedule = {} function IronCurtainAttackManagement() local IronCurtainObjectWhiteList = GetObjectTypeListForTeam(team,AddToObjectTypeList("HarvesterTypeList","GDIHarvester","NODHarvester","ALIENHarvester")) for i=1,getn(IronCurtainBuildingList),1 do if IronCurtainSchedule[IronCurtainBuildingList[i]] == 0 and not ObjectTestModelCondition(GetObj.Table(IronCurtainBuildingList[i]),"UNDERPOWERED") then for j=1,getn(IronCurtainObjectWhiteList),1 do if GetHealth(IronCurtainObjectWhiteList[j].ref) <= 25 then ObjectCreateAndFireTempWeapon(IronCurtainObjectWhiteList[j].ref,"IronCurtainWeapon") --ExecuteAction("NAMED_USE_COMMANDBUTTON_ABILITY_ON_NAMED", IronCurtainBuildingList[i], "Command_IronCurtainWeapon", IronCurtainObjectWhiteList[j].ref) end end else IronCurtainSchedule[IronCurtainBuildingList[i]] = IronCurtainSchedule[IronCurtainBuildingList[i]] - 1 end end end function GetObjectTypeListForTeam(team,ObjectType) --ObjectType or ObjectTypeList local RefList = {} local ObjRef = "" local y = 1 local TempObjectRefTable={} ExecuteAction("SET_IGNORE_SKIRMISH_VICTORY_CONDITIONS", 1) repeat ObjRef=RandomString(5) ExecuteAction("TEAM_SET_PLAYERS_NEAREST_UNIT_OF_TYPE_TO_REFERENCE",ObjectType,team,ObjRef) if EvaluateCondition("NAMED_NOT_DESTROYED",ObjRef) then RefList[y]={} RefList[y]["ref"]=ObjRef RefList[y]["team"]=team RefList[y]["type"]=ObjectType RefList[y]["powered"]=1 tinsert(TempObjectRefTable,ObjRef) ExecuteAction("UNIT_SET_TEAM",ObjRef,"team") y=y+1 else break end until(not EvaluateCondition("NAMED_NOT_DESTROYED",ObjRef)) for j=1,getn(TempObjectRefTable),1 do ExecuteAction("UNIT_SET_TEAM",TempObjectRefTable[j],team) end ExecuteAction("SET_IGNORE_SKIRMISH_VICTORY_CONDITIONS", 0) return RefList end function AddToObjectTypeList(ObjectTypeList,...) for i=1,getn(arg),1 do ExecuteAction("OBJECTLIST_ADDOBJECTTYPE", ObjectTypeList, arg[i]) end return ObjectTypeList end function GetHealth(input,HealthType) local HealthCompareType = "" if HealthType == nil or HealthType == "unit" or HealthType == 1 then HealthCompareType = "UNIT_HEALTH" else HealthCompareType = "EVAL_TEAM_HEALTH" end for i=1,101,1 do if EvaluateCondition(HealthCompareType, input, CompareTable["<"], i) then return i-1 end end return 0 end function GetIndexOfTableElement(table,element) for i=1,getn(table),1 do if table[i]==element then return i end end end function GetObj.Table(object) if type(object) == "table" then return object else for k,v in globals() do if strfind(k,"ObjID") ~= nil and strfind(ObjectDescription(v),object) then return rawget(globals(),k) --v end end return object end end -
Iron Curtain Harvester support?
Mjjstral replied to Madin's topic in Tiberium Wars / Kane's Wrath Maps & Modding
My solution assumes that a player will only have 1 iron curtain building at the same time (code adaption for more than 1 possible). 1. Make it a special power that triggers a weapon and then an attribute modifier. Use Ravendarks idea to trigger a SPECIALPOWER1_READY lua event that calls the function OnIronCurtainReady. 2. In additon for the harvesters use these events to trigger the functions OnHarvesterHealth25Percent and OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead. <ModelConditionEvent Name="HEALTH_PERCENT_25"> <Conditions>+HEALTH_PERCENT_25</Conditions> </ModelConditionEvent> <ModelConditionEvent Name="HEALTH_PERCENT_25_NOT"> <Conditions>-HEALTH_PERCENT_25</Conditions> </ModelConditionEvent> 3. Now the lua code: IronCurtain = {} IronCurtain["Targets"] = {} IronCurtain["BuildingRef"] = {} IronCurtain["Ready"] = {} function OnIronCurtainBuildingCreated(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = self IronCurtain["Targets"][ObjectTeamName(self)] = {} IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainBuildingDestroyed(self) IronCurtain["BuildingRef"][ObjectTeamName(self)] = nil IronCurtain["Ready"][ObjectTeamName(self)] = false end function OnIronCurtainReady(self) IronCurtain["Ready"][ObjectTeamName(self)] = true if getn(IronCurtain["Targets"][ObjectTeamName(self)]) > 0 and ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(IronCurtain["Targets"][1],"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(self),"SpecialPower_IronCurtain",GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1])) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25Percent(self) tinsert(IronCurtain["Targets"][ObjectTeamName(self)],self) if IronCurtain["BuildingRef"][ObjectTeamName(self)] and IronCurtain["Ready"][ObjectTeamName(self)] and not ObjectTestModelCondition(IronCurtain["BuildingRef"][ObjectTeamName(self)],"UNDERPOWERED") then --ObjectCreateAndFireTempWeapon(self,"IronCurtainWeapon") ExecuteAction("NAMED_FIRE_SPECIAL_POWER_AT_NAMED",GetObj.String(IronCurtain["BuildingRef"][ObjectTeamName(self)]),"SpecialPower_IronCurtain",GetObj.String(self)) IronCurtain["Ready"][ObjectTeamName(self)] = false end end function OnHarvesterHealth25PercentNOT_OR_OnHarvesterDead(self) local Index = GetIndexOfTableElement(IronCurtain["Targets"][ObjectTeamName(self)],self) if Index then tremove(IronCurtain["Targets"][ObjectTeamName(self)], Index) end end function GetIndexOfTableElement(table,element) for i=1,getn(table),1 do if table[i]==element then return i end end end So you need in total 5 lua events (3 for the structure and 2 for the harvesters). 4. optionaly if you want to always attack the harbester with the lowest health from the pool of all harvesters that have health < 25: function GetMinHealthUnit(UnitTable) if getn(UnitTable) == 0 then return nil end local MinHealthUnitRef = UnitTable[1] local MinHealth = GetHealth(UnitTable[1]) for i=2,getn(UnitTable),1 do if GetHealth(UnitTable[i]) < MinHealth then MinHealthUnitRef = UnitTable[i] end end return MinHealthUnitRef end function GetHealth(Unit) local UnitRef = GetObj.String(Unit) for i=1,101,1 do if EvaluateCondition("UNIT_HEALTH", UnitRef, CompareTable["<"], i) then return i-1 end end return 0 end So replace GetObj.String(IronCurtain["Targets"][ObjectTeamName(self)][1]) with GetObj.String(GetMinHealthUnit(IronCurtain["Targets"][ObjectTeamName(self)])) -
Trying to get AI to load infantry into transports
Mjjstral replied to Madin's topic in Tiberium Wars / Kane's Wrath Maps & Modding
I found the error: "NAMED_EXIT_ALL" gives a command to the transporter to thorw out it's inmates. So becomes much more simple: function OnSovietHalfTrackReallyDamaged(self) --add the lua event to scriptevents.xml too ExecuteAction("NAMED_EXIT_ALL",self) if PassengerTable[tostring(self)] == nil then return end for i=1,getn(PassengerTable[tostring(self)]),1 do ExecuteAction("UNIT_AI_TRANSFER",PassengerTable[tostring(self)][i],1) --enable ai in case it was disabled end PassengerTable[tostring(self)]=nil end If you want to give the exit commands to the inmates one by one you can use this: function OnSovietHalfTrackReallyDamaged(self) --add the lua event to scriptevents.xml too if PassengerTable[tostring(self)] == nil then return end for i=1,getn(PassengerTable[tostring(self)]),1 do ExecuteAction("NAMED_EXIT_BUILDING",PassengerTable[tostring(self)][i]) ExecuteAction("UNIT_AI_TRANSFER",PassengerTable[tostring(self)][i],1) --enable ai in case it was disabled end PassengerTable[tostring(self)]=nil end So to sum up: "NAMED_EXIT_ALL" = command for object harboring units, "NAMED_EXIT_BUILDING" = command for units within transporters (or buildings).