1 /*--
2 	Powder Keg
3 	A barrel filled with black powder.
4 
5 	@author Ringwaul
6 */
7 
8 #include Library_CarryHeavy
9 
10 local count;
11 local TimeToExplode = 90; // Number of frames to pass until keg explodes
12 local MaxContentsCount = 12;
13 
GetCarryTransform(clonk)14 public func GetCarryTransform(clonk)
15 {
16 	if(GetCarrySpecial(clonk))
17 		return Trans_Translate(1000, -6500, 0);
18 
19 	return Trans_Mul(Trans_Translate(1500, 0, -1500),Trans_Rotate(180,1,0,0));
20 }
GetCarryPhase()21 public func GetCarryPhase() { return 900; }
22 
Construction()23 protected func Construction()
24 {
25 	SetPowderCount(MaxContentsCount);
26 }
27 
GetPowderCount()28 public func GetPowderCount()
29 {
30 	return count;
31 }
32 
SetPowderCount(newcount)33 public func SetPowderCount(newcount)
34 {
35 	count = newcount;
36 	this.Description = Format("%s||$RemainingPowder$: %d", this.Prototype.Description, this.count);
37 	if (count == 0)
38 		ScheduleCall(this, "CheckEmpty", 1, 0);
39 	return;
40 }
41 
DoPowderCount(int change)42 public func DoPowderCount(int change)
43 {
44 	if (count == nil)
45 		return;
46 	return SetPowderCount(GetPowderCount() + change);
47 }
48 
CheckEmpty()49 private func CheckEmpty()
50 {
51 	if (count == 0)
52 	{
53 		ChangeDef(Barrel);
54 		this.Description = this.Prototype.Description;
55 		this->~Initialize();
56 	}
57 }
58 
59 // Do not put differently filled kegs on top of each other.
CanBeStackedWith(object other)60 public func CanBeStackedWith(object other)
61 {
62 	if (this.count != other.count) return false;
63 	return inherited(other, ...);
64 }
65 
66 // Display the powder as a bar over the keg icon.
GetInventoryIconOverlay()67 public func GetInventoryIconOverlay()
68 {
69 	if (this.count >= MaxContentsCount) return nil;
70 
71 	var percentage = 100 - 100 * this.count / this.MaxContentsCount;
72 
73 	// Overlay a usage bar.
74 	var overlay =
75 	{
76 		Bottom = "0.75em", Margin = ["0.1em", "0.25em"],
77 		BackgroundColor = RGB(0, 0, 0),
78 		margin =
79 		{
80 			Margin = "0.05em",
81 			bar =
82 			{
83 				BackgroundColor = RGB(150, 50, 50),
84 				Right = Format("%d%%", percentage),
85 			}
86 		}
87 	};
88 
89 	return overlay;
90 }
91 
Incineration(int caused_by)92 public func Incineration(int caused_by)
93 {
94 	SetController(caused_by);
95 	AddEffect("Fuse", this, 1, 1, this);
96 }
97 
FxFuseTimer(object target,effect,int timer)98 public func FxFuseTimer(object target, effect, int timer)
99 {
100 	// Particle effect
101 	var lifetime = PV_Random(10, 40);
102 	var amount = 6;
103 	if (Contained() && Contained()->~IsClonk())
104 	{
105 		var prec = 10;
106 		// Position, is always the same regardless of rotation
107 		var x = 2, y = -6;
108 		// Needs a loop for now, because CreateParticleAtBone seems to not use the PV_Random values in the dir array
109 		for (var i = 0; i < amount; ++i)
110 		{
111 			// Positions for array: [-y, 0, -x]
112 			Contained()->CreateParticleAtBone("Fire", "pos_tool1", [2 - y, 0, -1 - x], [prec * RandomX(-10, 20), 0, prec * RandomX(-10, 10)], lifetime, Particles_Glimmer(), 1);
113 		}
114 	}
115 	else
116 	{
117 		var x, y;
118 
119 		if (Contained())
120 		{
121 			// Display at the center if contained, because the fuse vertex might not be visible
122 			x = 0; y = 0;
123 		}
124 		else
125 		{
126 			// Display at a vertex, because this is easier than calculating the correct rotated position
127 			var fuse_vertex = 1;
128 			x = GetVertex(fuse_vertex, 0);
129 			y = GetVertex(fuse_vertex, 1);
130 		}
131 		CreateParticle("Fire", x, y, PV_Random(-10, 10), PV_Random(-20, 10), lifetime, Particles_Glimmer(), amount);
132 	}
133 	// Explosion after timeout
134 	if (timer > TimeToExplode)
135 	{
136 		Explode(GetExplosionStrength());
137 	}
138 }
139 
140 // Powderkeg explosion strength ranges from 17-32.
GetExplosionStrength()141 public func GetExplosionStrength() { return Sqrt(64 * (4 + count)); }
142 
IsProjectileTarget()143 public func IsProjectileTarget()
144 {
145 	return true;
146 }
147 
Damage(int change,int cause,int by_player)148 public func Damage(int change, int cause, int by_player)
149 {
150 	Incinerate(100, by_player);
151 }
152 
OnProjectileHit(object projectile)153 public func OnProjectileHit(object projectile)
154 {
155 	Incinerate(100, projectile->GetController());
156 }
157 
Hit()158 func Hit()
159 {
160 	Sound("Hits::Materials::Wood::DullWoodHit?");
161 }
162 
SaveScenarioObject(props)163 public func SaveScenarioObject(props)
164 {
165 	if (!inherited(props, ...)) return false;
166 	var v = GetPowderCount();
167 	if (v != MaxContentsCount) props->AddCall("Powder", this, "SetPowderCount", v);
168 	return true;
169 }
170 
IsChemicalProduct()171 func IsChemicalProduct() { return true; }
AlchemyProcessTime()172 func AlchemyProcessTime() { return 100; }
IsExplosive()173 public func IsExplosive() { return true; }
174 
175 
176 /*-- Properties --*/
177 
178 local Collectible = true;
179 local Name = "$Name$";
180 local Description = "$Description$";
181 local BlastIncinerate = 1;
182 local NoBurnDecay = true;
183 local ContactIncinerate = 2;
184 local Components = {Barrel = 1, Coal = 1};
185