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 (284 lines) | stat: -rwxr-xr-x 7,622 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
/*--
	Stackable
	Author: Newton
	
	Including this object means, the object is stackable. Other objects of
	the same type will be added automatically to the object. This functionality
	is similar to the Pack-functionality of the arrows in old clonk titles only
	more general.
	The count of how many objects are stacked together (into a single one, this
	one) is shown in the picture of the object and can be queried and set via
	GetStackCount()/SetStackCount().
	To take one object of the stack, call TakeObject(). As long
	as the object exists, one can always take an object, even if it is the last
	one (self). This object is always outside.
	Infinite stackable count can by achieved using SetInfiniteStackCount.
	
	On entrance (or to be more precise: on RejectEntrance), it will be checked
	if the entering stackable object can be distributed over the other objects
	of the same ID. If yes, this object is deleted and the other object(s) will
	have a higher stack-count.
	
	Example 1:
	'15x Arrow' is about to enter a clonk which has '5x Arrow'. 15 will be added
	to the stack-count of the clonks '5x Arrow'-object (making it '20x Arrow'),
	the entering object will be deleted.
	
	Example 2:
	'17x Arrow' is about to enter a clonk which has '15x Arrow' and a bow with
	'10x Arrow' in it's ammunition slot. 10 will be added to the stack-count
	of the arrows in the bow, 5 to the stack-count of the arrows in the clonk
	(assuming MaxStackCount() is 20) and the original arrows-object will have
	2 arrows left. If there is an inventory slot left, the '2x Arrow" object
	will enter the clonk.
	
	Most objects which can be stacked might want to set different pictures
	and ingame graphics for different counts of objects. This can be done
	by overloading UpdatePicture(), but remember to write _inherited() then.
--*/


local count, count_is_infinite;

// Max size of stack
static const Stackable_Max_Count = 999;

// What GetStackCount should return if the count is set to infinite
// Set this to a fairly large number and not e.g. -1, so naive
// implementations that update their graphics by GetStackCount() show a
// bunch of items. However, the number shouldn't be too large so the
// object doesn't get overly heavy.
// Note that count_is_infinite is a separate variable, so we can support
// stacks >999 but <Inf in the future.
static const Stackable_Infinite_Count = 50;

public func IsStackable() { return true; }
public func GetStackCount() { return Max(1, count); }
public func MaxStackCount() { return 20; }
public func IsFullStack() { return IsInfiniteStackCount() || (GetStackCount() >= MaxStackCount()); }
public func IsInfiniteStackCount() { return count_is_infinite; }

protected func Construction()
{
	count = MaxStackCount();
	return _inherited(...);
}

func Destruction()
{
	var container = Contained();
	if (container)
	{
		// has an extra slot
		if (container->~HasExtraSlot())
			container->~NotifyHUD();
	}
	return _inherited(...);
}

public func Stack(object obj)
{
	if (obj->GetID() != GetID())
		return 0;
	
	// Infinite stacks can always take everything
	if (IsInfiniteStackCount()) return obj->GetStackCount();
	if (obj->~IsInfiniteStackCount())
	{
		SetInfiniteStackCount();
		return obj->GetStackCount();
	}
	
	var howmany = Min(obj->GetStackCount(), MaxStackCount() - GetStackCount());
	
	if (howmany <= 0 || count + howmany > Stackable_Max_Count)
		return 0;
	
	SetStackCount(count + howmany);

	return howmany;
}

public func SetStackCount(int amount)
{
	count = BoundBy(amount, 0, Stackable_Max_Count);
	count_is_infinite = false;
	UpdateStackDisplay();
	return true;
}

public func DoStackCount(int change)
{
	count += change;
	if (count <= 0) RemoveObject();
	else UpdateStackDisplay();
}

public func SetInfiniteStackCount()
{
	count = Stackable_Infinite_Count;
	count_is_infinite = true;
	UpdateStackDisplay();
	return true;
}

