Code Monkey home page Code Monkey logo

doubledamnation's Issues

Level Editor - Play Mode Disabling Closest-to-warrior Tiles

This bug is caused by the camera's viewport absorbing the raycasts onto the level editor's tiles.
It is low priority, because I could excuse it as a "balancing feature" (not placing tiles instantly on yourself while playing)
But it is a bug, make no mistake.

See images below, I cannot place tiles within the camera bounds (camera should be unrelated to placing tiles), marked with blue arrows
ViewPortBug1
ViewPortBug2

Monsters hugging/camping walls (sidewalled bug)

Description of this bug

When enemies have a wall collision within -1 or +1 of their collisionPointX, sideWallDetected flag becomes true, and its logic makes enemies not move away from the wall.
The infamous sidewalled bug. Believe it or not, this is a regression bug, one which I was to fix, but development tasks shifted and when I came back, I forgot how I had planned to fix it.
The regression bug was with Satyrs jumping, who had some issues with horizontal movement (physics or AI behaviour?), and I added a sideWallDetected flag and their jump was fixed.
Except... satyrs and all monsters were bugged at sidewalls, but I noticed this weeks later.

Removing this flag and its logic, makes all non-jumping enemies work normally (see hollows at level editor, as a prime example where I override the sideWallDetected flag)

tl;dr: Remove 'sideWallDetected' flag and its logic, and fix the original jumping bug, whatever that was.

Client Monsterkill Desync

This tends to happen on very low pings (e.g. 0~10)
But there must be edge-cases not needing the above requirement.

Just think of mage killing an enemy 2 frames before the warrior.
Or at greater desyncs (real desyncs), mage killing an enemy by mistake and gaining 1 mana, but host does not get that 1 mana, nor is the enemy killed on his screen.

This should be detected and cause a desync, just like health causes desync if client takes damage before host.

Physics Engine linked to Framerate

The cardinal sin of gamedev.
I even knew about how bad it is to link framerate with physics, when I was writing the velocity system, yet I kept going because:
"Smash Bros runs at consistent 60 FPS, and surely Double Damnation can also run at consistent 60 FPS even in a shitty laptop"
The above was true at the time (2018), but I was so naive back then, thinking I had the feature-scale all figured out, when when I was still a newbie in programming (less than a year of programming!)

Every .velocity calculation ofc uses * Time.unscaledDeltaTime
So, where is the problem?
In my physics. Unity has no default acceleration values (it has velocity and force, but no acceleration), so I made my own, in order to recreate the DI (Directional Influence) feature from Smash Bros.
A blessing in disguise, as I implemented it for damage, but expanding it, I made Double Damnation have the unique and freeflow movement it is known for, today.
BUT. The acceleration increases per frame (this is why in speedruns, the lower framerate makes you faster, as less friction happens)
So, to solve this, I could go on acceleration (in-code, it is influenceX), and do * Time.unscaledDeltaTime
And indeed, it should become "framerate independent"

But since Time.unscaledDeltaTime is 0.0016 in 60 FPS, obviously the acceleration drops heavily, and the end result is not the same.
Hence, I must add a constant/hardcoded variable that when multiplied by 0.0016, returns the exact same values as current.

Why don't I do that?
Because it would take 1~3 days of playtesting, to get the exact value, and ensure the movement system will be the EXACT SAME as previous versions on 60 FPS (no movement regression, rip speedruns)
Its a boring process - I will do it eventually - but until then, I have my eyes on more important things.

Level Skip - Entire Level Skip (via boatsync)

When you skip a level, and the host is standing at the gate of that level, there is a tiny chance that you will somehow trigger levelcutscene or sth, and go onto next level.
I cannot deterministicly replicate this bug, hence the above mediocre explanation. Its a really rare bug, but unlike other Level Skip bugs, it softlocks the client (at least he can try to rejoin by quitting the game lol)
I can ensure its not exclusive to intro-boat skip (never saw it there, except maybe once), and is unrelated whether Host has skipLevelCutscene == true, or InLevelCutscene == true.
Like I tried all those scenarios with true/false (control group testing), and I didn't get this bug ONCE. So until I am able to deterministicly replicate it, I obviously can't solve it. Perhaps it will be solved when I fix the rest of Level Skip bugs.

Level Editor - Spinning Play Button

Circle should be spinning on pause/stopped mode. Otherwise it seems weird, out of place.
Especially when there is so little reaction/feedback on hovering it, or clicking it.
Optimally, it would spin faster when hovering, and do a fast 360 spin on clicking it.
The reason I haven't done this is simple. UI image animation rotation is jittery, and hence, very dizzying and distracting.
Using sprite renderer animation at UI too, rip. Using sprite render and transform, seperate of canvas, is bloated.

Level Editor - Victory Fanfare

Currently, when you clear a level, you feel nothing. It is half-assed.
What happens is just play mode toggles. Even the victory pose gets overriden.

No victory fanfare. It feels like no victory. Like you didn't reach The End, but it bugged and poof, back at the beginning...

