PDA

View Full Version : Code idea: Relating buildings directly among themselves.



Higor
06-02-2012, 08:43 PM
This isn't an impossible solution, it consists on buildings to directly relate themselves by storing each other's object references.

My inspiration for this comes from the game Seven Kingdoms 2 (aka 7K2, old RTS), which I happen to have the source code (did my modifications there as well and learnt a lot).
The game has various dynamic arrays which serve the purpose of making object location easier, but even at C++ level, relating fortresses, towns and other buildings to each other seemed like a obviously necessary choice to the coders to make enormous scale matches possible.

Let's compare and extrapolate:
7k2 has:
- General object dynamic array
- Units dynamic array
- Towns dynamic array
- Buildings dynamic array
- Every object has it's array indexes stored for immediate access
- ASM code in graphic interface (an obvious indicator that the game is heavily optimized)
- Matches normally have up to 400 units in field, 100 edifications.
- Full processing done on both server and clients (sync based multiplayer)

Unreal has:
- Actor dynamic array (actor list)
- Actor list iterator functions at C++ level.
- Collision hash iterator at C++ level.
- Linked lists and custom arrays at UnrealScript.
- Various useful events.
- Processing differs efficiently between clients and server. (replication based multiplayer)
Siege specific:
- Matches normally have up to 50 buildings, 100 other dynamic actors.
- Game processing handled by timers to avoid slowdowns.


While we're talking about to different games, the inclusion of buildings makes this specific Unreal Tournament mod and any RTS game have elements in common.
And having the full source code of a RTS no matter how old it is gives a lot of ideas.

Ideas:
- Dedicated lists that hold specific objects.
Since we can't have dynamic arrays here, and UnrealScript is kinda slow for long iterations, this isn't as attractive unless the target object list is lesser than 20 element long.
- Buildings directly related.
Buildings have object references to other buildings within their influence radius, provides super fast location in code.
- General event system.
Something is created, all edifications should know.
Something is deleted, all edifications should know as well.
Events might be logged or used by the game handler for other purposes as well.
- Advanced team scoring.
Just like siegestats but for teams, provides info on how many buildings there are, how much RU was spent on them, how many kills by mines, how long they lasted providing an efficiency level average.
- Minimap.
This is entirely possible, requires research and development, but i'm sure it can be done in this engine, even without a map texture at all.
- History log/messaging.
Being able to open a box with the latest 5 events, makes nuke messages, motion sensors and other news (building destroyed, building created) displayed in a more user friendly way.


Some other ideas might come up later, but the first of them to apply would be:
- Containers should hold other buildings's references themselves, so no RadiusActors() iterations are called.
- Notify system that will update container's arrays upon building creation/destruction.

Higor
06-02-2012, 09:28 PM
This is an unfinished sample in the Super Container class...
It addresses the repairing code.

Old code:

simulated event Timer()
{
local sgBuilding building;

Super.Timer();

if ( SCount > 0 || Role != ROLE_Authority )
return;

foreach RadiusActors(class'sgBuilding', building, 240+(HealRadius*0.25)*Grade)
if ( sgBaseCore(building) == None && FRand() <= 0.5 + Grade/5
&& building.Team == Team && !bDisabledByEMP && !building.bDisabledByEMP)
{
if (SiegeGI(Level.Game).bOverTime)
{
building.Energy = FMin(building.Energy + (HealAmount + Grade/5)/2,
building.MaxEnergy);
}
else
{
building.Energy = FMin(building.Energy + HealAmount + Grade/5,
building.MaxEnergy);
}
}
}


New code:

var sgBuilding sgRelated[32];
var int iRelated;

