New: Cotton, Cotton Seed, Cloth, Seed Library

shapetextures
Clonkonaut 2015-09-18 19:44:49 +02:00
parent a2f2aff572
commit 6a9ef8b961
50 changed files with 990 additions and 73 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,3 @@
[Particle]
Name=CottonBalloon
Face=0,0,32,32,-16,-16

View File

@ -2,14 +2,14 @@
id=Cloth
Version=6,0
Category=C4D_Object
Width=8
Height=4
Offset=-4,-2
Vertices=3
VertexX=0,3,-3
VertexY=0,1,1
VertexFriction=50,100,100
Width=12
Height=12
Offset=-6,-6
Vertices=4
VertexX=-4,-4,4,4
VertexY=4,-4,4,-4
VertexFriction=100,100,100,100
Value=10
Mass=1
Components=Cotton=1
Picture=32,0,64,64
Components=CottonSeed=1
Rotate=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -0,0 +1,21 @@
material Fabric_carryheavy
{
receive_shadows on
technique
{
pass
{
ambient 0.800000011920929 0.800000011920929 0.800000011920929 1.0
diffuse 0.800000011920929 0.800000011920929 0.800000011920929 1.0
specular 0.0 0.0 0.0 1.0 12.5
emissive 0.0 0.0 0.0 1.0
texture_unit
{
texture fabric.png
tex_address_mode wrap
filtering trilinear
}
}
}
}

View File

@ -1,12 +1,27 @@
/*-- Cloth --*/
protected func Hit()
#include Library_CarryHeavy
public func GetCarryMode(clonk) { return CARRY_BothHands; }
public func GetCarryPhase() { return 800; }
public func GetCarryTransform(clonk)
{
if(GetCarrySpecial(clonk))
return Trans_Translate(2000, 4500, 6500);
}
private func Hit()
{
Sound("GeneralHit?");
}
public func IsLoomProduct() { return true; }
private func Definition(def)
{
SetProperty("PictureTransformation", Trans_Mul(Trans_Rotate(-45,1), Trans_Rotate(-20,0,0,1)), def);
}
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 1;

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

View File

@ -1,13 +0,0 @@
[DefCore]
id=Cotton
Version=6,0
Category=C4D_Object
Width=8
Height=8
Offset=-4,-4
Vertices=2
VertexX=0,0
VertexY=1,-1
VertexFriction=40,40,40
Value=5
Mass=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -1,42 +0,0 @@
/*
Cotton
Author: Clonkonaut
For planting cotton plants or to get cloth
*/
/*public func ControlUse(object clonk, int x, int y, bool box)
{
if(clonk->GetAction() != "Walk") return true;
// Search for ground
x = 0; y = 0;
if (GBackSemiSolid(x,y)) return true;
var i = 0;
while (!GBackSolid(x,y) && i < 15) { ++y; ++i; }
if (!GBackSolid(x,y)) return true;
if (GetMaterialVal("Soil", "Material", GetMaterial(x,y)) == 1)
{
// Plant!
clonk->DoKneel();
CreateObjectAbove(Wheat, x, y, clonk->GetOwner())->SetCon(1);
RemoveObject();
}
else
clonk->Message("$NoSuitableGround$");
return true;
}*/
protected func Hit()
{
Sound("GeneralHit?");
}
public func IsLoomIngredient() { return true; }
local Collectible = 1;
local Name = "$Name$";
local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Rebuy = true;
local Plane = 460;

View File

@ -1,4 +0,0 @@
Name=Baumwolle
Description=Baumwolle zum Anpflanzen oder Verarbeiten zu Stoff
UsageHelp=Drücke [Benutzen] um hier eine Baumwollpflanze anzupflanzen.
NoSuitableGround=Untergrund ist zum|Anbau nicht geeignet!

View File

@ -1,4 +0,0 @@
Name=Cotton
Description=Cotton for planting a cotton plant or producing cloth.
UsageHelp=Press [Use] to plant a cotton plant here.
NoSuitableGround=Ground not suitable|for planting!

View File

@ -0,0 +1,13 @@
[DefCore]
id=CottonSeed
Version=6,1
Category=C4D_Object
Width=11
Height=11
Offset=-5,-5
Vertices=3
VertexX=0,3,-3
VertexY=4,-4,-4
VertexFriction=40,40,40
Value=5
Mass=1

View File

