vrtc / honeydew (public) (License: CC0) (since 2023-12-20) (hash sha1)
Warcraft 3: The Frozen Throne 1.27 custom scenario.
List of commits:
Subject Hash Author Date (UTC)
feat: add hero selection 230e7f365c3b564c39edeb7a5d76ceb343234da1 Vladyslav Bondarenko 2023-12-14 16:26:23
build: Use git-archive to package bundles 666f7110532eba52212a35290a9e194170a00bc7 Vladyslav Bondarenko 2023-12-14 12:11:09
feat!: build skeleton project da405d9978fcb6c8414249ceda4e07848e34f62a Vladyslav Bondarenko 2023-12-14 11:59:17
Commit 230e7f365c3b564c39edeb7a5d76ceb343234da1 - feat: add hero selection
Select hero at the altar and resurrect them after death for a fee.

Scenario is considered failed when all users have their heroes dead and
do not have gold to pay the revival fee.
Author: Vladyslav Bondarenko
Author date (UTC): 2023-12-14 16:26
Committer name: Vladyslav Bondarenko
Committer date (UTC): 2023-12-16 03:14
Parent(s): 666f7110532eba52212a35290a9e194170a00bc7
Signer:
Signing key: EFF9624877D25D02
Signing status: E
Tree: f10cf9d145815898d76329a3f1a0505e2f5271d9
File Lines added Lines deleted
Makefile 10 2
doc/honeydew.md 8 0
honeydew-0_0WIP-1_27-en.w3x 0 0
src/altar.j 74 0
src/config.j 19 9
src/defeat.j 99 0
src/herotoken.j 56 0
src/main.j 32 38
src/revive.j 278 0
src/user.j 96 0
src/war3map.m4 5 0
File Makefile changed (mode: 100644) (index d6ca0c1..28f27ee)
... ... CLASSIFIER=1_27
6 6 LANG=en LANG=en
7 7 NAMEFULL=$(NAME)-$(VERSION)-$(CLASSIFIER)-$(LANG) NAMEFULL=$(NAME)-$(VERSION)-$(CLASSIFIER)-$(LANG)
8 8
9 PJASS_OPTS=etc/common.j etc/Blizzard.j etc/common.ai -rb +filter +shadow +checkglobalsinit +checkstringhash
10 SNIPPETS=src/main.j src/config.j
9 PJASS_OPTS=etc/common.j etc/Blizzard.j -rb +filter +shadow +checkglobalsinit +checkstringhash
10
11 SNIPPETS= \
12 src/user.j \
13 src/altar.j \
14 src/revive.j \
15 src/defeat.j \
16 src/herotoken.j \
17 src/main.j \
18 src/config.j \
11 19
12 20 BUILDDIR=target BUILDDIR=target
13 21 BASEDIR=. BASEDIR=.
File doc/honeydew.md changed (mode: 100644) (index c2133c0..8fc94cf)
... ... Periodically, about every half a minute since the fight starts, the boss will ma
11 11 Maybe death knights (revenant creature, not hero) periodically appear and cast death coil on the boss to heal it. Maybe death knights (revenant creature, not hero) periodically appear and cast death coil on the boss to heal it.
12 12
13 13 Felhunters (mage hunters) appear from portals and mana burn player heroes. Felhunters (mage hunters) appear from portals and mana burn player heroes.
14
15 ## Boss arena
16
17 The arena in which boss battle takes place is a lake or a river during early spring or early winter.
18
19 Maybe make it so water leads to the hero's base.
20
21 The arena is isolated and must be teleported to. To activate the teleporter waygate, three keys must be obtained.
File honeydew-0_0WIP-1_27-en.w3x changed (mode: 100644) (index 40bcdcf..104a4df)
File src/altar.j added (mode: 100644) (index 0000000..353d984)
1 // src/altar.j
2 // requires src/user.j
3 globals
4 // src/altar.j fields
5 unit array altar_player_structure
6 endglobals
7
8 // src/altar.j functions
9
10 function altar_player_altar_type_get takes player p returns integer
11 return 'halt'
12 endfunction
13
14 function altar_player_altar_unit_get takes player p returns unit
15 if null == p then
16 return null
17 endif
18
19 return altar_player_structure[GetPlayerId(p)]
20 endfunction
21
22 function altar_player_structure_init takes player p returns unit
23 local unit u = null
24 local integer utype = 0
25 local location loc = null
26
27 if null == p then
28 return null
29 endif
30
31 set u = altar_player_structure[GetPlayerId(p)]
32
33 if u != null then
34 return u
35 endif
36
37 set utype = altar_player_altar_type_get(p)
38 set loc = GetStartLocationLoc(GetPlayerStartLocation(p))
39 set u = CreateUnitAtLoc(p, utype, loc, bj_UNIT_FACING)
40
41 call SetUnitInvulnerable(u, true)
42
43 if p == GetLocalPlayer() then
44 call ClearSelection()
45 call SelectUnit(u, true)
46 endif
47
48 call RemoveLocation(loc)
49
50 set altar_player_structure[GetPlayerId(p)] = u
51
52 return u
53 endfunction
54
55 function altar_user_force_callback takes nothing returns nothing
56 local player p = GetEnumPlayer()
57
58 call altar_player_structure_init(p)
59 endfunction
60
61 function altar_structure_init takes nothing returns nothing
62 local force user_force = null
63
64 set user_force = CreateForce()
65 call user_force_enum(user_force)
66
67 call ForForce(user_force, function altar_user_force_callback)
68
69 call DestroyForce(user_force)
70 endfunction
71
72 function altar_init takes nothing returns nothing
73 call altar_structure_init()
74 endfunction
File src/config.j changed (mode: 100644) (index 3339546..d10fc70)
1 // src/config.j
2
1 3 globals globals
4
5 // src/config.j fields
6
2 7 constant integer TEAM_ENEMY = 1 constant integer TEAM_ENEMY = 1
3 8 constant integer TEAM_USER = 0 constant integer TEAM_USER = 0
4 9 constant integer USER_QUANTITY_MAX = 3 constant integer USER_QUANTITY_MAX = 3
5 constant player PLAYER_DEMON = Player(3)
6 constant player USER0 = Player(0)
7 constant player USER1 = Player(1)
8 constant player USER2 = Player(2)
9 10 endglobals endglobals
10 11
12 // src/config.j functions
13
11 14 function config_slot_user takes integer n returns nothing function config_slot_user takes integer n returns nothing
12 15 local integer i = 0 local integer i = 0
13 16 local player p = null local player p = null
 
