// CubeScript survival mode in a nutshell:
//
//    * All official maps support it.
//    * Each map has a unique "zone" that the player must stay in, to continue playing.
//    * Every "zone" is located somewhere other than the usual two bases.
//    * When the player first enters the "zone", the timers/counters start.
//    * If the player has not found the "zone" within the set timeframe, they are automatically teleported there.
//    * The difficulty is "increased" over time, by spawning more and increasing the skill level of bots at "checkpoints".
//    * If the player dies, or leaves the "zone" for too long of a duration, the game ends.
//    * Total seconds survived, total number of enemies killed during, average amount of time between kills, and total damage dealt while alive are reported as stats when the game ends.
persistidents 0

const survResetAliases [gotCheckPoint lastCheckPoint survIsFinished startedSurvival startedOBCount startPreSurvMillis startSurvMillis survKills survTeleported survTotalDmg survOrigSets backInZone changingspeed changinggamma]

const resetSurvAliases [
  forceinit $survResetAliases 0
  check2initlist [spamSurvMsgs survDifficulty nonSurvDefaults] 0
  check2initlist [survBotsToAdd survDamageEffects] 1
  check2init survNumBots 6; check2init survMaxBots 20; check2init timeBetChkPts 60000; check2init timeUntilTele 90000
]

checkinit afterinit resetSurvAliases

const genSurvDifStr [
  if $nonSurvDefaults [ result "[CUSTOM]" ] [
    tmpstrlist = (concat (concatword (green) "[EASY]") (concatword (yellow) "[INTERMEDIATE]") (concatword (orange) "[HARD]") (concatword (dred) "[IMPOSSIBLE]"))
    if (&& $numargs (checkrange $arg1 0 3)) [ result (at $tmpstrlist $arg1) ] [ result (at $tmpstrlist $survDifficulty) ]
  ]
]

const survOnKill [
  if (&& (= $arg1 (player1 cn)) (!= $arg1 $arg2)) [            // Player1 was actor and was not suicide
    ++ survKills                                               // Increment enemies killed counter
    if (! (listlen $survTimeBetKills)) [                       // First kill
      survTimeBetKills = (- (millis) $startSurvMillis)         //
    ] [ add2list survTimeBetKills (- (millis) $survLastKill) ] // Other kills
      survLastKill = (millis)                                  // Update last kill
  ] [ 
    if (= $arg2 (player1 cn)) [
      add2list tmpSurvSets (concat $arg1 $arg2 $arg3 $arg4 (player $arg1 name)) // Player1 was target, pass arguments/data along for use in the after game results/stats messages.
    ]
  ] 
]

const survOnHit [
  if (&& (= $arg1 (player1 cn)) (!= $arg1 $arg2)) [
    += survTotalDmg $arg3
  ]
]

checkinit onKill [if $startedSurvival [survOnKill $arg1 $arg2 $arg3 $arg4]]
checkinit onHit [if $startedSurvival [survOnHit $arg1 $arg2 $arg3 $arg4]]


const survival [
  if (> $numargs 1) [ survSetDif $arg2 ]; kickallbots; if (! (player1 team)) [ team RVSF ]
  tmpSurvSets = $survNumBots; curSurvDifficulty = 0; forceinit [survLastKill survTimeBetKills] []; resetSurvAliases
  checkinit mapstartonce [
    startPreSurvMillis = (millis); add2list tmpSurvSets (curmap 1); sleep 3 [ storesets gamma survOrigSets ]
    echo (genSurvDifStr) (yellow)Bot survival mode prepared, gather items and find the arena to start! (orange)You have (white) (div $timeUntilTele 1000) (orange)seconds to prepare!
    setOfcZones; checkzone
  ]
  btdm $arg1 $MAX_BOT_MATCH_LENGTH
]

const startSurvival [
  startedSurvival = 1; startSurvMillis = (millis); addnbot $survNumBots CLA bad
  sleep 1000 [ if $survTeleported [ echo (orange)You were teleported to the zone. ]; echo (red)Starting survival mode, good luck! ]
  sleep $timeBetChkPts [ gotCheckPoint = 1; increaseDifficulty ]
]

const setzone [ if (> (listlen $arg1) 3) [ zone = []; loop szl 4 [ add2list zone (at $arg1 $szl) ] ]; drawzone (at $arg1 0) (at $arg1 1) (at $arg1 2) (at $arg1 3) ]

