EntityFind

» Siedler Map Source Forum » Siedler DEdK Script Forum » EntityFind

Seiten: 1

mcb
#1
01.10.2014 18:51
Beiträge: 1472

EntityFind

Da mir die Logic-Funktionen nicht reichen hab ich mal selber was gescriptet. Ich denke mal, das andere damit auch noch was anfangen können, daher stelle ich es mal hier rein. Fragen und Anregungen könnt ihr gerne hier Posten


--[[
			EntityFind			mcb				2.1
	Schnelle Möglichkeit Entitys zu suchen & sortieren.
	Geht alle Entity-Ids durch, anstatt diese zu erfragen. (bobbys Methode)
	
	
	EntityFind.GetEntities(_amount, _acceptFunc, ...)
	_amount == nil => alle
	[ _acceptFunc: muss für jedes Entity true zurückgeben, das ausgegeben werden soll: id, unpack(arg)
	]
	
	EntityFind.GetPlayerEntities(_player, _entityType, _amount, _acceptFunc, ...)
	_player: 1-8 => player
				oder 0 => neutral (player 0)
				oder nil => alle
	_entityType: EntityTypeId oder EntityTypeName oder table
				oder 0 oder nil => alle
				oder function => muss true zurückgeben, wenn nach EntityTyp gesucht werden soll: EntityTypeName, unpack(arg)
	_amount == nil => alle
	
	
	EntityFind.GetPlayerEntitiesInArea(_player, _entityType, _pos, _range, _amount, _acceptFunc, ...)
	_pos: Position oder existierendes Entity
	_range: Radius um _pos
	_amount: "dist" oder "distance" => alle suchen und nach Entfernung zu _pos sortieren (sort wird überschrieben)
	Rückgabe standardmäßig NICHT nach Entfernung zu _pos sortiert!
	
	Argumente können auch in einem table abgelegt sein:
		{player = ,		--in diesem Fall kann player auch ein table mit Playern sein
		entityType = ,
		amount = ,
		[acceptFunc = ,
		arg = {},		-- Argumente für alle übergebenen Funktionen
		entities = {},	-- schon gefundene Entities
		sort = ,		-- um entities zu sortieren, muss jedem Entity einen Wert zuweisen
							(in entities vorhandene werden mit 0 bewertet): id, unpack(arg)]
		[pos = ,		-- bei Area
		range = ,]
		}
	
	
	EntityFind.Set enthält vorgefertigte Entity-tables:
	Leader (ohne Helden)
	Hero
	MilitaryBuilding (Türme)
	Military (Leader + Hero + MilitaryBuilding)
	Building (mit Wällen)
	PU (mit Kanonen)
	PB
	CU
	CB (mit Wällen)
	P (PU + PB)
	C (CU + CB)
	XD
	
	und Mengen-Operationen für tables:
	Add: Alle Elemente, die in einem der tables vorkommen, werden übernommen
	All: Alle Elemente, die in allen tables vorkommen, werden übernommen (Schnittmenge)
	Dif: Entfernt aus dem ersten table alle Elemente die in weiteren tables gegeben sind
	(übergebene tables werden nicht geändert)
	
	Benötigt:
	IstDrin				! vor EntityFind im Script !
	GetDistance
	unpack-fix
]]
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EntityFind = EntityFind or {oldId = {}, highest = 65537}
function EntityFind_EntityCreated()
	local id = Event.GetEntityID()
	if id > 131072 then
		old = (id - math.floor(id/65536)*65536) + 65536
		EntityFind.oldId[old] = id
	else
		EntityFind.highest = id
	end
end
Trigger.RequestTrigger(Events.LOGIC_EVENT_ENTITY_CREATED, nil, "EntityFind_EntityCreated", 1)
Logic.DestroyEntity(Logic.CreateEntity(Entities.XD_ScriptEntity, 2, 2, 0, 0))