... ... function config_slot_user takes integer n returns nothing
21 24
22 25 call SetPlayerStartLocation(p, n) call SetPlayerStartLocation(p, n)
23 26 call SetPlayerColor(p, ConvertPlayerColor(n)) call SetPlayerColor(p, ConvertPlayerColor(n))
24 call SetPlayerRacePreference(p, RACE_PREF_HUMAN)
27 call SetPlayerRacePreference(p, RACE_PREF_RANDOM)
25 28 call SetPlayerRaceSelectable(p, true) call SetPlayerRaceSelectable(p, true)
26 29 call SetPlayerController(p, MAP_CONTROL_USER) call SetPlayerController(p, MAP_CONTROL_USER)
27 30 call SetPlayerTeam(p, TEAM_USER) call SetPlayerTeam(p, TEAM_USER)
 
... ... function config_slot_computer takes integer n returns nothing
52 55 set p = Player(n) set p = Player(n)
53 56
54 57 call SetPlayerStartLocation(p, n) call SetPlayerStartLocation(p, n)
58 call ForcePlayerStartLocation(p, n)
55 59 call SetPlayerColor(p, PLAYER_COLOR_BLUE) call SetPlayerColor(p, PLAYER_COLOR_BLUE)
56 60 call SetPlayerRacePreference(p, RACE_PREF_UNDEAD) call SetPlayerRacePreference(p, RACE_PREF_UNDEAD)
57 61 call SetPlayerRaceSelectable(p, false) call SetPlayerRaceSelectable(p, false)
58 62 call SetPlayerController(p, MAP_CONTROL_COMPUTER) call SetPlayerController(p, MAP_CONTROL_COMPUTER)
59 63 call SetPlayerTeam(p, TEAM_ENEMY) call SetPlayerTeam(p, TEAM_ENEMY)
64 call SetPlayerName(p, "Demon")
65 call SetPlayerOnScoreScreen(p, true)
60 66 endfunction endfunction
61 67
62 68 function config_every_start takes nothing returns nothing function config_every_start takes nothing returns nothing
 
