File: Script.c

package info (click to toggle)
openclonk 7.0-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 145,828 kB
  • ctags: 33,094
  • sloc: cpp: 163,891; ansic: 82,846; xml: 29,876; python: 1,203; php: 767; makefile: 138; sh: 77
file content (498 lines) | stat: -rwxr-xr-x 13,181 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
/**
	Inventory
	Functions to handle a multi-slot, multi-hand inventory.
*/

/*
	The inventory management:
	The objects in the inventory are saved (parallel to Contents()) in the
	array 'inventory'. They are accessed via GetItem(i) and GetItemPos(obj).
	Other properties are MaxContentsCount (defines the maximum number of
	contents)
	
	Furthermore the clonk has a defined amount of "hands", defined by local HandObjects.
	The array 'use_objects' is a mapping of "hands" onto the inventory-slots.
	The functions GetHandItem(i) returns the object in the "i"th hand.
	
	used properties:
	this.inventory.objects: items in the inventory, array
	this.inventory.hand_objects: items in the hands, array
	this.inventory.disableautosort: used to get default-Collection-behaviour (see Collection2)
	this.inventory.force_collection: used to pick stuff up, even though the hand-slots are all full (see RejectCollect + Collect with CON_Collect)
*/


/* Item limit */
local MaxContentsCount = 10; // Size of the inventory
local HandObjects = 1; // Amount of hands to select items

func Construction()
{
	if(this.inventory == nil)
		this.inventory = {};
	this.inventory.objects = [];
	this.inventory.disableautosort = false;
	this.inventory.hand_objects = [];
	this.inventory.force_collection = false;

	for(var i=0; i < HandObjects; i++)
		this.inventory.hand_objects[i] = i;
	return _inherited(...);
}

/** Get the 'i'th item in the inventory */
public func GetItem(int i)
{
	if (i >= GetLength(this.inventory.objects))
		return nil;
	if (i < 0) return nil;
		
	return this.inventory.objects[i];
}

/** Returns all items in the inventory */
public func GetItems()
{
	var inv = this.inventory.objects[:];
	RemoveHoles(inv);
	return inv;
}

/** Returns how many items are in the clonks inventory
    Does not have to be the same as ContentCounts() because of objects with special handling, like CarryHeavy */
public func GetItemCount()
{
	var count = 0;
	for(var i=0; i < GetLength(this.inventory.objects); i++)
		if(this.inventory.objects[i])
			count++;
	
	return count;
}

/** Get the 'i'th item in hands.
    These are the items that will be used with use-commands. (Left mouse click, etc...) */
public func GetHandItem(int i)
{
	// i is valid range
	if (i >= GetLength(this.inventory.hand_objects))
		return nil;
	if (i < 0) return nil;	
	return GetItem(this.inventory.hand_objects[i]);
}

/** Set the 'hand'th use-item to the 'inv'th slot */
public func SetHandItemPos(int hand, int inv)
{
	// indices are in range?	
	if(hand >= HandObjects || inv >= MaxContentsCount)
		return nil;
	if(hand < 0 || inv < 0) return nil;
	// no slot change?
	if (inv == GetHandItemPos(hand)) return nil;
	// the current hand object needs to be both notified and asked
	var old_object = GetHandItem(hand);
	if (old_object && old_object->~QueryRejectDeselection(this, hand))
		return nil;
	// when we want to swap placed with another hand, the other object also has to be asked
	var hand2 = GetHandPosByItemPos(inv);
	var old_hand2_object = nil;
	if (hand2 != nil)
	{
		old_hand2_object = GetHandItem(hand2);
		if (old_hand2_object && old_hand2_object->~QueryRejectDeselection(this, hand2)) return nil;
	}
	
	// changing slots cancels using, if the slot with the used object is contained
	if(this.control.current_object) // declared in ClonkControl.ocd
	{
		var used_slot = GetItemPos(this.control.current_object);
		if(used_slot != nil)
			if(used_slot == GetHandItemPos(hand) || used_slot == inv)
				this->~CancelUseControl(0,0);
	}
	
	// If the item is already selected, we can't hold it in another one too.
	if(hand2 != nil)
	{
		// switch places
		this.inventory.hand_objects[hand2] = this.inventory.hand_objects[hand];
		this.inventory.hand_objects[hand] = inv;
		
		// additional callbacks
		if (old_hand2_object)
			old_hand2_object->~Deselection(this, hand2);
		if (old_object)
			old_object->~Deselection(this, hand);
			
		// notify new hand2 item (which should be old hand-item) that it has been selected
		var hand_item;
		if (hand_item = GetHandItem(hand2))
		{
			this->~OnSlotFull(hand2);
			// OnSlotFull might have done something to the item
			if(GetHandItem(hand2) == hand_item)
				hand_item->~Selection(this, hand2);
		}
		else
			this->~OnSlotEmpty(hand2);
	}
	else
	{
		this.inventory.hand_objects[hand] = inv;
		
		// notify the old object that it was already deselected
		if (old_object)
			old_object->~Deselection(this, hand);
	}
	
	// notify the new item that it was selected
	var item;
	if(item = GetItem(inv))
	{
		this->~OnSlotFull(hand);
		// OnSlotFull might have done something to the item
		if(GetItem(inv) == item)
			GetItem(inv)->~Selection(this, hand);
	}
	else
	{
		this->~OnSlotEmpty(hand);
	}
}