function EntityFind.GetEntities(_amount, _acceptFunc, ...)
	--  table für gefundene
	local entitySafe = {}
	
	local sort
	
	-- table-aufruf entpacken
	if type(_amount) == "table" then
		_acceptFunc = _amount.acceptFunc
		arg = _amount.arg or {}
		sort = _amount.sort
		entitySafe = _amount.entities or {}
		_amount = _amount.amount
	end
	
	assert(type(arg)=="table" and type(entitySafe)=="table")
	
	-- sort prüfen
	assert(type(sort)=="function" or not sort, "EntityFind: Keine Sortierfunktion: "..tostring(sort).." Funktion oder nil erlaubt")
	local value = {}
	if sort then
		for k,_ in pairs(entitySafe) do
			value[k] = 0
		end
		_amount = nil
	end
	
	--  Menge prüfen
	assert(_amount==nil or type(_amount)=="number", "EntityFind: Keine Anzahl: "..tostring(_amount).." Zahl oder nil erlaubt!")
	
	--   acceptFunc prüfen
	_acceptFunc =  _acceptFunc or function() return true end
	assert(type(_acceptFunc)=="function", "EntityFind: Keine Prüffunktion: "..tostring(_acceptFunc).." Funktion oder nil erlaubt!")
	
	--   suchen!
	for i = 65536, EntityFind.highest do --   gehe alle ids durch
		local id = i
		if EntityFind.oldId[id] then -- eventuell ersatzid
			id = EntityFind.oldId[id]
		end
		if IsValid(id) and _acceptFunc(id, unpack(arg)) then -- prüfen
			if sort then
				local v = sort(id, unpack(arg))
				for i,val in ipairs(value) do
					if val > v then
						table.insert(entitySafe, i, id)
						table.insert(value, i, v)
						v = nil
						break
					end
				end
				if v then
					table.insert(entitySafe, id)
					table.insert(value, v)
				end
			else
				table.insert(entitySafe, id)
			end
		end
		if _amount and table.getn(entitySafe) >= _amount then -- genug gefunden: abbruch
			break
		end
	end
	
	return entitySafe
end

function EntityFind.GetPlayerEntities(_player, _entityType, _amount, _acceptFunc, ...)
	--  table für gefundene
	local entitySafe = {}
	
	
	local sort
	
	--  tableaufruf entpacken
	if type(_player) == "table" then
		_entityType = _player.entityType
		_amount = _player.amount
		_acceptFunc = _player.acceptFunc
		arg = _player.arg or {}
		entitySafe = _player.entities or {}
		sort = _player.sort
		_player = _player.player
	end
	
	-- sort prüfen
	assert(type(sort)=="function" or not sort, "EntityFind: Keine Sortierfunktion: "..tostring(sort).." Funktion oder nil erlaubt")
	local value = {}
	if sort then
		for k,_ in pairs(entitySafe) do
			value[k] = 0
		end
	end
	
	--   acceptFunc prüfen
	_acceptFunc =  _acceptFunc or function() return true end
	assert(type(_acceptFunc)=="function", "EntityFind: Keine Prüffunktion: "..tostring(_acceptFunc).." Funktion oder nil erlaubt!")
	
	--  player prüfen
	if _player == nil then
		_player = {0,1,2,3,4,5,6,7,8}
	elseif type(_player)~="table" then
		_player = {_player}
	end
	for _,pl in ipairs(_player) do
		assert(type(pl)=="number" and pl < 9 and pl >= 0, "EntityFind: Kein Spieler: "..tostring(pl))
	end
	
	--  typ prüfen
	if type(_entityType)=="function" then -- type-accepter
		local entityTypeAccept = _entityType
		_entityType = {}
		for tEntityTypeName, tEntityType in pairs(Entities) do
			if entityTypeAccept(tEntityTypeName, unpack(arg)) then
				table.insert(_entityType, tEntityType)
			end
		end
	end
	if _entityType == 0 or _entityType == nil then --alle
		_entityType = Entities
	end
	if type(_entityType)~="table" then
		_entityType = {_entityType}
	end
	for k,ty in pairs(_entityType) do
		if type(ty)=="string" then
			_entityType[k] = Entities[ty]
		end
		assert(type(_entityType[k])=="number" and type(Logic.GetEntityTypeName(_entityType[k]))=="string", 
			"EntityFind: Kein Entity-Typ: "..tostring(_entityType[k])
		)
	end
	
	-- key = true ist wesentlich schneller als IstDrin
	local ety = {}
	for _,et in pairs(_entityType) do
		ety[et] = true
	end
	_entityType = ety
	local pla = {}
	for _,pl in pairs(_player) do
		pla[pl] = true
	end
	_player = pla
	
	-- neue acceptFunc & sort
	local acc = function(id, player, entityType, acceptFunc, sortOld, ...)
		if player[GetPlayer(id)] and entityType[Logic.GetEntityType(id)] then	
			return acceptFunc(id, unpack(arg))
		end
		return false
	end
	local so = function(id, player, entityType, acceptFunc, sortOld, ...)
		return sortOld(id, unpack(arg))
	end
	
	--Übergabe
	return EntityFind.GetEntities{amount = _amount,
		acceptFunc = acc,
		arg = {_player, _entityType, _acceptFunc, sort or false, unpack(arg)},
		entities = entitySafe,
		sort = sort and so,
	}