// Zones consist of two points on a grid. Any area within these points are considered "inner zone".
// The order of the arguments given to setzone are important: setzone "minX maxX minY maxY"
// Hint: Use /whereami to find the two points.
const survOfcZones [
  ac_aqueous    159 198 128 142
  ac_arabian    128 196 93 146
  ac_arctic     81 120 137 176
  ac_arid       133 184 88 144
  ac_avenue     156 230 64 145
  ac_cavern     129 201 77 122
  ac_complex    105 150 129 181
  ac_depot      137 166 205 262
  ac_desert     70 133 110 178
  ac_desert2    65 98 149 190
  ac_desert3    25 62 97 134
  ac_douze      99 140 153 190
  ac_edifice    33 80 82 120
  ac_elevation  61 102 66 121
  ac_gothic     47 83 41 83
  ac_iceroad    325 374 244 274
  ac_industrial 319 357 180 244
  ac_ingress    133 181 66 112
  ac_keller     66 100 222 244
  ac_lainio     92 204 95 135
  ac_mines      95 167 136 193
  ac_outpost    84 126 74 137
  ac_power      155 216 211 244
  ac_rattrap    145 185 57 102
  ac_scaffold   34 73 38 99
  ac_shine      168 207 120 200
  ac_snow       17 54 102 127
  ac_stellar    81 144 47 160
  ac_sunset     107 152 48 183
  ac_swamp      136 180 159 188
  ac_terros     122 168 64 137
  ac_toxic      72 114 120 147
  ac_urban      135 200 45 90
  ac_venison    224 280 195 243
  ac_wasteland  92 128 100 185
  ac_werk       121 147 110 150
]

// Add zones for custom maps in the alias below...
const survCstZones [
  test 93 163 94 162
]

const setOfcZones [
  survMapIndex = (findlist $survOfcZones (curmap 1))
  if (!= $survMapIndex -1) [
    tmpSurvZone = []
    loop soz 4 [ add2list tmpSurvZone (at $survOfcZones (+ $survMapIndex (+ $soz 1))) ]
    setzone $tmpSurvZone
  ] [
    survMapIndex = (findlist $survCstZones (curmap 1))
    if (!= $survMapIndex -1) [
      tmpSurvZone = []
      loop soz 4 [ add2list tmpSurvZone (at $survCstZones (+ $survMapIndex (+ $soz 1))) ]
      setzone $tmpSurvZone
    ]
  ]
]

const findCenter [
  midZoneX = (+f (divf (-f (at $tmpSurvZone 1) (at $tmpSurvZone 0)) 2) (at $tmpSurvZone 0))
  midZoneY = (+f (divf (-f (at $tmpSurvZone 3) (at $tmpSurvZone 2)) 2) (at $tmpSurvZone 2))
]

const teleportPlayer [
  if (&& (! $survIsFinished) (&& $startPreSurvMillis (>= (- (millis) $startPreSurvMillis) 500))) [
    if (! (player1 alive)) attack
    if (!= $gamespeed 100) [ gamespeed 100 ]
    survTeleported = 1
    findCenter
    if (! $editing) edittoggle
    go_to $midZoneX $midZoneY 0
    sleep 25 edittoggle
  ]
]

const isInZone [ result (&& (checkrange (player1 x) (at $zone 0) (at $zone 1)) (checkrange (player1 y) (at $zone 2) (at $zone 3))) ]

forceinit [changingspeed changinggamma] 0

const changespeed [
  changingspeed = 1; curspeed = $gamespeed; targetspeed = $arg1; increments = $arg2
  if (!= $curspeed $targetspeed) [
    if (> $curspeed $targetspeed) [
      loops = (- $curspeed $targetspeed)
    ] [ loops = (- $targetspeed $curspeed) ]
    loop CSL $loops [
      sleep (* $CSL $increments) [ gamespeed ((at [+ -] (> $curspeed $targetspeed)) $gamespeed 1) ]
    ]
    sleep (* $loops $increments) [ changingspeed = 0 ]
  ] [ changingspeed = 0 ]
]

const changegamma [
  changinggamma = 1; curgamma = $gamma; targetgamma = $arg1; incrementss = $arg2
  if (!= $curgamma $targetgamma) [
    if (> $curgamma $targetgamma) [
      loopss = (- $curgamma $targetgamma)
    ] [ loopss = (- $targetgamma $curgamma) ]
    loop CGL $loopss [
      sleep (* $CGL $incrementss) [ gamma ((at [+ -] (> $curgamma $targetgamma)) $gamma 1) ]
    ]
    sleep (* $loopss $incrementss) [ changinggamma = 0 ]
  ] [ changinggamma = 0 ]
]

const applySurvDamageEffects [
  if (&& (!= $gamespeed (* (player1 health) 2)) (&& (! $changingspeed) (>= (player1 health) 5))) [
    changespeed (* (player1 health) 2) 10
  ]
  if (&& (checkrange (player1 health) 15 32) (&& (!= $gamma (* (player1 health) 3)) (! $changinggamma))) [
    changegamma (* (player1 health) 3) 10
  ]
]

