if MODPACK_ID == nil then rawset(_G, "MODPACK_ID", "main") end freeslot("MT_TIMEATK_POST", "S_TIMEATK_POST_IDLE", "S_TIMEATK_POST_FLASH", "S_TIMEATK_POST_SPIN"); mobjinfo[MT_TIMEATK_POST] = { spawnstate = S_TIMEATK_POST_IDLE, spawnhealth = 1, seestate = S_TIMEATK_POST_FLASH, seesound = sfx_None, reactiontime = 8, attacksound = sfx_None, painstate = S_TIMEATK_POST_SPIN, painsound = sfx_strpst, painchance = 0, meleestate = S_NULL, missilestate = S_NULL, deathstate = S_NULL, xdeathstate = S_NULL, deathsound = sfx_None, speed = 8, radius = 64*FRACUNIT, height = 80*FRACUNIT, dispoffset = 0, mass = 4, damage = 0, activesound = sfx_None, flags = MF_SPECIAL, raisestate = S_NULL } states[S_TIMEATK_POST_IDLE] = { sprite = SPR_STPT, frame = A, tics = -1, action = nil, var1 = 0, var2 = 0, nextstate = S_NULL } states[S_TIMEATK_POST_FLASH] = { sprite = SPR_STPT, frame = S | FF_ANIMATE, tics = -1, action = nil, var1 = 1, var2 = 2, nextstate = S_NULL } states[S_TIMEATK_POST_SPIN] = { sprite = SPR_STPT, frame = C | FF_ANIMATE, tics = 31, action = nil, var1 = 12, var2 = 1, nextstate = S_TIMEATK_POST_FLASH } local function post_2_2_11() return VERSION >= 202 and SUBVERSION >= 12 end local var_table = { name = "singleplayer_posts", flags = CV_NOTINNET|CV_ALLOWLUA, PossibleValue = CV_YesNo, func = nil } if post_2_2_11() then var_table["defaultvalue"] = "Yes" else var_table["defaultvalue"] = "No" end local singleplayer_posts = CV_RegisterVar(var_table) local record_file; local file_content = {} local last_checkpoint = -1; local read_pos = -1; local display_diff_until = -1; local function get_order(post) if post.angle == 0 then return post.extravalue1 end local angle = post.angle if angle < 360 then if post.extravalue1 == 0 then return 16 else return -1 end end local value = post.extravalue1 + 16; while angle >= 360 do angle = $ - 360 value = $ + 16 end return value end local function get_order_from_starpost(post) if post.spawnpoint.angle == 0 then return post.spawnpoint.extrainfo end local angle = post.spawnpoint.angle if angle < 360 then if post.spawnpoint.extrainfo == 0 then return 16 else return -1 end end local value = post.spawnpoint.extrainfo + 16; while angle >= 360 do angle = $ - 360 value = $ + 16 end return value end local function build_filepath(mapnum, player) if player.mrce then MODPACK_ID = "mrce" end return "client/time_comp/"..MODPACK_ID.."/"..G_BuildMapTitle(mapnum).."-"..skins[player.skin].name end local function on_map_loaded(mapnum) if io.type(record_file) == "file" then io.close(record_file) end file_content = {} display_diff_until = -1; if mapnum == titlemap or (not modeattacking and not singleplayer_posts.value) then return end record_file = io.openlocal(build_filepath(mapnum, displayplayer)..".txt", "r") if record_file != nil then local text = record_file:read("*a") record_file:close() record_file = io.openlocal(build_filepath(mapnum, displayplayer).."-prev.txt", "w+") record_file:write(text) record_file:close() end last_checkpoint = -1; read_pos = -1; if mapnum != titlemap and modeattacking then for mapthing in mapthings.iterate if mapthing.valid and mapthing.type == mobjinfo[MT_STARPOST].doomednum then local mobj = P_SpawnMobj(mapthing.x * FRACUNIT, mapthing.y * FRACUNIT, mapthing.z * FRACUNIT, MT_TIMEATK_POST) mobj.angle = mapthing.angle mobj.extravalue1 = mapthing.extrainfo mobj.extravalue2 = get_order(mobj) end end end end addHook("MapLoad", on_map_loaded) local diff_mins, diff_secs, diff_msecs; local ahead; local function diff_drawer(v, stplyr, cam) if display_diff_until > leveltime then local time_str = diff_mins..":"..diff_secs.."."..diff_msecs if ahead then v.drawString(160, 60,"-"..time_str, V_GREENMAP | (V_10TRANS * (leveltime % 10)), "center") else v.drawString(160, 60,"+"..time_str, V_REDMAP | (V_10TRANS * (leveltime % 10)), "center") end end end hud.add(diff_drawer, "game"); local function set_diff(diff) ahead = diff < 0 if ahead then diff = $ * -1 end display_diff_until = leveltime + TICRATE * 5; diff_mins = tostring(G_TicsToMinutes(diff)) if diff_mins:len() == 1 then diff_mins = "0"..$ end diff_secs = tostring(G_TicsToSeconds(diff)) if diff_secs:len() == 1 then diff_secs = "0"..$ end diff_msecs = tostring(G_TicsToMilliseconds(diff)) if diff_msecs:len() == 1 then diff_msecs = "0"..$ end if diff_msecs:len() == 2 then diff_msecs = "0"..$ end end local function update_records(leveltime) local file_path = build_filepath(mapnum, displayplayer)..".txt" file_content = {} record_file = io.openlocal(file_path, "r") if record_file != nil then local i = 0; for line in record_file:lines() do file_content[i] = string.gsub(line, '\r*\n*', '') i = $ + 1 end record_file:close() end record_file = io.openlocal(file_path, "w+") read_pos = 0 while read_pos < last_checkpoint do if file_content[read_pos] == nil then file_content[read_pos] = "" end record_file:write(file_content[read_pos].."\n") read_pos = $ + 1 end if file_content[read_pos] == nil or file_content[read_pos] == "" then file_content[read_pos] = tostring(leveltime) else local time = tonumber(file_content[read_pos]) local diff = leveltime - time set_diff(diff) if diff < 0 then file_content[read_pos] = tostring(leveltime) end end record_file:write(file_content[read_pos].."\n") read_pos = $ + 1 while file_content[read_pos] != nil do record_file:write(file_content[read_pos].."\n") read_pos = $ + 1 end record_file:close() end local function trigger_post(post) post.state = post.info.painstate S_StartSound(post, post.info.painsound) last_checkpoint = post.extravalue2 end local function on_timeatk_post_think(mobj) if mobj.state == S_TIMEATK_POST_IDLE then if GetSecSpecial(mobj.subsector.sector.special, 4) == 4096 then if consoleplayer != nil and consoleplayer.subsector != nil then if consoleplayer.subsector.sector == mobj.subsector.sector then trigger_post(mobj) end end elseif last_checkpoint >= mobj.extravalue2 then mobj.state = S_TIMEATK_POST_FLASH end end end addHook("MobjThinker", on_timeatk_post_think, MT_TIMEATK_POST) local function on_timeatk_post_touch_special(special, toucher) if special.state == S_TIMEATK_POST_IDLE then trigger_post(special) update_records(leveltime) end return true end addHook("TouchSpecial", on_timeatk_post_touch_special, MT_TIMEATK_POST) local function on_starpost_touch_special(special, toucher) if not singleplayer_posts.value then return false end if special.state == S_STARPOST_IDLE then last_checkpoint = get_order_from_starpost(special) if gametype & (GT_COMPETITION | GT_RACE) != 0 then update_records(leveltime - 4*TICRATE) else update_records(leveltime) end end return false end addHook("TouchSpecial", on_starpost_touch_special, MT_STARPOST)