end
function EntityFind.GetPlayerEntitiesInArea(_player, _entityType, _pos, _range, _amount, _acceptFunc, ...)
	--  table für gefundene
	local entitySafe = {}
	
	
	local sort
	
	--  tableaufruf entpacken
	if type(_player) == "table" then
		_entityType = _player.entityType
		_amount = _player.amount
		_acceptFunc = _player.acceptFunc
		arg = _player.arg or {}
		entitySafe = _player.entities or {}
		_pos = _player.pos
		_range = _player.range
		sort = _player.sort
		_player = _player.player
	end
	
	--Position prüfen
	if type(_pos) ~= "table" and IsValid(_pos) then
		_pos = GetPosition(_pos)
	end
	assert(type(_pos)=="table" and type(_pos.X)=="number" and type(_pos.Y)=="number",
		"EntityFind: keine Positionsangabe: "..tostring(_pos)
	)
	
	--Reichweite prüfen
	assert(type(_range)=="number" and _range > 0, "EntityFind: Keine Reichweitenangabe "..tostring(_range))
	
	-- acceptFunc prüfen
	_acceptFunc =  _acceptFunc or function() return true end
	assert(type(_acceptFunc)=="function", "EntityFind: Keine Prüffunktion: "..tostring(_acceptFunc).." Funktion oder nil erlaubt!")
	
	--Funktion um Reichweite zu prüfen
	local func = function(id, pos, range, acceptFunc, sortOld, entityTypeOld, ...)
		return GetDistance(GetPosition(id), pos) <= range and acceptFunc(id, unpack(arg))
	end
	
	-- Sortierung
	local sortOld
	if _amount == "distance" or _amount == "dist" then
		_amount = nil
		sort = function(id, pos)
			return GetDistance(id, pos)
		end
	elseif sort then
		sortOld = sort
		sort = function(id, pos, range, acceptFunc, sortOld, entityTypeOld, ...)
			return sortOld(id, unpack(arg))
		end
	end
	
	entityTypeOld = nil
	-- entityType-accepter
	if type(_entityType) == "function" then
		entityTypeOld = _entityType
		_entityType = function(typeName, pos, range, acceptFunc, sortOld, entityTypeOld, ...)
			return entityTypeOld(typeName, unpack(arg))
		end
	end
	
	--Übergabe
	return EntityFind.GetPlayerEntities{player = _player,
		entityType = _entityType,
		amount = _amount,
		acceptFunc = func,
		arg = {_pos, _range, _acceptFunc, sortOld or false, entityTypeOld or false, unpack(arg)},
		entities = entitySafe,
		sort = sort,
	}
end
EntityFind.Set = {
	Leader = {Entities.CU_AggressiveWolf, Entities.PU_Thief, Entities.PU_Scout,
		Entities.PU_BattleSerf, Entities.CU_Barbarian_Hero_wolf,
	},
	Hero = {Entities.CU_Barbarian_Hero, Entities.CU_BlackKnight, Entities.CU_Mary_de_Mortfichet, Entities.CU_Evil_Queen},
	Building = {},
	MilitaryBuilding = {Entities.PB_Tower2, Entities.PB_Tower3, Entities.PB_DarkTower2, Entities.PB_DarkTower3,
		Entities.CB_Evil_Tower1,
	},
	C = {},
	P = {},
	XD = {},
}
function EntityFind.Set.Add(...)
	local r = {}
	for _,t in ipairs(arg) do
		for _,ty in ipairs(t) do
			if not IstDrin(ty, r) then
				table.insert(r, ty)
			end
		end
	end
	return r