const checkzone [
  if (! $survIsFinished) [
    if (! $startedSurvival) [
      if (! (player1 team)) [ team RVSF ]
      if (isInZone) startSurvival
      if (>= (- (millis) $startPreSurvMillis) $timeUntilTele) teleportPlayer
    ] [
      if (>= (- (millis) $startSurvMillis) 500) [
        if (|| $editing (|| (idlebots 2) (! (botsshoot 2)))) [ endSurvival (concatword (genSurvDifStr) (red) [ Error: editmode, idlebots, and botsshoot are not allowed in bot survival mode.]) 1 ]
        if (! (player1 alive)) [ finalSurvMillis = (- (millis) $startSurvMillis); endSurvival (concatword (genSurvDifStr) (red) [ You survived for ] (white) (divf $finalSurvMillis 1000) (red) [ seconds, killed ] (white) $survKills (red) [ enemies. Average: ] (white) (divf (listaverage $survTimeBetKills) 1000) (red) [ seconds between kills. Total Damage Dealt: ] (white) $survTotalDmg) ]
      ]
      if (&& (! (isInZone)) (! $startedOBCount)) startOBCount [
        if (&& (isInZone) (&& $startedOBCount (! $backInZone))) [ backInZone = 1 ]
      ]
      if (&& (>= (- (millis) $lastCheckPoint) $timeBetChkPts) $gotCheckPoint) increaseDifficulty
      if $survDamageEffects [
        if (>= (player1 health) 50) [ if (&& (!= $gamespeed 100) (! $changingspeed)) [ changespeed 100 10 ] ]
        if (> (player1 health) 32) [ if (!= $gamma (at $survOrigSets 0)) [ if (! $changinggamma) [ changegamma (at $survOrigSets 0) 10 ] ] ]
        if (< (player1 health) 50) applySurvDamageEffects
      ]
    ]
    sleep 0 checkzone
  ]
]

const startOBCount [
  if (&& (! $survIsFinished) $startedSurvival) [
    startedOBCount = 1; startOBMillis = (millis); backInZone = 0; tmpSCtr = 5
    echo (red)Get back into the arena! (white)You only have (red)5 (white)seconds to return.
    loop soc 5 [ sleep (* $soc 1000) [ if (&& (! $survIsFinished) (&& (! (isInZone)) (! $backInZone))) [ if $spamSurvMsgs [ hudecho (concatword (orange) $tmpSCtr ...) ] [ echo (concatword (orange) $tmpSCtr ...) ]; -= tmpSCtr 1 ] [ backInZone = 1 ] ] ]
    sleep 5000 [ if (&& (! (isInZone)) (&& (! $backInZone) (>= (- (millis) $startOBMillis) 5000))) [ endSurvival (concatword (genSurvDifStr) (red) [ You took too long to return to the arena!]) 1 ] [ startedOBCount = 0 ] ]
  ]
]

const increaseDifficulty [
  lastCheckPoint = (millis)
  if (curmodeattr bot) [
    if (<= (+ $survNumBots $survBotsToAdd) $survMaxBots) [ += survNumBots $survBotsToAdd; addnbot $survBotsToAdd CLA best ] [ tmpSurvInt = (- $survMaxBots $survNumBots); += survNumBots $tmpSurvInt; addnbot $tmpSurvInt CLA best ]
    if (< $curSurvDifficulty 4) [ ++ curSurvDifficulty; botskillall (at [bad worse medium good best] $curSurvDifficulty) ]
  ]
]

const endSurvival [
  if (! $survIsFinished) [
    tmp_survstr = $arg1; tmp_nargs = $numargs; survIsFinished = 1; startedSurvival = 0; survNumBots = (at $tmpSurvSets 0)
    checkinit mapstartonce [
      if (! $dyngamma) [ gamma (at $survOrigSets 0) ]; gamespeed 100
      if (< $tmp_nargs 2) [
        echo (green)Bot Survival results for map: (white) (at $tmpSurvSets 1)
        if (!= (at $tmpSurvSets 2) (at $tmpSurvSets 3)) [
          if (= (at $tmpSurvSets 5) 1) [ sleep 5 [ echo (concatword (yellow) (format [you were %1 by %2] (gibmessage (at $tmpSurvSets 4)) (at $tmpSurvSets 6))) ] ] [
            sleep 5 [ echo (concatword (yellow) (format [you were %1 by %2] (fragmessage (at $tmpSurvSets 4)) (at $tmpSurvSets 6))) ]
          ]
        ] [ sleep 5 [ echo (concatword (yellow) [you suicided!]) ] ]
      ]
      echo $tmp_survstr
      if (&& $spamSurvMsgs $survKills) [ hudecho (green)GG ]
    ]
    if (curmodeattr bot) kickallbots
    tdm (rndmap)
  ]
]

// List of aliases that need to be cleared from saved.cfg on quit:
const survAliases (concat $survResetAliases [resetSurvAliases genSurvDifStr survival tmpSurvSets curSurvDifficulty survLastKill survTimeBetKills startPreSurvMillis startSurvival startedSurvival setzone survOfcZones setOfcZones survMapIndex tmpSurvZone findCenter midZoneX midZoneY teleportPlayer survTeleported isInZone checkzone startOBCount startedOBCount startOBMillis backInZone tmpSCtr increaseDifficulty lastCheckPoint tmpSurvInt endSurvival tmp_survstr tmp_nargs survIsFinished tmpstrlist finalSurvMillis zone changegamma changespeed loops loopss increments incrementss curgamma targetgamma curspeed targetspeed applySurvDamageEffects survSetsList survDifList survSetDif survResetAliases survAliases])
addListOnQuit $survAliases

persistidents 1
