diff options
| author | Markus Mittendrein <maxmitti@maxmitti.tk> | 2021-04-01 17:22:30 +0200 |
|---|---|---|
| committer | Markus Mittendrein <maxmitti@maxmitti.tk> | 2021-04-01 17:22:30 +0200 |
| commit | 2754b3f9f7869e1731bf0a070c4b2fcfc921f437 (patch) | |
| tree | e642395da002d845d1ee5bb854f8f5e4b24c5809 | |
| parent | 8c3e2aaf3c82321d47c5a4a409f48a594be07b97 (diff) | |
| download | ObjectsAppend-2754b3f9f7869e1731bf0a070c4b2fcfc921f437.tar.gz ObjectsAppend-2754b3f9f7869e1731bf0a070c4b2fcfc921f437.zip | |
Add CrewSelection rule
| -rw-r--r-- | CrewSelection.c4d/DefCore.txt | 8 | ||||
| -rw-r--r-- | CrewSelection.c4d/Graphics.png | bin | 0 -> 3585 bytes | |||
| -rw-r--r-- | CrewSelection.c4d/Names.txt | 2 | ||||
| -rw-r--r-- | CrewSelection.c4d/Script.c | 194 | ||||
| -rw-r--r-- | CrewSelection.c4d/StringTblDE.txt | 2 | ||||
| -rw-r--r-- | CrewSelection.c4d/StringTblUS.txt | 2 | ||||
| -rw-r--r-- | DTMenu.c4d/DefCore.txt | 7 | ||||
| -rw-r--r-- | DTMenu.c4d/DescDE.txt | 1 | ||||
| -rw-r--r-- | DTMenu.c4d/DescUS.txt | 1 | ||||
| -rw-r--r-- | DTMenu.c4d/Graphics.png | bin | 0 -> 10647 bytes | |||
| -rw-r--r-- | DTMenu.c4d/Names.txt | 2 | ||||
| -rw-r--r-- | DTMenu.c4d/Script.c | 1755 | ||||
| -rw-r--r-- | DTMenu.c4d/StringTblDE.txt | 7 | ||||
| -rw-r--r-- | DTMenu.c4d/StringTblUS.txt | 7 | ||||
| -rw-r--r-- | DTMenu.c4d/SymbolDummy.c4d/DefCore.txt | 5 | ||||
| -rw-r--r-- | DTMenu.c4d/SymbolDummy.c4d/Graphics.png | bin | 0 -> 126 bytes | |||
| -rw-r--r-- | DTMenu.c4d/SymbolDummy.c4d/Script.c | 16 | ||||
| -rw-r--r-- | System.c4g/DTArrays.c | 52 | ||||
| -rw-r--r-- | System.c4g/DTCallback.c | 397 | ||||
| -rw-r--r-- | System.c4g/DTCrewSelection.c | 12 | ||||
| -rw-r--r-- | System.c4g/DTScopedVars.c | 108 | ||||
| -rw-r--r-- | System.c4g/DTUtility.c | 817 |
22 files changed, 3395 insertions, 0 deletions
diff --git a/CrewSelection.c4d/DefCore.txt b/CrewSelection.c4d/DefCore.txt new file mode 100644 index 0000000..fe2a16d --- /dev/null +++ b/CrewSelection.c4d/DefCore.txt @@ -0,0 +1,8 @@ +[DefCore] +id=CS7I +Name=CrewSelection +Category=C4D_StaticBack|C4D_Rule +Width=1 +Height=1 +Picture=0,0,64,64 +Version=4,9,10,8 diff --git a/CrewSelection.c4d/Graphics.png b/CrewSelection.c4d/Graphics.png Binary files differnew file mode 100644 index 0000000..2cf36e5 --- /dev/null +++ b/CrewSelection.c4d/Graphics.png diff --git a/CrewSelection.c4d/Names.txt b/CrewSelection.c4d/Names.txt new file mode 100644 index 0000000..c4f89c3 --- /dev/null +++ b/CrewSelection.c4d/Names.txt @@ -0,0 +1,2 @@ +DE:Crewauswahl +US:Crew selection diff --git a/CrewSelection.c4d/Script.c b/CrewSelection.c4d/Script.c new file mode 100644 index 0000000..5282b3b --- /dev/null +++ b/CrewSelection.c4d/Script.c @@ -0,0 +1,194 @@ +#strict 2 + +func Activate(int player) +{ + var cursor = GetCursor(player); + if(!cursor) + { + return; + } + + var selectionMark = cursor->CreateSelectMark(); + var menu = CreateNewMenu + ([ + Menu_Caption(GetName(this)), + Menu_Symbol(GetID()), + Menu_Object(cursor), + Menu_Style_Context(), + + Menu_Factory([ObjectCallback("CrewFactory", true)], [player, selectionMark]), + Menu_Entry([ + Menu_Entry_Caption("$SelectAll$"), + Menu_Entry_Callbacks([Menu_Callback(ObjectCallback("SelectAll", true), Menu_CallbackType_Defaults, [Menu_CallbackArg_Args, Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject])]), + Menu_Entry_Args([player, 0, -3]) + ]), + Menu_Entry([ + Menu_Entry_Caption("$InvertSelection$"), + Menu_Entry_Callbacks([Menu_Callback(ObjectCallback("InvertSelection", true), Menu_CallbackType_Defaults, [Menu_CallbackArg_Args, Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject])]), + Menu_Entry_Args([player, 0, -2]) + ]), + Menu_Accept() + ]); + + var effect = AddEffect("CleanupSelectionMark", menu, 1, 0, this, 0); + EffectVar(0, menu, effect) = selectionMark; + EffectVar(1, menu, effect) = player; + + return menu; +} + +func CrewFactory(array args, int entryNumber) +{ + var player = args[0]; + var selectionMark = args[1]; + + var entries = []; + + for(var i = GetCrewCount(player) - 1; i >= 0 ; --i) + { + var crew = GetCrew(player, i); + if(crew) + { + var caption = GetName(crew); + if(!IsCrewSelected(player, crew)) + { + caption = Format("<c 888888>%s</c>", caption); + } + + ArrayAppend(entries, Menu_Entry + ([ + Menu_Entry_Caption(caption), + Menu_Entry_Symbol(crew), + + Menu_Entry_Callbacks + ([ + Menu_Callback(ObjectCallback("CrewSelection", true), Menu_CallbackType_Defaults, [Menu_CallbackArg_Args, Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject]), + Menu_Callback(ObjectCallback("HighlightCrew", true), Menu_CallbackType_Selection, [Menu_CallbackArg_Args]), + Menu_Callback(ObjectCallback("UnhighlightCrew", true), Menu_CallbackType_Deselection, [Menu_CallbackArg_Args]) + ]), + Menu_Entry_Args([player, crew, entryNumber, selectionMark]) + ])); + ++entryNumber; + } + } + + return entries; +} + +func CheckReopenMenu(array args, object currentCursor) +{ + if(GetCursor(args[0]) != currentCursor) + { + Activate(args[0])->SelectEntry(args[2]); + return Menu_React_Close; + } +} + +func CheckReopenOrCloseMenu(array args, int action, object currentCursor) +{ + if(action == Menu_CallbackType_Special2) + { + return CheckReopenMenu(args, currentCursor) || Menu_React_Refresh; + } + else + { + return Menu_React_Close; + } +} + +func CrewSelection(array args, int action, object currentCursor) +{ + if(action == Menu_CallbackType_Special2) + { + SelectCrew(args[0], args[1], !IsCrewSelected(args[0], args[1])); + } + else + { + UnselectCrew(args[0]); + SetCursor(args[0], args[1]); + } + return CheckReopenOrCloseMenu(args, action, currentCursor); +} + +func SelectAll(array args, int action, object currentCursor) +{ + for(var i = 0; i < GetCrewCount(args[0]); ++i) + { + var crew = GetCrew(args[0], i); + if(crew) + { + SelectCrew(args[0], crew, true); + } + } + return CheckReopenOrCloseMenu(args, action, currentCursor); +} + +func InvertSelection(array args, int action, object currentCursor) +{ + for(var i = 0; i < GetCrewCount(args[0]); ++i) + { + var crew = GetCrew(args[0], i); + if(crew && crew != currentCursor) + { + SelectCrew(args[0], crew, !IsCrewSelected(args[0], crew)); + } + } + + // in case the current cursor is the first clonk it can't be unselected otherwise + SelectCrew(args[0], currentCursor, false); + + return CheckReopenOrCloseMenu(args, action, currentCursor); +} + +func HighlightCrew(array args) +{ + SetPlrView(args[0], args[1]); + if(args[3]) + { + args[3]->MarkObject(args[1]); + } +} + +func UnhighlightCrew(array args) +{ + if(args[3]) + { + args[3]->Hide(); + SetPlrView(args[0], GetCursor(args[0])); + } +} + +func FxCleanupSelectionMarkStop(object target, int effectNumber, int reason, bool temp) +{ + if(!temp) + { + var selectionMark = EffectVar(0, target, effectNumber); + if(selectionMark) + { + RemoveObject(selectionMark); + } + + var player = EffectVar(1, target, effectNumber); + SetPlrView(player, GetCursor(player)); + } +} + +func IsCrewSelected(int player, object crew) +{ + for(var i = 0; i < GetCursorCount(); ++i) + { + if(GetCursor(player, i) == crew) + { + return true; + } + } + + return false; +} + +// this is needed because GetSelectCount seems to not update immediately -.- +func GetCursorCount(int player) +{ + for(var i = 0; GetCursor(player, i); ++i); + return i; +}
\ No newline at end of file diff --git a/CrewSelection.c4d/StringTblDE.txt b/CrewSelection.c4d/StringTblDE.txt new file mode 100644 index 0000000..39faf8d --- /dev/null +++ b/CrewSelection.c4d/StringTblDE.txt @@ -0,0 +1,2 @@ +SelectAll=Alle auswählen +InvertSelection=Auswahl umkehren diff --git a/CrewSelection.c4d/StringTblUS.txt b/CrewSelection.c4d/StringTblUS.txt new file mode 100644 index 0000000..1ef368c --- /dev/null +++ b/CrewSelection.c4d/StringTblUS.txt @@ -0,0 +1,2 @@ +SelectAll=Select all +InvertSelection=Invert selection diff --git a/DTMenu.c4d/DefCore.txt b/DTMenu.c4d/DefCore.txt new file mode 100644 index 0000000..bb2b85d --- /dev/null +++ b/DTMenu.c4d/DefCore.txt @@ -0,0 +1,7 @@ +[DefCore]
+id=MN7I
+Name=Menu
+Version=4,9,10,8
+Category=C4D_StaticBack
+Width=64
+Height=64
diff --git a/DTMenu.c4d/DescDE.txt b/DTMenu.c4d/DescDE.txt new file mode 100644 index 0000000..ce6bc05 --- /dev/null +++ b/DTMenu.c4d/DescDE.txt @@ -0,0 +1 @@ +Eine neue Objektdefinition.
\ No newline at end of file diff --git a/DTMenu.c4d/DescUS.txt b/DTMenu.c4d/DescUS.txt new file mode 100644 index 0000000..1dfbcf7 --- /dev/null +++ b/DTMenu.c4d/DescUS.txt @@ -0,0 +1 @@ +A new object definition.
\ No newline at end of file diff --git a/DTMenu.c4d/Graphics.png b/DTMenu.c4d/Graphics.png Binary files differnew file mode 100644 index 0000000..737dfed --- /dev/null +++ b/DTMenu.c4d/Graphics.png diff --git a/DTMenu.c4d/Names.txt b/DTMenu.c4d/Names.txt new file mode 100644 index 0000000..a955359 --- /dev/null +++ b/DTMenu.c4d/Names.txt @@ -0,0 +1,2 @@ +DE:Menü
+US:Menu
diff --git a/DTMenu.c4d/Script.c b/DTMenu.c4d/Script.c new file mode 100644 index 0000000..35dc576 --- /dev/null +++ b/DTMenu.c4d/Script.c @@ -0,0 +1,1755 @@ +#strict 2 + +static const DT_Menu = MN7I; +static const DT_Menu_Combined = -1; +static const DT_Menu_MenuVar = -1; + +// Enums and bitfields + +static const DT_Menu_Settings_Symbol = 0; +static const DT_Menu_Settings_Object = 1; +static const DT_Menu_Settings_Extra = 2; +static const DT_Menu_Settings_Caption = 3; +static const DT_Menu_Settings_ExtraData = 4; +static const DT_Menu_Settings_Style = 5; +static const DT_Menu_Settings_KeepOpen = 6; +static const DT_Menu_Settings_Parent = 7; +static const DT_Menu_Settings_Size = 8; +static const DT_Menu_Settings_RefreshInterval = 9; +static const DT_Menu_Settings_Selection = 10; +static const DT_Menu_Settings_ConditionDisableMode = 11; +static const DT_Menu_Settings_ConditionAllowSelection = 12; +static const DT_Menu_Settings_Callbacks = 13; +static const DT_Menu_Settings_Decoration = 14; +static const DT_Menu_Settings_RequireAction = 15; +static const DT_Menu_Settings_KeepParentOnClose = 16; +static const DT_Menu_Settings_Vars = 17; +static const DT_Menu_Settings_Closable = 18; + +static const DT_Menu_KeepOpen_Not = 0x0; +static const DT_Menu_KeepOpen_Keep = 0x1; +static const DT_Menu_KeepOpen_Force = 0x2; +static const DT_Menu_KeepOpen_Permanent = 0x4; +static const DT_Menu_KeepOpen_Refresh = 0x8; +static const DT_Menu_KeepOpen_RefreshContinuously = 0x10; +static const DT_Menu_KeepOpen_Refresh_Mask = 0x18; // DT_Menu_KeepOpen_Refresh | DT_Menu_KeepOpen_RefreshContinuously + +static const DT_Menu_Type_Setting = 0; +static const DT_Menu_Type_Entry = 1; +static const DT_Menu_Type_Factory = 2; + +static const DT_Menu_Action_Normal = 0; +static const DT_Menu_Action_Special2 = 1; +static const DT_Menu_Action_Close = 2; + +static const Menu_React_OverrideReaction = -1; +static const Menu_React_None = 0; +static const Menu_React_Close = 1; +static const Menu_React_Refresh = 2; +static const Menu_React_KeepOpen = 3; +static const Menu_React_Back = 4; +static const Menu_React_SelectionOffset = 5; +static const Menu_React_SelectionChange = 6; +static const Menu_React_ShowSubmenu = 7; + +global func Menu_React_OffsetSelection(int offset) { return [Menu_React_SelectionOffset, offset]; } +global func Menu_React_OverrideSelection(int override) { return [Menu_React_SelectionChange, override]; } +global func Menu_React_Override(override) { return [Menu_React_OverrideReaction, override]; } + +static const Menu_ConditionReact_Default = 0; +static const Menu_ConditionReact_Show = 1; +static const Menu_ConditionReact_Hide = 2; +static const Menu_ConditionReact_GrayOut = 3; +static const Menu_ConditionReact_CustomFormat = 4; + +global func Menu_ConditionReact_CustomColor(int color) { return [Menu_ConditionReact_CustomFormat, Format("<c %x>%%s</c>", color)]; } +global func Menu_ConditionReact_Format(string format) { return [Menu_ConditionReact_CustomFormat, format]; } + +static const Menu_Condition_Default = 0; +static const Menu_Condition_AllowSelection = 1; +static const Menu_Condition_DenySelection = 2; + +static const DT_Menu_Entry_Caption = 0; +static const DT_Menu_Entry_Callbacks = 1; +static const DT_Menu_Entry_Symbol = 2; +static const DT_Menu_Entry_Count = 3; +static const DT_Menu_Entry_InfoCaption = 4; +static const DT_Menu_Entry_Extra = 5; +static const DT_Menu_Entry_XPar1 = 6; +static const DT_Menu_Entry_XPar2 = 7; +static const DT_Menu_Entry_Args = 8; +static const DT_Menu_Entry_Condition = 9; +static const DT_Menu_Entry_Placeholder = 10; + +static const Menu_CallbackType_None = 0x0; +static const Menu_CallbackType_Special2 = 0x1; +static const Menu_CallbackType_Normal = 0x2; +static const Menu_CallbackType_Close = 0x4; +static const Menu_CallbackType_Selection = 0x8; +static const Menu_CallbackType_Deselection = 0x10; +static const Menu_CallbackType_ValueChanged = 0x20; +static const Menu_CallbackType_InputAborted = 0x40; +static const Menu_CallbackType_Defaults = 0x3; // Menu_CallbackType_Normal | Menu_CallbackType_Special2 +static const Menu_CallbackType_All = 0x7f; // Menu_CallbackType_Normal | Menu_CallbackType_Special2 | Menu_CallbackType_Close | Menu_CallbackType_Selection | Menu_CallbackType_Deselection | Menu_CallbackType_ValueChanged | Menu_CallbackType_InputAborted + +static const Menu_AdaptorType_Boolean = 0; +static const Menu_AdaptorType_Integer = 1; +static const Menu_AdaptorType_String = 2; +static const Menu_AdaptorType_ID = 3; +static const Menu_AdaptorType_Enum = 4; +static const Menu_AdaptorType_BitField = 5; + +static const Menu_Adaptor_Limits_Max = 0x7fffffff; +static const Menu_Adaptor_Limits_Min = 0x80000000; + +static const Menu_CallbackArg_All = -1; +static const Menu_CallbackArg_Action = 0; +static const Menu_CallbackArg_Symbol = 1; +static const Menu_CallbackArg_MenuObject = 2; +static const Menu_CallbackArg_Args = 3; +static const Menu_CallbackArg_NewSelection = 4; +static const Menu_CallbackArg_OldSelection = 5; +static const Menu_CallbackArg_NewValue = 6; +static const Menu_CallbackArg_OldValue = 7; +static const Menu_CallbackArg_FromSubmenu = 8; +static const Menu_CallbackArg_Menu = 9; +static const Menu_CallbackArg_Returned = 10; +static const Menu_CallbackArg_Menu = 11; +static const Menu_CallbackArg_EntryNumber = 12; + +static const DT_Menu_Adaptor_Type = 0; +static const DT_Menu_Adaptor_Variable = 1; +static const DT_Menu_Adaptor_Callbacks = 2; +static const DT_Menu_Adaptor_MessageBoardText = 3; +static const DT_Menu_Adaptor_Limits = 4; +static const DT_Menu_Adaptor_StepSize = 5; +static const DT_Menu_Adaptor_LayoutVals = 6; +static const DT_Menu_Adaptor_NoEmptyString = 7; +static const DT_Menu_Adaptor_EntryIndex = 8; +static const DT_Menu_Adaptor_Mask = 9; +static const DT_Menu_Adaptor_WrapAround = 10; +static const DT_Menu_Adaptor_Args = 11; +static const DT_Menu_Adaptor_EnumSubmenu = 12; +static const DT_Menu_Adaptor_EnumSubmenuCaption = 13; +static const DT_Menu_Adaptor_EnumSubmenuSymbol = 14; +static const DT_Menu_Adaptor_EnumAllowUnknown = 15; +static const DT_Menu_Adaptor_EnumInline = 16; + +static const Menu_Layout_Symbol = 1; +static const Menu_Layout_InfoCaption = 2; + +static const Menu_Layout__CaptionPos = 3; +static const Menu_Layout__ValuePos = 4; +// static const Menu_Layout__InputValuePos = 5; TODO + +static const Menu_Layout_Caption = 4; +static const Menu_Layout_Value = 8; +// static const Menu_Layout_InputValue = 16; TODO +static const Menu_Layout__NoFlagMask = 3; + +// ---------------------------------------------------------------------------- + +local settings; +local entries; +local createEntries; +local entryCount; +local currentSelection; +local suspended; +local closing; +local msgBoardMode; +local msgBoardEntry; +local noSelectionCallbacks; +local subMenu; +local vars; + +func Initialize() +{ + vars = []; +} + +func Destruction() +{ + Close(); +} + +func Create(array cSettings, array cEntries) +{ + settings = cSettings; + entries = []; + createEntries = cEntries; + currentSelection = -1; + settings[DT_Menu_Settings_ConditionDisableMode] = settings[DT_Menu_Settings_ConditionDisableMode] || Menu_ConditionReact_Hide; + entryCount = 0; + + if(settings[DT_Menu_Settings_Vars]) + { + var settingVars = settings[DT_Menu_Settings_Vars]; + + for(var i = 0; i < GetLength(settingVars); ++i) + { + if(settingVars[i]) + { + vars[i] = settingVars[i]; + } + } + settings[DT_Menu_Settings_Vars] = 0; + } + + var factoryArgs = []; + factoryArgs[Menu_CallbackArg_Menu] = this; + factoryArgs[Menu_CallbackArg_MenuObject] = settings[DT_Menu_Settings_Object]; + + HandleEntries(createEntries, entryCount, entries, factoryArgs); + + if(settings[DT_Menu_Settings_Parent]) settings[DT_Menu_Settings_Parent]->Suspend(); + + CreateMenu(settings[DT_Menu_Settings_Symbol], settings[DT_Menu_Settings_Object], this, settings[DT_Menu_Settings_Extra], settings[DT_Menu_Settings_Caption], settings[DT_Menu_Settings_ExtraData], settings[DT_Menu_Settings_Style], true, MN7I); + + if(GetType(settings[DT_Menu_Settings_Size]) == C4V_Array) SetMenuSize(settings[DT_Menu_Settings_Size][0], settings[DT_Menu_Settings_Size][1], settings[DT_Menu_Settings_Object]); + + AddEntries(entries); + + if(entryCount > 0) + { + SelectEntry(settings[DT_Menu_Settings_Selection]); + } + + if(settings[DT_Menu_Settings_Decoration]) + { + SetMenuDecoration(settings[DT_Menu_Settings_Decoration], settings[DT_Menu_Settings_Object]); + } + + if(!GetEffect("Menu", this)) AddEffect("Menu", this, 1, 1, this, 0); +} + +func SelectEntry(int selection) +{ + if(selection < 0) selection = entryCount + selection; + SelectMenuItem(selection, settings[DT_Menu_Settings_Object]); +} + +func ActivateEntry(int index, int action) +{ + if(index < 0) index = entryCount + index; + if(index >= entryCount || index < 0) + { + return; + } + + SelectEntry(index); + + var entry = entries[index]; + MenuItemCommand(entry[DT_Menu_Entry_Symbol], index, action); +} + +func SubMenu() +{ + return subMenu; +} + +func &Var(int index) +{ + return vars[index]; +} + +global func MenuVar(int index) +{ + return [DT_Menu_MenuVar, index]; +} + +/*func FxMenuStart(object target, int effectNumber, int temp) +{ + +}*/ + +func FxMenuTimer(object target, int effectNumber, int effectTime) +{ + if(!settings[DT_Menu_Settings_Object]) + { + return Close(); + } + else if(settings[DT_Menu_Settings_RequireAction]) + { + var obj = settings[DT_Menu_Settings_Object]; + var requirement = settings[DT_Menu_Settings_RequireAction]; + if(GetAction(obj) != requirement[0] || (requirement[1] && GetActionTarget(0, obj) != requirement[1])) + { + return Close(); + } + } + + if(suspended) return; + + if(msgBoardMode != 0 && !TestMessageBoard(GetOwner(settings[DT_Menu_Settings_Object]), true)) + { + var entry = entries[msgBoardEntry]; + var args = entry[DT_Menu_Entry_Args]; + var reaction = CallCallbacks(args[DT_Menu_Adaptor_Callbacks], Menu_CallbackType_InputAborted, [Menu_CallbackType_InputAborted, entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]); + if(reaction != Menu_React_None) + { + React(reaction, msgBoardEntry); + } + msgBoardMode = 0; + } + + if(!GetMenu(settings[DT_Menu_Settings_Object])) + { + if(settings[DT_Menu_Settings_KeepOpen] & (DT_Menu_KeepOpen_Refresh_Mask | DT_Menu_KeepOpen_Force) && !settings[DT_Menu_Settings_Closable]) + { + Refresh(currentSelection); + } + else + { + return FX_Execute_Kill; + } + } + + if(settings[DT_Menu_Settings_KeepOpen] & DT_Menu_KeepOpen_RefreshContinuously && !(effectTime % settings[DT_Menu_Settings_RefreshInterval])) + { + Refresh(currentSelection); + } +} + +func FxMenuStop(object target, int effectNumber, int reason, bool temp) +{ + if(temp) + { + return; + } + CloseMenu(settings[DT_Menu_Settings_Object]); + if(settings[DT_Menu_Settings_Parent]) settings[DT_Menu_Settings_Parent]->Suspend(true); + RemoveObject(this); +} + +func BindCallbackArgs(array args, array binding) +{ + var ret = CreateArray(GetLength(binding)); + var i = 0; + for(var arg in binding) + { + if(arg == Menu_CallbackArg_All) + { + ret[i] = args; + } + else + { + ret[i] = args[arg]; + } + ++i; + } + + return ret; +} + +func CallCallbacks(array callbacks, int type, array args, defaultRet, bool noGlobalCallbacks) +{ + var ret = defaultRet; + + args[Menu_CallbackArg_Menu] = this; + + if(callbacks) + { + for(var callback in callbacks) + { + if(callback[1] & type) + { + ret = CallA(callback[0], BindCallbackArgs(args, callback[2])); + break; + } + } + } + + + if(!noGlobalCallbacks) + { + args[Menu_CallbackArg_Returned] = ret; + var globalRet = CallCallbacks(settings[DT_Menu_Settings_Callbacks], type, args, defaultRet, true); + if(GetType(globalRet) == C4V_Array && globalRet[0] == Menu_React_OverrideReaction) + { + ret = globalRet[1]; + } + } + + return ret; +} + +func Close(bool closeParents) +{ + closing = true; + if(subMenu) + { + subMenu->Close(); + } + if(closeParents && settings[DT_Menu_Settings_Parent]) settings[DT_Menu_Settings_Parent]->Close(true); + RemoveEffect("Menu", this); +} + +func Suspend(bool cont) +{ + if(suspended == !cont) return; + if(suspended = !cont) + { + CloseMenu(settings[DT_Menu_Settings_Object]); + } + else if(!closing) + { + Refresh(currentSelection); + } +} + +func HandleEntries(array factoryEntries, int& i, array& ret, array& factoryArgs) +{ + for(var entry in factoryEntries) + { + if(entry[0] == DT_Menu_Type_Entry) + { + ret[i++] = entry[1]; + } + else if(entry[0] == DT_Menu_Type_Factory) + { + factoryArgs[Menu_CallbackArg_Args] = entry[1][1]; + + for(var callback in entry[1][0]) + { + factoryArgs[Menu_CallbackArg_EntryNumber] = i; + var factoryResult = CallA(callback, BindCallbackArgs(factoryArgs, entry[1][2])); + if(GetType(factoryResult) == C4V_Array) + { + var newEntries = []; + UncombineAndDistinguish(factoryResult, settings, newEntries); + HandleEntries(newEntries, i, ret, factoryArgs); + } + else if(factoryResult == Menu_React_Close) + { + return Close(); + } + } + } + } +} + +func AddEntries(array& entries) +{ + for(var i = 0; i < GetLength(entries); ++i) + { + var entry = entries[i]; + var condition = entry[DT_Menu_Entry_Condition], conditionRet; + var caption = entry[DT_Menu_Entry_Caption], noCommand = !entry[DT_Menu_Entry_Placeholder] || (entry[DT_Menu_Entry_Placeholder] != true && !entry[DT_Menu_Entry_Callbacks]); + if(condition) + { + if(noCommand || condition[1] == Menu_Condition_DenySelection || (condition[1] == Menu_Condition_Default && !settings[DT_Menu_Settings_ConditionAllowSelection])) + { + noCommand = true; + } + + conditionRet = CallA(condition[0], [entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]) || settings[DT_Menu_Settings_ConditionDisableMode]; + if(conditionRet == Menu_ConditionReact_Hide) + { + continue; + } + else if(conditionRet == Menu_ConditionReact_GrayOut) + { + caption = Format("<c 808080>%s</c>", caption); + } + else if(GetType(conditionRet) == C4V_Array && conditionRet[0] == Menu_ConditionReact_CustomFormat) + { + caption = Format(conditionRet[1], caption); + } + else + { + noCommand = false; + } + } + + var symbol = entry[DT_Menu_Entry_Symbol], symbolID = 0, deleteSymbol = 0; + if(GetType(symbol) == C4V_Array) + { + if(GetType(symbol[0]) == C4V_C4ID) + { + symbolID = symbol[0]; + if(symbol[2]) + { + symbol = [CreateSymbolDummy()->SetSymbol(symbolID, symbol[1])->SetColor(symbol[2]), true]; + } + else if(symbol[1]) + { + entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgIndexed; + entry[DT_Menu_Entry_XPar1] = symbol[1]; + } + } + + if(GetType(symbol[0]) == C4V_C4Object) + { + entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgObject; + entry[DT_Menu_Entry_XPar1] = symbol[0]; + deleteSymbol = symbol[1]; + symbolID = GetID(symbol[0]); + } + } + else + { + symbolID = symbol; + } + entry[DT_Menu_Entry_Symbol] = symbolID; + + entries[i] = entry; + + AddMenuItem(caption, !noCommand && "MenuItemCommand", symbolID, settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Count], i, entry[DT_Menu_Entry_InfoCaption], entry[DT_Menu_Entry_Extra], entry[DT_Menu_Entry_XPar1], entry[DT_Menu_Entry_XPar2]); + + if(deleteSymbol) + { + RemoveObject(deleteSymbol); + } + } +} + +func React(reaction, int itemNumber, int refreshDelayed) +{ + if(reaction == Menu_React_Close) + { + Close(!settings[DT_Menu_Settings_KeepParentOnClose]); + } + else if(reaction == Menu_React_Back) + { + Close(); + } + else if(reaction == Menu_React_Refresh || (settings[DT_Menu_Settings_KeepOpen] & DT_Menu_KeepOpen_Refresh)) + { + Refresh(itemNumber, refreshDelayed); + } + else if(GetType(reaction) == C4V_Array) + { + var selection = currentSelection; + if(reaction[0] == Menu_React_SelectionOffset) + { + selection += reaction[1]; + selection %= entryCount; + } + else if(reaction[0] == Menu_React_SelectionChange) + { + selection = BoundBy(reaction[1], 0, entryCount - 1); + } + + if(selection != currentSelection) + { + SelectEntry(selection); + } + } +} + +func CheckCondition(array entry) +{ + var condition = entry[DT_Menu_Entry_Condition]; + return !condition || (CallA(condition[0], [entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]) || settings[DT_Menu_Settings_ConditionDisableMode]) == Menu_ConditionReact_Show; +} + +func MenuItemCommand(id ID, int itemNumber, int action) +{ + var entry = entries[itemNumber]; + var condition = entry[DT_Menu_Entry_Condition]; + action = action || Menu_CallbackType_Normal; + var reaction; + if(CheckCondition(entry)) + { + reaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], action, [action, ID, settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]); + } + else + { + if(condition[1] == Menu_Condition_AllowSelection) + { + reaction = Menu_React_KeepOpen; + } + else + { + reaction = Menu_React_Refresh; + } + } + React(reaction, itemNumber); +} + +func SubmenuItemCallback(int action, object menuObject, args, array allArgs) +{ + allArgs[Menu_CallbackArg_Args] = args[1]; + var reaction = CallCallbacks(args[0], action, allArgs, Menu_React_None); + + if(((action & Menu_CallbackType_Defaults) && reaction == Menu_React_None) || reaction == Menu_React_ShowSubmenu) + { + subMenu = CreateNewMenu(args[2], settings, this); + return Menu_React_None; + } + else + { + return reaction; + } +} + +func MenuQueryCancel(int selection, object menuObject) +{ + var reaction; + if(selection != -1) + { + var entry = entries[selection]; + if(CheckCondition(entry)) + { + reaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Close, [Menu_CallbackType_Close, entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]); + } + + React(reaction, selection, true); + } + + if((settings[DT_Menu_Settings_KeepOpen] != DT_Menu_KeepOpen_Not && settings[DT_Menu_Settings_KeepOpen] != DT_Menu_KeepOpen_Permanent && !settings[DT_Menu_Settings_Closable]) || (reaction == Menu_React_KeepOpen)) + { + return true; + } +} + +func OnMenuSelection(int selection, object menuObject) +{ + if(selection != currentSelection) + { + var oldSelection = currentSelection; + var entry = entries[currentSelection]; + var deselectReaction = Menu_React_None; + var selectReaction = Menu_React_None; + if(!noSelectionCallbacks && CheckCondition(entry) && currentSelection != -1) + { + deselectReaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Deselection, [Menu_CallbackType_Deselection, entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args], selection, oldSelection]); + } + + entry = entries[currentSelection = selection]; + + if(!noSelectionCallbacks && CheckCondition(entry)) + { + selectReaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Selection, [Menu_CallbackType_Selection, entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args], selection, oldSelection]); + } + + if(deselectReaction != Menu_React_None) + { + React(deselectReaction, currentSelection); + } + + if(selectReaction != Menu_React_None) + { + React(selectReaction, currentSelection); + } + } +} + +func Refresh(int selection, bool delayed) +{ + if(suspended) + { + return; + } + + if(delayed) + { + ScheduleCall(this, "Refresh", 1, 0, selection); + } + else + { + var disabledCallbacks; + if(!noSelectionCallbacks) + { + disabledCallbacks = noSelectionCallbacks = true; + } + + CloseMenu(settings[DT_Menu_Settings_Object]); + Create(settings, createEntries); + SelectEntry(BoundBy(selection, 0, entryCount - 1)); + + if(disabledCallbacks) + { + noSelectionCallbacks = false; + } + } +} + +// ---------------------------------------------------------------------------- + +global func Menu__Setting(array setting) { return [DT_Menu_Type_Setting, setting]; } + +global func Menu_Symbol(id symbol) { return Menu__Setting([DT_Menu_Settings_Symbol, symbol]); } +global func Menu_Object(object obj) { return Menu__Setting([DT_Menu_Settings_Object, obj]); } +global func Menu__Extra(int extra, int data) { return Menu_Combined([Menu__Setting([DT_Menu_Settings_Extra, extra]), Menu_ExtraData(data)]); } +global func Menu_Size(int width, int height) { return Menu__Setting([DT_Menu_Settings_Size, [width, height]]); } +global func Menu_ExtraData(int data) { return Menu__Setting([DT_Menu_Settings_ExtraData, data]); } +global func Menu_Caption(string caption) { return Menu__Setting([DT_Menu_Settings_Caption, caption]); } +global func Menu_RefreshInterval(int interval) { return Menu__Setting([DT_Menu_Settings_RefreshInterval, interval + !interval]); } +global func Menu_Selection(int selection) { return Menu__Setting([DT_Menu_Settings_Selection, selection]); } +global func Menu__Style(int style) { return Menu__Setting([DT_Menu_Settings_Style, style]); } +global func Menu__KeepOpen(int mode) { return Menu__Setting([DT_Menu_Settings_KeepOpen, mode]); } + +global func Menu_Extra_None() { return Menu__Extra(C4MN_Extra_None); } +global func Menu_Extra_Components() { return Menu__Extra(C4MN_Extra_Components); } +global func Menu_Extra_Value() { return Menu__Extra(C4MN_Extra_Value); } +global func Menu_Extra_MagicValue(int compare) { return Menu__Extra(C4MN_Extra_MagicValue, compare); } +global func Menu_Extra_Info() { return Menu__Extra(C4MN_Extra_Info); } +global func Menu_Extra_ComponentsMagic() { return Menu__Extra(C4MN_Extra_ComponentsMagic); } + +global func Menu_Style_Normal() { return Menu__Style(C4MN_Style_Normal); } +global func Menu_Style_Context() { return Menu__Style(C4MN_Style_Context); } +global func Menu_Style_Info() { return Menu__Style(C4MN_Style_Info); } +global func Menu_Style_Dialog() { return Menu__Style(C4MN_Style_Dialog); } +global func Menu_Style_EqualItemHeight() { return Menu__Style(C4MN_Style_EqualItemHeight); } + +global func Menu_DontKeepOpen() { return Menu__KeepOpen(DT_Menu_KeepOpen_Not); } +global func Menu_KeepOpen() { return Menu__KeepOpen(DT_Menu_KeepOpen_Keep); } +global func Menu_ForceKeepOpen() { return Menu__KeepOpen(DT_Menu_KeepOpen_Force); } +global func Menu_Refresh() { return Menu__KeepOpen(DT_Menu_KeepOpen_Refresh); } +global func Menu_RefreshContinuously(int interval) { return Menu_Combined([Menu__KeepOpen(DT_Menu_KeepOpen_RefreshContinuously), Menu_RefreshInterval(interval)]); } +global func Menu_Permanent() { return Menu__KeepOpen(DT_Menu_KeepOpen_Permanent); } + +global func Menu_ConditionAllowSelection() { return Menu__Setting([DT_Menu_Settings_ConditionAllowSelection, true]);} +global func Menu_ConditionDenySelection() { return Menu__Setting([DT_Menu_Settings_ConditionAllowSelection, false]);} + +global func Menu_Callbacks(array callbacks) { return Menu__Setting([DT_Menu_Settings_Callbacks, callbacks]); } +global func Menu_Decoration(id decoration) { return Menu__Setting([DT_Menu_Settings_Decoration, decoration]); } +global func Menu_RequireAction(string action, object target) { return Menu__Setting([DT_Menu_Settings_RequireAction, [action, target]]); } +global func Menu_KeepParentOnClose(bool dontKeep) { return Menu__Setting([DT_Menu_Settings_KeepParentOnClose, !dontKeep]); } +global func Menu_ConditionDisableMode(mode) { return Menu__Setting([DT_Menu_Settings_ConditionDisableMode, mode]);} +global func Menu_Vars(array vars) { return Menu__Setting([DT_Menu_Settings_Vars, vars]); } +global func Menu_Closable() { return Menu__Setting([DT_Menu_Settings_Closable, true]); } +global func Menu_NotClosable() { return Menu__Setting([DT_Menu_Settings_Closable, false]); } + +global func Menu_Callback(array callback, int types, array argBinding) +{ + argBinding = argBinding || [Menu_CallbackArg_Action, Menu_CallbackArg_Symbol, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_NewSelection, Menu_CallbackArg_OldSelection, Menu_CallbackArg_NewValue, Menu_CallbackArg_OldValue, Menu_CallbackArg_FromSubmenu]; + return [callback, types || Menu_CallbackType_Defaults, argBinding]; +} + +global func Menu_Entry_Caption(string Caption) { return [DT_Menu_Entry_Caption, Caption]; } +global func Menu_Entry_Callbacks(array Callbacks) { return [DT_Menu_Entry_Callbacks, Callbacks]; } +global func Menu_Entry_Count(int Count) { return [DT_Menu_Entry_Count, Count]; } +global func Menu_Entry_InfoCaption(string InfoCaption) { return [DT_Menu_Entry_InfoCaption, InfoCaption]; } +global func Menu_Entry_Extra(int Extra) { return [DT_Menu_Entry_Extra, Extra]; } +global func Menu_Entry_XPar1(XPar1) { return [DT_Menu_Entry_XPar1, XPar1]; } +global func Menu_Entry_XPar2(XPar2) { return [DT_Menu_Entry_XPar2, XPar2]; } +global func Menu_Entry_Args(Args) { return [DT_Menu_Entry_Args, Args]; } +global func Menu_Entry_Placeholder(bool Placeholder) { return [DT_Menu_Entry_Placeholder, Placeholder]; } +global func Menu_Entry_Symbol(symbol, extra, int color) +{ + if(GetType(symbol) == C4V_Array) + { + return [DT_Menu_Entry_Symbol, symbol]; + } + else + { + return [DT_Menu_Entry_Symbol, [symbol, extra, color]]; + } +} +global func Menu_Entry_Condition(callback, int allowDisabledSelection) { return [DT_Menu_Entry_Condition, [callback, allowDisabledSelection]]; } +global func Menu_Entry_VariableCondition(array scopedVar, compare, int disableMode, bool invert, int allowDisabledSelection) +{ + return Menu_Entry_Condition(BindCallback(MN7I->MenuObjectCallback("VariableCondition"), [Bind(scopedVar), Bind(compare), Bind(disableMode), Bind(invert)]), allowDisabledSelection); +} +global func VariableCondition(array scopedVar, compare, int disableMode, bool invert) +{ + var disable = ScopedVar(scopedVar) != compare; + if(invert) + { + disable = !disable; + } + if(disable) + { + return disableMode; + } + else + + { + return Menu_ConditionReact_Show; + } +} +global func Menu_Entry_Object(object obj) +{ + return Menu_Combined([Menu_Entry_Symbol(obj), Menu_Entry_Caption(GetName(obj)), Menu_Entry_InfoCaption(GetDesc(obj))]); +} + +global func Menu_Entry(array settings) +{ + if(!settings) + { + settings = [Menu_Entry_Placeholder(false)]; + } + + var namedArgs = []; + namedArgs[DT_Menu_Entry_Caption] = ""; + namedArgs[DT_Menu_Entry_Placeholder] = -1; + MN7I->NamedArgs(settings, namedArgs); + if(!namedArgs[DT_Menu_Entry_InfoCaption]) + { + namedArgs[DT_Menu_Entry_Extra] |= C4MN_Add_ForceNoDesc; + } + return [DT_Menu_Type_Entry, namedArgs]; +} + +global func Menu_SubMenu(array entrySettings, array menuEntry_Settings) +{ + var ret = Menu_Entry(entrySettings); + ret[1][DT_Menu_Entry_Args] = [ret[1][DT_Menu_Entry_Callbacks], ret[1][DT_Menu_Entry_Args], menuEntry_Settings]; + ret[1][DT_Menu_Entry_Callbacks] = [Menu_Callback(MN7I->MenuObjectCallback("SubmenuItemCallback"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])]; + return ret; +} + +global func Menu_Factory(array callbacks, args, array binding) +{ + return [DT_Menu_Type_Factory, [callbacks, args, binding || [Menu_CallbackArg_Args, Menu_CallbackArg_EntryNumber, Menu_CallbackArg_Menu]]]; +} + +global func Menu_Text(string text, bool allowSelection) +{ + return Menu_Entry([Menu_Entry_Caption(text), Menu_Entry_Placeholder(allowSelection)]); +} + +global func Menu_Blank(bool allowSelection) +{ + return Menu_Entry([Menu_Entry_Placeholder(allowSelection)]); +} + +func DeclineAcceptBack(string caption, symbol, string callback, array settings) +{ + var ret = Menu_Entry([Menu_Entry_Caption(caption), Menu_Combined(settings || [])]); + ret[1][DT_Menu_Entry_Args] = [ret[1][DT_Menu_Entry_Callbacks], ret[1][DT_Menu_Entry_Args]]; + ret[1][DT_Menu_Entry_Callbacks] = Menu_Entry_Callbacks([Menu_Callback(MN7I->MenuObjectCallback(callback), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_Args, Menu_CallbackArg_All])])[1]; + ExtraSymbol(ret[1][DT_Menu_Entry_Caption], ret[1][DT_Menu_Entry_Symbol], symbol); + return ret; +} + +global func Menu_Decline(array settings) +{ + return MN7I->DeclineAcceptBack("$Decline$", [MN7I, 2], "DeclineAcceptCommand", settings); +} + +global func Menu_Accept(array settings) +{ + return MN7I->DeclineAcceptBack("$Accept$", [MN7I, 1], "DeclineAcceptCommand", settings); +} + +global func Menu_Back(array settings) +{ + return MN7I->DeclineAcceptBack("$Back$", [MN7I, 5], "BackCommand", settings); +} + +global func Menu_Adaptor_Type(int Type) { return [DT_Menu_Adaptor_Type, Type]; } +global func Menu_Adaptor_Variable(array Variable) { return [DT_Menu_Adaptor_Variable, Variable]; } +global func Menu_Adaptor_Callbacks(array Callbacks) { return [DT_Menu_Adaptor_Callbacks, Callbacks]; } +global func Menu_Adaptor_MessageBoardText(string MessageBoardText) { return [DT_Menu_Adaptor_MessageBoardText, MessageBoardText]; } +global func Menu_Adaptor_WrapAround(bool WrapAround) { return [DT_Menu_Adaptor_WrapAround, WrapAround]; } +global func Menu_Adaptor_EnumSubmenuSymbol(id EnumSubmenuSymbol) { return [DT_Menu_Adaptor_EnumSubmenuSymbol, EnumSubmenuSymbol]; } +global func Menu_Adaptor_EnumAllowUnknown(bool EnumAllowUnknown) { return [DT_Menu_Adaptor_EnumAllowUnknown, EnumAllowUnknown]; } +global func Menu_Adaptor_EnumInline(bool EnumInline) { return [DT_Menu_Adaptor_EnumInline, EnumInline]; } +global func Menu_Adaptor_EnumSubmenu(int callbackType) { return [DT_Menu_Adaptor_EnumSubmenu, callbackType]; } +global func Menu_Adaptor_Limits(min, max, args) +{ + return [DT_Menu_Adaptor_Limits, [min, max, args]]; +} +global func Menu_Adaptor_StepSize(int step, force) { return [DT_Menu_Adaptor_StepSize, [step, force]]; } +global func Menu_Adaptor_Enum(array enumVals, array layout, bool valuesAsSeparateLists) +{ + MN7I->AdaptorLayout(layout, enumVals, valuesAsSeparateLists); + return Menu_Combined([Menu_Adaptor_Type(Menu_AdaptorType_Enum), [DT_Menu_Adaptor_LayoutVals, [enumVals, layout]]]); +} +global func Menu_Adaptor_BitField(array fieldVals, array layout, bool valuesAsSeparateLists, bool bitPositionAsValue) +{ + MN7I->AdaptorLayout(layout, fieldVals, valuesAsSeparateLists); + if(bitPositionAsValue) + { + var valuePos = layout[Menu_Layout__ValuePos] - 1; + for(var i = 0; i < GetLength(fieldVals); ++i) + { + fieldVals[i][valuePos] = 1 << fieldVals[i][valuePos]; + } + } + return Menu_Combined([Menu_Adaptor_Type(Menu_AdaptorType_BitField), [DT_Menu_Adaptor_LayoutVals, [fieldVals, layout]]]); +} +global func Menu_Adaptor_NoEmptyString() { return [DT_Menu_Adaptor_NoEmptyString, true]; } +global func Menu_Adaptor_EnumSubmenuCaption(string menuCaption, string entryCaption) +{ + return [DT_Menu_Adaptor_EnumSubmenuCaption, [menuCaption, entryCaption]]; +} +global func Menu_Adaptor_Boolean() { return Menu_Adaptor_Type(Menu_AdaptorType_Boolean); } +global func Menu_Adaptor_Integer() { return Menu_Adaptor_Type(Menu_AdaptorType_Integer); } +global func Menu_Adaptor_String() { return Menu_Adaptor_Type(Menu_AdaptorType_String); } +global func Menu_Adaptor_ID() { return Menu_Adaptor_Type(Menu_AdaptorType_ID); } + +func AdaptorLayout(array& layout, array& vals, bool valuesAsSeparateLists) +{ + layout = layout || [Menu_Layout_Value | Menu_Layout_Caption]; + var layoutMap = []; + var index = 1; + for(var val in layout) + { + var noFlag = val & Menu_Layout__NoFlagMask; + + if(noFlag) + { + layoutMap[noFlag] = index; + } + + if(val & Menu_Layout_Caption) + { + layoutMap[Menu_Layout__CaptionPos] = index; + } + + if(val & Menu_Layout_Value) + { + layoutMap[Menu_Layout__ValuePos] = index; + } + +// if(val & Menu_Layout_InputValue) TODO +// { +// layoutMap[Menu_Layout__InputValuePos] = index; +// } + + ++index; + } + + layout = layoutMap; + + if(valuesAsSeparateLists) + { + var tempVals = vals; + var vals = []; + + for(var i = 0; i < GetLength(tempVals[0]); ++i) + { + vals[i] = []; + + for(var j = 0; j < GetLength(tempVals); ++j) + { + vals[i][j] = tempVals[j][i]; + } + } + } +} + +global func Menu_Adaptor(array entrySettings, array adaptorSettings) +{ + var adaptorArgs = []; + + adaptorArgs[DT_Menu_Adaptor_WrapAround] = -1; + adaptorArgs[DT_Menu_Adaptor_EnumSubmenu] = Menu_CallbackType_Special2; + + MN7I->NamedArgs(adaptorSettings, adaptorArgs); + + if(adaptorArgs[DT_Menu_Adaptor_WrapAround] == -1) + { + if(adaptorArgs[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Integer) + { + adaptorArgs[DT_Menu_Adaptor_WrapAround] = false; + } + else if(adaptorArgs[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Enum) + { + adaptorArgs[DT_Menu_Adaptor_WrapAround] = true; + } + } + + return Menu_Factory([MN7I->MenuObjectCallback("AdaptorFactory")], [Menu_Entry(entrySettings)[1], adaptorArgs, entrySettings]); +} + +func EnumValPos(array enumVals, array layout, val, bool allowUnknown) +{ + for(var i = 0; i < GetLength(enumVals); ++i) + { + if(enumVals[i][layout[Menu_Layout__ValuePos] - 1] == val) + { + return i; + } + } + return -1; +} + +func BooleanToggleCaption(bool val, string& caption, &symbol) +{ + val = !!val; + ExtraSymbol(caption, symbol, [MN7I, 2 - val, !val && RGB(128, 128, 128)]); +} + +func InlineSymbol(string& caption, symbol) +{ + if(symbol[2]) + { + caption = Format("<c %x>{{%i:%d}}</c> %s", symbol[2], symbol[0], symbol[1], caption); + } + else + { + caption = Format("{{%i:%d}} %s", symbol[0], symbol[1], caption); + } +} + +func ExtraSymbol(string& caption, &symbol, extraSymbol) +{ + if(GetType(extraSymbol) == C4V_C4ID) + { + extraSymbol = [extraSymbol]; + } + + if(symbol && extraSymbol && GetType(extraSymbol[0]) == C4V_C4ID) + { + InlineSymbol(caption, extraSymbol); + } + else + { + symbol = extraSymbol; + } +} + +func EnumEntrySettings(string& caption, &symbol, string& infoCaption, int index, array args, array entry) +{ + var layoutVals = args[DT_Menu_Adaptor_LayoutVals]; + var layout = layoutVals[1]; + layoutVals = layoutVals[0]; + + caption = entry[DT_Menu_Entry_Caption]; + if(layout[Menu_Layout__CaptionPos]) + { + caption = Format(caption, layoutVals[index][layout[Menu_Layout__CaptionPos] - 1]); + } + + if(layout[Menu_Layout_Symbol]) + { + ExtraSymbol(caption, symbol, layoutVals[index][layout[Menu_Layout_Symbol] - 1]); + } + + if(layout[Menu_Layout_InfoCaption]) + { + infoCaption = Format(infoCaption, layoutVals[index][layout[Menu_Layout_InfoCaption] - 1]); + } +} + +func AdaptorGetLimits(array limits) +{ + var ret = CreateArray(2); + if(!limits) + { + limits = [Menu_Adaptor_Limits_Min, Menu_Adaptor_Limits_Max]; + } + + for(var i = 0; i < 2; ++i) + { + if(!limits[i] || GetType(limits[i]) == C4V_Int) + { + ret[i] = limits[i]; + } + else + { + ret[i] = Call(limits[i], limits[3]); + } + } + + return ret; +} + +func AdaptorFactory(args, int entryIndex) +{ + var origArgs = args; + var entry = args[0]; + var entrySettings = args[2]; + args = args[1]; + var caption = entry[DT_Menu_Entry_Caption]; + var infoCaption = entry[DT_Menu_Entry_InfoCaption]; + var symbol = entry[DT_Menu_Entry_Symbol]; + var val = ScopedVar(args[DT_Menu_Adaptor_Variable]); + var defaultMsgboardText = "$EnterValue$"; + + var retSubmenu; + + args[DT_Menu_Adaptor_Args] = entry[DT_Menu_Entry_Args]; + args[DT_Menu_Adaptor_EntryIndex] = entryIndex; + args[DT_Menu_Adaptor_Callbacks] = args[DT_Menu_Adaptor_Callbacks] || []; + if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Boolean) + { + BooleanToggleCaption(val, caption, symbol); + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_String) + { + caption = Format(entry[DT_Menu_Entry_Caption], val); + defaultMsgboardText = "$EnterText$"; + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Integer) + { + var limits = AdaptorGetLimits(args[DT_Menu_Adaptor_Limits]), max, min; + var wrapAround = args[DT_Menu_Adaptor_WrapAround]; + if(limits && !wrapAround) + { + if(val >= limits[1]) + { + max = true; + } + if(val <= limits[0]) + { + min = true; + } + } + caption = Format("%s %s", Format(entry[DT_Menu_Entry_Caption], val), ["{{MN7I:4}}", "<c 808080>{{MN7I:4}}</c>"][min]); + ExtraSymbol(caption, symbol, [MN7I, 3, max && RGB(128, 128, 128)]); + defaultMsgboardText = "$EnterNumber$"; + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_ID) + { + caption = Format(entry[DT_Menu_Entry_Caption], val && GetName(0, val) || ""); + if(val) + { + ExtraSymbol(caption, symbol, val); + } + defaultMsgboardText = "$EnterIDOrName$"; + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Enum) + { + if(args[DT_Menu_Adaptor_EnumInline]) + { + return AdaptorEnumSubmenuFactory([args, entry]); + } + + var layoutVals = args[DT_Menu_Adaptor_LayoutVals]; + var layout = layoutVals[1]; + layoutVals = layoutVals[0]; + + var index = EnumValPos(layoutVals, layout, val, args[DT_Menu_Adaptor_EnumAllowUnknown]); + + if(index == -1) + { + if(!args[DT_Menu_Adaptor_EnumAllowUnknown]) + { + ScopedVar(args[DT_Menu_Adaptor_Variable]) = layoutVals[0][layout[Menu_Layout__ValuePos] - 1]; + } + index = 0; + } + + var submenuSymbol = symbol; + + EnumEntrySettings(caption, symbol, infoCaption, index, args, entry); + + if(args[DT_Menu_Adaptor_EnumSubmenu] != Menu_CallbackType_None) + { + args[DT_Menu_Adaptor_MessageBoardText] = args[DT_Menu_Adaptor_MessageBoardText] || defaultMsgboardText; + + retSubmenu = [Menu_Factory([MenuObjectCallback("AdaptorEnumSubmenuFactory")], [args, entry])]; + } + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_BitField) + { + return AdaptorBitFieldItemsFactory(origArgs); + } + args[DT_Menu_Adaptor_MessageBoardText] = args[DT_Menu_Adaptor_MessageBoardText] || defaultMsgboardText; + if(!retSubmenu) + { + return [Menu_Entry([Menu_Combined(entrySettings), Menu_Entry_Caption(caption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Args(args)])]; + } + else + { + return [Menu_SubMenu([Menu_Combined(entrySettings), Menu_Entry_Caption(caption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Args(args)], retSubmenu)]; + } +} + +func AdaptorEnumSubmenuFactory(array args) +{ + var entry = args[1]; + args = args[0]; + var layoutVals = args[DT_Menu_Adaptor_LayoutVals]; + var layout = layoutVals[1]; + layoutVals = layoutVals[0]; + + var submenuCaption = args[DT_Menu_Adaptor_EnumSubmenuCaption]; + var submenuSymbol = []; + + if(submenuCaption && submenuCaption[0]) + { + entry[DT_Menu_Entry_Caption] = submenuCaption[0]; + } + + var index = EnumValPos(layoutVals, layout, ScopedVar(args[DT_Menu_Adaptor_Variable]), args[DT_Menu_Adaptor_EnumAllowUnknown]); + var symbol, infoCaption; + EnumEntrySettings(submenuCaption, symbol, infoCaption, index, args, entry); + + var ret; + if(args[DT_Menu_Adaptor_EnumInline]) + { + ret = []; + } + else + { + ret = [ + Menu_Selection(index), + Menu_KeepParentOnClose(), + Menu_DontKeepOpen(), + Menu_Caption(submenuCaption) + ]; + } + + if(args[DT_Menu_Adaptor_EnumSubmenuSymbol]) + { + submenuSymbol = [args[DT_Menu_Adaptor_EnumSubmenuSymbol]]; + } + else if(!submenuSymbol && symbol && GetType(symbol[0]) == C4V_C4ID) + { + submenuSymbol = symbol; + } + + if(submenuSymbol && GetType(submenuSymbol[0]) == C4V_C4ID) + { + ArrayAppend(ret, Menu_Symbol(submenuSymbol[0])); + } + + var caption; + + var submenuCaption = args[DT_Menu_Adaptor_EnumSubmenuCaption]; + if(submenuCaption && submenuCaption[1]) + { + entry[DT_Menu_Entry_Caption] = submenuCaption[1]; + } + + for(var i = 0; i < GetLength(layoutVals); ++i) + { + symbol = 0; + EnumEntrySettings(caption, symbol, infoCaption, i, args, entry); + if(args[DT_Menu_Adaptor_EnumInline]) + { + BooleanToggleCaption(i == index, caption, symbol); + } + ArrayAppend(ret, Menu_Entry([Menu_Entry_Caption(caption), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorEnumSubmenuItem"), Menu_CallbackType_Defaults, [Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Args([i, args])])); + } + + return ret; +} + +func AdaptorEnumSubmenuItem(args, array allArgs) +{ + var index = args[0]; + args = args[1]; + + var val = ScopedVar(args[DT_Menu_Adaptor_Variable]); + var oldVal = val; + + var enumVals = args[DT_Menu_Adaptor_LayoutVals]; + var layout = enumVals[1]; + enumVals = enumVals[0]; + + ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = enumVals[index][layout[Menu_Layout__ValuePos] - 1]; + + allArgs[Menu_CallbackArg_Args] = args[DT_Menu_Adaptor_Args]; + allArgs[Menu_CallbackArg_FromSubmenu] = true; + + var reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs); + if(reaction != Menu_React_None) + { + return reaction; + } + + if(args[DT_Menu_Adaptor_EnumInline]) + { + return Menu_React_Refresh; + } + + return Menu_React_Back; +} + +func AdaptorBitFieldItemsFactory(args) +{ + var entry = args[0]; + var entrySettings = args[2]; + args = args[1]; + + var caption = entry[DT_Menu_Entry_Caption]; + var infoCaption = entry[DT_Menu_Entry_InfoCaption]; + var symbol = entry[DT_Menu_Entry_Symbol]; + var fieldValue = ScopedVar(args[DT_Menu_Adaptor_Variable]); + + var layoutVals = args[DT_Menu_Adaptor_LayoutVals]; + var layout = layoutVals[1]; + layoutVals = layoutVals[0]; + + var ret = []; + + for(var val in layoutVals) + { + var mask = val[layout[Menu_Layout__ValuePos] - 1]; + caption = entry[DT_Menu_Entry_Caption] || "%s"; + if(layout[Menu_Layout__CaptionPos]) + { + caption = Format(caption, val[layout[Menu_Layout__CaptionPos] - 1]); + } + + symbol = entry[DT_Menu_Entry_Symbol]; + BooleanToggleCaption((fieldValue & mask) == mask, caption, symbol); + if(layout[Menu_Layout_Symbol]) + { + ExtraSymbol(caption, symbol, val[layout[Menu_Layout_Symbol] - 1]); + } + + if(layout[Menu_Layout_InfoCaption]) + { + infoCaption = Format(infoCaption, val[layout[Menu_Layout_InfoCaption] - 1]); + } + + args[DT_Menu_Adaptor_Mask] = mask; + + ArrayAppend(ret, Menu_Entry([Menu_Combined(entrySettings), Menu_Entry_Caption(caption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Args(args)])); + } + + return ret; +} + +func WrapOrBind(int val, array limits, bool wrap) +{ + if(!limits) + { + return val; + } + + var min = limits[0]; + var max = limits[1]; + + if(val < min) + { + if(wrap) + { + return max; + } + else + { + return min; + } + } + else if(val > max) + { + if(wrap) + { + return min; + } + else + { + return max; + } + } + else + { + return val; + } +} + +func AdaptorCommandCallChangedCallback(callbacks, val, oldVal, array allArgs) +{ + allArgs[Menu_CallbackArg_Action] = Menu_CallbackType_ValueChanged; + allArgs[Menu_CallbackArg_NewValue] = val; + allArgs[Menu_CallbackArg_OldValue] = oldVal; + + return CallCallbacks(callbacks, Menu_CallbackType_ValueChanged, allArgs, ...); +} + +func AdaptorCommand(int action, object obj, args, array allArgs) +{ + var val = ScopedVar(args[DT_Menu_Adaptor_Variable]); + var oldVal = val; + + allArgs[Menu_CallbackArg_Args] = args[DT_Menu_Adaptor_Args]; + + var reaction = CallCallbacks(args[DT_Menu_Adaptor_Callbacks], action, allArgs, Menu_React_Refresh, true); + + + if(reaction != Menu_React_Refresh) + { + return reaction; + } + if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Boolean) + { + if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2)) + { + ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = !val; + reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs); + if(reaction != Menu_React_None) + { + return reaction; + } + } + else + { + return Menu_React_KeepOpen; + } + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Integer) + { + var limits = AdaptorGetLimits(args[DT_Menu_Adaptor_Limits]); + var wrapAround = args[DT_Menu_Adaptor_WrapAround]; + var step = args[DT_Menu_Adaptor_StepSize]; + var stepSize = 1; + if(step) + { + stepSize = step[0]; + } + if(action == Menu_CallbackType_Special2) + { + if(msgBoardMode == 0) + { + msgBoardMode = Menu_AdaptorType_Integer + 1; + msgBoardEntry = args[DT_Menu_Adaptor_EntryIndex]; + CallMessageBoard(this, false, args[DT_Menu_Adaptor_MessageBoardText], GetOwner(obj)); + } + return Menu_React_KeepOpen; + } + else if(action == Menu_CallbackType_Normal) + { + val += stepSize; + } + else if(action == Menu_CallbackType_Close) + { + val -= stepSize; + } + else + { + return Menu_React_KeepOpen; + } + + val = WrapOrBind(val, limits, wrapAround); + + if(val != oldVal) + { + ScopedVar(args[DT_Menu_Adaptor_Variable]) = val; + reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs); + if(reaction != Menu_React_None) + { + return reaction; + } + } + else + { + return Menu_React_KeepOpen; + } + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_String) + { + if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2)) + { + if(msgBoardMode == 0) + { + msgBoardMode = Menu_AdaptorType_String + 1; + msgBoardEntry = args[DT_Menu_Adaptor_EntryIndex]; + CallMessageBoard(this, false, args[DT_Menu_Adaptor_MessageBoardText], GetOwner(obj)); + } + } + if(action == Menu_CallbackType_Close) + { + return Menu_React_None; + } + else + { + return Menu_React_KeepOpen; + } + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_ID) + { + if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2)) + { + if(msgBoardMode == 0) + { + msgBoardMode = Menu_AdaptorType_ID + 1; + msgBoardEntry = args[DT_Menu_Adaptor_EntryIndex]; + CallMessageBoard(this, false, args[DT_Menu_Adaptor_MessageBoardText], GetOwner(obj)); + } + } + return Menu_React_KeepOpen; + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Enum) + { + if(GetType(args[DT_Menu_Adaptor_EnumSubmenu]) == C4V_Bool) + { + if(action & Menu_CallbackType_Defaults) + { + return Menu_React_ShowSubmenu; + } + else + { + return Menu_React_None; + } + } + + if(args[DT_Menu_Adaptor_EnumSubmenu] & action) + { + return Menu_React_ShowSubmenu; + } + + var enumVals = args[DT_Menu_Adaptor_LayoutVals]; + var layout = enumVals[1]; + enumVals = enumVals[0]; + + var index = EnumValPos(enumVals, layout, val, args[DT_Menu_Adaptor_EnumAllowUnknown]); + var wrapAround = args[DT_Menu_Adaptor_WrapAround]; + + if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2)) + { + ++index; + } + else if(action == Menu_CallbackType_Close) + { + --index; + } + else + { + return Menu_React_None; + } + + index = WrapOrBind(index, [0, GetLength(enumVals) - 1], wrapAround); + + ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = enumVals[index][layout[Menu_Layout__ValuePos] - 1]; + reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs); + if(reaction != Menu_React_None) + { + return reaction; + } + } + else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_BitField) + { + if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2)) + { + var mask = args[DT_Menu_Adaptor_Mask]; + if((val & mask) == mask) + { + val &= ~mask; + } + else + { + val |= mask; + } + + ScopedVar(args[DT_Menu_Adaptor_Variable]) = val; + reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs); + if(reaction != Menu_React_None) + { + return reaction; + } + } + else + { + return Menu_React_KeepOpen; + } + } + + return Menu_React_Refresh; +} + +func InputCallback(string input, int plr) +{ + var entry = entries[msgBoardEntry]; + var args = entry[DT_Menu_Entry_Args]; + var callbackArgs = args[DT_Menu_Adaptor_Args]; + var oldVal = ScopedVar(args[DT_Menu_Adaptor_Variable]); + var val = input; + if(msgBoardMode - 1 == Menu_AdaptorType_Integer) + { + var int = ParseInt(input); + var limits = AdaptorGetLimits(args[DT_Menu_Adaptor_Limits]); + var step = args[DT_Menu_Adaptor_StepSize]; + if(GetType(int) == C4V_Int || GetType(int) == C4V_Any) + { + if(step && step[1]) + { + var s = step[0]; + if(GetType(step[1]) == C4V_Int) + { + s = step[1]; + } + int = ((int + s / 2) / s) * s; + } + if(limits) + { + int = BoundBy(int, limits[0], limits[1]); + } + ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = int; + } + else + { + val = oldVal; + } + } + else if(msgBoardMode - 1 == Menu_AdaptorType_String) + { + if(args[DT_Menu_Adaptor_NoEmptyString] && (!input || input == "")) + { + val = oldVal; + } + else + { + ScopedVar(args[DT_Menu_Adaptor_Variable]) = input; + } + } + else if(msgBoardMode - 1 == Menu_AdaptorType_ID) + { + val = GetIDByName(input); // WARNING: desyncs between clients with different languages + if(!val && GetLength(input) == 4) val = C4Id(input); + if(!GetName(0, val)) val = 0; + ScopedVar(args[DT_Menu_Adaptor_Variable]) = val; + } + msgBoardMode = 0; + if(val != oldVal) + { + var reaction = CallCallbacks(args[DT_Menu_Adaptor_Callbacks], Menu_CallbackType_ValueChanged, [Menu_CallbackType_ValueChanged, entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], callbackArgs, 0, 0, val, oldVal]); + if(reaction != Menu_React_None) + { + return React(reaction, msgBoardEntry); + } + } + Refresh(msgBoardEntry); +} + +global func Menu_Combined(array combined) { return [DT_Menu_Combined, combined]; } + +global func CreateNewMenu(array menu, inheritSettings, object parentMenu) +{ + var settings = inheritSettings; + + if(GetType(settings) != C4V_Array) settings = []; + settings[DT_Menu_Settings_Style] &= ~C4MN_Style_EqualItemHeight; // EqualItemHeight can't be inherited + settings[DT_Menu_Settings_Selection] = 0; // Selection can't be inherited + settings[DT_Menu_Settings_Callbacks] = 0; // Global callbacks can't be inherited (maybe filter callbacks) + + settings[DT_Menu_Settings_Parent] = parentMenu; + + var entries = []; + + MN7I->UncombineAndDistinguish(menu, settings, entries); + + var menuObj = CreateObject(MN7I); + + menuObj->Create(settings, entries); + + return menuObj; +} + +func NamedArg(array namedArg, array &args) +{ + args[namedArg[0]] = namedArg[1]; +} + +func UncombineAndDistinguish(array combined, array &settings, array &entries) +{ + for(var val in combined) + { + if(val[0] == DT_Menu_Type_Setting) + { + if((val[1][0] == DT_Menu_Settings_Style && val[1][1] == C4MN_Style_EqualItemHeight) || settings[DT_Menu_Settings_Style] == C4MN_Style_EqualItemHeight) + { + settings[DT_Menu_Settings_Style] |= val[1][1]; + } + else + { + NamedArg(val[1], settings); + } + } + else if(val[0] == DT_Menu_Type_Entry || val[0] == DT_Menu_Type_Factory) + { + entries[GetLength(entries)] = val; + } + else if(val[0] == DT_Menu_Combined) + { + UncombineAndDistinguish(val[1], settings, entries); + } + } +} + +func NamedArgs(array namedArgs, array& args) +{ + for(var arg in namedArgs) + { + if(arg[0] == DT_Menu_Combined) + { + NamedArgs(arg[1], args); + } + else + { + NamedArg(arg, args); + } + } +} + +func DeclineAcceptBackCommand(int action, args, int defaultAction, array allArgs) +{ + if(args[0]) + { + allArgs[Menu_CallbackArg_Args] = args[1]; + var reaction = CallCallbacks(args[0], action, allArgs); + if(reaction != Menu_React_None) + { + return reaction; + } + } + + if(action & Menu_CallbackType_Defaults) + { + return defaultAction; + } + else + { + return Menu_React_None; + } +} + +func DeclineAcceptCommand(int action, args, array allArgs) +{ + return DeclineAcceptBackCommand(action, args, Menu_React_Close, allArgs); +} + +func BackCommand(int action, args, array allArgs) +{ + return DeclineAcceptBackCommand(action, args, Menu_React_Back, allArgs); +} + +func MenuObjectCallback(string name) +{ + return [CallbackTarget_Scenario - 1, name]; +} + +func CallCustom(callback, args) +{ + if(GetType(callback) == C4V_Array && GetLength(callback) == 2 && callback[0] == CallbackTarget_Scenario - 1) + { + return CallA(ObjectCallback(callback[1], this), args, ...); + } + else + { + return _inherited(callback, args, ...); + } +} + +func CustomScopedVar(array variable) +{ + if(variable && variable[0] == DT_Menu_MenuVar) + { + return ArrayVar(variable[1], LocalVar("vars")); + } + else + { + return _inherited(variable, ...); + } +} + +func CheckCustomScopedVar(array variable) +{ + if(variable && variable[0] == DT_Menu_MenuVar && ((GetType(variable[1]) == C4V_Int && variable[1] > 0) || variable[1] == 0)) + { + return true; + } + else + { + return _inherited(variable, ...); + } +} + +func Update() +{ + return Refresh(currentSelection); +} + +func GetSelection() +{ + return currentSelection; +} + +func GetObject() +{ + return settings[DT_Menu_Settings_Object]; +} + +func CreateSymbolDummy() +{ + return CreateContents(DT_Menu_SymbolDummy); +}
\ No newline at end of file diff --git a/DTMenu.c4d/StringTblDE.txt b/DTMenu.c4d/StringTblDE.txt new file mode 100644 index 0000000..85bc5b6 --- /dev/null +++ b/DTMenu.c4d/StringTblDE.txt @@ -0,0 +1,7 @@ +Accept=Bestätigen
+Decline=Abbrechen
+Back=Zurück
+EnterValue=Wert
+EnterNumber=Zahl
+EnterText=Text
+EnterIDOrName=ID oder Name
diff --git a/DTMenu.c4d/StringTblUS.txt b/DTMenu.c4d/StringTblUS.txt new file mode 100644 index 0000000..1a30f7d --- /dev/null +++ b/DTMenu.c4d/StringTblUS.txt @@ -0,0 +1,7 @@ +Accept=Accept
+Decline=Decline
+Back=Back
+EnterValue=Value
+EnterNumber=Number
+EnterText=Text
+EnterIDOrName=ID or name
diff --git a/DTMenu.c4d/SymbolDummy.c4d/DefCore.txt b/DTMenu.c4d/SymbolDummy.c4d/DefCore.txt new file mode 100644 index 0000000..1fd66a9 --- /dev/null +++ b/DTMenu.c4d/SymbolDummy.c4d/DefCore.txt @@ -0,0 +1,5 @@ +[DefCore]
+id=MS7I
+Name=Menu symbol dummy
+Version=4,9,10,8
+Category=C4D_StaticBack
diff --git a/DTMenu.c4d/SymbolDummy.c4d/Graphics.png b/DTMenu.c4d/SymbolDummy.c4d/Graphics.png Binary files differnew file mode 100644 index 0000000..6b1b504 --- /dev/null +++ b/DTMenu.c4d/SymbolDummy.c4d/Graphics.png diff --git a/DTMenu.c4d/SymbolDummy.c4d/Script.c b/DTMenu.c4d/SymbolDummy.c4d/Script.c new file mode 100644 index 0000000..4032726 --- /dev/null +++ b/DTMenu.c4d/SymbolDummy.c4d/Script.c @@ -0,0 +1,16 @@ +#strict 2 + +static const DT_Menu_SymbolDummy = MS7I; + +func SetSymbol(id symbol, int nr) +{ + SetGraphics(0, this, symbol); + SetPicture(GetDefCoreVal("Picture", "DefCore", symbol, 0) + nr * GetDefCoreVal("Picture", "DefCore", symbol, 2), GetDefCoreVal("Picture", "DefCore", symbol, 1), GetDefCoreVal("Picture", "DefCore", symbol, 2), GetDefCoreVal("Picture", "DefCore", symbol, 3)); + return this; +} + +func SetColor(int color) +{ + SetClrModulation(color); + return this; +} diff --git a/System.c4g/DTArrays.c b/System.c4g/DTArrays.c new file mode 100644 index 0000000..0b78ef4 --- /dev/null +++ b/System.c4g/DTArrays.c @@ -0,0 +1,52 @@ +#strict 2
+
+global func ArrayErase(array& arr, int index)
+{
+ if(index < GetLength(arr))
+ {
+ for(var i = index; i < GetLength(arr) - 1; ++i)
+ {
+ arr[i] = arr[i + 1];
+ }
+ SetLength(arr, GetLength(arr) - 1);
+ }
+}
+
+global func ArrayEraseItem(array& arr, item, bool multiple)
+{
+ var index, count = 0;
+ while((index = GetIndexOf2(item, arr)) != -1)
+ {
+ ArrayErase(arr, index);
+ if(!multiple)
+ {
+ return index;
+ }
+ ++count;
+ }
+ return (!multiple && -1) || count;
+}
+
+global func ArrayInsert(array& arr, int index, value)
+{
+ for(var i = GetLength(arr); i > index; --i)
+ {
+ arr[i] = arr[i - 1];
+ }
+ arr[index] = value;
+}
+
+global func ArrayAppend(array& arr, value)
+{
+ arr[GetLength(arr)] = value;
+}
+
+global func ArrayAppendArray(array& arr, array append)
+{
+ var i = GetLength(arr);
+ SetLength(arr, GetLength(arr) + GetLength(append));
+ for(var val in append)
+ {
+ arr[i++] = val;
+ }
+}
diff --git a/System.c4g/DTCallback.c b/System.c4g/DTCallback.c new file mode 100644 index 0000000..91730d2 --- /dev/null +++ b/System.c4g/DTCallback.c @@ -0,0 +1,397 @@ +/*-- Extensible callback system --*/ +#strict 2 + +static const CallbackTarget_Bind = -1; +static const CallbackTarget_Global = -2; +static const CallbackTarget_Scenario = -3; + +// NOTE: fast callbacks (fast = true for ObjectCallback or DefinitionCallback) use ObjectCall/DefinitionCall directly for greater performance if possible (refs == 0 and GetLength(args) <= 8 for CallA()) and thus must be real functions of the desired object/definition. safe mode is ignored for fast callbacks + +global func GlobalCallback(string name) { return [CallbackTarget_Global, name]; } +global func ScenarioCallback(string name) { return [CallbackTarget_Scenario, name]; } +global func ObjectCallback(string name, bool fast, object target) { return [target || this || FatalError("ObjectCallback without target object"), name, fast]; } +global func DefinitionCallback(string name, bool fast, id target) { return [target || GetID() || FatalError("DefinitionCallback without target definition"), name, fast]; } +global func Callback(string name, target) { return [target || this || GetID() || CallbackTarget_Global, name]; } +global func BindCallback(callback, array binding) { return [CallbackTarget_Bind, callback, binding]; } + +static const BindCallback_Bind_Value = -1; +static const BindCallback_Bind_Context = -2; +static const BindCallback_Bind_ContextDef = -3; +static const BindCallback_Bind_CallbackResult = -4; +static const BindCallback_Bind_ArgumentArrayPart = -5; +static const BindCallback_Bind_Reference = -6; +global func Bind(value) { return [BindCallback_Bind_Value, value]; } +global func Bind_Context() { return [BindCallback_Bind_Context]; } +global func Bind_ContextDef() { return [BindCallback_Bind_ContextDef]; } +global func Bind_CallbackResult(callback) { return [BindCallback_Bind_CallbackResult, callback]; } +global func Bind_ArgumentArrayPart(int argument, part) +{ + if(GetType(part) != C4V_Array) + { + part = [part]; + } + return [BindCallback_Bind_ArgumentArrayPart, argument, part]; +} +global func Bind_Reference(array scopedVar) { return [BindCallback_Bind_Reference, scopedVar]; } + +global func Call(callback) +{ + if(GetType(callback) == C4V_String) + { + return _inherited(callback, ...); + } + else + { + return CallA(callback, CreateFilledArray(...)); + } +} + +global func CallA(callback, args, bool safe, array refs) +{ + if(GetType(callback) == C4V_String) + { + callback = [this, callback, true]; + } + + if(GetType(callback) != C4V_Array) + { + return _inherited(callback, args, ...); + } + + var target = callback[0], function = callback[1]; + + if((GetType(target) == C4V_C4Object || GetType(target) == C4V_C4ID) && GetLength(callback) == 3 && callback[2] && (!refs || refs == []) && GetLength(args) <= 8) // fast callback + { + if(GetType(target) == C4V_C4Object) + { + return ObjectCall(target, function, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + } + else if(GetType(target) == C4V_C4ID) + { + return DefinitionCall(target, function, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); + } + } + + refs = refs || []; + + if(target == CallbackTarget_Scenario) + { + return GameCall(function, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); + } + else if(target == CallbackTarget_Bind) + { + return CallA(function, BindArgs(callback[2], args, safe, refs), safe, refs); + } + + if(GetType(target) == C4V_C4Object || GetType(target) == C4V_C4ID || target == CallbackTarget_Global) + { + var safeModifier = ""; + if(safe) + { + safeModifier = "~"; + } + var argsStr = "", pos = 0; + for(var arg in args) + { + if(pos > 0) + { + argsStr = Format("%s, ", argsStr); + } + if(refs[pos]) + { + argsStr = Format("%sScopedVar(%s)", argsStr, Serialize(arg)); + } + else + { + argsStr = Format("%s%s", argsStr, Serialize(arg)); + } + ++pos; + } + + if(target == CallbackTarget_Global) + { + return GlobalEval(Format("%s(%s)", function, argsStr)); + } + else if(GetType(target) == C4V_C4Object || GetType(target) == C4V_C4ID) + { + return eval(Format("%s->%s%s(%s)", Serialize(target), safeModifier, function, argsStr)); + } + } + else + { + if(this) + { + return this->~CallCustom(callback, args, ...); + } + else if(GetID()) + { + return GetID()->~CallCustom(callback, args, ...); + } + else + { + return CallCustom(callback, args, ...); + } + } +} + +global func CallCustom() { return _inherited(...); } + +global func BindArgs_ArgumentArrayPart(array binding, array args, bool safe) +{ + var ret = args[binding[0]]; + for(var i = 1; i < GetLength(binding); ++i) + { + if(!safe || GetType(ret) == C4V_Array) + { + ret = ret[binding[i]]; + } + else + { + return 0; + } + } + return ret; +} + +global func BindCustomArgs() { return _inherited(...); } // this allows "overloading" this function even if the "overloading" function is loaded before + +global func BindArgs(array binding, array args, bool safe, array& refs) +{ + refs = []; + var ret = CreateArray(GetLength(binding)), pos = 0; + for(var bind in binding) + { + if(GetType(bind) == C4V_Int || GetType(bind) == C4V_Any && bind >= 0) + { + ret[pos] = args[bind]; + } + else if(GetType(bind) == C4V_Array && GetLength(bind)) + { + if(bind[0] == BindCallback_Bind_Value) + { + ret[pos] = bind[1]; + } + else if(bind[0] == BindCallback_Bind_Context) + { + ret[pos] = this; + } + else if(bind[0] == BindCallback_Bind_ContextDef) + { + ret[pos] = GetID(); + } + else if(bind[0] == BindCallback_Bind_CallbackResult) + { + ret[pos] = CallA(bind[1], args, safe); + } + else if(bind[0] == BindCallback_Bind_ArgumentArrayPart) + { + ret[pos] = BindArgs_ArgumentArrayPart(bind[2], args[bind[1]], safe); + } + else if(bind[0] == BindCallback_Bind_Reference) + { + refs[pos] = true; + ret[pos] = bind[1]; + } + else if(GetType(bind[0]) == C4V_Int && bind[0] < 0) + { + if(this) + { + ret[pos] = this->~BindCustomArgs(bind, args, safe); + } + else if(GetID()) + { + ret[pos] = GetID()->~BindCustomArgs(bind, args, safe); + } + else + { + ret[pos] = BindCustomArgs(bind, args, safe); + } + } + else + { + ret[pos] = BindArgs(bind, args, safe); + } + } + else + { + FatalError(Format("BindArgs: incorrect binding %v", bind)); + } + ++pos; + } + return ret; +} + +global func GlobalEval(string code) +{ + var effect = AddEffect("IntGlobalEval", 0, 1); + var ret = EffectCall(0, effect, "Eval", code); + RemoveEffect(0, 0, effect); + return ret; +} + +global func FxIntGlobalEvalEval(object target, int effectNumber, string code) +{ + return eval(code); +} + +global func foreach(array arr, callback) +{ + var i = 0; + for(var val in arr) + { + CallA(callback, [val, i++]); + } +} + +global func CheckCallback(callback) +{ + if(GetType(callback) == C4V_String) + { + return true; + } + + if(GetType(callback) == C4V_Array) + { + if(GetType(callback[0]) == C4V_Int) + { + if(callback[0] == CallbackTarget_Bind && GetLength(callback) == 3) + { + if(CheckCallback(callback[1]) && CheckBindCallbackBinding(callback[2])) + { + return true; + } + } + else if(callback[0] == CallbackTarget_Global || callback[0] == CallbackTarget_Scenario) + { + if(GetLength(callback) == 2 && GetType(callback[1]) == C4V_String && callback[1] != "") + { + return true; + } + } + } + else if(GetType(callback[0]) == C4V_C4ID || GetType(callback[0]) == C4V_C4Object) + { + if(GetLength(callback) == 2 || (GetLength(callback) == 3 && (!callback[2] || GetType(callback[2]) == C4V_Bool))) // length 2 for callbacks created by Callback() or Object/DefinitionCallback() before fast was introduced + { + if(GetType(callback[1]) == C4V_String && callback[1] != "") + { + return true; + } + } + } + } + + if(this) + { + return this->~CheckCustomCallback(callback, ...); + } + else if(GetID()) + { + return GetID()->~CheckCustomCallback(callback, ...); + } + else + { + return CheckCustomCallback(callback, ...); + } +} + +// to silence the error if no ScopedVars are included +global func CheckScopedVar() { return _inherited(...); } + +global func CheckCustomCallback() { return _inherited(...); } + +global func CheckBindCallbackBinding(array binding) +{ + for(var b in binding) + { + if(b && GetType(b) != C4V_Int) + { + if(GetType(b) == C4V_Array) + { + if(b[0] == BindCallback_Bind_Value) + { + if(GetLength(b) != 2) + { + return false; + } + } + else if(b[0] == BindCallback_Bind_Context || b[0] == BindCallback_Bind_ContextDef) + { + continue; + } + else if(b[0] == BindCallback_Bind_CallbackResult) + { + if(GetLength(b) != 2 || !CheckCallback(b[1])) + { + return false; + } + } + else if(b[0] == BindCallback_Bind_ArgumentArrayPart) + { + if(GetLength(b) != 3) + { + return false; + } + + if(b[1] && (GetType(b[1]) != C4V_Int || b[1] < 0)) + { + return false; + } + + for(var part in b[2]) + { + if(part && (GetType(part) != C4V_Int || part < 0)) + { + return false; + } + } + } + else if(b[0] == BindCallback_Bind_Reference) + { + if(GetLength(b) != 2 || !CheckScopedVar(b[1])) + { + return false; + } + } + else if(GetType(b[0]) == C4V_Int && b[0] < 0) + { + var ret; + if(this) + { + ret = this->~CheckBindCallbackCustomBinding(b); + } + else if(GetID()) + { + ret = GetID()->~CheckBindCallbackCustomBinding(b); + } + else + { + ret = CheckBindCallbackCustomBinding(b); + } + if(!ret) + { + return false; + } + } + else + { + if(!CheckBindCallbackBinding(b)) + { + return false; + } + } + } + else + { + return false; + } + } + else if(b < 0) + { + return false; + } + } + return true; +} + +global func CheckBindCallbackCustomBinding() { return _inherited(...); }
\ No newline at end of file diff --git a/System.c4g/DTCrewSelection.c b/System.c4g/DTCrewSelection.c new file mode 100644 index 0000000..4e12db1 --- /dev/null +++ b/System.c4g/DTCrewSelection.c @@ -0,0 +1,12 @@ +#strict 2 +#appendto CLNK + +func Initialize() +{ + if(!FindObject(CS7I)) + { + CreateObject(CS7I, 0, 0, NO_OWNER); + } + + return _inherited(...); +}
\ No newline at end of file diff --git a/System.c4g/DTScopedVars.c b/System.c4g/DTScopedVars.c new file mode 100644 index 0000000..0cde670 --- /dev/null +++ b/System.c4g/DTScopedVars.c @@ -0,0 +1,108 @@ +#strict 2 +// TODO: ScopedVars with temporary target (like a reference to a function local var) + +static const ScopedVar_Global = 1; +static const ScopedVar_Local = 2; +static const ScopedVar_ArrayIndex = 3; + +global func GlobalVar(name) { return [ScopedVar_Global, name]; } +global func LocalVar(name, object target) { return [ScopedVar_Local, name, target || this]; } +global func ArrayVar(index, array scopedVar) { return [ScopedVar_ArrayIndex, scopedVar, index]; } + +global func &ScopedVar(array variable) +{ + if(!variable) + { + return 0; + } + if(variable[0] == ScopedVar_Global) + { + if(GetType(variable[1]) == C4V_String) + { + return GlobalN(variable[1]); + } + else + { + return Global(variable[1]); + } + } + else if(variable[0] == ScopedVar_Local) + { + if(GetType(variable[1]) == C4V_String) + { + return LocalN(variable[1], variable[2]); + } + else + { + return Local(variable[1], variable[2]); + } + } + else if(variable[0] == ScopedVar_ArrayIndex) + { + var index = variable[2]; + if(GetType(index) != C4V_Int && GetType(index) != C4V_Any) + { + index = ScopedVar(index); + } + return ScopedVar(variable[1])[index]; + } + else + { + if(this) + { + return ScopedVar(this->~CustomScopedVar(variable)); + } + else if(GetID()) + { + return ScopedVar(GetID()->~CustomScopedVar(variable)); + } + else + { + return ScopedVar(CustomScopedVar(variable)); + } + } +} + +global func CustomScopedVar() { return _inherited(...); } // this allows "overloading" this function even if the "overloading" function is loaded before + +global func CheckScopedVar(array variable) +{ + if(!variable) + { + return false; + } + + if(GetType(variable[0]) == C4V_Int) + { + if(variable[0] == ScopedVar_Global || variable[0] == ScopedVar_Local) + { + if(!variable[1] || GetType(variable[1]) == C4V_Int || (GetType(variable[1]) == C4V_String && variable[1] != "")) + { + return true; + } + } + + if(variable[0] == ScopedVar_ArrayIndex) + { + if(CheckScopedVar(variable[1]) && !variable[2] || (GetType(variable[2]) == C4V_Int && variable[2] >= 0)) + { + return true; + } + } + } + + if(this) + { + return this->~CheckCustomScopedVar(variable); + } + else if(GetID()) + { + return GetID()->~CheckCustomScopedVar(variable); + } + else + { + return CheckCustomScopedVar(variable); + } +} + +global func CheckCustomScopedVar() { return _inherited(...); }
\ No newline at end of file diff --git a/System.c4g/DTUtility.c b/System.c4g/DTUtility.c new file mode 100644 index 0000000..d491567 --- /dev/null +++ b/System.c4g/DTUtility.c @@ -0,0 +1,817 @@ +#strict 2
+
+global func Isolate(object obj) // Puts the object in its own layer to prevent interaction
+{
+ obj || obj = this;
+ return obj->SetObjectLayer(obj);
+}
+
+global func GetIndexOf2(val, array arr) // Because GetIndexOf doesn't find Format("%c", GetChar("a", 0)) in ["a"]
+{
+ var i = 0;
+ for(var value in arr)
+ {
+ if(val == value) return i;
+ ++i;
+ }
+ return -1;
+}
+
+global func Serialize(value)
+{
+ if(GetType(value) == C4V_Array)
+ {
+ var first = true;
+ var ret = "[";
+ for(var val in value)
+ {
+ if(first)
+ {
+ first = false;
+ }
+ else
+ {
+ ret = Format("%s,", ret);
+ }
+ ret = Format("%s%s", ret, Serialize(val));
+ }
+ return Format("%s]", ret);
+ }
+ else if(GetType(value) == C4V_Bool) if(value) return "true"; else return "false";
+ else if(GetType(value) == C4V_C4ID) return Format("%i", value);
+ else if(GetType(value) == C4V_C4Object) return Format("Object(%d)", ObjectNumber(value));
+ else if(GetType(value) == C4V_Int || GetType(value) == C4V_Any) return Format("%d", value);
+ else if(GetType(value) == C4V_String) return Format("\"%s\"", EscapeString(value));
+}
+
+global func EscapeString(string value)
+{
+ var ret = "";
+ for(var i = 0; i < GetLength(value); ++i)
+ {
+ var c = GetChar(value, i);
+ if(c == 34) // "
+ {
+ ret = Format("%s\\\"", ret);
+ }
+ else if(c == 92) // \
+ {
+ ret = Format("%s\\\\", ret);
+ }
+ else
+ {
+ ret = Format("%s%c", ret, c);
+ }
+ }
+ return ret;
+}
+
+global func CreateEnum(string prefix, array enum)
+{
+ var log = Format("%c// This enum was created with CreateEnum(%s, %s);%c", 10, Serialize(prefix), Serialize(enum), 10);
+ var switch = "";
+
+ for(var i = 0; i < GetLength(enum); ++i)
+ {
+ log = Format("%s%cstatic const %s%s = %d;", log, 10, prefix, enum[i], i);
+
+ if(switch == "")
+ {
+ switch = Format("if(switchval == %s%s)%c{%c%c}%c", prefix, enum[i], 10, 10, 10, 10);
+ }
+ else
+ {
+ switch = Format("%selse if(switchval == %s%s)%c{%c%c}%c", switch, prefix, enum[i], 10, 10, 10, 10);
+ }
+ }
+
+ Log("%s%c%c// Template if-structure - replace switchval with the value to compare.%c%c%s", log, 10, 10, 10, 10, switch);
+}
+
+global func CreateBitField(string prefix, array flags)
+{
+ if(GetLength(flags) > 32)
+ {
+ FatalError("CreateBitField: A bitfield can only have a maximum of up to 32 flags.");
+ return;
+ }
+
+ var log = Format("%c// This bitfield was created with CreateBitField(%s, %s);%c", 10, Serialize(prefix), Serialize(flags), 10);
+
+ for(var i = 0, j = 1; i < GetLength(flags); ++i)
+ {
+ log = Format("%s%cstatic const %s%s = %d;", log, 10, prefix, flags[i], j);
+ j = j << 1;
+ }
+
+ Log("%s%c%c", log, 10, 10);
+}
+
+// pos >= 0 => begin at pos | pos < 0 => begin at GetLength(string) - pos
+// count = 0 => replace all | count > 0 replace a maximum of count occurences from starting from pos
+
+global func Lowercase(int c)
+{
+ if((c >= 65 && c <= 90) || (c >= 192 && c <= 214) || (c >= 216 && c <= 221)) return c + 32;
+ else return c;
+}
+
+global func StringLowercase(string string)
+{
+ var ret = "";
+ for(var i = 0; i < GetLength(string); ++i)
+ {
+ ret = Format("%s%c", ret, Lowercase(GetChar(string, i)));
+ }
+ return ret;
+}
+
+global func MultiStringLowercase(array strings)
+{
+ var ret = [];
+ for(var string in strings)
+ {
+ ret[GetLength(ret)] = StringLowercase(string);
+ }
+ return ret;
+}
+
+global func CaseInsensitive(int c, bool caseSensitive) // TODO
+{
+ if(caseSensitive) return c;
+ else return Lowercase(c);
+}
+
+global func StringConcatenate(array strings)
+{
+ var ret = strings[0];
+
+ for(var i = 1; i < GetLength(strings); ++i)
+ {
+ ret = Format("%s%s", ret, strings[i]);
+ }
+
+ return ret;
+}
+
+global func MultiStringReplace(find, strings, replace, bool caseInsensitive, int count, int pos, int maxpos)
+{
+ var returnString = false;
+ if(GetType(strings) == C4V_String)
+ {
+ strings = [strings];
+ returnString = true;
+ }
+ else if(GetType(strings) != C4V_Array) return strings;
+ if(GetType(find) == C4V_String) find = [find];
+ else if(GetType(find) != C4V_Array) return strings;
+ if(GetType(replace) == C4V_String) replace = [find];
+ else if(GetType(replace) != C4V_Array) return strings;
+
+ var ret = [];
+
+ for(var i = 0; i < GetLength(strings); ++i)
+ {
+ ret[i] = strings[i];
+ for(var j = 0; j < GetLength(find); ++j)
+ {
+ ret[i] = StringReplace(find[j], ret[i], replace[j % GetLength(replace)], caseInsensitive, count, pos, maxpos);
+ }
+ }
+
+ if(returnString) return ret[0];
+ else return ret;
+}
+
+global func StringReplace(string find, string string, string replace, bool caseInsensitive, int count, int pos, int maxpos)
+{
+ if(GetLength(find) == 0 || GetLength(string) == 0) return string;
+ var ret = "";
+ if(pos < 0) pos = GetLength() - pos;
+ if(maxpos < 0) maxpos = GetLength(string) - pos;
+ if(maxpos == 0) maxpos = GetLength(string);
+ if(pos < 0 || pos > GetLength(string) || maxpos < 0 || maxpos > GetLength(string) || count < 0) return string;
+ if(count == 0) count = -1;
+
+ for(var i = 0; i < pos; ++i)
+ {
+ ret = Format("%s%c", ret, GetChar(string, i));
+ }
+
+ var match = 0;
+
+ var i;
+
+ for(i = pos; i + GetLength(find) - match - 1 < maxpos && count != 0; ++i)
+ {
+ var c = GetChar(string, i);
+ if(CaseInsensitive(c, !caseInsensitive) == CaseInsensitive(GetChar(find, match), !caseInsensitive))
+ {
+ if(++match == GetLength(find))
+ {
+ ret = StringConcatenate([ret, replace]);
+ match = 0;
+ --count;
+ }
+ }
+ else
+ {
+ if(match != 0)
+ {
+ for(var j = i - match; j < i; ++j)
+ {
+ ret = Format("%s%c", ret, GetChar(string, j));
+ }
+ }
+ match = 0;
+ ret = Format("%s%c", ret, GetChar(string, i));
+ }
+ }
+
+ for(i -= match; i < GetLength(string); ++i)
+ {
+ ret = Format("%s%c", ret, GetChar(string, i));
+ }
+
+ return ret;
+}
+
+global func StringReplaceMulti(string find, string string, array replace)
+{
+ var ret = [];
+ for(var rep in replace)
+ {
+ ret[GetLength(ret)] = StringReplace(find, string, rep);
+ }
+ return ret;
+}
+
+global func MultiStringReplaceMulti(string string, array findReplace)
+{
+ var ret = [];
+
+ for(var j = 0; j < GetLength(findReplace[0][1]); ++j)
+ {
+ var part = string;
+ for(var i = 0; i < GetLength(findReplace); ++i)
+ {
+ part = StringReplace(findReplace[i][0], part, findReplace[i][1][j]);
+ }
+ ret[j] = part;
+ }
+
+ return ret;
+}
+
+global func GetMapValue(key, array map, default)
+{
+ for(var val in map)
+ {
+ if(val[0] == key) return val;
+ }
+ return default;
+}
+
+global func GetIDByName(string name, int category) // WARNING: desyncs between clients with different languages
+{
+ var i, id;
+ while(id = GetDefinition(i++, category))
+ {
+ if(GetName(0, id) == name) return id;
+ }
+ return 0;
+}
+
+global func GetIDsByName(string name, int category) // WARNING: desyncs between clients with different languages
+{
+ var i, id, ret = [];
+ while(id = GetDefinition(i++, category))
+ {
+ if(GetName(0, id) == name)
+ {
+ ret[GetLength(ret)] = id;
+ }
+ }
+ return ret;
+}
+
+global func ArrayConcatenate(array arrays)
+{
+ var ret = [];
+ for(var arr in arrays)
+ {
+ for(var val in arr)
+ {
+ ret[GetLength(ret)] = val;
+ }
+ }
+ return ret;
+}
+
+global func SetPlrExtraDataString(int plr, string name, string data)
+{
+ var part = 0;
+ var i;
+ for(i = 0; i < GetLength(data); ++i)
+ {
+ var c = GetChar(data, i);
+
+ if((i % 4) == 0 && i != 0)
+ {
+ SetPlrExtraData(plr, Format("%s%d", name, i/4 - 1), part);
+ part = 0;
+ }
+
+ part |= (c << ((i % 4) * 8));
+ }
+
+ if(part != 0)
+ {
+ SetPlrExtraData(plr, Format("%s%d", name, i/4), part);
+ }
+
+ i = i/4 + 1;
+ while(GetPlrExtraData(plr, Format("%s%d", name, i)))
+ {
+ SetPlrExtraData(plr, Format("%s%d", name, i++), 0);
+ }
+}
+
+global func GetPlrExtraDataString(int plr, string name)
+{
+ var ret = "";
+ var part = true;
+ for(var i = 0; part != 0; ++i)
+ {
+ part = CastInt(GetPlrExtraData(plr, Format("%s%d", name, i)));
+ for(var j = 0; j < 4; ++j)
+ {
+ var c = (part >> (j * 8)) & 0xFF;
+ if(c == 0)
+ {
+ part = 0;
+ break;
+ }
+ else
+ {
+ ret = Format("%s%c", ret, c);
+ }
+ }
+ }
+ return ret;
+}
+
+global func SetPlrExtraDataArray(int plr, string name, array arr)
+{
+ return SetPlrExtraDataString(plr, name, Serialize(arr));
+}
+
+global func GetPlrExtraDataArray(int plr, string name)
+{
+ var function = GetPlrExtraDataString(plr, name);
+ if(function != "")
+ {
+ return eval(function);
+ }
+}
+
+global func SetPlrExtraDataIntArray(int plr, string name, array arr)
+{
+ var lengthName = Format("%sL", name);
+
+ var oldLength = CastInt(GetPlrExtraData(plr, lengthName));
+
+ var length = GetLength(arr);
+
+ for(var i = 0; i < length; ++i)
+ {
+ var partName = Format("%s%d", name, i);
+ if(arr[i] != 0)
+ {
+ SetPlrExtraData(plr, partName, arr[i]);
+ }
+ else if(GetPlrExtraData(plr, partName) != 0)
+ {
+ SetPlrExtraData(plr, partName, 0);
+ }
+ }
+
+ SetPlrExtraData(plr, lengthName, length);
+
+ for(var i = length; i < oldLength; ++i)
+ {
+ var partName = Format("%s%d", name, i);
+ if(GetPlrExtraData(plr, partName) != 0)
+ {
+ SetPlrExtraData(plr, partName, 0);
+ }
+ }
+}
+
+global func GetPlrExtraDataIntArray(int plr, string name)
+{
+ var length = CastInt(GetPlrExtraData(plr, Format("%sL", name)));
+
+ var ret = CreateArray(length);
+
+ for(var i = 0; i < length; ++i)
+ {
+ ret[i] = CastInt(GetPlrExtraData(plr, Format("%s%d", name, i)));
+ }
+
+ return ret;
+}
+
+global func EnableScripting()
+{
+ AddMsgBoardCmd("s", "ScriptingFunc(\"%s\", %player%)", C4MSGCMDR_Escaped);
+}
+
+global func ScriptingFunc(string script, int player)
+{
+ Log("-> %s", script);
+ Log("= %v (by %s)", eval(script), GetTaggedPlayerName(player));
+}
+
+global func ParseInt(string int, bool hex)
+{
+ if(GetLength(int) == 0)
+ {
+ return "";
+ }
+ var ret = 0, neg = false;
+ for(var i = 0; i < GetLength(int); ++i)
+ {
+ var c = GetChar(int, i);
+ if(i == 0)
+ {
+ if(c == 45) // "-"
+ {
+ neg = true;
+ continue;
+ }
+ else if(c == 43) // "+"
+ {
+ continue;
+ }
+ }
+ c -= 48; // "0" == 48
+ if(c < 0 || ((!hex && c > 9) || (hex && c > 54))) // "f" - "0" == 54
+ {
+ return "";
+ }
+ if(hex)
+ {
+ ret = ret << 4;
+ }
+ else
+ {
+ ret *= 10;
+ }
+ if(c > 9)
+ {
+ c -= 39;
+ }
+ ret += c;
+ }
+ return ret * (1 - (2 * neg));
+}
+
+global func ParseFloat(string float, int precision) // precision = number of digits after decimal point and multiplier of 10^precision
+{
+ precision = precision || 3;
+ if(GetLength(float) == 0)
+ {
+ return "";
+ }
+ var ret = 0, neg = false, decimalPoint = -1;
+ for(var i = 0; i < GetLength(float); ++i)
+ {
+ if(decimalPoint != -1 && decimalPoint + precision < i)
+ {
+ // out of precision
+ break;
+ }
+
+ var c = GetChar(float, i);
+ if(i == 0)
+ {
+ if(c == 45) // "-"
+ {
+ neg = true;
+ continue;
+ }
+ else if(c == 43) // "+"
+ {
+ continue;
+ }
+ }
+ c -= 48; // "0" == 48
+ if(c < 0 || c > 9)
+ {
+ if((c == -2 || c == -4) && decimalPoint == -1) // "." || ","
+ {
+ decimalPoint = i;
+ continue;
+ }
+ else
+ {
+ return "";
+ }
+ }
+ ret *= 10;
+ ret += c;
+ }
+ if(decimalPoint == -1)
+ {
+ decimalPoint = i - 1;
+ }
+
+ for(; i <= decimalPoint + precision; ++i)
+ {
+ ret *= 10;
+ }
+
+ return ret * (1 - (2 * neg));
+}
+
+global func CreateFilledArray()
+{
+ var ret = [];
+ for(var i = 9; i >= 0; --i)
+ {
+ if(Par(i))
+ {
+ ret[i] = Par(i);
+ }
+ }
+ return ret;
+}
+
+global func Find_Procedure(string proc)
+{
+ return [C4FO_Func, "Find_ProcedureCheck", proc];
+}
+
+global func Find_ProcedureCheck(string proc)
+{
+ return proc == GetProcedure();
+}
+
+global func GetSolidMask(object obj)
+{
+ obj = obj || this;
+ return
+ [
+ GetObjectVal("SolidMask", 0, obj, 0), GetObjectVal("SolidMask", 0, obj, 1),
+ GetObjectVal("SolidMask", 0, obj, 2), GetObjectVal("SolidMask", 0, obj, 3),
+ GetObjectVal("SolidMask", 0, obj, 4), GetObjectVal("SolidMask", 0, obj, 5)
+ ];
+}
+
+// getValue:
+// 0 - offset index (the preferred way is to always get the offset index first and to pass the result to following calls for performance reasons)
+// 1 - overlay ID
+// 2 - overlay mode
+// 3 - graphics definition ID
+// 4 - graphics name
+// 5 - action name
+// 6 - blit mode
+// 7 - index of Transformation-matrix
+// 8 - enumerated overlay object (i.e. object number of the overlay object); WARNING: this is only valid and true directly after saving or loading a savegame
+
+global func GetOverlayValueByIndex(int index, int startIndex, int getValue, object obj) // startIndex can be given as a valid index (retrieved with this function beforehand) and the asked index is used as offset (better performance for iterating over all indices)
+{
+ if(getValue < 0 || getValue > 8)
+ {
+ // invalid getValues could cause infinite loops
+ return -2;
+ }
+ obj = obj || this;
+ var found = false;
+ if(index == 0)
+ {
+ if(getValue == 0)
+ {
+ if(GetObjectVal("GfxOverlay", 0, obj, startIndex) == 0)
+ {
+ // there is no such overlay
+ return -1;
+ }
+ else
+ {
+ return startIndex;
+ }
+ }
+ else if(getValue == 1)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, startIndex);
+ }
+ found = true;
+ }
+ for(var i = startIndex + 1, foundIndex = 0; ;) // skip overlay ID
+ {
+ var val = GetObjectVal("GfxOverlay", 0, obj, i);
+ if(val == 0)
+ {
+ // there is no overlay left
+ return -1;
+ }
+ if(val == GFXOV_MODE_Object)
+ {
+ // this is the overlay mode GFXOV_MODE_Object. Graphics-Definition and -Name are left out
+ if(found && (getValue == 3 || getValue == 4))
+ {
+ return 0;
+ }
+ else if(found && getValue == 2)
+ {
+ return GFXOV_MODE_Object;
+ }
+ }
+ else if(GetType(val) == C4V_C4ID)
+ {
+ if(found)
+ {
+ if(getValue == 3)
+ {
+ return val;
+ }
+ else if(getValue == 4)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 1);
+ }
+ }
+ i += 2; // skip Graphics-Definition and -Name, so i points to OverlayMode here too
+ if(found && getValue == 2)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i);
+ }
+ }
+ else
+ {
+ //something went wrong already
+ return -3;
+ }
+
+ if(found)
+ {
+ if(getValue == 5)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 1);
+ }
+ else if(getValue == 6)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 2);
+ }
+ else if(getValue == 7)
+ {
+ return i + 4;
+ }
+ }
+
+ i += 10; // this is either the last row of the Transformation matrix or the Color-Modulation
+ val = GetObjectVal("GfxOverlay", 0, obj, i);
+
+ if(GetType(val) == C4V_String)
+ {
+ // part of the matrix, skip it
+ i += 3;
+ }
+
+ if(found && getValue == 8)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i + 2);
+ }
+
+ i += 3; // and the rest
+
+ if(++foundIndex == index)
+ {
+ if(GetObjectVal("GfxOverlay", 0, obj, i) == 0)
+ {
+ // there is no overlay left
+ return -1;
+ }
+ else
+ {
+ found = true;
+ if(getValue == 0)
+ {
+ return i;
+ }
+ else if(getValue == 1)
+ {
+ return GetObjectVal("GfxOverlay", 0, obj, i);
+ }
+ }
+ }
+
+ ++i; // skip the next overlay ID
+ }
+}
+
+global func GetOverlayValueOffsetByID(int overlay, object obj)
+{
+ obj = obj || this;
+
+ if(overlay != 0)
+ {
+ var offset = 0, diffIndex = 0;
+ while((offset = obj->GetOverlayValueByIndex(diffIndex, offset)) >= 0)
+ {
+ if(GetOverlayValueByIndex(0, offset, 1) == overlay)
+ {
+ return offset;
+ }
+ diffIndex = 1;
+ }
+ }
+
+ return -1;
+}
+
+global func GetObjDrawTransform(int overlay, int overlayOffset, object obj)
+{
+ obj = obj || this;
+ if(overlay == 0)
+ {
+ var first = GetObjectVal("DrawTransform", 0, obj, 0);
+ if(first == 0)
+ {
+ return [1000, 0, 0, 0, 1000, 0];
+ }
+ var ret = [ParseFloat(first, 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 1), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 2), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 3), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 4), 3), ParseFloat(GetObjectVal("DrawTransform", 0, obj, 5), 3)];
+
+ var rest = GetObjectVal("DrawTransform", 0, obj, 7); // 6 is left out intentionally, because its only FlipDir (used internally for Actions with FlipDir)
+ if(GetType(rest) == C4V_String) // the last 3 matrix-values are only decompiled if they are not the default of [0, 0, 1.0]
+ {
+ SetLength(ret, 9);
+ ret[6] = ParseFloat(rest, 3);
+ ret[7] = ParseFloat(GetObjectVal("DrawTransform", 0, obj, 8), 3);
+ ret[8] = ParseFloat(GetObjectVal("DrawTransform", 0, obj, 9), 3);
+ }
+
+ return ret;
+ }
+ else
+ {
+ var index = 0;
+ if(overlay == -1)
+ {
+ index = overlayOffset;
+ }
+ else
+ {
+ index = obj->GetOverlayValueOffsetByID(overlay);
+ }
+ if(index < 0)
+ {
+ return index;
+ }
+ index = obj->GetOverlayValueByIndex(0, index, 7);
+ if(index < 0)
+ {
+ return index;
+ }
+
+ var ret = [ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 1), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 2), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 3), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 4), 3), ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 5), 3)];
+
+ var rest = GetObjectVal("GfxOverlay", 0, obj, index + 7); // 6 is left out intentionally, because its only FlipDir (used internally for Actions with FlipDir)
+ if(GetType(rest) == C4V_String) // the last 3 matrix-values are only decompiled if they are not the default of [0, 0, 1.0]
+ {
+ SetLength(ret, 9);
+ ret[6] = ParseFloat(rest, 3);
+ ret[7] = ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 8), 3);
+ ret[8] = ParseFloat(GetObjectVal("GfxOverlay", 0, obj, index + 9), 3);
+ }
+
+ return ret;
+ }
+}
+
+global func GetShape(object obj)
+{
+ obj = obj || this;
+ return [GetObjectVal("Offset", 0, obj, 0), GetObjectVal("Offset", 0, obj, 1), GetObjectVal("Width", 0, obj), GetObjectVal("Height", 0, obj)];
+}
+
+global func InRect(array point, array rect)
+{
+ var x = point[0], y = point[1];
+ return rect[0] < x && x < rect[0] + rect[2] && rect[1] < y && y < rect[1] + rect[3];
+}
+
+global func GetPlayerByID(int id)
+{
+ for(var i = 0; i < GetPlayerCount(); ++i)
+ {
+ var plr = GetPlayerByIndex(i);
+ if(GetPlayerID(plr) == id)
+ {
+ return plr;
+ }
+ }
+
+ return NO_OWNER;
+}
|