... ... function config_every_start takes nothing returns nothing
72 78 loop loop
73 79 exitwhen i >= USER_QUANTITY_MAX exitwhen i >= USER_QUANTITY_MAX
74 80 set x = GetLocationX(origin_loc) + 128.0 * (i + 1) set x = GetLocationX(origin_loc) + 128.0 * (i + 1)
75 set y = GetLocationY(origin_loc) - 128.0
81 set y = GetLocationY(origin_loc) - 512.0 * 4.0
76 82 set loc = Location(x, y) set loc = Location(x, y)
77 83 call DefineStartLocationLoc(i, loc) call DefineStartLocationLoc(i, loc)
78 84 call RemoveLocation(loc) call RemoveLocation(loc)
79 85 set i = i + 1 set i = i + 1
80 86 endloop endloop
81 87
88 set x = GetLocationX(origin_loc)
89 set y = GetLocationY(origin_loc) + 512.0 * 4.0
90 set loc = Location(x, y)
82 91 call DefineStartLocationLoc(3, origin_loc) call DefineStartLocationLoc(3, origin_loc)
92 call RemoveLocation(loc)
83 93
84 94 call RemoveLocation(origin_loc) call RemoveLocation(origin_loc)
85 95 endfunction endfunction
 
... ... endfunction
99 109 function config takes nothing returns nothing function config takes nothing returns nothing
100 110 call SetMapName( "TRIGSTR_001" ) call SetMapName( "TRIGSTR_001" )
101 111 call SetMapDescription( "TRIGSTR_003" ) call SetMapDescription( "TRIGSTR_003" )
102 call SetTeams(1)
103 call SetPlayers(USER_QUANTITY_MAX)
104 call SetGamePlacement(MAP_PLACEMENT_USE_MAP_SETTINGS)
112 call SetTeams(2)
113 call SetPlayers(USER_QUANTITY_MAX + 1)
114 call SetGamePlacement(MAP_PLACEMENT_TEAMS_TOGETHER)
105 115
106 116 call SetGameTypeSupported(GAME_TYPE_USE_MAP_SETTINGS, true) call SetGameTypeSupported(GAME_TYPE_USE_MAP_SETTINGS, true)
107 117
File src/defeat.j added (mode: 100644) (index 0000000..aca3219)
1 // src/defeat.j
2 // requres src/revive.j
3
4 globals
5
6 // src/defeat.j fields
7
8
9 endglobals
10
11 // src/defeat.j functions
12
13 function defeat_trig_filter takes nothing returns boolean
14 local unit u = GetFilterUnit()
15 local player p = null
16 local boolean flag = true
17
18 if null == u then
19 return false
20 endif
21
22 set flag = IsHeroUnitId(GetUnitTypeId(u)) and flag
23 set flag = IsUnitType(u, UNIT_TYPE_HERO) and flag
24 set p = GetOwningPlayer(u)
25 set flag = (MAP_CONTROL_USER == GetPlayerController(p) or PLAYER_SLOT_STATE_LEFT == GetPlayerSlotState(p)) and flag
26 return flag
27 endfunction
28
29 // Users are considered defeated when there are no units left alive,
30 // and no gold left to revive dead heroes,
31 // and no revivals are ongoing, across all user force.
32 function defeat_check takes nothing returns boolean
33 local integer revive_price_min = 170
34 local integer q = 0
35 local integer i = 0
36 local integer g = 0
37 local player p = null
38 local integer y = 0
39
40 if revive_ongoing_check() then
41 return false
42 endif
43
44 set i = 0
45 loop
46 exitwhen i >= bj_MAX_PLAYERS
47 set p = Player(i)
48 if MAP_CONTROL_USER == GetPlayerController(p) and PLAYER_SLOT_STATE_EMPTY != GetPlayerSlotState(p) then
49 set y = y + 1
50 set q = GetPlayerUnitCount(p, true) + q
51 set g = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + g
52 call BJDebugMsg("i:" + I2S(i) + "q:" + I2S(GetPlayerTechCount(p, 'HERO', false)) + "g:" + I2S(GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)))
53 endif
54 set i = i + 1
55 endloop
56
57 return y > 0 and 0 == q and g < revive_price_min
58 endfunction
59
60 // Force defeat dialog on all users.
61 function defeat takes nothing returns nothing
62 local integer i = 0
63 local player p = null
64
65 set i = 0
66 loop
67 exitwhen i >= bj_MAX_PLAYERS
68 set p = Player(i)
69 if MAP_CONTROL_USER == GetPlayerController(p) and PLAYER_SLOT_STATE_EMPTY != GetPlayerSlotState(p) then
70 call CustomDefeatDialogBJ(p, "All heroes are dead and cannot revive.")
71 endif
72 set i = i + 1
73 endloop
74 endfunction
75
76 function defeat_trig_action takes nothing returns nothing
77 if defeat_check() then
78 call defeat()
79 endif
80 endfunction
81
82 function defeat_init takes nothing returns trigger
83 local trigger t = null
84 local integer i = 0
85 local boolexpr filter = null
86
87 set t = CreateTrigger()
88
89 set filter = Condition(function defeat_trig_filter)
90
91 loop
92 exitwhen i >= bj_MAX_PLAYERS
93 call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_DEATH, null)
94 set i = i + 1
95 endloop
96 call TriggerAddAction(t, function defeat_trig_action)
97
98 return t
99 endfunction
File src/herotoken.j added (mode: 100644) (index 0000000..a99f4fd)
1 // src/herotoken.j
2 // requires src/user.j
3 globals
4 // src/herotoken.j fields
5 constant integer HEROTOKEN_QUANTITY_MAX = 3
6 endglobals
7
8 // src/herotoken.j functions
9
10 // Every player will only have one hero of the same type.
11 function herotoken_unique_hero_trig_init takes nothing returns nothing
12 call MeleeStartingHeroLimit()
13 // TODO Add custom heroes to the list of limited units.
14 endfunction
15
16 // Live users at any time may buy or summon at most HEROTOKEN_QUANTITY_MAX hero units total across all users.
17 // function herotoken_init distributes these hero slots evenly among all present users.
18 function herotoken_hero_limit_init takes nothing returns nothing
19 local integer hero_slot_available_quantity = HEROTOKEN_QUANTITY_MAX
20 local integer user_quantity = 0
21 local integer i = 0
22 local integer q = 0
23 local player p = null
24
25 set user_quantity = user_quantity_get()
26 set hero_slot_available_quantity = HEROTOKEN_QUANTITY_MAX
27 if user_quantity > hero_slot_available_quantity then
28 call BJDebugMsg("FIXME too many users and not enough hero slots available")
29 endif
30
31 loop
32 exitwhen hero_slot_available_quantity <= 0 or user_quantity < 1
33 set i = 0
34 loop
35 exitwhen i >= bj_MAX_PLAYERS
36 set p = Player(i)
37 if user_player_is_user_check(p) then
38 set q = GetPlayerTechMaxAllowed(p, 'HERO') + 1
39 call SetPlayerTechMaxAllowed(p, 'HERO', q)
40 call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, 425 * q)
41 call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, 100 * q)
42 set hero_slot_available_quantity = hero_slot_available_quantity - 1
43 endif
44 set i = i + 1
45 endloop
46 endloop
47 endfunction
48
49 function herotoken_init takes nothing returns nothing
50 call herotoken_hero_limit_init()
51 call herotoken_unique_hero_trig_init()
52
53 call BJDebugMsg("HERO: Player0:" + I2S(GetPlayerTechMaxAllowed(Player(0), 'HERO')))
54 call BJDebugMsg("HERO: Player1:" + I2S(GetPlayerTechMaxAllowed(Player(1), 'HERO')))
55 call BJDebugMsg("HERO: Player2:" + I2S(GetPlayerTechMaxAllowed(Player(2), 'HERO')))
56 endfunction
File src/main.j changed (mode: 100644) (index b0b672a..83a88e0)
1 //***************************************************************************
2 //*
3 //* Global Variables
4 //*
5 //***************************************************************************
6
1 // src/main.j
2 // requires src/user.j
3 // requires src/herotoken.j
4 // requires src/defeat.j
5 // requires src/revive.j
6 // requires src/altar.j
7 7 globals globals
8 // Generated
9 trigger gg_trg_Melee_Initialization = null
8 // src/main.j fields
9 constant player PLAYER_DEMON = Player(3)
10 10 endglobals endglobals
11 11
12 function InitGlobals takes nothing returns nothing
13 endfunction
14
15 //***************************************************************************
16 //*
17 //* Triggers
18 //*
19 //***************************************************************************
20
21 //===========================================================================
22 // Trigger: Melee Initialization
23 //===========================================================================
24 function Trig_Melee_Initialization_Actions takes nothing returns nothing
25 endfunction
26
27 //===========================================================================
28 function InitTrig_Melee_Initialization takes nothing returns nothing
29 set gg_trg_Melee_Initialization = CreateTrigger( )
30 call TriggerAddAction( gg_trg_Melee_Initialization, function Trig_Melee_Initialization_Actions )
31 endfunction
32
33 //===========================================================================
34 function InitCustomTriggers takes nothing returns nothing
35 call InitTrig_Melee_Initialization( )
12 // src/main.j functions
13
14 function init_demon takes player p returns nothing
15 local location loc = null
16 local integer start = 0
17 local unit u = null
18 set start = GetPlayerStartLocation(p)
19 set loc = GetStartLocationLoc(start)
20 set u = CreateUnitAtLoc(p, 'Udre', loc, bj_UNIT_FACING)
21 call SetUnitScale(u, 1.3, 1.3, 1.3)
22 call SetHeroLevel(u, 10, false)
23 call SetHeroStr(u, GetHeroStr(u, false) + 36, true)
24 call SetHeroInt(u, GetHeroInt(u, false) + 12, true)
25 call SetUnitUseFood(u, false)
26 call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP, 200)
27 call RemoveLocation(loc)
36 28 endfunction endfunction
37 29
38 //===========================================================================
39 function RunInitializationTriggers takes nothing returns nothing
40 call ConditionalTriggerExecute( gg_trg_Melee_Initialization )
30 function init takes nothing returns nothing
31 call user_init()
32 call init_demon(PLAYER_DEMON)
33 call herotoken_init()
34 call altar_init()
35 call revive_init()
36 call defeat_init()
41 37 endfunction endfunction
42 38
43 39 function main takes nothing returns nothing function main takes nothing returns nothing
 