/** Returns the position in the inventory of the 'i'th use item */
public func GetHandItemPos(int i)
{
	if (i >= GetLength(this.inventory.hand_objects))
		return nil;
	if (i < 0) return nil;
	
	return this.inventory.hand_objects[i];
}


/** Returns in which hand-slot the inventory-slot is */
private func GetHandPosByItemPos(int o) // sorry for the horribly long name --boni
{
	for(var i=0; i < GetLength(this.inventory.hand_objects); i++)
		if(this.inventory.hand_objects[i] == o)
			return i;

	return nil;
}

/** Drops the item in the inventory slot, if any */
public func DropInventoryItem(int slot)
{
	var obj = GetItem(slot);
	if(!obj || obj->~QueryRejectDeparture(this))
		return nil;
	// Notify other libraries of deliberate drop.
	this->~OnDropped(obj);
	// And make the engine drop the object.
	this->AddCommand("Drop",obj);
}

/** Search for the index of an item */
public func GetItemPos(object item)
{
	if (item)
		if (item->Contained() == this)
		{
			var i = 0;
			for(var obj in this.inventory.objects)
			{
				if (obj == item) return i;
				++i;
			}
		}
	return nil;
}

/** Switch two items in the clonk's inventory */
public func Switch2Items(int one, int two)
{
	// no valid inventory index: cancel
	if (!Inside(one,0,MaxContentsCount-1)) return;
	if (!Inside(two,0,MaxContentsCount-1)) return;

	// switch them around
	var temp = this.inventory.objects[one];
	this.inventory.objects[one] = this.inventory.objects[two];
	this.inventory.objects[two] = temp;
	
	// callbacks: cancel use, variable declared in ClonkControl.ocd
	if (this.control.current_object == this.inventory.objects[one] || this.control.current_object == this.inventory.objects[two])
		this->~CancelUse();
	
	var handone, handtwo;
	handone = GetHandPosByItemPos(one);
	handtwo = GetHandPosByItemPos(two);
	
	// callbacks: (de)selection
	if (handone != nil)
		if (this.inventory.objects[two]) this.inventory.objects[two]->~Deselection(this,one);
	if (handtwo != nil)
		if (this.inventory.objects[one]) this.inventory.objects[one]->~Deselection(this,two);
		
	if (handone != nil)
		if (this.inventory.objects[one]) this.inventory.objects[one]->~Selection(this,one);
	if (handtwo != nil)
		if (this.inventory.objects[two]) this.inventory.objects[two]->~Selection(this,two);
	
	// callbacks: to self, for HUD
	if (handone != nil)
	{
		if (this.inventory.objects[one])
			this->~OnSlotFull(handone);
		else
			this->~OnSlotEmpty(handone);
	}
	if (handtwo != nil)
	{
		if (this.inventory.objects[two])
			this->~OnSlotFull(handtwo);
		else
			this->~OnSlotEmpty(handtwo);
	}
	
	this->~OnInventoryChange(one, two);
}


/* Overload of Collect function
   Allows inventory/hands-Handling with forced-collection
*/
public func Collect(object item, bool ignoreOCF, int pos, bool force)
{
	// Whenever a script calls the Collect function manually, an intended force is assumed.
	// That means, items will usually be collected with Collect() even if the current hand-slot is not free.
	force = force ?? true;
	
	this.inventory.force_collection = force;
	var success = false;
	if (pos == nil || item->~IsCarryHeavy())
	{
		success = _inherited(item,ignoreOCF);
		this.inventory.force_collection = false;
		return success;
	}
	// fail if the specified slot is full
	if (GetItem(pos) == nil && pos >= 0 && pos < MaxContentsCount)
	{
		if (item)
		{
			this.inventory.disableautosort = true;
			// collect but do not sort in_
			// Collection2 will be called which attempts to automatically sort in
			// the collected item into the next free inventory slot. Since 'pos'
			// is given as a parameter, we don't want that to happen and sort it
			// in manually afterwards
			var success = _inherited(item);
			this.inventory.disableautosort = false;
			if (success)
			{
				this.inventory.objects[pos] = item;
				var handpos = GetHandPosByItemPos(pos); 
				// if the slot was a selected hand slot -> update it
				if(handpos != nil)
				{
					this->~OnSlotFull(handpos);
				}
			}
		}
	}
	
	this.inventory.force_collection = false;
	return success;
}