simulated event Timer()
{
local int i;

Super.Timer();

if ( SCount > 0 || Role != ROLE_Authority || !bDisabledByEMP || (iRelated <= 0) )
return;

While ( i < iRelated )
{
if ( !sgRelated[i].bDisabledByEMP && (sgRelated[i].Energy < sgRelated[i].MaxEnergy) && (FRand() <= 0.5 + Grade*0.2 )
{
if ( SiegeGI(Level.Game).bOverTime )
sgRelated[i].Energy = FMin(sgRelated[i].Energy + (HealAmount + Grade*0.2)*0.5, sgRelated[i].MaxEnergy);
else
sgRelated[i].Energy = FMin(sgRelated[i].Energy + HealAmount + Grade*0.2, sgRelated[i].MaxEnergy);
}
++i;
}
}

Differences:
- Only iterate if we have one or more sgRelated building.
- sgRelated buildings already pass the Team and Distance checks.

Consequences:
- Faster code, specially on leech games.
- Limitation: up to 32 buildings are healed per container.

Q: How do we add elements to the sgRelated?
A: That will be handled by an event system, these events should be called on every sgBuilding subclass with these two methods: BuildingCreated( building), BuildingDestroyed( building). Then these methods will be re-implemented in the container classes to filter out buildings that shouln't be healed and add those that should into the sgRelated array.
Also, on every upgrade, the list will be refreshed so that more things enter the sgRelated array.


Feralidragon, I was wrong, the healing isn't simulated, the reason we see the numbers move so smoothly, is due to the server updating them so frequently.
Maybe it would be appropiate to leave the old RadiusActors() iterator for clientside simulation in case NetUpdateFrequency is finally reduced.

Higor
06-03-2012, 11:04 AM
General event system that interacts with all sgBuildings and GameInfo.

sgBuilding:

PostBeginPlay:

event PostBeginPlay()
{
if ( Pawn(Owner) != None )
Team = Pawn(Owner).PlayerReplicationInfo.Team;

if ( Team < 0 || Team > 3 )
Team = 0;

if ( Team == 0 )
Texture = SpriteRedTeam;
else
Texture = SpriteBlueTeam;

Energy = MaxEnergy/5;
SCount = BuildTime*10;
//LightHue = Team*160;
DoneBuilding = false;

Spawn(Class'sgSmokeGenerator');

SetTimer(0.1, true);
Timer();

SiegeGI(Level.Game).BuildingCreated( self);
}
- Modified indentation style to Unreal.
- Calls BuildingCreated on the Siege game info actor.


Destroyed, we implement this event on this class now.

//WARNING: ALL SUBCLASSES THAT IMPLEMENT DESTROYED() MUST CALL SUPER.DESTROYED()!!!!
event Destroyed()
{
SiegeGI(Level.Game).BuildingDestroyed( self);
}
- Calls BuildingDestroyed on the Siege game info.


Now we go into the SiegeGI class, and add these functions:

//General event system, a building has been created and initialized
function BuildingCreated( sgBuilding sgNew)
{
local sgBuilding sgB;

ForEach AllActors (class'sgBuilding', sgB)
{
if ( sgB != sgNew )
sgB.BuildingCreated( sgNew);
}
}

//General event system, a building has been destroyed
function BuildingDestroyed( sgBuilding sgOld)
{
local sgBuilding sgB;

ForEach AllActors (class'sgBuilding', sgB)
{
if ( sgB != sgOld )
sgB.BuildingDestroyed( sgOld);
}
}
- Those two functions can be later used to collect information necessary to generate team statistics, map information, and other user-created systems like a unified building construction message without having to implement it on every subclass.


Back to sgBuilding now, we implement the user event notifications:

//Notifications
function BuildingCreated( sgBuilding sgNew);
function BuildingDestroyed( sgBuilding sgOld);

- These events, just like ones called from C++ will provide extended functionality to subclasses that needs them.


Go to WildcardsSuperContainer, we implement these events in this subclass:

//Super container notifications
function BuildingCreated( sgBuilding sgNew)
{
local float InRadius;

if ( (sgNew.Team == Team) && (sgBaseCore(sgNew) == None) && (iRelated < ArrayCount(sgRelated)) )
{
InRadius = 240+(HealRadius*0.25)*Grade + sgNew.CollisionRadius;
if ( VSize(Location - sgNew.Location) <= InRadius )
sgRelated[iRelated++] = sgNew;
}
}

function BuildingDestroyed( sgBuilding sgOld)
{
local int i;

For ( i=iRelated-1 ; i>=0 ; i-- )
{
if ( sgRelated[i] == sgOld )
{
sgRelated[i] = sgRelated[--iRelated];
sgRelated[iRelated] = none;
break;
}
}
}
- Buildings are now added into the sgRelated array upon creation.
- Buildings will be deleted from the sgRelated array upon destruction.

Still in WildcardsSuperContainer, we add another rule, because containers gain heal radius when upgraded.
Old function:

function Upgraded()
{
local float percent;
local float scale;

if ( SiegeGI(Level.Game) != None )
{
if ( Grade < 2 )
scale = 1;
else if ( Grade < 3 )
scale = 2;
else if ( Grade < 4 )
scale = 3;
else if ( Grade < 5 )
scale = 4;
else if ( Grade >= 5 )
scale = 5;
SiegeGI(Level.Game).MaxRus[Team] -= StorageAmount;
StorageAmount = default.StorageAmount + 50 * scale;
SiegeGI(Level.Game).MaxRus[Team] += StorageAmount;
percent = Energy/MaxEnergy;
MaxEnergy = default.MaxEnergy * (1 + Grade/2);
Energy = percent * MaxEnergy;
}
}

New function:

function Upgraded()
{
local float percent, scale;
local sgBuilding sgB;

if ( SiegeGI(Level.Game) != None )
{
scale = Clamp( Grade, 0, 5);
SiegeGI(Level.Game).MaxRus[Team] -= StorageAmount;
StorageAmount = default.StorageAmount + 50 * scale;
SiegeGI(Level.Game).MaxRus[Team] += StorageAmount;
percent = Energy/MaxEnergy;
MaxEnergy = default.MaxEnergy * (1 + Grade/2);
Energy = percent * MaxEnergy;

//Rebuild sgRelated array
iRelated = 0;
foreach RadiusActors(class'sgBuilding', sgB, 240+(HealRadius*0.25)*Grade)
if ( (sgBaseCore(sgB) == None) && (sgB.Team == Team) && (iRelated < 32) )
sgRelated[iRelated++] = sgB;
}
}
- sgRelated array is rebuilt when the heal radius increases, instead of setting the array elements to NONE, we simply set the ceiling index (iRelated) to 0, that's because we're sure that the new array will have equal or more elements than the old one.
- Removed unnecessary code with the 'scale' determination, Clamp( V, A, B) returns an integer V between the values A and B.

Also, sub implementation of PostBeginPlay in WildcardsSuperContainer for containers added after other constructions:

event PostBeginPlay()
{
local sgBuilding sgB;

Super.PostBeginPlay();

iRelated = 0;
foreach RadiusActors(class'sgBuilding', sgB, 240+(HealRadius*0.25)*Grade)
if ( (sgBaseCore(sgB) == None) && (sgB.Team == Team) && (iRelated < 32) )
sgRelated[iRelated++] = sgB;
}
- The sgRelated array is generated for the first time when the Super Container finishes building.

SAM
06-04-2012, 11:19 AM
Excellent ideas Higor. Have you played Total Annihilation??

Higor
06-04-2012, 01:05 PM
Nope, but if there's any good concept to apply, fire away.

|uK|B|aZe//.
06-04-2012, 04:40 PM
personally i would like to see a capture function of some sort which would take a certain length of time or a certain amount of RU to capture an enemys super container or other sort of buildings like a nuke etc

Higor
06-04-2012, 10:26 PM
personally i would like to see a capture function of some sort which would take a certain length of time or a certain amount of RU to capture an enemys super container or other sort of buildings like a nuke etc
WOLOLO!! (win if get the reference)
This would indeed be the ultimate trolling tactic in Siege.

|uK|Grimreaper
06-05-2012, 06:16 AM
higor your on fire dude keep up the work!

SAM
06-05-2012, 06:55 AM
WOLOLO!! (win if get the reference)
This would indeed be the ultimate trolling tactic in Siege.

Lol it would...so would reclaim (unbuild) an enemy structure and reclaim x amount of the build RU if successful.

|uK|B|aZe//.
06-05-2012, 09:52 AM
WOLOLO!! (win if get the reference)
This would indeed be the ultimate trolling tactic in Siege.

like i said it would be very cool if you could capture an enemy's building

reclaiming an enemy's building would be too easy essentially it should be capturing then "removing aka reclaiming" which is the same thing pretty much depending on what building type it is (nuke or super cont etc)

--- Updated ---


WOLOLO!! (win if get the reference)
This would indeed be the ultimate trolling tactic in Siege.

LOL EXACTLY MY POINT

Higor
07-12-2012, 08:00 PM
UPDATED PREVIOUS LONG POST:

WildcardsSuperContainer.PostBeginPlay(): fixed a case where nearby buildings don't get repaired.
WildcardsSuperContainer.Upgraded(): fixed a case where nearby buildings don't get repaired, fixed upgrade bug where 50 extra RU capacity is given when upgrading to a level < 1.0

These functions should be reapplied.
These changes should be properly added to sgContainer as well.

About the extra 50 RU bug: build a container, upgrade it to 0.1, now this container has 100 RU capacity.