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
|
/**
Windbag
Automatically collects air when it is full which can be released into a blast.
@author MimmoO, Maikel
*/
local fill_amount;
/*-- Engine Callbacks --*/
func Initialize()
{
SetR(-45);
AddEffect("IntReload", this, 100, 1, this);
}
func Hit()
{
Sound("Hits::GeneralHit?");
}
/*-- Usage --*/
public func DefaultCrosshairAngle(object clonk, int d)
{
// Easy mode for gamepad users: automatically boost a jump.
if (clonk->GetYDir() < -10)
return Angle(0, 0, -clonk->GetXDir(), -clonk->GetYDir(), 10);
return 0;
}
protected func ControlUse(object clonk, x, y)
{
if (!GetEffect("IntReload", this) && !GetEffect("IntBurstWind", this))
{
if (!GBackLiquid())
BlastWind(clonk, x, y);
return true;
}
clonk->Message("$MsgReloading$");
clonk->PauseUse(this, "ReadyToBeUsed", {clonk = clonk});
return true;
}
func ReadyToBeUsed(proplist data)
{
var clonk = data.clonk;
return !GetEffect("IntReload", this);
}
/*-- Loading --*/
public func DoFullLoad()
{
fill_amount = MaxIntake;
return;
}
func FxIntReloadStart(object target, proplist effect, int temp)
{
if (temp)
return FX_OK;
effect.Interval = 1;
effect.sound = false;
return FX_OK;
}
func FxIntReloadTimer(object target, proplist effect, int time)
{
if (fill_amount > MaxIntake)
return FX_Execute_Kill;
if (GBackSolid(0,0) || GBackLiquid(0,0))
{
if (effect.sound)
{
Sound("Objects::Windbag::Charge", {loop_count = -1});
Sound("Objects::Windbag::ChargeStop");
effect.sound = false;
}
return FX_OK;
}
var radius = RandomX(12, 24);
var angle = Random(360);
var angle_var = RandomX(-25, 25);
var x = Sin(angle + angle_var, radius);
var y = Cos(angle + angle_var, radius);
// Check for a spot of air from which to take the air in.
if (!GBackSolid(x, y) && !GBackLiquid(x, y) && !GBackSolid(0, 0) && !GBackLiquid(0, 0))
{
if (!effect.sound)
{
Sound("Objects::Windbag::Charge", {loop_count = 1});
effect.sound = true;
}
// Particles from the point where the air is sucked in.
var air = {
Prototype = Particles_Air(),
Size = PV_KeyFrames(0, 0, 0, 250, 3, 1000, 0)
};
CreateParticle("Air", x, y, -2 * x / 3, -2 * y / 3, 15, air);
// Increase the fill amount proportional to the number of frames.
fill_amount += effect.Interval;
}
return FX_OK;
}
func FxIntReloadStop(object target, proplist effect, int reason, bool temp)
{
if (temp)
return FX_OK;
if (effect.sound)
{
Sound("Objects::Windbag::Charge", {loop_count = -1});
Sound("Objects::Windbag::ChargeStop");
}
return FX_OK;
}
/*-- Blasting --*/
public func BlastWind(object clonk, int x, int y)
{
if (fill_amount <= 0)
{
fill_amount = 0;
AddEffect("IntReload", this, 100, 1, this);
return;
}
// The blast is handled by an effect.
AddEffect("IntBurstWind", this, 100, 1, this, nil, clonk, x, y);
}
func FxIntBurstWindStart(object target, proplist effect, int temp, object clonk, int x, int y)
{
if (temp)
return FX_OK;
effect.Interval = 1;
effect.clonk = clonk;
effect.x = clonk->GetX();
effect.y = clonk->GetY();
effect.angle = Angle(0, 0, x, y);
// Sound effect.
Sound("Objects::Windbag::Gust");
// Particle effect.
for (var dr = 12; dr < 32; dr++)
{
var r = RandomX(-20, 20);
var sx = Sin(effect.angle + r, dr / 2);
var sy = -Cos(effect.angle + r, dr / 2);
var vx = Sin(effect.angle + r, 2 * fill_amount / 3 + 12);
var vy = -Cos(effect.angle + r, 2 * fill_amount / 3 + 12);
if (!GBackSolid(sx, sy))
CreateParticle("Air", sx, sy, vx, vy, 36, Particles_Air());
}
// Make a timer call for the instant movement effect.
FxIntBurstWindTimer(target, effect, 0);
return FX_OK;
}
func FxIntBurstWindTimer(object target, proplist effect, int time)
{
var real_time = time + 1;
if (real_time > 8)
return FX_Execute_Kill;
// Determine blast strength.
var vx = Sin(effect.angle, 20 * fill_amount + 150);
var vy = -Cos(effect.angle, 20 * fill_amount + 150);
// Change the velocity of the shooter.
if (effect.clonk && effect.clonk->GetAction() != "Walk")
{
var cx = effect.clonk->GetXDir(100);
var cy = effect.clonk->GetYDir(100);
effect.clonk->SetXDir(cx - vx / (8 * real_time), 100);
effect.clonk->SetYDir(cy - vy / (8 * real_time), 100);
}
// Move other objects in a cone around the burst direction.
var criteria = Find_And(Find_Not(Find_Category(C4D_Structure)), Find_Not(Find_Func("IsEnvironment")), Find_Not(Find_Func("RejectWindbagForce")),
Find_Layer(GetObjectLayer()), Find_NoContainer(), Find_Exclude(effect.clonk), Find_PathFree(effect.clonk));
var dist = 14 + 9 * real_time / 2;
var rad = 8 + 8 * real_time / 3;
var cone_x = Sin(effect.angle, dist);
var cone_y = -Cos(effect.angle, dist);
var vx_cone = vx / 6;
var vy_cone = vy / 6;
//DrawParticleRing(rad, cone_x + AbsX(effect.x), cone_y + AbsY(effect.y));
for (var obj in FindObjects(Find_Distance(rad, cone_x + AbsX(effect.x), cone_y + AbsY(effect.y)), criteria))
{
var ox = obj->GetXDir(100);
var oy = obj->GetYDir(100);
var vx_cone_reduced = vx_cone / 2 + vx_cone / (2 * Max(1, obj->GetMass() / 4));
var vy_cone_reduced = vy_cone / 2 + vy_cone / (2 * Max(1, obj->GetMass() / 4));
obj->SetXDir(ox + vx_cone_reduced, 100);
obj->SetYDir(oy + vy_cone_reduced, 100);
obj->SetController(GetController());
}
return FX_OK;
}
func FxIntBurstWindStop(object target, proplist effect, int reason, bool temp)
{
if (temp)
return FX_OK;
// Reset the fill amount and start reloading.
fill_amount = 0;
AddEffect("IntReload", target, 100, 1, target);
return FX_OK;
}
func DrawParticleRing(int r, int x, int y)
{
for (var angle = 0; angle < 360; angle += 15)
CreateParticle("SphereSpark", x + Cos(angle, r), y + Sin(angle, r), 0, 0, 36, { Size = 2 });
}
/*-- Production --*/
public func IsInventorProduct() { return true; }
/*-- Display --*/
public func GetCarryMode(object clonk)
{
return CARRY_Blunderbuss;
}
public func GetCarryTransform(object clonk, bool idle, bool nohand, bool second_on_back)
{
if (idle)
{
if (!second_on_back)
return Trans_Mul(Trans_Rotate(180, 1), Trans_Translate(0,-3000));
else
return Trans_Mul(Trans_Rotate(180, 1), Trans_Translate(3000,-3000), Trans_Rotate(-30, 0, 1));
}
if (nohand)
return Trans_Mul(Trans_Rotate(180, 1), Trans_Translate(0,3000));
return Trans_Mul(Trans_Rotate(220, 0, 1, 0), Trans_Rotate(30, 0, 0, 1), Trans_Rotate(-26, 1, 0, 0));
}
public func GetCarryPhase() { return 600; }
func Definition(def)
{
SetProperty("PictureTransformation", Trans_Mul(Trans_Scale(1500), Trans_Rotate(150, 0, 0, 1), Trans_Rotate(-170, 1, 0, 0), Trans_Rotate(10, 0, 1, 0)), def);
}
/*-- Properties --*/
local Name = "$Name$";
local Description = "$Description$";
local Collectible = true;
local MaxIntake = 30;
local Components = {Cloth = 1, Metal = 1};
|