end
function EntityFind.Set.All(...)
	local r = {}
	local t1 = table.remove(arg)
	for _,ty in ipairs(t1) do
		local ok=true
		for _,t in ipairs(arg) do
			if not IstDrin(ty, t) then
				ok = false
				break
			end
		end
		if ok then
			table.insert(r, ty)
		end
	end
	return r
end
function EntityFind.Set.Dif(t1, ...)
	local r = {}
	for _,ty in ipairs(t1) do
		local ok = true
		for _,t in ipairs(arg) do
			if IstDrin(ty, t) then
				ok = false
				break
			end
		end
		if ok then
			table.insert(r, ty)
		end
	end
	return r
end
function EntityFind.LoadSet()
	local ig = {"PB_Tower2_Ballista", "PB_Tower3_Cannon", "PB_DarkTower2_Ballista",
		"PB_DarkTower3_Cannon", "CB_Evil_Tower1_ArrowLauncher",
	}
	for na, ty in pairs(Entities) do
		if not IstDrin(na, ig) then
			if string.find(na, "Leader") then
				table.insert(EntityFind.Set.Leader, ty)
			end
			if string.find(na, "Hero") then
				table.insert(EntityFind.Set.Hero, ty)
			end
			if string.find(na, "CU_") or string.find(na, "CB_") or string.find(na, "XD_Wall") or string.find(na, "XD_DarkWall") then
				table.insert(EntityFind.Set.C, ty)
			end
			if string.find(na, "PU_") or string.find(na, "PB_") or string.find(na, "PV_") then
				table.insert(EntityFind.Set.P, ty)
			end
			if string.find(na, "CB_") or string.find(na, "PB_") or string.find(na, "XD_Wall") or string.find(na, "XD_DarkWall") then
				table.insert(EntityFind.Set.Building, ty)
			end
			if string.find(na, "XD_") then
				table.insert(EntityFind.Set.XD, ty)
			end
		end
	end
	EntityFind.Set.Military = EntityFind.Set.Add(EntityFind.Set.Leader, EntityFind.Set.Hero, EntityFind.Set.MilitaryBuilding)
	EntityFind.Set.CU = EntityFind.Set.Dif(EntityFind.Set.C, EntityFind.Set.Building)
	EntityFind.Set.CB = EntityFind.Set.All(EntityFind.Set.C, EntityFind.Set.Building)
	EntityFind.Set.PU = EntityFind.Set.Dif(EntityFind.Set.P, EntityFind.Set.Building)
	EntityFind.Set.PB = EntityFind.Set.All(EntityFind.Set.P, EntityFind.Set.Building)
end
EntityFind.LoadSet()

Dieser Beitrag wurde von mcb am 01.11.2014 um 16:22 editiert.

mcb
#2
01.11.2014 16:25
Beiträge: 1472

Version 2.1:
Performance-Verbesserung durch bessere table-Verwaltung (entityType = true anstatt IstDrin)

Kalle
#3
08.11.2014 19:43
Beiträge: 1150

wie wird die Funktion angewendet? Ein Beispiel?

____________________
Lieber Siedeln statt (fern)sehen.....

mcb
#4
09.11.2014 00:12
Beiträge: 1472

Ich hab die Funktionen ähnlich aufgebaut wie die Logic-Funktionen, aber als Rückgabe gibt es "nur" ein table mit allen ids.
EntityFind.GetPlayerEntities:

local ids = EntityFind.GetPlayerEntities(1, Entities, nil, function(id) return GetHealth(id)<=50 end)


Gibt ein table mit allen Entities von Player 1 zurück, die weniger als 50% LP haben.

EntityFind.GetPlayerEntitiesInArea:

local ids = EntityFind.GetPlayerEntitiesInArea(1, Entities.PU_LeaderBow1, GetPosition("irgendwo"), 1500, 5, function(id) return GetHealth(id)<=50 end)


Daselbe mit 5 Kurzbogenschützen in der Nähe von "irgendwo".

totalwarANGEL
#5
11.11.2014 19:40
Beiträge: 2123

