Skip to content

100pluscuci Malaysia Players Malaysia Malaysia Games Official Youtube Channel Supported Menyatukan

100cuci free credit no deposit casino promotions malaysia, cuci100 login and access your malaysia casino account, log in to your 100cuci account and enjoy 100plus Casino Live Dan Fightercuci Series 100plus Casino Menyatukan Website Special Webpages thousands of slots live casino malaysia super league sports betting e sports and lottery all in one platform you already trust.

100 cuci malaysia online online slot sportsbook, 100pluscuci malaysia players online gambling casino, shopee indonesia 提供多樣化的購物選擇 讓用戶輕鬆管理訂單和購買記錄, explore original 2026 guides to games platform features promotion terms and safer 100cuci Malaysia Kredit Players Casino 100 Cuci Membawa Aliran Load play checks for malaysian adults at 100cuci game.

Free Kredit V70 Slot Cuci100asia Brings Premier Check Aliran

Masuk ke laman rasmi 100cuci malaysia untuk free kredit rm100 100cuci Free Cuci100asia Vip Now Join Cuci88 Videos Baru Primary slot game casino login daftar akaun deposit rm1 withdraw rm25 dan panduan bermain, daftar 100cuci dan semak tawaran free kredit rm100 tanpa deposit pusing ganti 5 had pengeluaran ikut terma dan akses rasmi app h5.

100cuc casino malaysia free credit slots live dealer games, 100cuci malaysia login slots online casino guide, cuci100asia malaysia slot providers with live casino, join cuci88 malaysia s favourite online casino and sportsbook play live casino slots Fightercuci Malaysia Vip Cuci100asia Claim Rm99 Instantly Also Vip and sports betting with myr fast payouts via touch n go boost.

Shopee Indonesia Arrests Untuk Log Masuk Akaun Masuk Media 100cuc

100plus casino menyatukan live casino slots dan sportsbook dengan touch n go grabpay boost serta fpx untuk wilayah malaysia yang disokong, 100cuci login and access your account instantly, 100cuci login access Mediation Of Log Bonus Official Youtube Disokong Use your account play now, official youtube channel of the deputy mayor of surabaya follow armuji s other social media accounts get it ig cakj1 cakj1 twitter cakarmuji cakarmuji tiktok cakj1 cakj1 facebook.

Eligible new members can check the current 100cuci rm5 free credit no deposit promotion before making an initial deposit the promotion page also includes daily free credit reload bonuses rebates referral rewards vip bonuses and other current 100cuci offers, log in on any browser M2cuci Free Games 365 Your 365cuci Malaysia S Malaysian Dan Konteks using your credentials and the otp sent to your registered number your account wallet grabpay or boost links and account balance all load the same on any device.

100cuci game malaysia 2026 game guides safer play, Cuci88 Malaysian Live Pemain Jual Logging Into Into Personal 100pluscuci satukan live casino slots dan sportsbook dengan touch n go grabpay boost dan fpx untuk wilayah malaysia yang disokong, 100cuci official premier online casino malaysia website.

365freecreditewallet Now Games Baru Tiap Semester There Across

365cuci malaysia s most trusted free credit 365 online casino claim free credit no deposit play top online slots live casino Mediation Of Beli Access Akaun If Your Login Surabaya Channel Explore sports betting sign up now win real money, cuci100 login to your account and access the lobby, if your login is not going through wrong password otp not arriving or account showing a hold there are a few routes to get it sorted the fastest path is live chat accessible from the login page itself without needing to be signed in.

Browse files
committed
Show cumulative race statistics on all match result screens
1 parent 933fb73 commit 6499095

5 files changed

Lines changed: 223 additions & 10 deletions

File tree

‎.github/workflows/deploy-pages.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ jobs:
7777
run_retaliation_tests.gd \
7878
run_human_strategy_tests.gd \
7979
run_individual_pursuit_tests.gd \
80+
run_match_results_tests.gd \
8081
run_squad_ai_tests.gd \
8182
run_tile_defence_tests.gd \
8283
run_symmetric_map_tests.gd \