public func TakeObject()
{
	if (count == 1)
	{
		Exit();
		return this;
	}
	else if (count > 1)
	{
		if (!count_is_infinite) SetStackCount(count - 1);
		var take = CreateObjectAbove(GetID(), 0, 0, GetOwner());
		take->SetStackCount(1);
		if (!count_is_infinite) UpdateStackDisplay();
		return take;
	}
}

public func UpdateStackDisplay()
{
	UpdatePicture();
	UpdateMass();
	UpdateName();
	// notify hud
	var container = Contained();
	if (container)
	{
		// has an extra slot
		if (container->~HasExtraSlot())
		{
			container->~NotifyHUD();
		}
		// is a clonk with new inventory system
		else
		{
			container->~OnInventoryChange();
		}
	}
}

private func UpdatePicture()
{
	// Allow other objects to adjust their picture.
	return _inherited(...);
}

private func UpdateName()
{
	if (IsInfiniteStackCount())
		SetName(Format("$Infinite$ %s", GetID()->GetName()));
	else
		SetName(Format("%dx %s", GetStackCount(), GetID()->GetName()));
}

private func UpdateMass()
{
	SetMass(GetID()->GetMass() * Max(GetStackCount(), 1) / MaxStackCount());
}

/*
	Try to merge packs BEFORE entering the container.
	That means that a container can not prevent objects stacking into it.
	However, the other way round (after the object has entered) presents more issues.
*/
protected func RejectEntrance(object into)
{
	if (TryPutInto(into))
		return true;
	return _inherited(into, ...);
}

/* Value */

public func CalcValue(object in_base, int for_plr)
{
	return GetID()->GetValue() * Max(GetStackCount(), 1) / MaxStackCount();
}

/* Tries to add this object to another stack. Returns true if successful.
	This call might remove this item. */
public func TryAddToStack(object other)
{
	if (other == this) return false;
	
	// Is a stack possible in theory?
	if (other->~IsStackable() && other->GetID() == GetID())
	{
			var howmany = other->Stack(this);
			if (howmany > 0)
			{
				count -= howmany;
				if(count <= 0) RemoveObject();
				// Stack succesful! No matter how many items were transfered.
				return true;
			}
		}
	return false;
}

/* Attempts to add this stack either to existing stacks in an object or
	if only_add_to_existing_stacks is not set, also recursively into HasExtraSlot containers in that object.*/
public func TryPutInto(object into, bool only_add_to_existing_stacks)
{
	only_add_to_existing_stacks = only_add_to_existing_stacks ?? false;
	var before = count;
	var contents = FindObjects(Find_Container(into));

	if (!only_add_to_existing_stacks)
	{
		// first check if stackable can be put into object with extra slot
		for (var content in contents)
		{
			if (!content)
				continue;
			if (content->~HasExtraSlot())
				if (TryPutInto(content))
					return true;
		}
	}
	
	// then check this object
	for (var content in contents)
	{
		var howmany = 0;
		if (!content)
			continue;
		TryAddToStack(content);
		if (!this) return true;
	}
	
	// IFF anything changed, we need to update the display.
	if (before != count)
		UpdateStackDisplay();
	return false;
}

// Infinite stacks can only be stacked on top of others.
public func CanBeStackedWith(object other)
{
	if (other.count_is_infinite != this.count_is_infinite) return false;
	return _inherited(other, ...);
}

// Infinite stacks show a little symbol in their corner.
public func GetInventoryIconOverlay()
{
	if (!count_is_infinite) return nil;
	return {Left = "50%", Bottom="50%", Symbol=Icon_Number, GraphicsName="Inf"};
}

// Save stack counts in saved scenarios
public func SaveScenarioObject(props)
{
	if (!inherited(props, ...)) return false;
	props->Remove("Name");
	if (IsInfiniteStackCount())
		props->AddCall("Stack", this, "SetInfiniteStackCount");
	else if (GetStackCount() != MaxStackCount())
		props->AddCall("Stack", this, "SetStackCount", GetStackCount());
	return true;
}