26.07.2026, 09:42
(Dieser Beitrag wurde zuletzt bearbeitet: 26.07.2026, 23:37 von Manfred Aabye.)
Auto Plant Skript 2.36
Neue Version mit Berechtigungsprüfung und besserer Terrainbearbeitung.
Das neue Paket steht wieder auf Wright Plaza.
Dies macht jetzt einen interessanten tiefen Wald, der nicht so langweilig wirkt.
Die Geschwindigkeit des Pflanzens habe ich jetzt auf 1 Sekunde also doppelt so schnell gestellt.
AutoPlant236.lsl
Neue Version mit Berechtigungsprüfung und besserer Terrainbearbeitung.
Das neue Paket steht wieder auf Wright Plaza.
Dies macht jetzt einen interessanten tiefen Wald, der nicht so langweilig wirkt.
Die Geschwindigkeit des Pflanzens habe ich jetzt auf 1 Sekunde also doppelt so schnell gestellt.
AutoPlant236.lsl
PHP-Code:
// ============================================================
// AutoPlant236.lsl
// OPENSIM WALDPFLANZER SKRIPT MIT DIALOG-MENÜ
// ============================================================
// ===== GLOBALE VARIABLEN =====
integer gIntX;
integer gIntY;
integer gIntZ;
integer gIntStartX;
integer gIntStartY;
integer gIntEndX;
integer gIntEndY;
float gFltPlantProbability;
float gFltRandMax;
float FLATTEN_HEIGHT = 21.0; // Standard-Höhe zum Einebnen
float ROUGHNESS_AMOUNT = 0.15; // 0.0 = keine Änderung, 1.0 = maximale Änderung
float MAX_HEIGHT_CHANGE = 2.0; // Maximale Höhenänderung in Metern
float NOISE_SCALE = 10.0; // Skalierung für Perlin-Noise
float NOISE_AMPLITUDE = 3.0; // Amplitude für Perlin-Noise
float BASE_HEIGHT = 20.0; // Basis-Höhe für die Region
// ===== START/STOP-FUNKTION =====
integer gIntRunning = FALSE;
key gKeyToucher;
integer gIntCurrentX;
integer gIntCurrentY;
float Pflanszeit = 0.5; // Geschwindigkeit der Pflanzenbildung
// ===== PFLANZPARAMETER =====
integer PFLANZABSTAND = 6;
// ===== KOMBINIERTE LISTE FÜR ALLE OBJEKTE =====
// Format: [Name, Höhe, Priorität, X-Rotation, Y-Rotation, Z-Rotation, Z-Offset]
// Prioritäten basierend auf deutschen Waldanteilen:
// Kiefer: 22%, Fichte: 21%, Rotbuche: 17%, Eiche: 12%, Birke: 4.7%, Ahorn: 3.0%, Lärche: 2.9%, Esche: 1.8%
list ALLE_OBJEKTE = [
// Bäume mit deutschen Waldanteilen
"European-Beech-tree-A", 10.0, 17, 0.0, 0.0, 0.0, 0.0,
"European-Beech-tree-B", 10.0, 17, 0.0, 0.0, 0.0, 0.0,
"European-Oak-tree", 12.0, 12, 0.0, 0.0, 0.0, 0.0,
"European-Larch-tree", 15.0, 3, 90.0, 0.0, 0.0, 0.0,
"European-Pine-tree", 12.0, 22, 0.0, 0.0, 0.0, 0.0,
"European-Spruce-tree", 15.0, 21, 0.0, 0.0, 0.0, 0.0,
"European-Birch-tree", 10.0, 5, 90.0, 0.0, 0.0, 0.0,
"European-Maple-tree", 12.0, 3, 0.0, 0.0, 0.0, 0.0,
"European-Ash-tree", 13.0, 2, 0.0, 0.0, 0.0, 0.0,
// Büsche (Priorität 8)
"Buesche-1", 3.0, 8, 90.0, 0.0, 0.0, -2.00,
"Buesche-2", 3.0, 8, 90.0, 0.0, 0.0, -2.00,
"Buesche-3", 3.0, 8, 90.0, 0.0, 0.0, -2.00,
"Buesche-4", 3.0, 8, 90.0, 0.0, 0.0, -2.00,
// Farne (Priorität 6)
"Fern-1", 1.2, 6, 90.0, 0.0, 0.0, -0.70,
"Fern-2", 1.2, 6, 90.0, 0.0, 0.0, -0.70,
"Fern-3", 1.2, 6, 90.0, 0.0, 0.0, -0.70,
"Fern-4", 1.2, 6, 90.0, 0.0, 0.0, -0.70,
// Todholz (Priorität 4)
"Todholz-1", 1.5, 4, 0.0, 0.0, 0.0, -1.30,
"Todholz-2", 1.5, 4, 0.0, 0.0, 0.0, -1.30,
"Todholz-3", 1.5, 4, 0.0, 0.0, 0.0, -1.30,
"Todholz-4", 1.5, 4, 0.0, 0.0, 0.0, -1.30,
// Gräser (Priorität 5)
"Grass-1", 0.5, 5, 0.0, 0.0, 0.0, -0.10,
"Grass-2", 0.5, 5, 0.0, 0.0, 0.0, -0.10,
"Grass-3", 0.5, 5, 180.0, 0.0, 0.0, 0.70,
"Grass-4", 0.5, 5, 0.0, 0.0, 0.0, -0.10,
// Steine (Priorität 3)
"Stone-1", 0.8, 3, 0.0, 0.0, 0.0, -0.40,
"Stone-2", 0.8, 3, 0.0, 0.0, 0.0, -3.00,
"Stone-3", 0.8, 3, 0.0, 0.0, 0.0, -0.40,
"Stone-4", 0.8, 3, 0.0, 0.0, 0.0, -0.40
];
// ===== TERRAIN-TEXTUREN =====
float TextureLow = 10.0;
float TextureHigh = 30.0;
key Terrain_Dirt = "1e78c37b-90a1-40d3-a913-31c965309679";
key Terrain_Grass = "a9b4d0d5-5679-4fd5-b9e3-ac4367ea9b95";
key Terrain_Mountain = "795204d3-bd28-467c-b526-08b95090841e";
key Terrain_Rock = "4b012b05-a357-46af-8e6e-e8a9aa501a8c";
// ===== DIALOG-MENÜ VARIABLEN =====
integer MENU_CHANNEL;
integer listenHandle;
string MENU_TEXT = "\n? Wähle eine Aktion:";
list MENU_BUTTONS = ["? Aufrauhen", "?️ Einebnen","❌ Schließen", "? Pflanzen", "? Terrain"];
// ===== HILFSFUNKTIONEN =====
float getObjectHeight(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 1);
return 4.0;
}
float getObjectRotationX(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 3);
return 0.0;
}
float getObjectRotationY(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 4);
return 0.0;
}
float getObjectRotationZ(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 5);
return 0.0;
}
float getObjectZOffset(string objectName)
{
integer index = llListFindList(ALLE_OBJEKTE, [objectName]);
if (index != -1)
return llList2Float(ALLE_OBJEKTE, index + 6);
return 0.0;
}
rotation getRotation(float gradX, float gradY, float gradZ)
{
return llEuler2Rot(<gradX * DEG_TO_RAD, gradY * DEG_TO_RAD, gradZ * DEG_TO_RAD>);
}
// ===== BERECHTIGUNGSPRÜFUNG =====
integer checkLandPermissions(key avatar)
{
// 1. Prüfen, ob der Nutzer der Besitzer des Landes ist
key parcelID = osGetParcelID();
list parcelDetails = osGetParcelDetails(parcelID, [PARCEL_DETAILS_OWNER]);
key parcelOwner = llList2Key(parcelDetails, 0);
if (parcelOwner == avatar)
{
return TRUE;
}
// 2. Prüfen, ob der Nutzer in der Gruppenliste des Grundstücks ist
parcelDetails = osGetParcelDetails(parcelID, [PARCEL_DETAILS_GROUP]);
key parcelGroup = llList2Key(parcelDetails, 0);
if (parcelGroup != NULL_KEY)
{
if (llSameGroup(avatar))
{
return TRUE;
}
}
// 3. Prüfen, ob der Nutzer allgemeine Baurechte hat
integer parcelFlags = llGetParcelFlags(llGetPos());
if (parcelFlags & PARCEL_FLAG_ALLOW_CREATE_OBJECTS)
{
return TRUE;
}
return FALSE;
}
// ===== TERRAIN-FUNKTIONEN =====
setTerrain()
{
if (!checkLandPermissions(llGetOwner()))
{
llOwnerSay("❌ Keine Berechtigung für Terrain-Änderungen!");
return;
}
list textures = [Terrain_Dirt, Terrain_Grass, Terrain_Mountain, Terrain_Rock];
integer types = 2;
osSetTerrainTextures(textures, types);
osSetTerrainTexture(0, Terrain_Dirt);
osSetTerrainTextureHeight(0, TextureLow, TextureHigh);
osSetTerrainTexture(1, Terrain_Grass);
osSetTerrainTextureHeight(1, TextureLow, TextureHigh);
osSetTerrainTexture(2, Terrain_Mountain);
osSetTerrainTextureHeight(2, TextureLow, TextureHigh);
osSetTerrainTexture(3, Terrain_Rock);
osSetTerrainTextureHeight(3, TextureLow, TextureHigh);
llModifyLand(LAND_NOISE, 0);
osTerrainFlush();
llOwnerSay("✅ Terrain-Texturen wurden gesetzt und aufgeraut!");
}
// ===== FUNKTION ZUM EINEBNEN DER REGION =====
flattenRegion(float targetHeight)
{
// Prüfen ob der Aufrufer Berechtigungen hat
if (!checkLandPermissions(llGetOwner()))
{
llOwnerSay("❌ Keine Berechtigung zum Einebnen der Region!");
return;
}
llOwnerSay("?️ Region wird auf " + (string)targetHeight + " Meter eingeebnet...");
// Region-Größe ermitteln
vector regionSize = osGetRegionSize();
integer regionX = (integer)regionSize.x;
integer regionY = (integer)regionSize.y;
// In Blöcken arbeiten um Heap-Overflow zu vermeiden
integer blockSize = 10;
integer x;
integer y;
for (x = 0; x < regionX; x += blockSize)
{
for (y = 0; y < regionY; y += blockSize)
{
integer endX = x + blockSize;
integer endY = y + blockSize;
if (endX > regionX) endX = regionX;
if (endY > regionY) endY = regionY;
integer ix;
integer iy;
for (ix = x; ix < endX; ix++)
{
for (iy = y; iy < endY; iy++)
{
osSetTerrainHeight(ix, iy, targetHeight);
}
}
llSleep(0.01);
}
}
// Änderungen ans Terrain übergeben
osTerrainFlush();
llOwnerSay("✅ Region wurde auf " + (string)targetHeight + " Meter eingeebnet!");
}
// ===== FUNKTION ZUM AUFRAUHEN DER REGION (OPTIMIERT) =====
roughenRegion(float roughnessAmount, float maxHeightChange)
{
if (!checkLandPermissions(llGetOwner()))
{
llOwnerSay("❌ Keine Berechtigung zum Aufrauhen der Region!");
return;
}
llOwnerSay("?️ Region wird aufgeraut (Stärke: " + (string)(roughnessAmount * 100) + "%, max. Höhenänderung: " + (string)maxHeightChange + "m)...");
// Region-Größe ermitteln
vector regionSize = osGetRegionSize();
integer regionX = (integer)regionSize.x;
integer regionY = (integer)regionSize.y;
// In Blöcken arbeiten um Heap-Overflow zu vermeiden
integer blockSize = 10;
integer x;
integer y;
for (x = 0; x < regionX; x += blockSize)
{
for (y = 0; y < regionY; y += blockSize)
{
// Nur innerhalb der Region bleiben
integer endX = x + blockSize;
integer endY = y + blockSize;
if (endX > regionX) endX = regionX;
if (endY > regionY) endY = regionY;
// Block verarbeiten
integer ix;
integer iy;
for (ix = x; ix < endX; ix++)
{
for (iy = y; iy < endY; iy++)
{
float currentHeight = osGetTerrainHeight(ix, iy);
// Zufällige Höhenänderung
float heightChange = (llFrand(1.0) * 2.0 - 1.0) * maxHeightChange * roughnessAmount;
float newHeight = currentHeight + heightChange;
// Begrenzen auf sinnvolle Werte
if (newHeight < 0.0) newHeight = 0.0;
if (newHeight > 100.0) newHeight = 100.0;
osSetTerrainHeight(ix, iy, newHeight);
}
}
// Nach jedem Block kurz pausieren für Heap-Management
llSleep(0.01);
}
}
// Änderungen ans Terrain übergeben
osTerrainFlush();
llOwnerSay("✅ Region wurde erfolgreich aufgeraut!");
}
// ===== PFLANZEN-FUNKTIONEN =====
plantOneTree()
{
if (!gIntRunning) return;
if (gIntCurrentX >= gIntEndX || gIntCurrentY >= gIntEndY)
{
gIntRunning = FALSE;
llOwnerSay("✅ Pflanzvorgang abgeschlossen!");
llSetText("Fertig - Klicke für neues Menü", <0,1,0>, 1.0);
llSetTimerEvent(0.0);
return;
}
gIntX = gIntCurrentX;
gIntY = gIntCurrentY;
llSetRegionPos(<gIntX, gIntY, gIntZ>);
llSetText("X: " + (string)gIntX + " Y: " + (string)gIntY, <1,1,1>, 1.0);
// Zufällig entscheiden, ob überhaupt etwas gepflanzt wird
float fltDoIRez = llFrand(1.0);
if (fltDoIRez >= gFltPlantProbability)
{
gIntCurrentY += PFLANZABSTAND;
if (gIntCurrentY >= gIntEndY)
{
gIntCurrentY = gIntStartY + (PFLANZABSTAND / 2);
gIntCurrentX += PFLANZABSTAND;
}
return;
}
// ===== GEWICHTETE AUSWAHL =====
list verfuegbareObjekte = [];
integer listLength = llGetListLength(ALLE_OBJEKTE) / 7;
integer i;
for (i = 0; i < listLength; i++)
{
string objName = llList2String(ALLE_OBJEKTE, i * 7);
if (llGetInventoryType(objName) == INVENTORY_OBJECT)
{
float objHeight = llList2Float(ALLE_OBJEKTE, (i * 7) + 1);
integer objPriority = llList2Integer(ALLE_OBJEKTE, (i * 7) + 2);
float objRotationX = llList2Float(ALLE_OBJEKTE, (i * 7) + 3);
float objRotationY = llList2Float(ALLE_OBJEKTE, (i * 7) + 4);
float objRotationZ = llList2Float(ALLE_OBJEKTE, (i * 7) + 5);
float objZOffset = llList2Float(ALLE_OBJEKTE, (i * 7) + 6);
verfuegbareObjekte += [objName, objHeight, objPriority, objRotationX, objRotationY, objRotationZ, objZOffset];
}
}
if (llGetListLength(verfuegbareObjekte) == 0)
{
llOwnerSay("⚠️ Keine Objekte im Inventar gefunden!");
gIntRunning = FALSE;
llSetTimerEvent(0.0);
return;
}
// Gesamtgewicht berechnen
integer availableCount = llGetListLength(verfuegbareObjekte) / 7;
integer totalWeight = 0;
for (i = 0; i < availableCount; i++)
{
totalWeight += llList2Integer(verfuegbareObjekte, (i * 7) + 2);
}
// Zufällige Auswahl basierend auf Gewicht
integer randomWeight = (integer)llFrand(totalWeight);
integer selectedIndex = 0;
integer cumulativeWeight = 0;
for (i = 0; i < availableCount; i++)
{
integer priority = llList2Integer(verfuegbareObjekte, (i * 7) + 2);
cumulativeWeight += priority;
if (randomWeight < cumulativeWeight)
{
selectedIndex = i;
i = availableCount;
}
}
// Objekt auswählen
string gStrObjectName = llList2String(verfuegbareObjekte, selectedIndex * 7);
float objectHeight = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 1);
float rotationX = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 3);
float rotationY = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 4);
float rotationZ = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 5);
float zOffset = llList2Float(verfuegbareObjekte, (selectedIndex * 7) + 6);
// Position setzen
vector vecNowPos = llGetPos();
float fltLandHeight = osGetTerrainHeight((integer)vecNowPos.x, (integer)vecNowPos.y);
integer intNewX = (integer)vecNowPos.x;
integer intNewY = (integer)vecNowPos.y;
integer intNewZ = (integer)fltLandHeight;
vecNowPos = <intNewX, intNewY, intNewZ>;
llSetRegionPos(vecNowPos);
// ===== ROTATION =====
// Basis-Rotation aus der Liste (X und Y fest, Z wird durch zufälligen Wert ersetzt)
rotation baseRotation = getRotation(rotationX, rotationY, 0.0);
// Zufällige Z-Rotation für natürlichen Wald (0-360 Grad)
float randomZRotation = llFrand(360.0);
rotation randomRotation = llEuler2Rot(<0.0, 0.0, randomZRotation * DEG_TO_RAD>);
// Beide Rotationen kombinieren
rotation finalRotation = baseRotation * randomRotation;
// Objekt rezzen mit kombinierter Rotation
llRezObject(gStrObjectName,
llGetPos() + <0.0, 0.0, objectHeight + zOffset>,
<0.0, 0.0, 0.0>,
finalRotation,
0);
llModifyLand(LAND_NOISE, 0);
llSleep(0.1);
// Debug-Ausgabe (optional)
// llOwnerSay("? " + gStrObjectName + " | X-Rot: " + (string)rotationX + "° | Y-Rot: " + (string)rotationY + "° | Z-Rot: " + (string)(randomZRotation) + "° | Z-Off: " + (string)zOffset);
// Zur nächsten Position
gIntCurrentY += PFLANZABSTAND;
if (gIntCurrentY >= gIntEndY)
{
gIntCurrentY = gIntStartY + (PFLANZABSTAND / 2);
gIntCurrentX += PFLANZABSTAND;
}
}
startPlanting(key toucher)
{
if (!checkLandPermissions(toucher))
{
llOwnerSay("❌ Du hast nicht die erforderlichen Landrechte zum Pflanzen!");
llOwnerSay("Benötigte Berechtigungen (mindestens eine):");
llOwnerSay(" • Grundstücksbesitzer");
llOwnerSay(" • Mitglied der Grundstücksgruppe");
llOwnerSay(" • Baurechte auf dem Grundstück");
return;
}
vector regionSize = osGetRegionSize();
gIntStartX = 0;
gIntStartY = 0;
gIntEndX = (integer)regionSize.x;
gIntEndY = (integer)regionSize.y;
gFltPlantProbability = 0.7;
gIntZ = 0;
gIntRunning = TRUE;
gIntCurrentX = gIntStartX + (PFLANZABSTAND / 2);
gIntCurrentY = gIntStartY + (PFLANZABSTAND / 2);
// Anzahl der verfügbaren Objekte zählen
integer availableCount = 0;
integer listLength = llGetListLength(ALLE_OBJEKTE) / 7;
integer i;
for (i = 0; i < listLength; i++)
{
string objName = llList2String(ALLE_OBJEKTE, i * 7);
if (llGetInventoryType(objName) == INVENTORY_OBJECT)
availableCount++;
}
llOwnerSay("? Starte Pflanzvorgang mit " + (string)availableCount + " verfügbaren Objekten...");
llOwnerSay("? Waldzusammensetzung (deutscher Wald):");
llOwnerSay(" • Kiefer: 22%, Fichte: 21%, Rotbuche: 17%");
llOwnerSay(" • Eiche: 12%, Birke: 4.7%, Ahorn: 3.0%");
llOwnerSay(" • Lärche: 2.9%, Esche: 1.8%");
llOwnerSay("? Berechtigung für " + llKey2Name(toucher) + " verifiziert.");
llSetText("? Pflanze... Klicke zum Stoppen", <1,1,0>, 1.0);
llSetTimerEvent(Pflanszeit);
}
// ===== HAUPTTEIL DES SKRIPTS =====
default
{
state_entry()
{
MENU_CHANNEL = -1 - (integer)("0x" + llGetSubString((string)llGetKey(), -7, -1));
llOwnerSay("? Waldpflanzer bereit. Klicke für Menü.");
llSetText("Bereit - Klicke für Menü", <0,1,0>, 1.0);
llSetTimerEvent(0.0);
}
touch_start(integer num_detected)
{
if (gIntRunning)
{
gIntRunning = FALSE;
llSetTimerEvent(0.0);
llListenRemove(listenHandle);
llOwnerSay("⏹️ Pflanzvorgang wurde gestoppt!");
llSetText("Gestoppt - Klicke für neues Menü", <1,0,0>, 1.0);
return;
}
key toucher = llDetectedKey(0);
gKeyToucher = toucher;
if (!checkLandPermissions(toucher))
{
string avatarName = llKey2Name(toucher);
llOwnerSay("❌ " + avatarName + " hat nicht die erforderlichen Landrechte!");
llOwnerSay("Benötigte Berechtigungen (mindestens eine):");
llOwnerSay(" • Grundstücksbesitzer");
llOwnerSay(" • Mitglied der Grundstücksgruppe");
llOwnerSay(" • Baurechte auf dem Grundstück");
return;
}
llListenRemove(listenHandle);
listenHandle = llListen(MENU_CHANNEL, "", toucher, "");
llDialog(toucher, MENU_TEXT, MENU_BUTTONS, MENU_CHANNEL);
llSetTimerEvent(60.0);
}
listen(integer channel, string name, key id, string message)
{
llSetTimerEvent(0.0);
llListenRemove(listenHandle);
if (message == "? Pflanzen")
{
if (!checkLandPermissions(id))
{
llOwnerSay("❌ Du hast nicht die erforderlichen Landrechte zum Pflanzen!");
return;
}
llOwnerSay("? Starte Pflanzvorgang...");
startPlanting(id);
}
else if (message == "? Terrain")
{
if (!checkLandPermissions(id))
{
llOwnerSay("❌ Du hast nicht die erforderlichen Landrechte für Terrain-Änderungen!");
return;
}
setTerrain();
llDialog(id, "✅ Terrain-Texturen wurden aktualisiert.", ["OK"], MENU_CHANNEL);
}
else if (message == "?️ Einebnen")
{
if (!checkLandPermissions(id))
{
llOwnerSay("❌ Du hast nicht die erforderlichen Landrechte zum Einebnen!");
return;
}
flattenRegion(FLATTEN_HEIGHT);
llDialog(id, "✅ Region wurde auf " + (string)FLATTEN_HEIGHT + " Meter eingeebnet.", ["OK"], MENU_CHANNEL);
}
else if (message == "? Aufrauhen")
{
if (!checkLandPermissions(id))
{
llOwnerSay("❌ Du hast nicht die erforderlichen Landrechte zum Aufrauhen!");
return;
}
// Einfache Variante
roughenRegion(ROUGHNESS_AMOUNT, MAX_HEIGHT_CHANGE);
llDialog(id, "✅ Region wurde aufgeraut.", ["OK"], MENU_CHANNEL);
}
else if (message == "❌ Schließen")
{
llOwnerSay("Menü geschlossen.");
}
else if (message == "OK")
{
llOwnerSay("✅ OK.");
}
}
timer()
{
if (gIntRunning)
{
plantOneTree();
}
else
{
llSetTimerEvent(0.0);
}
}
}
Ein Metaversum sind viele kleine Räume, die nahtlos aneinander passen,
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.
sowie direkt sichtbar und begehbar sind, als wäre es aus einem Guss.

