HedgewarsScriptLoad("/Scripts/Locale.lua")
HedgewarsScriptLoad("/Scripts/Utils.lua")
HedgewarsScriptLoad("/Scripts/Params.lua")
local hTag = nil
local hTagHeight = 33000
local tTag = nil
local rTag = nil
local startTime = 0
local MaxHeight = 32640
local RecordHeight = 33000
local RecordHeightHogName = nil
local Fire = {}
--local BoomFire = nil
local HH = {}
local totalHedgehogs = 0
local deadHedgehogs = 0
local currTeam = ''
local teams = {}
local teamScoreStats = {}
local teamBests = {}
local teamTimes = {}
local MrMine -- in honour of sparkle's first arrival in the cabin
local YouWon = false
local YouLost = false
local HogsAreInvulnerable = false
local WaterRise = nil
local Cake = nil
local CakeTries = 0
local addCake = true
local Stars = {}
local tauntNoo = false
local jokeAwardNavy = nil
local jokeAwardSpeed = nil
local jokeAwardDamage = nil
local recordBroken = false
local ready = false
local showWaterStats = false -- uses the AI team to draw water height.
local scaleGraph = false
local dummyHog = nil
local dummySkip = 0
local baseWaterSpeed = 2
local waterSpeed = 0
local waterAccel = 0
local delayHeight = 32000
local delayTime = 0
local airMineX = {}
local airMineY = {}
local airMine = {}
local init = true
function onParameters()
parseParams()
if params["speed"] ~= nil then
baseWaterSpeed = params["speed"]
end
if params["accel"] ~= nil then
waterAccel = params["accel"]
if waterAccel ~= 0 then waterAccel = div(32640000,waterAccel) end
end
if params["delaytime"] ~= nil then
delayTime = params["delaytime"]
end
if params["delaytime"] ~= nil then
delayHeight = 32768-params["delayheight"]
end
if params["nocake"] ~= nil then addCake = false end
end
function onGameInit()
-- Ensure people get same map for same theme
TurnTime = 999999999
CaseFreq = 0
Explosives = 0
MineDudPercent = 0
EnableGameFlags(gfOneClanMode)
DisableGameFlags(gfBottomBorder+gfBorder)
--This reduced startup time by only about 15% and looked ugly
--EnableGameFlags(gfDisableLandObjects)
-- force seed instead. Some themes will still be easier, but at least you won't luck out on the same theme
Seed = ClimbHome
if showWaterStats then
AddTeam(" ", 0x545C9D, "Simple", "Island", "Default")
elseif scaleGraph then
AddTeam(" ", 0x050505, "Simple", "Island", "Default")
end
if showWaterStats or scaleGraph then
dummyHog = AddHog(" ", 0, 1, "NoHat")
HH[dummyHog] = nil
totalHedgehogs = totalHedgehogs - 1
teams[GetHogTeamName(dummyHog)] = nil
SendStat(siClanHealth, tostring(32640), " ")
end
end
function onGearAdd(gear)
if GetGearType(gear) == gtHedgehog then
HH[gear] = 1
totalHedgehogs = totalHedgehogs + 1
teams[GetHogTeamName(gear)] = 1
elseif init and GetGearType(gear) == gtAirMine then
airMine[gear] = 1
end
end
function onGearDelete(gear)
if gear == MrMine then
AddCaption("Once you set off the proximity trigger, Mr. Mine is not your friend",0xffffff,0)
MrMine = nil
elseif gear == Cake then
Cake = nil
elseif GetGearType(gear) == gtHedgehog then
onGameTick20()
onGearDamage(gear, 0)
HH[gear] = nil
end
end
function onGameStart()
if showWaterStats or scaleGraph then
DeleteGear(dummyHog)
end
--SetClanColor(ClansCount-1, 0x0000ffff) appears to be broken
SendHealthStatsOff()
ShowMission(loc("Climb Home"),
loc("Rope to safety"),
loc("You are far from home, and the water is rising, climb up as high as you can!|Your score will be based on your height."),
-amRope, 0)
local x = 1818
for h,i in pairs(HH) do
if h ~= nil then
-- SetGearPosition(h,x,32549)
SetGearPosition(h,x,108)
SetHealth(h,1)
if x < 1978 then x = x+32 else x = 1818 end
if GetEffect(h,heInvulnerable) == 0 then
SetEffect(h,heInvulnerable,1)
else
HogsAreInvulnerable = true
end
SetState(h,bor(GetState(h),gstInvisible))
end
end
-- 1925,263 - Mr. Mine position
MrMine = AddGear(1925,263,gtMine,0,0,0,0)
end
function onAmmoStoreInit()
SetAmmo(amRope, 9, 0, 0, 0)
end
function onNewTurn()
if init then
init = false
for a,i in pairs(airMine) do
x,y = GetGearPosition(a)
airMineX[a] = x
airMineY[a] = y
end
else
for a,i in pairs(airMine) do
local x,y = GetGearPosition(a)
if not x or airMineX[a] ~= x or airMineY[a] ~= y then
DeleteGear(a)
AddGear(airMineX[a],airMineY[a], gtAirMine, gsttmpFlag, 0, 0, 0)
end
end
end
ready = false
startTime = GameTime
--disable to preserve highest over multiple turns
--will need to change water check too ofc
MaxHeight = 32640
hTagHeight = 33000
SetWaterLine(32768)
YouWon = false
YouLost = false
tauntNoo = false
recordBroken = false
currTeam = GetHogTeamName(CurrentHedgehog)
if CurrentHedgehog ~= nil then
if CurrentHedgehog ~= dummyHog then
SetGearPosition(CurrentHedgehog, 1951,32640)
if not HogsAreInvulnerable then SetEffect(CurrentHedgehog,heInvulnerable,0) end
AddVisualGear(1951,32640,vgtExplosion,0,false)
SetState(CurrentHedgehog,band(GetState(CurrentHedgehog),bnot(gstInvisible)))
SetWeapon(amRope)
ready = true
else
dummySkip = GameTime+1
end
end
for f,i in pairs(Fire) do
DeleteGear(f)
end
for s,i in pairs(Stars) do
DeleteVisualGear(s)
Stars[s] = nil
end
if CurrentHedgehog ~= dummyHog then
for i = 0,12 do
flame = AddGear(2000+i*2,308, gtFlame, gsttmpFlag, 0, 0, 0)
SetTag(flame, 999999+i)
Fire[flame]=1
end
end
if Cake ~= nil then DeleteGear(Cake) end
CakeTries = 0
end
--function onGearDelete(gear)
-- if gear == WaterRise and MaxHeight > 500 and CurrentHedgehog ~= nil and band(GetState(CurrentHedgehog),gstHHDriven) ~= 0 then
-- WaterRise = AddGear(0,0,gtWaterUp, 0, 0, 0, 0)
-- end
--end
function FireBoom(x,y,d) -- going to add for rockets too
AddVisualGear(x,y,vgtExplosion,0,false)
-- should approximate circle by removing corners
--if BoomFire == nil then BoomFire = {} end
for i = 0,50 do
fx = GetRandom(d)-div(d,2)
fy = GetRandom(d)-div(d,2)
if fx<0 then
fdx = -5000-GetRandom(3000)
else
fdx = 5000+GetRandom(3000)
end
if fy<0 then
fdy = -5000-GetRandom(3000)
else
fdy = 5000+GetRandom(3000)
end
flame = AddGear(x+fx, y+fy, gtFlame, gsttmpFlag, fdx, fdy, 0)
SetTag(flame, 999999+i)
SetFlightTime(flame, 0)
Fire[flame]=1
-- BoomFire[flame]=1
end
end
function onGameTick20()
local x,y
if math.random(20) == 1 then AddVisualGear(2012,56,vgtSmoke,0,false) end
if CurrentHedgehog == dummyHog and dummySkip ~= 0 and dummySkip < GameTime then
ParseCommand("/skip")
dummySkip = 0
end
--if BoomFire ~= nil then
-- for f,i in pairs(BoomFire) do
-- if band(GetState(f),gstCollision~=0) then DeleteGear(f) end
-- end
-- BoomFire = nil
--end
for s,i in pairs(Stars) do
g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(s)
if g1 > WaterLine + 500 then
DeleteVisualGear(s)
Stars[s] = nil
end
--else wasn't really visible, pointless.
-- g5 = g5+1
-- if g5 > 360 then g5 = 0 end
-- SetVisualGearValues(s, g1, g2, g3, g4, g5, g6, g7, g8, g9, g10)
--end
end
if CurrentHedgehog ~= nil then x,y = GetGearPosition(CurrentHedgehog) end
if Cake ~= nil and CurrentHedgehog ~= nil then
local cx,cy = GetGearPosition(Cake)
if y < cy-1500 then
DeleteGear(Cake)
Cake = nil
end
if Cake ~= nil and GetHealth(Cake) < 999980 and gearIsInCircle(CurrentHedgehog,cx,cy,450) then
FireBoom(cx,cy,200) -- todo animate
DeleteGear(Cake)
Cake = nil
end
end
if CurrentHedgehog ~= nil and TurnTimeLeft > 0 and band(GetState(CurrentHedgehog),gstHHDriven) ~= 0 then
if MaxHeight < delayHeight and
TurnTimeLeft<(999999999-delayTime) and
MaxHeight > 286 and WaterLine > 286 then
if waterAccel ~= 0 then
SetWaterLine(WaterLine-(baseWaterSpeed+div(getActualHeight(MaxHeight)*100,waterAccel)))
else
SetWaterLine(WaterLine-baseWaterSpeed)
end
end
if y > 0 and y < 30000 and MaxHeight > 286 and math.random(y) < 500 then
local s = AddVisualGear(0, 0, vgtStraightShot, 0, true)
local c = div(250000,y)
if c > 255 then c = 255 end
c = c * 0x10000 + 0xFF0000FF
SetVisualGearValues(s,
math.random(2048), -5000, 0, -1-(1/y*1000),
math.random(360),
0,
999999999, -- frameticks
171, -- star
0, c)
--, 0xFFCC00FF) -- could be fun to make colour shift as you rise...
Stars[s] = 1
end
end
if CurrentHedgehog ~= nil and band(GetState(CurrentHedgehog),gstHHDriven) == 0 then
for f,i in pairs(Fire) do -- takes too long to fall otherwise
DeleteGear(f)
end
if Cake ~= nil then
DeleteGear(Cake)
Cake = nil
end
end
if CurrentHedgehog ~= nil and TurnTimeLeft > 0 then
local vx, vy = GetGearVelocity(CurrentHedgehog)
local distanceFromWater = WaterLine - y
--[[ check joke awards ]]
-- navy award: when distance from main map is over 1000
local navyDistance = 1250
if x < -navyDistance or x > LAND_WIDTH+navyDistance then
local awarded = false
local dist = 0
if jokeAwardNavy == nil then
awarded = true
else
if x < 0 then
dist = math.abs(x)
else
dist = x - LAND_WIDTH
end
if dist > jokeAwardNavy.distance then
awarded = true
end
end
if awarded == true then
jokeAwardNavy = {
hogName = GetHogName(CurrentHedgehog),
teamName = GetHogTeamName(CurrentHedgehog),
distance = dist
}
end
end
-- Speed award for largest distance from water
if distanceFromWater > 3000 and WaterLine < 32000 then
local awarded = false
if jokeAwardSpeed == nil or distanceFromWater > jokeAwardSpeed.distance then
awarded = true
end
if awarded == true then
jokeAwardSpeed = {
hogName = GetHogName(CurrentHedgehog),
teamName = GetHogTeamName(CurrentHedgehog),
distance = distanceFromWater
}
end
end
if isSinglePlayer then
if distanceFromWater < 0 and not YouLost and not YouWon then
makeSinglePlayerLoserStats()
YouLost = true
end
if not YouWon and not YouLost and gearIsInBox(CurrentHedgehog, 1920, 252, 50, 50) then
local finishTime = (GameTime-startTime)/1000
local roundedFinishTime = math.ceil(math.floor(finishTime+0.5))
AddCaption(loc("Victory!"))
ShowMission(loc("Climb Home"),
loc("Made it!"),
string.format(loc("Ahhh, home, sweet home. Made it in %d seconds."), roundedFinishTime),
-amRope, 0)
PlaySound(sndVictory,CurrentHedgehog)
SetState(CurrentHedgehog, gstWinner)
SendStat(siGameResult, loc("You have beaten the challenge!"))
SendStat(siGraphTitle, loc("Your height over time"))
SendStat(siCustomAchievement, string.format(loc("%s reached home in %.3f seconds. Congratulations!"), loc(GetHogName(CurrentHedgehog)), finishTime))
SendStat(siCustomAchievement, string.format(loc("%s bravely climbed up to a dizzy height of %d to reach home."),loc(GetHogName(CurrentHedgehog)), getActualHeight(RecordHeight)))
SendStat(siPointType, loc("seconds"))
SendStat(siPlayerKills, tostring(roundedFinishTime), loc(GetHogTeamName(CurrentHedgehog)))
EndGame()
onAchievementsDeclaration()
YouWon = true
end
elseif distanceFromWater < 0 and not YouLost then
makeMultiPlayerLoserStat(CurrentHedgehog)
deadHedgehogs = deadHedgehogs + 1
YouLost = true
if deadHedgehogs >= totalHedgehogs then
makeFinalMultiPlayerStats()
EndGame()
onAchievementsDeclaration()
end
end
if GameTime % 500 == 0 then
--if isSinglePlayer and MaxHeight < 32000 and WaterRise == nil then
-- WaterRise = AddGear(0,0,gtWaterUp, 0, 0, 0, 0)
--end
if showWaterStats == true then
SendStat(siClanHealth, tostring(getActualHeight(WaterLine)), " ")
end
for t,i in pairs(teams) do
if currTeam == t then
SendStat(siClanHealth, tostring(getActualHeight(y)), t)
else
SendStat(siClanHealth, '0', t)
end
end
-- play taunts
if not YouWon and not YouLost then
local nooDistance = 500
if ((x < -nooDistance and vx < 0) or (x > LAND_WIDTH+nooDistance and vx > 0)) then
if (tauntNoo == false and distanceFromWater > 80) then
PlaySound(sndNooo, CurrentHedgehog)
tauntNoo = true
end
end
end
if addCake and CakeTries < 10 and y < 32600 and y > 3000 and Cake == nil and band(GetState(CurrentHedgehog),gstHHDriven) ~= 0 then
-- doing this just after the start the first time to take advantage of randomness sources
-- Pick a clear y to start with
if y > 31000 then cy = 24585 elseif
y > 28000 then cy = 21500 elseif
y > 24000 then cy = 19000 elseif
y > 21500 then cy = 16000 elseif
y > 19000 then cy = 12265 elseif
y > 16000 then cy = 8800 elseif
y > 12000 then cy = 5700 else
cy = 400 end
Cake = AddGear(GetRandom(2048), cy, gtCake, 0, 0, 0, 0)
SetHealth(Cake,999999)
CakeTries = CakeTries + 1
end
if (y > 286) or (y < 286 and MaxHeight > 286) then
if MaxHeight > 286 and y <= 286 then
-- wow, reached top
local teamName = GetHogTeamName(CurrentHedgehog)
if teamTimes[teamName] == nil or teamTimes[teamName] > GameTime - startTime then
teamTimes[teamName] = GameTime - startTime
end
MaxHeight = 286
end
if y < MaxHeight and y > 286 then MaxHeight = y end
if MaxHeight < hTagHeight then
hTagHeight = MaxHeight
if hTag ~= nil then DeleteVisualGear(hTag) end
hTag = AddVisualGear(0, 0, vgtHealthTag, 0, true)
local g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(hTag)
-- snagged from space invasion
SetVisualGearValues (
hTag, --id
-(ScreenWidth/2) + 40, --xoffset
ScreenHeight - 60, --yoffset
0, --dx
0, --dy
1.1, --zoom
1, --~= 0 means align to screen
g7, --frameticks
-- 116px off bottom for lowest rock, 286 or so off top for position of chair
-- 32650 is "0"
32640-hTagHeight, --value
99999999999,--timer
GetClanColor(GetHogClan(CurrentHedgehog))
)
end
if MaxHeight < RecordHeight then
RecordHeight = MaxHeight
local oldName = RecordHeightHogName
RecordHeightHogName = GetHogName(CurrentHedgehog)
if oldName == nil then recordBroken = true end
if not isSinglePlayer and RecordHeight > 1500 and not recordBroken then
recordBroken = true
AddCaption(string.format(loc("%s has passed the best height of %s!"), RecordHeightHogName, oldName))
end
if not isSinglePlayer then
if rTag ~= nil then DeleteVisualGear(rTag) end
rTag = AddVisualGear(0, 0, vgtHealthTag, 0, true)
local g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(hTag)
-- snagged from space invasion
SetVisualGearValues (
rTag, --id
-(ScreenWidth/2) + 100, --xoffset
ScreenHeight - 60, --yoffset
0, --dx
0, --dy
1.1, --zoom
1, --~= 0 means align to screen
g7, --frameticks
-- 116px off bottom for lowest rock, 286 or so off top for position of chair
-- 32650 is "0"
getActualHeight(RecordHeight), --value
99999999999,--timer
GetClanColor(GetHogClan(CurrentHedgehog))
)
end
end
end
if MaxHeight > 286 then
if tTag ~= nil then DeleteVisualGear(tTag) end
tTag = AddVisualGear(0, 0, vgtHealthTag, 0, true)
local g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 = GetVisualGearValues(tTag)
-- snagged from space invasion
SetVisualGearValues (
tTag, --id
-(ScreenWidth/2) + 40, --xoffset
ScreenHeight - 100, --yoffset
0, --dx
0, --dy
1.1, --zoom
1, --~= 0 means align to screen
g7, --frameticks
(GameTime-startTime)/1000, --value
99999999999,--timer
0xffffffff
)
end
end
end
end
function onGearDamage(gear, damage)
if GetGearType(gear) == gtHedgehog and not YouLost and not YouWon then
-- Joke award for largest damage to hog
local qualifyDamage = 50
if (damage >= qualifyDamage) then
local awarded = false
if jokeAwardDamage == nil or damage > jokeAwardDamage.damage then
awarded = true
end
if awarded == true then
jokeAwardDamage = {
hogName = GetHogName(CurrentHedgehog),
teamName = GetHogTeamName(CurrentHedgehog),
damage = damage
}
end
end
if isSinglePlayer then
makeSinglePlayerLoserStats()
else
deadHedgehogs = deadHedgehogs + 1
if deadHedgehogs >= totalHedgehogs then
makeFinalMultiPlayerStats()
EndGame()
onAchievementsDeclaration()
end
makeMultiPlayerLoserStat(gear)
end
YouLost = true
end
end
function makeLoserComment()
local m
if isSinglePlayer then m = 10 else m = 6 end
local r = math.random(1,m)
if r == 1 then text = loc("%s never got the ninja diploma.")
elseif r == 2 then text = loc("You have to move upwards, not downwards, %s!")
elseif r == 3 then text = loc("%s never wanted to reach for the sky in the first place.")
elseif r == 4 then text = loc("%s should try the rope training mission first.")
elseif r == 5 then text = loc("%s skipped ninja classes.")
elseif r == 6 then text = loc("%s doesn’t really know how to handle a rope properly.")
elseif r == 7 then text = loc("Better luck next time!")
elseif r == 8 then text = loc("It was all just bad luck!")
elseif r == 9 then text = loc("Well, that escalated quickly!")
elseif r == 10 then text = loc("What? Is it over already?") end
return text
end
function makeSinglePlayerLoserStats()
local actualHeight = getActualHeight(RecordHeight)
SendStat(siGameResult, loc("You lose!"))
SendStat(siGraphTitle, loc("Your height over time"))
local text
if actualHeight > 30000 then text = loc("%s was damn close to home.")
elseif actualHeight > 28000 then text = loc("%s was close to home.")
elseif actualHeight > 24265 then text = loc("%s was good, but not good enough.")
elseif actualHeight > 16177 then text = loc("%s managed to pass half of the distance towards home.")
elseif actualHeight > 8088 then text = loc("%s went over a quarter of the way towards home.")
elseif actualHeight > 5100 then text = loc("%s still had a long way to go.")
elseif actualHeight > 2000 then text = loc("%s made it past the hogosphere.")
elseif actualHeight > 1500 then text = loc("%s barely made it past the hogosphere.")
else
text = makeLoserComment()
end
if actualHeight > 1500 then
SendStat(siCustomAchievement, string.format(text, RecordHeightHogName, actualHeight))
else
SendStat(siCustomAchievement, string.format(text, RecordHeightHogName))
end
SendStat(siPointType, loc("points"))
SendStat(siPlayerKills, actualHeight, loc(GetHogTeamName(CurrentHedgehog)))
EndGame()
onAchievementsDeclaration()
end
function makeMultiPlayerLoserStat(gear)
local teamName = GetHogTeamName(gear)
local actualHeight = getActualHeight(MaxHeight)
if teamBests[teamName] == nil then teamBests[teamName] = actualHeight end
if teamBests[teamName] < actualHeight then teamBests[teamName] = actualHeight end
if teamScoreStats[teamName] == nil then teamScoreStats[teamName] = {} end
table.insert(teamScoreStats[teamName], actualHeight)
--SendStat(siClanHealth, tostring(teamBests[teamName]), teamName)
end
function makeFinalMultiPlayerStats()
local ranking = {}
for k,v in pairs(teamBests) do
table.insert(ranking, {name=k, score=v})
end
local comp = function(table1, table2)
if table1.score < table2.score then
return true
else
return false
end
end
table.sort(ranking, comp)
local winner = ranking[#ranking]
local loser = ranking[1]
SendStat(siGameResult, string.format(loc("%s wins!"), winner.name))
SendStat(siGraphTitle, string.format(loc("Team’s best heights per round")))
if winner.score < 1500 then
SendStat(siCustomAchievement, string.format(loc("This round’s award for ultimate disappointment goes to: Everyone!")))
else
if winner.score > 30000 then text = loc("%s (%s) reached for the sky and beyond with a height of %d!")
elseif winner.score > 24750 then text = loc("%s (%s) was certainly not afraid of heights: Peak height of %d!")
elseif winner.score > 16500 then text = loc("%s (%s) does not have to feel ashamed for their best height of %d.")
elseif winner.score > 8250 then text = loc("%s (%s) reached a decent peak height of %d.")
else text = loc("%s (%s) reached a peak height of %d.") end
SendStat(siCustomAchievement, string.format(text, RecordHeightHogName, winner.name, winner.score))
if loser.score < 1500 then
text = makeLoserComment()
SendStat(siCustomAchievement, string.format(text, loser.name))
end
end
checkAwards()
for i = #ranking, 1, -1 do
SendStat(siPointType, loc("points"))
SendStat(siPlayerKills, tostring(ranking[i].score), ranking[i].name)
end
end
function checkAwards()
if jokeAwardNavy ~= nil then
if isSinglePlayer then
SendStat(siCustomAchievement, string.format(loc("The Navy greets %s for managing to get in a distance of %d away from the mainland!"), jokeAwardNavy.hogName, jokeAwardNavy.distance))
else
SendStat(siCustomAchievement, string.format(loc("Greetings from the Navy, %s (%s), for being a distance of %d away from the mainland!"), jokeAwardNavy.hogName, jokeAwardNavy.teamName, jokeAwardNavy.distance))
end
end
if jokeAwardSpeed ~= nil then
if isSinglePlayer then
SendStat(siCustomAchievement, string.format(loc("Your hedgehog was panicly afraid of the water and decided to go in a safe distance of %d from it."), jokeAwardSpeed.distance))
else
SendStat(siCustomAchievement, string.format(loc("%s (%s) was panicly afraid of the water and decided to get in a safe distance of %d from it."), jokeAwardSpeed.hogName, jokeAwardSpeed.teamName, jokeAwardSpeed.distance))
end
end
if jokeAwardDamage ~= nil then
if isSinglePlayer then
SendStat(siCustomAchievement, string.format(loc("Ouch! That must have hurt. You mutilated your poor hedgehog hog with %d damage."), jokeAwardDamage.damage))
else
SendStat(siCustomAchievement, string.format(loc("Ouch! That must have hurt. %s (%s) hit the ground with %d damage points."), jokeAwardDamage.hogName, jokeAwardDamage.teamName, jokeAwardDamage.damage))
end
end
end
function getActualHeight(height)
return 32640-height
end
function onAchievementsDeclaration()
for teamname, score in pairs(teamBests) do
DeclareAchievement("height reached", teamname, "ClimbHome", score)
end
for teamname, score in pairs(teamTimes) do
DeclareAchievement("rope race", teamname, "ClimbHome", score)
end
end