// 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  // don't save default values

// set defaults - may be overwritten by saved.cfg
looplist [
  spamSurvMsgs 0
  survDifficulty 0
  nonSurvDefaults 0
  survBotsToAdd 1
  survDamageEffects 1
  survNumBots 6
  survMaxBots 20
  timeBetChkPts 60000
  timeUntilTele 90000
] [ a v ] [ alias $a $v ]
alias survMap (rnd (listlen $__maps_all))

// reset working variables
const survResetAliases [gotCheckPoint lastCheckPoint survIsFinished startedSurvival startedOBCount startPreSurvMillis startSurvMillis survKills survTeleported survTotalDmg survOrigSets backInZone changingspeed changinggamma]
const resetSurvAliases [ looplist $survResetAliases a [ tempalias $a 0 ] ]
//checkinit afterinit resetSurvAliases
resetSurvAliases

// build menus
const survSetsList [survNumBots survBotsToAdd timeBetChkPts timeUntilTele survMaxBots]
const survDifList ["6 1 60000 90000 20" "8 2 45000 60000 30" "10 3 30000 45000 40" "12 5 10000 20000 50"]
const survSetDif [if (checkrange $arg1 0 3) [ survDifficulty = $arg1; loop sdl 5 [ (at $survSetsList $sdl) = (at (at $survDifList $survDifficulty) $sdl) ] ] [ sleep 1000 [ echo "\f3Error: \f5invalid difficulty, defaulting to" (genSurvDifStr 0) ] 1; survSetDif 0 ]; nonSurvDefaults = 0 ]

newmenu [Survival Mode]
menuitemslider "Map: " 0 -1 "$survMap" [ac_africa ac_alcove ac_aqueous ac_arabian ac_arctic ac_arctic2 ac_arid ac_avenue ac_coal ac_complex ac_depot ac_desert ac_desert2 ac_desert3 ac_douze ac_dusk ac_edifice ac_elevation ac_gothic ac_industrial ac_ingress ac_kazemat ac_keller ac_lainio ac_lotus ac_mines ac_nocturne ac_origin ac_outpost ac_power ac_rampart ac_rattrap ac_scaffold ac_shine ac_snow ac_stellar ac_sunset ac_swamp ac_terros ac_toxic ac_urban ac_venison ac_wasteland ac_werk] [ survMap = $arg1 ]
menuitemslider "Difficulty: " 0 3 "$survDifficulty" [easy intermediate hard impossible] [ survSetDif $arg1 ]
menuitem [] -1
menuitem "\t\t  Customize..." [ showmenu [Survival Mode - Customize] ]
menuitem "\t\t\f2  [ Start! ]"    [ survival (at $__maps_all $survMap) ]

newmenu [Survival Mode - Customize]
menuitemcheckbox "Enable survival damage effects (uses gamespeed/gamma)" "$survDamageEffects" [ survDamageEffects = $arg1 ]
menuitemslider "# of bots to spawn on start: " 6 20 "$survNumBots" 1          [ survNumBots = $arg1; nonSurvDefaults = 1 ]
menuitemslider "# of bots to spawn at checkpoints: " 1 5 "$survBotsToAdd" 1   [ survBotsToAdd = $arg1; nonSurvDefaults = 1 ]
menuitemslider "# of milliseconds between checkpoints: " 10000 120000 "$timeBetChkPts" 10000  [ timeBetChkPts = $arg1; nonSurvDefaults = 1 ]
menuitemslider "# of milliseconds before teleporting: " 10000 180000 "$timeUntilTele" 10000   [ timeUntilTele = $arg1; nonSurvDefaults = 1 ]
menuitemslider "Maximum # of bots in survival mode: " 10 50 "$survMaxBots" 2  [ survMaxBots = $arg1; nonSurvDefaults = 1 ]
menuitemslider "Spam survival mode messages: " 0 1 "$spamSurvMsgs"  [ no yes ] [ spamSurvMsgs = $arg1 ]
menuitem [] -1
menuitem "\t\t\t\f2       [ Back ]" [ closecurmenu ]