@ -0,0 +1,24 @@
material cotton_seed
{
receive_shadows on
technique
{
pass
{
cull_hardware none
depth_check on
ambient 1.0 1.0 1.0 1.0
diffuse 1.0 1.0 1.0 1.0
specular 0.0 0.0 0.0 1.0 12.5
emissive 0.0 0.0 0.0 1.0
texture_unit
{
texture cotton_seed.png
tex_address_mode wrap
filtering trilinear
}
}
}
}

View File

@ -0,0 +1,26 @@
/*
Cotton Seed
Author: Clonkonaut
For planting cotton plants or to get cloth
*/
#include Library_Seed
local lib_seed_plant = Cotton;
public func GetCarryMode() { return CARRY_HandBack; }
public func GetCarryBone() { return "main"; }
private func Hit()
{
Sound("GeneralHit?");
}
local Collectible = 1;
local Name = "$Name$";
local Description = "$Description$";
local Rebuy = true;
local Plane = 460;
local BlastIncinerate = 5;
local ContactIncinerate = 2;

View File

@ -0,0 +1,4 @@
Name=Baumwollsamen
Description=Baumwollsamen können angepflanzt oder zu Stoff verarbeitet werden.
UsageHelp=Drücke [Benutzen] um hier eine Baumwollpflanze anzupflanzen.
NoSuitableGround=Untergrund ist zum|Anbau nicht geeignet!

View File

@ -0,0 +1,4 @@
Name=Cotton Seed
Description=Cotton seed may be sown or fabricated into cloth.
UsageHelp=Press [Use] to plant a cotton plant here.
NoSuitableGround=Ground not suitable|for planting!

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -0,0 +1,4 @@
[DefCore]
id=Library_Seed
Version=6,1
Category=C4D_StaticBack

View File

@ -0,0 +1,151 @@
/**
Seed
Logic for plant seed that will eventually sprout a plant if left alone long enough.
Seeds may also be manually planted by using leftclick.
@author Clonkonaut
*/
// The last known position of the seed
local lib_seed_pos;
// The timer cycles the seed has been lying idly (until it has reached 10)
local lib_seed_idletime = 0;
// The time it takes until a new plant might be created, feel free to reassign
local lib_seed_planttime = 10500;
// ****** Must be the id of the plant to create. Do not leave empty.
local lib_seed_plant;
/* Reproduction control */
/** Distance the seeds checks for plants. Default is 250.
@return the maximum distance.
*/
private func SeedArea()
{
return 250;
}
/** The amount of plants allowed within SeedArea. Default is 10.
@return the maximum amount of plants.
*/
private func SeedAmount()
{
return 10;
}
/** The closest distance a new plant may seed to its nearest neighbour. Default is 20.
@return the maximum amount of plants.
*/
private func SeedOffset()
{
return 20;
}
/* Reproduction */
private func Initialize()
{
AddTimer("CheckSprout", lib_seed_planttime / 10);
lib_seed_pos = { x = GetX(), y = GetY() };
}
// Check whether a plant should be created
private func CheckSprout()
{
if (Contained()) return;
if (OnFire()) return;
if (!CheckPosition()) return;
lib_seed_idletime++;
if (lib_seed_idletime >= 10)
{
// Is this place nice to make a new plant?
if (!CheckPlantConditions())
lib_seed_idletime = 0;
else
Sprout();
}
}
// Check whether the seed was moved
private func CheckPosition()
{
if (GetX() != lib_seed_pos.x || GetY() != lib_seed_pos.y)
{
lib_seed_pos.x = GetX();
lib_seed_pos.y = GetY();
lib_seed_idletime = 0;
return false;
}
return true;
}
/* Sprouting */
// Returns the relative y to the ground from center to max 20 pixels.
// If no ground is found returns -1, also if the center is underground (stuck in material)
private func GetGroundPos()
{
if (GBackSolid()) return -1;
var y = 0;
while (!GBackSolid(0,y) && y < 21)
y++;
if (y >= 21) return -1;
return y;
}
/** Overload as necessary to check for further conditions. Return true if a new plant should be created.
*/
private func CheckPlantConditions()
{
var ground = GetGroundPos();
// No solid ground
if (ground == -1) return false;
// No fertile ground
if (GetMaterialVal("Soil", "Material", GetMaterial(0,ground)) != 1) return false;
// Do not grow underground
if (!GBackSky(0, ground-1) && !GBackSky(0,0) && !GBackSky(0,-30)) return false;
// Too many plants around
var size = SeedArea();
if (ObjectCount(Find_ID(lib_seed_plant), Find_InRect(-size / 2, -size / 2, size, size)) > SeedAmount()) return false;
// Another plant too close
var neighbour = FindObject(Find_ID(lib_seed_plant), Sort_Distance());
if (neighbour)
if (ObjectDistance(neighbour) < SeedOffset())
return false;
return true;
}
/** Overload as necessary. Should remove the seed.
*/
private func Sprout()
{
var ground = GetGroundPos();
var plant = CreateObjectAbove(lib_seed_plant, 0, ground, GetOwner());
if (!plant) return; // What now?
plant->SetCon(1);
RemoveObject();
}
/* Planting */
public func ControlUse(object clonk, int x, int y, bool box)
{
if(!clonk->~IsWalking()) return true;
var ground = GetGroundPos();
if (ground == -1)
return CustomMessage(Format("$TxtBadGround$", GetName()), clonk, clonk->GetController(), 0, 0, 0xff0000);
// Soil is needed
if (GetMaterialVal("Soil", "Material", GetMaterial(0,ground)) != 1)
return CustomMessage(Format("$TxtBadGround$", GetName()), clonk, clonk->GetController(), 0, 0, 0xff0000);
// Plant!
clonk->DoKneel();
Sprout();
return true;
}
local UsageHelp = "$UsageHelp$";

