Cogito, ergo sum
Legacy:Daemonica/Previous Problems
Contents
Previous Problems
I'm considering these problems resolved now :)
Locally Viewed Actors
How to work out if an Actor (UT) is being viewed on the local players view. In other words making an object hidden to some people, but visible to others.
Why am I working on this problem
Because of The Undying Curse where players have access to a spell called Scrye, which allows them to see hidden objects. I only want people who are actually 'Scrying' to be able to see the objects without effecting anyone else's perception of the world around them. My head hurts after this ;)
Foxpaw: I think I may have a solution to this problem, if you are still seeking one.. One way of course would be to have it client-side - you could put it in the objects Tick function to be executed client side - if they are scrying, bHidden is set to false, otherwise it is set to true. Of course, since it's client side it could probrably be hacked.. but if the actor is considered relevant to the network and is being replicated it could likely be hacked in the same way. If you want it to be client-side and only replicate to people who can see it... well, that could be a doozie. But the client-side implementation probrably wouldn't be too difficult. I don't recall the code to determine whether a given pawn/controller is being controlled by a local player or over a network, but I've seen it around.
Daemonica: yeah, that's what I've been thinking, it works almost all the way upto Listen Servers, they end up doing weird things due to replication.
Daemonica: Well, I've moved over to UT2003 now :) Finally, this lil problem has been put on the backburner for now, hopefully I can figure this out so it works on Listen Servers too!
Daemonica: Back on this problem now and it's getting there. I'm using a simulated Tick function as explained above but it's only working on the Listen Server/Single player side, client's don't seem to get this right. I'm using the following code
//----------------------------------------------------------- // UCScryeObject - Any object that is viewable only when // a player is 'scrying' // Copyright (C), Undying Curse //----------------------------------------------------------- class UCScryeObject extends UCInteractiveItem; simulated function Tick(float DeltaTime) { local UCPlayer UCLocalPlayer; if (Level.NetMode != NM_DedicatedServer) { UCLocalPlayer = UCPlayer(Level.GetLocalPlayerController()); if ( UCLocalPlayer != none ) { if ( UCLocalPlayer.bScrying ) bHidden=false; else bHidden=true; } } } defaultproperties { bHidden=True bNetTemporary=True bTearOff=True }
I'm assuming the actor is not being replicated to the clients. Should a bAlwaysRelevent be set to true?
Edit: Full source code now'
EntropicLqd: As far as I can tell it depends on where the Actor is spawned from. If the actor is spawned from a function that is called on both the client and server then bAlwaysRelevant doesn't seem to be required. When an actor is spawned from pure server side code I've always had to set the bAlwaysRelevant flag to True in order to get the Actor to replicate. This approach has always seemed like a hack to me and I've always had the feeling that I'm just missing something that everyone else in the world seems to know about. You'll probably need to ensure that the actor has a SimulatedProxy role.
Wormbo: When the actor is spawned from a function that's executed on server and clients, you get seperate actors and additionally the server-spawned one could get replicated to the client.
Daemonica: What happens if the actor is built into the map, how is that spawned? I need an object that is handles 100% by the client (or listen server) to alter it's bHidden property based on the Local Players (Controller) 'bScrying' property
Daemonica: Been working with the team and the following code is 80% working. It appears that if the server has bHidden=false then the scrying works, otherwise it's the actor remains invisible to everyone. So I hit on the idea of leaving the objects bHidden=false on the server and let the client play around with that to their hears content. In the meantime, use the listen servers LocalPlayerController to become the owner of the object and use bOwnerNoSee on the Listen Server only. Slight problem is, my attempts to set the owner at PostBeginPlay have failed as Level.GetLocalPlayerController() returns none at this time. The latest code is below, the older code is removed :D
//----------------------------------------------------------- // UCScryeObject - Any object that is viewable only when // a player is 'scrying' // Copyright (C), Undying Curse //----------------------------------------------------------- class UCScryeObject extends UCInteractiveItem; function PostNetBeginPlay() { log("UC-TESTING: Setting Owner for Scrye Object - "$Level.NetMode); super.PostNetBeginPlay(); if (Level.NetMode == NM_ListenServer) { log("UC-TESTING: Acknowledged Listen Server, Setting Owner - "$Level.GetLocalPlayerController()); SetOwner(Level.GetLocalPlayerController()); } log("UC-TESTING: Scrye Object now owned - "$Owner); } simulated function Tick(float DeltaTime) { local UCPlayer UCLocalPlayer; if (Level.NetMode != NM_DedicatedServer) { UCLocalPlayer = UCPlayer(Level.GetLocalPlayerController()); if ( UCLocalPlayer != none ) { if ( (Level.NetMode == NM_Client) || (Level.NetMode == NM_StandAlone) ) { if ( UCLocalPlayer.bScrying ) bHidden=false; else bHidden=true; } else { bHidden=false; if ( UCLocalPlayer.bScrying ) bOwnerNoSee=false; else bOwnerNoSee=true; } } } } defaultproperties { RemoteRole=ROLE_SimulatedProxy bHidden=False bOwnerNoSee=True }
Lilguy: You could always put the code that sets the owner in the tick function, and set it once the player controller is not none...Not sureif that will help, but it's an idea...It seems like you'll run into trouble if you change the value of bHidden on the listen server, because the value of the variable will be replicated to all the clients (Unless you set bSkipActorPropertyReplicationto true, but you might not want to do that)
It seems like an easy solution to all this would be to ensure that the entities are owned by the person who's "scrying", and then put something like this in the tick function:
simulated function tick(float dt){ UCLocalPlayer = UCPlayer(Level.GetLocalPlayerController()); if ( UCLocalPlayer != none ){ bVisible = (Owner == UCLocalPlayer); } }
That way, even though bVisible would be replicated as true if the listen server player was "Scrying", the clients would immediately set the value to false, and they wouldn't be drawn. Hope that helps you...
Daemonica: Problem with making the object owned by the 'scrying' player is any number of players can be 'scrying' at the same time. Setting the visibility using my code above now works fine on the client-side and single player, the Listen server is causing the problem. I tried to set the owner in PreBeginPlay, PostBeginPlay, PostNetBeginPlay and the simulated Tick, but to no avail, the SetOwner(UCLocalPlayer) doesn't seem to do anything :(
Foxpaw: I wouldn't worry about the owner stuff - the reason your previous code wasn't working is because if an actor has bHidden true and is not owned by the player in question, it is not considered relevant. The reason why it works on a dedicated server but not a listen server is clear. (to me :D )
If it's on a dedicated server that whole code bunch gets skipped. bHidden remains false, and the actor remains relevant. If it's a listen server, that bit doesn't get skipped, and if the local player can't see it, it's bHidden true, and thus not replicated.
Okay, now the fix. It's easy. There's two main ways to control whether something is drawn or not. bHidden is one way. It is tied into replication, which is usually what you want, but in this case it's not. The other way is to set it's drawtype DT_None. Also, Tick probrably isn't the best place for it, but I can't think of a better one off the top of my head so maybe there's no better choice. So basically:
//----------------------------------------------------------- // UCScryeObject - Any object that is viewable only when // a player is 'scrying' // Copyright (C), Undying Curse //----------------------------------------------------------- class UCScryeObject extends UCInteractiveItem; var EDrawType RealDrawType; simulated function Tick(float DeltaTime) { local UCPlayer UCLocalPlayer; Super.Tick( Delta ); // If it's a dedicated server, Level.GetLocalPlayerController() // will return None, so the dedicated server check was // redundant. UCLocalPlayer = UCPlayer(Level.GetLocalPlayerController()); if ( UCLocalPlayer != none ) { if ( UCLocalPlayer.bScrying ) { // Spare yourself some clock cycles by not setting // drawtype every tick. if ( DrawType == DT_None ) SetDrawType( RealDrawType ); } else if ( DrawType != DT_None ) SetDrawType( DT_None ); } } defaultproperties { bHidden=True bNetTemporary=True bTearOff=True }
Daemonica: Of course, makes perfect sense now, I'll give this a go, but looks good here. I totally forgot about drawtypes :D
Angel Mapper MadNad did something similar to this with Excessive Overkill's night vision. It drew an exact replica of players and vehicles on the HUD with a custom texture to make them appear brighter. You could ask him for the code, I'm sure he'll let you use it.
Daemonica: We've changed our minds on how this will work. It's going to be an 'Area of effect' now so if one person casts it, everyone will be effected. It's not ideal and could change if we can ever work it out, but for now my head hurts!
Menu Music
How to change the music that plays when the GUIPage Main Menu is shown.
Tarquin: If you've resolved this, could you write up the results on GUIPage please? The same goes for other things: please incorporate results into the main body of the wiki :)
Daemonica: Good point, these has been added to GUIPage hopefully clear enough, any suggestions where the tracing information should go? Probably somewhere near the trace function I suspect?
Why am I working on this problem
Because of The Undying Curse which is now a UT2003 Mod, playing the default KR-UT2003-Menu music just doesn't have the right feel.
Foxpaw: This might be a bit tricky to explain, depending on how well you understand the Unreal Engine, but here's the skinny:
There is always at least one, and at most two levels loaded in any given instance of an Unreal Engine game. The first level is generally referred to as the "Entry" level. The second level is the one that you generally play in, and is the one that is changed by the servertravel and start console commands.
The Entry level is loaded when the Engine starts, and cannot change to a different level or be unloaded in any way. (That's not entirely true, but generally speaking..) You can still play in it, it is a level in every respect, but generally for performance reasons (since it's going to be running at the same time as the map you're playing on) it is generally just a subtracted cube without any bells and whistles in it.
Now, one level or the other is "screened out" depending on the context. If the "active" level is the entry level, your input will go to your controller in that level, etc. It's almost like having a completely separate instance of the game, but it isn't really.
Now, how this relates to your question: when you first start UT2003, the "Entry" level is loaded. The music you hear during the menu, is actually just the level's music. When the context switches to another level, you no longer hear sounds from the "entry" level and start hearing them from the other level.
So: to change the menu music you have to change the entry level. The entry level for UT2003 is in your maps directory, and is called Entry.UT2. Make a copy of it, and name it whatever you like, then open it and change the map's music. Then you'll have to make a copy of UT2003.ini, and in it change the reference to Entry.UT2 to your map. (it might just be entry, I don't remember if the UT2 gets appended automatically or not) You will then have to run UT2003 with a commandline parameter to read your modified ini instead of the default UT2003.ini. Most mods that do this also include a batch file so that you don't have to manually specify the command line argument.
Daid303: Well, I tried to change the entry level, but I didn't have any luck. It just kept using the default Entry.ut2. So I made this crappy hack:
function InitComponent(GUIController MyController, GUIComponent MyOwner) { Super.InitComponent(MyController, MyOwner); //Hack Controller.ViewPortOwner.Actor.GetEntryLevel().Song = "UMS_Main_Menu_Music"; }
in my mainmenu class.
Foxpaw: Hmm, it works fine on my mod. I use the "Entry" level as the workshop where you design your vehicles. That way you can very quickly switch back and forth between the combat simulator and the design level. It seems to have worked fine for me.
Daemonica: I encountered the same problems as Daid303, My own copy of Entry.UT2 (Entry-TUC.UT2) was totally ignored, the hack however worked a treat. The only entry in UT2003.ini that refers to Entry.UT2 is NetBrowseMap=Entry.ut2, but didn't make squat 'o' difference :( Thanks to you both. I'm happy this solution works
Foxpaw: Whoops! You're right. :P UT2003 has a separate entry level for network and local games. (not sure why) LocalMap is the local one. Unfortunately, that contains a call to start up the menu so you can't really replace it as easily. I'd say, continue using the hack method above. Alternatively, write your own GUI from scratch using Interactions like I did.. then you can replace the entry level without worrying. :D
Daemonica: Can I not and just pretend I did ;), besides the Hack works well, and I _really_ don't have the time to write a GUI from scratch. I'm _reasonably_ happy with the current implementation of menus but looking forward to the improvements from UT2004
GRAF1K: Can you say "UWindows returns" – or, more to the point – "YIPEE!"? ;-):D
Daemonica: YIPEE! :)
Ian Pilipski: There is a solution, you have to create your own Entry.ut2 map and save it in your mod path maps directory (with the name Entry.ut2). Then add your map path for your mod in your .ini file. I would recommend creating your own copy of the UT2003.ini file to something like MyModUT2003.ini. Then launch ut2003 with this command line switch UT2003.exe -ini=MyModUT2003.ini. The ini entry should look something like this:
[Core.System] ... ... Paths=../MyMod/Maps/*.ut2 Paths=../Maps/*.ut2 Paths= ... ... ...
Now when you run your mod, it will use your Entry.ut2 file as the entry level because it finds it first when searching the paths. Using this method can also allow you to do interesting things on the menu screens. If you set the menu backgrounds to none, then you will actually see the Entry.ut2 level in play. You can add some Matinee scene in there and you now have a very interesting menu system. As Foxpaw points out, if this level is always loaded during game play, you should not overload it by making it too complex. That would affect the performance of your mod.
Useable Actors
How do I check to see if an Actor is useable (i.e. It has a function UsedBy(Pawn User))
Why am I working on this problem
I need a way of highlighting useable actors on the HUD, however there is now bUseable variable, defined in Actor (or something similar). The reason I need to check this is that these usable items include pickups, triggers & decorations!
Foxpaw: I don't believe that there is any way to check if it "has" the UsedBy function, because every actor has it. (I think) What you CAN do, however, is have every "useable" think spawn an invisible "tag" that points to that object. Then, when you want to highlight the actors that are "useable," you can iterate through all of your "tags" and you will essentially have a list of all the things that should be highlighted.
Daemonica: Foxpaw comes to the rescue again :) Thanks. I'm asuming then somewhere in the PostBeginPlay() function I would Spawn this TagActor, then I can do an Other.IsA('TagActor') to varify useable items? Now I'm trying to get trace to actually return the object I'm looking at and not just the world!
// Called from a HUD to determine if the player is looking at a hostile, useable item //etc, so the crosshair colour can be changed :) function string PlayerLookingAt() { local vector HitLocation,HitNormal,StartTrace,EndTrace; local actor Other; StartTrace = Pawn.Location; StartTrace.Z += Pawn.BaseEyeHeight; EndTrace = StartTrace + vector(Pawn.GetViewRotation()) * 1000.0; Other=Pawn.Trace(HitLocation, HitNormal, EndTrace, StartTrace, True); if ( Other != none ) { // Assumes that all useable items have spawned a UseableItem actor :) if ( Other.IsA('UseableItem') ) return("Useable"); else return("Hostile"); } else return "None"; }
Foxpaw: Well, the implementation would depend on what exactly you had in mind. If you wanted the crosshair to respond to things underneath it, the code would be something like the following:
simulated function string PlayerLookingAt() { local vector HitLocation,HitNormal,StartTrace,EndTrace; local actor Other; StartTrace = Pawn.Location; StartTrace.Z += Pawn.BaseEyeHeight; EndTrace = StartTrace + vector(Pawn.GetViewRotation()) * 1000.0; Other = Pawn.Trace(HitLocation, HitNormal, EndTrace, StartTrace, True); if ( Other != none ) { if ( IsUseable( Other ) ) return("Useable"); else return("Hostile"); } else return "None"; } simulated function bool IsUseable( actor A ) { local UseableTag Tag; foreach AllObjects(class'UseableTag', Tag) if ( Tag.Actor == A ) return true; return false; }
That is of course a logical implementation, certainately not the fastest implementation. To speed it up you could make the UseableTags actors with bHidden true, then use DynamicActors instead of AllObjects. (would be much faster, not sure if you can do that in a HUD though anyway.) Even better, you could keep a list of all the UseableTags in the HUD, and just have every useabletag register itself with the HUD when it is created.
However, if you're going to do that (I'm kind of on a roll now :D) you might as well scrap the useabletags and just have the HUD keep a dynamic array pointing to all useable things, and then just iterate through that. Then when an actor wants to register itself as useable, it can find the HUD fairly easily by calling Level.GetLocalPlayerController().myHUD.. or something like that anyway. However, this method might not replicate very well because not only can you not replicate a dynamic array, but even if you worked around it you might constantly be adding and removing things as they become relevant or irrelevant.. so maybe just stick with the DynamicActors iterator for that reason.
Daemonica:You really went for it there :), I seem to have run into a bug at the moment, the trace is only colliding the world and not any actors, at all, ever. I think I've got my head round the implementation, if only there was a nifty bool bIsUsable back in actor this would be soooo much easier ;)
Foxpaw: Hmm, well, my first thought would be that the trace is not set to collide with actors. There's a bool in the trace function, bTraceActors, that sets it to do this. In both the code examples above it's set to true, but I would check to see if you accidentally omitted that in your code because that's what would normally cause that sort of behaviour.
Daemonica: Yup, that's what I thought, but the trace is acurate, it just don't like returning any actors :( The LookingAt() function now returns the actor the player is looking at (in theory) with the following definition
function actor PlayerLookingAt() { local vector HitLocation,HitNormal,StartTrace,EndTrace; local actor Other; StartTrace = Pawn.Location + Pawn.EyePosition(); EndTrace = StartTrace + vector(Pawn.GetViewRotation()) * 1000.0; Other=Pawn.Trace(HitLocation, HitNormal, EndTrace, StartTrace, true); if ( Other != none && !Other.bWorldGeometry ) return Other; else return none; }
This is defined in TUCPlayer (extends PlayerController) so as far as I can see it should work. It hits Static meshes correctly so I know the vectors for the trace are correct, it just needs to hit actors now!
Daemonica: And a little more work reveals that if the object I'm LookingAt() has bBlockPlayers set to True then the trace works, otherwise it fails, this to me is not usefull as I want know what actor I am looking at, even if this doesn't block the player (as most pickups won't actually block a player)..... which can be done by setting bProjTarget = True for those actors ;) Finally it works, but only because I'm working on a TC, I would hate to try and do this for mutators/etc. You would have to use ForEach type things as mentioned above (Eeeky, that's just nasty!)