* AutoDefense devices like ICX target dead, phantom missiles at their projected location. Shrike has posted more details in the topic ICX devices don't notice when missiles are shot down.
* Recurring events given to missiles continue even after the missile is dead, but there is no way to check if the missile is dead. I tried (objGetProperty gSource 'abandoned), where gSource is the missile, but it always returns Nil even if the missile is dead but not yet removed from the game. It is impossible to determine if a given missile is dead or not, and prevent events from occuring based on alive-or-dead status. Events that do something with the missile's position originate at the missile's projected location, not where it died.
Below is a test mod that overrides and changes NAMI missile launcher into a MIRV launcher. These MIRVs can split even after being destroyed - sit on a planet next to an enemy base and fire. Planet absorbs MIRV, then four payload missiles magically appear a short distance away.
Code: Select all
<?xml version="1.0" ?> <!DOCTYPE TranscendenceExtension> <TranscendenceExtension UNID="0xd9120999" name="BUG: Phantom Missile Example" apiVersion="26" > ; Need to reference standard entites. <Library unid="&unidHumanSpaceLibrary;"/> ; Modified NAMI missile launcher. ; We modify NAMI launcher because WOlfen starts with one, making it easy to playtest without additional assistance. <ItemType UNID="&itNAMIMissileLauncher;" name= "NAMI MIRV launcher" attributes= "commonwealth, majorItem, NAMI" level= "3" frequency= "common" value= "1000" mass= "1000" description= "This launcher is compatible with a full range of popular missiles including the KM100 Longbows and the XM900 Lucifers... then converts them into MIRVs." > <Image imageID="&rsItemsNAMI2;" imageX="0" imageY="0" imageWidth="96" imageHeight="96"/> <Weapon fireRate= "30" powerUse= "10" launcher= "true" > <Missiles> <Missile ammoID="&itFragmentationMissile;"/> <Missile ammoID="&itKM100Missile;"/> <Missile ammoID="&itKM120Missile;"/> <Missile ammoID="&itKM500Missile;"/> <Missile ammoID="&itKM550Missile;"/> <Missile ammoID="&itXM300Missile;"/> <Missile ammoID="&itXM900Missile;"/> </Missiles> </Weapon> ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Added events to make our custom MIRVs work, or try to. ; The bug is the MIRV can split even after it is dead, but not yet removed from the game. ; This bug is not unlike missile defense targeting and shooting at missiles already neutralized. ; To see this phantom missile bug, put ship on a planet near enemies, and fire missile. ; Missile should explode and die, but then a moment later, more missiles spawn ahead as if the MIRV was not dead. ; I tried to check 'abandoned for missiles in objGetProperty, but it is always Nil even if missile is dead. <Events> ; OnDamage event here would not affect missiles. ; There needs to be a way for dead missiles to not use events or be detected by missile defense. ; Actually, we need a way to determine if a missile object *is* dead or not! <OnFireWeapon> (block (theSpeed theShot) (setq theSpeed (typGetDataField aWeaponType "speed")) ; Spawn the shot. (setq theShot (sysCreateWeaponFire aWeaponType gSource aFirePos aFireAngle theSpeed aTargetObj Nil aWeaponBonus)) (objIncVel theShot (objGetVel gSource)) ; Need to remember some data for later. (objSetObjRefData theShot "attacker" gSource) (objSetData theShot "bonus" aWeaponBonus) (objSetData theShot "target" aTargetObj) ; MIRV won't split immediately, only after a brief delay if an enemy is detected. (objSetData theShot "armed" (add (unvGetTick) 10)) (objSetEventHandler theShot &itNAMIMissileLauncher;) ; Should use a longer delay, but we use short interval to minimize phantom missile bug problems. (sysAddObjRecurringTimerEvent 5 theShot "ThinkSplit") ; Launched missile already, so return not-Nil to block normal procedure. 0 ) </OnFireWeapon> ; Our MIRV splits only when it is near its target or an enemy, and it multitargets hostiles within range. <ThinkSplit> (if (geq (unvGetTick) (objGetData gSource "armed")) ; YES: Missile flew long enough, check for split. (block (theAttacker splitDist splitNow theTarget allTargets) (setq theAttacker (objGetObjRefData gSource "attacker")) (setq splitDist 30) (setq splitNow Nil) (if (setq theTarget (objGetData gSource "target")) ; YES: Focus on assigned target. (if (leq (objGetDistance gSource (objGetData gSource "target")) splitDist) (setq splitNow True) ) ; NO: Search for any threat. (if (filter (sysFindObject gSource (cat "s T A N:" splitDist " P")) thisOne (or (objIsEnemy gSource thisOne) (and (objIsAngryAt thisOne gSource) (neq (shpGetOrder thisOne) 'mine)) ) ) (setq splitNow True) ) ) (if splitNow ; YES: Launch payload. (block (allTargets theType thePos theAngle theBonus theSpeed who missiles) (setq allTargets (filter (sysFindObject gSource "s T A N:50 P S:d;") thisOne (or (objIsEnemy gSource thisOne) (and (objIsAngryAt thisOne gSource) (neq (shpGetOrder thisOne) 'mine)) ) )) ; Move primary target to the head of the hitlist. (if theTarget (setq allTargets (append (list theTarget) (filter allTargets thisTarget (neq thisTarget theTarget)) )) ) (setq theType &itKM500Missile;) (setq thePos (objGetPos gSource)) (setq theAngle (objGetProperty gSource 'rotation)) (setq theBonus (objGetData gSource "bonus")) (setq theSpeed (typGetDataField theType "speed")) ; Our payload. Could play a whoosh sound here. (setq missiles 4) (if allTargets ; YES: Launch missile at a target as we cycle through hitlist. ; Remember that primary target is at the top of the list. (loop (gr missiles 0) (enumwhile allTargets (gr missiles 0) who (block (theFireAngle) (setq missiles (subtract missiles 1)) (setq theFireAngle (modulo 'degrees (add theAngle 270 (multiply missiles 60)) 360)) (sysCreateWeaponFire theType theAttacker thePos theFireAngle theSpeed who Nil theBonus) ) ) ) ; NO: Shouldn't happen, but just in case, launch missiles without a target. (loop (gr missiles 0) (block (theFireAngle) (setq missiles (subtract missiles 1)) (setq theFireAngle (modulo 'degrees (add theAngle 270 (multiply missiles 60)) 360)) (sysCreateWeaponFire theType theAttacker thePos theFireAngle theSpeed Nil Nil theBonus) ) ) ) ; Cannot use objDestroy because it removes smoke trail in addition to the source missile. ; Thus, we destroy the missile with damage. But before we do that... ; We stop the missile (objSetVel gSource Nil) ; Remove recurring event so it does not think as it dies and spawn more missiles. (sysCancelTimerEvent gSource "ThinkSplit") ; APA is one of the strongest attacks in the game, it will one-hit kill this missile. ; In another mod, another type would be dedicated to destroying these missiles with an ; effect that simulates the missile fragmenting to unleash its payload. (objDamage gSource &itAresPlasmaCannon; gSource) ) ; NO: No more, pass for now. ) ) ; NO: Do nothing for now, too soon to scan and split. ) </ThinkSplit> </Events> ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - </ItemType> </TranscendenceExtension>