View File

@ -0,0 +1,2 @@
TxtBadGround=%s kann hier|nicht gepflanzt werden!
UsageHelp=Drücke [Benutzen] zum Anpflanzen.

View File

@ -0,0 +1,2 @@
TxtBadGround=Can't plant %s here!
UsageHelp=Press [Use] to plant.

View File

@ -0,0 +1,7 @@
[DefCore]
id=Cotton_Branch
Version=6,1
Category=C4D_StaticBack
Width=20
Height=16
Offset=-10,-8

View File

@ -0,0 +1,24 @@
material Cotton_Branch
{
receive_shadows on
technique
{
pass
{
cull_hardware clockwise
depth_check on
ambient 1.0 1.0 1.0 1.0
diffuse 1.0 1.0 1.0 1.0
specular 0.0 0.0 0.0 1.0 12.5
emissive 0.0 0.0 0.0 1.0
texture_unit
{
texture cottonbranch.png
tex_address_mode wrap
filtering trilinear
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

View File

@ -0,0 +1,11 @@
[DefCore]
id=Cotton_Fruit
Version=6,1
Width=11
Height=11
Offset=-5,-5
Vertices=3
VertexX=0,3,-3
VertexY=4,-4,-4
VertexFriction=10,10,10
BorderBound=1

View File

@ -0,0 +1,42 @@
material Cotton_Fruit
{
receive_shadows on
technique
{
pass
{
cull_hardware none
scene_blend alpha_blend
depth_check on
alpha_rejection greater_equal 128
ambient 1.0 1.0 1.0 1.0
diffuse 1.0 1.0 1.0 1.0
specular 0.0 0.0 0.0 1.0 12.5
emissive 0.0 0.0 0.0 1.0
texture_unit
{
texture cottonfruit_growing.png
tex_address_mode wrap
filtering trilinear
}
}
}
}
material Cotton_Fruit_Ripe: Cotton_Fruit
{
technique 0
{
pass 0
{
texture_unit
{
texture cottonfruit.png
tex_address_mode wrap
filtering trilinear
}
}
}
}

View File

@ -0,0 +1,214 @@
/*
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)
{
Pop();
// Pop deletes this object
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(); }
public func Pop()
{
CreateParticle("CottonBalloon", 0,0, PV_Random(-15,15), PV_Random(-10,-5), 100, Particles_CottonBalloon(), RandomX(4,8));
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;

View File

@ -0,0 +1,2 @@
Name=Baumwollfrucht
Description=Ein gasgefüllter Ballon, der die begehrte Wolle birgt. Beim Treffer von einer Waffe platzt der Ballon.

View File

@ -0,0 +1,2 @@
Name=Cotton fruit
Description=A gas filled balloon that holds the precious cotton. Strike with any weapon to let it pop.

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

View File

@ -0,0 +1,15 @@
[DefCore]
id=Cotton
Version=6,1
Category=C4D_StaticBack
Width=20
Height=36
Offset=-10,-18
Vertices=2
VertexX=0,0
VertexY=17,19
VertexCNAT=16,8
VertexFriction=10,100
Mass=6
StretchGrowth=4
Oversize=1

View File

@ -0,0 +1,25 @@
material Cotton_Bush
{
receive_shadows on
technique
{
pass
{
cull_hardware none
scene_blend alpha_blend
depth_check off
ambient 1.0 1.0 1.0 1.0
diffuse 1.0 1.0 1.0 1.0
specular 0.0 0.0 0.0 1.0 12.5
emissive 0.0 0.0 0.0 1.0
texture_unit
{
texture cotton.png
tex_address_mode wrap
filtering trilinear
}
}
}
}

View File

@ -0,0 +1,354 @@
/*
Cotton
Author: Clonkonaut
Medium crop for farming.
*/
#include Library_Plant
#include Library_Crop
local direction = 1;
local capacity; // How much fruit this plant will yield
local branches; // Array with 3 proplists containing all the needed values for the 3 branches
local branch_proto = {
// As returned by AttachMesh
attach_slot = 0,
// Growing time until ready to bear fruit, growing time afterwards is determined by the fruit
grow_time = 4500,
// The branch's animation length is 875, 490 marks the point just before the branch start raising up
first_animation_stage = 490,
// Whether the branch is ready to bear fruit yet
grown = false,
// As returned by PlayAnimation
grow_animation = -1,
// As returned by AddEffect
grow_effect = nil,
// The fruit object (in Contents)
fruit = nil,
// As returned by AttachMesh
fruit_slot = 0,
// Internal value to (securely) stop new fruit from growing at this branch
no_fruit = false,
};
private func Construction()
{
_inherited(...);
RemoveTimer("Seed");
capacity = Random(3) + 3;
StartGrowth(this.growth);
AddTimer("WaterCheck", 70+Random(10));
// The mesh doesn't have more than 3 bones, beware
branches = CreateArray(3);
branches[0] = new branch_proto {};
branches[1] = new branch_proto {};
branches[2] = new branch_proto {};
// Make half of the plants switch direction for more variety
if (!Random(2)) direction = -1;
SetProperty("MeshTransformation", Trans_Rotate(RandomX(70,110) * direction, 0,1,0));
}
private func Initialize()
{
AddTimer("Reproduction", 72);
}
/* Reproduction */
private func Reproduction()
{
if (OnFire()) return;
if (GetCon() < 100) return RemoveTimer("Reproduction");
if (!capacity && !HasFruit()) return Perish();
if (CheckSeedChance())
{
var can_branch = GetNextGrowableBranch();
var can_fruit = GetNextFruitableBranch();
if ((can_branch != -1 && Random(3)) || can_fruit == -1)
return GrowBranch();
if (can_fruit != -1)
{
GrowFruit();
capacity--;
}
}
}
// The plant will wither away after it has grown all its fruit
private func Perish()
{
RemoveTimer("Reproduction");
StartGrowth(this.degrowth);
SetClrModulation(RGB(185, 122, 87));
}
/* Branch growth */
// Will grow the next branch unless all 3 are grown (returns false then, true otherwise)
// If fullgrown is true, the branch will be fully grown but not bear a fruit!
public func GrowBranch(bool fullgrown, int branch)
{
if (branch != nil)
var next_to_grow = branch;
else
var next_to_grow = GetNextGrowableBranch(fullgrown);
if (next_to_grow == -1) return false;
if (branches[next_to_grow].grow_animation != branch_proto.grow_animation) // Already growing, fullgrown must be true
{
if (!fullgrown) return false; // This can't happen
FinishBranchGrowth(next_to_grow);
return true;
}
branches[next_to_grow].attach_slot = AttachMesh(Cotton_Branch, Format("Stem%d", next_to_grow + 1), "main", GetBranchAttachTransform(next_to_grow, 1));
branches[next_to_grow].grow_animation = PlayAnimation("grow", next_to_grow + 1, Anim_Linear(0,0, branches[next_to_grow].first_animation_stage, branches[next_to_grow].grow_time, ANIM_Hold), Anim_Const(1000), nil, branches[next_to_grow].attach_slot);
branches[next_to_grow].grow_effect = AddEffect("IntBranchGrowth", this, 1, 35, this);
branches[next_to_grow].grow_effect.branch = next_to_grow;
if (fullgrown) FinishBranchGrowth(next_to_grow);
return true;
}
// Determines the next branch to grow. If fullgrown is true, growing but unfinished branches are also returned
private func GetNextGrowableBranch(bool fullgrown)
{
var ret = -1;
var i = -1;
while(++i < GetLength(branches))
{
if (branches[i].grown) continue;
if (branches[i].grow_animation != branch_proto.grow_animation && !fullgrown) continue;
ret = i;
break;
}
return ret;
}
private func FxIntBranchGrowthTimer(object target, proplist effect, int time)
{
UpdateBranchAttachTransform(effect.branch, GetAnimationPosition(branches[effect.branch].grow_animation, branches[effect.branch].attach_slot) * 500 / branches[effect.branch].first_animation_stage);
if (GetAnimationPosition(branches[effect.branch].grow_animation, branches[effect.branch].attach_slot) >= branches[effect.branch].first_animation_stage)
FinishBranchGrowth(effect.branch);
}
private func FinishBranchGrowth(int branch)
{
branches[branch].grown = true;
RemoveEffect(nil, nil, branches[branch].grow_effect);
UpdateBranchAttachTransform(branch, 500);
SetAnimationPosition(branches[branch].grow_animation, Anim_Const(branches[branch].first_animation_stage), branches[branch].attach_slot);
}
private func UpdateBranchAttachTransform(int branch, int scale)
{
SetAttachTransform(branches[branch].attach_slot, GetBranchAttachTransform(branch, scale));
}
private func GetBranchAttachTransform(int branch, int scale)
{
// These transforms have been determined by careful testing
if (branch == 0) return Trans_Mul(Trans_Rotate(-30,0,1), Trans_Rotate(-100,0,0,1), Trans_Rotate(-90,0,1), Trans_Translate(1000), Trans_Scale(scale));
if (branch == 1) return Trans_Mul(Trans_Rotate(30,0,1), Trans_Rotate(-100,0,0,1), Trans_Rotate(90,0,1), Trans_Translate(1000), Trans_Scale(scale));
if (branch == 2) return Trans_Mul(Trans_Rotate(-100,0,0,1), Trans_Rotate(-90,0,1), Trans_Scale(scale));
}
/* Fruit growth */
// Will grow a fruit but only if there's a fully grown branch without a fruit available
// If fullgrown is true, the fruit will be ripe immediately. If there was no full grown branch, a branch will also be complete.
// Does return the fruit object or nil
public func GrowFruit(bool fullgrown)
{
var next_to_grow = GetNextFruitableBranch(fullgrown);
if (next_to_grow == -1) return nil;
if (branches[next_to_grow].fruit) // Already growing, fullgrown must be true
{
if (!fullgrown) return false; // This can't happen
branches[next_to_grow].fruit->Grow(next_to_grow, fullgrown);
return branches[next_to_grow].fruit;
}
if (!branches[next_to_grow].grown)
GrowBranch(fullgrown, next_to_grow);
branches[next_to_grow].fruit = CreateContents(Cotton_Fruit);
if (!branches[next_to_grow].fruit) return false;
branches[next_to_grow].fruit_slot = AttachMesh(branches[next_to_grow].fruit, "fruit_target", "leafes", GetFruitAttachTransform(1, 0), nil, branches[next_to_grow].attach_slot);
branches[next_to_grow].fruit->Grow(next_to_grow, fullgrown);
return branches[next_to_grow].fruit;
}
// Determines the next brnach to grow a fruit. If fullgrown is true, unfinished branches are also returned
private func GetNextFruitableBranch(bool fullgrown)
{
var ret = -1;
var i = -1;
while(++i < GetLength(branches))
{
if (branches[i].no_fruit) continue;
if (branches[i].fruit && !(fullgrown && branches[i].fruit->IsGrowing())) continue;
if (!branches[i].grown && !fullgrown) continue;
ret = i;
break;
}
return ret;
}
// Usually called by the fruit
public func UpdateFruitAttachTransform(int branch, int scale)
{
SetAttachTransform(branches[branch].fruit_slot, GetFruitAttachTransform(scale, 0));
}
private func GetFruitAttachTransform(int scale, int extra_r)
{
return Trans_Mul(Trans_Rotate(180, 0,1,0), Trans_Rotate(extra_r, 0,0,1), Trans_Scale(scale));
}
// Called by the fruit when the first growing animation is done
// The branch will restart its animation with duration of time
public func FruitFills(int branch, int time, bool fullgrown)
{
var pos = GetAnimationPosition(branches[branch].grow_animation, branches[branch].attach_slot);
if (fullgrown)
SetAnimationPosition(branches[branch].grow_animation, Anim_Const(GetAnimationLength("grow", branches[branch].attach_slot)), branches[branch].attach_slot);
else
SetAnimationPosition(branches[branch].grow_animation, Anim_Linear(GetAnimationPosition(branches[branch].grow_animation,
branches[branch].attach_slot),
0,
GetAnimationLength("grow", branches[branch].attach_slot),
time,
ANIM_Hold), branches[branch].attach_slot);
}
// Called by the fruit before it flies off to give fair warning
public func ShakeFruit(int branch)
{
var effect = AddEffect("IntShakeFruit", this, 1, 1, this);
effect.branch = branch;
}
private func FxIntShakeFruitTimer(object target, proplist effect, int time)
{
if (time > 24)
return FX_Execute_Kill;
var angle = Sin(time * 45, 4);
SetAttachTransform(branches[effect.branch].fruit_slot, GetFruitAttachTransform(2000, angle));
return FX_OK;
}
// Called by the fruit
// Returns the exit position of the fruit relative to the cotton plant
// TODO: Do not hardcode positions but get the bone's position in Clonk coordinates (as of now feature is missing)
public func GetFruitExitPosition(int branch)
{
if (branch == 0) return { x = 7 * direction, y = 0};
if (branch == 1) return { x = -7 * direction, y = -1};
if (branch == 2) return { x = 11 * direction, y = -16};
return {}; // Unknown
}
// Called by the fruit when it exits the plant
// Will reverse the animation to full grown but not with fruit state in 35 frames
// Accordingly, branches[].fruit will be cleared after 35 frames
public func DetachFruit(int branch)
{
DetachMesh(branches[branch].fruit_slot);
SetAnimationPosition(branches[branch].grow_animation, Anim_Linear(GetAnimationPosition(branches[branch].grow_animation,
branches[branch].attach_slot),
GetAnimationLength("grow",
branches[branch].attach_slot),
branches[branch].first_animation_stage,
35,
ANIM_Hold), branches[branch].attach_slot);
ScheduleCall(this, "ClearFruit", 35, nil, branch);
// This is to ensure that within the next 35 frames no new fruit will grow on this branch
// This is necessary because the actual fruit object might be deleted before that time
branches[branch].no_fruit = true;
}
private func ClearFruit(int branch)
{
branches[branch].fruit = nil;
branches[branch].fruit_slot = nil;
branches[branch].no_fruit = nil;
}
private func HasFruit()
{
for (var branch in branches)
if (branch.fruit)
return true;
return false;
}
/* Crop Library */
public func SickleHarvesting()
{
return true;
}
// Only harvestable if it has a ripe fruit
public func IsHarvestable()
{
for (var branch in branches)
if (branch.fruit)
if (!branch.fruit->~IsGrowing())
return true;
return false;
}
public func Harvest(object clonk)
{
var fruit = -1;
for (var i = 0; i < GetLength(branches); i++)
if(branches[i].fruit)
if(!branches[i].fruit->~IsGrowing())
{
fruit = i;
break;
}
if (fruit == -1) return false;
branches[fruit].fruit->Fly();
return true;
}
/*-- Saving --*/
public func SaveScenarioObject(proplist props)
{
// The fruit are not saved if still inside, do a rudimentary save.
// Saving all little details of growth is a bit too much for such an always changing state.
for (var i = 0; i < GetLength(branches); i++)
{
// Branch has a fruit, save if grown
if (branches[i].fruit)
{
if (!branches[i].fruit->IsGrowing())
{
props->AddCall("Branch", this, "GrowBranch", true, i);
props->AddCall("Fruit", this, "GrowFruit", true);
}
else // If fruit is not fully grown, just save the branch
props->AddCall("Branch", this, "GrowBranch", true, i);
}
else if (branches[i].grown) // Save only fully grown branches
props->AddCall("Branch", this, "GrowBranch", true, i);
}
return true;
}
/* Definition */
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 0;
local growth = 3;
local degrowth = -6;
local fastgrowth = 9;

View File

@ -0,0 +1,2 @@
Name=Baumwolle
Description=Eine fruchttragende Pflanze. Die Früchte enthalten Wolle, die zur Stoffproduktion verwendet werden kann.

View File

@ -0,0 +1,2 @@
Name=Cotton
Description=A fruit bearing plant. The fruit contain wool that may be used to produce cloth.

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

View File

@ -243,6 +243,17 @@ global func Particles_Straw()
};
}
global func Particles_CottonBalloon()
{
return
{
Prototype = Particles_WoodChip(),
Phase = PV_Random(0, 3),
Size = PV_Random(4, 8),
Attach = nil
};
}
global func Particles_Air()
{
return