File: territorial_functions.lua

package info (click to toggle)
widelands 1%3A21-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 594,308 kB
  • sloc: cpp: 134,669; ansic: 18,676; python: 7,200; sh: 912; xml: 439; makefile: 175
file content (272 lines) | stat: -rw-r--r-- 10,806 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
-- RST
-- territorial_functions.lua
-- ---------------------------
--
-- This file contains common code for the "Territorial Lord" and "Territorial Time" win conditions.

set_textdomain("win_conditions")

include "scripting/richtext.lua"
include "scripting/win_conditions/win_condition_functions.lua"
include "scripting/win_conditions/win_condition_texts.lua"

local team_str = _"Team %i"
local wc_has_territory = _"%1$s has %2$3.0f%% of the land (%3$i of %4$i)."
local wc_had_territory = _"%1$s had %2$3.0f%% of the land (%3$i of %4$i)."

-- Used by calculate_territory_points keep track of when the winner changes
local winning_players = {}
local winning_teams = {}

-- RST
-- .. data:: territory_points
--
--    This table contains information about the current points and winning status for all
--    players and teams:
--
--    .. code-block:: lua
--
--       territory_points = {
--          -- The currently winning team, if any. -1 means that no team is currently winning.
--          last_winning_team = -1,
--          -- The currently winning player, if any. -1 means that no player is currently winning.
--          last_winning_player = -1,
--          -- The name of the currently winning player, if any. Empty means that no player is currently winning.
--          last_winning_player_name = "",
--          -- Remaining time in secs for victory by > 50% territory. Default value is also used to calculate whether to send a report to players.
--          remaining_time = 10,
--          -- Points by player
--          all_player_points = {},
--          -- Points by rank, used to generate messages to the players
--          points = {}
--       }
--
territory_points = {
   -- TODO(GunChleoc): We want to be able to list multiple winners in case of a draw.
   last_winning_team = -1,
   last_winning_player = -1,
   -- We record the last winning player name here to prevent crashes with retrieving
   -- the player name when the player was just defeated a few ms ago
   last_winning_player_name = "",
   remaining_time = 1201,
   all_player_points = {},
   points = {}
}

