- Joined
- Feb 22, 2025
- Messages
- 315
Hi everyone,
Figured I'd finally start a thread to act as a dev diary for my current project. I've been chipping away at this since about February. Progress has been a bit on the slow side, mostly because you know how I like to obsess over the polish and get things behaving exactly right.
I won't write out a massive wall of text to hype it up; here’s an intro and some gameplay—a 5-minute video showing the latest iteration and some testing runs.
----> INTRO //(stay on web, no need to install Medal app)
NEW UPDATE:
----> GAMEPLAY
Code:
Dev Diary:
5.3.2026 - added A state-based AI system that dynamically reacts to catapult danger zones,
navigates to optimal positions, and performs timed area-cleansing attacks.
Built with hashtable persistence, order deduplication,
and timer-driven actions to ensure smooth, non-spammy behavior in large-scale battles.
5.6.2026 - added scs of ambient environment
--Code Snippet:
-
Kodo AI
-

Events
-


Time - Every 0.10 seconds of game time
-
-

Conditions
-


(Kodo_Unit is alive) Equal to True
-
-

Actions
-


Custom script: set udg_Kodo_State = LoadInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 330)
-


-------- // ---- IDLE & RETURN ---- --------
-


If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-



If - Conditions
-




Kodo_State Equal to 0
-
-



Then - Actions
-




-------- ///////// Cooldown check////// --------
-




Custom script: set udg_TempTime = TimerGetElapsed(udg_AI_GameTimer) - LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 334)
-




Custom script: if udg_TempTime < udg_Kodo_BreathCD then
-




Custom script: return
-




Custom script: endif
-




-------- // Find the nearest LANDED danger zone --------
-




Set TempInt = -1
-




Set TempDist = 999999.00
-




Custom script: set udg_TempTime = TimerGetElapsed(udg_AI_GameTimer)
-




For each (Integer DangerCheck) from 0 to 1, do (Actions)
-





Loop - Actions
-






-------- // Zone must be active AND already impacted --------
-






If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-







If - Conditions
-








Danger_EndTime[DangerCheck] Greater than TempTime
-








TempTime Greater than or equal to Danger_ImpactTime[DangerCheck]
-
-







Then - Actions
-








Custom script: set udg_TempReal = (GetUnitX(udg_Kodo_Unit)-udg_Danger_X[udg_DangerCheck])[I](GetUnitX(udg_Kodo_Unit)-udg_Danger_X[udg_DangerCheck]) + (GetUnitY(udg_Kodo_Unit)-udg_Danger_Y[udg_DangerCheck])[/I](GetUnitY(udg_Kodo_Unit)-udg_Danger_Y[udg_DangerCheck])
-








If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-









If - Conditions
-










TempReal Less than TempDist
-
-









Then - Actions
-










Set TempInt = DangerCheck
-










Set TempDist = TempReal
-
-









Else - Actions
-
-
-







Else - Actions
-
-
-
-




-------- // If a zone was found, immediately move there // break combat --------
-




If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-





If - Conditions
-






TempInt Not equal to -1
-
-





Then - Actions
-






-------- / Safe approach point: 600 units from zone centre toward the Kodo --------
-






Custom script: set udg_Kodo_SafeX = udg_Danger_X[udg_TempInt] + 600.0 * Cos(Atan2(GetUnitY(udg_Kodo_Unit)-udg_Danger_Y[udg_TempInt], GetUnitX(udg_Kodo_Unit)-udg_Danger_X[udg_TempInt]))
-






Custom script: set udg_Kodo_SafeY = udg_Danger_Y[udg_TempInt] + 600.0 * Sin(Atan2(GetUnitY(udg_Kodo_Unit)-udg_Danger_Y[udg_TempInt], GetUnitX(udg_Kodo_Unit)-udg_Danger_X[udg_TempInt]))
-






-------- // Break combat and move --------
-






Custom script: set udg_TempReal = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 338)
-






Custom script: set udg_TempReal2 = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 339)
-






Custom script: if (udg_TempReal != udg_Kodo_SafeX) or (udg_TempReal2 != udg_Kodo_SafeY) then
-






Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 338, udg_Kodo_SafeX)
-






Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 339, udg_Kodo_SafeY)
-






Custom script: call IssueImmediateOrder(udg_Kodo_Unit, "stop")
-






Custom script: call IssuePointOrder(udg_Kodo_Unit, "move", udg_Kodo_SafeX, udg_Kodo_SafeY)
-