NG+ Auto-Connect

There should be an automatic reconnection after the NG+ cutscene (final scene)

Otherwise, players finish game, and then they will chat a little, and exit the game, as they see they have disconnected and their inputs are fully local, and would have to reconnect.
In short, this subtle action of disconnection is like saying "We are done here. The game is finished, there is nothing else for you here."

If the players are connected after NG+, then a simple flail of the joystick or any keyboard input, will signal to the other player that they want to play, or could play a bit more - a signal that NG+ is a thing!
In short, to try different endings, and the gameplay will be far different and swifter since they have mastered the game. Could reach The End again within 10 minutes!
But if they are disconnected, there isnt even a subtle hint for this.

Code
See the following variables of playerCoreStats which are saved as JSON
bool playedAsHost
bool playedAsClient
ulong steamLobbyId
ulong steamFriendId

So, at the start of the game, the above 4 values are parsed from the latest JSON, and then host/connection logic happens.

Level Editor - Speedrun Timer

Clearing a level is not the victory reward by itself. It is also how fast you finished it.
For a game like Double Damnation, speed is vital. So, comparing with others how fast you finished the level, is what gives true replayability in the Level Editor.

Currently, this is missing.
What needs to be done for this is mainly some tweaking on SpeedrunManager, and making a new .json for PlayerStats, exclusive for LevelEditor.
So, each level would have its own .json (some kind of hashing for identifying the json, or just the Base64 string), and inside is a list of double variables, storing the time it took you to clear the level.
Should take 2 days of work.

Level Editor - DeathRewind Idle Pose

When you die while in play mode, until you spawn at checkpoint (duration is 1.1 seconds), the character has an ugly IDLE pose.

IdlePose.when.dying.for.1.second.mp4

I do not know why. It doesn't happen at normal "rewind" spell, nor while paused (so I first have to playtest with timescale)

12-Player Online Multiplayer - Soulseek Mode

So, 2 players for each "character", makes for 6 "characters" in a world. Let's look into the game design first.

Game-Design

Obviously, with this movement, it is very hard to find or hit another player, in such vast worlds/levels (see Super Mario 64)
Let's consider the default implementation, the classic implementation for online multiplayer 2D movement-focused games
Everyone starts at checkpoint, and its a race to the end of the level. In Double Damnation, a level can be finished via over 20+ paths, no player will stumble onto another player! Not to mention the speed difference of a newbie and speedrunner, will often be 10 times faster...

In short, aside of the very start (chokepoint exit), you will never interact with another player. The final experience will be no different than solo speedrunning, aside of a few dead monsters and fallen platforms. If levels were linear with max 3 paths, and movement had less depth, there would be player interactions and the default speedrun race could be a fun experience.

Soulseek Mode

For reference, have this open in another tab.

So, your goal is to place a "flag" anywhere on the level. You are given 1 minute.
This task, every player does. Of course, to place a "flag", you don't click it by mouse on some minimap, but have 1 minute to move to a point and place your "flag" all the while other players do the same (they can attack you, kill monsters etc etc)

When 1 minute passes, each player is given an image-minimap of the level and where the flags are placed.
Probably with a spell ("map"/"show" or whatever)

And the goal of every player to win? Get all flags, before others. To make the routes unique, all flags - 1, so you are to design the optimal route among the flags, instead of having it be obvious where to go, but you also think how to go.

Note that "flags" are not consumable. Once a player gets a "flag", it stays there for other players to get it as well.

So, Soulseek Mode provides insane replayability, because of the interactions with other players (routes will often collide, as the direction is random), think for example creating pillars which block paths, or placing "flags" at spots which normally require falling platforms but these platforms are now unavailable for the rest (fallen platform). And ofc, a different route in each game, instead of grinding start of level -> end of level

There could be some polishing touches like a X/Y restriction (placing "flag" outside level ffs), and allowing Soulseek Mode for the level editor (true infinite replayability)

tl;dr: You have 1 minute to place your "flag". After 1 minute, its a race for who gets the most "flags" first!

Netcoding

Requires having finished most of Mirror Porting
Other players are not duplicates or prefabs of Warrior but brand new classes. A player who isn't local is just a gameobject with a sprite renderer, an animator, and a SoulseekWarrior.cs class, nothing more, no collisions even.
Player Position sync is just a Vector2, which should not even interpolate if outside camera range.
As for visual sync? 2 ways:

  1. Sending the sprite index (I doubt it will look clean on >50 ping)
  2. Sending animation index, e.g. "PlayerX at timestamp 8.12361 played Idle" and hence, "Idle" animation on that player's sprite plays (hence ensuring smoothness of multiple sprites playing with proper timings from each other)

Attacks (health hitbox and spear hitboxes) may need some work, but what is certain is that player collisions dont exist, hence players can pass through you. It is possible to release without attack/damaging other players, but honestly that would be boring, and luckily isn't much work.

Spells are to be duplicated from current netcoding (e.g. see how fireball works)

