User:Knorke/Sandbox3: Difference between revisions

From Spring
Jump to navigationJump to search
Knorke (talk | contribs)
mNo edit summary
8611 (talk | contribs)
expanded the part about more advanced scripting
 
(3 intermediate revisions by one other user not shown)
Line 1: Line 1:
__TOC__
==Interessted in creating a game on the spring engine?==


{{FIXME}} remove off-site links for images once uploading works again http://springrts.com/mantis/view.php?id=4612
Honestly, here isn't really the place to ask. The amount of available developers here is 0. We are all working on our own projects.


{{Caution|The Lua examples on this page has been tested and ''should'' work. However there is always the possibility that between copying from script into this text sometimes has been lost. If you notice something missing/wrong or something absolutely does not work then please tell in forum.}}
Sucks, but we are a small community. I would suggest posting on moddb. There are usually people there looking for work.
 
 
=Introduction=
This text talks about how to put features onto the map, using Lua.
 
The text can be read in two ways:
*As a Lua tutorial that expands on [[Lua:Tutorial_GettingStarted]] and {{FIXME}} that other page in sandbox
*For mappers as way to put features onto their map.
**In this case you can skip some of the "finer details" and just copypaste what seems interessting.
 
To follow the examples you can either...
* ...use your WIP map in .sdd format
* ...or unpack an existing map
* ...or use the test-mutator example from previous tutorial.
 
This text will be based around the map '''"DesertTriad"'''
 
=Creating a feature: Spring.CreateFeature=
 
Again we use our test-hill in the bottom right corner, of which we already know the coordinates:
 
[[File:DesertTriadHillCoordinates.jpg]]
 
'''We want to create a feature on this hill.'''
 
Create a file: <code>LuaRules\gadgets\MyLuaFeatureTesting.lua</code>
 