Custom script: endif
-






-------- /////// Store state/target ////// --------
-






Custom script: call SaveInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 330, 1)
-






Custom script: call SaveInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 331, udg_TempInt)
-






Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 332, udg_Kodo_SafeX)
-






Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 333, udg_Kodo_SafeY)
-






-------- ///////////// --------
-
-





Else - Actions
-






-------- // No danger zone – return to home rect --------
-






Custom script: set udg_Kodo_HomeX = GetRectCenterX(udg_Kodo_HomeRect)
-






Custom script: set udg_Kodo_HomeY = GetRectCenterY(udg_Kodo_HomeRect)
-






Custom script: set udg_TempDist = (GetUnitX(udg_Kodo_Unit) - udg_Kodo_HomeX)[I](GetUnitX(udg_Kodo_Unit) - udg_Kodo_HomeX) + (GetUnitY(udg_Kodo_Unit) - udg_Kodo_HomeY)[/I](GetUnitY(udg_Kodo_Unit) - udg_Kodo_HomeY)
-






If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-







If - Conditions
-








TempDist Greater than 40000.00
-
-







Then - Actions
-








-------- -------- // > 200 units away: Only issue Move if target changed -------- --------
-








Custom script: set udg_TempReal = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 338)
-








Custom script: set udg_TempReal2 = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 339)
-








Custom script: if (udg_TempReal != udg_Kodo_HomeX) or (udg_TempReal2 != udg_Kodo_HomeY) then
-








Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 338, udg_Kodo_HomeX)
-








Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 339, udg_Kodo_HomeY)
-








Custom script: call IssuePointOrder(udg_Kodo_Unit, "move", GetRectCenterX(udg_Kodo_HomeRect), GetRectCenterY(udg_Kodo_HomeRect))
-








Custom script: endif
-
-







Else - Actions
-








-------- -------- // Arrived home: Clear saved coords so she doesn't get stuck later -------- --------
-








Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 338, 0.0)
-








Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 339, 0.0)
-
-
-
-
-
-



Else - Actions
-




-------- // ── MOVING: check if close enough to safe point ── --------
-




If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-





If - Conditions
-






Kodo_State Equal to 1
-
-





Then - Actions
-






-------- // Validate that the target zone is still active --------
-






Custom script: set udg_TempInt = LoadInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 331)
-






Custom script: if udg_Danger_EndTime[udg_TempInt] <= TimerGetElapsed(udg_AI_GameTimer) then
-






Custom script: call SaveInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 330, 0)
-






Custom script: return
-






Custom script: endif
-






-------- ///////////////// --------
-






Custom script: set udg_Kodo_SafeX = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 332)
-






Custom script: set udg_Kodo_SafeY = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 333)
-






Custom script: set udg_TempDist = (GetUnitX(udg_Kodo_Unit)-udg_Kodo_SafeX)[I](GetUnitX(udg_Kodo_Unit)-udg_Kodo_SafeX) + (GetUnitY(udg_Kodo_Unit)-udg_Kodo_SafeY)[/I](GetUnitY(udg_Kodo_Unit)-udg_Kodo_SafeY)
-






If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-







If - Conditions
-








TempDist Greater than 40000.00
-
-







Then - Actions
-








-------- // Still far – keep moving, only if target changed --------
-








Custom script: set udg_TempReal = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 338)
-








Custom script: set udg_TempReal2 = LoadReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 339)
-








Custom script: if (udg_TempReal != udg_Kodo_SafeX) or (udg_TempReal2 != udg_Kodo_SafeY) then
-








Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 338, udg_Kodo_SafeX)
-








Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 339, udg_Kodo_SafeY)
-








Custom script: call IssueImmediateOrder(udg_Kodo_Unit, "stop")
-








Custom script: call IssuePointOrder(udg_Kodo_Unit, "move", udg_Kodo_SafeX, udg_Kodo_SafeY)
-








Custom script: endif
-
-







Else - Actions
-








-------- // Close enough – begin channeling --------
-








Custom script: set udg_TempInt = LoadInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 331)
-








-------- // expire the zone --------
-








Custom script: set udg_Danger_EndTime[udg_TempInt] = TimerGetElapsed(udg_AI_GameTimer)
-








-------- // FACE the danger zone centre --------
-








Custom script: set udg_TempReal = bj_RADTODEG * Atan2(udg_Danger_Y[udg_TempInt] - GetUnitY(udg_Kodo_Unit), udg_Danger_X[udg_TempInt] - GetUnitX(udg_Kodo_Unit))
-








