1 /* Global editor props for all objects */
2 
3 local Name = "EditorBase";
4 
5 // Do not create
Construction()6 public func Construction() { RemoveObject(); }
7 
8 local EditorProps;
9 local Plane = 1;
10 
11 local CountedID, IDList, AnyDef, IDSet, PlayerNumber, TeamID, PlayerMask;
12 local ItemPlusParameter, ItemPlusParameterOptionMap;
13 local ItemPlusParameterList;
14 
15 local DefinitionPriority=100; // Call this definition early to allow EditorProp initialization
Definition(def)16 func Definition(def)
17 {
18 	// Basic properties of all objects
19 	def.EditorProps = {
20 		Invincibility = { Name = "$Invincibility$", EditorHelp = "$InVincibilityHelp$", Type = "has_effect", Effect = "IntInvincible", Set = "SetInvincibility" },
21 		Visibility = { Type = "enum", Name = "$Visibility$", EditorHelp = "$VisibilityHelp$", Options = [ { Name="$Unknown$", Value=-1 }, { Name="$DefaultVisible$" }, { Name="$Visible$", Value=VIS_All }, { Name="$Invisible$", Value=VIS_None }, { Name="$EditorVisible$", Value=VIS_Editor } ] },
22 		PlayerColor = { Name="$PlayerColor$", EditorHelp="$PlayerColorHelp$", Type = "color", AsyncGet = "GetColor", Set = "SetColor" },
23 		ClrModulation = { Name="$ClrModulation$", EditorHelp="$ClrModulationHelp$", Type = "color", AsyncGet = "GetClrModulation", Set = "SetClrModulation" },
24 		BlitMode = { Name="$BlitMode$", EditorHelp="$BlitModeHelp$", Type = "enum", AsyncGet = "GetObjectBlitMode", Set = "SetObjectBlitMode", Options = [
25 			{ Name="$Unknown$", Value=-1 },
26 			{ Name="$Default$", Value=0 },
27 			{ Name="$Additive$", Value=GFX_BLIT_Additive|GFX_BLIT_Custom },
28 			{ Name="$Mod2$", EditorHelp="$Mod2Help$", Value=GFX_BLIT_Mod2|GFX_BLIT_Custom },
29 			{ Name="$Wireframe$", EditorHelp="$WireframeHelp$", Value=GFX_BLIT_Wireframe|GFX_BLIT_Custom } ] },
30 		Name = { Name="$Name$", Type = "string", AsyncGet = "GetName", Set = "SetName" },
31 		CustomInitializationScript = { Type = "string", Name = "$CustomInitialization$", EditorHelp = "$CustomInitializationHelp$" }
32 	};
33 	// Property delegate types
34 	CountedID = { Type = "proplist", Display = "{{count}}x{{id}}", DefaultValue = { count=1, id=nil }, Name = "$IDListEntry$", EditorProps = {
35 		count = { Type = "int", Min = 1 },
36 		id = { Type = "def" } } };
37 	IDList = { Name = "ID list", Type = "array", Display = 3, Elements = CountedID };
38 	AnyDef = { Type = "def" };
39 	IDSet = { Name = "ID set", Type = "array", Display = 5, Elements = AnyDef };
40 	PlayerNumber = { Type="int" };
41 	TeamID = { Type="int" };
42 	PlayerMask = { Name="$PlayerMask$", Type="enum", OptionKey="Option", Options = [
43 		{ Name="$None$" },
44 		{ Name="$All$", Value={ Option="all" } },
45 		{ Name="$Specific$", Value={ Option="number" }, ValueKey="Data", Delegate=PlayerNumber },
46 		{ Name="$Team$", Value={ Option="team" }, ValueKey="Data", Delegate=TeamID },
47 		] };
48 	// Item plus extra stuff (contents, stack, etc.)
49 	ItemPlusParameterOptionMap = {};
50 	ItemPlusParameter = { Name="$Item", Type="enum", Sorted=true, Options = [ { Name="$Nothing$", Priority=50 } ] };
51 	var itemdef, i = 0, n = 0, option, contents_def, j, n2, contents_defs, mat;
52 	while ((itemdef = GetDefinition(i++)))
53 		if (itemdef.Collectible || itemdef->~GetLiquidType())
54 		{
55 			var group = itemdef->GetDefinitionGroupPath();
56 			if (WildcardMatch(group, "Objects/Items/*"))
57 				group = ReplaceString(group, "Objects/Items/", ""); // Shortcut this group since most items will be here
58 			else
59 				group = "$Other$";
60 			option = { Name=itemdef->GetName(), Group=group, Value=itemdef };
61 			var def_id = Format("%i", itemdef);
62 			ItemPlusParameterOptionMap[def_id] = option;
63 			// Test various kinds of extra parameters for new items
64 			if (itemdef->~IsLiquidContainer() && itemdef->~GetLiquidContainerMaxFillLevel())
65 			{
66 				// Liquid container: Offer to fill with liquid
67 				j = 0; n2 = 0;
68 				contents_defs = [{Name="$Nothing$"}];
69 				while ((contents_def = GetDefinition(j++)))
70 					if ((mat = contents_def->~GetLiquidType()))
71 						if (itemdef->IsLiquidContainerForMaterial(mat))
72 							contents_defs[++n2] = contents_def;
73 				if (n2)
74 				{
75 					option.Value = { ItemPlusParameter="liquid", ID=itemdef };
76 					option.OptionKey = "ID";
77 					option.ValueKey = "Liquid";
78 					option.Delegate = { Name="$Liquid$", Type="enum", Sorted=true, Options=contents_defs }; // Options resolved later uisng ItemPlusParameterOptionMap
79 				}
80 			}
81 			else if (itemdef.ExtraSlotFilter)
82 			{
83 				// Extra slot objects: Offer contents
84 				j = 0; n2 = 0;
85 				contents_defs = [{Name="$Nothing$"}];
86 				while ((contents_def = GetDefinition(j++)))
87 					if (contents_def[itemdef.ExtraSlotFilter])
88 						if (contents_def->Call(itemdef.ExtraSlotFilter))
89 							contents_defs[++n2] = contents_def;
90 				if (n2)
91 				{
92 					option.Value = { ItemPlusParameter="contents", ID=itemdef };
93 					option.OptionKey = "ID";
94 					option.ValueKey = "Contents";
95 					option.Delegate = { Name="$Contents$", Type="enum", Sorted=true, Options=contents_defs }; // Options resolved later uisng ItemPlusParameterOptionMap
96 				}
97 			}
98 			else if (itemdef->~IsStackable())
99 			{
100 				// Stackable: Offer stack count
101 				option.Value = { ItemPlusParameter="stack", ID=itemdef };
102 				option.OptionKey = "ID";
103 				option.ValueKey = "StackCount";
104 				option.Delegate = { Name="$Contents$", Type="enum", Options=[
105 					{ Name=Format("$DefaultStack$", itemdef->InitialStackCount()) },
106 					{ Name="$CustomStack$", Type=C4V_Int, Value=itemdef->InitialStackCount(), Delegate={ Type="int", Min=1/*, Max=itemdef->MaxStackCount()*/ } }, // there's no reason to restrict the max stack in editor
107 					{ Name="$InfiniteStack$", Value="infinite" }
108 					]};
109 			}
110 			// Add to item list if it's an item
111 			// Do not add liquids; they're just processed here to get the stackable definition
112 			if (itemdef.Collectible) ItemPlusParameter.Options[++n] = option;
113 		}
114 	// Link item contents parameter menus, but ignore group because it's usually a low number of items anyway
115 	for (option in ItemPlusParameter.Options)
116 		if (option.ValueKey == "Contents" || option.ValueKey == "Liquid")
117 			for (i = 1; i < GetLength(option.Delegate.Options); ++i)
118 			{
119 				var option_item = ItemPlusParameterOptionMap[Format("%i", option.Delegate.Options[i])] ?? option.Delegate.Options[i];
120 				if (option_item.Prototype == Global)
121 					option.Delegate.Options[i] = { Name=option_item->GetName(), Value=option_item }; // Regular definition
122 				else
123 					option.Delegate.Options[i] = new option_item { Group=nil }; // Definition with extra parameter
124 			}
125 	ItemPlusParameterList = { Name = "$ItemPlusParameterList$", Type = "array", Display = 3, Elements = ItemPlusParameter };
126 	return true;
127 }
128 
129 // Check if given player is in mask
EvaluatePlayerMask(proplist mask,int player)130 public func EvaluatePlayerMask(proplist mask, int player)
131 {
132 	if (!mask) return false;
133 	var option = mask.Option;
134 	if (option == "all") return true;
135 	if (option == "number") return player == mask.Data;
136 	if (option == "team") return GetPlayerTeam(player) == mask.Data;
137 	// Unknown player mask option
138 	return false;
139 }
140 
141 // Evaluate player mask to list of players
EvaluatePlayers(proplist mask)142 public func EvaluatePlayers(proplist mask)
143 {
144 	if (!mask) return [];
145 	var result = [], n=0;
146 	for (var i = 0; i < GetPlayerCount(C4PT_User); ++i)
147 	{
148 		var plr = GetPlayerByIndex(i, C4PT_User);
149 		if (EvaluatePlayerMask(mask, plr)) result[n++] = plr;
150 	}
151 	return result;
152 }
153 
154 // Return an ID-List EditorProp with only IDs available that meet the condition
GetConditionalIDList(string condition,string name,proplist default_id,string help)155 public func GetConditionalIDList(string condition, string name, proplist default_id, string help)
156 {
157 	var counted_id = { Type = "proplist", Display = "{{count}}x{{id}}", Name = Format("$Entry$", name), EditorProps = {
158 		count = { Type = "int", Min = 1 },
159 		id = { Type = "def", Filter=condition } } };
160 	return { Name = name, Type = "array", Display = 3, DefaultValue = { count=1, id=default_id }, Elements = counted_id, EditorHelp = help };
161 }
162 
163 // Create item specieid in ItemsPlusParameters delegate
CreateItemPlusParameter(proplist param,int x,int y,int owner)164 public func CreateItemPlusParameter(proplist param, int x, int y, int owner)
165 {
166 	if (!param) return nil;
167 	var id;
168 	if (param.ItemPlusParameter) id = param.ID; else id = param;
169 	var obj = CreateObject(id, x, y, owner);
170 	return ApplyContentsPlusParameter(param, obj);
171 }
172 
CreateContentsPlusParameter(proplist param,object container)173 public func CreateContentsPlusParameter(proplist param, object container)
174 {
175 	if (!param || !container) return nil;
176 	var id;
177 	if (param.ItemPlusParameter) id = param.ID; else id = param;
178 	var obj = container->CreateContents(id);
179 	return ApplyContentsPlusParameter(param, obj);
180 }
181 
ApplyContentsPlusParameter(proplist param,object to_obj)182 private func ApplyContentsPlusParameter(proplist param, object to_obj)
183 {
184 	// Apply object contents or stack count setting
185 	if (to_obj && param.ItemPlusParameter)
186 	{
187 		if (param.ItemPlusParameter == "liquid")
188 		{
189 			CreateContentsPlusParameter(param.Liquid, to_obj);
190 		}
191 		else if (param.ItemPlusParameter == "contents")
192 		{
193 			CreateContentsPlusParameter(param.Contents, to_obj);
194 		}
195 		else if (param.ItemPlusParameter == "stack" && GetType(param.StackCount))
196 		{
197 			if (param.StackCount == "infinite")
198 			{
199 				to_obj->SetInfiniteStackCount();
200 			}
201 			else
202 			{
203 				to_obj->SetStackCount(param.StackCount);
204 			}
205 		}
206 	}
207 	return to_obj;
208 }
209