Merge branch 'master' into qteditor

Conflicts:
	planet/Objects.ocd/Items.ocd/Tools.ocd/Dynamite.ocd/Script.c
	planet/Objects.ocd/Libraries.ocd/Animal.ocd/CreatureControl.ocd/DefCore.txt
qteditor
Sven Eberhardt 2016-08-13 23:42:59 -04:00
commit bce903ee04
81 changed files with 1808 additions and 663 deletions

View File

@ -77,6 +77,9 @@ func DrawSmallIslandsMap(proplist map)
if (Abs(x-w/2) < w/10) szx += Random(3); // central islands sometimes wider
map->Draw("^Ice-ice2", nil, [x-szx,y,1+2*szx,szy]);
}
// Balloon spawn: do nothing further
if (SCENPAR_SpawnType == 1)
return true;
// Starting islands for player spawns
var spawn_island_count = Max(GetStartupPlayerCount(), 2);
g_player_spawn_positions = CreateArray(spawn_island_count);

View File

@ -34,6 +34,24 @@ Default=1
Description=$DescWeaponsExplosive$
Value=1
[ParameterDef]
Name=$SpawnType$
Description=$DescSpawn$
ID=SpawnType
Default=1
[Options]
[Option]
Name=$ClassicSpawn$
Description=$DescClassicSpawn$
Value=0
[Option]
Name=$BalloonSpawn$
Description=$DescBalloonSpawn$
Value=1
[ParameterDef]
Name=$Rounds$
Description=$DescRounds$

View File