For connecting players, just creating a new type of lobby via Steam API
https://partner.steamgames.com/doc/features/multiplayer/game_servers
https://partner.steamgames.com/doc/features/multiplayer/matchmaking

How to Access Soulseek Mode?

What do you think the top gate in TutorialScene is for?
Currently, its the same as bottom, but if this mode was created, you can be certain it would load the same level, but with Soulseek Mode ;)

Disable Editor Analytics

I want to give some personal context for this egregious bug, as for me personally, this is the worse of all.
I started using Unity in start of 2017, doing tutorials and stuff.
I didn't know programming (except if you consider programming the knowledge of if/for)
I obviously didn't even know what privacy means (I am Greek, English is not my native language), nor what a closed ecosystem meant.
I even used Skype, back then...

So, I picked Unity, started editing, expressing myself and having fun.
Little did I know back then, that it uploaded some of my data with each upload.
When I learned about privacy, first thing I did was clean my windows 7 from all default spyware (e.g. process logger), and just avoid everything google, everything microsoft, everything social media

And at the start of 2021, I started fiddling with Linux.
Do you know why I couldn't move fully onto Linux, that liberating OS? Because what I did full-time was Unity, and Unity is proprietary software, which I cannot even disable from outbound packets.
It will not work without internet connection.

"What are you talking about? "Work Offline" exists!"
Yes, enabled only after you have logged in with your Unity account, and uploaded your hardware's fingerprint (unique identification of your pc, ofc includes additive stuff like IP)
And you cannot even ensure it won't try to send packets, as it is proprietary, you don't even have the encryption keys to the packets it sends.

Anyway, my point is, I was forced to dual-boot because of Unity. At least, my Linux workflow was smoothly integrated, with Windows unable to read Linux drive, but Linux able to read Windows', and freely editing in it. Bless rsync btw.
To return, at least Unity doesn't take images every 5 minutes and uploads it to Google or Microsoft
But still, its disgusting. There was no consent, no warning either. Europe's GDPR is also a joke, as Unity can just say "use latest version, we don't care that we have harvested 5 years worth of your data and activity", as if latest version won't have this (but legally its dodgeable since it is indeed a different version)

So anyone who tries to update Double Damnation, will have some of his data stolen.
Anyone aware of privacy's benefits, will not install Unity, nor update Double Damnation.
For a few bugfixes or a feature addition, he would have to sacrifice privacy, hence ruining the open-source development model of Double Damnation.
This issue is fixable only by a hex editor, and it is niche (2018.1f1 version exclusive), and I don't know how to do it.

But at least, let it be a warning. Do not write open-source code which relies on proprietary frameworks/engines.

Detect Controller Disconnection

What if you play and suddenly you pull back and the wire of your controller gets unplugged? The game doesn't pause. It should.

This is often a problem with problematic controllers (I remember a ps4 one) which have a very "sensitive" wire plug.

Coding Implementation

string[] gamepadNames = Input.GetJoystickNames();
	if (gamepadNames.Length == 0)
		//Give warning

In case controller isnt detected normally (e.g. television controllers also count), make a class where in each Update() checks for each button, and once pressed, destroys itself.

Remake Enemy Logic

It is no secret that the code for the enemies is not well-designed. After all, it was made back in 2018, I didn't even know a design pattern back then.
I didn't read on AI either, it was quite literally handmade without any guidance, I still remember the "breakthroughs" and how making the AI felt like exploring, happy times.
There are some good things, like the split of enemy onto pathfinder and behaviour, and the attacks being split in classes, hence monsters are "modular" (e.g. hollow with some clicks, could get the jump attack of satyr, or the rush attack of centaur and minotaur)

However! There are 5 horrible things, which linger to this very day, as I didn't know anything about AI, and I was coding whatever came into my head:

  1. Attack Detection Hitbox and Attack Damage Hitbox are MERGED
    Yes, this is as horrible as it sounds. The same hitbox which says "if in X range, use your first attack" is also the hitbox which checks for applying damage within X range.
    It isn't really noticeable in-game, because with the above, enemies attack when they get in range of their attack. But it is noticeable for Harpy's spin attack.
    Harpy's spinattack is clunky/janky, because of this flawed design.

  2. Sidewalled Bug
    A serious bug, where enemies at wall sides have bugged AI (e.g. they run towards you but cannot move towards you...)

  3. Flying Type
    Instead of splitting EnemyBehaviour into Grounded and Flying, I slapped Flying code onto default EnemyBehaviour. And if (flying) then bloated code overrides default.
    Not a bug, not wrong, but it makes the code ugly and bloated.

  4. Delete IDamageable
    My first experience with Interfaces, and I did it with monsters. Unity is not meant for interfaces, it goes against its component system. And if you override them, you create insane spagghetti.

  5. Decouple State Machine from Physics.velocity
    Its such a mess. Physics.velocity is set on Update() and pathfinder, and FixedUpdate()
    This is the only part which is worse than WarriorMovement