const genSurvDifStr [
  if $nonSurvDefaults [ result "[CUSTOM]" ] [
    tempalias 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 1                                             // Increment enemies killed counter
    if (! (listlen $survTimeBetKills)) [                       // First kill
      tempalias survTimeBetKills (- (millis) $startSurvMillis) //
    ] [ add2list survTimeBetKills (- (millis) $survLastKill) ] // Other kills
      tempalias 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 ]
  tempalias tmpSurvSets $survNumBots; tempalias curSurvDifficulty 0; tempalias survLastKill []; tempalias survTimeBetKills []; resetSurvAliases
  checkinit mapstartonce [
    tempalias startPreSurvMillis (millis); add2list tmpSurvSets (curmap); 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 [
  tempalias startedSurvival 1; tempalias 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 [ tempalias gotCheckPoint 1; increaseDifficulty ]
]

const setzone [ if (> (listlen $arg1) 3) [ tempalias 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_africa     111 164 67 164
  ac_alcove     76 132 184 220
  ac_aqueous    159 198 128 142
  ac_arabian    128 196 93 146
  ac_arctic     81 120 137 176
  ac_arctic2    33 99 153 218
  ac_arid       133 184 88 144
  ac_avenue     156 230 64 145
  ac_coal       204 253 124 180
  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_dusk       9 98 42 87
  ac_edifice    33 80 82 120
  ac_elevation  61 102 66 121
  ac_gothic     47 83 41 83
  ac_industrial 319 357 180 244
  ac_ingress    133 181 66 112
  ac_kazemat    87 194 173 211
  ac_keller     66 100 222 244
  ac_lainio     92 204 95 135
  ac_lotus      56 123 80 133
  ac_mines      95 167 136 193
  ac_nocturne   79 166 129 194
  ac_origin     145 222 139 191
  ac_outpost    84 126 74 137
  ac_power      155 216 211 244
  ac_rampart    103 140 90 158
  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 [
  tempalias survMapIndex (findlist $survOfcZones (curmap))
  if (!= $survMapIndex -1) [
    tempalias tmpSurvZone []
    loop soz 4 [ add2list tmpSurvZone (at $survOfcZones (+ $survMapIndex (+ $soz 1))) ]
    setzone $tmpSurvZone
  ] [
    survMapIndex = (findlist $survCstZones (curmap))
    if (!= $survMapIndex -1) [
      tempalias tmpSurvZone []
      loop soz 4 [ add2list tmpSurvZone (at $survCstZones (+ $survMapIndex (+ $soz 1))) ]
      setzone $tmpSurvZone
    ]
  ]
]

const findCenter [
  tempalias midZoneX (+f (divf (-f (at $tmpSurvZone 1) (at $tmpSurvZone 0)) 2) (at $tmpSurvZone 0))
  tempalias 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
    gotoposition $midZoneX $midZoneY
    sleep 25 edittoggle
  ]
]

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

tempalias changingspeed 0
tempalias changinggamma 0

const changespeed [
  if (= $connected 0) [
    if (&& (>= $arg1 10) (<= $arg1 1000)) [
      changingspeed = 1; tempalias curspeed $gamespeed; tempalias targetspeed $arg1; tempalias increments $arg2
      if (!= $curspeed $targetspeed) [
        if (> $curspeed $targetspeed) [
          tempalias loops (- $curspeed $targetspeed)
        ] [ tempalias loops (- $targetspeed $curspeed) ]
        loop CSL $loops [
          sleep (* $CSL $increments) [ gamespeed ((at [+ -] (> $curspeed $targetspeed)) $gamespeed 1) ]
        ]
        sleep (* $loops $increments) [ changingspeed = 0 ]
      ] [ changingspeed = 0 ]
    ] [
      echo "valid range for gamespeed is 10..1000"
    ]
  ] [
    echo "operation not available in multiplayer"
  ]
]

const changegamma [
  changinggamma = 1; tempalias curgamma $gamma; tempalias targetgamma $arg1; tempalias incrementss $arg2
  if (!= $curgamma $targetgamma) [
    if (> $curgamma $targetgamma) [
      tempalias loopss (- $curgamma $targetgamma)
    ] [ tempalias 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)) [ tempalias 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) [
    tempalias startedOBCount 1; tempalias startOBMillis (millis); tempalias backInZone 0; tempalias 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 [
  tempalias 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 1 ; botskillall (at [bad worse medium good best] $curSurvDifficulty) ]
  ]
]

const endSurvival [
  if (! $survIsFinished) [
    tempalias tmp_survstr $arg1; tempalias tmp_nargs $numargs; tempalias survIsFinished 1; tempalias startedSurvival 0; tempalias survNumBots (at $tmpSurvSets 0)
    checkinit mapstartonce [
      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)
  ]
]
