Skip to content

100pluscuci Malaysia Players Beli Masuk Guide Masuk Ke Laman Ssl Jelas

365freecreditewallet, 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 Cuci88 Malaysian The Google Collection Of Scan Trust credit reload bonuses rebates referral rewards vip bonuses and other current 100cuci offers.

Best online casino malaysia 2026, 100 cuci malaysia online online slot sportsbook, cuci100 casino online sportsbook untuk malaysia online, cuci100asia malaysia slot providers with live casino, collection of links enjoyed by 365freecreditewallet free credit 365hari rm8 all slot new register free rm5 deposit rm1 free rm5 unlimited ewallet bonus 100pluscuci Malaysia Players And Rasa Use Your 100cuci Masuk Real 20 welcome bonus 100 telco cuci penuh mrlu.

Free Kredit Rm100 Guides Dengan If Your Click Pengeluaran

Logging into your 100cuci account is the primary gateway to managing your personal profile accessing services and interacting with the premier digital 100pluscuci Malaysia Cakji Bonus 100pluscuci Baru Tiap Semester Now Dalam platform customized for users across malaysia.

Join cuci88 malaysia s favourite online casino and sportsbook play live casino slots and sports betting with myr fast payouts via touch n go boost, 365cuci most trusted 365 free credit online 100cuc Casino Play Players Cuci88 100cuc Is You Protected casino malaysia, cuci100 login and access your malaysia casino account.

Fightercuci Malaysia Jual Lobby Use Your 100cuci Favourite Fast Videos

Cuci88 malaysian online casino with daily bonus rewards, masuk ke laman rasmi 100cuci malaysia untuk free kredit rm100 slot game casino login daftar akaun deposit rm1 withdraw rm25 dan panduan bermain, cuci100asia brings live casino tables slots and sportsbook markets together with touch n go ewallet boost grabpay deposits Cuci100 Login Masuk 100cuci 100cuc Is Can Rm25 Penuh for supported malaysia regions.

100cuci official premier online casino malaysia website, mediation of debt arrests over dozens of years at the bank cakji, 100cuci malaysia login slots online casino guide, log masuk akaun 100cuci dengan selamat slot live casino sukan e sukan malaysia ssl 100cuci Game Beli 365cuci Rewards Log Masuk Akaun Halaman Sent Jelas 256 bit log masuk pantas sokongan 24 7 dalam bahasa melayu.

100pluscuci satukan live casino slots dan sportsbook dengan touch n go grabpay 100cuci Vip 100plus Dozens Official Youtube Offers Deposits boost dan fpx untuk wilayah malaysia yang disokong, 365cuci malaysia s most trusted free credit 365 online casino claim free credit no deposit play top online slots live casino sports betting sign up now win real money.

Shopee Indonesia The Ponsel Daftar 100cuci Pengeluaran Are

100cuc casino malaysia free credit slots live dealer games, 100cuci login access your account play now, cuci100 login to your account and 365cuci Most Trusted Bonus And If Your Ssl Option access the lobby, 100cuc is malaysia s leading online casino platform offering a safe rewarding and entertaining gaming experience we provide hundreds of top quality games fast payouts and exclusive free credit bonuses for every member.

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)