For this reason, netcoding enemies was impossible, and I just disabled EnemyBehaviour altogether for the client.

Each Attack ANGLE To Have Different Properties

Currently, whether you attack diagonally down or diagonally up or plain horizontal, it is irrelevant - except the hitbox of course.
spearhitboxes
The above monster requiring a diagonal down attack to be hit in equal elevation, is a good example of hitbox importance.

Ideally, dash attacks would have more horizontal knockback depending on the velocity/influenceX
As for angles?

Diagonal down should "dig" an enemy, hence, more knockbackY, less knockbackX.
Diagonal up should either have more knockbackX than plain horizontal attack (1.0,0.0)
Or simply push enemies diagonally upwards.

With the above, each attack input would matter. The only difference right now is the hitbox, which is dead obvious to attack an enemy.

CODING
I would have to refactor TakeDamage() on enemies, as they make absolutely no use of knockbackPowerX and knockbackPowerY...
Hence, it fits with the general planned enemy refactoring, whose main goals are:

  • Each attack to have seperated AI detect hitbox, and damage hitbox
  • Splitting flying and ground units
  • Decouple state machine from physics.velocity
  • Remove the IDamageable inheritance (interfaces in Unity were a mistake, as you cannot override them without spagghetti)
  • Fix the sidewalled bug

Changing the movement, health/damage and monster attacks? Its easier to remake enemies, than refactor 70% of the code.
So, as you understand, simply "attackInput changes enemy push" would take a full month or so - most of the time spent on debugging.
This is a huge undertaking for such little results. Huge backend change, for almost no front-end difference, hence it is delayed forever.

P.S. Related to this issue is how spellword "Push" works, and could be expanded upon.

Soft Merge Collision Merge

Hard Merge is the process of merging a single tile onto a vertical/horizontal, and a vertical/horizontal onto a double.
TilesLevelEditor
This was pretty autistic to do

Soft Merge is the process of merging any tiles together with one or more of their adjacent (of same Y top and bottom), so as to gain performance.
This performance gain happens by reducing the total amount of collision boxes. Miniscule gains for a few tiles, but for an entire level, the performance difference is obvious, and this is easily seen comparing the 3 default levels/worlds which have soft merging, compared to any level editor's.

In short, levels that aren't using soft merge, cannot reach huge sizes without reducing FPS below 60.
Soft Merge isn't implemented currently, anywhere.

I see 3 implementation ways for Soft Merge. First is for Level Editor, the other 2 are the classic ones.

First

  1. Get adjacent tiles
  2. If top and bottom of current tile (column) is the same as the adjacent, do soft merge
  3. Copy-Paste their sprite renderers
    Bonus Autism: Make use of triple doubles sprites from default levels
  4. Move towards the equal center distance of adjacent tile(s)
  5. Expand box collision depending on how much the merge happened

Second
https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html

Third

  1. Get adjacent collider
  2. Expand one of the 2 colliders, to cover the bounds of the other (so you offset at center, and double size)
  3. Delete covered collider

Level Editor - Revivestop Glitch

While you are dying, if you click the "Stop" button (the opposite of "Play" button), the monsters keep playing normally instead of stopping.
But even worse, the monsters duplicate in their stopped form. So, a centaur will have both a normal playing centaur, and a stopped-version which means a sprite and a collision you cannot pass through!

Cinematic Camera on Level Enter

When you enter a new level/area, the camera should start greatly zoomed in, just like death/revive dialogue.
And when it ends, slowly zooming out ofc.

The reason I didn't implement this, and its low priority, is that the camera zoom system was the first data-oriented system I coded, and naturally being the first application of a new paradigm (data seperated from logic), it is an overengineered mess.
I think a hack for this can be done in a day, however. Total Lines of Code for this hack shouldnt be over 20.

Corpse Synchronization

  1. When an enemy dies, he doesnt move while dying. This part should run locally, once death is detected (aka override client restrictions, see desynced client)

  2. While desynced, killing a monster for one player, kills it wherever it was in the other player. This means monsters die on the spot for the other player, and their corpses are located at the spawn locations. Could be easily solved by also sending a Vector2 position on death, so the enemy is relocated there right before .Die() is triggered

Level Editor Advanced Values

Description
There are 4 values which could be changed for different gameplay.
This is speed and gravity.
In short:

  • GroundSpeed
  • MidairSpeed
  • GravityGrounded
  • GravityMidair

Changing any of the above, changes gameplay greatly. To the point that it can throw off your predictions of movement, hence I did not include it by default:
Because most levels would tweak these, and you would never git gut or adapt.

But obviously, once in a while, these levels are great as they slightly increase replayability (e.g. a no jump level lol)

Note that changing any of these, should change the trail to be distorted (see "Winds of Oblivion")
level 3 trail
An obvious "sign" that the darkwind is distorted, and in-game it shows to the player that the movement is different from what he is used to.

Implementation
There would need to be a "flip page" button in the level editor, to display these advanced values.
Pressing Tab would also serve as the hotkey for this.