‎Scripts/Classic/classic_game.gd‎

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ var recruit_bar: ProgressBar
3232
varrecruit_label: Label
3333
varhint_panel: PanelContainer
3434
varannouncement_label: Label
35+
varmodal_header: BoxContainer
36+
varresult_stats: GridContainer
37+
var_showing_results: bool=false
3538
varmodal_title: Label
3639
varmodal_body: Label
3740
varmodal_scroll: ScrollContainer
@@ -286,8 +289,19 @@ func _build_modal() -> void:
286289
varbox: VBoxContainer=VBoxContainer.new()
287290
box.add_theme_constant_override("separation", 16)
288291
margin.add_child(box)
292+
modal_header=BoxContainer.new()
293+
modal_header.add_theme_constant_override("separation", 20)
294+
box.add_child(modal_header)
289295
modal_title=_label("Paused", 30)
290-
box.add_child(modal_title)
296+
modal_title.size_flags_horizontal=Control.SIZE_EXPAND_FILL
297+
modal_header.add_child(modal_title)
298+
result_stats=GridContainer.new()
299+
result_stats.name="ResultStats"
300+
result_stats.columns=4
301+
result_stats.add_theme_constant_override("h_separation", 12)
302+
result_stats.add_theme_constant_override("v_separation", 4)
303+
result_stats.hide()
304+
modal_header.add_child(result_stats)
291305
modal_body=_label("", 15)
292306
modal_body.autowrap_mode=TextServer.AUTOWRAP_WORD_SMART
293307
modal_body.size_flags_horizontal=Control.SIZE_EXPAND_FILL
@@ -300,7 +314,7 @@ func _build_modal() -> void:
300314
box.add_child(resume_button)
301315
replay_button=_button("Play again", func() ->void: _start_match(0if_spectatingelse_team, _spectating))
302316
box.add_child(replay_button)
303-
quit_button=_button("Return to school selection", _show_menu)
317+
quit_button=_button("Return to menu", _show_menu)
304318
box.add_child(quit_button)
305319

306320

@@ -368,8 +382,11 @@ func _layout() -> void:
368382
announcement_label.size=Vector2(size.x, 40)
369383
ifmodal!=null:
370384
vardialog: Control=modal.get_node("Dialog")
371-
dialog.position=Vector2((size.x-560) *0.5, (size.y-_dialog_height) *0.5)
372-
dialog.size=Vector2(560, _dialog_height)
385+
vardialog_width: float=minf(720.0if_showing_resultselse560.0, maxf(320.0, size.x-32.0))
386+
modal_header.vertical=_showing_resultsanddialog_width<650.0
387+
vardialog_height: float= (440.0ifmodal_header.verticalelse360.0) if_showing_resultselse_dialog_height
388+
dialog.size=Vector2(dialog_width, dialog_height)
389+
dialog.position= (size-dialog.size) *0.5
373390

374391

375392
func_update_difficulty_tooltip(selected: int) ->void:
@@ -414,6 +431,8 @@ func _show_menu() -> void:
414431

415432