... ... function main takes nothing returns nothing
48 44 call SetAmbientNightSound( "LordaeronWinterNight" ) call SetAmbientNightSound( "LordaeronWinterNight" )
49 45 call SetMapMusic( "Music", true, 0 ) call SetMapMusic( "Music", true, 0 )
50 46 call InitBlizzard( ) call InitBlizzard( )
51 call InitGlobals( )
52 call InitCustomTriggers( )
53 call RunInitializationTriggers( )
47 call init()
54 48 endfunction endfunction
File src/revive.j added (mode: 100644) (index 0000000..5080e63)
1 // src/revive.j
2 // requires src/user.j
3 globals
4 // src/revive.j fields
5 constant real REVIVE_DURATION_SEC_MIN = 12.0
6 constant real REVIVE_DURATION_SEC_MAX = 60.0
7 constant real REVIVE_DURATION_SEC_BASE = 12.0
8 constant real REVIVE_DURATION_SEC_COEF = 6.0
9
10 integer array revive_player_count
11 timer array revive_timer_list
12 timerdialog array revive_dialog_list
13 unit array revive_unit_list
14 endglobals
15
16 // src/revive.j functions
17
18 // Check if any user has hero revival pending.
19 function revive_ongoing_check takes nothing returns boolean
20 local integer i = 0
21 local integer q = 0
22
23 set i = 0
24 loop
25 exitwhen i >= bj_MAX_PLAYERS
26 set q = q + revive_player_count[i]
27 set i = i + 1
28 endloop
29 return q > 0
30 endfunction
31
32 // Calculate the revival duration for the given hero unit.
33 function revive_duration_get takes unit u returns real
34 local real duration = REVIVE_DURATION_SEC_MIN
35
36 if null == u then
37 set duration = 0.0
38 elseif IsHeroUnitId(GetUnitTypeId(u)) and IsUnitType(u, UNIT_TYPE_HERO) then
39 set duration = REVIVE_DURATION_SEC_BASE + (GetHeroLevel(u) - 1) * REVIVE_DURATION_SEC_COEF
40 else
41 set duration = REVIVE_DURATION_SEC_BASE + (GetUnitLevel(u) - 1) * REVIVE_DURATION_SEC_COEF
42 endif
43
44 return RMinBJ(RMaxBJ(REVIVE_DURATION_SEC_MIN, duration), REVIVE_DURATION_SEC_MAX)
45 endfunction
46
47 // Calculate the revival gold price for the given hero unit.
48 function revive_price_get takes unit u returns integer
49 if null == u then
50 return 0
51 endif
52
53 if IsHeroUnitId(GetUnitTypeId(u)) and IsUnitType(u, UNIT_TYPE_HERO) then
54 return 170 + (GetHeroLevel(u) - 1) * 42
55 endif
56
57 return 0
58 endfunction
59
60 // Instantly and without constraints or penalties
61 // revive given hero unit at the owner's start location.
62 function revive_unit takes unit u returns nothing
63 local player p = null
64 local integer start = -1
65 local location loc = null
66
67 if null == u then
68 return
69 endif
70
71 set p = GetOwningPlayer(u)
72 set start = GetPlayerStartLocation(p)
73 set loc = Location(GetStartLocationX(start), GetStartLocationY(start) - 128.0)
74
75 call ReviveHeroLoc(u, loc, true)
76 call RemoveLocation(loc)
77 if not IsUnitAliveBJ(u) then
78 return
79 endif
80 call SetUnitFacing(u, bj_UNIT_FACING)
81 if p == GetLocalPlayer() then
82 call PanCameraTo(GetUnitX(u), GetUnitY(u))
83 call SelectUnit(u, true)
84 endif
85 endfunction
86
87 function revive_schedule_timer_callback takes nothing returns nothing
88 local timer t = GetExpiredTimer()
89 local unit u = null
90 local integer i = 0
91 local timerdialog g = null
92 local integer k = bj_MAX_PLAYERS
93
94 if null == t then
95 return
96 endif
97
98 set i = 0
99 loop
100 exitwhen i >= JASS_MAX_ARRAY_SIZE or u != null or null == revive_timer_list[i]
101 if t == revive_timer_list[i] then
102 set u = revive_unit_list[i]
103 set g = revive_dialog_list[i]
104 endif
105 set i = i + 1
106 endloop
107 call DestroyTimer(t)
108 call DestroyTimerDialog(g)
109
110 set k = GetPlayerId(GetOwningPlayer(u))
111 set revive_player_count[k] = revive_player_count[k] - 1
112
113 call revive_unit(u)
114 endfunction
115
116 // Pay gold price for revival of the given hero unit,
117 // then delay effective revival for some duration.
118 function revive_schedule takes unit u returns timer
119 local timer t = null
120 local real duration = REVIVE_DURATION_SEC_MIN
121 local timerdialog g = null
122 local string label = null
123 local integer i = 0
124 local integer j = JASS_MAX_ARRAY_SIZE
125 local integer k = bj_MAX_PLAYERS
126
127 if null == u then
128 return null
129 endif
130
131 if IsHeroUnitId(GetUnitTypeId(u)) and IsUnitType(u, UNIT_TYPE_HERO) then
132 set t = CreateTimer()
133 set duration = revive_duration_get(u)
134 set duration = RMinBJ(RMaxBJ(REVIVE_DURATION_SEC_MIN, duration), REVIVE_DURATION_SEC_MAX)
135
136 set g = CreateTimerDialog(t)
137 set label = GetPlayerName(GetOwningPlayer(u)) + " " + GetUnitName(u)
138 call TimerDialogSetTitle(g, label)
139 call TimerDialogDisplay(g, true)
140
141 call TimerStart(t, duration, false, function revive_schedule_timer_callback)
142
143 set k = GetPlayerId(GetOwningPlayer(u))
144 set revive_player_count[k] = revive_player_count[k] + 1
145
146 set i = 0
147 loop
148 exitwhen i >= JASS_MAX_ARRAY_SIZE or (j < JASS_MAX_ARRAY_SIZE and j >= 0)
149 if null == revive_timer_list[i] then
150 set j = i
151 set revive_timer_list[j] = t
152 set revive_dialog_list[j] = g
153 set revive_unit_list[j] = u
154 endif
155 set i = i + 1
156 endloop
157 endif
158
159 return t
160 endfunction
161
162 // Check if owning player has the gold to revive given hero unit.
163 // If so, then schedule revival.
164 function revive_request takes unit u returns boolean
165 local integer gold_available = 0
166 local integer gold_price = 0
167 local boolean hero_flag = false
168 local player p = null
169 local timer t = null
170
171 if null == u then
172 return false
173 endif
174
175 set hero_flag = IsHeroUnitId(GetUnitTypeId(u)) and IsUnitType(u, UNIT_TYPE_HERO)
176
177 set p = GetOwningPlayer(u)
178 set gold_available = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
179 set gold_price = revive_price_get(u)
180
181 if hero_flag and gold_available >= gold_price then
182 call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, gold_available - gold_price)
183 set t = revive_schedule(u)
184 return t != null
185 endif
186
187 return false
188 endfunction
189
190 function revive_retry_timer_callback takes nothing returns nothing
191 local timer t = GetExpiredTimer()
192 local unit u = null
193 local boolean success = false
194 local integer i = 0
195
196 if null == t then
197 return
198 endif
199
200 set i = 0
201 loop
202 exitwhen i >= JASS_MAX_ARRAY_SIZE or u != null or null == revive_timer_list[i]
203 if t == revive_timer_list[i] then
204 set u = revive_unit_list[i]
205 endif
206 set i = i + 1
207 endloop
208
209 set success = revive_request(u)
210 if success then
211 call DestroyTimer(t)
212 endif
213 endfunction
214
215 // Repeatedly request revival until the owning player has enough gold to schedule revival.
216 // There are more clever and optimized ways of doing this, but this way is also robust and obvious.
217 function revive_retry takes unit u returns nothing
218 local timer t = null
219 local integer i = 0
220 local integer j = JASS_MAX_ARRAY_SIZE
221
222 if u != null and IsHeroUnitId(GetUnitTypeId(u)) and IsUnitType(u, UNIT_TYPE_HERO) then
223 set t = CreateTimer()
224
225 set i = 0
226 loop
227 exitwhen i >= JASS_MAX_ARRAY_SIZE or (j < JASS_MAX_ARRAY_SIZE and j >= 0)
228 if null == revive_timer_list[i] then
229 set j = i
230 set revive_timer_list[j] = t
231 set revive_unit_list[j] = u
232 endif
233 set i = i + 1
234 endloop
235
236 call TimerStart(t, 2.0, true, function revive_retry_timer_callback)
237 endif
238 endfunction
239
240 function revive_trig_action takes nothing returns nothing
241 local unit u = null
242 local boolean success = false
243
244 set u = GetRevivableUnit()
245
246 if null == u then
247 return
248 endif
249
250 set success = revive_request(u)
251
252 if not success then
253 call revive_retry(u)
254 endif
255 endfunction
256
257 // Set up mechanism to conditionally revive player heroes.
258 function revive_init takes nothing returns trigger
259 local integer i = 0
260 local trigger t = null
261 local player p = null
262
263 set t = CreateTrigger()
264
265 set i = 0
266 loop
267 exitwhen i >= bj_MAX_PLAYERS
268 set p = Player(i)
269 set revive_player_count[i] = 0
270 if user_player_is_user_check(p) then
271 call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_HERO_REVIVABLE, null)
272 endif
273 set i = i + 1
274 endloop
275 call TriggerAddAction(t, function revive_trig_action)
276
277 return t
278 endfunction
File src/user.j added (mode: 100644) (index 0000000..4720733)
1 // src/user.j
2 globals
3 // src/user.j fields
4 // Unit groups or player forces are mutable collections.
5 // They may never be effectively constant.
6 endglobals
7
8 // src/user.j functions
9
10 function user_player_is_user_check takes player p returns boolean
11 local boolean flag = true
12 set flag = MAP_CONTROL_USER == GetPlayerController(p) and flag
13 set flag = PLAYER_SLOT_STATE_PLAYING == GetPlayerSlotState(p) and flag
14 set flag = not IsPlayerObserver(p) and flag
15 return flag
16 endfunction
17
18 function user_force_filter takes nothing returns boolean
19 local player p = GetFilterPlayer()
20 return user_player_is_user_check(p)
21 endfunction
22
23 function user_init_player takes player p returns nothing
24 local location loc = null
25 local integer start = 0
26
27 call SetPlayerTechMaxAllowed(p, 'HERO', 0)
28 call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP, 40)
29 call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, 0)
30 call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, 0)
31
32 set start = GetPlayerStartLocation(p)
33 set loc = GetStartLocationLoc(start)
34
35 call RemoveLocation(loc)
36 endfunction
37
38 function user_init_player_callback takes nothing returns nothing
39 local player p = GetEnumPlayer()
40 call user_init_player(p)
41 endfunction
42
43 function user_force_enum takes force user_force returns force
44 local filterfunc filter = null
45 if null == user_force then
46 return null
47 endif
48 set filter = Filter(function user_force_filter)
49 call ForceEnumPlayers(user_force, filter)
50 call DestroyFilter(filter)
51 return user_force
52 endfunction
53
54 // Blizzard.j:function CountPlayersInForceBJ serves the same purpose.
55 // The benefit of this alternative implementation is that is avoids mutable global variables.
56 function user_force_count takes force anyforce returns integer
57 local integer i = 0
58 local player p = null
59 local integer q = 0
60
61 if null == anyforce then
62 return -1
63 endif
64
65 set i = 0
66 loop
67 exitwhen i >= bj_MAX_PLAYER_SLOTS
68 set p = Player(i)
69 if IsPlayerInForce(p, anyforce) then
70 set q = q + 1
71 endif
72 set i = i + 1
73 endloop
74
75 return q
76 endfunction
77
78 function user_quantity_get takes nothing returns integer
79 local force user_force = null
80 local integer q = 0
81
82 set user_force = CreateForce()
83 call user_force_enum(user_force)
84 set q = user_force_count(user_force)
85
86 call DestroyForce(user_force)
87 return q
88 endfunction
89
90 function user_init takes nothing returns nothing
91 local force user_force = CreateForce()
92 call user_force_enum(user_force)
93
94 call ForForce(user_force, function user_init_player_callback)
95 call DestroyForce(user_force)
96 endfunction
File src/war3map.m4 changed (mode: 100644) (index 0118ad7..7aedbe9)
... ... define(endglobals, `divert(0)divert(2)')dnl
3 3 divert(2)dnl divert(2)dnl
4 4 dnl # WARNING: included files must never contain the tilde glyph, dnl # WARNING: included files must never contain the tilde glyph,
5 5 dnl # otherwise this m4 script produces malformed *.j snippet dnl # otherwise this m4 script produces malformed *.j snippet
6 include(`src/user.j')dnl
7 include(`src/herotoken.j')dnl
8 include(`src/altar.j')dnl
9 include(`src/revive.j')dnl
10 include(`src/defeat.j')dnl
6 11 include(`src/main.j')dnl include(`src/main.j')dnl
7 12 include(`src/config.j')dnl include(`src/config.j')dnl
8 13 divert(0)dnl divert(0)dnl
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/vrtc/honeydew

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/vrtc/honeydew

Clone this repository using git:
git clone git://git.rocketgit.com/user/vrtc/honeydew

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main