@ -61,7 +61,8 @@ func InitializeRound()
// Checking for victory: Only active after a Clonk dies.
g_check_victory_effect = AddEffect("CheckVictory", nil, 1, 0);
g_player_spawn_index = 0;
ShuffleArray(g_player_spawn_positions);
if (GetType(g_player_spawn_positions) == C4V_Array)
ShuffleArray(g_player_spawn_positions);
// Materials: Chests
var i,pos;
@ -142,20 +143,34 @@ func InitPlayerRound(int plr)
var ls_wdt = LandscapeWidth(), ls_hgt = LandscapeHeight();
var crew = GetCrew(plr), start_pos;
// Position by map type?
if (g_player_spawn_positions && g_player_spawn_index < GetLength(g_player_spawn_positions))
if (SCENPAR_SpawnType == 0)
{
start_pos = g_player_spawn_positions[g_player_spawn_index++];
var map_zoom = ls_wdt / g_map_width;
start_pos = {x=start_pos[0]*map_zoom+map_zoom/2, y=start_pos[1]*map_zoom};
if (g_player_spawn_positions && g_player_spawn_index < GetLength(g_player_spawn_positions))
{
start_pos = g_player_spawn_positions[g_player_spawn_index++];
var map_zoom = ls_wdt / g_map_width;
start_pos = {x=start_pos[0]*map_zoom+map_zoom/2, y=start_pos[1]*map_zoom};
}
else
{
// Start positions not defined or exhausted: Spawn in lower area for both maps becuase starting high is an an advantage.
start_pos = FindLocation(Loc_InRect(ls_wdt/5,ls_hgt/2,ls_wdt*3/5,ls_hgt/3), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = FindLocation(Loc_InRect(ls_wdt/10,0,ls_wdt*8/10,ls_hgt*4/5), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = {x=Random(ls_wdt*6/10)+ls_wdt*2/10, y=ls_hgt*58/100};
}
crew->SetPosition(start_pos.x, start_pos.y-10);
}
else
else // Balloon spawn
{
// Start positions not defined or exhausted: Spawn in lower area for both maps becuase starting high is an an advantage.
start_pos = FindLocation(Loc_InRect(ls_wdt/5,ls_hgt/2,ls_wdt*3/5,ls_hgt/3), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = FindLocation(Loc_InRect(ls_wdt/10,0,ls_wdt*8/10,ls_hgt*4/5), Loc_Wall(CNAT_Bottom), Loc_Func(Scenario.IsStartSpot));
if (!start_pos) start_pos = {x=Random(ls_wdt*6/10)+ls_wdt*2/10, y=ls_hgt*58/100};
var spawn_x = ls_wdt/3, spawn_y = 10;
spawn_x += Random(spawn_x);
var balloon = CreateObject(BalloonDeployed, spawn_x, spawn_y - 16, plr);
crew->SetPosition(spawn_x, spawn_y);
balloon->SetRider(crew);
crew->SetAction("Ride", balloon);
balloon->SetSpeed(0,0);
crew->SetSpeed(0,0);
}
crew->SetPosition(start_pos.x, start_pos.y-10);
// initial material
if (SCENPAR_Weapons == 0)
{
@ -190,9 +205,19 @@ func InitPlayerRound(int plr)
// Disable the Clonk during the countdown.
crew->SetCrewEnabled(false);
crew->SetComDir(COMD_Stop);
if (SCENPAR_SpawnType == 1 && balloon)
balloon->CreateEffect(IntNoGravity, 1, 1);
return true;
}
local IntNoGravity = new Effect {
Timer = func() {
Target->SetSpeed(0,0);
}
};
// Called by the round start countdown.
func OnCountdownFinished()
{
@ -201,6 +226,8 @@ func OnCountdownFinished()
{
clonk->SetCrewEnabled(true);
SetCursor(clonk->GetOwner(), clonk);
if (SCENPAR_SpawnType == 1 && clonk->GetActionTarget())
RemoveEffect("IntNoGravity", clonk->GetActionTarget());
}
}

View File

@ -10,6 +10,12 @@ WeaponsClassic=Klassisch
DescWeaponsClassic=Bögen, Speere, Keulen und einige Feuersteine
WeaponsExplosive=Explosiv
DescWeaponsExplosive=Nur Granatwerfer mit Endlosmunition
SpawnType=Startpunkte
DescSpawn=Legt fest, wo die Clonks der Spieler starten.
ClassicSpawn=Klassisch
DescClassicSpawn=Die Clonks starten auf den Eisinseln.
BalloonSpawn=Ballons
DescBalloonSpawn=Die Clonks fallen mit Ballons vom Himmel.
Rounds=Rundenzahl
DescRounds=Mehrere Runden spielen
Stalemate=Unentschieden!

View File

@ -5,13 +5,19 @@ DescMapTypeBigIsland=One central main island with small spots of ice in the air
MapTypeSpots=Small islands
DescMapTypeSpots=Many small spots of ice in the air.
Weapons=Weapons
DescWeapons=Defines which weapons are available for players
DescWeapons=Defines which weapons are available for players.
WeaponsClassic=Classic
DescWeaponsClassic=Bows, spears and clubs available in chests
DescWeaponsClassic=Bows, spears and clubs available in chests.
WeaponsExplosive=Explosive
DescWeaponsExplosive=Only grenade lauchers and wind bags available
DescWeaponsExplosive=Only grenade lauchers and wind bags available.
SpawnType=Spawn points
DescSpawn=Defines where the starting positions will be.
ClassicSpawn=Classic
DescClassicSpawn=All clonks start on the ice islands.
BalloonSpawn=Balloons
DescBalloonSpawn=The clonks will drop with balloons from the sky.
Rounds=Number of rounds
DescRounds=Play for multiple rounds
DescRounds=Play for multiple rounds.
Stalemate=Stalemate!
WinningTeam=Winning team: %s
RemainingRounds=%d rounds remaining.

View File

@ -17,7 +17,6 @@ public func Place(int amount, proplist area)
var location = nil;
if (area) location = Loc_InArea(area->GetBoundingRectangle());
var owner = GetCreaturePlayer();
while(amount > 0)
{
var p = nil;
@ -33,9 +32,7 @@ public func Place(int amount, proplist area)
// small circle
for(var r = 0; (r < 360) && (amount > 0); r += 40+Random(40))
{
var o = CreateObject(Chippie_Egg, p.x + Sin(r, 10 + RandomX(-2, 2)), p.y - Cos(r, 10 + RandomX(-2, 2)), owner);
if(owner == nil)
o->SetCreatureControlled();
var o = CreateObject(Chippie_Egg, p.x + Sin(r, 10 + RandomX(-2, 2)), p.y - Cos(r, 10 + RandomX(-2, 2)), NO_OWNER);
o->SetCon(RandomX(90, 100));
--amount;
}

View File

@ -15,8 +15,6 @@ public func Construction()
{
AddEffect("Activity", this, 1, 10, this);
SetAction("Walk");
if (GetOwner() == NO_OWNER)
SetCreatureControlled();
energy_sucked = 0;
return true;
}
@ -106,7 +104,7 @@ private func StartJump()
private func FxJumpCheckTimer(target, effect, time)
{
var e = FindObject(Find_AtPoint(), Find_OCF(OCF_Alive), Find_Hostile(GetOwner()));
var e = FindObject(Find_AtPoint(), Find_OCF(OCF_Alive), Find_AnimalHostile(GetOwner()));
if(e)
{
ClawTo(e);
@ -191,7 +189,7 @@ private func FxActivityTimer(target, effect, time)
if(!GetEffect("DmgShock", this) && !GBackSemiSolid())
{
for(var enemy in FindObjects(Find_Distance(100), Find_OCF(OCF_Alive), Find_Hostile(GetOwner()), Sort_Distance()))
for(var enemy in FindObjects(Find_Distance(100), Find_OCF(OCF_Alive), Find_AnimalHostile(GetOwner()), Sort_Distance()))
{
if(!PathFree(GetX(), GetY(), enemy->GetX(), enemy->GetY())) continue;

View File

@ -62,8 +62,6 @@ func Construction()
CheckTurn(GetDir());
SetTailOnFire();
SetCreatureControlled();
}
func Death()
@ -508,7 +506,7 @@ func UpdateEnemy()
var x = GetX();
var y = GetY();
for (var obj in FindObjects(Find_Distance(200), Find_OCF(OCF_Alive), Find_Hostile(GetOwner()), Sort_Distance()))
for (var obj in FindObjects(Find_Distance(200), Find_OCF(OCF_Alive), Find_AnimalHostile(GetOwner()), Sort_Distance()))
{
if (!PathFree(x, y, obj->GetX(), obj->GetY())) continue;
if (obj->GBackLiquid()) continue;

View File

@ -71,8 +71,6 @@ public func Construction()
if (Random(2)) SetDir(DIR_Left);
else SetDir(DIR_Right);
SetCreatureControlled();
// Two spot layouts are available.
if (!Random(2))
SetMeshMaterial("Puka2");
@ -348,7 +346,7 @@ private func UpdateEnemy()
var x = GetX();
var y = GetY();
for (var obj in FindObjects(Find_Distance(100), Find_OCF(OCF_Alive), Find_Hostile(GetOwner()), Sort_Distance()))
for (var obj in FindObjects(Find_Distance(100), Find_OCF(OCF_Alive), Find_AnimalHostile(GetOwner()), Sort_Distance()))
{
if (!PathFree(x, y, obj->GetX(), obj->GetY())) continue;
enemy = obj;
@ -377,7 +375,7 @@ private func DoElectroCircle()
};
// Punish all close enemies (not allied animals, though).
for (var obj in FindObjects(Find_Distance(30), Find_OCF(OCF_Alive), Find_Hostile(GetOwner()), Find_Exclude(this)))
for (var obj in FindObjects(Find_Distance(30), Find_OCF(OCF_Alive), Find_AnimalHostile(GetOwner()), Find_Exclude(this)))
{
var delta_x = 3 * (obj->GetX() - GetX());
var delta_y = 3 * (obj->GetY() - GetY());
@ -645,7 +643,7 @@ private func EndShockWater()
if (GetDir() == DIR_Right) x *= -1;
var y = 15;
for (var obj in FindObjects(Find_Distance(120), Find_OCF(OCF_Alive), Find_Hostile(this->GetOwner())))
for (var obj in FindObjects(Find_Distance(120), Find_OCF(OCF_Alive), Find_AnimalHostile(this->GetOwner())))
{
if (!obj->GBackLiquid()) continue;
var angle = Angle(GetX(), GetY(), obj->GetX(), obj->GetY());

View File

@ -594,6 +594,16 @@ func CheckScaleTop()
return true;
}
func CheckScaleTopHelper()
{
// Check if the clonk has passed the material with its leg vertices
// and if COMD_Up is used to climb in which case corner scale would fail
if (GBackSolid(-3+6*GetDir(), 6)) return false;
if (GetComDir() != COMD_Up) return false;
return true;
}
func FxIntScaleStart(target, effect, tmp)
{
if(tmp) return;
@ -624,6 +634,22 @@ func FxIntScaleTimer(target, number, time)
// The animation's graphics has to be shifet a bit to adjust to the clonk movement
var pos = GetAnimationPosition(number.animation_id);
SetScaleRotation(0, 0, 0, 0, 0, 1);
// Check if corner scale help is needed
if (CheckScaleTopHelper())
{
if (GetDir() == DIR_Left)
SetComDir(COMD_UpLeft);
else
SetComDir(COMD_UpRight);
number.corner_scale_helper = true;
}
else if (number.corner_scale_helper)
number.corner_scale_helper = false;
}
else if (number.corner_scale_helper)
{
// This will delay everything for 1 frame just for cleanup, hopefully it's not too bad
number.corner_scale_helper = false;
}
else if(!GBackSolid(-10+20*GetDir(), 8))
{
@ -708,9 +734,13 @@ func FxIntScaleStop(target, number, reason, tmp)
/* if(number.animation_mode == 1) PlayAnimation(Clonk_WalkStand, CLONK_ANIM_SLOT_Movement, GetWalkAnimationPosition(Clonk_WalkStand), Anim_Const(1000));
// Finally stop if the user has scheduled a stop
if(number.ScheduleStop) SetComDir(COMD_Stop);*/
// and reset the transform
// Reset the transform
SetScaleRotation(0);
// SetObjDrawTransform(1000, 0, 0, 0, 1000, 0);
// Remove the corner scale helper com dir
if (number.corner_scale_helper)
if (GetComDir() == COMD_UpLeft || GetComDir() == COMD_UpRight)
Schedule(this, "SetComDir(COMD_Up)", 2);
}
/*--

View File

@ -108,6 +108,13 @@ protected func CatchBlow()
if (GetAction() == "Dead") return;
if (!Random(5)) PlaySoundHurt();
}
protected func OnEnergyChange(int change, int cause, int caused_by)
{
if (change < 0 && GetCursor(GetOwner()) == this)
PlayRumble(GetOwner(), Min(300 + 1000 * -change / this.MaxEnergy, 1000), 150);
return _inherited(...);
}
protected func Grab(object pTarget, bool fGrab)
{

View File

@ -1,2 +1,2 @@
Name=Boiling Laval
Name=Boiling Lava
Description=Causes Lava on the map to boil

View File

@ -156,7 +156,7 @@ func FxIntCheckObjectsTimer(target, effect fx)
container_restriction = Find_Or(Find_Container(container), Find_InArray([container]));
}
var new_objects = FindObjects(Find_AtRect(target->GetX() - 5, target->GetY() - 10, 10, 20), container_restriction, Find_Layer(target->GetObjectLayer()),
var new_objects = FindObjects(Find_AtRect(target->GetX() - 5, target->GetY() - 10, 10, 21), container_restriction, Find_Layer(target->GetObjectLayer()),
// Find all containers and objects with a custom menu.
Find_Or(Find_Func("IsContainer"), Find_Func("HasInteractionMenu")),
// Do not show objects with an extra slot though - even if they are containers. They count as items here and can be accessed via the surroundings tab.
@ -997,7 +997,7 @@ func FxIntRefreshContentsMenuTimer(target, effect, time)
{
var j = 0, e = nil;
var found_tracker = false;
while (e = GetEffect(nil, obj, j++))
while (e = GetEffect("ExtraSlotTracker", obj, j++))
{
if (e.keep_alive != extra_slot_keep_alive) continue;
found_tracker = true;
@ -1005,9 +1005,10 @@ func FxIntRefreshContentsMenuTimer(target, effect, time)
}
if (!found_tracker)
{
var e = AddEffect("ExtraSlotTracker", obj, 1, 30 + Random(60), this);
var e = AddEffect("ExtraSlotTracker", obj, 1, 30 + Random(60), nil, GetID());
e.keep_alive = extra_slot_keep_alive;
e.callback_effect = effect;
e.obj = effect.obj;
}
}
// How many objects are this object?!
@ -1170,8 +1171,8 @@ func FxIntRefreshContentsMenuTimer(target, effect, time)
func FxExtraSlotTrackerTimer(object target, proplist effect, int time)
{
if (!effect.keep_alive) return -1;
return 1;
if (!effect.keep_alive)
return -1;
}
// This is called by the extra-slot library.

View File

@ -1,14 +1,95 @@
/**
Dynamite
A volatile tool that can be pressed into wallsfor accurate
mining, burning a short fuse before exploding.
@author Newton
A volatile tool that can be pressed into wallsfor accurate mining, burning a short fuse before exploding.
@author: Newton
*/
/*-- Engine Callbacks --*/
// time in frames until explosion
local FuseTime = 140;
func Hit()
{
Sound("Hits::GeneralHit?");
}
func Incineration(int caused_by)
{
Extinguish();
Fuse();
SetController(caused_by);
}
func RejectEntrance()
{
return GetAction() == "Ready";
}
/*-- Callbacks --*/
public func OnCannonShot(object cannon)
{
Fuse();
}
// Drop fusing dynamite on death to prevent explosion directly after respawn
public func IsDroppedOnDeath(object clonk)
{
return (GetAction() == "Fuse");
}
public func IsFusing()
{
return GetAction() == "Fuse";
}
// Called by the Dynamite box
public func SetReady()
{
SetAction("Ready");
}
// Called by the Dynamite box
public func SetFuse()
{
SetAction("Fuse");
// Object can't be collected anymore when it fuses.
this.Collectible = false;
}
public func Reset()
{
SetAction("Idle");
// Object can be collected again.
this.Collectible = true;
}
public func OnFuseFinished(object fuse)
{
SetController(fuse->GetController());
DoExplode();
}
// This will only when inside a dynamite box to display the remaining dynamite sticks in the HUD
public func GetStackCount()
{
if (Contained())
if (Contained()->GetID() == DynamiteBox)
{
return Contained()->ContentsCount(GetID());
}
}
public func IsInfiniteStackCount()
{
return false;
}
public func IsGrenadeLauncherAmmo() { return true; }
/*-- Usage --*/
public func ControlUse(object clonk, int x, int y, bool box)
{
// if already activated, nothing (so, throw)
@ -52,7 +133,19 @@ public func ControlUse(object clonk, int x, int y, bool box)
return false;
}
private func Place(object clonk, int x, int y, bool box)
public func Fuse()
{
if (GetAction() != "Fuse")
{
if (!FindObject(Find_Category(C4D_StaticBack), Find_Func("IsFuse"), Find_ActionTargets(this)))
Sound("Fire::Fuse");
SetAction("Fuse");
// Object can't be collected anymore when it fuses.
this.Collectible = false;
}
}
func Place(object clonk, int x, int y, bool box)
{
var angle = Angle(0,0,x,y);
var pos = GetWall(angle);
@ -69,26 +162,9 @@ private func Place(object clonk, int x, int y, bool box)
return false;
}
public func Fuse()
{
if (GetAction() != "Fuse")
{
if (!FindObject(Find_Category(C4D_StaticBack), Find_Func("IsFuse"), Find_ActionTargets(this)))
Sound("Fire::Fuse");
SetAction("Fuse");
// Object can't be collected anymore when it fuses.
this.Collectible = false;
}
}
public func OnCannonShot(object cannon)
{
Fuse();
}
// returns true if there is a wall in direction in which "clonk" looks
// and puts the offset to the wall into "xo, yo" - looking from the clonk
private func GetWall(int angle)
func GetWall(int angle)
{
var dist = 12;
for (var dist = 12; dist < 18; dist++)
@ -101,41 +177,7 @@ private func GetWall(int angle)
return false;
}
protected func Hit() { Sound("Hits::GeneralHit?"); }
protected func Incineration(int caused_by)
{
Extinguish();
Fuse();
SetController(caused_by);
}
protected func RejectEntrance()
{
return GetAction() == "Ready";
}
// Controle of the Dynamite box
public func SetReady()
{
SetAction("Ready");
}
// Controle of the Dynamite box
public func SetFuse()
{
SetAction("Fuse");
// Object can't be collected anymore when it fuses.
this.Collectible = false;
}
public func Reset()
{
SetAction("Idle");
// Object can be collected again.
this.Collectible = true;
}
private func Fusing()
func Fusing()
{
var x = Sin(GetR(), 5);
var y = -Cos(GetR(), 5);
@ -155,11 +197,6 @@ private func Fusing()
return;
}
public func OnFuseFinished(object fuse)
{
SetController(fuse->GetController());
DoExplode();
}
public func DoExplode()
{
@ -169,17 +206,9 @@ public func DoExplode()
Explode(26);
}
/*-- Production --*/
public func IsChemicalProduct() { return true; }
public func IsGrenadeLauncherAmmo() { return true; }
public func IsFusing() { return GetAction() == "Fuse"; }
// Drop fusing dynamite on death to prevent explosion directly after respawn
public func IsDroppedOnDeath(object clonk)
{
return (GetAction() == "Fuse");
}
/*-- Properties --*/
@ -207,8 +236,7 @@ local ActMap = {
};
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 1;
local Collectible = true;
local BlastIncinerate = 1;
local ContactIncinerate = 1;
local Components = {Coal = 1, Firestone = 1};
local Components = {Coal = 1, Firestone = 1};

View File

@ -38,9 +38,11 @@ public func ControlUse(object clonk, int x, int y)
{
if (clonk->GetAction() != "Walk")
return true;
ignited = 1;
if (ignited)
return true;
ignited = true;
// The clonk has to stand.
clonk->SetAction("Stand");
clonk->SetXDir(0);
@ -91,7 +93,13 @@ public func GetCarryMode()
public func GetCarryPhase() { return 250; }
public func GetCarrySpecial(clonk)
public func GetCarryTransform()
{
if (ignited)
return Trans_Mul(Trans_Rotate(0, 1), Trans_Translate(-1000));
}
public func GetCarrySpecial()
{
if (ignited)
return "pos_hand2";

View File

@ -1,10 +1,12 @@
/**
Dynamite box
Contains five dynamite sticks which can be placed and detonated from a distance.
Contains five dynamite sticks which can be placed and detonated from a distance.
@author: Newton
*/
#include Library_HasExtraSlot
static const DYNA_MaxLength = 500;
static const DYNA_MaxCount = 5;
@ -17,6 +19,8 @@ local wire;
func Initialize()
{
CreateContents(Dynamite, DYNA_MaxCount);
count = DYNA_MaxCount;
dynamite_sticks = [];
wires = [];
@ -29,7 +33,6 @@ func Initialize()
// Hide it TODO: Remove if the mesh isn't shown if there is a picture set
this.PictureTransformation = Trans_Scale();
UpdatePicture();
return;
}
func Hit()
@ -37,24 +40,84 @@ func Hit()
Sound("Hits::Materials::Wood::DullWoodHit?");
}
func Incineration(int caused_by)
func Incineration(int caused_by)
{
ActivateFuse();
if (!GetEffect("Fuse", this)) AddEffect("Fuse", this, 100, 1, this);
Sound("Fire::Fuse");
SetController(caused_by);
return;
}
func Damage(int change, int type, int by_player)
{
Incinerate(nil, by_player);
return;
}
public func OnCannonShot(object cannon)
func RejectCollect(id def, object obj)
{
Incinerate(nil, cannon->GetController());
if (obj->GetID() != Dynamite)
return true;
// One dynamite box can only support 5 sticks of dynamite, regardless if these are in the box
// or already taken out (connected with wires)
var sticks = ContentsCount(Dynamite);
for (var i = 0; i < GetLength(wires); i++)
if (wires[i])
sticks++;
if (sticks >= DYNA_MaxCount)
return true;
return false;
}
func Ejection()
{
count--;
if (count == 0)
{
ChangeToIgniter();
if (Contained())
{
var pos = Contained()->~GetItemPos(this);
Contained()->~UpdateAttach();
Contained()->~OnSlotFull(pos);
}
}
else
{
UpdatePicture();
}
// Make sure the inventory gets notified of the changes.
if (Contained())
Contained()->~OnInventoryChange();
}
func ContentsDestruction()
{
Ejection();
}
func Collection2()
{
if (count == 0 && GetID() == Igniter)
{
ChangeToBox();
if (Contained())
{
var pos = Contained()->~GetItemPos(this);
Contained()->~UpdateAttach();
Contained()->~OnSlotFull(pos);
}
}
count++;
UpdatePicture();
if (Contained())
Contained()->~OnInventoryChange();
}
/*-- Callbacks --*/
@ -78,6 +141,11 @@ public func OnFuseFinished(object fuse)
DoExplode();
}
public func OnCannonShot(object cannon)
{
Incinerate(nil, cannon->GetController());
}
/*-- Usage --*/
public func SetDynamiteCount(int new_count)
@ -87,49 +155,34 @@ public func SetDynamiteCount(int new_count)
// Update inventory if contained in a crew member.
if (Contained())
Contained()->~OnInventoryChange();
return;
}
public func HoldingEnabled() { return true; }
public func ControlUse(object clonk, int x, int y)
{
var dynamite = dynamite_sticks[count - 1] = CreateContents(Dynamite);
var dynamite = Contents();
if (!dynamite || dynamite->GetID() != Dynamite)
return false;
if (!dynamite->ControlUse(clonk, x, y, 1))
{
dynamite->RemoveObject();
return true;
}
if(wire)
wire->Connect(dynamite_sticks[count], dynamite);
wire = CreateObject(Fuse);
wire->Connect(dynamite, this);
Sound("Objects::Connect");
wires[count - 1] = wire;
count--;
if (count == 0)
{
var pos = clonk->GetItemPos(this);
ChangeToIgniter();
clonk->UpdateAttach();
clonk->OnSlotFull(pos);
}
else
{
UpdatePicture();
}
wires[count] = wire;
// Make sure the inventory gets notified of the changes.
clonk->~OnInventoryChange();
return true;
}
// Empty this box and turn it into an igniter
public func ChangeToIgniter()
{
if (GetID() == Igniter) return;
count = 0;
UpdatePicture();
ChangeDef(Igniter);
@ -137,6 +190,16 @@ public func ChangeToIgniter()
return true;
}
// Change back into a box
public func ChangeToBox()
{
if (GetID() == DynamiteBox) return;
ChangeDef(DynamiteBox);
UpdatePicture();
return true;
}
public func ActivateFuse()
{
// Activate all fuses.
@ -199,7 +262,7 @@ func UpdatePicture()
}
// Display the remaining dynamite sticks in menus.
public func GetInventoryIconOverlay()
/*public func GetInventoryIconOverlay()
{
// Full boxes don't need an overlay. Same for igniters.
if (count == DYNA_MaxCount || count <= 0) return nil;
@ -231,17 +294,19 @@ public func GetInventoryIconOverlay()
}
return overlay;
}
}*/
func Definition(def) {
func Definition(def)
{
SetProperty("PictureTransformation", Trans_Mul(Trans_Rotate(150, 1, 0, 0), Trans_Rotate(140, 0, 1, 0)), def);
}
/*-- Properties --*/
local Collectible = 1;
local Name = "$Name$";
local Description = "$Description$";
local Collectible = true;
local BlastIncinerate = 1;
local ContactIncinerate = 2;
local Components = {Wood = 1, Coal = 2, Firestone = 2};
local MaxContentsCount = 5;

View File

@ -169,8 +169,10 @@ public func OnRopeBreak()
/*-- Grapple rope controls --*/
public func FxIntGrappleControlControl(object target, proplist effect, int ctrl, int x, int y, int strength, repeat, release)
public func FxIntGrappleControlControl(object target, proplist effect, int ctrl, int x, int y, int strength, bool repeat, int status)
{
if (status == CONS_Moved) return false;
var release = status == CONS_Up;
// Cancel this effect if clonk is now attached to something.
if (target->GetProcedure() == "ATTACH")
{

View File

@ -101,11 +101,14 @@ public func CanBeStackedWith(object other)
return inherited(other) && (PipeState == other.PipeState);
}
/**
The pump calls this function to prevent clogging of the intake.
Cycles through several aperture offset indices.
*/
func CycleApertureOffset()
public func HasAperture() { return true; }
public func CycleApertureOffset()
{
// Cycle in three steps of three px each through X and Y
// covering a 3x3 grid on points -3,0,+3

View File

@ -33,6 +33,8 @@ public func IsDigging() { return is_digging; }
public func HoldingEnabled() { return true; }
public func DefaultCrosshairAngle(object clonk, int d) { return 900 * d; }
public func ControlUseStart(object clonk, int x, int y)
{
AddEffect("ShovelDig", clonk, 1, 1, this);

View File

@ -22,7 +22,15 @@ func Hit()
/*-- Usage --*/
public func ControlUse(object clonk, x, y)
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))
{
@ -251,4 +259,4 @@ local Name = "$Name$";
local Description = "$Description$";
local Collectible = true;
local MaxIntake = 30;
local Components = {Cloth = 1, Metal = 1};
local Components = {Cloth = 1, Metal = 1};

View File

@ -0,0 +1,14 @@
[DefCore]
id=Helmet
Version=8,0
Category=C4D_Object
Width=10
Height=10
Offset=-5,-5
Vertices=2
VertexX=0,0
VertexY=-4,5
VertexFriction=50,50
Value=10
Mass=10
Rotate=1

View File

@ -0,0 +1,23 @@
material Helmet_Greek
{
receive_shadows on
technique
{
pass
{
cull_hardware none
ambient 0.8 0.8 0.8 1.0
diffuse 0.64 0.64 0.64 1.0
specular 0.16 0.16 0.16 1.0 12.5
emissive 0.0 0.0 0.0 1.0
texture_unit
{
texture helmet.jpg
tex_address_mode wrap
filtering trilinear
}
}
}
}

View File

@ -0,0 +1,102 @@
/**
Helmet
Protective head armor.
@author: pluto, Clonkonaut
*/
#include Library_Wearable
/*-- Engine Callbacks --*/
func Hit()
{
Sound("Hits::Materials::Metal::DullMetalHit?");
}
/*-- Usage --*/
public func ControlUse(object clonk)
{
if (IsWorn())
TakeOff();
else
PutOn(clonk);
return true;
}
// Helmet effect: 20% less damage
func OnDamage(int damage, int cause, int by_player)
{
// Do nothing on energy gained
if (damage > 0) return damage;
// Doesn't protect against all damage
if (cause == FX_Call_EngBlast || cause == FX_Call_EngFire || cause == FX_Call_EngAsphyxiation || cause == FX_Call_EngCorrosion)
return damage;
return damage - (damage*20/100);
}
/*-- Production --*/
public func IsWeapon() { return true; }
public func IsArmoryProduct() { return true; }
/*-- Display --*/
public func GetWearPlace()
{
return WEARABLE_Head;
}
public func GetWearBone()
{
return "Main";
}
public func GetWearTransform()
{
return Trans_Mul(Trans_Rotate(90, 0, 0, 1), Trans_Translate(0, -300));
}
public func GetCarryMode(object clonk, bool secondary)
{
if (IsWorn() || display_disabled)
return CARRY_None;
if (secondary || !clonk->~HasHandAction(false, true))
return CARRY_Back;
return CARRY_BothHands;
}
public func GetCarryPhase(object clonk)
{
return 550;
}
public func GetCarryBone(object clonk, bool secondary)
{
return "Main";
}
public func GetCarryTransform(object clonk, bool secondary, bool no_hand, bool on_back)
{
if (secondary)
return Trans_Mul(Trans_Rotate(180, 1), Trans_Rotate(0, 0, 1), Trans_Rotate(90, 0, 0, 1), Trans_Translate(-4000));
if (no_hand || on_back)
return Trans_Mul(Trans_Rotate(0, 1), Trans_Rotate(0, 0, 1), Trans_Rotate(90, 0, 0, 1), Trans_Translate(-4000));
return Trans_Mul(Trans_Rotate(80, 0, 0, 1), Trans_Rotate(-90, 0, 1), Trans_Rotate(-45, 0, 0, 1), Trans_Translate(-1000, 4000));
}
func Definition(def)
{
SetProperty("PictureTransformation", Trans_Mul(Trans_Rotate(45, 0, 1), Trans_Rotate(10, 0, 0, 1)),def);
}
/*-- Properties --*/
local Name = "$Name$";
local Description = "$Description$";
local Collectible = true;
local Components = {Wood = 1, Metal = 1};

View File

@ -0,0 +1,2 @@
Name=Helm
Description=Bietet dem Clonk einigen Schutz. Drücke [Benutzen] um den Helm auf- oder abzusetzen.

View File

@ -0,0 +1,2 @@
Name=Helmet
Description=Provides the clonk with some protection. Press [Use] to take the helmet on or off.

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -1,5 +0,0 @@
[DefCore]
id=Library_CreatureControl
Version=6,1
Category=C4D_StaticBack
HideInCreator=true

View File

@ -1,76 +0,0 @@
/**
AnimalControl
Cares about the ownership of non-player-controlled units.
They are hostile to every other player.
global func GetCreaturePlayer()
global func SetAnimalControlled()
*/
static CreatureControl_animal_player;
static CreatureControl_yet_to_set;
static CreatureControl_initializing;
private func Enqueue(obj)
{
if(GetType(CreatureControl_yet_to_set) != C4V_Array)
CreatureControl_yet_to_set = [];
PushBack(CreatureControl_yet_to_set, obj);
}
/**
Returns the hostile NPC player or creates it.
Returns nil when the NPC player is currently joining.
*/
global func GetCreaturePlayer()
{
if(CreatureControl_animal_player != nil)
return CreatureControl_animal_player;
if(CreatureControl_initializing == true)
return nil;
CreatureControl_initializing = true;
CreateScriptPlayer("Creatures", RGB(50, 100, 50), 0, CSPF_NoScenarioInit | CSPF_NoEliminationCheck | CSPF_Invisible, Library_CreatureControl);
return nil;
}
/**
Sets the owner of an object to the hostile NPC player.
*/
global func SetCreatureControlled()
{
if(!this) return false;
var o = GetCreaturePlayer();
if(o != nil) return SetOwner(o);
// No owner during creation. If the scripter overwrites it to a real owner, it's not changed later.
SetOwner(NO_OWNER);
Library_CreatureControl->Enqueue(this);
}
public func InitializeScriptPlayer(plr, team)
{
CreatureControl_animal_player = plr;
if(CreatureControl_yet_to_set != nil)
{
for(var obj in CreatureControl_yet_to_set)
{
if (!obj) continue;
// Overwritten by the scripter?
if (obj->GetOwner() != NO_OWNER) continue;
obj->SetOwner(CreatureControl_animal_player);
}
}
CreatureControl_yet_to_set = nil;
// hostile!
for(var i = 0; i < GetPlayerCount(); ++i)
{
var p = GetPlayerByIndex(i);
if(p == CreatureControl_animal_player) continue;
SetHostility(p, plr, true, true, true);
SetHostility(plr, p, true, true, true);
}
}

View File

@ -5,68 +5,85 @@
Virtual cursor for gamepad controls
*/
local crew, angle, dirx, diry, xpos,ypos, analogaim, aiming, menu;
local crew, angle, xpos, ypos, aiming, menu;
static const CURSOR_Radius = 100;
// This is supposed to be a constant, but C4Script doesn't allow constant expressions there.
private func CURSOR_Deadzone() { return PLRCON_MaxStrength / 5; }
protected func Initialize()
{
this["Visibility"] = VIS_None;
dirx = diry = xpos = ypos = 0;
SetVisibility(false);
xpos = ypos = 0;
aiming = false;
}
public func FxMoveTimer()
{
var speed = 0;
var dpad_rotatespeed = 35;
// dpad mode
if(diry)
if (!crew)
{
if (diry < 0) speed = -Sin(angle,100,10);
else if (diry > 0) speed = +Sin(angle,100,10);
angle += dpad_rotatespeed*speed/100;
UpdateAnalogpadPos();
RemoveObject();
return FX_Execute_Kill;
}
if(dirx)
{
if (dirx < 0) speed = -Cos(angle,100,10);
else if (dirx > 0) speed = +Cos(angle,100,10);
angle += dpad_rotatespeed*speed/100;
UpdateAnalogpadPos();
}
// analog pad mode
if(!dirx && !diry)
{
var target_angle = Angle(0,0,xpos,ypos)*10;
var analog_strength = BoundBy(Sqrt(xpos*xpos+ypos*ypos),0,100);
var target_angle = Angle(0,0,xpos,ypos)*10;
if (!Visible() && !InDeadzone())
{
// The player moved the aiming stick while the crosshair wasn't visible: Use angle directly.
angle = target_angle;
SetVisibility(true);
}
else if (!InDeadzone())
{
// Smooth small movements of the stick while the crosshair is visible.
var angle_diff = Normalize(target_angle - angle, -1800, 10);
if (angle_diff == 0) angle_diff = 1;
angle = angle + angle_diff * analog_strength / 100 / 8;
if (Abs(angle_diff) < 450)
angle = angle + angle_diff / 8;
else
angle = target_angle;
}
else if (!aiming)
{
// The player doesn't touch the stick and no item is using the crosshair right now.
SetVisibility(false);
// Aim somewhere useful. Note that this can be overwritten by objects and isn't used for throwing.
angle = 800*(crew->GetDir()*2-1);
}
UpdatePosition();
if(aiming) crew->TriggerHoldingControl();
crew->TriggerHoldingControl();
}
private func UpdateAnalogpadPos()
private func AnalogStrength() { return BoundBy(Sqrt(xpos*xpos+ypos*ypos), 0, PLRCON_MaxStrength); }
private func InDeadzone() { return AnalogStrength() < CURSOR_Deadzone(); }
private func Visible() { return this.Visibility != VIS_None; }
// Updates the visibility, returing true if it was changed.
private func SetVisibility(bool visible)
{
xpos = Sin(angle/10,100);
ypos = Cos(angle/10,-100);
var newvis, oldvis;
if (visible)
newvis = VIS_Owner;
else
newvis = VIS_None;
oldvis = this.Visibility;
this.Visibility = newvis;
return newvis != oldvis;
}
public func StartAim(object clonk, bool stealth, object GUImenu)
private func CreateMoveEffect(object clonk)
{
// only reinitialize angle if the crosshair hasn't been there before
if(!GetEffect("Move",this))
{
// which should basically be only the case on the first time aiming
angle = 800*(clonk->GetDir()*2-1);
}
crew = clonk;
UpdatePosition();
RemoveEffect("Move",this);
AddEffect("Move",this,1,1,this);
}
public func StartAim(object clonk, int default_angle, object GUImenu)
{
aiming = true;
// gui or landscape mode:
if (GUImenu)
{
@ -79,22 +96,12 @@ public func StartAim(object clonk, bool stealth, object GUImenu)
SetCategory(C4D_StaticBack | C4D_IgnoreFoW);
menu = nil;
}
// set starting position for analog pad
UpdateAnalogpadPos();
crew = clonk;
UpdatePosition();
RemoveEffect("Move",this);
AddEffect("Move",this,1,1,this);
if(!stealth)
{
this["Visibility"] = VIS_Owner;
crew->SetComDir(COMD_Stop);
aiming = true;
EnableKeyAimControls(true);
}
// Use the given angle if the player wasn't aiming before.
if (SetVisibility(true) && default_angle)
angle = default_angle;
CreateMoveEffect(clonk);
}
private func UpdatePosition()
@ -112,65 +119,41 @@ private func UpdatePosition()
private func MirrorCursor()
{
return;
angle = -Normalize(angle,-1800,10);
UpdateAnalogpadPos();
}
public func StopAim()
{
RemoveEffect("Move",this);
this["Visibility"] = VIS_None;
dirx = 0;
diry = 0;
EnableKeyAimControls(false);
analogaim = false;
aiming = false;
}
private func EnableKeyAimControls(bool enable)
{
SetPlayerControlEnabled(GetOwner(), CON_AimUp, enable);
SetPlayerControlEnabled(GetOwner(), CON_AimDown, enable);
SetPlayerControlEnabled(GetOwner(), CON_AimLeft, enable);
SetPlayerControlEnabled(GetOwner(), CON_AimRight, enable);
}
// Aiming means that some object is currently actively using the crosshair.
public func IsAiming()
{
return aiming;
}
public func Aim(int ctrl, object clonk, int strength, int repeat, int release)
// The crosshair is also active when the player is holding the aiming stick.
public func IsActive()
{
return aiming || Visible();
}
public func Aim(int ctrl, object clonk, int strength, int repeat, int status)
{
// start (stealth) aiming
if(!GetEffect("Move",this))
StartAim(clonk,true);
CreateMoveEffect(clonk);
// aiming with analog pad
if (ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight)
if (status == CONS_Moved &&
(ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight))
{
dirx = diry = 0;
if(ctrl == CON_AimAxisUp) ypos = -strength;
if(ctrl == CON_AimAxisDown) ypos = strength;
if(ctrl == CON_AimAxisLeft) xpos = -strength;
if(ctrl == CON_AimAxisRight) xpos = strength;
analogaim = true;
return true;
}
// stop
else if (release && !analogaim)
{
if(ctrl == CON_AimUp || ctrl == CON_AimDown) diry = 0;
else if(ctrl == CON_AimLeft || ctrl == CON_AimRight) dirx = 0;
return true;
}
else if(!release /*&& !repeat */ && !analogaim)
{
if(ctrl == CON_AimUp) diry = -1;
else if(ctrl == CON_AimDown) diry = 1;
else if(ctrl == CON_AimLeft) dirx = -1;
else if(ctrl == CON_AimRight) dirx = 1;
return true;
}
return false;

View File

@ -46,6 +46,9 @@ static const ACTIONTYPE_EXTRA = 4;
// elevators within this range (x) can be called
static const ELEVATOR_CALL_DISTANCE = 30;
// default throwing angle used while the Clonk isn't aiming
static const DEFAULT_THROWING_ANGLE = 500;
/* ++++++++++++++++++++++++ Clonk Inventory Control ++++++++++++++++++++++++ */
/*
@ -178,13 +181,13 @@ public func GetExtraInteractions()
/* +++++++++++++++++++++++++++ Clonk Control +++++++++++++++++++++++++++ */
/* Main control function */
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, int status)
{
if (!this)
return false;
// Contents menu
if (ctrl == CON_Contents && !release)
if (ctrl == CON_Contents && status == CONS_Down)
{
// Close any menu if open.
if (GetMenu())
@ -221,10 +224,11 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
else ctrl = CON_Use;
repeat = true;
release = false;
status = CONS_Down;
}
// controls except a few reset a previously given command
else SetCommand("None");
else if (status != CONS_Moved)
SetCommand("None");
/* aiming with analog pad or keys:
This works completely different. There are CON_AimAxis* and CON_Aim*,
@ -238,23 +242,29 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
CON_Left is still called afterwards. So if the clonk finally starts to
aim, the virtual cursor already aims into the direction in which he ran
*/
if (ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight
|| ctrl == CON_AimUp || ctrl == CON_AimDown || ctrl == CON_AimLeft || ctrl == CON_AimRight)
if (ctrl == CON_AimAxisUp || ctrl == CON_AimAxisDown || ctrl == CON_AimAxisLeft || ctrl == CON_AimAxisRight)
{
var success = VirtualCursor()->Aim(ctrl,this,strength,repeat,release);
var success = VirtualCursor()->Aim(ctrl,this,strength,repeat,status);
// in any case, CON_Aim* is called but it is only successful if the virtual cursor is aiming
return success && VirtualCursor()->IsAiming();
}
// Simulate a mouse cursor for gamepads.
if (HasVirtualCursor())
{
x = this.control.mlastx;
y = this.control.mlasty;
}
// save last mouse position:
// if the using has to be canceled, no information about the current x,y
// is available. Thus, the last x,y position needs to be saved
if (ctrl == CON_Use || ctrl == CON_UseAlt)
else if (ctrl == CON_Use || ctrl == CON_UseAlt)
{
this.control.mlastx = x;
this.control.mlasty = y;
}
var proc = GetProcedure();
// building, vehicle, mount, contents, menu control
@ -269,23 +279,23 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// menu
if (this.control.menu)
{
return Control2Menu(ctrl, x,y,strength, repeat, release);
return Control2Menu(ctrl, x,y,strength, repeat, status);
}
var contents = this->GetHandItem(0);
// usage
var use = (ctrl == CON_Use || ctrl == CON_UseDelayed || ctrl == CON_UseAlt || ctrl == CON_UseAltDelayed);
var use = (ctrl == CON_Use || ctrl == CON_UseAlt);
if (use)
{
if (house)
{
return ControlUse2Script(ctrl, x, y, strength, repeat, release, house);
return ControlUse2Script(ctrl, x, y, strength, repeat, status, house);
}
// control to grabbed vehicle
else if (vehicle && proc == "PUSH")
{
return ControlUse2Script(ctrl, x, y, strength, repeat, release, vehicle);
return ControlUse2Script(ctrl, x, y, strength, repeat, status, vehicle);
}
else if (vehicle && proc == "ATTACH")
{
@ -301,25 +311,25 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
usage via CancelUse().
*/
if (ControlUse2Script(ctrl, x, y, strength, repeat, release, vehicle))
if (ControlUse2Script(ctrl, x, y, strength, repeat, status, vehicle))
return true;
else
{
// handled if the horse is the used object
// ("using" is set to the object in StartUse(Delayed)Control - when the
// ("using" is set to the object in StartUseControl - when the
// object returns true on that callback. Exactly what we want)
if (this.control.current_object == vehicle) return true;
// has been cancelled (it is not the start of the usage but no object is used)
if (!this.control.current_object && (repeat || release)) return true;
if (!this.control.current_object && (repeat || status == CONS_Up)) return true;
}
}
// releasing the use-key always cancels shelved commands (in that case no this.control.current_object exists)
if(release) StopShelvedCommand();
if(status == CONS_Up) StopShelvedCommand();
// Release commands are always forwarded even if contents is 0, in case we
// need to cancel use of an object that left inventory
if (contents || (release && this.control.current_object))
if (contents || (status == CONS_Up && this.control.current_object))
{
if (ControlUse2Script(ctrl, x, y, strength, repeat, release, contents))
if (ControlUse2Script(ctrl, x, y, strength, repeat, status, contents))
return true;
}
}
@ -327,7 +337,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// A click on throw can also just abort usage without having any other effects.
// todo: figure out if wise.
var currently_in_use = this.control.current_object != nil;
if ((ctrl == CON_Throw || ctrl == CON_ThrowDelayed) && currently_in_use && !release)
if (ctrl == CON_Throw && currently_in_use && status == CONS_Down)
{
CancelUse();
return true;
@ -336,7 +346,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// Throwing and dropping
// only if not in house, not grabbing a vehicle and an item selected
// only act on press, not release
if ((ctrl == CON_Throw || ctrl == CON_ThrowDelayed) && !house && (!vehicle || proc == "ATTACH" || proc == "PUSH") && !release)
if (ctrl == CON_Throw && !house && (!vehicle || proc == "ATTACH" || proc == "PUSH") && status == CONS_Down)
{
if (contents)
{
@ -365,33 +375,19 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
if (only_drop || Distance(0, 0, x, y) < 10 || (Abs(x) < 10 && y > 10))
only_drop = true;
// throw
if (ctrl == CON_Throw)
CancelUse();
if (only_drop)
return ObjectCommand("Drop", contents);
else
{
CancelUse();
if (only_drop)
return ObjectCommand("Drop", contents);
else
return ObjectCommand("Throw", contents, x, y);
}
// throw delayed
if (ctrl == CON_ThrowDelayed)
{
CancelUse();
if (release)
if (HasVirtualCursor() && !VirtualCursor()->IsActive())
{
VirtualCursor()->StopAim();
if (only_drop)
return ObjectCommand("Drop", contents);
else
return ObjectCommand("Throw", contents, this.control.mlastx, this.control.mlasty);
}
else
{
VirtualCursor()->StartAim(this);
return true;
var angle = DEFAULT_THROWING_ANGLE * (GetDir()*2 - 1);
x = +Sin(angle, CURSOR_Radius, 10);
y = -Cos(angle, CURSOR_Radius, 10);
}
return ObjectCommand("Throw", contents, x, y);
}
}
}
@ -402,14 +398,14 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// forward to script...
if (house)
{
return ControlMovement2Script(ctrl, x, y, strength, repeat, release, house);
return ControlMovement2Script(ctrl, x, y, strength, repeat, status, house);
}
else if (vehicle)
{
if (ControlMovement2Script(ctrl, x, y, strength, repeat, release, vehicle)) return true;
if (ControlMovement2Script(ctrl, x, y, strength, repeat, status, vehicle)) return true;
}
return ObjectControlMovement(plr, ctrl, strength, release);
return ObjectControlMovement(plr, ctrl, strength, status);
}
// Do a roll on landing or when standing. This means that the CON_Down was not handled previously.
@ -433,7 +429,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// Fall through half-solid mask
if (ctrl == CON_FallThrough)
{
if(!release)
if(status == CONS_Down)
{
if (this->IsWalking())
{
@ -469,7 +465,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
// Unhandled control
return _inherited(plr, ctrl, x, y, strength, repeat, release, ...);
return _inherited(plr, ctrl, x, y, strength, repeat, status, ...);
}
// A wrapper to SetCommand to catch special behaviour for some actions.
@ -617,18 +613,12 @@ func CanReIssueCommand(proplist data)
if(data.ctrl == CON_Use)
return !data.obj->~RejectUse(this);
if(data.ctrl == CON_UseDelayed)
return !data.obj->~RejectUse(this);
}
func ReIssueCommand(proplist data)
{
if(data.ctrl == CON_Use)
return StartUseControl(data.ctrl, this.control.mlastx, this.control.mlasty, data.obj);
if(data.ctrl == CON_UseDelayed)
return StartUseDelayedControl(data.ctrl, data.obj);
}
func StartUseControl(int ctrl, int x, int y, object obj)
@ -651,6 +641,17 @@ func StartUseControl(int ctrl, int x, int y, object obj)
this.control.using_type = DetermineUsageType(obj);
this.control.alt = ctrl != CON_Use;
if (HasVirtualCursor())
{
var cursor = VirtualCursor(), angle;
if (!cursor->IsActive() && (angle = obj->~DefaultCrosshairAngle(this, GetDir()*2 - 1)))
{
x = +Sin(angle, CURSOR_Radius, 10);
y = -Cos(angle, CURSOR_Radius, 10);
}
cursor->StartAim(this, angle);
}
var hold_enabled = obj->Call("~HoldingEnabled");
if (hold_enabled)
@ -687,37 +688,6 @@ func StartUseControl(int ctrl, int x, int y, object obj)
return handled;
}
func StartUseDelayedControl(int ctrl, object obj)
{
this.control.started_use = false;
if(obj->~RejectUse(this))
{
// remember for later:
ShelveCommand(this, "CanReIssueCommand", this, "ReIssueCommand", {obj = obj, ctrl = ctrl});
// but still catch command
return true;
}
// Disable climb/hangle actions for the duration of this use
if (obj.ForceFreeHands && !GetEffect("IntControlFreeHands", this)) AddEffect("IntControlFreeHands", this, 130, 0, this);
this.control.current_object = obj;
this.control.using_type = DetermineUsageType(obj);
this.control.alt = ctrl != CON_UseDelayed;
VirtualCursor()->StartAim(this);
// call UseStart
var handled = obj->Call(GetUseCallString("Start"),this,this.control.mlastx,this.control.mlasty);
this.control.noholdingcallbacks = !handled;
if(handled)
this.control.started_use = true;
return handled;
}
func CancelUseControl(int x, int y)
{
// forget possibly stored commands
@ -782,11 +752,6 @@ func HoldingUseControl(int ctrl, int x, int y, object obj)
{
var mex = x;
var mey = y;
if (ctrl == CON_UseDelayed || ctrl == CON_UseAltDelayed)
{
mex = this.control.mlastx;
mey = this.control.mlasty;
}
//Message("%d,%d",this,mex,mey);
@ -829,29 +794,6 @@ func HoldingUseControl(int ctrl, int x, int y, object obj)
return handled;
}
func StopUseDelayedControl(object obj)
{
// ControlUseStop, ControlUseAltStop, ContainedUseAltStop, etc...
var handled = obj->Call(GetUseCallString("Stop"), this, this.control.mlastx, this.control.mlasty);
if (!handled)
handled = obj->Call(GetUseCallString(), this, this.control.mlastx, this.control.mlasty);
if (obj == this.control.current_object)
{
VirtualCursor()->StopAim();
// see StopUseControl
if(handled != -1)
{
this.control.current_object = nil;
this.control.using_type = nil;
this.control.alt = false;
}
this.control.noholdingcallbacks = false;
}
return handled;
}
// very infrequent timer to prevent dangling effects, this is not necessary for correct functioning
func FxItemRemovalCheckTimer(object target, proplist effect, int time)
{
@ -876,43 +818,31 @@ func FxItemRemovalCheckStop(object target, proplist effect, int reason, bool tem
// Control use redirected to script
func ControlUse2Script(int ctrl, int x, int y, int strength, bool repeat, bool release, object obj)
func ControlUse2Script(int ctrl, int x, int y, int strength, bool repeat, int status, object obj)
{
// standard use
if (ctrl == CON_Use || ctrl == CON_UseAlt)
{
if (!release && !repeat)
if (status == CONS_Down && !repeat)
{
return StartUseControl(ctrl,x, y, obj);
}
else if (release && (obj == this.control.current_object || obj == GetActionTarget()))
else if (status == CONS_Up && (obj == this.control.current_object || obj == GetActionTarget()))
{
return StopUseControl(x, y, obj);
}
}
// gamepad use
else if (ctrl == CON_UseDelayed || ctrl == CON_UseAltDelayed)
{
if (!release && !repeat)
{
return StartUseDelayedControl(ctrl, obj);
}
else if (release && (obj == this.control.current_object || obj == GetActionTarget()))
{
return StopUseDelayedControl(obj);
}
}
// more use (holding)
if (ctrl == CON_Use || ctrl == CON_UseAlt || ctrl == CON_UseDelayed || ctrl == CON_UseAltDelayed)
if (ctrl == CON_Use || ctrl == CON_UseAlt)
{
if (release)
if (status == CONS_Up)
{
// leftover use release
CancelUse();
return true;
}
else if (repeat && !this.control.noholdingcallbacks)
else if (status == CONS_Down && repeat && !this.control.noholdingcallbacks)
{
return HoldingUseControl(ctrl, x, y, obj);
}
@ -922,7 +852,7 @@ func ControlUse2Script(int ctrl, int x, int y, int strength, bool repeat, bool r
}
// Control use redirected to script
func ControlMovement2Script(int ctrl, int x, int y, int strength, bool repeat, bool release, object obj)
func ControlMovement2Script(int ctrl, int x, int y, int strength, bool repeat, int status, object obj)
{
// overloads of movement commandos
if (ctrl == CON_Left || ctrl == CON_Right || ctrl == CON_Down || ctrl == CON_Up || ctrl == CON_Jump)
@ -931,7 +861,7 @@ func ControlMovement2Script(int ctrl, int x, int y, int strength, bool repeat, b
if (Contained() == obj)
control_string = "Contained";
if (release)
if (status == CONS_Up)
{
// if any movement key has been released, ControlStop is called
if (obj->Call(Format("~%sStop", control_string), this, ctrl))
@ -1040,7 +970,7 @@ func SetMenu(new_menu, bool unclosable)
SetComDir(COMD_Stop);
if (PlayerHasVirtualCursor(GetOwner()))
VirtualCursor()->StartAim(this,false, new_menu);
VirtualCursor()->StartAim(this, 0, new_menu);
else
{
if (GetType(new_menu) == C4V_C4Object && new_menu->~CursorUpdatesEnabled())

View File

@ -19,12 +19,12 @@ local virtual_cursor;
/* This part of gamepad control handles only object-style menus.
Fullscreen menus are handled differently. */
func Control2Menu(int ctrl, int x, int y, int strength, bool repeat, bool release)
func Control2Menu(int ctrl, int x, int y, int strength, bool repeat, int status)
{
/* all this stuff is already done on a higher layer - in playercontrol.c
now this is just the same for gamepad control */
if (!PlayerHasVirtualCursor(GetOwner()))
if (!HasVirtualCursor())
return true;
if (!this->GetMenu()) return false;
@ -36,27 +36,27 @@ func Control2Menu(int ctrl, int x, int y, int strength, bool repeat, bool releas
// update angle for visual effect on the menu
if (repeat)
{
if (ctrl == CON_UseDelayed || ctrl == CON_UseAltDelayed)
if (ctrl == CON_Use || ctrl == CON_UseAlt)
this->GetMenu()->~UpdateCursor(mex,mey);
}
// click on menu
if (release)
if (status == CONS_Up)
{
// select
if (ctrl == CON_UseDelayed)
if (ctrl == CON_Use)
this->GetMenu()->~OnMouseClick(mex,mey);
}
return true;
}
public func ObjectControlMovement(int plr, int ctrl, int strength, bool release)
public func ObjectControlMovement(int plr, int ctrl, int strength, int status)
{
// from PlayerControl.c
var result = inherited(plr,ctrl,strength,release,...);
var result = inherited(plr,ctrl,strength,status,...);
// do the following only if strength >= CON_Gamepad_Deadzone
if(!release)
if(status == CONS_Down)
if(strength != nil && strength < CON_Gamepad_Deadzone)
return result;
@ -66,7 +66,7 @@ public func ObjectControlMovement(int plr, int ctrl, int strength, bool release)
if(!virtual_cursor) return result;
// change direction of virtual_cursor
if(!release)
if(status == CONS_Down)
virtual_cursor->Direction(ctrl);
return result;
@ -79,7 +79,7 @@ func ReinitializeControls()
// if is aiming or in menu and no virtual cursor is there, create one
if (!virtual_cursor)
if (this.menu || this.control.current_object) // properties declared in ClonkControl.ocd
VirtualCursor()->StartAim(this,false,this.menu);
VirtualCursor()->StartAim(this,0,this.menu);
}
else
{
@ -91,6 +91,9 @@ func ReinitializeControls()
/* Virtual cursor stuff */
// Helper function.
private func HasVirtualCursor() { return PlayerHasVirtualCursor(GetOwner()); }
// get virtual cursor, if noone is there, create it
private func VirtualCursor()
{
@ -122,19 +125,12 @@ public func UpdateVirtualCursorPos()
public func TriggerHoldingControl()
{
// using has been commented because it must be possible to use the virtual
// cursor aim also without a used object - for menus
// However, I think the check for 'this.control.current_object' here is just an unecessary safeguard
// since there is always a using-object if the clonk is aiming for a throw
// or a use. If the clonk uses it, there will be callbacks that cancel the
// callbacks to the virtual cursor
// - Newton
if (/*this.control.current_object && */!this.control.noholdingcallbacks)
if (this.control.current_object && !this.control.noholdingcallbacks)
{
var ctrl = CON_UseDelayed;
var ctrl = CON_Use;
if (this.control.alt)
ctrl = CON_UseAltDelayed;
ObjectControl(GetOwner(), ctrl, 0, 0, 0, true, false);
ctrl = CON_UseAlt;
ObjectControl(GetOwner(), ctrl, 0, 0, 0, true, CONS_Down);
}
}
@ -143,4 +139,4 @@ func RemoveVirtualCursor()
{
if (virtual_cursor)
virtual_cursor->StopAim();
}
}

View File

@ -26,13 +26,13 @@ public func OnShiftCursor(object new_cursor)
return _inherited(new_cursor, ...);
}
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, int status)
{
if (!this)
return inherited(plr, ctrl, x, y, strength, repeat, release, ...);
return inherited(plr, ctrl, x, y, strength, repeat, status, ...);
// Begin interaction.
if (ctrl == CON_Interact && !release)
if (ctrl == CON_Interact && status == CONS_Down)
{
this->CancelUse();
BeginInteract();
@ -50,7 +50,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
// Finish picking up (aka "collect").
if (ctrl == CON_Interact && release)
if (ctrl == CON_Interact && status == CONS_Up)
{
EndInteract();
return true;
@ -71,7 +71,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
}
return inherited(plr, ctrl, x, y, strength, repeat, release, ...);
return inherited(plr, ctrl, x, y, strength, repeat, status, ...);
}
private func FxIntHighlightInteractionStart(object target, proplist fx, temp, proplist interaction, proplist interaction_help)

View File

@ -55,17 +55,17 @@ func RejectCollect(id objid, object obj)
return false;
}
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, int status)
{
if (!this)
return inherited(plr, ctrl, x, y, strength, repeat, release, ...);
return inherited(plr, ctrl, x, y, strength, repeat, status, ...);
// Quickswitch changes the current active inventory slot
if (ctrl == CON_QuickSwitch && !release)
if (ctrl == CON_QuickSwitch && status == CONS_Down)
{
// but ignore quickswitch if we have more than 1 hand-slot
if(this.HandObjects > 1)
return inherited(plr, ctrl, x, y, strength, repeat, release, ...);;
return inherited(plr, ctrl, x, y, strength, repeat, status, ...);;
// A number key (hotkey) is pressed, change quick switch slot
/*if (this.inventory.hotkey_down != nil)
@ -78,7 +78,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
SetHandItemPos(0, this.inventory.quick_slot); // quick_slot is updated in SetHandItemPos
return true;
}
if (ctrl == CON_QuickSwitch && release) // Do nothing for now but will be used in the future
if (ctrl == CON_QuickSwitch && status == CONS_Up) // Do nothing for now but will be used in the future
{
return true;
}
@ -86,7 +86,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
if (!Contained())
{
// Quick-pickup item via click? Note that this relies on being executed after the normal Clonk controls
if (ctrl == CON_Use && !this->GetHandItem(0) && !release)
if (ctrl == CON_Use && !this->GetHandItem(0) && status == CONS_Down)
{
var sort = Sort_Distance(x, y);
var items = FindAllPickupItems(sort);
@ -97,7 +97,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
// Begin picking up objects.
if (ctrl == CON_PickUp && !release)
if (ctrl == CON_PickUp && status == CONS_Down)
{
this->CancelUse();
BeginPickingUp();
@ -105,7 +105,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
// Drop the mouse item?
if (ctrl == CON_Drop && !release)
if (ctrl == CON_Drop && status == CONS_Down)
{
// Do not immediately collect another thing unless chosen with left/right.
if (this.inventory.is_picking_up)
@ -139,7 +139,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
// Finish picking up (aka "collect").
if (ctrl == CON_PickUp && release)
if (ctrl == CON_PickUp && status == CONS_Up)
{
EndPickingUp();
return true;
@ -223,7 +223,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
if (this.inventory.hotkey_down != nil && hot > 0 && hot <= this.MaxContentsCount && this.inventory.hotkey_down != hot)
{
// do nothing if this is just key down
if (!release)
if (status == CONS_Down)
return true;
// switch the two slots
this->~Switch2Items(this.inventory.hotkey_down-1, hot-1);
@ -240,7 +240,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
// hotkey up: perform slot selection
if (hot > 0 && hot <= this.MaxContentsCount && release)
if (hot > 0 && hot <= this.MaxContentsCount && status == CONS_Up)
{
// This wasn't liked by many players, so slot selection is back to key down.
@ -256,7 +256,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
return true;
}
// a hotkey is pressed, save it for now
if (hot > 0 && hot <= this.MaxContentsCount && !release)
if (hot > 0 && hot <= this.MaxContentsCount && status == CONS_Down)
{
this.inventory.hotkey_down = hot;
// For safety
@ -267,7 +267,7 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
return true;
}
return inherited(plr, ctrl, x, y, strength, repeat, release, ...);
return inherited(plr, ctrl, x, y, strength, repeat, status, ...);
}
private func FxIntHighlightItemStart(object target, proplist fx, temp, object item)

View File

@ -83,12 +83,12 @@ public func FxControlConstructionPreviewStart(object clonk, effect, int temp, id
}
// Called by Control2Effect
public func FxControlConstructionPreviewControl(object clonk, effect, int ctrl, int x, int y, int strength, bool repeat, bool release)
public func FxControlConstructionPreviewControl(object clonk, effect, int ctrl, int x, int y, int strength, bool repeat, int status)
{
if (ctrl != CON_Aim)
{
// CON_Use is accept, but don't remove the preview, this is done on releasing the button.
if (ctrl == CON_Use && !release)
if (ctrl == CON_Use && status == CONS_Down)
{
var ok = CreateConstructionSite(clonk, effect.structure, AbsX(effect.preview->GetX()), AbsY(effect.preview->GetY() + effect.preview.dimension_y/2), effect.preview.blocked, effect.preview.direction, effect.preview.stick_to);
if (ok)
@ -105,7 +105,7 @@ public func FxControlConstructionPreviewControl(object clonk, effect, int ctrl,
// (yes, this means that actionbar-hotkeys wont work for it. However clicking the button will.)
else if (IsInteractionControl(ctrl))
{
if (release)
if (status == CONS_Up)
effect.preview->Flip();
return true;
}

View File

@ -337,13 +337,13 @@ public func FxIntClimbControlStop(object target, effect fx, int reason, bool tmp
return FX_OK;
}
public func FxIntClimbControlControl(object target, effect fx, int ctrl, int x, int y, int strength, bool repeat, bool release)
public func FxIntClimbControlControl(object target, effect fx, int ctrl, int x, int y, int strength, bool repeat, int status)
{
// Only handle movement controls.
if (ctrl != CON_Up && ctrl != CON_Down && ctrl != CON_Right && ctrl != CON_Left)
return false;
// Perform actions on key down and not on release.
if (release)
if (status != CONS_Down)
return false;
// Move up and down by setting com dir.
if (ctrl == CON_Up)

View File

@ -125,6 +125,11 @@ func PutLiquid(liquid_name, int amount, object source)
return after - before;
}
func AcceptsLiquid(liquid_name, int amount)
{
return amount <= this->GetLiquidContainerMaxFillLevel() - GetLiquidAmount(liquid_name);
}
private func GetLiquidDef(liquid_name)
{
if (GetType(liquid_name) == C4V_String)

View File

@ -0,0 +1,4 @@
[DefCore]
id=Library_Wearable
Version=8,0
Category=C4D_StaticBack

View File

@ -0,0 +1,180 @@
/**
Library_Wearable
Library for all clothing and other things worn on the clonk.
The library will make sure that not two objects are worn at the same
position at the same time.
@author: Clonkonaut
*/
/* Bone names of attachment on the clonk and also identifiers */
// Headwear like helmets, caps or similar
static const WEARABLE_Head = "skeleton_head";
local wear_effect;
local display_disabled = false;
/* Overloads */
// These functions must exist in order for this library to work
public func GetWearPlace()
{
return; // must return one of the WEARABLE_* constants
}
/* Other functions that can be present in the object:
func GetWearBone: return the bone with which it is attached to the clonk (default: "main")
func GetWearTransform(clonk): transformation added when worn
func StayAfterDeath: return true if the item should remain on the clonk after its death (default is that it does not stay)
func OnPutOn(clonk): callback after the item was put on
func OnTakenOff(clonk): callback after the item was taken off
func OnDamage(damage_amount, cause, by_player): Callback whenever the wearer is damaged, parameters are passed on from the effect Damage callback
Return value is returned (useful for protective clothing)
func GetCarryMode: must(!) return CARRY_None whenever display_disabled is true, otherwise display error will likely occur
*/
/* Engine Callbacks */
// It is assumed that a wearable must be contained in the clonk to be worn.
func Departure()
{
if (IsWorn())
TakeOff();
_inherited(...);
}
func Destruction()
{
if (IsWorn())
TakeOff();
_inherited(...);
}
/* Interface */
// The clonk will put on the item and take off any other item currently worn
// in the same place, unless no_force is true in which case false will be returned
// when something else is worn.
public func PutOn(object clonk, bool no_force)
{
// ???
if (!clonk->~IsClonk()) return false;
// Remove all other things before putting on
if (!no_force)
{
var effect;
for (var i = GetEffectCount("Wearing", clonk); effect = GetEffect("Wearing", clonk, i); i--)
if (effect.identifier == GetWearPlace())
RemoveEffect(nil, clonk, effect);
}
// It is not impossible that the item is currently held in the hand of the clonk.
// If so, temporarily disable display because the same mesh cannot be attached twice.
// Any item must adhere this variable in GetCarryMode!
display_disabled =true;
clonk->~UpdateAttach();
wear_effect = clonk->CreateEffect(Wearing, 2, nil, GetWearPlace(), this);
if (wear_effect == -1) // got rejected
wear_effect = nil;
display_disabled = false;
clonk->~UpdateAttach();
if (wear_effect)
{
// Callback to do whatever
this->~OnPutOn(clonk);
return true;
}
return false;
}
public func IsWorn()
{
return wear_effect;
}
public func TakeOff()
{
if (!wear_effect)
return false;
return RemoveEffect(nil, nil, wear_effect);
}
func TakenOff()
{
wear_effect = nil;
if (Contained())
Contained()->~UpdateAttach();
// Callback to do whatever; note that at this point the item isn't necessary contained.
this->~OnTakenOff();
}
/* Wearing effect */
local Wearing = new Effect {
Construction = func(string wearing_identifier, object worn_item) {
// Save where this thing is worn
this.identifier = wearing_identifier;
// Save what is worn
this.item = worn_item;
},
Start = func() {
// Check if parameters are properly set
if (this.identifier == nil) return -1;
if (this.item == nil) return -1;
var attachment_bone = this.item->~GetWearBone() ?? "main";
var attachment_transform = this.item->~GetWearTransform(this.Target);
this.attach = Target->AttachMesh(this.item, this.identifier, attachment_bone, attachment_transform);
},
Damage = func(int damage, int cause, int by_player) {
if (!this.item) return damage;
var ret = this.item->~OnDamage(damage, cause, by_player);
if (ret == nil)
ret = damage;
return ret;
},
Effect = func(string new_name, var1) {
// Reject wearing effects if in the same place
if (new_name == "Wearing")
if (var1 == this.identifier)
return -1;
},
Stop = func(int reason) {
// Items can prevent being removed from the clonk on death
if (reason == FX_Call_RemoveDeath)
if (this.item && this.item->~StayAfterDeath(this.Target))
return -1;
if (this.Target) this.Target->DetachMesh(this.attach);
this.attach = nil;
},
Destruction = func() {
if (this.attach != nil && this.Target)
this.Target->DetachMesh(this.attach);
if (this.item)
this.item->TakenOff();
}
};

View File

@ -35,7 +35,7 @@ func OnClonkDeathEx(object clonk, int plr, int killed_by)
var which_one = Random(3) + 1;
var log="";
if (!GetPlayerName(killed_by))
log=Format(Translate(Format("KilledByGaya%d", which_one)), GetTaggedPlayerName(plr), name);
log=Format(Translate(Format("KilledByGaia%d", which_one)), GetTaggedPlayerName(plr), name);
else if (plr == killed_by)
log=Format(Translate(Format("Selfkill%d", which_one)), GetTaggedPlayerName(plr), name);
else if (!Hostile(plr,killed_by))

View File

@ -7,9 +7,9 @@ Selfkill3=%s hat Selbstmord begangen! Der arme %s.
KilledByPlayer1=%s hat einen %s an %s verloren.
KilledByPlayer2=%ss %s konnte nichts gegen %s machen.
KilledByPlayer3=%s konnte mit seinem %s nicht gut genug kämpfen für %s.
KilledByGaya1=%s hat einen %s weggezaubert bekommen.
KilledByGaya2=%ss %s stirbt von alleine.
KilledByGaya3=%s hat einen %s an Gaya verloren.
KilledByGaia1=%s hat einen %s weggezaubert bekommen.
KilledByGaia2=%ss %s stirbt von alleine.
KilledByGaia3=%s hat einen %s an Gaia verloren.
Teamkill1=%s hat einen %s an %s verloren. Teamkiller!
Teamkill2=%ss %s ist bei %s in Ungnade gefallen.
Teamkill3=%ss %s wurde von %s sauber hintergangen.

View File

@ -7,9 +7,9 @@ Selfkill3=%s committed suicide! Poor %s.
KilledByPlayer1=%s has lost his %s to %s.
KilledByPlayer2=%s's %s couldn't do anything against %s.
KilledByPlayer3=%s's %s was not able to defeat %s.
KilledByGaya1=%s's %s was spirited away.
KilledByGaya2=%s's %s died on its own.
KilledByGaya3=%s lost a %s to Gaya.
KilledByGaia1=%s's %s was spirited away.
KilledByGaia2=%s's %s died on its own.
KilledByGaia3=%s lost a %s to Gaia.
Teamkill1=%s has lost a %s to %s. Teamkiller!
Teamkill2=%s's %s fell out of %s's favour.
Teamkill3=%s's %s was denied by %s.

View File

@ -326,7 +326,6 @@ protected func Pumping()
break;
}
}
stored_material_amount = i;
if (stored_material_amount <= 0)
stored_material_name = nil;
@ -338,8 +337,8 @@ protected func Pumping()
}
else
{
// Put into wait state if no liquid could be pumped for a while
if (++clog_count >= max_clog_count)
// Put into wait state if no liquid could be pumped for a while or if the drain has no aperture, i.e. is a liquid tank.
if (++clog_count >= max_clog_count || !drain_obj->~HasAperture())
{
SetState("WaitForLiquid");
}
@ -383,7 +382,6 @@ func InsertMaterialAtDrain(object drain_obj, string material_name, int amount)
drain_obj->InsertMaterial(material_index, drain_obj.ApertureOffsetX, drain_obj.ApertureOffsetY);
}
}
return amount <= 0;
}
@ -403,9 +401,10 @@ func CheckState()
}
else
{
// can pump but has no liquid -> wait for liquid
var source_ok = IsLiquidSourceOk();
var drain_ok = IsLiquidDrainOk();
// Can pump but has no liquid or can't dispense liquid -> wait.
var source_mat = GetLiquidSourceMaterial();
var source_ok = source_mat != nil;
var drain_ok = GetLiquidDrainOk(source_mat);
if (!source_ok || !drain_ok)
{
if (!source_ok)
@ -532,30 +531,39 @@ private func PumpHeight2Power(int pump_height)
return used_power;
}
// TODO: check usage of this, probably has to return true if the source is a container
// Returns whether there is liquid at the source pipe to pump.
private func IsLiquidSourceOk()
private func GetLiquidSourceMaterial()
{
// source
// Get the source object and check whether there is liquid.
// TODO: If the source is a liquid container check which material will be supplied.
var source_obj = GetSourceObject();
if(!source_obj->GBackLiquid(source_obj.ApertureOffsetX, source_obj.ApertureOffsetY))
var is_liquid = source_obj->GBackLiquid(source_obj.ApertureOffsetX, source_obj.ApertureOffsetY);
var liquid = MaterialName(source_obj->GetMaterial(source_obj.ApertureOffsetX, source_obj.ApertureOffsetY));
if (!is_liquid)
{
source_obj->~CycleApertureOffset(this); // try different offsets, so we can resume pumping after clog because 1px of earth was dropped on the source pipe
return false;
// Try different offsets, so we can resume pumping after clog because 1px of earth was dropped on the source pipe.
source_obj->~CycleApertureOffset(this);
return;
}
return true;
return liquid;
}
// TODO: check usage of this, probably has to return true if the drain is a container
// Returns whether the drain pipe is free.
private func IsLiquidDrainOk()
// Returns whether the drain pipe is free or the liquid container accepts the given material.
private func GetLiquidDrainOk(string liquid)
{
// target (test with the very popular liquid "water")
var drain_obj = GetDrainObject();
if(!drain_obj->CanInsertMaterial(Material("Water"),drain_obj.ApertureOffsetX, drain_obj.ApertureOffsetY))
if (drain_obj->~HasAperture())
{
drain_obj->~CycleApertureOffset(this); // try different offsets, so we can resume pumping after clog because 1px of earth was dropped on the source pipe
return false;
if (!drain_obj->CanInsertMaterial(Material(liquid), drain_obj.ApertureOffsetX, drain_obj.ApertureOffsetY))
{
drain_obj->~CycleApertureOffset(this); // try different offsets, so we can resume pumping after clog because 1px of earth was dropped on the source pipe
return false;
}
}
else if (drain_obj->~IsLiquidContainer())
{
if (!drain_obj->AcceptsLiquid(liquid, 1))
return false;
}
return true;
}

View File

@ -1 +1 @@
Name=Kokosnussbaum
Name=Kokospalme

View File

@ -1 +1 @@
Name=Coconut Tree
Name=Coconut Palm

View File

@ -1,7 +1,7 @@
[Material]
Name=Brick
Shape=Smoother
Density=50
Density=90
Friction=15
Placement=80
TextureOverlay=brick

View File

@ -21,9 +21,8 @@ global func IsMovementControl(int ctrl)
/** Control throws selected item */
global func IsThrowControl(int ctrl)
{
// left mouse button
if(ctrl == CON_Throw
|| ctrl == CON_ThrowDelayed)
// right mouse button
if(ctrl == CON_Throw)
return true;
return false;
@ -99,4 +98,4 @@ global func IsUseControl(int ctrl)
{
if (ctrl == CON_Use || ctrl == CON_UseAlt) return true;
return false;
}
}

View File

@ -137,6 +137,15 @@ global func Find_Hostile(int plr)
return p;
}
/*
Similar to Find_Hostile, but defaults to treating all players as hostile when plr = NO_OWNER.
*/
global func Find_AnimalHostile(int plr)
{
if (plr == NO_OWNER) return Find_Not(Find_Owner(NO_OWNER));
return Find_Or(Find_Owner(NO_OWNER), Find_Hostile(plr));
}
global func Find_Allied(int plr)
{
var p = [C4FO_Or];

View File

@ -18,12 +18,11 @@ global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int stren
{
var release = status == CONS_Up;
//Log("%d, %s, %i, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), spec_id, x,y,strength, repeat, status);
if (status == CONS_Moved) return false;
// Control handled by definition? Forward
if (spec_id) return spec_id->ForwardedPlayerControl(plr, ctrl, x, y, strength, repeat, release);
if (spec_id) return spec_id->ForwardedPlayerControl(plr, ctrl, x, y, strength, repeat, status);
// Forward control to player
if (Control2Player(plr,ctrl, x, y, strength, repeat, release)) return true;
if (Control2Player(plr,ctrl, x, y, strength, repeat, status)) return true;
// Forward control to cursor
var cursor = GetCursor(plr);
@ -34,7 +33,7 @@ global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int stren
// menu controls:
if (cursor->~GetMenu())
if (cursor->~GetMenu() && status != CONS_Moved)
{
// direction keys are always forwarded to the menu
// (because the clonk shall not move while the menu is open)
@ -83,11 +82,11 @@ global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int stren
}
// Overload by effect?
if (cursor->Control2Effect(plr, ctrl, cursorX, cursorY, strength, repeat, release)) return true;
if (cursor->Control2Effect(plr, ctrl, cursorX, cursorY, strength, repeat, status)) return true;
if (cursor->ObjectControl(plr, ctrl, cursorX, cursorY, strength, repeat, release))
if (cursor->ObjectControl(plr, ctrl, cursorX, cursorY, strength, repeat, status))
{
if (cursor && !release && !repeat)
if (cursor && status == CONS_Down && !repeat)
{
// non-mouse controls reset view
if (!x && !y) ResetCursorView(plr);
@ -127,7 +126,7 @@ global func PlayerHasVirtualCursor(int plr)
// Control2Player
// Player-wide controls
global func Control2Player(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
global func Control2Player(int plr, int ctrl, int x, int y, int strength, bool repeat, int status)
{
// select previous or next
if (ctrl == CON_PreviousCrew)
@ -202,7 +201,7 @@ global func StopSelected(int plr)
// Control2Effect
// Call control function in all effects that have "Control" in their name
global func Control2Effect(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
global func Control2Effect(int plr, int ctrl, int x, int y, int strength, bool repeat, int status)
{
// x and y are local coordinates
if (!this) return false;
@ -213,7 +212,7 @@ global func Control2Effect(int plr, int ctrl, int x, int y, int strength, bool r
{
iEffect = GetEffect("*Control*", this, i);
if (iEffect)
if (EffectCall(this, iEffect, "Control", ctrl, x,y,strength, repeat, release))
if (EffectCall(this, iEffect, "Control", ctrl, x,y,strength, repeat, status))
return true;
}
// No effect handled the control
@ -224,7 +223,7 @@ global func Control2Effect(int plr, int ctrl, int x, int y, int strength, bool r
// Called from PlayerControl when a control is issued to the cursor
// Return whether handled
// To be overloaded by specific objects to enable additional controls
global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, int status)
{
if (!this) return false;
@ -233,7 +232,7 @@ global func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
// Movement controls
if (ctrl == CON_Left || ctrl == CON_Right || ctrl == CON_Up || ctrl == CON_Down || ctrl == CON_Jump)
return ObjectControlMovement(plr, ctrl, strength, release, repeat);
return ObjectControlMovement(plr, ctrl, strength, status, repeat);
// Unhandled control
return false;
@ -276,7 +275,7 @@ global func NameComDir(comdir)
}
// Called when CON_Left/Right/Up/Down controls are issued/released
// Return whether handled
global func ObjectControlMovement(int plr, int ctrl, int strength, bool release, bool repeat)
global func ObjectControlMovement(int plr, int ctrl, int strength, int status, bool repeat)
{
if (!this) return false;
@ -284,13 +283,13 @@ global func ObjectControlMovement(int plr, int ctrl, int strength, bool release,
if (Contained()) return false;
// this is for controlling movement with Analogpad
if(!release)
if(status == CONS_Down)
if(strength != nil && strength < CON_Gamepad_Deadzone)
return true;
var proc = GetProcedure();
// Some specific movement controls
if (!release)
if (status == CONS_Down)
{
// Jump control
if (ctrl == CON_Jump)

View File

@ -37,8 +37,6 @@
#
# Gamepad controls
# -------------------------------------
# ThrowDelayed
# UseDelayed
# AimUp AimDown AimLeft AimRight
# AimAxisUp AimAxisDown AimAxisLeft AimAxisRight
#
@ -59,26 +57,6 @@
DefaultDisabled=1
CoordinateSpace=Viewport
[ControlDef]
Identifier=AimUp
DefaultDisabled=1
Hold=1
[ControlDef]
Identifier=AimDown
DefaultDisabled=1
Hold=1
[ControlDef]
Identifier=AimLeft
DefaultDisabled=1
Hold=1
[ControlDef]
Identifier=AimRight
DefaultDisabled=1
Hold=1
[ControlDef]
Identifier=AimAxisUp
GUIName=$CON_AimAxisUp$
@ -164,12 +142,6 @@
Hold=1
SendCursorPos=1
[ControlDef]
Identifier=ThrowDelayed
GUIName=$CON_Throw$
GUIDesc=$CON_Throw_Desc$
Hold=1
[ControlDef]
Identifier=Drop
GUIName=$CON_Drop$
@ -286,13 +258,6 @@
Hold=1
SendCursorPos=1
[ControlDef]
Identifier=UseDelayed
GUIName=$CON_Use$
GUIDesc=$CON_Use_Desc$
Hold=1
SendCursorPos=1
[ControlDef]
Identifier=UseAlt
GUIName=$CON_UseAlt$
@ -300,13 +265,6 @@
Hold=1
SendCursorPos=1
[ControlDef]
Identifier=UseAltDelayed
GUIName=$CON_UseAlt$
GUIDesc=$CON_UseAlt_Desc$
Hold=1
SendCursorPos=1
[ControlDef]
Identifier=CancelUse
@ -960,11 +918,54 @@
GUIGroup=40
Control=Interact
[Assignment]
Key=CON_Interact,CON_Left
Control=InteractNext_Left
Priority=75
[Assignment]
Key=CON_Interact,CON_Right
Control=InteractNext_Right
Priority=75
[Assignment]
Key=CON_Interact,CON_Up
Control=InteractNext_CycleObject
Priority=75
[Assignment]
Key=CON_Interact,CON_Down
Control=InteractNext_Stop
Priority=75
[Assignment]
Key=ControllerButtonX
Control=PickUp
GUIGroup=50
[Assignment]
Key=CON_PickUp,CON_Left
Control=PickUpNext_Left
[Assignment]
Key=CON_PickUp,CON_Right
Control=PickUpNext_Right
[Assignment]
Key=CON_PickUp,CON_Up
Control=PickUpNext_All
Priority=75
[Assignment]
Key=CON_PickUp,CON_Down
Control=PickUpNext_Stop
Priority=75
[Assignment]
Key=CON_PickUp,CON_Throw
Control=Drop
Priority=75
# Crew
[Assignment]
@ -985,13 +986,13 @@
GUIDesc=$KEY_GamepadUse_Desc$
GUIGroup=20
Priority=100
Control=UseDelayed
Control=Use
[Assignment]
Key=ControllerLeftTrigger
GUIGroup=20
Priority=100
Control=ThrowDelayed
Control=Throw
# TODO: Zoom
@ -1017,58 +1018,12 @@
Name=*_GamepadCon_*
[Assignment]
Key=CON_AimAxisLeft
Priority=50
Control=Left
[Assignment]
Key=CON_AimAxisRight
Priority=50
Control=Right
[Assignment]
Key=CON_AimAxisDown
Priority=50
Control=Down
[Assignment]
Key=CON_AimAxisUp
Priority=50
Control=Up
[Assignment]
Key=CON_Left
Priority=70
Control=AimLeft
[Assignment]
Key=CON_Right
Priority=70
Control=AimRight
[Assignment]
Key=CON_Down
Priority=70
Control=AimDown
[Assignment]
Key=CON_Up
Priority=70
Control=AimUp
[Assignment]
Key=CON_Down,CON_UseDelayed
Key=CON_Down,CON_Throw
GUIDisabled=1
GUIGroup=20
Priority=150
Control=Drop
[Assignment]
Key=CON_UseDelayed
GUIName=None
Priority=50
Control=ThrowDelayed
# ======================================================================= #
# Default mouse control #
# ======================================================================= #

View File

@ -2,13 +2,13 @@
#appendto Library_ClonkControl
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, int status)
{
if (!this)
return false;
// Spawn menu
if (ctrl == CON_SpawnMenu && !release)
if (ctrl == CON_SpawnMenu && status == CONS_Down)
{
// Close any menu if open.
if (GetMenu())
@ -31,5 +31,5 @@ public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool re
}
// Unhandled control will be handled by the library itself.
return _inherited(plr, ctrl, x, y, strength, repeat, release, ...);
}
return _inherited(plr, ctrl, x, y, strength, repeat, status, ...);
}

View File

@ -1,9 +1,9 @@
// Shows and hides the tutorial guide if the [H] button is pressed.
global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat, bool release)
global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat, int status)
{
if (ctrl != CON_TutorialGuide)
return _inherited(plr, ctrl, spec_id, x, y, strength, repeat, release, ...);
return _inherited(plr, ctrl, spec_id, x, y, strength, repeat, status, ...);
// Don't do anything if the player is a sequence.
if (GetActiveSequence())
return;
@ -17,4 +17,4 @@ global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int stren
else
guide->HideGuide();
return;
}
}

View File

@ -2,7 +2,7 @@
Icon=34
Title=AcidRift
Version=6,0
Difficulty=70
Difficulty=80
[Definitions]
Definition1=Objects.ocd

View File

@ -2,7 +2,7 @@
Icon=36
Title=Chine
Version=6,0
Difficulty=50
Difficulty=60
[Definitions]
Definition1=Objects.ocd

View File

@ -2,7 +2,7 @@ Flooded Veins
You stumble upon an abandoned settlement beneath the Forest of Orgos in your quest for more gold. Legends tell a story of even more valuable materials named gems which may be found here. Might this abandoned settlement be a failed attempt to find these gems? Set up an expedition to explore the flooded caves around and to discover possible gem veins.
Goal: Sell gems
Goal: Sell gems.
Hints:
- The flooded caves may be drained using pumps, you can get rid off the excess water where you entered the cave.

View File

@ -2,7 +2,7 @@
Title=GemGrabbers
Icon=35
Version=6,0
Difficulty=80
Difficulty=90
[Definitions]
Definition1=Objects.ocd

View File

@ -2,7 +2,7 @@
Icon=23
Title=Krakatoa
Version=6,0
Difficulty=60
Difficulty=70
[Definitions]
Definition1=Objects.ocd

View File

@ -0,0 +1,9 @@
Rasches Raffinieren
An apparently infinite oil well is hidden deep inside a cave. Connect the well to the refinery drain outside of the cave using pumps and supply the refinery.
Ziel: Pumpe Öl.
Tipps:
- Do not set oil on fire, it will burn for a long time.
- You can connect multiple pipes to the refinery drain.

View File

@ -0,0 +1,9 @@
Rapid Refining
An apparently infinite oil well is hidden deep inside a cave. Connect the well to the refinery drain outside of the cave using pumps and supply the refinery.
Goal: Pump oil.
Hints:
- Do not set oil on fire, it will burn for a long time.
- You can connect multiple pipes to the refinery drain.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,250 @@
/**
Rapid Refining
Use the oil from an underground well to power your settlement.
@author Maikel
*/
#include Library_Map
// Called be the engine: draw the complete map here.
protected func InitializeMap(proplist map)
{
// Map settings.
var overground_wdt = 40;
var overground_hgt = 20;
// Retrieve the settings according to the MapSize setting.
var map_size;
if (SCENPAR_MapSize == 1)
map_size = [250, 150];
if (SCENPAR_MapSize == 2)
map_size = [275, 150];
if (SCENPAR_MapSize == 3)
map_size = [300, 150];
// Set the map size.
map->Resize(map_size[0], map_size[1]);
// The overground area is in the top left corner, the underground are thereby the remainder.
var underground = {Algo = MAPALGO_Not, Op = {Algo = MAPALGO_Rect, X = 0, Y = 0, Wdt = overground_wdt, Hgt = overground_hgt}};
Draw("Earth", underground);
// Draw materials in the underground area and then overlay the rest of the map.
DrawMaterial("Earth-earth_root", underground, 2, 12);
DrawMaterial("Earth-earth_spongy", underground, 2, 12);
DrawMaterial("Granite", underground, 3, 6);
DrawMaterial("Tunnel", underground, 5, 8);
DrawMaterial("Rock-rock", underground, 3, 4);
DrawMaterial("Rock", underground, 3, 4);
DrawMaterial("Ore", underground, 6, 6);
DrawMaterial("Firestone", underground, 5, 4);
DrawMaterial("Coal", underground, 6, 4);
// Draw some underground water lakes.
var underground_water = {Algo = MAPALGO_And, Op = [underground, {Algo = MAPALGO_Rect, X = 0, Y = map.Hgt / 2, Wdt = map.Wdt - 20, Hgt = map.Hgt / 2}]};
DrawMaterial("Water", underground_water, [4, 10], 6);
// The entrance border is out of granite.
var entrance_border = {Algo = MAPALGO_Border, Left = 3, Op = underground};
entrance_border = {Algo = MAPALGO_Or, Op = [entrance_border, {Algo = MAPALGO_Turbulence, Iterations = 2, Amplitude = [6, 8], Scale = [6, 8], Seed = Random(65536), Op = entrance_border}]};
DrawRock(entrance_border);
// The entrance floor is out of earth and brick.
var entrance_floor = {Algo = MAPALGO_Border, Top = 3, Op = underground};
Draw("Brick", entrance_floor);
var entrance_floor_earth = {Algo = MAPALGO_Border, Top = 2, Op = underground};
var entrance_floor_earth = {Algo = MAPALGO_And, Op = [entrance_floor_earth, {Algo = MAPALGO_Rect, X = 4, Y = 0, Wdt = 17, Hgt = map.Hgt}]};
Draw("Earth", entrance_floor_earth);
// There are borders on the full underground area of the map.
var wdt = 3;
var underground_border = {Algo = MAPALGO_Not, Op = {Algo = MAPALGO_Rect, X = wdt, Y = wdt, Wdt = map.Wdt - 2 * wdt, Hgt = map.Hgt - 2 * wdt}};
underground_border = {Algo = MAPALGO_And, Op = [underground_border, underground]};
underground_border = {Algo = MAPALGO_Or, Op = [underground_border, {Algo = MAPALGO_Turbulence, Iterations = 6, Amplitude = 12, Scale = 16, Seed = Random(65536), Op = underground_border}]};
underground_border = {Algo = MAPALGO_And, Op = [underground_border, underground, {Algo = MAPALGO_Not, Op = entrance_floor}]};
DrawRock(underground_border);
// Draw a tunnel to the oil well.
DrawMainTunnel(map, overground_wdt, overground_hgt);
// Some slabs of granite/rock which block the path to the oil.
DrawRockSlabs(map);
// There is a smaller oil field below the entrance.
DrawOilLakes(map);
// The main oil well is in the bottom right of the map.
DrawOilWell(map, underground_border);
// A large water lake at the bottom of the map.
DrawWaterLake(map, underground_border);
// Some liquid veins above the oil well.
DrawLiquidVeins(map, underground_border);
// Fix liquid borders.
FixLiquidBorders();
// Return true to tell the engine a map has been successfully created.
return true;
}
public func DrawMainTunnel(proplist map, int overground_wdt, int overground_hgt)
{
var sx = overground_wdt;
var sy = overground_hgt - 3;
var ex = map.Wdt - 20;
var ey = map.Hgt - 30;
var nr_steps = 10;
var tunnel_x = [], tunnel_y = [];
for (var index = 0; index <= nr_steps; index++)
{
var dev = 4 * Min(index, nr_steps - index);
tunnel_x[index] = sx + (ex - sx) * index / nr_steps + RandomX(-dev, dev);
tunnel_y[index] = sy + (ey - sy) * index / nr_steps + RandomX(-dev, dev);
}
// Draw main tunnel.
var tunnel = {Algo = MAPALGO_Polygon, X = tunnel_x, Y = tunnel_y, Wdt = 3, Empty = true, Open = true};
tunnel = {Algo = MAPALGO_Or, Op = [tunnel, {Algo = MAPALGO_Turbulence, Iterations = 2, Amplitude = [6, 8], Scale = [6, 8], Seed = Random(65536), Op = tunnel}]};
Draw("Tunnel", tunnel);
// Draw branches.
for (var index = 2; index <= nr_steps - 2; index++)
{
var x = tunnel_x[index];
var y = tunnel_y[index];
var to_x = x + RandomX(-5, 5);
var to_y = y + (2 * Random(2) - 1) * RandomX(16, 22);
var tunnel_branch = {Algo = MAPALGO_Polygon, X = [x, to_x], Y = [y, to_y], Wdt = 2, Empty = true, Open = true};
tunnel_branch = {Algo = MAPALGO_Turbulence, Iterations = 4, Amplitude = 12, Scale = 8, Seed = Random(65536), Op = tunnel_branch};
Draw("Tunnel", tunnel_branch);
}
return;
}
public func DrawRockSlabs(proplist map)
{
var slabs = {Algo = MAPALGO_Lines, X = 6, Y = 1, Distance = 32};
slabs = {Algo = MAPALGO_And, Op = [slabs, {Algo = MAPALGO_RndChecker, Ratio = 75, Seed = Random(65536)}]};
slabs = {Algo = MAPALGO_Turbulence, Iterations = 2, Amplitude = [6, 8], Scale = [6, 8], Seed = Random(65536), Op = slabs};
slabs = {Algo = MAPALGO_And, Op = [slabs, {Algo = MAPALGO_Rect, X = map.Wdt / 3, Y = 0, Wdt = 2 * map.Wdt / 3, Hgt = map.Hgt}]};
DrawRock(slabs);
return;
}
public func DrawWaterLake(proplist map, proplist underground_border)
{
var lake_height = 20;
var lake_width = 80;
var waterfall_height = 62;
var tunnel_height = 6;
// Draw a large lake with tunnel above.
underground_border = {Algo = MAPALGO_And, Op = [underground_border, {Algo = MAPALGO_Not, Op = {Algo = MAPALGO_Rect, X = 0, Y = map.Hgt - lake_height - 2, Wdt = lake_width, Hgt = 6}}]};
var lake_floor_rock = {Algo = MAPALGO_And, Op = [{Algo = MAPALGO_Lines, X = 3, Y = 0, Distance = 10}, {Algo = MAPALGO_Rect, X = 0, Y = map.Hgt - 7, Wdt = lake_width, Hgt = 7}]};
var lake_floor_rock = {Algo = MAPALGO_Turbulence, Iterations = 4, Amplitude = 16, Scale = 8, Seed = Random(65536), Op = lake_floor_rock};
underground_border = {Algo = MAPALGO_Or, Op = [underground_border, lake_floor_rock]};
DrawRock(lake_floor_rock);
var tunnel = {Algo = MAPALGO_Rect, X = 0, Y = map.Hgt - lake_height - tunnel_height, Wdt = lake_width, Hgt = tunnel_height};
tunnel = {Algo = MAPALGO_Or, Op = [tunnel, {Algo = MAPALGO_Turbulence, Iterations = 4, Amplitude = 16, Scale = 8, Seed = Random(65536), Op = tunnel}]};
tunnel = {Algo = MAPALGO_And, Op = [tunnel, {Algo = MAPALGO_Not, Op = underground_border}]};
Draw("Tunnel", tunnel);
var lake = {Algo = MAPALGO_Rect, X = 0, Y = map.Hgt - lake_height, Wdt = lake_width, Hgt = lake_height};
lake = {Algo = MAPALGO_And, Op = [lake, {Algo = MAPALGO_Not, Op = underground_border}]};
Draw("Water:Water", lake);
var lake_floor = {Algo = MAPALGO_Border, Bottom = 2, Op = lake};
Draw("Sand", lake_floor);
var lake_boundary = {Algo = MAPALGO_Rect, X = lake_width - 1, Y = map.Hgt - lake_height, Wdt = 2, Hgt = lake_height};
lake_boundary = {Algo = MAPALGO_Or, Op = [lake_boundary, {Algo = MAPALGO_Turbulence, Iterations = 3, Amplitude = 12, Scale = 12, Seed = Random(65536), Op = lake_boundary}]};
Draw("Everrock", lake_boundary);
var lake_boundary_rock = {Algo = MAPALGO_Border, Wdt = -2, Op = lake_boundary};
DrawRock(lake_boundary_rock);
// Draw a waterfall pooring into the lake.
var waterfall = {Algo = MAPALGO_Rect, X = 3, Y = map.Hgt - lake_height - waterfall_height, Wdt = 9, Hgt = waterfall_height};
waterfall = {Algo = MAPALGO_Or, Op = [waterfall, {Algo = MAPALGO_Turbulence, Iterations = 3, Amplitude = 12, Scale = 12, Seed = Random(65536), Op = waterfall}]};
waterfall = {Algo = MAPALGO_And, Op = [waterfall, {Algo = MAPALGO_Not, Op = underground_border}, {Algo = MAPALGO_Not, Op = lake}]};
Draw("Tunnel", waterfall);
return;
}
public func DrawOilLakes(proplist map)
{
for (var cnt = 0; cnt < 3; cnt++)
{
var oil_lake_x = RandomX(map.Wdt / 10, 3 * map.Wdt / 10);
var oil_lake_y = RandomX(2 * map.Hgt / 7, 5 * map.Hgt / 7);
var oil_lake = {Algo = MAPALGO_Rect, X = oil_lake_x, Y = oil_lake_y, Wdt = 13, Hgt = 15};
oil_lake = {Algo = MAPALGO_Turbulence, Iterations = 4, Amplitude = 10, Scale = 10, Seed = Random(65536), Op = oil_lake};
var oil_lake_filled = {Algo = MAPALGO_And, Op = [oil_lake, {Algo = MAPALGO_Rect, X = 0, Y = oil_lake_y - 3, Wdt = map.Wdt, Hgt = map.Hgt}]};
Draw("Tunnel", oil_lake);
Draw("Oil", oil_lake_filled);
var oil_lake_border = {Algo = MAPALGO_Border, Left = 2, Right = 2, Bottom = 3, Top = 1, Op = oil_lake};
DrawRock(oil_lake_border);
}
return;
}
public func DrawOilWell(proplist map, proplist underground_border)
{
var oil_field = {Algo = MAPALGO_Polygon,
X = [map.Wdt, map.Wdt - 16, map.Wdt - 14, map.Wdt - 10, map.Wdt - 17, map.Wdt - 22, map.Wdt - 22, map.Wdt - 27, map.Wdt - 29],
Y = [map.Hgt - 5, map.Hgt - 5, map.Hgt - 12, map.Hgt - 18, map.Hgt - 23, map.Hgt - 20, map.Hgt - 12, map.Hgt - 12, map.Hgt - 16],
Wdt = 3, Empty = true, Open = true};
var oil_field_oil = {Algo = MAPALGO_And, Op = [oil_field, {Algo = MAPALGO_Rect, X = 0, Y = map.Hgt - 8, Wdt = map.Wdt, Hgt = 8}]};
var oil_field_water = {Algo = MAPALGO_And, Op = [oil_field, {Algo = MAPALGO_Rect, X = 0, Y = 0, Wdt = map.Wdt, Hgt = map.Hgt - 8}]};
Draw("Oil:Oil", oil_field_oil);
Draw("Water", oil_field_water);
var oil_field_border = {Algo = MAPALGO_Border, Left = -3, Right = -3, Top = -3, Bottom = -5, Op = oil_field};
Draw("Everrock", oil_field_border);
var oil_field_entrance = {Algo = MAPALGO_Rect, X = map.Wdt - 31, Y = map.Hgt - 22, Wdt = 5, Hgt = 5};
Draw("Tunnel", oil_field_entrance);
return;
}
public func DrawLiquidVeins(proplist map, proplist underground_border)
{
// Draw some gold as a source of wealth.
var gold = {Algo = MAPALGO_Rect, X = map.Wdt - 60, Y = 10, Wdt = 50, Hgt = 36};
gold = {Algo = MAPALGO_And, Op = [gold, {Algo = MAPALGO_Turbulence, Iterations = 4, Amplitude = 12, Scale = 12, Seed = Random(65536), Op = gold}]};
DrawMaterial("Gold", gold, 4, 30);
// Draw the veins.
var vein_material = "Water";
if (SCENPAR_Difficulty == 2)
vein_material = "Acid";
if (SCENPAR_Difficulty == 3)
vein_material = "DuroLava";
var vein_area = {Algo = MAPALGO_Polygon, X = [map.Wdt - 96, map.Wdt, map.Wdt, map.Wdt - 44], Y = [0, 0, 108, 82]};
vein_area = {Algo = MAPALGO_And, Op = [vein_area, {Algo = MAPALGO_Not, Op = underground_border}]};
var veins = {Algo = MAPALGO_Or, Op = [
{Algo = MAPALGO_Lines, X = 2, Y = -1, Distance = 12},
{Algo = MAPALGO_Lines, X = 2, Y = 1, Distance = 12},
{Algo = MAPALGO_Lines, X = 0, Y = 1, Distance = 16}
]};
veins = {Algo = MAPALGO_And, Op = [veins, vein_area]};
veins = {Algo = MAPALGO_Turbulence, Iterations = 4, Amplitude = 12, Scale = 12, Seed = Random(65536), Op = veins};
Draw(vein_material, veins);
return;
}
/*-- Helper Functions --*/
public func DrawRock(proplist layer)
{
Draw("Granite", layer);
DrawMaterial("Rock-rock", layer, 4, 18);
DrawMaterial("Rock", layer, 4, 18);
return;
}

View File

@ -0,0 +1,47 @@
[ParameterDef]
Name=$Difficulty$
Description=$DescDifficulty$
ID=Difficulty
Default=1
LeagueValue=3
[Options]
[Option]
Name=$DiffNormal$
Description=$DescDiffNormal$
Value=1
[Option]
Name=$DiffHard$
Description=$DescDiffHard$
Value=2
[Option]
Name=$DiffInsane$
Description=$DescDiffInsane$
Value=3
[ParameterDef]
Name=$MapSize$
Description=$DescMapSize$
ID=MapSize
Default=1
LeagueValue=1
[Options]
[Option]
Name=$MapSmall$
Description=$DescMapSmall$
Value=1
[Option]
Name=$MapAverage$
Description=$DescMapAverage$
Value=2
[Option]
Name=$MapLarge$
Description=$DescMapLarge$
Value=3

View File

@ -0,0 +1,14 @@
[DefCore]
id=RefineryDrain
Version=6,0
Category=C4D_Structure
Width=16
Height=32
Offset=-8,-16
Vertices=4
VertexX=-8,-8,8,8
VertexY=-2,15,-2,15
VertexCNAT=5,9,6,10
VertexFriction=50,50,100,100
Construction=1
Mass=10

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,99 @@
/**
Refinery Drain
Oil must be pumped into this structure to be processed further.
@author Maikel
*/
#include Library_Structure
#include Library_Ownable
#include Library_Tank
/*-- Pipeline --*/
public func GetLiquidContainerMaxFillLevel()
{
return 10**9;
}
public func IsLiquidContainerForMaterial(string liquid)
{
return WildcardMatch("Oil", liquid);
}
public func QueryConnectPipe(object pipe)
{
if (pipe->IsDrainPipe() || pipe->IsNeutralPipe())
{
return false;
}
else
{
pipe->Report("$MsgPipeProhibited$");
return true;
}
}
public func OnPipeConnect(object pipe, string specific_pipe_state)
{
SetNeutralPipe(pipe);
pipe->Report("$MsgConnectedPipe$");
}
/*-- Interaction Interface --*/
public func GetOilAmount()
{
var oil = FindObject(Find_ID(Oil), Find_Container(this));
if (oil)
return oil->GetStackCount() / 1000;
return 0;
}
public func HasInteractionMenu() { return true; }
public func GetInteractionMenus(object clonk)
{
var menus = _inherited() ?? [];
var oil_menu =
{
title = "$MsgOilOverview$",
entries_callback = this.GetOilDisplayMenuEntries,
callback_hover = "OnOilDisplayHover",
callback_target = this,
BackgroundColor = RGB(0, 50, 50),
Priority = 20
};
PushBack(menus, oil_menu);
return menus;
}
public func GetOilDisplayMenuEntries(object clonk)
{
return
[{
symbol = Oil,
extra_data = "oil",
custom = {
Style = GUI_FitChildren | GUI_TextVCenter | GUI_TextLeft,
Bottom = "1.1em",
BackgroundColor = {Std = 0, OnHover = 0x50ff0000},
Priority = 1,
Text = Format("$MsgOilPumped$", GetOilAmount())
}
}];
}
public func OnOilDisplayHover(id symbol, string extra_data, desc_menu_target, menu_id)
{
GuiUpdateText(Format("$MsgOilDescription$", GetOilAmount()), menu_id, 1, desc_menu_target);
return;
}
/*-- Properties --*/
local Name = "$Name$";
local Description = "$Description$";

View File

@ -0,0 +1,9 @@
Name=Raffinerie Drain
Description=Attach the drain pipe of a pump to this structure to pump oil into the refinery. Multiple pipes can be connected to the refinery drain.
MsgOilOverview=Oil Overview
MsgOilPumped=Oil amount: %d {{Oil}}.
MsgOilDescription=Currently %d {{Oil}} has been transferred into the refinery.
MsgConnectedPipe=Rohr angeschlossen.
MsgPipeProhibited=Zuflussrohre können nicht an den Raffinerie angeschlossen werden.

View File

@ -0,0 +1,9 @@
Name=Refinery Drain
Description=Attach the drain pipe of a pump to this structure to pump oil into the refinery. Multiple pipes can be connected to the refinery drain.
MsgOilOverview=Oil Overview
MsgOilPumped=Oil amount: %d {{Oil}}.
MsgOilDescription=Currently %d {{Oil}} has been transferred into the refinery.
MsgConnectedPipe=Connected pipe.
MsgPipeProhibited=Source pipes cannot be connected to the foundry.

View File

@ -0,0 +1,5 @@
[DefCore]
id=Goal_Refinery
Version=6,0
Category=C4D_StaticBack|C4D_Goal
Picture=0,0,128,128

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,49 @@
/**
Refinery Goal
A certain amount of oil has to be pumped into the refinery drain.
@author Maikel
*/
#include Library_Goal
local goal_amount;
protected func Initialize()
{
goal_amount = 0;
return _inherited(...);
}
public func SetGoalAmount(int amount)
{
goal_amount = amount;
return;
}
private func GetPumpedAmount()
{
var refinery_drain = FindObject(Find_ID(RefineryDrain));
if (!refinery_drain)
return 0;
return refinery_drain->GetOilAmount();
}
public func IsFulfilled()
{
return GetPumpedAmount() >= goal_amount;
}
public func GetDescription(int plr)
{
if (IsFulfilled())
return "$DescriptionCompleted$";
return Format("$Description$", GetPumpedAmount(), goal_amount);
}
/*-- Proplist --*/
local Name = "$Name$";
local Description = "$Description$";

View File

@ -0,0 +1,3 @@
Name=Raffinerie
Description=You have currently pumped %d {{Oil}} into the refinery drain out of the %d {{Oil}} needed to fulfill this goal.
DescriptionCompleted=Congratulations! You have pumped enough oil into the refinery drain.

View File

@ -0,0 +1,3 @@
Name=Refinery
Description=You have currently pumped %d {{Oil}} into the refinery drain out of the %d {{Oil}} needed to fulfill this goal.
DescriptionCompleted=Congratulations! You have pumped enough oil into the refinery drain.

View File

@ -0,0 +1,31 @@
[Head]
Title=RapidRefining
Icon=24
Version=6,0
Difficulty=50
[Definitions]
Definition1=Objects.ocd
[Player1]
Crew=Clonk=2
[Player2]
Crew=Clonk=2
[Player3]
Crew=Clonk=2
[Player4]
Crew=Clonk=2
[Landscape]
Sky=Clouds1
TopOpen=0
BottomOpen=0
[Weather]
Climate=0
YearSpeed=0
Wind=0,50,-50,50

View File

@ -0,0 +1,223 @@
/**
Rapid Refining
Use the oil from an underground well to power your settlement.
@author Maikel
*/
// Whether the intro has been initialized.
static intro_init;
// Whether the first players has been initialized.
static first_plr_init;
protected func Initialize()
{
// Show wealth in HUD.
GUI_Controller->ShowWealth();
// Rules: team account and buying at flagpole.
CreateObject(Rule_TeamAccount);
CreateObject(Rule_BuyAtFlagpole);
// Goal: pump oil into refinery drain.
var goal = CreateObject(Goal_Refinery);
var amount = 400;
if (SCENPAR_Difficulty == 2)
amount = 800;
else if (SCENPAR_Difficulty == 3)
amount = 1600;
goal->SetGoalAmount(amount);
// Initialize different parts of the scenario.
InitEnvironment(SCENPAR_Difficulty);
InitVegetation(SCENPAR_MapSize);
InitAnimals(SCENPAR_MapSize, SCENPAR_Difficulty);
return;
}
protected func OnGoalsFulfilled()
{
// Give the remaining players their achievement.
GainScenarioAchievement("Done", BoundBy(SCENPAR_Difficulty, 1, 3));
return false;
}
/*-- Player Initialization --*/
protected func InitializePlayer(int plr)
{
// Harsh zoom range.
SetPlayerZoomByViewRange(plr, 400, nil, PLRZOOM_Direct | PLRZOOM_LimitMax);
SetPlayerViewLock(plr, true);
SetFoW(false, plr);
// First player inits the base.
if (!first_plr_init)
{
InitBase(plr, 4 - SCENPAR_Difficulty);
first_plr_init = true;
// Give only the first joined player some wealth.
SetWealth(plr, 150 - 50 * SCENPAR_Difficulty);
}
// Position and materials for the crew.
var crew;
for (var i = 0; crew = GetCrew(plr, i); ++i)
{
crew->SetPosition(20 + Random(32), 160 - 10);
crew->CreateContents(Shovel);
}
// Give the player basic and pumping knowledge.
GivePlayerBasicKnowledge(plr);
GivePlayerPumpingKnowledge(plr);
GivePlayerWeaponryKnowledge(plr);
GivePlayerAdvancedKnowledge(plr);
GivePlayerFarmingKnowledge(plr);
RemovePlayerSpecificKnowledge(plr, [WindGenerator]);
// Give the player the elementary base materials.
GivePlayerElementaryBaseMaterial(plr);
GivePlayerToolsBaseMaterial(plr);
// Initialize the intro sequence if not yet started.
if (!intro_init)
{
//StartSequence("Intro", 0);
intro_init = true;
}
return;
}
private func InitBase(int owner, int amount)
{
var y = 160;
// The basic settlement: flagpole, wind generator, chest.
CreateObjectAbove(Flagpole, 184, y, owner);
var chest = CreateObjectAbove(Chest, 202, y, owner);
chest->CreateContents(Hammer, 2);
chest->CreateContents(Axe, 2);
CreateObjectAbove(WindGenerator, 222, y, owner);
// Two pumps connected to the refinery drain.
var refinery_exit = CreateObjectAbove(RefineryDrain, 8, y, owner);
var pump1 = CreateObjectAbove(Pump, 250, y, owner);
var pump2 = CreateObjectAbove(Pump, 284, y, owner);
var pipe1 = pump1->CreateObject(Pipe);
pipe1->ConnectPipeTo(pump1);
pipe1 = pump1->CreateObject(Pipe, 8);
pipe1->ConnectPipeTo(pump1);
pipe1->ConnectPipeTo(refinery_exit);
var pipe2 = pump2->CreateObject(Pipe, 8);
pipe2->ConnectPipeTo(pump2);
pipe2 = pump2->CreateObject(Pipe, -8);
pipe2->ConnectPipeTo(pump2);
// Additional material in the chest.
if (amount >= 2)
{
chest->CreateContents(Wood, 12);
chest->CreateContents(Metal, 8);
chest->CreateContents(Dynamite, 4);
if (amount >= 3)
{
chest->CreateContents(Wood, 12);
chest->CreateContents(Metal, 8);
chest->CreateContents(Loam, 4);
chest->CreateContents(Pickaxe, 2);
chest->CreateContents(Bread, 4);
}
}
return;
}
/*-- Scenario Initialization --*/
private func InitEnvironment(int difficulty)
{
// Sky has some parallax.
SetSkyParallax(1, 20, 20);
// Some earthquakes if difficulty prescribes it.
if (difficulty >= 2)
Earthquake->SetChance(4 * (difficulty - 1));
// A waterfall above the underground lake.
var waterfall_x = 50;
var waterfall_y = LandscapeHeight() - 600;
var trunk = CreateObjectAbove(Trunk, waterfall_x, waterfall_y);
trunk->DoCon(30); trunk->SetR(150); trunk.Plane = 510;
trunk.MeshTransformation = [-70, 0, 998, 0, 0, 1000, 0, 0, -998, 0, -70, 0];
trunk->MakeInvincible();
var waterfall = CreateWaterfall(waterfall_x + 22, waterfall_y - 10, 10, "Water");
waterfall->SetDirection(2, 0, 3, 6);
waterfall->SetSoundLocation(waterfall_x + 40, waterfall_y + 240);
CreateLiquidDrain(8, 1040, 10);
CreateLiquidDrain(16, 1040, 10);
CreateLiquidDrain(24, 1040, 10);
return;
}
private func InitVegetation(int map_size)
{
var wdt = LandscapeWidth();
var hgt = LandscapeHeight();
// Some plants and trees on the outside.
Tree_Deciduous->Place(16, Shape->Rectangle(0, 0, 240, 160));
Tree_Coniferous2->Place(2, Shape->Rectangle(0, 0, 240, 160));
Cotton->Place(4, Shape->Rectangle(0, 0, 240, 160));
SproutBerryBush->Place(2, Shape->Rectangle(0, 0, 240, 160));
Grass->Place(100);
// Some plants and in the caves.
LargeCaveMushroom->Place(40 + 6 * map_size, nil, {terraform = false});
Fern->Place(40 + 6 * map_size);
Mushroom->Place(40 + 6 * map_size);
Branch->Place(20 + 3 * map_size);
// Some objects in the earth.
PlaceObjects(Rock, 40 + 10 * map_size + Random(5),"Earth");
PlaceObjects(Firestone, 40 + 10 * map_size + Random(5), "Earth");
PlaceObjects(Loam, 40 + 10 * map_size + Random(5), "Earth");
// Underwater plants.
var place_rect = Shape->Rectangle(0, 2 * hgt / 3, wdt / 4, hgt / 3);
Seaweed->Place(12, place_rect);
Coral->Place(8, place_rect);
// Place some diamonds in the hard to reach location.
Diamond->Place(20, Shape->Rectangle(5 * wdt / 6, 0, wdt / 6, hgt / 4));
return;
}
private func InitAnimals(int map_size, int difficulty)
{
var wdt = LandscapeWidth();
var hgt = LandscapeHeight();
// Some butterflies as atmosphere.
for (var i = 0; i < 8; i++)
PlaceAnimal(Butterfly);
// Some wipfs underground.
Wipf->Place(10);
// Place some fishes and piranhas as difficulty prescribes it.
var place_rect = Shape->Rectangle(0, 2 * hgt / 3, wdt / 4, hgt / 3);
var fish_count = 15;
Fish->Place(fish_count * (3 - difficulty), place_rect);
Piranha->Place(fish_count * (difficulty - 1), place_rect);
// Bats on higher difficulty.
if (difficulty >= 2)
Bat->Place((difficulty - 1) * 12, Shape->Rectangle(2 * wdt / 3, 0, wdt / 3, hgt));
return;
}

View File

@ -0,0 +1,19 @@
# Scenario parameters: difficulty
Difficulty=Schwierigkeit
DescDifficulty=Setzt die Schwierigkeit dieser Runde.
DiffNormal=Normal
DescDiffNormal=Die Schwierigkeit dieser Runde ist normal.
DiffHard=Schwer
DescDiffHard=Die Schwierigkeit dieser Runde ist schwer.
DiffInsane=Irrsinnig
DescDiffInsane=Die Schwierigkeit dieser Runde ist irrsinnig.
# Scenario parameters: map size
MapSize=Kartengröße
DescMapSize=Setzt die Kartengröße dieser Runde.
MapSmall=Klein
DescMapSmall=Die Karte dieser Runde wird klein sein
MapAverage=Durchschnittlich
DescMapAverage=Die Karte dieser Runde wird durchschnittlich sein.
MapLarge=Groß
DescMapLarge=Die Karte dieser Runde wird groß sein.

View File

@ -0,0 +1,19 @@
# Scenario parameters: difficulty
Difficulty=Difficulty
DescDifficulty=Sets this round's difficulty.
DiffNormal=Normal
DescDiffNormal=This round's difficulty will be set to normal.
DiffHard=Hard
DescDiffHard=This round's difficulty will be set to hard.
DiffInsane=Insane
DescDiffInsane=This round's difficulty will be set to insane.
# Scenario parameters: map size
MapSize=Map size
DescMapSize=Sets this round's map size.
MapSmall=Small
DescMapSmall=This round's map will be small.
MapAverage=Average
DescMapAverage=This round's map will be average.
MapLarge=Large
DescMapLarge=This round's map will be large.

View File

@ -0,0 +1,9 @@
// Maximum pipe length depends on difficulty.
#appendto PipeLine
public func Definition(proplist def)
{
def.PipeMaxLength = def.PipeMaxLength * (12 - 2 * SCENPAR_Difficulty) / 10;
return;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@ -0,0 +1,2 @@
DE:Rasches Raffinieren
US:Rapid Refining

View File

@ -62,6 +62,7 @@ don't need to include this file or any of the files it includes. */
#include <string>
#include <utility>
#include <vector>
#include <math.h>
#include "lib/Standard.h"
#include "C4Prototypes.h"