error: virtual call
» Siedler Map Source Forum » Siedler DEdK Script Forum » error: virtual call
Seiten: 1
Play4FuN
|
#1 17.02.2018 23:38 Beiträge: 704 |
error: virtual call
Mir ist bewusst, dass dieser Fehler wohl vom internen C++ Code kommen wird. Nachdem ich google fragte, war mir zwar auch klar, was das in etwa bedeutet, aber daran ändern kann ich wohl leider nix ...
Finde das Problem jedoch sehr interessant (und natürlich mega nervig). Folgendes:
Auf der Karte sind 8 Lehmgruben verteilt, die sammel ich erst mal so ein:
tCLAYMINES = { Logic.GetEntities( Entities.XD_ClayPit1, 16) }
Ich habe ne KI, die fleißig baut. Läuft alles super, Gebäude werden "live" nach und nach in den ConstructionPlan eingefügt.
Ich habe nen Trigger...
TRIGGER_ONCREATED = Trigger.RequestTrigger( Events.LOGIC_EVENT_ENTITY_CREATED, "", "EV_ON_ENTITY_CREATED", 1)
... der wie folgt aussieht:
function EV_ON_ENTITY_CREATED() local ent_ID = Event.GetEntityID() local ent_typ = Logic.GetEntityTypeName(Logic.GetEntityType(ent_ID)) local ent_P = GetPlayer(ent_ID) local ent_pos = GetPosition(ent_ID) if KI[ent_P] ~= nil then if (Logic.IsBuilding(ent_ID) == 1) and (ent_P > 0) and (ent_P < 9) then if tCLAYMINES ~= nil then if tCLAYMINES[1] > 0 then for i = 2, tCLAYMINES[1]+1 do if (GetDistance(GetPosition(tCLAYMINES[i]), GetPosition(ent_ID)) < 2000) and (Logic.GetSector(ent_ID) == Logic.GetSector(tCLAYMINES[i])) then if Logic.GetSector(ent_ID) == 0 or Logic.GetSector(tCLAYMINES[i]) == 0 then Message("error: 0 sector") -- does not happen end if KI[ent_P].minesToBuild == nil then KI[ent_P].minesToBuild = {} end --LuaDebugger.Break() if not IstDrin(tCLAYMINES[i], KI[ent_P].minesToBuild) then --Trigger.UnrequestTrigger(TRIGGER_ONCREATED) table.insert(KI[ent_P].minesToBuild, tCLAYMINES[i]) Message(i.." - player "..ent_P.." builds clay mine at "..GetPosition(tCLAYMINES[i]).X.."; "..GetPosition(tCLAYMINES[i]).Y) GUI.ScriptSignal(GetPosition(tCLAYMINES[i]).X, GetPosition(tCLAYMINES[i]).Y, 0) -- green --KI_BuildWorkPlace(ent_P, Entities.PB_ClayMine1, GetPosition(tCLAYMINES[i])) --KI_StartBuild(ent_P, Entities.PB_ClayMine1, GetPosition(tCLAYMINES[i]), 0) --KI_StartBuild(ent_P, Entities.PB_ClayMine1, ent_pos, 0) -- Error: virtual call end end end end end end end end
Dabei sieht KI_StartBuild aktuell so aus:
function KI_StartBuild(_pID, _type, _pos, _level) -- return: empty (0/1) if KI[_pID].defeated == true then return false end local _position if _pos == 0 then _position = invalidPosition else if type(_pos) == "table" then _position = {} _position.X = _pos.X _position.Y = _pos.Y elseif type(_pos) == "string" then _position = GetPosition(_pos) end end local empty = AI.Village_ConstructionQueueIsEmpty(_pID) local _constructionplan = {{ type = _type, pos = _position, level = (_level or 0) }} FeedAiWithConstructionPlanFile(_pID, _constructionplan) return empty end
Die Rückgabe empty benötige ich nur an anderer Stelle...
Der einzelne Aufruf von
KI_StartBuild(1, Entities.PB_ClayMine1, GetPosition(tCLAYMINES[6]), 0)
ohne Trigger klappt ohne Probleme, die KI baut ne Lehmmine und alle sind glücklich
Wird aber ein Gebäude in der Nähe platziert und die ifs im Trigger-Event abgeklappert, kommt es fast sofort bis einige Sekunden nach dem Minenbau zum Absturz mit der Meldung virtual call, game turn ... hilft mir ja nicht.
Ich muss also ne ähnliche Lösung finden für meine Ansprüche ... aber wo zum Teufel kommt dieser Fehler zustande?
Auch wenn ich den Trigger verwende, den Baubefehl jedoch an anderer Stelle verwende, stürzt das Spiel nicht (zumindest nicht direkt) ab ... Mysteriös.
____________________
LG Play4FuN
Siedler DEdK Mapping + Scripting Tutorials
mcb
|
#2 18.02.2018 00:14 Beiträge: 1472 |
Was genau willst du den mit dem Trigger erreichen? Ich glaube, das Problem liegt darin, das du den Auftrag gibst eine Lehmmine zu bauen, das den Trigger auslöst, was nochmal den Auftrag für eine Lehmmine in derselben Position ergibt (was dann zum crash führt, weil Siedler keine Parameter prüft).
Play4FuN
|
#3 18.02.2018 07:47 Beiträge: 704 |
Jou daran hab ich auch schon gedacht, dazu ist ja minesToBuild und die Abfrage IstDrin da, um genau das zu verhindern. Denke das sollte eigtl nicht der Fall sein (?)
Zudem: wenn ich "normal" 2x den Auftrag gebe an einer Stelle etwas zu bauen, passiert schlimmstenfalls dass die KI nix baut, so einen crash hatte ich da noch nicht.
Den Trigger nutze ich so, weil die KI eine entsprechende Mine nur dann bauen soll, wenn es die sozusagen erschlossen hat (also eigene Gebäude sehr nah gebaut)
____________________
LG Play4FuN
Siedler DEdK Mapping + Scripting Tutorials
Play4FuN
|
#5 18.02.2018 22:48 Beiträge: 704 |
Ich habe
(string.find(ent_typ, "mine") == nil)
als Bedingung hinzugefügt - leider ohne Änderung.
Nachtrag: ich muss es natürlich mit "Mine" versuchen ... ich check das nochmal
____________________
LG Play4FuN
Siedler DEdK Mapping + Scripting Tutorials
Kantelo
|
#6 19.02.2018 01:10 Beiträge: 357 |
Es könnte sein, dass der Fehler darin liegt, dass bei einem Bau einer Lehmmine, in Wirklichkeit gleich 3 Entities erstellt werden:
1. Entities.PB_ClayMine1
2. Entities.ZB_ConstructionSiteClayMine1
3. (!) Entities.PB_GenericMine
Ob es sich jetzt tasächlich um ein relevantes Gebäude handelt kann man zum Beispiel abfragen mit Logic.IsEntityInCategory(ent_ID,EntityCategories.Workplace)
Play4FuN
|
#7 19.02.2018 09:18 Beiträge: 704 |
Okay, doch das müsste jetzt ja durch
(string.find(ent_typ, "Mine") == nil)
auch abgefangen werden! Werde da noch weiter experimentieren.
____________________
LG Play4FuN
Siedler DEdK Mapping + Scripting Tutorials
warrior1024
|
#8 19.02.2018 19:15 Beiträge: 345 |
Was würde dagegen sprechen, den Aufruf aus dem Trigger rauszuschieben, also so etwas wie:
DelayedCall( "KI_StartBuild", {ent_P,Entities.PB_ClayMine1, ent_pos, 0})
im Trigger und sowas wie
DelayedCallTable = {} function DelayedCall( _f, _args) if table.getn(DelayedCallTable) == 0 then StartSimpleHiResJob("DelayedCallJob") end table.insert( DelayedCallTable, {f=_f, args = _args}) end function DelayedCallJob() for k,v in pairs(DelayedCallTable) do _G[v.f](unpack(v.args)) end DelayedCallTable = {} return true end
als Comfort?
Ist schnell zusammengebastelt und ungetestet, aber im Prinzip verschiebt man die Ausführung aus dem Trigger in den nächsten HiResJob-Tick.
____________________
"Banken machen keine Fehlentscheidungen! Haben Euch das Eure Eltern nicht beigebracht?"
- Bankier Samael Silren, Enderal
mcb
|
#9 19.02.2018 19:52 Beiträge: 1472 |
@Kantelo: Wusste nicht mal, das BuildingSites und GenericMines bei sowas auftauchen. Ich benutze in solchen Fällen immer
--- Original: Noigi -- Erweitert: mcb, mehrere Entitytypen -- -- Gibt zurück, ob ein Entity von einem der angegebenen Typen ist. -- -- Parameter: -- - id Entity (id, name) -- - ... typen (Entities.XXX, XXX) -- -- Rückgabe: -- - true/false -- function IsEntityOfType(id, ...) if IsDestroyed(id) then return false end local ty = Logic.GetEntityType(GetID(id)) assert(table.getn(arg)>0) for _,t in ipairs(arg) do if type(t)=="string" then t = Entities[t] end if ty == t then return true end end return false end
da bekomme ich nur die Entitytypen die ich genau haben will.
@Play4Fun: string.find hilft nicht gegen die beiden, da beide Mine als Substring haben.
Play4FuN
|
#10 19.02.2018 20:04 Beiträge: 704 |
Das mit dem Delay ist unnötig, wenn ich es hinbekomme, dass der Aufruf nur dann gestartet wird, wenn er es auch wirklich soll, also genau ein mal.
Die Funktion von Noigi sieht hilfreich aus.
Das mit dem String verstehe ich nicht ganz, wenn ...
t = "entity" string.find(t, "Mine") -->nil
während z.B.
s = "GenericMine" string.find(s, "Mine") -->(8,11)
also nil wenn kein Treffer und Anfang und Ende falls gefunden ... sollte also doch passen.
Nochmal zur Erklärung: ist "Mine" im String (entity Typ) enhalten, dann wird der Bauaufruf NICHT aufgerufen.
____________________
LG Play4FuN
Siedler DEdK Mapping + Scripting Tutorials
Seiten: 1