Script v1.7.1.0
- AI
- Animals
- Contracts
- Debug
- Economy
- Effects
- Events
- Farms
- GUI
- Handtools
- I3d
- Materials
- Misc
- Objects
- AnimatedMapObject
- AnimatedObject
- AnimatedObjectEvent
- Bale
- Basketball
- Bga
- BgaSellStation
- BunkerSilo
- BuyingStation
- DigitalDisplay
- DogBall
- HelpIcons
- LoadingStation
- NightIllumination
- Nightlight2
- NightlightFlicker
- PhysicsObject
- Rotator
- SellingStation
- SimParticleSystem
- Storage
- StorageSystem
- SunAdmirer
- TourIcons
- UnloadingStation
- VehicleSellingPoint
- WildlifeSpawner
- Placeables
- Player
- Shop
- Sounds
- Specializations
- Triggers
- Utils
- Vehicles
- Weather
Engine v1.7.1.0
- AI
- Animation
- Camera
- Entity
- Fillplanes
- General
- I3D
- Input
- Lighting
- Math
- Network
- Node
- Overlays
- Particle System
- Physics
- Rendering
- Scenegraph
- Shape
- Sound
- Spline
- String
- Terrain Detail
- Text Rendering
- Tire Track
- XML
- general
Foundation Reference
WildlifeSpawner
DescriptionWildlife Spawner classXML Configuration Parameters
wildlifeSpawner#maxCost | / max cost of all animals generated by the spawner |
wildlifeSpawner#checkTimeInterval | / how often creation process is executed (in seconds) |
wildlifeSpawner.area#areaSpawnRadius | / radius of the main spawn circle, testing circles are placed on this radius |
wildlifeSpawner.area#areaMaxRadius | / radius where animals are removed |
wildlifeSpawner.area#spawnCircleRadius | / radius of testing circles |
wildlifeSpawner.area.species#name | / name of the animal to spawn |
wildlifeSpawner.area.species#config | / configuration file of the animal to spawn |
wildlifeSpawner.area.species.spawnRules#hours | / daytimes where we can spawn an animal |
wildlifeSpawner.area.species.spawnRules#onField | / if true, will spawn on field ground |
wildlifeSpawner.area.species.spawnRules#hasTrees | / if true, will spawn if trees in testing circle |
wildlifeSpawner.area.species.cost | / cost of one animal |
wildlifeSpawner.area.species.maxCount | / maximal amount of animal to spawn |
wildlifeSpawner.area.species.spawnCount | / how many animals to spawn in one group |
wildlifeSpawner.area.species.groupSpawnRadius | / radius within which the animals are spawned |
Functions
- addAreaOfInterest
- animalExists
- checkArea
- checkAreas
- checkHours
- consoleCommandAddWildlifeAnimalToDebug
- consoleCommandRemoveWildlifeAnimalToDebug
- consoleCommandToggleShowWildlife
- consoleCommandToggleShowWildlifeAnimation
- consoleCommandToggleShowWildlifeId
- consoleCommandToggleShowWildlifeSteering
- countAnimalsTobeSpawned
- countTrees
- debugDraw
- delete
- getIsInWater
- getIsOnField
- getPlayerCenter
- isInDebugList
- loadMapData
- new
- onConnectionClosed
- onGhostAdd
- onGhostRemove
- parseHours
- removeAllAnimals
- removeFarAwayAnimals
- spawnAnimals
- treeCountTestCallback
- trySpawnAtArea
- unloadMapData
- update
- updateAreaOfInterest
- updateSpawner
addAreaOfInterest
DescriptionAdds an area of interest to checkDefinition
addAreaOfInterest(float liveTime, float posX, float posZ, float radius)Arguments
float | liveTime | how long the area is available |
float | posX | x world position |
float | posZ | z world position |
float | radius | radius of the area in m |
644 | function WildlifeSpawner:addAreaOfInterest(liveTime, posX, posZ, radius) |
645 | if #self.areasOfInterest <= self.maxAreaOfInterest then |
646 | local info = {} |
647 | info.liveTime = liveTime |
648 | info.positionX = posX |
649 | info.positionZ = posZ |
650 | info.radius = radius |
651 | info.timeToLive = self.areaOfInterestliveTime |
652 | table.insert(self.areasOfInterest, info) |
653 | end |
654 | end |
animalExists
DescriptionDefinitionanimalExists()Return Values
bool |
707 | function WildlifeSpawner:animalExists(spawnId, animalId) |
708 | for _, area in pairs(self.areas) do |
709 | for _, species in pairs(area.species) do |
710 | if species.classType == "companionAnimal" then |
711 | for _, spawn in pairs(species.spawned) do |
712 | if spawn.spawnId == spawnId and animalId < spawn.count then |
713 | return true |
714 | end |
715 | end |
716 | end |
717 | end |
718 | end |
719 | return false |
720 | end |
checkArea
DescriptionCheck area with to see if we should spawn animals (trees amount, is a field, is in water, hours of the day)Definition
checkArea(float x, float y, float z, table rules, float radius)Arguments
float | x | x world position from which areas are checked |
float | y | y world position from which areas are checked |
float | z | z world position from which areas are checked |
table | rules | rules to check against for the species |
float | radius | radius of the test |
bool | returns | true if all tests are validated |
446 | function WildlifeSpawner:checkArea(x, y, z, rules, radius) |
447 | local nbTrees = self:countTrees(x, y, z, radius) |
448 | local isOnField = self:getIsOnField(x, y, z) |
449 | local isInWater = self:getIsInWater(x, y, z) |
450 | local hoursTest = self:checkHours(rules) |
451 | local onFieldTest = (rules.onField and isOnField) or (not rules.onField and not isOnField) |
452 | local hasTreesTest = (rules.hasTrees and nbTrees > 0) or (not rules.hasTrees and nbTrees == 0) |
453 | local inWaterTest = (rules.inWater and isInWater) or (not rules.inWater and not isInWater) |
454 | if self.bypassRules or (hoursTest and onFieldTest and hasTreesTest and inWaterTest) then |
455 | return true |
456 | end |
457 | return false |
458 | end |
checkAreas
DescriptionFor each areas, we try to spawn first in areas of interests that have been registered by workAreas. If there is no spawn then, we try to spawn at a random position around the player.Definition
checkAreas(float x, float y, float z)Arguments
float | x | x world position from which areas are checked |
float | y | y world position from which areas are checked |
float | z | z world position from which areas are checked |
411 | function WildlifeSpawner:checkAreas(x, y, z) |
412 | local testX = x |
413 | local testZ = z |
414 | local testY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, testX, 0, testZ) + 0.5 |
415 | |
416 | for _, area in pairs(self.areas) do |
417 | local hasSpawned = false |
418 | for _, interestArea in pairs(self.areasOfInterest) do |
419 | local distSq = (testX - interestArea.positionX) * (testX - interestArea.positionX) + (testZ - interestArea.positionZ) * (testZ - interestArea.positionZ) |
420 | if (distSq < (area.areaSpawnRadius * area.areaSpawnRadius)) then |
421 | hasSpawned = self:trySpawnAtArea(area.species, interestArea.radius, testX, testY, testZ) |
422 | if hasSpawned then |
423 | break |
424 | end |
425 | end |
426 | end |
427 | if not hasSpawned then |
428 | local angle = math.rad(math.random(0, 360)) |
429 | testX = x + area.areaSpawnRadius * math.cos(angle) - area.areaSpawnRadius * math.sin(angle) |
430 | testZ = z + area.areaSpawnRadius * math.cos(angle) + area.areaSpawnRadius * math.sin(angle) |
431 | testY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, testX, 0, testZ) + 0.5 |
432 | |
433 | self:trySpawnAtArea(area.species, area.spawnCircleRadius, testX, testY, testZ) |
434 | end |
435 | end |
436 | end |
checkHours
DescriptionCheck daytime hour is in one of the valid hour rangesDefinition
checkHours()Return Values
bool | returns | true if current time in hours range |
514 | function WildlifeSpawner:checkHours(rules) |
515 | local currentHour = math.floor(g_currentMission.environment.dayTime / (60 * 60 * 1000)) |
516 | |
517 | for _, hours in pairs(rules.hours) do |
518 | if (currentHour >= hours.from and currentHour <= hours.to) then |
519 | return true |
520 | end |
521 | end |
522 | return false |
523 | end |
consoleCommandAddWildlifeAnimalToDebug
DescriptionDefinitionconsoleCommandAddWildlifeAnimalToDebug()Return Values
string | that | will be displayed on console |
737 | function WildlifeSpawner:consoleCommandAddWildlifeAnimalToDebug(spawnId, animalId) |
738 | local argsTest = true |
739 | spawnId = tonumber(spawnId) |
740 | if spawnId == nil then |
741 | argsTest = false |
742 | end |
743 | animalId = tonumber(animalId) |
744 | if animalId == nil then |
745 | argsTest = false |
746 | end |
747 | |
748 | if argsTest and self:animalExists(spawnId, animalId) then |
749 | table.insert(self.debugAnimalList, {spawnId = spawnId, animalId = animalId}) |
750 | return string.format("-- added [spawn(%d)][animal(%d)] to debug list.", spawnId, animalId) |
751 | else |
752 | return string.format("-- gsAddWildlifeAnimalToDebug [spawnId][animalId]") |
753 | end |
754 | end |
consoleCommandRemoveWildlifeAnimalToDebug
DescriptionDefinitionconsoleCommandRemoveWildlifeAnimalToDebug()Return Values
string | that | will be displayed on console |
759 | function WildlifeSpawner:consoleCommandRemoveWildlifeAnimalToDebug(spawnId, animalId) |
760 | local argsTest = true |
761 | spawnId = tonumber(spawnId) |
762 | if spawnId == nil then |
763 | argsTest = false |
764 | end |
765 | animalId = tonumber(animalId) |
766 | if animalId == nil then |
767 | argsTest = false |
768 | end |
769 | if argsTest then |
770 | for key, entry in pairs(self.debugAnimalList) do |
771 | if entry.spawnId == spawnId and entry.animalId == animalId then |
772 | table.remove(self.debugAnimalList, key) |
773 | return string.format("-- removed [spawn(%d)][animal(%d)] from debug list.", spawnId, animalId) |
774 | end |
775 | end |
776 | end |
777 | return string.format("-- gsRemoveWildlifeAnimalToDebug [spawnId][animalId]") |
778 | end |
consoleCommandToggleShowWildlife
DescriptionDefinitionconsoleCommandToggleShowWildlife()Return Values
string | that | will be displayed on console |
659 | function WildlifeSpawner:consoleCommandToggleShowWildlife() |
660 | self.debugShow = not self.debugShow |
661 | |
662 | return string.format("-- show Wildlife debug = %s", tostring(self.debugShow)) |
663 | end |
consoleCommandToggleShowWildlifeAnimation
DescriptionDefinitionconsoleCommandToggleShowWildlifeAnimation()Return Values
string | that | will be displayed on console |
698 | function WildlifeSpawner:consoleCommandToggleShowWildlifeAnimation() |
699 | self.debugShowAnimation = not self.debugShowAnimation |
700 | |
701 | return string.format("-- show Wildlife Animation = %s", tostring(self.debugShowAnimation)) |
702 | end |
consoleCommandToggleShowWildlifeId
DescriptionDefinitionconsoleCommandToggleShowWildlifeId()Return Values
string | that | will be displayed on console |
668 | function WildlifeSpawner:consoleCommandToggleShowWildlifeId() |
669 | self.debugShowId = self.debugShowId + 1 |
670 | |
671 | if self.debugShowId > WildlifeSpawner.DEBUGSHOWIDSTATES.MAX then |
672 | self.debugShowId = WildlifeSpawner.DEBUGSHOWIDSTATES.NONE |
673 | end |
674 | |
675 | local state = "" |
676 | if (self.debugShowId == WildlifeSpawner.DEBUGSHOWIDSTATES.NONE) then |
677 | state = "NONE" |
678 | elseif (self.debugShowId == WildlifeSpawner.DEBUGSHOWIDSTATES.SINGLE) then |
679 | state = "SINGLE" |
680 | elseif (self.debugShowId == WildlifeSpawner.DEBUGSHOWIDSTATES.ALL) then |
681 | state = "ALL" |
682 | end |
683 | return string.format("-- show Wildlife Id = %s", state) |
684 | end |
consoleCommandToggleShowWildlifeSteering
DescriptionDefinitionconsoleCommandToggleShowWildlifeSteering()Return Values
string | that | will be displayed on console |
689 | function WildlifeSpawner:consoleCommandToggleShowWildlifeSteering() |
690 | self.debugShowSteering = not self.debugShowSteering |
691 | |
692 | return string.format("-- show Wildlife Steering = %s", tostring(self.debugShowSteering)) |
693 | end |
countAnimalsTobeSpawned
DescriptionCalculate a random amount of animals that can be spawned for a speciesDefinition
countAnimalsTobeSpawned(table species)Arguments
table | species |
integer | returns | the number of animals to spawn |
529 | function WildlifeSpawner:countAnimalsTobeSpawned(species) |
530 | local remainingAnimal = math.floor((self.maxCost - self.totalCost) / species.cost) |
531 | |
532 | if species.minCount > remainingAnimal then |
533 | return 0 |
534 | end |
535 | local deltaNbAnimals = species.maxCount - species.minCount |
536 | local nbAnimals = species.minCount + math.random(1, deltaNbAnimals) |
537 | nbAnimals = math.min(remainingAnimal, nbAnimals) |
538 | return nbAnimals |
539 | end |
countTrees
DescriptionCount number of treesDefinition
countTrees(float x, float y, float z, radius radius)Arguments
float | x | x world position from which areas are checked |
float | y | y world position from which areas are checked |
float | z | z world position from which areas are checked |
radius | radius | of the test in m |
integer | number | of trees found |
467 | function WildlifeSpawner:countTrees(x, y, z, radius) |
468 | self.treeCount = 0 |
469 | overlapSphere(x, y, z, radius, "treeCountTestCallback", self, 2, false, true, false) |
470 | --overlapBox(x, y, z, 0, 1, 0, radius, radius, radius, "treeCountTestCallback", self, self.collisionDetectionMask, false, true, false) |
471 | return self.treeCount |
472 | end |
debugDraw
DescriptionDisplay debug informationDefinition
debugDraw()Code
583 | function WildlifeSpawner:debugDraw() |
584 | renderText(0.02, 0.95, 0.02, string.format("Wildlife Info\nCost(%d / %d)", self.totalCost, self.maxCost)) |
585 | local passedTest, originX, originY, originZ = self:getPlayerCenter() |
586 | |
587 | if passedTest then |
588 | for _, area in pairs(self.areas) do |
589 | for _, species in pairs(area.species) do |
590 | for _, spawn in pairs(species.spawned) do |
591 | if spawn.spawnId ~= nil then |
592 | local distance = 0.0 |
593 | |
594 | if species.classType == "companionAnimal" then |
595 | distance, _ = getCompanionClosestDistance(spawn.spawnId, originX, originY, originZ) |
596 | elseif species.classType == "lightWildlife" then |
597 | distance = species.lightWildlife:getClosestDistance(originX, originY, originZ) |
598 | distance = math.sqrt(distance) |
599 | end |
600 | local text = string.format("[%s][%d]\n- nearest player distance (%.3f)", species.name, spawn.spawnId, distance) |
601 | |
602 | Utils.renderTextAtWorldPosition(spawn.posX, spawn.posY + 0.12, spawn.posZ, text, getCorrectTextSize(0.012), 0) |
603 | DebugUtil.drawDebugCubeAtWorldPos(spawn.posX, spawn.posY, spawn.posZ, 1, 0, 0, 0, 1, 0, 0.05, 0.05, 0.05, 1.0, 1.0, 0.0) |
604 | DebugUtil.drawDebugCircle(spawn.posX, spawn.posY, spawn.posZ, species.groupSpawnRadius, 10.0, {1.0, 1.0, 0.0}) |
605 | end |
606 | end |
607 | end |
608 | end |
609 | end |
610 | |
611 | for _, area in pairs(self.areas) do |
612 | for _, species in pairs(area.species) do |
613 | if species.classType == "companionAnimal" then |
614 | for _, spawn in pairs(species.spawned) do |
615 | for animalId=0, spawn.count - 1 do |
616 | local showAdditionalInfo = self:isInDebugList(spawn.spawnId, animalId) |
617 | local showId = (self.debugShowId == WildlifeSpawner.DEBUGSHOWIDSTATES.SINGLE and showAdditionalInfo) or self.debugShowId == WildlifeSpawner.DEBUGSHOWIDSTATES.ALL |
618 | companionDebugDraw(spawn.spawnId, animalId, showId, showAdditionalInfo and self.debugShowSteering, showAdditionalInfo and self.debugShowAnimation) |
619 | end |
620 | end |
621 | end |
622 | end |
623 | end |
624 | end |
delete
DescriptionDelete instanceDefinition
delete()Code
76 | function WildlifeSpawner:delete() |
77 | self:removeAllAnimals() |
78 | |
79 | removeConsoleCommand("gsToggleShowWildlife") |
80 | removeConsoleCommand("gsToggleShowWildlifeId") |
81 | removeConsoleCommand("gsToggleShowWildlifeSteering") |
82 | removeConsoleCommand("gsToggleShowWildlifeAnimation") |
83 | removeConsoleCommand("gsAddWildlifeAnimalToDebug") |
84 | removeConsoleCommand("gsRemoveWildlifeAnimalToDebug") |
85 | end |
getIsInWater
DescriptionCheck if position is in waterDefinition
getIsInWater(float x, float y, float z)Arguments
float | x | x world position from which areas are checked |
float | y | y world position from which areas are checked |
float | z | z world position from which areas are checked |
bool | returns | true if there is water |
505 | function WildlifeSpawner:getIsInWater(x, y, z) |
506 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z) |
507 | return g_currentMission.waterY >= terrainHeight |
508 | end |
getIsOnField
DescriptionCheck if position is on a fieldDefinition
getIsOnField(float x, float y, float z)Arguments
float | x | x world position from which areas are checked |
float | y | y world position from which areas are checked |
float | z | z world position from which areas are checked |
bool | return | true is on field |
494 | function WildlifeSpawner:getIsOnField(x, y, z) |
495 | local densityBits = getDensityAtWorldPos(g_currentMission.terrainDetailId, x, y, z) |
496 | return densityBits ~= 0 |
497 | end |
getPlayerCenter
DescriptionGet player location from which the tests should be doneDefinition
getPlayerCenter()Return Values
float | x | world position. default is 0 |
float | x | world position. default is 0 |
float | x | world position. default is 0 |
364 | function WildlifeSpawner:getPlayerCenter() |
365 | if g_currentMission.controlPlayer and g_currentMission.player ~= nil then |
366 | local x, y, z = getWorldTranslation(g_currentMission.player.rootNode) |
367 | return true, x, y, z |
368 | elseif g_currentMission.controlledVehicle ~= nil then |
369 | local x,y,z = getWorldTranslation(g_currentMission.controlledVehicle.rootNode) |
370 | return true, x, y, z |
371 | end |
372 | return false, 0, 0, 0 |
373 | end |
isInDebugList
DescriptionDefinitionisInDebugList()Return Values
bool |
725 | function WildlifeSpawner:isInDebugList(spawnId, animalId) |
726 | for key, entry in pairs(self.debugAnimalList) do |
727 | if entry.spawnId == spawnId and entry.animalId == animalId then |
728 | return true |
729 | end |
730 | end |
731 | return false |
732 | end |
loadMapData
DescriptionLoads xml file (areas and containing animals)Definition
loadMapData(table xmlFile)Arguments
table | xmlFile | XML file handle |
bool | returns | true if load is successful |
119 | function WildlifeSpawner:loadMapData(xmlFile) |
120 | local filename = Utils.getFilename(getXMLString(xmlFile, "map.wildlife#filename"), g_currentMission.baseDirectory) |
121 | if filename == nil or filename == "" then |
122 | print("Error: Could not load wildlife config file '"..tostring(filename).."'!") |
123 | return false |
124 | end |
125 | |
126 | local wildlifeXmlFile = loadXMLFile("wildlife", filename) |
127 | |
128 | if wildlifeXmlFile ~= nil then |
129 | self.maxCost = Utils.getNoNil(getXMLInt(wildlifeXmlFile, "wildlifeSpawner#maxCost"), 0) |
130 | self.checkTimeInterval = Utils.getNoNil(getXMLFloat(wildlifeXmlFile, "wildlifeSpawner#checkTimeInterval"), 1.0) * 1000.0 |
131 | self.maxAreaOfInterest = Utils.getNoNil(getXMLFloat(wildlifeXmlFile, "wildlifeSpawner#maxAreaOfInterest"), 1) |
132 | self.areaOfInterestliveTime = Utils.getNoNil(getXMLFloat(wildlifeXmlFile, "wildlifeSpawner#areaOfInterestliveTime"), 1.0) * 1000.0 |
133 | self.bypassRules = Utils.getNoNil(getXMLBool(wildlifeXmlFile, "wildlifeSpawner#bypassRules"), false) |
134 | local i = 0 |
135 | while true do |
136 | local areaBaseString = string.format("wildlifeSpawner.area(%d)", i) |
137 | if not hasXMLProperty(wildlifeXmlFile, areaBaseString) then |
138 | break |
139 | end |
140 | local newArea = {} |
141 | newArea.areaSpawnRadius = Utils.getNoNil(getXMLFloat(wildlifeXmlFile, areaBaseString .. "#areaSpawnRadius"), 1.0) |
142 | newArea.areaMaxRadius = Utils.getNoNil(getXMLFloat(wildlifeXmlFile, areaBaseString .. "#areaMaxRadius"), 1.0) |
143 | newArea.spawnCircleRadius = Utils.getNoNil(getXMLFloat(wildlifeXmlFile, areaBaseString .. "#spawnCircleRadius"), 1.0) |
144 | |
145 | newArea.species = {} |
146 | local j = 0 |
147 | while true do |
148 | local speciesBaseString = string.format("%s.species(%d)", areaBaseString, j) |
149 | if not hasXMLProperty(wildlifeXmlFile, speciesBaseString) then |
150 | break |
151 | end |
152 | local classTypeString = getXMLString(wildlifeXmlFile, speciesBaseString .. "#classType") |
153 | local classType = nil |
154 | |
155 | if classTypeString ~= nil then |
156 | if string.lower(classTypeString) == "companionanimal" then |
157 | classType = "companionAnimal" |
158 | elseif string.lower(classTypeString) == "lightwildlife" then |
159 | classType = "lightWildlife" |
160 | end |
161 | end |
162 | if classType ~= nil then |
163 | local newSpecies = {} |
164 | newSpecies.classType = classType |
165 | newSpecies.name = getXMLString(wildlifeXmlFile, speciesBaseString .. "#name") |
166 | newSpecies.configFilename = getXMLString(wildlifeXmlFile, speciesBaseString .. "#config") |
167 | newSpecies.spawnRule = {} |
168 | newSpecies.spawnRule.hours = self:parseHours(getXMLString(wildlifeXmlFile, speciesBaseString .. ".spawnRules#hours")) |
169 | newSpecies.spawnRule.onField = getXMLBool(wildlifeXmlFile, speciesBaseString .. ".spawnRules#onField") |
170 | newSpecies.spawnRule.hasTrees = getXMLBool(wildlifeXmlFile, speciesBaseString .. ".spawnRules#hasTrees") |
171 | newSpecies.spawnRule.inWater = getXMLBool(wildlifeXmlFile, speciesBaseString .. ".spawnRules#inWater") |
172 | newSpecies.cost = getXMLFloat(wildlifeXmlFile, speciesBaseString .. ".cost") |
173 | newSpecies.minCount = getXMLInt(wildlifeXmlFile, speciesBaseString .. ".minCount") |
174 | newSpecies.maxCount = getXMLInt(wildlifeXmlFile, speciesBaseString .. ".maxCount") |
175 | newSpecies.currentCount = 0 |
176 | newSpecies.spawnCount = getXMLInt(wildlifeXmlFile, speciesBaseString .. ".spawnCount") |
177 | newSpecies.groupSpawnRadius = getXMLInt(wildlifeXmlFile, speciesBaseString .. ".groupSpawnRadius") |
178 | newSpecies.spawned = {} |
179 | newSpecies.lightWildlife = nil |
180 | if classType == "lightWildlife" then |
181 | if newSpecies.name == "crow" then |
182 | newSpecies.lightWildlife = CrowsWildlife:new() |
183 | newSpecies.lightWildlife:load(Utils.getNoNil(getXMLString(wildlifeXmlFile, speciesBaseString .. "#config"), "")) |
184 | end |
185 | end |
186 | table.insert(newArea.species, newSpecies) |
187 | end |
188 | j = j + 1 |
189 | end |
190 | table.insert(self.areas, newArea) |
191 | i = i + 1 |
192 | end |
193 | delete(wildlifeXmlFile) |
194 | return true |
195 | end |
196 | return false |
197 | end |
new
DescriptionCreating instanceDefinition
new(table customMt)Arguments
table | customMt | custom meta table |
table | instance | Instance of object |
39 | function WildlifeSpawner:new(customMt) |
40 | local mt = customMt |
41 | if mt == nil then |
42 | mt = WildlifeSpawner_mt |
43 | end |
44 | local self = {} |
45 | setmetatable(self, mt) |
46 | |
47 | self.collisionDetectionMask = 4096 -- dynamic_objects |
48 | self.maxCost = 0 |
49 | self.checkTimeInterval = 0.0 |
50 | self.nextCheckTime = 0.0 |
51 | self.areas = {} |
52 | self.areasOfInterest = {} |
53 | self.totalCost = 0 |
54 | self.treeCount = 0 |
55 | |
56 | -- Debug |
57 | -- if g_addCheatCommands then |
58 | self.debugAnimalList = {} |
59 | self.debugShow = false |
60 | self.debugShowId = WildlifeSpawner.DEBUGSHOWIDSTATES.NONE |
61 | self.debugShowSteering = false |
62 | self.debugShowAnimation = false |
63 | addConsoleCommand("gsToggleShowWildlife", "Toggle shows/hide all wildlife debug information.", "consoleCommandToggleShowWildlife", self) |
64 | addConsoleCommand("gsToggleShowWildlifeId", "Toggle shows/hide all wildlife animal id.", "consoleCommandToggleShowWildlifeId", self) |
65 | addConsoleCommand("gsToggleShowWildlifeSteering", "Toggle shows/hide animal steering information.", "consoleCommandToggleShowWildlifeSteering", self) |
66 | addConsoleCommand("gsToggleShowWildlifeAnimation", "Toggle shows/hide animal animation information.", "consoleCommandToggleShowWildlifeAnimation", self) |
67 | addConsoleCommand("gsAddWildlifeAnimalToDebug", "Adds an animal to a debug list.", "consoleCommandAddWildlifeAnimalToDebug", self) |
68 | addConsoleCommand("gsRemoveWildlifeAnimalToDebug", "Removes an animal to a debug list.", "consoleCommandRemoveWildlifeAnimalToDebug", self) |
69 | -- end |
70 | |
71 | return self |
72 | end |
onConnectionClosed
DescriptionDefinitiononConnectionClosed()Code
89 | function WildlifeSpawner:onConnectionClosed() |
90 | self:removeAllAnimals() |
91 | end |
onGhostAdd
DescriptionOn ghost add callbackDefinition
onGhostAdd()Code
267 | function WildlifeSpawner:onGhostAdd() |
268 | for _, area in pairs(self.areas) do |
269 | for _, species in pairs(area.species) do |
270 | if species.classType == "companionAnimal" then |
271 | for _, spawn in pairs(species.spawned) do |
272 | if spawn.spawnId ~= nil then |
273 | setCompanionsVisibility(spawn.spawnId, true) |
274 | setCompanionsPhysicsUpdate(spawn.spawnId, true) |
275 | end |
276 | end |
277 | end |
278 | end |
279 | end |
280 | end |
onGhostRemove
DescriptionOn ghost remove callbackDefinition
onGhostRemove()Code
250 | function WildlifeSpawner:onGhostRemove() |
251 | for _, area in pairs(self.areas) do |
252 | for _, species in pairs(area.species) do |
253 | if species.classType == "companionAnimal" then |
254 | for _, spawn in pairs(species.spawned) do |
255 | if spawn.spawnId ~= nil then |
256 | setCompanionsVisibility(spawn.spawnId, false) |
257 | setCompanionsPhysicsUpdate(spawn.spawnId, false) |
258 | end |
259 | end |
260 | end |
261 | end |
262 | end |
263 | end |
parseHours
DescriptionParse hours stringDefinition
parseHours(string input)Arguments
string | input | string to parse |
218 | function WildlifeSpawner:parseHours(input) |
219 | local hoursResult = {} |
220 | if input ~= nil then |
221 | input = input:gsub('[^-,0-9]','') -- remove all but numbers, '-' and ',' |
222 | local hourRangesStrings = StringUtil.splitString(",", input) -- split by ',' |
223 | local num = table.getn(hourRangesStrings) |
224 | |
225 | for i = 1, num do |
226 | local hourFromStartString = StringUtil.splitString("-", hourRangesStrings[i]) -- split string by '-' |
227 | local num2 = table.getn(hourFromStartString) |
228 | local fromVal = 0 |
229 | local toVal = 0 |
230 | |
231 | if num2 == 1 then |
232 | fromVal = tonumber(hourFromStartString[1]) |
233 | toVal = tonumber(hourFromStartString[1]) |
234 | elseif num2 == 2 then |
235 | fromVal = tonumber(hourFromStartString[1]) |
236 | toVal = tonumber(hourFromStartString[2]) |
237 | end |
238 | |
239 | -- sanity checks |
240 | if (num2 == 1 or num2 == 2) and (fromVal <= toVal) and (fromVal >= 0 and fromVal <= 24) and (toVal >= 0 and toVal <= 24) then |
241 | table.insert(hoursResult, {from = fromVal, to = toVal}) |
242 | end |
243 | end |
244 | end |
245 | return hoursResult |
246 | end |
removeAllAnimals
DescriptionDefinitionremoveAllAnimals()Code
95 | function WildlifeSpawner:removeAllAnimals() |
96 | for _, area in pairs(self.areas) do |
97 | for _, species in pairs(area.species) do |
98 | for i=#species.spawned, 1, -1 do |
99 | if species.classType == "companionAnimal" then |
100 | local spawn = species.spawned[i] |
101 | if spawn.spawnId ~= nil then |
102 | delete(spawn.spawnId) |
103 | spawn.spawnId = nil |
104 | end |
105 | elseif species.classType == "lightWildlife" and species.lightWildlife ~= nil then |
106 | species.lightWildlife:removeAllAnimals() |
107 | end |
108 | table.remove(species.spawned, i) |
109 | end |
110 | end |
111 | end |
112 | self.totalCost = 0 |
113 | end |
removeFarAwayAnimals
DescriptionRemoving animals that are too far awayDefinition
removeFarAwayAnimals()Code
320 | function WildlifeSpawner:removeFarAwayAnimals() |
321 | local passedTest, originX, originY, originZ = self:getPlayerCenter() |
322 | |
323 | if passedTest then |
324 | for _, area in pairs(self.areas) do |
325 | for _, species in pairs(area.species) do |
326 | if species.classType == "companionAnimal" then |
327 | for i=#species.spawned, 1, -1 do |
328 | local spawn = species.spawned[i] |
329 | if spawn.spawnId ~= nil then |
330 | local distance, _ = getCompanionClosestDistance(spawn.spawnId, originX, originY, originZ) |
331 | |
332 | if distance > area.areaMaxRadius then |
333 | delete(spawn.spawnId) |
334 | spawn.spawnId = nil |
335 | species.currentCount = species.currentCount - spawn.count |
336 | self.totalCost = self.totalCost - species.cost * spawn.count |
337 | table.remove(species.spawned, i) |
338 | end |
339 | end |
340 | end |
341 | elseif species.classType == "lightWildlife" and species.lightWildlife ~= nil then |
342 | local removedAnimalsCount = species.lightWildlife:removeFarAwayAnimals(area.areaMaxRadius, originX, originY, originZ) |
343 | if removedAnimalsCount > 0 then |
344 | species.currentCount = species.currentCount - removedAnimalsCount |
345 | self.totalCost = self.totalCost - species.cost * removedAnimalsCount |
346 | for i=#species.spawned, 1, -1 do |
347 | --local spawn = species.spawned[i] |
348 | if species.lightWildlife:countSpawned() == 0 then |
349 | table.remove(species.spawned, i) |
350 | end |
351 | end |
352 | end |
353 | end |
354 | end |
355 | end |
356 | end |
357 | end |
spawnAnimals
DescriptionSpawn animalsDefinition
spawnAnimals(table species, float spawnPosX, float spawnPosY, float spawnPosZ)Arguments
table | species | species to spawn |
float | spawnPosX | x world position to spawn |
float | spawnPosY | y world position to spawn |
float | spawnPosZ | z world position to spawn |
bool | returns | true if animals are spawned |
548 | function WildlifeSpawner:spawnAnimals(species, spawnPosX, spawnPosY, spawnPosZ) |
549 | local xmlFilename = Utils.getFilename(species.configFilename, g_currentMission.loadingMapBaseDirectory) |
550 | |
551 | if species.name == nil or |
552 | xmlFilename == nil or |
553 | g_currentMission.terrainRootNode == nil or |
554 | species.currentCount >= species.maxCount then |
555 | return false |
556 | end |
557 | local nbAnimals = self:countAnimalsTobeSpawned(species) |
558 | if nbAnimals == 0 then |
559 | return false |
560 | end |
561 | local id = nil |
562 | if species.classType == "companionAnimal" then |
563 | id = createAnimalCompanionManager(species.name, xmlFilename, "wildlifeAnimal", spawnPosX, spawnPosY, spawnPosZ, g_currentMission.terrainRootNode, g_currentMission:getIsServer(), g_currentMission:getIsClient(), nbAnimals) |
564 | setCompanionWaterLevel(id, g_currentMission.waterY) |
565 | for animalId=0, nbAnimals - 1 do |
566 | setCompanionCommonSteeringParameters(id, animalId, 0.5, 3.0, MathUtil.degToRad(20.0), 0.2) |
567 | setCompanionWanderSteeringParameters(id, animalId, spawnPosX, spawnPosY, spawnPosZ, 10.0, 12.0, 0.01) |
568 | end |
569 | elseif species.classType == "lightWildlife" then |
570 | id = species.lightWildlife:createAnimals(species.name, spawnPosX, spawnPosY, spawnPosZ, nbAnimals) |
571 | end |
572 | if (id ~= nil) and (id ~= 0) then |
573 | table.insert(species.spawned, {spawnId = id, posX = spawnPosX, posY = spawnPosY, posZ = spawnPosZ, count=nbAnimals}) |
574 | species.currentCount = species.currentCount + nbAnimals |
575 | self.totalCost = self.totalCost + species.cost * nbAnimals |
576 | return true |
577 | end |
578 | return false |
579 | end |
treeCountTestCallback
DescriptionTree count callbackDefinition
treeCountTestCallback(integer transformId)Arguments
integer | transformId | - transformId of the element detected in the overlap test |
bool | true | to continue counting trees |
478 | function WildlifeSpawner:treeCountTestCallback(transformId) |
479 | if transformId ~= 0 and getHasClassId(transformId, ClassIds.SHAPE) then |
480 | local object = getParent(transformId) |
481 | if object ~= nil and getSplitType(transformId) ~= 0 then |
482 | self.treeCount = self.treeCount + 1 |
483 | end |
484 | end |
485 | return true |
486 | end |
trySpawnAtArea
DescriptionWe try to spawn one animal type if the rules are validDefinition
trySpawnAtArea(table species, float spawnCircleRadius, float testX, float testY, float testZ)Arguments
table | species | species information |
float | spawnCircleRadius | spawn circle radius in m |
float | testX | x world position to test |
float | testY | y world position to test |
float | testZ | z world position to test |
bool | returns | true if animals are spawned |
392 | function WildlifeSpawner:trySpawnAtArea(species, spawnCircleRadius, testX, testY, testZ) |
393 | for _, animalType in pairs(species) do |
394 | if self:checkArea(testX, testY, testZ, animalType.spawnRule, spawnCircleRadius) then |
395 | local spawnPosX = testX + math.random() * spawnCircleRadius |
396 | local spawnPosZ = testZ + math.random() * spawnCircleRadius |
397 | local spawnPosY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, spawnPosX, 0, spawnPosZ) + 0.5 |
398 | if self:spawnAnimals(animalType, spawnPosX, spawnPosY, spawnPosZ) then |
399 | return true |
400 | end |
401 | end |
402 | end |
403 | return false |
404 | end |
unloadMapData
DescriptionUnload data on mission deleteDefinition
unloadMapData()Code
201 | function WildlifeSpawner:unloadMapData() |
202 | self:removeAllAnimals() |
203 | for _, area in pairs(self.areas) do |
204 | for _, species in pairs(area.species) do |
205 | if species.lightWildlife ~= nil then |
206 | species.lightWildlife:delete() |
207 | end |
208 | end |
209 | end |
210 | |
211 | self.areas = {} |
212 | end |
update
DescriptionUpdate function. Regulate animal population.Definition
update(float dt)Arguments
float | dt | time since last call in ms |
285 | function WildlifeSpawner:update(dt) |
286 | -- remove outdated areas of interest |
287 | self:updateAreaOfInterest(dt) |
288 | -- add animals |
289 | self.nextCheckTime = self.nextCheckTime - dt |
290 | if (self.nextCheckTime < 0.0) then |
291 | self.nextCheckTime = self.checkTimeInterval |
292 | self:updateSpawner() |
293 | end |
294 | -- remove animals too far away |
295 | self:removeFarAwayAnimals() |
296 | |
297 | -- update animal context |
298 | for _, area in pairs(self.areas) do |
299 | for _, species in pairs(area.species) do |
300 | if species.classType == "companionAnimal" then |
301 | for _, spawn in pairs(species.spawned) do |
302 | if spawn.spawnId ~= nil then |
303 | setCompanionDaytime(spawn.spawnId, g_currentMission.environment.dayTime) |
304 | end |
305 | end |
306 | elseif species.classType == "lightWildlife" then |
307 | species.lightWildlife:update(dt) |
308 | end |
309 | end |
310 | end |
311 | |
312 | if self.debugShow then |
313 | -- debug |
314 | self:debugDraw() |
315 | end |
316 | end |
updateAreaOfInterest
DescriptionRemoves an area of interest if the time to live expiresDefinition
updateAreaOfInterest(float dt)Arguments
float | dt | delta time in ms |
629 | function WildlifeSpawner:updateAreaOfInterest(dt) |
630 | for key, area in pairs(self.areasOfInterest) do |
631 | area.timeToLive = area.timeToLive - dt |
632 | if area.timeToLive <= 0.0 then |
633 | table.remove(self.areasOfInterest, key) |
634 | end |
635 | end |
636 | end |
updateSpawner
DescriptionUpdate spawner logicDefinition
updateSpawner()Code
377 | function WildlifeSpawner:updateSpawner() |
378 | local passedTest, x, y, z = self:getPlayerCenter() |
379 | if passedTest then |
380 | self:checkAreas(x, y, z) |
381 | end |
382 | end |