LeaderEnemyBoss Posted April 4, 2016 Share Posted April 4, 2016 (edited) Hi everyone,Until now i managed to get by with copying existing stuff and not caring much about the more obscure things like gamestates and the history. I am not much of a programmer, so everything i do is a blind playthrough so to speak. I want to create a ability, that calls in reinforcements from the avenger. For my "proof of concept" version i use the SeqAct_SpawnUnitFromAvenger class. Spawnlocations, nice animations, etc. are topics for a later :wink:. So far everything works fine: the ability is usable, it correctly picks soldiers from the avengers armory, if they die they are also missing from the armory after the mission ends etc. . Except always when I use the ability, a redscreen appears. Now i could just ignore it since everything works fine, but i'm wary about further consequences of this down the line or in prolonged games. I strongly suspect that i have to do "stuff" with the effect i created, but i dont know what or how. Here is my Ability: static function X2AbilityTemplate CreateRiftbeaconAbility() { local X2AbilityTemplate Template; local X2AbilityCost_ActionPoints ActionPointCost; local X2Effect_Riftbeacon RiftportalEffect; local X2Condition_UnitValue IsClosed; `CREATE_X2ABILITY_TEMPLATE(Template, 'Riftbeacon'); Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_psi_rift"; Template.Hostility = eHostility_Offensive; Template.AbilitySourceName = 'eAbilitySource_Standard'; Template.eAbilityIconBehaviorHUD = EAbilityIconBehavior_AlwaysShow; Template.AddShooterEffectExclusions(); ActionPointCost = new class'X2AbilityCost_ActionPoints'; ActionPointCost.iNumPoints = 1; ActionPointCost.bConsumeAllPoints = false; Template.AbilityCosts.AddItem(ActionPointCost); Template.AbilityCosts.AddItem(new class'X2AbilityCost_ConsumeItem'); Template.AbilityTriggers.AddItem(default.PlayerInputTrigger); Template.AbilityToHitCalc = default.DeadEye; Template.AbilityTargetStyle = default.SelfTarget; Template.BuildNewGameStateFn = TypicalAbility_BuildGameState; Template.BuildVisualizationFn = TypicalAbility_BuildVisualization; Template.bShowActivation = false; Template.CustomFireAnim = 'HL_CallReinforcementsA'; RiftportalEffect = new class 'X2Effect_Riftbeacon'; Template.AddShooterEffect(RiftportalEffect); return Template; } Here is my Effect: class X2Effect_Riftbeacon extends X2Effect; simulated protected function OnEffectAdded(const out EffectAppliedData ApplyEffectParameters, XComGameState_BaseObject kNewTargetState, XComGameState NewGameState, XComGameState_Effect NewEffectState) { class'SeqAct_SpawnUnitFromAvenger'.static.SpawnUnitFromAvenger(); }The code for spawning the Unit is exactly the same that gets activated, when one uses the "spawnreinforcements" console command. Only that this command doesnt produce redscreens.Here is the error message: Warning: Redscreen: ***Game State Created but Never Added to History*** New Game State: eGameRule_UnitAdded -- XComGameState_38600 Pending Game States: ==> Riftbeacon ( XGUnit_16 - ObjectID: 274 ) -- XComGameState_38598 Stack: Script call stack: (Default__SeqAct_SpawnUnitFromAvenger) SeqAct_SpawnUnitFromAvenger::AddStrategyUnitToBoard (Default__SeqAct_SpawnUnitFromAvenger) SeqAct_SpawnUnitFromAvenger::SpawnUnitFromAvenger (X2Effect_Riftbeacon_0) X2Effect_Riftbeacon::OnEffectAdded (X2Effect_Riftbeacon_0) X2Effect::ApplyEffect (X2Ability_Riftkeeper_0) X2Ability::ApplyEffectsToTarget (X2Ability_Riftkeeper_0) X2Ability::TypicalAbility_FillOutGameState (X2Ability_Riftkeeper_0) X2Ability::TypicalAbility_BuildGameState (XComGameStateContext_Ability_74) XComGameStateContext_Ability::ContextBuildGameState (X2TacticalGameRuleset_0) X2GameRuleset::SubmitGameStateContext (Default__XComGameStateContext_Ability) XComGameStateContext_Ability::ActivateAbility (UITacticalHUD_AbilityContainer_0) UITacticalHUD_AbilityContainer::ConfirmAbility (UITacticalHUD_AbilityContainer_0) UITacticalHUD_AbilityContainer::OnAccept (UITacticalHUD_ShotHUD_0) UITacticalHUD_ShotHUD::OnMouseEvent (UIMovie_2D_4) UIMovie::FlashRaiseMouseEvent [0107.83] Warning: Redscreen: ***Game State Added to History Without Known Creation*** PendingGameStates: Stack: Script call stack: (X2TacticalGameRuleset_0) X2GameRuleset::SubmitGameStateInternal (X2TacticalGameRuleset_0) X2GameRuleset::SubmitGameStateContext (Default__XComGameStateContext_Ability) XComGameStateContext_Ability::ActivateAbility (UITacticalHUD_AbilityContainer_0) UITacticalHUD_AbilityContainer::ConfirmAbility (UITacticalHUD_AbilityContainer_0) UITacticalHUD_AbilityContainer::OnAccept (UITacticalHUD_ShotHUD_0) UITacticalHUD_ShotHUD::OnMouseEvent (UIMovie_2D_4) UIMovie::FlashRaiseMouseEventThis board has been very kind to me so far, hopefully some knows how to solve this problem aswell. Edited April 5, 2016 by LeaderEnemyBoss Link to comment Share on other sites More sharing options...
CaveRat Posted April 5, 2016 Share Posted April 5, 2016 I don't think that this function class'SeqAct_SpawnUnitFromAvenger'.static.SpawnUnitFromAvenger(); can be used within an effect in such a manner. It creates and submits its own game state before effect game state is submitted. Basically this creates a mess of things. You'd probably want to base your effect on X2Effect_SpawnUnit.uc and re-use some of the code from SecAct_SpawnUnitFromAvenger.uc. Link to comment Share on other sites More sharing options...
LeaderEnemyBoss Posted April 6, 2016 Author Share Posted April 6, 2016 (edited) I don't think that this function class'SeqAct_SpawnUnitFromAvenger'.static.SpawnUnitFromAvenger(); can be used within an effect in such a manner. It creates and submits its own game state before effect game state is submitted. Basically this creates a mess of things. You'd probably want to base your effect on X2Effect_SpawnUnit.uc and re-use some of the code from SecAct_SpawnUnitFromAvenger.uc. Thank you for the hint, however i was not able to create a new effect based X2Effect_SpawnUnit so far. SpawnUnit in its default state seems to work for enemy units and things like the mimic beacon, where every unit is essentially the same. However soldiers with their different loadouts etc. seem to work differently. However i was able to get the SecAct class working without any redscreens, although I'm sure what I'm doing atm is probably not a good way to do it. I am also totally unable to get an nice visualization going. The new unit just appears, and i have no clue how to make the camera look at it and how to play the spawn particle effects. I know I need to build a visualization track, but so far, i was not able to understand the usually huge visualization functions i found in the files let alone apply them to my case. This is how my ability looks now: static function X2AbilityTemplate CreateRiftbeaconAbility() { local X2AbilityTemplate Template; local X2AbilityCost_ActionPoints ActionPointCost; `CREATE_X2ABILITY_TEMPLATE(Template, 'Riftbeacon'); Template.IconImage = "img:///UILibrary_PerkIcons.UIPerk_psi_rift"; Template.Hostility = eHostility_Offensive; Template.AbilitySourceName = 'eAbilitySource_Standard'; Template.eAbilityIconBehaviorHUD = EAbilityIconBehavior_AlwaysShow; Template.AddShooterEffectExclusions(); ActionPointCost = new class'X2AbilityCost_ActionPoints'; ActionPointCost.iNumPoints = 1; ActionPointCost.bConsumeAllPoints = false; Template.AbilityCosts.AddItem(ActionPointCost); Template.AbilityCosts.AddItem(new class'X2AbilityCost_ConsumeItem'); Template.AbilityTriggers.AddItem(default.PlayerInputTrigger); Template.AbilityToHitCalc = default.DeadEye; Template.AbilityTargetStyle = default.SelfTarget; Template.BuildNewGameStateFn = CreateRiftbeaconUnit; Template.BuildVisualizationFn = TypicalAbility_BuildVisualization; Template.bShowActivation = false; Template.CustomFireAnim = 'HL_CallReinforcementsA'; return Template; } simulated function XComGameState CreateRiftbeaconUnit(XComGameStateContext Context) { local XComGameState NewGameState; class'SeqAct_SpawnUnitFromRiftbeacon'.static.SpawnUnitFromRiftbeacon(); NewGameState = `XCOMHISTORY.CreateNewGameState(true, Context); TypicalAbility_FillOutGameState(NewGameState); return NewGameState; } Calling the SeqAct class from my slightly modified typicalgamestate function works flawlessly gameplay wise. I also managed to modify the spawn locations of the soldiers. But I am not able to make it look nice. My best bet is looking at the XComGameState_AIReinforcementSpawner class since it also brings its own visualization and basically behaves like i want my ability to behave, but so far I am unable to understand how to implement these things. Tl;dr: I have no idea what I'm doing! Edited April 6, 2016 by LeaderEnemyBoss Link to comment Share on other sites More sharing options...
LeaderEnemyBoss Posted April 9, 2016 Author Share Posted April 9, 2016 (edited) Okay I'm officially stuck. Since i am unable to create a pleasing visualization (or any visualization) when i used the SeqAct class, I decided to try the "intended" route again and created a new effect based on existing spawnunit effects. Creating new rookies with this effect is simple and I even managed to create a nice visual effect for it. However I just cant manage to spawn in an existing unit without the same redscreens i postet initially. I think the problem is, that while the game does the stuff that is in the effect class, a gamestate is active. And appearently you cant spawn in an existing soldier without creating a new context and therefore a new gamestate. So if anyone knows how to solve the problem or how to add visualization effects to the seqact class, im grateful for any help. Relevant code for my effect (modified X2Effect_SpawnUnit): function TriggerSpawnEvent(const out EffectAppliedData ApplyEffectParameters, XComGameState_Unit EffectTargetUnit, XComGameState NewGameState)//, XComGameState_Effect EffectGameState) { local XComGameState_Unit TargetUnitState, SpawnedUnit, StrategyUnit; local XComGameStateHistory History; local XComAISpawnManager SpawnManager; local StateObjectReference NewUnitRef; local XComWorldData World; local XComGameStateContext_TacticalGameRule NewGameStateContext; local Vector SpawnLocation; History = `XCOMHISTORY; SpawnManager = `SPAWNMGR; World = `XWORLD; TargetUnitState = XComGameState_Unit(History.GetGameStateForObjectID(ApplyEffectParameters.TargetStateObjectRef.ObjectID)); `assert(TargetUnitState != none); StrategyUnit = ChooseStrategyUnit(History); `log("Strageyunit" @STrategyUnit); Spawnlocation = GetSpawnLocation(ApplyEffectParameters); // Spawn the new unit SpawnedUnit = AddStrategyUnitToBoard(StrategyUnit, History, Spawnlocation); SpawnedUnit.bTriggerRevealAI = false; NewUnitRef = SpawnedUnit.GetReference(); `log("NewUnitRef" @NewUnitRef.ObjectID); EffectTargetUnit.SetUnitFloatValue(default.SpawnedUnitValueName, NewUnitRef.ObjectID, eCleanup_BeginTurn); EffectTargetUnit.SetUnitFloatValue(default.SpawnedThisTurnUnitValueName, NewUnitRef.ObjectID, eCleanup_BeginTurn); OnSpawnComplete(ApplyEffectParameters, NewUnitRef, NewGameState); } private static function XComGameState_Unit AddStrategyUnitToBoard(XComGameState_Unit Unit, XComGameStateHistory History, Vector SpawnLocation) { local X2TacticalGameRuleset Rules; local XComGameStateContext_TacticalGameRule NewGameStateContext; local XComGameState NewGameState; local XComGameState_Player PlayerState; local StateObjectReference ItemReference; local XComGameState_Item ItemState; local X2EquipmentTemplate EquipmentTemplate; local XComWorldData WorldData; local XComAISpawnManager SpawnManager; NewGameStateContext = class'XComGameStateContext_TacticalGameRule'.static.BuildContextFromGameRule(eGameRule_UnitAdded); //!!!! Redscreen appears after next command !!!! NewGameState = History.CreateNewGameState(true, NewGameStateContext); Unit = XComGameState_Unit(NewGameState.CreateStateObject(class'XComGameState_Unit', Unit.ObjectID)); Unit.SetVisibilityLocationFromVector(SpawnLocation); (rest of code not important) Edited April 9, 2016 by LeaderEnemyBoss Link to comment Share on other sites More sharing options...
CaveRat Posted April 9, 2016 Share Posted April 9, 2016 Try passing NewGameState along to the AddStrategyUnitToBoard() function instead of History. // Spawn the new unit SpawnedUnit = AddStrategyUnitToBoard(StrategyUnit, NewGameState, Spawnlocation) And the function should be changed too. private static function XComGameState_Unit AddStrategyUnitToBoard(XComGameState_Unit Unit, XComGameState NewGameState, Vector SpawnLocation) { local X2TacticalGameRuleset Rules; local XComGameStateContext_TacticalGameRule NewGameStateContext; local XComGameState NewGameState; local XComGameState_Player PlayerState; local StateObjectReference ItemReference; local XComGameState_Item ItemState; local X2EquipmentTemplate EquipmentTemplate; local XComWorldData WorldData; local XComAISpawnManager SpawnManager; // not needed // NewGameStateContext = class'XComGameStateContext_TacticalGameRule'.static.BuildContextFromGameRule(eGameRule_UnitAdded); //!!!! Redscreen appears after next command !!!! // no need to create new state // NewGameState = History.CreateNewGameState(true, NewGameStateContext); Unit = XComGameState_Unit(NewGameState.CreateStateObject(class'XComGameState_Unit', Unit.ObjectID)); Unit.SetVisibilityLocationFromVector(SpawnLocation); (rest of code not important) Also don't submit the NewGameState at the end. Link to comment Share on other sites More sharing options...
LeaderEnemyBoss Posted April 9, 2016 Author Share Posted April 9, 2016 (edited) Try passing NewGameState along to the AddStrategyUnitToBoard() function instead of History. // Spawn the new unit SpawnedUnit = AddStrategyUnitToBoard(StrategyUnit, NewGameState, Spawnlocation) And the function should be changed too. private static function XComGameState_Unit AddStrategyUnitToBoard(XComGameState_Unit Unit, XComGameState NewGameState, Vector SpawnLocation) { local X2TacticalGameRuleset Rules; local XComGameStateContext_TacticalGameRule NewGameStateContext; local XComGameState NewGameState; local XComGameState_Player PlayerState; local StateObjectReference ItemReference; local XComGameState_Item ItemState; local X2EquipmentTemplate EquipmentTemplate; local XComWorldData WorldData; local XComAISpawnManager SpawnManager; // not needed // NewGameStateContext = class'XComGameStateContext_TacticalGameRule'.static.BuildContextFromGameRule(eGameRule_UnitAdded); //!!!! Redscreen appears after next command !!!! // no need to create new state // NewGameState = History.CreateNewGameState(true, NewGameStateContext); Unit = XComGameState_Unit(NewGameState.CreateStateObject(class'XComGameState_Unit', Unit.ObjectID)); Unit.SetVisibilityLocationFromVector(SpawnLocation); (rest of code not important) Also don't submit the NewGameState at the end. Don't have much time now, just wanted to tell you THANK YOU! This costed me so much hours and the solution is so simple. Interestingly enough I had the same idea over night (yes this project follows me into the sleep!), however without you mentioning it I would have probably forgotten to not submit the gamestate. Needs some polish, but the basic function works and looks nice. Finally progress! Somehow i thought the whole time, that i need the "eGameRule_Added" line, but as I digged trhough the code, the role only seems to add a small visualization bit that makes the new unit visible immediatly. I didnt want that anyway since I handle visibility later in the skill's visualization. Edited April 9, 2016 by LeaderEnemyBoss Link to comment Share on other sites More sharing options...
CaveRat Posted April 9, 2016 Share Posted April 9, 2016 Glad I could help. :thumbsup: Link to comment Share on other sites More sharing options...
Recommended Posts