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
|
/*
Cotton Fruit
Author: Clonkonaut, Win
*/
// Animation length is 875
// At about 500 the fruit starts filling up with gas
local first_animation_stage = 500;
// Growing time for first stage in frames
local grow_time = 3100;
local grow_anim;
local attach_branch;
// Filling time & also animation duration for second stage
local fill_time = 3100;
// When the balloon is full of gas, it's ripe!
local ripened = false;
// Time the filled balloon will stay on the branch (time the player has to prepare for harvesting)
local ripe_time = 3500;
// Time the player has for harvesting before the balloon starts rising
local float_time = 350;
// Half of the time the balloon will fly up and half will just drift with the wind
local fly_time = 700;
private func Construction()
{
SetProperty("MeshTransformation", Trans_Rotate(180, 0,0,1));
}
/* Growing & filling with gas */
public func Grow(int branch, bool fullgrown)
{
attach_branch = branch;
if (!fullgrown)
{
grow_anim = PlayAnimation("grow", 1, Anim_Linear(0,0, first_animation_stage, grow_time, ANIM_Hold), Anim_Const(1000));
AddTimer("Growing", 35);
ScheduleCall(this, "Fill", grow_time);
}
else
{
if (!grow_anim) grow_anim = PlayAnimation("grow", 1, Anim_Linear(GetAnimationLength("grow"),0, GetAnimationLength("grow"), 1, ANIM_Hold), Anim_Const(1000));
else SetAnimationPosition(grow_anim, Anim_Const(GetAnimationLength("grow")));
if (Contained())
{
Contained()->UpdateFruitAttachTransform(attach_branch, 2000);
Contained()->FruitFills(attach_branch, nil, true);
}
Ripen();
}
}
public func IsGrowing()
{
return !ripened;
}
// Every ~1 second update scaling on cotton plant
private func Growing()
{
// Shouldn't happen
if (!Contained()) return RemoveTimer("Growing");
Contained()->UpdateFruitAttachTransform(attach_branch, GetAnimationPosition(grow_anim) * 1000 / first_animation_stage);
if (GetAnimationPosition(grow_anim) >= first_animation_stage) RemoveTimer("Growing");
}
// The fruit is now grown and starts filling with gas, this will also move the branch
private func Fill()
{
// No plant, no fill
if (!Contained()) return;
SetAnimationPosition(grow_anim, Anim_Linear(GetAnimationPosition(grow_anim), 0, GetAnimationLength("grow"), fill_time, ANIM_Hold));
Contained()->FruitFills(attach_branch, fill_time, false); // Will start the branch's animation
ScheduleCall(this, "Ripen", fill_time);
}
/* Ripening & Flying */
private func Ripen()
{
SetMeshMaterial("Cotton_Fruit_Ripe");
ripened = true;
AddEffect("IntPrepareFlight", this, 1, 1, this);
}
private func FxIntPrepareFlightTimer(object target, proplist effect, int time)
{
if (!Contained()) return FX_Execute_Kill;
if (time > 10 && !effect.shakes)
{
effect.shakes = 1;
Contained()->ShakeFruit(attach_branch);
}
if (time > ripe_time / 2 && effect.shakes == 1)
{
effect.shakes = 2;
Contained()->ShakeFruit(attach_branch);
}
if (time >= ripe_time)
{
Fly();
return FX_Execute_Kill;
}
return FX_OK;
}
// May be called from elsewhere e.g. the cotton plant when harvesting with a sickle
public func Fly() // you fools
{
var plant = Contained();
if (plant)
{
var pos = plant->GetFruitExitPosition(attach_branch);
Exit(pos.x, pos.y);
plant->DetachFruit(attach_branch); // bye-bye
}
SetAction("Fly");
SetYDir(-1);
var effect = AddEffect("IntFlight", this, 1, 5, this);
effect.random = Random(360);
}
private func FxIntFlightTimer(object target, proplist effect, int time)
{
var xdir = GetXDir();
var ydir = GetYDir();
if (time < float_time)
{
ydir += Sin(time + effect.random, 2);
}
// Fly upwards
if (time > float_time)
{
if (time < float_time + fly_time / 2)
ydir -= Max(Tan((900 - time + float_time)%900, 10, 10), 0);
// Fly with the wind
if (GetWind())
xdir += Tan((time - float_time)%900, GetWind(), 10);
else if (xdir != 0)
xdir -= xdir / Abs(xdir);
}
if (time > float_time + fly_time / 2)
{
if (ydir < 0) ydir += 1;
// ~1 second until pop!
}
if (time > float_time + fly_time + 35)
{
if (TryPop())
{
// Pop deletes this object
return FX_Execute_Kill;
}
else
{
// Try again in a second or so.
fly_time += 20 + Random(20);
if (!Random(5) || GetContact())
{
Pop(true);
return FX_Execute_Kill;
}
}
}
SetSpeed(xdir, ydir);
SetProperty("MeshTransformation", Trans_Rotate(180 + BoundBy(xdir, -50, 50), 0,0,1));
return FX_OK;
}
/* Shooting, harvesting, popping */
public func IsProjectileTarget() { return true; }
public func IsHarvestable() { return true; }
public func SickleHarvesting() { return true; }
public func OnProjectileHit() { Pop(); }
public func Damage() { Pop(); }
public func Harvest() { Pop(); }
// Checks whether the ground below would be suitable for planting and then pops the fruit.
public func TryPop()
{
// Find the ground below.
var max_h = 300;
var stepsize = 20;
var y = 0;
for (; y < max_h; y += stepsize)
if (GBackSolid(0, y))
break;
// No ground hit?
if (y >= max_h) return false;
// Ground found! Then check again in smaller steps to actually find the surface.
y -= stepsize;
for (var i = 0; i < stepsize; i += 2)
{
y += 2;
if (!GBackSolid(0, y)) continue;
// Solid! But is it any good?
if (GetMaterialVal("Soil", "Material", GetMaterial(0, y)) != 1)
return false;
break;
}
// Soil found! Overpopulated already?
if (ObjectCount(Find_AtPoint(0, y), Find_ID(Cotton)) > 1) return false;
// Spot found! Place a plant and cast some "seed" like particles.
var plant = CreateObjectAbove(Cotton, 0, y, GetOwner());
plant->SetCon(1);
var particles =
{
Prototype = Particles_Glimmer(),
ForceX = PV_Wind(10),
Stretch = PV_Speed(500, 500),
R = 255, G = 255, B = 255,
BlitMode = nil,
Size = PV_Linear(1, 0)
};
CreateParticle("SphereSpark", 0, 0, PV_Random(-5, 5), PV_Random(-5, 20), PV_Random(100, 300), particles, 30);
Pop(true);
return true;
}
public func Pop(bool no_seed)
{
CreateParticle("CottonBalloon", 0,0, PV_Random(-30,30), PV_Random(-30,30), PV_Random(40, 60), Particles_CottonBalloon(), 50);
if (!no_seed)
{
var seed = CreateObject(CottonSeed);
if (OnFire()) seed->Incinerate();
}
RemoveObject();
}
/*-- Saving --*/
public func SaveScenarioObject(proplist props)
{
// Do not save fruit inside a cotton plant
if (Contained())
if (Contained()->GetID() == Cotton)
return false;
var effect;
if (effect = GetEffect("IntFlight", this))
{
props->AddCall("Flight", this, "ResumeFlight", effect.time, effect.random);
}
return true;
}
// Loading
private func ResumeFlight(int effect_time, int effect_random)
{
var effect = AddEffect("IntFlight", this, 1, 5, this);
effect.time = effect_time;
effect.random = effect_random;
}
local ActMap = {
Fly = {
Prototype = Action,
Name = "Fly",
Procedure = DFA_FLOAT,
Speed = 50,
Accel = 5,
NextAction = "Fly",
},
};
local Plane = 460;
local BlastIncinerate = 1;
local ContactIncinerate = 3;
local BorderBound = C4D_Border_Sides;
|