To change the advanced values, just copy-paste the health input implementation.
There also needs to be a tiny button next to the inputfield, which resets to default.
And if possible, middle mouse click resets to default (I wish this was universal among games)

To save the advanced variables effectively, it would need just 1 bit flag change on the header, and 4 bytes each, to be stored before the end location spam.

Level Editor Death Note

When you die, there should be a "visual aide" spawning where it marks your death. Think of bloodstain in Dark Souls, or super mario maker's crossX spam.

Level Skip - Intro-Intro Packet Delay

Now, this is an extremely rare bug, I have seen it only once, but I deem it worthy to record it.
Both players must be at IntroScene (boat), and Client to skip intro before the Host (so he arrives at TutorialScene before the host, but initial connection is established while both are at intro)
Now, if Client spams spacebar/dash, when Host joins and interpolation begins, all snapshots of the Client are played with a 1 second delay for some reason. Its like the Forge interpolation bug, so it could even be Forge to blame.
Anyway, branching and linking fixes it, as usual with all player interpolation bugs.

I am certain I will never have to bugfix this, but if its ever going to be bugfixed, it will be the result of something else, e.g. Porting to Mirror instead of Forge (yeah I'm definitely going to spend 3 months porting what mostly works lol!)

Level Editor Aesthetics

Loading any level, the level should automatically populated with trees where appropriate (e.g. a vast horizontal line of tiles, should have some trees)

Level Skip - Death Dialogue

If host is having a dialogue after death, and client joins right there, client cannot change state, and is on a permanently "Idle" state, regardless of movement.
I have not investigated what causes this. Inputs do pass to host (he can magejump properly) but probably disabled locally (as if in cutscene)

Regardless, it is low priority because its easily fixed by simply dying/reviving.

Level Editor Description

Some levels should be classified by their creator e.g. "Casual level" or "hardc0re level, for gamers only!#!@"
They could also contain tips like "Just drop down when in doubt"

A short description is surely useful. Especially for some wacky levels

However! Text costs a lot of memory compared to my binary encryption (.wind) and this would cripple URL sharing.
Hence, the data of the description should be omitted when sharing a level via URL.
But when uploading a .wind file on a server or to a friend, it should stay, instead of being erased.

Position Sync takes too much bandwidth

On a debug build, press RightShift + F10...
Every timePerSnapshot (50 milliseconds), the host sends the position to client (Vector2)

This floods the network with useless snapshots, e.g. when warrior stays idle.
Since the same code (NetworkPositionInterpolationController) is extended for Monsters, each monster floods the network, even if its doing absolutely nothing, and you have gone afk!
It is disgusting, honestly. For those of you who dont know netcoding, its like checking some flag every frame, instead of having a callback/trigger.

tl;dr: A refactoring of Network(Enemy)PositionInterpolationController is required.
Maybe I can fix jittering on sudden ping increase as well.

The benefits of this are that low-bandwidth players (rural areas) can play the game without lag, and that you can download movies/games while playing, instead of Double Damnation leeching your entire bandwidth.

Shift Spam - Sticky Keys

You dash by using any of the following keys:

  • Shift
  • Ctrl
  • Alt

These keys are on both sides, and dash on the matching side (left or right)

Anyway, the problem is that if you "phantom dodgeroll" aka dash successively (you can stack dashes without interruption, within a tight timing window), you get a Windows popup, instantly freezing the game, killing the flow, and annoying the players.
I legitimately have no idea how to disable Sticky Keys by code. Not a single way (I have heard something about registry editing, no idea how to code that)

It is low priority because on the first time sticky keys popup happens, players can just use Ctrl or Alt.

P.S. Originally, you could dash only by Shift (Shifting the dark winds~), but since Alt and Ctrl were unoccupied and did nothing, binding them to the same action as Shift seemed obvious.
Regardless, freeing one of these 3 keys, would allow for an extended spellword system.

Crouch mapped to R2 and L2, instead of left joystick

Synopsis
Crouch values are from left joystick Y -0.1f to -0.8f
R2 and L2 can provide the same values and "remove" the left joystick Y crouch mechanic, so the left joystick negative values are exclusively for dropping off platforms (and fastfall)

Positives

  1. Simpler controls. More accurate.

  2. Instant Drop - Tight Feedback
    When you want to drop off a platform with influenceX > 25f (a dash is 24f), aka you want to drop off a platform after 2 dashes, it takes around 2~3 frames to drop off a platform. The reason for this platform-drop-delay is the same as the reason input buffers were implemented in old games (smash bros melee)
    When you fling the joystick fully downwards (0, -1) from a neutral center position (0,0), it takes a few frames to reach -1.
    There will be 2 frames in between which will record for example (0, -0.323462f) and (0, -0.8812345) and then (0, -1)
    By removing the crouch from joystick and placing it on L2/R2 (unused buttons btw), you can get instant feedback from dropping off platforms, aka not a single frame of delay, just like dropping off a platform normally.

  3. Crouch range with left joystick is from -0.1f to -0.8f
    With L2/R2 it can become -0.1f to -1, giving slightly more depth and nuance (also making it easier to plan your trajectory)

  4. Instant Drop Regardless of InfluenceX
    With left joystick X you cannot drop a platform instantly as long you have over influenceX...
    (This is imo the best positive)

Negatives

  1. Less intuitive.
    You press down, you crouch, makes sense.
    Since you can crouch only while influenceX > 0 (99% of the times, while dashing), it takes time to figure out you can crouch, and some players finish the game without figuring out perfect-crouch (which restores the dash)
    How can a player find this out with L2/R2? He presses L2/R2 once, twice, does nothing. The chances of randomly pressing L2/R2 while dashing are slim to none. They will never find crouch at all.
    And players finding the mechanics themselves without text boxes or hand-holding is part of the fun, as you feel you are exploring the game, and becoming stronger by finding new mechanics

  2. Crouch Cap Punishment
    Get greedy on reducing influenceX the most (-0.79f), but if you get too greedy, you straight up drop a platform.
    Conflicts with the positives of 3. and 4., but I personally enjoy this. That you cannot reduce dash at will, without any risk.

Satisfying Everyone
I will use DarkwindDistortionManager, aka config in-game, where you can slightly change some mechanics or straight up cheat (infinite mana, infinite HP)
darkwind distortion
The default mapping of Crouch will be R2/L2.
But you can also revert to "Legacy" via Darkwind Distortion Manager.

Level Skip - Cutscene Dialogue(?)

The below do not make sense, I wrote them in a rush and forgot to clean them up, and time has passed and i dont remember the exact references...

If at end of level -> Interrupt Dialogue (without moving or being able to cast spells) and then end level
If at start of level -> Same as death dialogue.

And of course, not skip future level cutscenes.

I tried making the above, but realized I had made some spagghetti and it wasn't as easy as it looked, so it would take a day or 2 to debug stuff and add more flags and events.

Code to look into:

  • WarriorMovement.cs -> case Dialogue -> if (InLevelCutscene)
  • DialogueManager.cs

Level Editor - Pausing Enemies

Each ground tile has 1 BoxCollider2D
In the below images, the hollow running to the right (runs towards the player), stops as if it hit a wall to the right.
Sure, if the right ground tile was higher in elevation, even by a pixel, the box collider would instantly stop.
But this is not the case, as you can see in the images below.

pauseenemyBug4
pauseenemyBug1
pauseenemyBug2
pauseenemyBug3

Since the colliders are at the exact same height/elevation, I think it is a Unity Engine bug.
For now, I did a disgusting hack, which is:
if (level editor && not satyr) sidewalled = false;

Optimally, I would merge the ground tiles in the level editor, which would result in great performance too.
But dont collision meshes merge by default, when they are marked/tagged as static? -_-

P.S. GroundCollision attached to every enemy, has nothing to do with sidewall, because it exists exclusively to check if grounded. Child's trigger collider certainly doesn't trigger parent's normal collider. tl;dr: The bug is located in EnemyBehaviour and that same gameobject's collider.

Disable User Analytics

All builds by default have "editor analytics" which "help the developer understand the player flow" blabla
Obviously, this contains privacy-sensitive data, like when you opened the game.
I do not know where these packets are uploaded, could be some Google server, and that's shady.
Every Unity game does this!

Anyhow, greedy Unity has done the following: If you have free edition, you cannot disable user analytics. So, if you want to ensure user privacy, you are blackmailed.

But since Unity is full of bugs, even this is bugged!
Ironic. Even if I bought "Pro" edition, I cannot disable it.

And if I upgrade to future versions of Unity, where this "bug" is fixed, not only I get new bugs, but many features of Double Damnation will be deprecated/obsolete... UNABLE TO COMPILE.

Source for the above claims:
https://forum.unity.com/threads/completely-disabling-analytics-at-runtime.520827/#post-3557207

Synchronize Credits

Inputs are local. Should make 2 new RPCs sending warriorInputData and mageInputData, and using those to synchronize the credits.
Meme feature, so it is peak low priority. At least it is easy to do.

When you join another player, the facing is not synchronized

Luckily, this is mechanically irrelevant and cannot lead to desyncs, as its purely visual, and on the first movement, the facing IS synchronized.
However, it would be ideal to have the same initial facing, so players can coordinate at the start like "Do you see that enemy ahead of us?" which currently leads to temporary confusion.

Just send facing list, on WarriorSendGameState()

ONLINE MULTIPLAYER

Double Damnation can already be finished fully online, on Steam and LAN
Then, why is it not advertised as such on Steam?
It even has desync as a feature, which exists in no other game in existence - this is where I spent most of my netcoding devtime (5 months?), yet why is it nowhere mentioned or promoted?

Because Forge Remastered(the netcoding framework I used) clashes with SteamP2P. The steamworks integration is buggy, but even worse, Forge Remastered has packet bugs (only noticeable at >0 ping)

Ultimately, on Steam, this means there is jitter for the client (Player2/Mage), and all packets are unreliable (unironically UDP) hence you can imagine the infinite bugs.
On LAN, there is no jitter and all packets are reliable (except streaming packets like position and input snapshots), but there are still some packet bugs:
One of the worse being code-wise that the host has to send a position snapshot each frame even if his position is exactly the same as the past snapshot, otherwise packet acks flood the network because of faulty timestamps!

tl;dr: High-Level Netcoding is complete. Low-Level Netcoding is bugged, and I must port to Mirror.

Forge Remastered is dead, so its not like devs gave up development but at least it was a complete, stable project, no.
Anyway, assuming I'm hyped to return to do some huge update on Double Damnation, I will focus on Online Multiplayer, since all of netcoding game logic is complete, and its a damn shame the transport code below is bugged because of the netcoding framework. "Swapping" low-level should be possible. Porting in Mirror must be done.

==================

Mirror Tracker below, to complete and public release Online Multiplayer

===================

That last item may have surprised you. Believe it or not, coding more players is easier than having another player "share" the same entity. Seriously, generally speaking, all you need to do is sync a position (Vector2), and an animation/sprite (enum ushort)

And for this reason, I plan to release online multiplayer + 12 player mode, together, so its not "the exact same game with the exact same content, but with online multiplayer", but having an incredible unique new mode atop of that! A proper "ultimate" version.

WHEN?
When I get the will and desire. The hype, so to speak. Explained here

Pre-Final Level Music Fade-Out

Music Fadeout happens at the end of each level, when dialogue starts.

For the level before the end duel, the music fadeout should happen when you enter the final corridor, and on the condition it has flipped.

I haven't done it because MusicSoundManager is absolute shit, untouched since 2018. Barely can be called a state machine, needs 2 new flags.

The greatest issue is not adding 2 new flags, but playtesting for an hour+ to ensure music didnt break (aka to ensure no regression bug)

Level Editor - Tile Selection Nitpick

When selecting a different placementTypeTile, there are around 5 brief black frames on the lastselected placementTypeTile.
None of these should exist. It should go from fullwhite (255 transparency) to instant 0.

Tag: Needs more info, because it may have been fixed. Gotta re-test.

"up" pillar to resynchronize properly

When you cast "up" while desynchronized, pillar shows only on you. That is a good thing.
However, on resynchronization, I kill all pillars, otherwise real desync would happen.
Ideally, on resynchronization, the pillars while desynced would merge in the same game.

Let's see at an example. You cast "branch"
You move away from the other player, and mage casts "up"
Both players have 1 pillar (different position)
When you cast "link", both players should have 2 pillars, adding the pillar of the other player.

Code-wise, I would have to refactor how "up" works for netcoding, as currently, it is a hack on "wait some frames, then relocate", instead of truly creating a new pillar.
The important classes to refactor:

  • FinalPillarBehaviour.cs
  • NetworkSpellcasting.cs
  • and of course, use PillarStorage (see LevelManager.cs) to check the diff pillars on resynchronization.

Ideally, you would mark which pillars were made on desynchronization, and send to the other player to create these pillars alongside their positions.

Automatic Level Load

Currently, if you close the game, you start all over. This imo isn't a problem, since the game is short, and players who have finished a level, can easily finish it in a single run. It does get annoying if this happens at "Edge of Oblivion" though, because the "Winds of Oblivion" aren't easy, even if you have finished them once.

Anyway, for all the devcycle, I decided to not add save/load functionality. But for integrating steam with my netcoding, I eventually desired the following behaviour: "if (player is in previous level) -> skip current level and go to other player's level"
So, I implemented a level sync pretty much, works from anywhere, even the boat intro. The problem is that this is used exclusively for netcoding.

So, just recycle it and use it for local multiplayer too.
Simply read .json of previous run, and skip to the previous run's level, if its detected the game is unfinished.

New Voicelines

The quality of the voicelines is all over the place. Some are good, some are bad, most are ok.
They are all very different, as these voicelines were not taken in the same time period, and lore-wise it makes sense as the protagonist(s) is borderline schizo, with reality being questionable, and sometimes losing his memories - via the (curse of the) Darkwind.

Anyway, if you find some voiceline which is really horrible, do say. Currently from a playtest I did just now, I have to report the first voiceline when player(s) enter the very final level "What is this, what did you do?"

Surely there are more which are just bad, so if you find any, report them, one day in the far future, I should come to re-voice all bad voicelines, just like I revoiced the intro yesterday (the original was horrible)

Level Browser

Sharing levels is as important as making them.
Currently, you only play levels made by your friends. But the best would be for someone to make a level, and its shared globally, hence one-by-one player creating levels, true replayability is achieved. Because currently, you can literally run out of levels to play!!

I have not determined exactly how to integrate this browser into the game. Ideally, the database is accessible by more than just DoubleDamnation.exe
For example, a website storing levels uploaded by users, or steam workshop

Steam Workshop

  • Bloated to subscribe to items for a <1 minute map.
  • Bloated to code, too much event handling and setup needed for something so simple!
  • Cannot even download independently from web browser
  • Proprietary Software - Demands steam access to play the game again and again...

JS Website
Bloated as it needs JS. Remove Javascript bloat, and you get the peak of user-made sharing levels, which has a static site generator.
See https://www.epicwar.com
Given I don't have the following fields:

  • Image
  • PlayerNumber
  • Share this map

It becomes a lot simpler.
No one should be able to delete the levels (even their uploaders) except server admins ofc.

Final Implementation
MySQL. Every map submitted has an internal unique ID as its filename, hence thats how they are retrieved.
https://www.geeksforgeeks.org/retrieve-image-and-file-stored-as-a-blob-from-mysql-table-using-python/

Anyway, data url, SQL databases or whatever, having a website for this ftw!

And obviously, you can access this website's back-end from in-game with a brand new UI (themed on current level editor UI)

Level Editor Mid-Joining

Currently, as long both players are in the same level, they can join each other.
However, Level Editor is problematic for a simple reason. Because what if a player has already made some of a level? To you, it will not appear.

The solution: On NetworkLevelEditor.Activate(), there should be a new RPC which sends the entire level to the other player. Since a level is binary-serializable (.wind) and by string, this shouldn't be a problem.
Of course, only the host (NetworkCommunicationController.IsServer()) should send the game state.

Upgrade Unity from 2018 LTS to Unity 2019 LTS

Required for Mirror netcoding framework which allows for proper Online Multiplayer

Mirror officially supports Unity 2020+, unofficially also 2019 LTS
I did upgrade Double Damnation from 2018.1 to 2018 LTS without any noticeable issues (can't playtest everything, e.g. physics autism)

Porting Mirror, I get namespace issues, which even if I manually resolve, I cannot resolve Steamworks namespace problems. So, to import Mirror, Double Damnation needs Unity 2019 LTS.

yet...
Upgrading into 2019 LTS, straight up breaks TextMeshPro
textmeshpro 2018 conversion issues
In javascript words, another day of goblin development.
In simpler words, dependencies fucked me up, and even if I spend a full week, I cannot progress. Unless I find some insane hack, or spend a month writing down each text mesh component on a .txt and add TMPro components and re-write the values of each one (hello, SNES gamedevelopment?)

The only hope I have is this working

Insanity Music

Every song should have an "insanity" variant, where the music is corrupted/distorted.
So, when you trigger insanity, the track fades into the "insanity" variant at the exact same minute:second ofc.
And obviously, when you toggle insanity off, it fades into normal, with same fadein/out mechanic.

Needs an "insanity" version for each music track.

Level Skip - Level-End

If a player joins the other player while he is on the levelendcutscene (so, dialogue happens in front of that gate), the client will skip that cutscene and load the next level.
Luckily, the game is fully functional, as in, when both players finish dialogue, the game continues as normal.

To fix this ugly (visual) desync, what needs to be done is call DontSkipAllDialogues not with a hacky timer.
Possible position is:

  1. Start of Level (needs skipped dialogue)
  2. End of Level (needs not-skipped dialogue)
  3. Anywhere else (needs not-skipped dialogue)

So detect if start of level, and then skip or something. will certainly be used.

Individual Default Values on Darkwind Distortion

Darkwind Distortion has a "Reset to Default" button at the bottom. This resets ALL values to default.
However, there is code for resetting each value to its default value, individually. It even works as-is with netcoding.

All that needs to be done, is find an appropriate art asset, and copy-paste it on the UI, next to each label

The alternative is even simpler. You click the label (so there is an invisible button) and it resets to default.
I low-key wish all games had this actually. Clicking an (unclickable) label, to place all values below it on default values!

Level Skip - Dialogue Stuck

This bug refers when the client is stuck in the "Idle" pose. In reality, the client is stuck at the DIALOGUE switch-case.
Only death can save him, and that is why this is Low Priority.

In short, the bug originates somewhere on level skip and level cutscene dialogue. It tends to happen on the gate of the level's start, its no coincidence I think.

Linking Levels (to create a full Metroidvania World)

Levels have no limits, yet, the bigger a level gets, the laggier it gets to load.
What if you want to make a world as big as the first level, or even greater? A full metroidvania world?
This is possible with this feature.

In short, in every level exit/ending, you can attach a level ID, so instead of finishing and going to the level browser, you instantly load that level.
I will tell you why this is hype. Because look at any metroidvania world design. There are often max 3 entrances to a new area. Often there is just one entrance, and a second is harder to reach or is the exit of that area.
Look at Symphony of the Night for example.
00-CD
These areas are literally the level links of this here.

tl;dr: Link levels to create your own metroidvania world without any limit.

"B-But the loading time!"
The downloading of the linked level should not start when you get to the exit, but start when you get X range from it.
And ofc it is cached in your pc and instantly loaded once you get inside.

This is the evolution of the level editor.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.