Darf man fragen, was der On-Created-Trigger dort zu suchen hat? Was steckt denn hinter der Formel da drin?

____________________
Die Welt ist arschlochförmig und wir leben in der Mitte.

mcb
#6
11.11.2014 22:05
Beiträge: 1472

Darfst du Das ist ein Teil der Ursprünglich von bobby stammt.
S5 hat nur eine begrenzte Anzahl an EntityIds. Damit die nicht so schnell aufgebraucht sind, werden die ids wiederverwendet (oder zumindest der Arbeitsspeicher davon). Damit mann nicht versehentlich ein "neues" entity mit einer "alten" id anspricht, wird da ein entsprechender Wert draufgerechnet, so dass jede id einzigartig ist. Die seltsame Formel rechnet das zurück und speichert das ganze in einem table, damit die richtigen entities in der Schleife verwendet werden können.
Außerdem speichert der Trigger die id des höchsten entitys, damit bei wenigen entitys nicht alle durchgegangen werden müssen.
Das ganze dient nur dazu, die laufzeit der Schleife zu verkürzen, indem einfach weniger nicht existente entitys geprüft werden.

totalwarANGEL
#7
12.11.2014 18:49
Beiträge: 2123

Also bei S6 ist die höchste EntityID 179583. Das müsste ich dann also nur anpassen. (Alles was ich nicht verstanden habe, habe ich bisher weggelassen...)

____________________
Die Welt ist arschlochförmig und wir leben in der Mitte.

mcb
#8
12.11.2014 19:09
Beiträge: 1472

Was ist mit der ersten entityId?

Für alles bis auf die Schleife und den EntityCreatedTrigger müsstest du eigentlich nur die Funktionen gegen die von S6 austauschen. (Also sowas wie GetDistance, IsAlive...) Der ganze Kram mit den Sets ist nicht so wichtig. Das sind nur tables mit vorsortierten entitytypen. Bei S6 werden die so nicht funktionieren.

totalwarANGEL
#9
12.11.2014 19:12
Beiträge: 2123

Die niedrigste ID ist deckungsgleich.

Zitat von mcb:
Der ganze Kram mit den Sets ist nicht so wichtig. Das sind nur tables mit vorsortierten entitytypen. Bei S6 werden die so nicht funktionieren.


Schon klar.

Danke für die Infos.

____________________
Die Welt ist arschlochförmig und wir leben in der Mitte.

mcb
#10
12.11.2014 19:16
Beiträge: 1472

Dann würd ichs einfach probieren. Viel mehr als das S6 abstürzt kann nicht passieren...

mundn
#11
24.06.2018 19:15
Beiträge: 83

Ich hätte da eine Frage:
Wo bekomme ich die benötigten Comforts "IstDrin", "GetDistance" und "unpack-fix" her? Wenn ich nämlich versuche meine Karte mit dem eingefügten Script zu starten gibt er mir nur Fehlermeldungen.

PS: gibt es elegantere Methoden alle Ballistatürme/Kanonentürme auf der Karte zu zählen als

local ids = EntityFind.GetPlayerEntities(1, Entities.PB_Tower3, nil)



____________________
Scripten bei Master pewe in DEdK Script-Wiki gelernt ich habe.

mcb
#12
24.06.2018 19:39
Beiträge: 1472

Hier im Forum oder im Wiki. unpack-fix ist beim trigger-fix dabei.
(Wenn du die nicht findest, kann ich sie aber auch hier reinkopieren)

EntityFind ist aber ehrlich gesagt veraltet. Ich würde S5Hook.EntityIterator verwenden.

Zählen: Logic.GetNumberOfEntitiesOfTypeOfPlayer(_player, _eTyp)

mundn
#13
24.06.2018 20:13
Beiträge: 83

Vielen Dank, hab alles gefunden und probier aus ob es funktioniert.


Logic.GetNumberOfEntitiesOfTypeOfPlayer(_player, _eTyp)

hätte ich echt früher finden müssen...

____________________
Scripten bei Master pewe in DEdK Script-Wiki gelernt ich habe.

Seiten: 1

SiteEngine v1.5.0 by nevermind, ©2005-2007
Design by SpiderFive (www.siedler-games.de) - English translation by juja

Impressum