Custom script: call SetUnitFacing(udg_Kodo_Unit, udg_TempReal)
-








-------- // Switch to CHANNELING --------
-








Custom script: call SaveInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 330, 2)
-








-------- // cooldown start --------
-








Custom script: call SaveReal(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 334, TimerGetElapsed(udg_AI_GameTimer))
-








-------- // Pause and start fireeeee --------
-








Custom script: call PauseUnit(udg_Kodo_Unit, true)
-








Animation - Play Kodo_Unit's spell puke animation
-








Animation - Change Kodo_Unit's animation speed to 50.00% of its original speed
-








-------- // ----- Fire breath via SFX Manager ----- --------
-








-------- // Compute direction toward the danger zone (radians) --------
-








Custom script: set udg_TempReal = Atan2(udg_Danger_Y[udg_TempInt] - GetUnitY(udg_Kodo_Unit), udg_Danger_X[udg_TempInt] - GetUnitX(udg_Kodo_Unit))
-








-------- // Mouth position: 150 units in front of the Kodo --------
-








Custom script: set udg_GLOBAL_Temp_Point = Location(GetUnitX(udg_Kodo_Unit) + 250.0 * Cos(udg_TempReal), GetUnitY(udg_Kodo_Unit) + 250.0 * Sin(udg_TempReal))
-








Set SFX_ModelPath = Doodads\Cinematic\FireTrapSide\FireTrapSide.mdl
-








Set SFX_CreatePoint = GLOBAL_Temp_Point
-








Set SFX_CreateDelay = 0.50
-








Set SFX_DestroyDelay = 3.00
-








Set SFX_Scale = 3.00
-








Set SFX_TimeScale = 0.40
-








-------- // correct model orientation --------
-








Custom script: set udg_SFX_Yaw = udg_TempReal - bj_PI/2
-








Set SFX_RemovePoint = True
-








Custom script: call GuiSpecialEffectSystem_Run()
-








-------- // Start 3‑second timer --------
-








Custom script: set udg_Kodo_ChannelTimer = CreateTimer()
-








Custom script: call SaveUnitHandle(udg_AI_Hashtable, GetHandleId(udg_Kodo_ChannelTimer), 0, udg_Kodo_Unit)
-








Custom script: call TimerStart(udg_Kodo_ChannelTimer, 3.00, false, function KodoChannelEnd)
-
-
-
-





Else - Actions
-






If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-







If - Conditions
-








Kodo_State Equal to 2
-
-







Then - Actions
-








-------- // Safety: if somehow unpaused, reset to idle --------
-








Custom script: if not IsUnitPaused(udg_Kodo_Unit) then
-








Custom script: call SaveInteger(udg_AI_Hashtable, GetHandleId(udg_Kodo_Unit), 330, 0)
-








Custom script: endif
-
-







Else - Actions
-
-
-
-
-
-
-
Technical Notes & Performance
I’m currently running this on Quenching graphics, which has been great for the aesthetic I’m aiming for. However, I’ve been keeping a close eye on technical stability. Lately, I’ve been looking into how the engine handles DirectX 12.While it’s running okay for now, I’m a bit worried about potential performance bottlenecks or memory issues as the project grows in complexity—especially since DX12 can sometimes be a bit "temperamental" with the x86_64 client[cite: 1]. If anyone here is knowledgeable about the engine's response to DX12—specifically regarding draw call overhead or how it manages the rendering pipeline; I’d love to hear your thoughts or experiences.
What's Next?
The current focus is on refining the core mechanics and ensuring the "feel" of the gameplay is responsive. I’m trying to avoid the "feature creep" trap by polishing what’s already there before moving on to the next major milestone.Why the Slow Burn?
I should mention that a big reason for the paced development is that I’m building most of my own systems and mechanics from the ground up. Rather than relying on standard templates, I’m trying to create a very specific RPG/RTS merge.The project is quite heavy on:
- Custom Gameplay Systems: Tailored mechanics to bridge the gap between hero-focused RPG play and large-scale RTS strategy.
- Narrative & Scenarios: Deeply integrated story-driven missions.
- Cinematics: High-effort in-engine sequences to keep the immersion consistent.
I'll be updating this thread as I make more progress. ( I’ll keep you updated on medal too)
Thanks for checking it out!
- Axolotl
Last edited:

