2011-10-05 03:31:06 +00:00
/**
Plant
Basic functionality for all plants
2010-07-21 09:58:17 +00:00
2011-10-05 03:31:06 +00:00
@ author Clonkonaut
*/
2010-07-21 09:58:17 +00:00
2011-10-05 03:31:06 +00:00
// This is a plant
public func IsPlant ( )
2010-07-21 09:58:17 +00:00
{
2011-10-05 03:31:06 +00:00
return true ;
2010-07-21 09:58:17 +00:00
}
2012-06-07 17:29:00 +00:00
/** Automated positioning via RootSurface, make sure to call this if needed (in case Construction is overloaded)
*/
protected func Construction ( )
{
Schedule ( this , " RootSurface() " , 1 ) ;
AddTimer ( " Seed " , 72 ) ;
_inherited ( . . . ) ;
}
2012-06-07 00:45:38 +00:00
/* Placement */
/** Places the given amount of plants inside the area. If no area is given, the whole landscape is used.
@ param amount The amount of plants to be created ( not necessarily every plant is created ) .
@ param rectangle The area where to put the plants .
2012-06-07 17:29:00 +00:00
@ param settings A proplist defining further setttings : { growth = 100000 , keep_area = false } . Growth will get passed over to PlaceVegetation , keep_area will confine the plants and their offspring to rectangle .
@ return Returns an array of all objects created .
2012-06-07 00:45:38 +00:00
*/
2012-06-07 17:29:00 +00:00
public func Place ( int amount , proplist rectangle , proplist settings )
2012-06-07 00:45:38 +00:00
{
// No calls to objects, only definitions
if ( GetType ( this ) = = C4V_C4Object ) return ;
2012-06-07 17:29:00 +00:00
// Default parameters
if ( ! settings ) settings = { growth = 100000 , keep_area = false } ;
if ( ! settings . growth ) settings . growth = 100000 ;
2012-06-07 00:45:38 +00:00
if ( ! rectangle )
rectangle = Rectangle ( 0 , 0 , LandscapeWidth ( ) , LandscapeHeight ( ) ) ;
2012-06-07 17:29:00 +00:00
var plants = CreateArray ( ) , plant ;
2012-06-07 00:45:38 +00:00
for ( var i = 0 ; i < amount ; i + + )
2012-06-07 17:29:00 +00:00
{
plant = PlaceVegetation ( this , rectangle . x , rectangle . y , rectangle . w , rectangle . h , settings . growth ) ;
if ( plant )
{
plants [ GetLength ( plants ) ] = plant ;
if ( settings . keep_area )
plant - > KeepArea ( rectangle ) ;
}
plant = nil ;
}
return plants ;
2012-06-07 00:45:38 +00:00
}
/* Reproduction */
2012-06-07 17:29:00 +00:00
/** Will confine the the plant and its offspring to a certain area.
@ params rectangle The confinement area .
*/
func KeepArea ( proplist rectangle )
{
this . Confinement = rectangle ;
}
2011-10-05 03:31:06 +00:00
/** Chance to reproduce plant. Chances are one out of return value. Default is 500.
@ return the chance , higher = less chance .
*/
2012-06-07 00:45:38 +00:00
private func SeedChance ( )
{
return 500 ;
}
2012-02-25 15:15:17 +00:00
2012-05-06 11:33:39 +00:00
/** Distance the seeds may travel. Default is 250.
2011-10-05 03:31:06 +00:00
@ return the maximum distance .
*/
2012-06-07 00:45:38 +00:00
private func SeedArea ( )
{
return 250 ;
}
2012-02-25 15:15:17 +00:00
2012-05-06 11:33:39 +00:00
/** The amount of plants allowed within SeedAreaSize. Default is 10.
2011-10-05 03:31:06 +00:00
@ return the maximum amount of plants .
*/
2012-06-07 00:45:38 +00:00
private func SeedAmount ( )
{
return 10 ;
}
2011-10-05 03:31:06 +00:00
2013-09-22 20:39:43 +00:00
/** 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 ;
}
2012-05-06 11:33:39 +00:00
/** Reproduction of plants: Called every 2 seconds by a timer.
2011-10-05 03:31:06 +00:00
*/
2012-05-05 10:09:44 +00:00
public func Seed ( )
2010-07-21 09:58:17 +00:00
{
2012-05-06 11:33:39 +00:00
// Find number of plants in seed area.
var size = SeedArea ( ) ;
var amount = SeedAmount ( ) ;
2013-09-22 20:39:43 +00:00
var area = Rectangle ( size / - 2 , size / - 2 , size , size ) ;
2012-06-07 17:29:00 +00:00
if ( this . Confinement )
area = RectangleEnsureWithin ( area , this . Confinement ) ;
2013-09-22 20:39:43 +00:00
var plant_cnt = ObjectCount ( Find_ID ( GetID ( ) ) , Find_InRect ( area . x , area . y , area . w , area . h ) ) ;
2012-05-06 11:33:39 +00:00
// If there are not much plants in the seed area compared to seed amount
// the chance of seeding is improved, if there are much the chance is reduced.
var chance = SeedChance ( ) ;
var chance = chance / Max ( 1 , amount - plant_cnt ) + chance * Max ( 0 , plant_cnt - amount ) ;
2015-04-02 17:46:33 +00:00
// Place a plant if we are lucky, but no more than seed amount.
if ( plant_cnt < amount & & ! Random ( chance ) )
2010-07-21 09:58:17 +00:00
{
2012-05-06 11:33:39 +00:00
// Place the plant but check if it is not close to another one.
2013-09-22 20:39:43 +00:00
var plant = PlaceVegetation ( GetID ( ) , area . x , area . y , area . w , area . h , 3 ) ;
2012-05-06 11:33:39 +00:00
if ( plant )
2010-07-21 09:58:17 +00:00
{
2012-05-06 11:33:39 +00:00
var neighbour = FindObject ( Find_ID ( GetID ( ) ) , Find_Exclude ( plant ) , Sort_Distance ( plant - > GetX ( ) - GetX ( ) , plant - > GetY ( ) - GetY ( ) ) ) ;
var distance = ObjectDistance ( plant , neighbour ) ;
2013-09-22 20:39:43 +00:00
// Closeness check
if ( distance < SeedOffset ( ) )
2012-05-06 11:33:39 +00:00
plant - > RemoveObject ( ) ;
2012-06-07 17:29:00 +00:00
else if ( this . Confinement )
plant - > KeepArea ( this . Confinement ) ;
2010-07-21 09:58:17 +00:00
}
}
2012-05-06 11:33:39 +00:00
return ;
2010-07-21 09:58:17 +00:00
}
2011-10-14 00:04:33 +00:00
/* Chopping */
/** Determines whether this plant gives wood (we assume that are 'trees').
@ return \ c true if the plant is choppable by the axe , \ c false otherwise ( default ) .
*/
public func IsTree ( )
{
return false ;
}
/** Determines whether the tree can still be chopped down (i.e. has not been chopped down).
@ return \ c true if the tree is still a valid axe target .
*/
public func IsStanding ( )
{
return GetCategory ( ) & C4D_StaticBack ;
}
/** Maximum damage the tree can take before it falls. Each blow from the axe deals 10 damage.
@ return \ c the maximum amount of damage .
*/
private func MaxDamage ( )
{
return 50 ;
}
protected func Damage ( )
{
2012-03-24 15:18:55 +00:00
// do not grow for a few seconds
var g = GetGrowthValue ( ) ;
if ( g )
{
StopGrowth ( ) ;
ScheduleCall ( this , " RestartGrowth " , 36 * 10 , 0 , g ) ;
}
2011-10-14 00:04:33 +00:00
// Max damage reached -> fall down
if ( GetDamage ( ) > MaxDamage ( ) & & IsStanding ( ) ) ChopDown ( ) ;
_inherited ( . . . ) ;
}
2012-03-24 15:18:55 +00:00
// restarts the growing of the tree (for example after taking damage)
func RestartGrowth ( int old_value )
{
var g = GetGrowthValue ( ) ; // safety
if ( g ) StopGrowth ( ) ;
g = Max ( g , old_value ) ;
StartGrowth ( g ) ;
}
2011-10-14 00:04:33 +00:00
/** Called when the trees shall fall down (has taken max damage). Default behaviour is unstucking (5 pixel movement max) and removing C4D_StaticBack.
*/
public func ChopDown ( )
{
2012-04-17 12:28:37 +00:00
// stop growing!
ClearScheduleCall ( this , " RestartGrowth " ) ;
StopGrowth ( ) ;
2011-10-14 00:04:33 +00:00
this . Touchable = 1 ;
2012-07-21 16:15:47 +00:00
this . Plane = 300 ;
2011-10-14 00:04:33 +00:00
SetCategory ( GetCategory ( ) & ~ C4D_StaticBack ) ;
if ( Stuck ( ) )
{
var i = 5 ;
while ( Stuck ( ) & & i )
{
SetPosition ( GetX ( ) , GetY ( ) - 1 ) ;
i - - ;
}
}
2012-07-22 19:15:49 +00:00
Sound ( " TreeCrack " ) ;
AddEffect ( " TreeFall " , this , 1 , 1 , this ) ;
}
// determine a random falling direction and passes it on to the FxTreeFallTimer.
func FxTreeFallStart ( object target , proplist effect )
{
effect . direction = Random ( 2 ) ;
if ( effect . direction = = 0 ) effect . direction - = 1 ;
}
/* animates the falling of the tree: First 10 slow degress then speed up and play the landing sound at 80+ degrees.
remember that degrees range from - 180 to 180. */
func FxTreeFallTimer ( object target , proplist effect )
{
//simple falling if tree is not fully grown
2012-10-14 19:51:21 +00:00
if ( target - > GetCon ( ) < = 50 )
2012-07-22 19:15:49 +00:00
{
2012-10-14 19:51:21 +00:00
target - > SetRDir ( effect . direction * 10 ) ;
2012-07-22 19:15:49 +00:00
}
//else rotate slowly first until about 10 degree. This will be the time needed for the crack sound and makes sense as a tree will start falling slowly.
else
{
2012-10-14 19:51:21 +00:00
if ( Abs ( target - > GetR ( ) ) < 10 )
2012-07-22 19:15:49 +00:00
{
2012-10-14 19:51:21 +00:00
target - > SetRDir ( effect . direction * 1 ) ;
2012-07-22 19:15:49 +00:00
//Turn of gravity so the tree doesn't get stuck before its done falling.
2012-10-14 19:51:21 +00:00
target - > SetYDir ( 0 ) ;
2012-07-22 19:15:49 +00:00
}
else
{
//Then speed up and let gravity do the rest.
2012-10-14 19:51:21 +00:00
target - > SetRDir ( effect . direction * 10 ) ;
2012-07-22 19:15:49 +00:00
}
}
//if the tree does not lend on a cliff or sth. (is rotated more then 80 degrees in the plus or minus direction) Play the landing sound of the tree.
2012-10-14 19:51:21 +00:00
if ( Abs ( target - > GetR ( ) ) > 80 )
2012-07-22 19:15:49 +00:00
{
2012-10-14 19:51:21 +00:00
target - > SetRDir ( 0 ) ;
if ( target - > GetCon ( ) > 50 ) target - > Sound ( " TreeLanding " , false ) ;
2012-07-22 19:15:49 +00:00
return - 1 ;
}
//check every frame if the tree is stuck and stop rotation in that case this is necessary as a tree could get stuck before reaching 80 degrees
2012-10-14 19:51:21 +00:00
if ( ( target - > GetContact ( - 1 , CNAT_Left ) | target - > GetContact ( - 1 , CNAT_Right ) ) > 0 )
2012-07-22 19:15:49 +00:00
{
2012-10-14 19:51:21 +00:00
target - > SetRDir ( 0 ) ;
2012-07-22 19:15:49 +00:00
return - 1 ;
}
2011-10-14 00:04:33 +00:00
}