416433
func_show_dialog(title: String, body: String, can_resume: bool, can_replay: bool) ->void:
434+
_showing_results=false
435+
result_stats.hide()
417436
board.paused=true
418437
board.stop_audio()
419438
board.cancel_selection()
@@ -427,6 +446,8 @@ func _show_dialog(title: String, body: String, can_resume: bool, can_replay: boo
427446
replay_button.visible=can_replay
428447
modal.show()
429448
_layout()
449+
# Recenter after containers apply text wrapping and minimum sizes.
450+
_layout.call_deferred()
430451
ifcan_resume:
431452
resume_button.grab_focus()
432453
else:
@@ -449,7 +470,7 @@ func _show_pause() -> void:
449470

450471

451472
func_show_quit() ->void:
452-
_show_dialog("Leave this match?", "Returning to school selection ends the current match.", true, false)
473+
_show_dialog("Leave this match?", "Returning to the menu ends the current match.", true, false)
453474

454475

455476
func_resume() ->void:
@@ -461,11 +482,38 @@ func _show_result(won: bool) -> void:
461482
if_spectating:
462483
varwinner: int=board.simulation.get_winner_team()
463484
_show_dialog("Battle Concluded", "%s stand victorious. All rival armies have fallen."%board.team_names[winner] ifwinner>=0else"The battle has ended.", false, true)
464-
return
465-
ifmedieval:
485+
elifmedieval:
466486
_show_dialog("Victory!"ifwonelse"Defeat", "%s stand victorious. All rival armies have fallen, and the battlefield is yours."%board.team_names[_team] ifwonelse"Your army has fallen, but the war is not over. Rally your warriors and march into battle once more.", false, true)
487+
else:
488+
_show_dialog("You win !!"ifwonelse"You lose...", "%s school eliminated all three opponents."%board.team_names[_team] ifwonelse"Your last student has been defeated. Choose a school and try again.", false, true)
489+
_showing_results=true
490+
ifboard.simulation.active.count(true) >1:
491+
modal_body.text+="\nSurviving armies ranked provisionally by living units, then kills."
492+
# Match-finished can fire partway through the final simulation tick. Read
493+
# totals after that tick completes so later attacks in it are included.
494+
_refresh_result_stats.call_deferred()
495+
496+
497+
func_refresh_result_stats() ->void:
498+
ifnot_showing_resultsornotmodal.visible:
467499
return
468-
_show_dialog("You win !!"ifwonelse"You lose...", "%s school eliminated all three opponents."%board.team_names[_team] ifwonelse"Your last student has been defeated. Choose a school and try again.", false, true)
500+
forchild: Nodeinresult_stats.get_children():
501+
result_stats.remove_child(child)
502+
child.queue_free()
503+
forheading: Stringin ["Race"ifmedievalelse"School", "Time", "Kills", "Deaths"]:
504+
varlabel: Label=_label(heading, 12)
505+
label.add_theme_color_override("font_color", Color("aaaaaa"))
506+
result_stats.add_child(label)
507+
forrecord: Dictionaryinboard.simulation.get_match_results():
508+
varseconds: int=int(record.survival_ticks) /ClassicRules.TICKS_PER_SECOND
509+
varvalues: Array[String] = [board.team_names[record.team] +" :", "%dm %02ds"% [seconds/60, seconds%60], str(record.kills), str(record.deaths)]
510+
forcolumn: intin4:
511+
varlabel: Label=_label(values[column], 13)
512+
ifcolumn>0:
513+
label.horizontal_alignment=HORIZONTAL_ALIGNMENT_RIGHT
514+
result_stats.add_child(label)
515+
result_stats.show()
516+
_layout()
469517

470518

471519
func_announce(text: String) ->void:

‎Scripts/Classic/classic_simulation.gd‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ var last_spawn_base: Array[Vector2i] = []
1818
varplayer_team: int=0
1919
varspectating: bool=false
2020
varticks: int=0
21+
varteam_kills: Array[int] = [0, 0, 0, 0]
22+
varteam_deaths: Array[int] = [0, 0, 0, 0]
23+
vareliminated_at: Array[int] = [-1, -1, -1, -1]
24+
varelimination_order: Array[int] = []
2125
varleader: int=-1
2226
varfinished: bool=false
2327
varai_enabled: bool=true
@@ -62,6 +66,10 @@ func setup(team: int, map_seed: int, spectator: bool = false) -> void:
6266
recruit_progress.assign([0, 0, 0, 0])
6367
last_spawn_base.assign([Vector2i(-1, -1), Vector2i(-1, -1), Vector2i(-1, -1), Vector2i(-1, -1)])
6468
ticks=0
69+
team_kills.assign([0, 0, 0, 0])
70+
team_deaths.assign([0, 0, 0, 0])
71+
eliminated_at.assign([-1, -1, -1, -1])
72+
elimination_order.clear()
6573
leader=-1
6674
finished=false
6775
varorder: Array[int] = [0, 1, 2, 3]
@@ -1151,6 +1159,9 @@ func _apply_hit(student: ClassicStudent, target: ClassicStudent) -> void:
11511159
target.orc_conversion_hits.append(ticks)
11521160
target.health-=get_punch_damage(student, target)
11531161
iftarget.health<=0.0:
1162+
iftarget.is_alive() andstudent.team!=target.team:
1163+
team_kills[student.team] +=1
1164+
team_deaths[target.team] +=1
11541165
target.health=0.0
11551166
_target_died(target)
11561167
target.state=ClassicStudent.State.DEAD
@@ -1217,6 +1228,8 @@ func _dying(student: ClassicStudent) -> void:
12171228
func_check_elimination(team: int) ->void:
12181229
ifactive[team] andget_count(team) ==0:
12191230
active[team] =false
1231+
eliminated_at[team] =ticks
1232+
elimination_order.append(team)
12201233
team_eliminated.emit(team)
12211234
varsurvivors: int=active.count(true)
12221235
ifsurvivors==1or (player_team>=0andnotactive[player_team]):
@@ -1622,3 +1635,30 @@ func _respond_to_hit(defender: ClassicStudent, attacker: ClassicStudent) -> void
16221635

16231636
funcget_winner_team() ->int:
16241637
returnactive.find(true) ifactive.count(true) ==1else-1
1638+
1639+
1640+
funcget_match_results() ->Array[Dictionary]:
1641+
# Living armies lead. A player defeat may freeze several surviving armies;
1642+
# rank those provisionally by living count, kills, then stable faction order.
1643+
varsurvivors: Array[int] = []
1644+
varliving_counts: Array[int] = [0, 0, 0, 0]
1645+
forunit: ClassicStudentinstudents:
1646+
ifunit.is_alive():
1647+
living_counts[unit.team] +=1
1648+
forteam: intin4:
1649+
ifactive[team]:
1650+
survivors.append(team)
1651+
survivors.sort_custom(func(a: int, b: int) ->bool:
1652+
ifliving_counts[a] !=living_counts[b]:
1653+
returnliving_counts[a] >living_counts[b]
1654+
ifteam_kills[a] !=team_kills[b]:
1655+
returnteam_kills[a] >team_kills[b]
1656+
returna<b)
1657+
varordered: Array[int] =survivors.duplicate()
1658+
forindex: intinrange(elimination_order.size() -1, -1, -1):
1659+
ordered.append(elimination_order[index])
1660+
varresult: Array[Dictionary] = []
1661+
forteam: intinordered:
1662+
varduration: int=eliminated_at[team] ifeliminated_at[team] >=0elseticks
1663+
result.append({"team": team, "survival_ticks": duration, "kills": team_kills[team], "deaths": team_deaths[team]})
1664+
returnresult

‎Testing/run_match_results_tests.gd‎

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
extendsSceneTree
2+
3+
varfailures: int=0
4+
5+
func_initialize() ->void:
6+
call_deferred("_run")
7+
8+
func_check(ok: bool, message: String) ->void:
9+
ifnotok:
10+
failures+=1
11+
push_error(message)
12+
13+
func_fixture(medieval: bool=true, spectator: bool=true) ->ClassicSimulation:
14+
varsim:=ClassicSimulation.new()
15+
sim.medieval=medieval
16+
sim.setup(0, 123, spectator)
17+
sim.ai_enabled=false
18+
forunit: ClassicStudentinsim.students:
19+
unit.state=ClassicStudent.State.UNUSED
20+
sim.occupancy.clear()
21+
forrace: intin4:
22+
sim._initialize_student(sim.students[race], race, Vector2i(20+race, 20))
23+
sim.students[race].state=ClassicStudent.State.SIT
24+
returnsim
25+
26+
func_run() ->void:
27+
formedieval: boolin [false, true]:
28+
varsim:=_fixture(medieval)
29+
varkiller: ClassicStudent=sim.students[0]
30+
varvictim: ClassicStudent=sim.students[1]
31+
victim.health=1
32+
sim._apply_hit(killer, victim)
33+
_check(sim.team_kills[0] ==1andsim.team_deaths[1] ==1, "Lethal damage counts once in both modes")
34+
sim._initialize_student(victim, 1, Vector2i(21,20))
35+
victim.health=1
36+
sim._apply_hit(killer,victim)
37+
_check(sim.team_kills[0] ==2andsim.team_deaths[1] ==2, "Match totals survive unit replacement")
38+
sim.setup(0,123,true)
39+
_check(sim.team_kills== [0,0,0,0] andsim.team_deaths== [0,0,0,0] andsim.elimination_order.is_empty(), "Replay resets all cumulative results")
40+
# A guaranteed conversion still records the victim's death and killer's race.
41+
varsim:=_fixture()
42+
varorc: ClassicStudent=sim.students[1]
43+
varvictim: ClassicStudent=sim.students[2]
44+
victim.health=1
45+
victim.orc_conversion_hits.assign([0,0,0])
46+
sim._apply_hit(orc,victim)
47+
_check(sim.team_kills[1] ==1andsim.team_deaths[2] ==1andsim.get_converted_count(1) ==1, "Conversion records one kill/death without counting its new unit as dead")
48+
# Placement follows elimination sequence, including ties in simulation ticks.
49+
sim=_fixture()
50+
varexpected_times: Array[int] = [1080,720,180,900]
51+
forrace: intin [2,1,3]:
52+
sim.ticks=expected_times[race]
53+
sim.students[race].state=ClassicStudent.State.UNUSED
54+
sim._check_elimination(race)
55+
sim.ticks=1080
56+
varrows: Array[Dictionary] =sim.get_match_results()
57+
_check(rows.size() ==4androws[0].team==0androws[1].team==3androws[2].team==1androws[3].team==2, "Winner and reverse elimination order give all four placements")
58+
forrow: Dictionaryinrows:
59+
_check(row.survival_ticks==expected_times[row.team], "Eliminated times freeze while winner time uses final battle tick")
60+
varfrozen: int=sim.ticks
61+
sim.step()
62+
_check(sim.ticks==frozen, "Finished match does not advance survival time")
63+
sim=_fixture()
64+
forrace: intin [2,1,3]:
65+
sim.ticks=180
66+
sim.students[race].state=ClassicStudent.State.UNUSED
67+
sim._check_elimination(race)
68+
rows=sim.get_match_results()
69+
_check(rows[1].team==3androws[3].team==2, "Same-tick eliminations preserve actual elimination sequence")
70+
await_test_dialogs()
71+
print("Match results tests: %d failure(s)"%failures)
72+
quit(1iffailureselse0)
73+
74+
func_test_dialogs() ->void:
75+
varscene: PackedScene=load("res://Scenes/Classic/classic_game.tscn")
76+
formedieval: boolin [false,true]:
77+
forspectator: boolin [false,true]:
78+
forwon: boolin [false,true]:
79+
vargame=scene.instantiate()
80+
game.medieval=medieval
81+
root.add_child(game)
82+
game.set_process(false)
83+
game.board.set_process(false)
84+
game._spectating=spectator
85+
game._team=-1ifspectatorelse0
86+
game.board.simulation=_fixture(medieval, spectator)
87+
varsim: ClassicSimulation=game.board.simulation
88+
ifwonorspectator:
89+
forrace: intin [2,1,3]:
90+
sim.ticks+=180
91+
sim.students[race].state=ClassicStudent.State.UNUSED
92+
sim._check_elimination(race)
93+
else:
94+
sim.ticks=1350
95+
sim.students[0].state=ClassicStudent.State.UNUSED
96+
sim._check_elimination(0)
97+
sim.team_kills.assign([7,4,2,1])
98+
sim.team_deaths.assign([2,8,6,3])
99+
game._show_result(won)
100+
awaitprocess_frame
101+
awaitprocess_frame
102+
_check(game.result_stats.visibleandgame.result_stats.get_child_count() ==20, "Every victory/defeat/spectator dialog has header and four stat rows")
103+
_check(game.quit_button.text=="Return to menu", "All result screens use Return to menu")
104+
ifnotwonandnotspectator:
105+
_check("provisionally"ingame.modal_body.text, "Early player defeat identifies provisional surviving places")
106+
_check(game.result_stats.get_child(16).text==game.board.team_names[0] +" :", "Eliminated player follows all surviving armies")
107+
else:
108+
_check(game.result_stats.get_child(4).text==game.board.team_names[0] +" :", "Winner appears first")
109+
# Check wide and compact layouts with the actual Godot containers/fonts.
110+
forwidth: intin [960,576]:
111+
game.size=Vector2(width,600)
112+
game._layout()
113+
awaitprocess_frame
114+
awaitprocess_frame
115+
vardialog: Control=game.modal.get_node("Dialog")
116+
_check(dialog.get_rect().end.x<=game.size.xanddialog.position.x>=0, "Result dialog fits viewport width")
117+
_check(game.result_stats.get_global_rect().end.y<=game.replay_button.get_global_rect().position.y, "Stats remain above replay controls")
118+
_check(game.result_stats.get_global_rect().end.x<=dialog.get_global_rect().end.x, "Stat columns fit inside dialog")
119+
game._show_pause()
120+
_check(notgame.result_stats.visible, "Pause/help dialogs do not leak prior results")
121+
game._show_menu()
122+
game.free()

‎game_technicals.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Medieval Wars — Current Game Mechanics
22

3-
Checked against `main` gameplay code at **1b13e414** on **3 November 2026**. This document describes the `/v2` Medieval rules. Shared mechanics appear first; each race's stats, exceptions, progression and AI appear only in its race section. Times are battle seconds at 1× speed unless stated otherwise.
3+
Checked against current gameplay code on **3 November 2026**. This document describes the `/v2` Medieval rules. Shared mechanics appear first; each race's stats, exceptions, progression and AI appear only in its race section. Times are battle seconds at 1× speed unless stated otherwise.
44

55
## Contents
66

@@ -286,6 +286,8 @@ AI Demons do not initiate healing retreats. The old casualty-based ranged-respon
286286

287287
## 10. Interface
288288

289+
Every victory, defeat and spectator result displays a four-row table of race, survival time (`Xm YYs`), cumulative kills and cumulative deaths. Time uses simulation ticks from match start to recorded elimination; surviving armies use the final tick. Lethal defeats count immediately, totals survive replacement recruits, and a new match resets all statistics. Final standings put the winner first, followed by reverse elimination order; equal-tick eliminations retain their recorded sequence. Early player defeat can leave several armies alive: those survivors are provisionally sorted by living units, then kills, then faction order, above eliminated armies. The result screen labels these provisional standings. The exit button reads **Return to menu**.
290+
289291
- The top bar reports regular population/capacity. Fallen units can remain counted until cleanup. Race-specific extra population is defined in that race's section.
290292
- The bottom-left spawning box shows the shared meter, capacity-full/base-blocked state and an estimate based on current boosts. Spectator mode identifies itself instead of reporting one selected army's meter.
291293
- One selected warrior shows current/max HP and base-plus-gain punch, reach, movement and attack rate. Active temporary damage bonuses are included; several selected warriors show a count instead of aggregate stats.
@@ -311,4 +313,4 @@ The playable entry scene is `Scenes/Classic/classic_game.tscn`. `/` selects orig
311313

312314
**Retained-code distinction:**`medieval_ranged_response.gd` still contains older hit-triggered group/chase/retreat routines and direct tests. Normal AI attack processing registers universal retaliation first; its guards then prevent creating those legacy groups. Their existence is not evidence that old group casualty rolls or hit-triggered flee decisions normally activate in a running match. Active shared-target squad orders and tile defence remain, even though coordinated pursuit routing was removed.
313315

314-
For this documentation update, facts were checked against the source above; **14 existing Godot 4.3 headless mechanics suites passed**, and all **23 document links/contents anchors** were validated. Gameplay code is unchanged. Run the relevant `Testing/run_*tests.gd` scripts with Godot headless for executable checks. Web export/browser CI is defined in [.github/workflows/deploy-pages.yml](.github/workflows/deploy-pages.yml).
316+
Facts were checked against the source above. Mechanics and match-results regression suites cover these rules; document links and contents anchors were also validated. Run the relevant `Testing/run_*tests.gd` scripts with Godot headless for executable checks. Web export/browser CI is defined in [.github/workflows/deploy-pages.yml](.github/workflows/deploy-pages.yml).

0 commit comments

Comments
 (0)