1 /**
2 	Base Material & Production
3 	Library to control the players base material and production. The initial values are read
4 	from the Scenario.txt entries and per script one can modify these by:
5      * GetBaseMaterial(int player, id material, int index, int category)
6      * SetBaseMaterial(int player, id material, int amount)
7      * DoBaseMaterial(int player, id material, int change)
8      * GetBaseProduction(int player, id material, int index, int category)
9      * SetBaseProduction(int player, id material, int amount)
10      * DoBaseProduction(int player, id material, int change)
11     Performs also two callbacks to a base of the player:
12      * OnBaseMaterialChange(id material, int change);
13      * OnBaseProductionChange(id material, int change);
14 
15 	@author Randrian, Maikel
16 */
17 
18 
19 // Local variables to store the player's material and production.
20 // Is an array filled with [id, count] arrays.
21 local base_material;
22 local base_production;
23 local production_unit = 0;
24 
25 // Maximum number of material.
26 static const BASEMATERIAL_MaxBaseMaterial = 25;
27 // Maximum number of production.
28 static const BASEMATERIAL_MaxBaseProduction = 10;
29 // Produce every X frames (currently set to a minute).
30 static const BASEMATERIAL_ProductionRate = 2160;
31 
32 
33 /*-- Global interface --*/
34 
GetBaseMaterial(int player,id material,int index,int category)35 global func GetBaseMaterial(int player, id material, int index, int category)
36 {
37 	var base = Library_BaseMaterial->GetBaseMaterialManager(player);
38 	if (base)
39 		return base->GetBaseMat(material, index, category);
40 }
41 
SetBaseMaterial(int player,id material,int amount)42 global func SetBaseMaterial(int player, id material, int amount)
43 {
44 	var base = Library_BaseMaterial->GetBaseMaterialManager(player);
45 	if (base)
46 		return base->SetBaseMat(material, amount);
47 }
48 
DoBaseMaterial(int player,id material,int change)49 global func DoBaseMaterial(int player, id material, int change)
50 {
51 	var base = Library_BaseMaterial->GetBaseMaterialManager(player);
52 	if (base)
53 		return base->DoBaseMat(material, change);
54 }
55 
GetBaseProduction(int player,id material,int index,int category)56 global func GetBaseProduction(int player, id material, int index, int category)
57 {
58 	var base = Library_BaseMaterial->GetBaseMaterialManager(player);
59 	if (base)
60 		return base->GetBaseProd(material, index, category);
61 }
62 
SetBaseProduction(int player,id material,int amount)63 global func SetBaseProduction(int player, id material, int amount)
64 {
65 	var base = Library_BaseMaterial->GetBaseMaterialManager(player);
66 	if (base)
67 		return base->SetBaseProd(material, amount);
68 }
69 
DoBaseProduction(int player,id material,int change)70 global func DoBaseProduction(int player, id material, int change)
71 {
72 	var base = Library_BaseMaterial->GetBaseMaterialManager(player);
73 	if (base)
74 		return base->DoBaseProd(material, change);
75 }
76 
77 
78 /*-- Definition Interface --*/
79 
GetBaseMaterialManager(int player)80 protected func GetBaseMaterialManager(int player)
81 {
82 	var base = FindObject(Find_ID(Library_BaseMaterial), Find_AnyLayer(),  Find_Owner(player));
83 	if (!base)
84 	{
85 		base = CreateObject(Library_BaseMaterial, 0, 0, player);
86 	}
87 	return base;
88 }
89 
90 /*-- Object Interface --*/
91 
Initialize()92 protected func Initialize()
93 {
94 	// Gather base materials based on Scenario.txt player entries.
95 	// TODO: Check teams and get the fitting player section
96 	var player = GetOwner() % 4 + 1;
97 	var section = Format("Player%d", player);
98 
99 	// Initialize arrays for material and production.
100 	base_material = [];
101 	base_production = [];
102 
103 	// Load materials from Scenario.txt
104 	var index;
105 	var material, count;
106 	while (true)
107 	{
108 		material = GetScenarioVal("BaseMaterial", section, index * 2);
109 		count = GetScenarioVal("BaseMaterial", section, index * 2 + 1);
110 		if (!material && !count) break;
111 		if (material)
112 		{
113 			PushBack(base_material, [material, count]);
114 		}
115 		index++;
116 	}
117 
118 	// Load production from Scenario.txt
119 	index = 0;
120 	while (true)
121 	{
122 		material = GetScenarioVal("BaseProduction", section, index * 2);
123 		count = GetScenarioVal("BaseProduction", section, index * 2 + 1);
124 		if (!material && !count) break;
125 		if (material)
126 		{
127 			PushBack(base_production, [material, count]);
128 		}
129 		index++;
130 	}
131 
132 	// Add a timer for executing base production.
133 	AddTimer("ExecBaseProduction", BASEMATERIAL_ProductionRate);
134 	return;
135 }
136 
137 // Called every minute and updates the materials according to production.
ExecBaseProduction()138 public func ExecBaseProduction()
139 {
140 	production_unit++;
141 	// Look at all production.
142 	for (var combo in base_production)
143 	{
144 		// Check if this id is produced and check if it isn't already full.
145 		if (combo[1] > 0 && GetBaseMat(combo[0]) < BASEMATERIAL_MaxBaseMaterial)
146 		{
147 			// Produce the material every production value / BASEMATERIAL_MaxBaseProduction times.
148 			if (production_unit % BoundBy(BASEMATERIAL_MaxBaseProduction + 1 - combo[1], 1, BASEMATERIAL_MaxBaseProduction) == 0)
149 				DoBaseMat(combo[0], 1);
150 		}
151 	}
152 	return;
153 }
154 
GetBaseMat(id material,int index,int category)155 public func GetBaseMat(id material, int index, int category)
156 {
157 	// Get the count if the id is given.
158 	if (material)
159 	{
160 		for (var combo in base_material)
161 			if (combo[0] == material)
162 				return combo[1];
163 		return nil;
164 	}
165 	// If an index is given look for the id.
166 	category = category ?? 0xffffff;
167 	index = Max(0, index);
168 	var count = 0;
169 	for (var combo in base_material)
170 	{
171 		if (combo[0]->GetCategory() & category)
172 		{
173 			if (count == index)
174 			{
175 				return combo[0];
176 			}
177 			count++;
178 		}
179 	}
180 	return;
181 }
182 
SetBaseMat(id material,int amount)183 public func SetBaseMat(id material, int amount)
184 {
185 	if (amount == nil)
186 		return;
187 	amount = Max(0, amount);
188 	var change = 0;
189 	// Scan through current list of id's and set material if available.
190 	var found = false;
191 	for (var index = 0; index < GetLength(base_material); ++index)
192 	{
193 		if (base_material[index][0] == material)
194 		{
195 			change = amount - base_material[index][1];
196 			if (amount > 0)
197 			{
198 				base_material[index][1] = amount;
199 			}
200 			else
201 			{
202 				RemoveArrayIndex(base_material, index);
203 			}
204 			found = true;
205 			break;
206 		}
207 	}
208 	// If material is not available add it to the existing list.
209 	if (!found && amount > 0)
210 	{
211 		change = amount;
212 		PushBack(base_material, [material, amount]);
213 	}
214 	// Callback to the bases of the player.
215 	BroadcastBaseMaterialChange(material, change);
216 	return;
217 }
218 
DoBaseMat(id material,int change)219 public func DoBaseMat(id material, int change)
220 {
221 	if (change == 0)
222 		return;
223 	// Scan through current list of id's and increase material if available.
224 	var found = false;
225 	for (var index = 0; index < GetLength(base_material); ++index)
226 	{
227 		if (base_material[index][0] == material)
228 		{
229 			// Change must at least be minus the original value.
230 			change = Max(change, -base_material[index][1]);
231 			base_material[index][1] += change;
232 			if (base_material[index][1] == 0)
233 			{
234 				RemoveArrayIndex(base_material, index);
235 			}
236 			found = true;
237 			break;
238 		}
239 	}
240 	// If material is not available add it to the existing list.
241 	if (!found)
242 	{
243 		// Change must at least be zero.
244 		change = Max(change, 0);
245 		if (change > 0)
246 		{
247 			PushBack(base_material, [material, change]);
248 		}
249 	}
250 	// Callback to the bases of the player.
251 	BroadcastBaseMaterialChange(material, change);
252 	return;
253 }
254 
GetBaseProd(id material,int index,int category)255 public func GetBaseProd(id material, int index, int category)
256 {
257 	// Get the count if the id is given.
258 	if (material)
259 	{
260 		for (var combo in base_production)
261 			if (combo[0] == material)
262 				return combo[1];
263 		return;
264 	}
265 	// If an index is given look for the id.
266 	category = category ?? 0xffffff;
267 	index = Max(0, index);
268 	var count = 0;
269 	for (var combo in base_production)
270 	{
271 		if (combo[0]->GetCategory() & category)
272 		{
273 			if (count == index)
274 			{
275 				return combo[0];
276 			}
277 			count++;
278 		}
279 	}
280 	return;
281 }
282 
SetBaseProd(id material,int amount)283 public func SetBaseProd(id material, int amount)
284 {
285 	if (amount == nil)
286 		return;
287 	amount = Max(0, amount);
288 	var change = 0;
289 	// Scan through current list of id's and set production if available.
290 	var found = false;
291 	for (var index = 0; index < GetLength(base_production); ++index)
292 	{
293 		if (base_production[index][0] == material)
294 		{
295 			change = amount - base_production[index][1];
296 			if (amount > 0)
297 			{
298 				base_production[index][1] = amount;
299 			}
300 			else
301 			{
302 				RemoveArrayIndex(base_production, index);
303 			}
304 			found = true;
305 			break;
306 		}
307 	}
308 	// If material is not available add it to the existing list.
309 	if (!found && amount > 0)
310 	{
311 		change = amount;
312 		PushBack(base_production, [material, amount]);
313 	}
314 	// Callback to the bases of the player.
315 	BroadcastBaseProductionChange(material, change);
316 	return;
317 }
318 
DoBaseProd(id material,int change)319 public func DoBaseProd(id material, int change)
320 {
321 	if (change == 0)
322 		return;
323 	// Scan through current list of id's and increase production if available.
324 	var found = false;
325 	for (var index = 0; index < GetLength(base_production); ++index)
326 	{
327 		if (base_production[index][0] == material)
328 		{
329 			// Change must at least be minus the original value.
330 			change = Max(change, -base_production[index][1]);
331 			base_production[index][1] += change;
332 			if (base_production[index][1] == 0)
333 			{
334 				RemoveArrayIndex(base_production, index);
335 			}
336 			found = true;
337 			break;
338 		}
339 	}
340 	// If production is not available add it to the existing list.
341 	if (!found)
342 	{
343 		// Change must at least be zero.
344 		change = Max(change, 0);
345 		if (change > 0)
346 		{
347 			PushBack(base_production, [material, change]);
348 		}
349 	}
350 	// Callback to the bases of the player.
351 	BroadcastBaseProductionChange(material, change);
352 	return;
353 }
354 
355 
356 /*-- Miscellaneous --*/
357 
BroadcastBaseProductionChange(id material,int change)358 protected func BroadcastBaseProductionChange(id material, int change)
359 {
360 	var i = 0, base;
361 	while (base = FindBase(GetOwner(), i++))
362 		base->~OnBaseProductionChange(material, change);
363 }
364 
BroadcastBaseMaterialChange(id material,int change)365 protected func BroadcastBaseMaterialChange(id material, int change)
366 {
367 	var i = 0, base;
368 	while (base = FindBase(GetOwner(), i++))
369 		base->~OnBaseMaterialChange(material, change);
370 }
371 
372 // Internal management object not saved. Use Scenario.txt or script
373 // to adjust base materials and production.
SaveScenarioObject()374 func SaveScenarioObject() { return false; }
375