-- variables for the territorial winconditions statsistics hook
fields = 0
statistics = {
      -- TRANSLATORS: subtext of the territorial statistics hook. Keep it short and consistent with the translation of the Win condition.
      name = _"Territory percentage",
      pic = "images/wui/stats/genstats_territorial_small.png",
      calculator = function(p)
         local pts = count_owned_valuable_fields_for_all_players(wl.Game().players)
         return (pts[p.number]*100//fields)
      end,
   }


-- RST
-- .. function:: calculate_territory_points(fields, players, wc_descname, wc_version)
--
--    First checks if a player was defeated, then fills the ``territory_points`` table
--    with current data.
--
--    :arg fields: Number of all valuable fields
--    :arg players: Table of all players
--    :arg wc_descname: String with the win condition's descname
--    :arg wc_version: Number with the win condition's descname
--
function calculate_territory_points(fields, players)
   local points = {} -- tracking points of teams and players without teams
   local territory_was_kept = false

   territory_points.all_player_points = count_owned_valuable_fields_for_all_players(players)
   local ranked_players = rank_players(territory_points.all_player_points, players)

   -- Check if we have a winner. The table was sorted, so we can simply grab the first entry.
   local winning_points = -1
   -- Peaceful mode needs more land than any other can gain (lead points > remaining fields)
   local plrs = wl.Game().players
   if plrs[1]:is_attack_forbidden(plrs[2].number) then
      local remaining_points = fields
      for tidx, teaminfo in ipairs(ranked_players) do
         remaining_points = remaining_points - teaminfo.points
      end
      if (ranked_players[1].points - ranked_players[2].points) > remaining_points then
         winning_points = ranked_players[1].points
      end
   -- Without peaceful mode we need half the useful fields to win
   elseif ranked_players[1].points > ( fields / 2 ) then
      winning_points = ranked_players[1].points
   end

   -- Calculate which team or player is the current winner, and whether the winner has changed
   for tidx, teaminfo in ipairs(ranked_players) do
      local is_winner = teaminfo.points == winning_points
      if teaminfo.team ~= 0 then
         points[#points + 1] = { team_str:format(teaminfo.team), teaminfo.points }
         if is_winner then
            territory_was_kept = winning_teams[teaminfo.team] ~= nil
            winning_teams[teaminfo.team] = true
            territory_points.last_winning_team = teaminfo.team
            territory_points.last_winning_player = -1
            territory_points.last_winning_player_name = ""
         else
            winning_teams[teaminfo.team] = nil
         end
      end

      for pidx, playerinfo in ipairs(teaminfo.players) do
         if is_winner and teaminfo.team == 0 and teaminfo.points == playerinfo.points then
            territory_was_kept = winning_players[playerinfo.number] ~= nil
            winning_players[playerinfo.number] = true
            territory_points.last_winning_player = playerinfo.number
            territory_points.last_winning_player_name = playerinfo.name
            territory_points.last_winning_team = -1
         else
            winning_players[playerinfo.number] = nil
         end
         if teaminfo.team == 0 then
            points[#points + 1] = { playerinfo.name, playerinfo.points }
         end
      end
   end

   -- Set the remaining time according to whether the winner is still the same
   if territory_was_kept then
      -- Still the same winner
      territory_points.remaining_time = territory_points.remaining_time - 1
   elseif winning_points == -1 then
      -- No winner. This value is used to calculate whether to send a report to players.
      if territory_points.remaining_time == 1800 then
         territory_points.remaining_time = 1201
      elseif territory_points.remaining_time ~= 1201 then
         territory_points.remaining_time = 1800
      end

   else
      -- Winner changed
      territory_points.remaining_time = 20 * 60 -- 20 minutes
   end
   -- we are in peaceful mode and a player can't be stopped anymore
   if plrs[1]:is_attack_forbidden(plrs[2].number) and winning_points > 0 then
      territory_points.remaining_time = 0
   end

   territory_points.points = points
end

-- RST
-- .. function:: territory_status(fields, has_had)
--
--    Returns a string containing the current land percentages of players/teams
--    for messages to the players
--
--    :arg fields: Number of all valuable fields
--    :arg has_had: Use "has" for an interim message, "had" for a game over message.
--
--    :returns: a richtext-formatted string with information on current points for each player/team
--
function territory_status(fields, has_had)
   local function _percent(part, whole)
      return (part * 100) / whole
   end

   local msg = ""
   for i=1,#territory_points.points do
      if (has_had == "has") then
         msg = msg ..
            li(
               (wc_has_territory):bformat(
                  territory_points.points[i][1],
                  _percent(territory_points.points[i][2], fields),
                  territory_points.points[i][2],
                  fields))
      else
         msg = msg ..
            li(
               (wc_had_territory):bformat(
                  territory_points.points[i][1],
                  _percent(territory_points.points[i][2], fields),
                  territory_points.points[i][2],
                  fields))
      end

   end
   return p(msg)
end

-- RST
-- .. function:: winning_status_header()
--
--    Returns a string containing a status message header for a winning player
--
--    :returns: a richtext-formatted string with header information for a winning player
--
function winning_status_header()
   set_textdomain("win_conditions")
   local remaining_minutes = math.max(0, math.floor(territory_points.remaining_time / 60))

   local message = p(_"You own more than half of the map’s area.")
   message = message .. p(ngettext("Keep it for %i more minute to win the game.",
             "Keep it for %i more minutes to win the game.",
             remaining_minutes))
         :format(remaining_minutes)
   return message
end

-- RST
-- .. function:: losing_status_header(players)
--
--    Returns a string containing a status message header for a losing player
--
--    :arg players: Table of all players
--
--    :returns: a richtext-formatted string with header information for a losing player
--
function losing_status_header(players)
   set_textdomain("win_conditions")
   local winner_name = "Error"
   if territory_points.last_winning_team >= 0 then
      winner_name = team_str:format(territory_points.last_winning_team)
   elseif territory_points.last_winning_player >= 0 then
      winner_name = territory_points.last_winning_player_name
   end
   local remaining_minutes = math.max(0, math.floor(territory_points.remaining_time / 60))

   local message = p(_"%s owns more than half of the map’s area."):format(winner_name)
   message = message .. p(ngettext("You’ve still got %i minute to prevent a victory.",
             "You’ve still got %i minutes to prevent a victory.",
             remaining_minutes))
         :format(remaining_minutes)
   return message
end

-- RST
-- .. function:: territory_game_over(fields, players, wc_descname, wc_version)
--
--    Updates the territory points and sends game over reports
--
--    :arg fields: Number of all valuable fields
--    :arg players: Table of all players
--
function territory_game_over(fields, players, wc_descname, wc_version)
   calculate_territory_points(fields, players, wc_descname, wc_version)

   for idx, pl in ipairs(players) do
      local wonmsg = won_game_over.body .. game_status.body
      local lostmsg = lost_game_over.body .. game_status.body
      for i=1,#territory_points.points do
         if territory_points.points[i][1] == team_str:format(pl.team) or territory_points.points[i][1] == pl.name then
            if territory_points.points[i][2] >= territory_points.points[1][2] then
               pl:send_message(won_game_over.title, wonmsg .. territory_status(fields, "had"))
               wl.game.report_result(pl, 1, make_extra_data(pl, wc_descname, wc_version, {score=territory_points.all_player_points[pl.number]}))
            else
               pl:send_message(lost_game_over.title, lostmsg .. territory_status(fields, "had"))
               wl.game.report_result(pl, 0, make_extra_data(pl, wc_descname, wc_version, {score=territory_points.all_player_points[pl.number]}))
            end
         end
      end
   end
end