At [[Lua_SyncedCtrl#Feature_Handling]] we find the function <code>Spring.CreateFeature</code>
 
We need the parameters: ''featurename'' and ''x,y,z'' coordinates. The other parameters do not matter for now.
 
The map "DesertTriad" already includes some rock features that we could use. If using the test-mutator then we can also use whatever features the game includes, usually that is mostly wrecks of destroyed units.
 
Another group of features that we can use are the ones hardcoded in the engine: Geovents and trees.
 
We use a feature named "treetype0", it is a tree that comes with Spring engine.
 
Putting feature name and coordinates as parameters:
 
<code>Spring.CreateFeature ("treetype0", 3830,  390, 3600)</code>
 
==GameStart() or Initialize() or GamePreload() ?==
Where should we put the CreateFeature code?
 
There are three callin functions that appear suitable:
 
'''GameStart() Initialize() GamePreload() - can callin can I take?'''
 
* <code>GameStart()</code> is called as soon as the game begins.
**However, it is not yet called when the players are ingame but have not readied up yet.
** => If this callin was used then features would not appear until the game is started. Thus players could not see while choosing their start positions etc.
 
* <code>GamePreload()</code> is called as soon as soon the "game is ingame."
**Features spawned in this function will be visible before readying up.
**However, it is only called once: Right after the game has finished loading.
***That means if we want to test our Lua by typing ''/luarules reload'' then it will not trigger again.
 
* <code>Initialize()</code> is called when the gadget is initialized.
**Features spawned in this function will be visible before readying up.
**When typing ''/luarules reload'' the gadgets get initialized again and the function gets called again.
***This makes it suitable for testing.
 
=> Both <code>Initialize()</code> and <code>GamePreload()</code> can be used to spawn features without "delay."
 
=> Maybe <code>GamePreload()</code> is a bit "more technical correct" but <code>Initialize()</code> is more suited for testing, so we use that.
 
 
=complete gadget that plants a tree=
Thus the gadget now looks like this:
 
<code><pre>
function gadget:GetInfo()
  return {
    name      = "lua map stuff",
    desc      = "create some features",
    author    = "youe name",
    date      = "the day a lua was made",
    license  = "GNU GPL, v2",
    layer    = 0,
    enabled  = true,
  }
end
 
if not gadgetHandler:IsSyncedCode() then
 
function gadget:Initialize()
Spring.CreateFeature ("treetype0", 3830,  390, 3600)
end
 
else --unsynced part below
 
end
</pre></code>
 
The result:
 
[[File:OneTreeOnHill.jpg]]
 
[File:http://abload.de/img/onetreeonhillpcs0m.jpg]
 
{{Tip|'''Such simple code is really all it takes to put features on a map.'''
 
If one wanted one could now replaces the featurename and coordinates and use it to populate a map. In fact some maps do just that.}}
 
{{Tip|If you need to remove the tree for testing purposes, then reclaim it with the Commander or a constructor. (We will learn a better way soon)}}
 
 
==If a tree falls from the sky... does it make a sound?==
One interessting thing is that Spring physics do not get activated until the game actually starts.
 
Why is that relevant for spawning features?
 
If we use a too high y-coordinate (vertical axis / height) then the tree will hover in the air until the game starts.
 
[[File:FeaturesFallAtGameStart.gif]]
[File:http://abload.de/img/featuresfallatgamestaaesnm.gif]
 
And then it will suddendly fall down! (Look at above gif and watch the small countdown)
 
Similiar if we use too low y-coordinate then the tree will be created underground and only "pop" above ground once the game starts!
 
{{Tip|It is important to spawn features (and units) at the correct y-coordinate.}}
 
===GetGroundHeight===
Instead of reading the ground height from the tooltip we can also use the function <code>Spring.GetGroundHeight</code>:
 
<code><pre>
function gadget:Initialize()
x = 3830
z = 3600
y = Spring.GetGroundHeight (x,z)
Spring.CreateFeature ("treetype0", x,y,z)
end
</pre></code>
 
 
=Functional forest=
We can put our tree-spawner code into a function and call it from <code>Initialize()</code> multiple times with different coordinates:
<code><pre>
function PlantTree (x,z)
y = Spring.GetGroundHeight (x,z)
Spring.CreateFeature ("treetype0", x,y,z)
end
 
function gadget:Initialize()
PlantTree (3830,3600)
PlantTree (3850,3650)
PlantTree (3800,3650)
PlantTree (3810,320)
end
</pre></code>
 
Result:
 
[[File:TreesSpawnedOnHill.jpg]]
 
[File:http://abload.de/img/treesspawnedonhill74shj.jpg]
 
{{Tip|For practice try to make a function that spawns geothermal spots! The featurename is "geovent".}}
 
=Deleting all features from map=
Sometimes one might want to test the spawning of features again & again, on a clean plate.
The function below deletes all features on the map:
<code><pre>
function DestroyAllFeatures ()
local features = Spring.GetAllFeatures ()
for i in pairs(features) do
Spring.DestroyFeature (features[i])
end
end</pre></code>
 
It can be called in <code>Initialize()</code> ''before'' we spawn our features, to ensure we always test on an empty map.
 
When packing the map for release, one might want to comment it out again.
 
=A forest is many, many, many trees=
A handful of trees is often not enough for a map. Let's turn the whole bottom left corner of the map into a forest.
 
But planting a whole forest by typing coordinates of many trees seems very cumbersome?
 
What about randomizing the placement of the tree?
 
We look up the coordinates of that area: x-coordinate we want 3500 to 4000, for z-coordinate we want 3000 to 4000.
 
...and put it into a random number generator function.
 
<code><pre>
x = math.random (3500,4000)
z = math.random (3000,4000)
PlantTree (x,z)</pre></code>
 
We want to plant many trees. Instead of copy-pasting those lines 100 times, we use a loop:
<code><pre>
function gadget:Initialize()
DestroyAllFeatures ()
for i=1,100 do
x = math.random (3500,4000)
z = math.random (3000,4000)
PlantTree (x,z)
end
end</pre></code>
 
The result:
 
[[File:TreesSpawnedRandomly.jpg]]
 
[File:http://abload.de/img/treesspawnedrandomlyf3snt.jpg]
 
{{Tip|Forests are perfect for such random placement because the excact position of an indiviual tree does not matter much.}}
 
==Spawning features in patterns==
With a little creativity one can quickly fill the map in interessting ways.
 
For example try to create a function that spawns small clusters of trees.
 
Or imagine a function that spawns a big mineral crystal, surrounded by smaller rocks.
 
Or imagine an ore-mining facility with pieces of ore spread around it.
 
etc
 
Below is some examples that are hopefully understandable without too lenghty explainations.
 
==Lua rocks==
<code><pre>
function Rock (x,z)
local featureName = "pdrock" .. math.random (1, 4)
Spring.CreateFeature (featureName, x,Spring.GetGroundHeight (x,z),z, math.random (0,32000))
end
 
function rockCircle (x,z, radius, numberOfRocks)
for d = 0,360, 360/numberOfRocks do
local x = x + (math.sin (math.rad(d)) * radius)
local z = z + (math.cos (math.rad(d)) * radius)
Rock (x, z)
end
end
</pre></code>
Example usage:
<code><pre>rockCircle (2000,2000, 240, 25)</pre></code>
 
Result:
 
[[File:CircleOfRocks.jpg]]
[File:http://abload.de/img/circle_of_rockso5stt.jpg]
 
*Note 1: <code>local featureName = "pdrock" .. math.random (1, 4)</code>
The rock features included in map "DesertTriad" are named "pdrock1" to "pdrock4". So we take "pdrock" and append a random number in range of 1 to 4 to that base name.
 
*Note 2: The heading-parameter is used here too, randomized. The heading (rotation) of features is probally not measured 0 to 32000, but for this case that does not matter. If the number is too high it will simply "wrap around."
 
 
==In my maps I have seen rings of smoke through the trees...==
''...and the voices of those who stand looking.''
 
<code><pre>
function TreeSpiral (x,z)
for d = 0,720, 20 do
local x = x + (math.sin (math.rad(d)) * d/2)
local z = z + (math.cos (math.rad(d)) * d/2)
PlantTree (x,z)
end
y = Spring.GetGroundHeight (x,z)
Spring.CreateFeature ("geovent", x,y,z)
end
</pre></code>
 
Example usage:
<code><pre>TreeSpiral (2000,2000)</pre></code>
 
Result:
 
A geothermal vent in the middle of a spiral of trees.
 
[[File:TreeSpiralWithGeovent.jpg]]
 
[File:http://abload.de/img/treespiralwithgeoventu1swc.jpg]
 
=Typing so many coordinates sucks!=
We saw how some typing could be saved by using loops or random placement.
But surely there must be another way?
 
We could try to save the coordinates of all features of the map into a file.
 
If we wanted to do that, we would preferably save the coordinates in a way that can also be read back by another script.
For now that seems too difficult so we think of another way.
 
* think * think * think *
 
==Coordinates Helper widget==
Spring allows players to mark tactical locations on the map by holding ~ or ^ and clicking the mousewheel.
 
That might also be useful for marking locations of features, if we can just save those coordinates?
 
Let's see if we can create a small '''helper widget''' for that purpose.
 
Create a file <code>LuaUI\widgets\MapMarkerCoordinatesLogger.lua</code>
 
 
==MapDrawCmd: 1,2,3 testing!==
For start let's just see if we can print the coordinates of a map marker to the console.
 
We discover the callin: <code>MapDrawCmd(playerID, cmdType, px, py, pz, label)</code>
 
<code><pre>
function widget:GetInfo()
  return {
    name      = "MapMarkerCoordinatesLogger",
    desc      = "prints map marker coordinates",
    author    = "this lame tutorial or something",
    date      = "9 Nov 2010",
    license  = "GNU GPL, v2",
    layer    = -5,
    enabled  = true,
  }
end
 
function widget:MapDrawCmd(playerID, cmdType, px, py, pz, label)
Spring.Echo (px, py,pz)
end</pre></code>
 
Result:
 
[[File:MapMarkerCoordinatesToChat.jpg]]
 
[File:http://abload.de/img/mapmarkercoordinatestnwsng.jpg]
 
We see that the coordinates of the markers get printed to chat. (and thus to infolog.txt, too)
 
Not bad, but could be less bad.
 
==Turning test into something useful==
 
One obvious issue is that it does not just log placing markers but also outputs numbers when drawing lines or deleting.
 
Let's upgrade the widget so that:
#It only logs placing markers but not erasing or lines.
#instead of coordinates it outputs a whole function call like <code>PlantTree (3830,3600)</code>
#writes to a file instead of chat
#The functionname that gets written should be changeable from within game
#It has a cooler name
 
{{Tip|'''Before reading onwards: Try figure out a solution by yourself!'''
 
1) Sadly the wiki does not tell us how we know the difference between marker/lines/erasing. We must figure it out!
 
<code>MapDrawCmd(playerID, cmdType, px, py, pz, label)</code>
 
The paremter ''cmdType'' looks interessting. Let's just print that one too:
<code><pre>Spring.Echo (px, py,pz, cmdType)</pre></code>
 
We see that cmdType has the value "point" when placing markers and other values when doing other things.
 
So if cmdType is of wrong value we just return and do nothing:
<code><pre>
function widget:MapDrawCmd(playerID, cmdType, px, py, pz, label)
if cmdType ~= "point" then return end
...
</pre></code>
 
 
2) Creating the complete function call like "PlantTree (3830,3600)" is just a matter of appending some strings:
<code><pre>
local functionName = "PlantTree"
local s = functionName .. " (" .. px ..", " .. pz .. ")"
Spring.Echo (s)
</pre></code>
 
Note that we only save the X and Z coordinates because the Y-coordinate we can later get anyway with Spring.GetGroundHeight
 
3) About writing to file we can find many tutorials on web if we google something like "Lua write to file" or similiar.
 
The result:
<code><pre>
local file = io.open("myCoordinatesFile.txt", "a")
file:write(s)
file:flush()
file:close()
</pre></code>
 
4) Markers can have a label text attached to them. Let's use that to allow user to change the functionname.
<code><pre>if label ~= "" then functionName = label end</pre></code>
 
5) Since the widget writes points into files for features placement, we name it: '''Pifffp'''
 
 
 
=Complete Pifffp widget=
Putting everything together we get:
 
<code><pre>
function widget:GetInfo()
  return {
    name      = "Pifffp aka MapMarkerCoordinatesLogger",
    desc      = "prints Lua functions for placing featues",
    author    = "this lame tutorial or something",
    date      = "today",
    license  = "GNU GPL, v2",
    layer    = -5,
    enabled  = true,
  }
end
 
local functionName = "PlantTree"
function widget:MapDrawCmd(playerID, cmdType, px, py, pz, label)
if cmdType ~= "point" then return end
if label ~= "" then functionName = label end
--Spring.Echo (px, py,pz, cmdType, "-"..label.."-")
local s = functionName .. " (" .. px ..", " .. pz .. ")"
Spring.Echo (s)
local file = io.open("myCoordinatesFile.txt", "a")
file:write(s .. "\n")
file:flush()
file:close()
end
</pre></code>
 
 
==Using the Pifffp tool as a user/mapper==
Activate the widget in F11 list.
 
Now start placing map markers. Every time you place a marker the coordinates get logged into '''myCoordinatesFile.txt''', already formated as parameters to a function call.
 
(The file should usually be written next to spring.exe)
 
To change the name of the function place a marker with a label text. All folling markers will then use that text, until a new label text is used.
 
Example usage:
 
[[File:Pifffp_Markers.jpg]]
 
[File:http://abload.de/img/pifffp_markers7lsq1.jpg]
 
Example output:
 
<code><pre>
PlantTree (1977, 3569)
PlantTree (1933, 3372)
PlantTree (1901, 3489)
PlantTree (1850, 3612)
PlantTree (2008, 3643)
PlantTree (2101, 3497)
PlantTree (2112, 3637)
PlantTree (2228, 3605)
PlantTree (2226, 3464)
PlantTree (2043, 3406)
PlantTree (2163, 3357)
PlantTree (2293, 3278)
PlantTree (2125, 3179)
PlantTree (1986, 3193)
Rock (1526, 3099)
Rock (1576, 3024)
Rock (1500, 2926)
Rock (1373, 3035)
Rock (1438, 3212)
Rock (1556, 3264)
Rock (1699, 3122)
Rock (1670, 2919)
Rock (1398, 2918)
Geovent (1818, 3298)
rockCircle (2308, 2788)
</pre></code>
 
We recognize these functions from the beginning of the tutorial.
 
Hopefully still have them ready in our <code>LuaRules\gadgets\MyLuaFeatureTesting.lua</code> file so that we only need to paste the Pifffp output into <code>Initialize</code>
 
(I you are making a real map you might want to use a better filename like "spawnMyFeatures.lua")
 
Note that for the function rockCircle we still need to fill in the parameters radius and amount of rocks like so:
<code><pre>
rockCircle (2308, 2788, 200, 25)</pre></code>
 
Example result:
 
[[File:Pifffp_ResultFeatures.jpg]]
 
[File:http://abload.de/img/pifffp_resultfeaturesiisdi.jpg]
 
Now we have created ourselves a quick way to put features onto map with Lua.

Latest revision as of 18:12, 14 April 2015

Interessted in creating a game on the spring engine?

Honestly, here isn't really the place to ask. The amount of available developers here is 0. We are all working on our own projects.

Sucks, but we are a small community. I would suggest posting on moddb. There are usually people there looking for work.