protected func Collection2(object obj)
{
	var sel = 0;

	// See Collect()
	if (this.inventory.disableautosort) return _inherited(obj,...);
	
	var success = false;
	var i;
	
	// sort into selected hands if empty
	for(i = 0; i < HandObjects; i++)
		if(!GetHandItem(i))
		{
			sel = GetHandItemPos(i);
			this.inventory.objects[sel] = obj;
			success = true;
			break;
		}
		
	// otherwise, first empty slot
	if(!success)
	{
		for(var i = 0; i < MaxContentsCount; ++i)
		{
			if (!GetItem(i))
			{
				sel = i;
				this.inventory.objects[sel] = obj;
				success = true;
				break;
			}
		}
	}
	
	// callbacks
	if (success)
	{
		var handpos = GetHandPosByItemPos(sel); 
		// if the slot was a selected hand slot -> update it
		if(handpos != nil)
		{
			this->~OnSlotFull(handpos);
			// OnSlotFull might have done something to obj
			if(GetHandItem(handpos) == obj)
				obj->~Selection(this, handpos);
		}
	}
		

	return _inherited(obj,...);
}

func Ejection(object obj)
{
	// if an object leaves this object
	// find obj in array and delete (cancel using too)
	var i = 0;
	var success = false;
	
	for(var item in this.inventory.objects)
    {
	   if (obj == item)
	   {
			this.inventory.objects[i] = nil;
			success = true;
			break;
	   }
	   ++i;
    }
    
    // variable declared in ClonkControl.ocd
	if (this.control.current_object == obj) this->~CancelUse();

	// callbacks
	if (success)
	{
		var handpos = GetHandPosByItemPos(i); 
		// if the slot was a selected hand slot -> update it
		if(handpos != nil)
		{
			this->~OnSlotEmpty(handpos);
			obj->~Deselection(this, handpos);
		}
	}
	
	// we have over-weight? Put the next unindexed object inside that slot
	// this happens if the clonk is stuffed full with items he can not
	// carry via Enter, CreateContents etc.
	var inventory_count = 0;
	for(var io in this.inventory.objects)
		if(io != nil)
			inventory_count++;
			
	if (ContentsCount() > inventory_count && !GetItem(i))
	{
		for(var c = 0; c < ContentsCount(); ++c)
		{
			var o = Contents(c);
			if (!o) continue; // safety in case callbacks delete some objects
			if(o->~IsCarryHeavy())
				continue;
			if (GetItemPos(o) == nil)
			{
				// found it! Collect it properly
				this.inventory.objects[i] = o;
				
				var handpos = GetHandPosByItemPos(i); 
				// if the slot was a selected hand slot -> update it
				if(handpos != nil)
				{
					this->~OnSlotFull(handpos);
					// OnSlotFull might have done something to o
					if(GetHandItem(handpos) == o)
						o->~Selection(this, handpos);
				}
					
				break;
			}
		}
	}
	
	_inherited(obj,...);
}

func ContentsDestruction(object obj)
{
	// tell the Hud that something changed
	this->~OnInventoryChange();
	_inherited(obj, ...);
}

protected func RejectCollect(id objid, object obj)
{
	// collection of that object magically disabled?
	if(GetEffect("NoCollection", obj)) return true;
	
	// Only handle extra-slot objects if the object was not dropped on purpose.
	if (this.inventory.force_collection)
	{
		// try to stuff obj into an object with an extra slot
		for(var i=0; Contents(i); ++i)
			if (Contents(i)->~HasExtraSlot())
				if (!(Contents(i)->Contents(0)))
					if (Contents(i)->Collect(obj,true))
						return true;
						
		// try to stuff an object in clonk into obj if it has an extra slot
		if (obj->~HasExtraSlot())
			if (!(obj->Contents(0)))
				for(var i=0; Contents(i); ++i)
					if (obj->Collect(Contents(i),true))
						return false;
	}
	// Can't carry bucket material with bare hands.
	if (obj->~IsBucketMaterial()) return true;
	// check max contents. But do not count CarryHeavy towards contents.
	var contents_count = this->ContentsCount();
	var carry_heavy_obj = this->GetCarryHeavy();
	if (carry_heavy_obj && carry_heavy_obj->Contained() == this) --contents_count;
	if (contents_count >= MaxContentsCount) return true;
	
	return _inherited(objid,obj,...);
}

public func GrabContents(object source, ...)
{
	// Try to put grabbed items into same slot (for respawn)
	if (source)
	{
		var i = source->ContentsCount();
		while (--i >= 0)
		{
			var item = source->Contents(i);
			if (item)
			{
				var item_pos = source->GetItemPos(item);
				// Collect this into same slot index if it's a valid, free slot for this object
				if (GetType(item_pos) && item_pos >=0 && item_pos < MaxContentsCount && !GetItem(item_pos))
				{
					Collect(item, true, item_pos);
				}
			}
		}
	}
	// Grab remaining items
	return inherited(source, ...);
}