//Freeslot object declarations. The rest of the declarations are in MAINCFG.
freeslot("MT_THERMOS","MT_CELL","MT_BATTERY","MT_REBREATHER","MT_CARD_EMPTY","MT_MEDIKIT","MT_CONVERTER","MT_BULLET","MT_SLUNGBULLET","MT_FIRESEED","MT_BULLETPILE","MT_CLIP","MT_BULLETBOX","MT_SPARKLASER")
//Options
local opt_barmode = 1 //If 1 or 4, show HP/EP bars only. If 2, show HP/EP bars and numbers both. If 3, show only numbers. Note that the latter is always done while in inventory or dialogue, unless mode 4 is chosen.
//Shop Inventories
local shopInv = {{},{},{},{},{}}
local buyBack = {}
local shopName = {"Hunted Deals","Global Grocery","Glenn's Boutique","Marshall's Arms","Wyre Wares"}
local shopSeeds = {}
local shopSeedsBuyback = {}
local interactText = {} //The text to show when using examining the area. If this is specified, item 1 will have the question mark icon.
local interactDialogue = {} //The dialogue tree to use when speaking with whoever is in the area. If this is specified, item 1 will have the talk icon.
local interactFunc = {} //The function to execute when interacting with the area. If this is specified, item 1 will have the exclamation mark icon.
// 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
local price = {0, 6, 8, 10, 10, 250, 400, 410, 50, 500, 500, 550, 425, 450, 550, 425, 500, 500, 0, 1000, 1000, 6, 10, 8, 200, 50, 10, 50, 50, 50, 1000}
local itemName = {"Misc. Action", "Thermos", "Water", "Warm Drink", "Cold Drink", "Egg Rebreather", "Egg Card", "Super Ring Egg Card", "Medikit", "Armageddon Shield Card", "Force Shield Card", "Robotnik Card", "Elemental Shield Card", "Shield Card", "Invincibility Card", "Speed Card", "Whirlwind Shield Card", "Attraction Shield Card", "Sling", "Inferno Shield Card", "Liquid Shield Card", "Fire Seeds", "Instant Recharge", "Sling Bullets", "Flametongue", "Duster", "Bullet Clip", "Bullet Box", "Wyre Miner", "Wyre Zapper", "Ambrosial Apple"} //The names of each item, for the description bar. Unlike the display names at the top of the HUD, the item names shown in the description bar never change.
local itemDesc = {"Examine and interact with the world in various ways", "Fill with water to drink later.", "Drink to cool off.", "Drink to warm up.","Drink to cool off.", "A prototype mini-rebreather device, used to replenish air.", "A card that can retrieve and store the contents of a monitor.", "A card storing 10 rings.", "Treats bleeding and burns.", "Use to receive an Armageddon Shield.", "Use to receive a Force Shield.", "Use to be healed by three bars of health.","Use to receive an elemental shield.", "Use to receive a shield.", "Use to gain brief invulnerability.", "Use to gain increased speed for a while.", "Use to receive a Whirlwind Shield.", "Use to receive an Attraction Shield.", "Hurls sling bullets at foes.", "Use to receive an Inferno Shield.","Use to receive a Liquid Shield.", "Eat to spew flames at your foes.", "Refills one bar of energy instantly upon purchase.", "30 sling bullets.","Spews fire at foes.", "A standard, reliable pistol.", "A clip of six bullets.", "A box of 30 bullets.", "Intended for easy mining.", "Intended for self-defense.", "Fully heals and permanently adds one bar to maximum health."} //The first line of the description.
local itemDesc2 = {"", "", "", "", "", "Revolutionary, but highly energy-inefficient.", "Energy cost varies by monitor type. Must be right in front of the monitor.", "Use to receive the rings.","", "Use Misc. Action in midair to manually detonate.", "", "", "", "", "", "", "Jump in midair for an extra boost.", "", "Somewhat slow, not good against heavily armored foes.","", "", "Damages the body, however.", "", "", "Uses fire seeds instead of bullets.", "", "", "", "Extremely low range, but pierces armor.", "Weak, but pierces armor.", "Maximum health cannot exceed 12 bars."} //The second line of the description.
local itemShortDesc = {"Examine and interact with the world", "Fill with water to drink later", "Drink to cool off", "Drink to warm up", "Drink to cool off", "Replenishes air at high energy cost", "Extract contents of adjacent monitor.", "Use to receive 10 rings.", "Treats bleeding and burns.", "Use to receive an Armageddon Shield.", "Use to receive a Force Shield.", "Use to be healed by three bars of health.","Use to receive an elemental shield.", "Use to receive a shield.", "Use to gain brief invulnerability.", "Use to gain increased speed for a while.", "Use to receive a Whirlwind Shield", "Use to receive an Attraction Shield", "Hurls sling bullets that only hurt weaker foes", "Use to receive an Inferno Shield", "Use to receive a Liquid Shield", "Eat and spew fire at the cost of health.", "Instantly refills one bar of energy", "30 sling bullets", "Uses fire seeds to spew fire", "A standard pistol", "A clip of six bullets.", "A box of 30 bullets", "Strong laser with extremely short range", "Weak electrical projectile", "Adds 1 bar to max health up to 12 and fully heals"} //The description to show at low resolutions.
local emptyItem = {0, 0, 6, 6, 6, 0, 0, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} //What the item becomes when emptied. For most, this is 0, which will forbid emptying the item.
local delayType = {0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0}
local gameFrozen = false //Is the game pseudo-paused?
local cachedIcons = {} //Loading these on the fly causes the game to slow to a crawl. A cache is necessary. To prevent the game from slowing down too much over time, this cache is cleared every time the inventory is opened or closed.
local cacheUsed = {} //Stores when the patch was last used. Any patch not used during that frame is removed from the cache.
local nearestMonitor = nil //Closest monitor within egg card range.
/*local function v.cachePatch(patchName)
if cachedIcons[patchName] == nil then
cachedIcons[patchName] = v.cachePatch(patchName)
print("Added "..patchName.." to cache")
end
cacheUsed[patchName] = leveltime
return cachedIcons[patchName]
end*/
local function freezeAll(frozen)
gameFrozen = frozen
if frozen then
for obj in thinkers.iterate("mobj")
if obj.flags & (MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_ENEMY) or obj.type == MT_PLAYER then
A_SetObjectFlags(obj,MF_NOTHINK,2)
end
end
else
for obj in thinkers.iterate("mobj")
if obj.flags & (MF_PUSHABLE|MF_BOSS|MF_MISSILE|MF_ENEMY) or obj.type == MT_PLAYER then
A_SetObjectFlags(obj,MF_NOTHINK,1)
end
end
end
end
local function STagChange(oldT,newT)
for sec in sectors.iterate
if sec.tag==oldT then sec.tag=newT end
end
end
//Look for the tag in the sector the player is in or any of its FOFs.
local function checkSectors(p, st)
local s = p.mo.subsector.sector
if s.tag == st then return true end
for fof in s.ffloors()
if fof.sector.tag == st then return true end
end
return false
end
local function isPress(p,bf) //This detects whether the given key has JUST been pressed down.
if p.cmd.buttons & bf and not (p.oldButtons & bf) then return true end
return false
end
//Thanks, StackOverflow! http://stackoverflow.com/questions/5249629/modifying-a-character-in-a-string-in-lua
local function replace_char(pos, str, r)
return str:sub(1, pos-1) .. r .. str:sub(pos+1)
end
//A text wrapping function. This returns a string with newlines in proper locations to wrap the string s to the w constraint. This function assumes there are no double-spaces. This also does not properly wrap single words that exceed the width.
local function wrap(v,s,w,widthType)
s = string.gsub(s,"-","- ") //This (and the reverse done later) is done to properly handle wrapping around dashes.
//Relevent character positions.
local lastNewline = 0
local noth
local lastSpace = string.find(s," ")
if lastSpace == nil then return s end //If there are no spaces in the string, return the string.
local currentSpace = string.find(s," ",lastSpace+1)
local reachedEnd = false //If this is set to true, return the string at the end of the iteration.
if widthType == nil then widthType = "normal" end //If widthType is undefined, presume normal.
while true do //Not actually an infinite loop, since it contains a return statement.
if currentSpace == nil then //If no new space was found, set the effective new space to just past the string length and set the function to return at the end of this iteration.
currentSpace = string.len(s)+1
reachedEnd = true
end
//This makes sure that manually-added newline characters do not confuse the algorithm.
while string.find(string.sub(s,lastNewline+1,currentSpace),"\n") != nil do
if v.stringWidth(string.gsub(string.sub(s,lastNewline+1,lastNewline+string.find(string.sub(s,lastNewline+1,currentSpace),"\n")),"- ","-"),0,widthType) > w then //The gsub prevents dashes from skewing the results. If the text from the last newline character exceeds the width allowed...
s = replace_char(lastSpace, s, "\n")
end
lastNewline = lastNewline + string.find(string.sub(s,lastNewline+1,currentSpace),"\n")
end
if v.stringWidth(string.gsub(string.sub(s,lastNewline+1,currentSpace-1),"- ","-"),0,widthType) > w then //The gsub prevents dashes from skewing the results. If the text from the last newline character exceeds the width allowed...
s = replace_char(lastSpace, s, "\n")
lastNewline = lastSpace
end
//Get a new currentSpace and set lastSpace to old currentSpace value.
lastSpace = currentSpace
currentSpace = string.find(s," ",lastSpace+1)
if reachedEnd == true then
s = string.gsub(s,"- ","-")
return s
end
end
end
//Initiates the dialogue. d, in this case, is the dialogue tree, while p is the player who is having the dialogue.
local function converse(p,d)
p.oldPowers = {}
for i=0,22
p.oldPowers[i] = p.powers[i]
end
freezeAll(true)
p.mo.momx = 0
p.mo.momy = 0
p.conversation = d //The current dialogue.
p.cursor = 1 //The selected option.
p.dialogueSector = p.mo.subsector.sector //The sector the conversation started in. Some conversations may modify the tag to make a different conversation happen later.
//Set how many options there are. This will influence the movement wrapping of the cursor.
if d[5] != nil then p.optionCount = 4
elseif d[4] != nil then p.optionCount = 3
elseif d[3] != nil then p.optionCount = 2
else p.optionCount = 1 end
end
//Main settings menu dialogue.
local settingsMain = {"Select an option.\n\n\130Stat Mode:\128 Whether to show bars, numbers, or both for health and energy.",
{"Stat Mode",
{"\130Stat Mode\128\nWhether to show bars, numbers, or both for health and energy.\n\nWhile in the inventory, the display will default to numbers only, unless \"Always Bars\" is chosen.",
{"Bars only (default)", function(p)
opt_barmode = 1
converse(p,settingsMain)
end},
{"Bars and numbers", function(p)
opt_barmode = 2
converse(p,settingsMain)
end},
{"Numbers only", function(p)
opt_barmode = 3
converse(p,settingsMain)
end},
{"Always bars", function(p)
opt_barmode = 4
converse(p,settingsMain)
end}
},
},
{"Exit", function(p)
p.menuOpen = true
end}
}
//Draws the dialogue boxes and text.
local function drawDialogue(v,p)
if p.conversation == nil then return end //Draw nothing if not in a conversation.
v.draw(7,10,v.cachePatch("DIALOGUE")) //Draw main box.
v.draw(7,142,v.cachePatch("CHOICES")) //Draw choice box.
v.drawString(12,15,wrap(v,p.conversation[1],296)) //Draw text for the text box.
//For each option, draw it in the options box, in yellow if selected.
for i=1,p.optionCount do
if i==p.cursor then
v.drawString(12,137+(i*10),string.char(0x82)..p.conversation[i+1][1]..string.char(0x80)) //For whatever reason, using V_YELLOWMAP forced the string into all caps.
else v.drawString(12,137+(i*10),p.conversation[i+1][1]) end
end
end
//Responds to input while dialogue is open.
local function dialogueHandler(p)
//Move the cursor if up or down is held and play a beep.
if p.cmd.forwardmove > 25 and p.oldForwardMove <= 25 and p.optionCount > 1 then
p.cursor = (p.cursor - 1) % p.optionCount
S_StartSound(nil,142,p)
end
if p.cmd.forwardmove < -25 and p.oldForwardMove >= -25 and p.optionCount > 1 then
p.cursor = (p.cursor + 1) % p.optionCount
S_StartSound(nil,142,p)
end
if p.cursor < 1 then p.cursor = p.cursor + p.optionCount end //If the cursor position is 0 or negative, correct that.
//If the user presses jump or spin, activate the option selected and play a beep.
if isPress(p,BT_USE) or isPress(p,BT_CUSTOM1) or isPress(p,BT_CUSTOM2) then
S_StartSound(nil,142,p)
//If it's a table, set the conversation to it.
if type(p.conversation[p.cursor+1][2]) == "table" then
converse(p,p.conversation[p.cursor+1][2])
//If it's a function, close the conversation and activate it. Note that the function in question can open a new conversation.
elseif type(p.conversation[p.cursor+1][2]) == "function" then
local func = p.conversation[p.cursor+1][2]
p.conversation = nil
func(p)
if p.menuOpen == false then freezeAll(false) end
else
p.conversation = nil //Close the conversation.
freezeAll(false)
end
end
end
//Return the string representing the icon for the item in question. This could be handled by an array IN THEORY, but some items change icon depending on circumstances.
local function getIcon(p,inventory,num)
local item = inventory[num]
if item == 1 then
if P_IsObjectOnGround(p.mo) == false and p.powers[pw_shield] == 4 then
return "ICONARMA"
elseif interactText[p.mo.subsector.sector.tag] != nil then
return "ICONINTE"
elseif interactDialogue[p.mo.subsector.sector.tag] != nil then
return "ICONINTT"
else
return "ICONINTN"
end
elseif item == 2 then
if checkSectors(p,701) or checkSectors(p,702) or checkSectors(p,703) then return "ICONTHOP"
else return "ICONTHER" end
elseif item == 3 then return "ICONTHWA"
elseif item == 4 then return "ICONTHHT"
elseif item == 5 then return "ICONTHCD"
elseif item == 6 then return "ICONREBR"
//Empty Card
elseif item == 7 then
if nearestMonitor == nil then return "ICONCDEM" end
if nearestMonitor.type == MT_SUPERRINGBOX or nearestMonitor.type == MT_SNEAKERTV then
if p.energy >= 500 then return "ICONCDYS" else return "ICONCDNO" end
elseif nearestMonitor.type == MT_PITYTV then
if p.energy >= 1000 then return "ICONCDYS" else return "ICONCDNO" end
elseif nearestMonitor.type == MT_FIRETV or nearestMonitor.type == MT_WATERTV then
if p.energy >= 1500 then return "ICONCDYS" else return "ICONCDNO" end
elseif nearestMonitor.type == MT_BLUETV or nearestMonitor.type == MT_BLACKTV or nearestMonitor.type == MT_GREENTV or nearestMonitor.type == MT_WHITETV or nearestMonitor.type == MT_YELLOWTV then
if p.energy >= 2000 then return "ICONCDYS" else return "ICONCDNO" end
elseif nearestMonitor.type == MT_EGGMANBOX or nearestMonitor.type == MT_INV then
if p.energy >= 3000 then return "ICONCDYS" else return "ICONCDNO" end
end
return "ICONCDEM"
elseif item == 8 then return "ICONCDRG"
elseif item == 9 then return "ICONMEDI"
elseif item == 10 then return "ICONCDAR"
elseif item == 11 then return "ICONCDFO"
elseif item == 12 then return "ICONCDEG"
elseif item == 13 then return "ICONCDEL"
elseif item == 14 then return "ICONCDPI"
elseif item == 15 then return "ICONCDIN"
elseif item == 16 then return "ICONCDSP"
elseif item == 17 then return "ICONCDWH"
elseif item == 18 then return "ICONCDMA"
elseif item == 19 then return "ICONSLNG"
elseif item == 20 then return "ICONCDFI"
elseif item == 21 then return "ICONCDWA"
elseif item == 22 then return "ICONFISD"
elseif item == 23 then return "ICONCEL"..(leveltime%6+1)
elseif item == 24 then return "ICONBULS"
elseif item == 25 then return "ICONFLTN"
elseif item == 26 then return "ICONDUST"
elseif item == 27 then return "ICONCLIP"
elseif item == 28 then return "ICONBBOX"
elseif item == 29 then return "ICONMINR"
elseif item == 30 then return "ICONZAPR"
elseif item == 31 then return "ICONAMAP"
else return "" end
end
//Return the number to show under the item's icon.
local function getItemNum(p,num,overrideShopMode)
if p.shopMode == 1 and num > 0 then
local item = shopInv[p.currentShop][num]
if item == 19 then return shopSlingBullets
elseif item == 22 then return shopSeeds[p.currentShop]
else return "" end
elseif p.shopMode == 3 and num > 0 then
local item = buyBack[num]
if item == 19 then return shopSlingBulletsBuyback
elseif item == 22 then return shopSeedsBuyback[p.currentShop]
else return "" end
else
local item = p.slots[num]
if item == 19 then return p.bullets
elseif item == 26 then return p.ammo
elseif item == 22 or item == 25 then return p.seeds
else return "" end
end
end
//Return the string representing the name for the item in question. This could be handled by an array IN THEORY, but some items change name depending on circumstances.
local function getName(p,num)
local item = p.slots[num]
if item == 1 then
if P_IsObjectOnGround(p.mo) == false and p.powers[pw_shield] == 4 then
return "Detonate"
elseif interactText[p.mo.subsector.sector.tag] != nil then
return "Examine"
elseif interactDialogue[p.mo.subsector.sector.tag] != nil then
return "Talk"
else
return ""
end
elseif item == 2 then return "Thermos"
elseif item == 3 then return "Water"
elseif item == 4 then return "Warm Drink"
elseif item == 5 then return "Cold Drink"
elseif item == 6 then return "Rebreather"
elseif item == 7 then return "Egg Card"
elseif item == 8 then return "10 Rings"
elseif item == 9 then return "Medikit"
elseif item == 10 then return "Armageddon"
elseif item == 11 then return "Force"
elseif item == 12 then return "Robotnik"
elseif item == 13 then return "Elemental"
elseif item == 14 then return "Shield"
elseif item == 15 then return "Invincibility"
elseif item == 16 then return "Speed"
elseif item == 17 then return "Whirlwind"
elseif item == 18 then return "Attraction"
elseif item == 19 then return "Sling"
elseif item == 20 then return "Inferno"
elseif item == 21 then return "Liquid"
elseif item == 22 then return "Fire Seeds"
elseif item == 25 then return "Flametongue"
elseif item == 26 then return "Duster"
elseif item == 29 then return "Wyre Miner"
elseif item == 30 then return "Wyre Zapper"
else return "" end
end
//Use the specified item.
local function useItem(p,num)
local item = p.slots[num]
//Misc. Action
if item == 1 then
//Examine
if P_IsObjectOnGround(p.mo) == false and p.powers[pw_shield] == 4 then
P_BlackOw(p)
elseif interactText[p.mo.subsector.sector.tag] != nil then
print(interactText[p.mo.subsector.sector.tag])
elseif interactDialogue[p.mo.subsector.sector.tag] != nil then
converse(p,interactDialogue[p.mo.subsector.sector.tag])
end
//Thermos
elseif item == 2 then
if checkSectors(p,701) then
p.slots[num] = 3
S_StartSound(nil,53,p)
elseif checkSectors(p,703) then
p.slots[num] = 4
S_StartSound(nil,53,p)
elseif checkSectors(p,702) then
p.slots[num] = 5
S_StartSound(nil,53,p)
end
//Water
elseif item == 3 then
if p.temp >= 1000 then p.temp = p.temp - 1000
elseif p.temp < 1000 and p.temp > 0 then p.temp = 0 end
S_StartSound(nil,55,p)
p.slots[num] = 2
//Warm Drink
elseif item == 4 then
if p.temp <= -3000 then p.temp = p.temp + 3000
elseif p.temp > -3000 and p.temp < 0 then p.temp = 0 end
S_StartSound(nil,55,p)
p.slots[num] = 2
//Cold Drink
elseif item == 5 then
if p.temp >= 3000 then p.temp = p.temp - 3000
elseif p.temp < 3000 and p.temp > 0 then p.temp = 0 end
S_StartSound(nil,55,p)
p.slots[num] = 2
//Rebreather
elseif item == 6 and p.mo.eflags & MFE_UNDERWATER and p.energy >= 1000 then
p.powers[pw_underwater] = 1050
P_RestoreMusic(p)
p.energy = p.energy - 1000
S_StartSound(nil,36,p)
//Empty Card
elseif item == 7 then
if nearestMonitor == nil then return end
if nearestMonitor.type == MT_SUPERRINGBOX then
if p.energy >= 500 then
p.slots[num] = 8
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 500
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_BLACKTV then
if p.energy >= 2000 then
p.slots[num] = 10
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 2000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_BLUETV then
if p.energy >= 2000 then
p.slots[num] = 11
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 2000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_EGGMANBOX then
if p.energy >= 3000 then
p.slots[num] = 12
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 3000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_GREENTV then
if p.energy >= 2000 then
p.slots[num] = 13
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 2000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_PITYTV then
if p.energy >= 1000 then
p.slots[num] = 14
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 1000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_INV then
if p.energy >= 3000 then
p.slots[num] = 15
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 3000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_SNEAKERTV then
if p.energy >= 500 then
p.slots[num] = 16
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 500
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_WHITETV then
if p.energy >= 2000 then
p.slots[num] = 17
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 2000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_YELLOWTV then
if p.energy >= 2000 then
p.slots[num] = 18
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 2000
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_WATERTV then
if p.energy >= 1500 then
p.slots[num] = 21
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 1500
else
S_StartSound(nil,24,p)
end
elseif nearestMonitor.type == MT_FIRETV then
if p.energy >= 1500 then
p.slots[num] = 20
S_StartSound(nil,298,p)
nearestMonitor.type = MT_TV_EMPTY
P_SetMobjStateNF(nearestMonitor, S_TV_EMPTY)
p.energy = p.energy - 1500
else
S_StartSound(nil,24,p)
end
end
//Super Ring Card
elseif item == 8 then
p.mo.health = p.mo.health + 10
S_StartSound(nil,108,p)
p.slots[num] = 7
//Medikit
elseif item == 9 and p.hp < p.temphp then
p.hp = min(p.temphp,p.hp+30)
S_StartSound(nil,163,p)
p.slots[num] = 0
//Armageddon Shield Card
elseif item == 10 and p.powers[pw_shield] != 4 and p.powers[pw_shield] != 260 then
p.powers[pw_shield] = SH_BOMB
P_SpawnShieldOrb(p)
S_StartSound(nil,120,p)
p.slots[num] = 7
//Force Shield Card
elseif item == 11 and p.powers[pw_shield] < 512 then
p.powers[pw_shield] = SH_FORCE|1
P_SpawnShieldOrb(p)
S_StartSound(nil,120,p)
p.slots[num] = 7
//Robotnik Card
elseif item == 12 and p.hp < p.maxhp then
p.hp = p.hp + 30
p.temphp = p.temphp + 30
S_StartSound(nil,163,p)
p.slots[num] = 7
//Elemental Shield Card
elseif item == 13 and p.powers[pw_shield] != 3 and p.powers[pw_shield] != 259 then
p.powers[pw_shield] = SH_ELEMENTAL
P_SpawnShieldOrb(p)
S_StartSound(nil,120,p)
p.slots[num] = 7
//Shield Card
elseif item == 14 and p.powers[pw_shield] == 0 or p.powers[pw_shield] == 256 then
p.powers[pw_shield] = SH_PITY
P_SpawnShieldOrb(p)
S_StartSound(nil,120,p)
p.slots[num] = 7
//Invincibility Card
elseif item == 15 and p.powers[pw_invulnerability] == 0 then
p.powers[pw_invulnerability] = 20*TICRATE
P_RestoreMusic(p)
p.slots[num] = 7
//Speed Card
elseif item == 16 and p.powers[pw_sneakers] == 0 then
p.powers[pw_sneakers] = 20*TICRATE
P_RestoreMusic(p)
p.slots[num] = 7
//Whirlwind Shield Card
elseif item == 17 and p.powers[pw_shield] != 1 and p.powers[pw_shield] != 257 then
p.powers[pw_shield] = SH_JUMP
P_SpawnShieldOrb(p)
S_StartSound(nil,120,p)
p.slots[num] = 7
//Attraction Shield Card
elseif item == 18 and p.powers[pw_shield] != 2 and p.powers[pw_shield] != 258 then
p.powers[pw_shield] = SH_ATTRACT
P_SpawnShieldOrb(p)
S_StartSound(nil,120,p)
p.slots[num] = 7
//Sling
elseif item == 19 and p.bullets > 0 and p.attackTimer == 0 then
S_StartSound(nil,103,p)
p.bullets = p.bullets - 1
P_SpawnPlayerMissile(p.mo,MT_SLUNGBULLET)
p.attackTimer = 20
//Inferno Shield Card
elseif item == 20 and p.powers[pw_shield] != 9 and p.powers[pw_shield] != 265 then
A_FlameShield(p.mo,-1)
p.slots[num] = 7
//Liquid Shield Card
elseif item == 21 and p.powers[pw_shield] != 10 and p.powers[pw_shield] != 266 then
A_AquaShield(p.mo,-1)
p.slots[num] = 7
//Fire Seeds
elseif item == 22 and p.attackTimer == 0 then
p.flameHarmful = true
p.fireTimer = P_RandomRange(18,27)
p.seeds = p.seeds - 1
if p.seeds <= 0 then
p.seeds = 0
p.slots[num] = 0
end
//Flametongue
elseif item == 25 and p.seeds > 0 and p.attackTimer == 0 then
p.flameHarmful = false
p.fireTimer = P_RandomRange(18,27)
p.attackTimer = p.fireTimer //Doing this will prevent health from being lost, as well.
p.seeds = p.seeds - 1
if p.seeds <= 0 then
p.seeds = 0
p.slots[num] = 0
end
//Duster
elseif item == 26 and p.ammo > 0 and p.attackTimer == 0 then
S_StartSound(nil,sfx_s3k4d,p)
p.ammo = p.ammo - 1
P_SpawnPlayerMissile(p.mo,MT_JETTBULLET)
p.attackTimer = 15
//Wyre Miner
elseif item == 29 and p.energy >= 100 and p.attackTimer == 0 then
p.energy = p.energy - 100
P_SpawnPlayerMissile(p.mo,MT_LASER)
p.attackTimer = 10
//Wyre Zapper
elseif item == 30 and p.energy >= 100 and p.attackTimer == 0 then
p.energy = p.energy - 100
P_SpawnPlayerMissile(p.mo,MT_SPARKLASER)
p.attackTimer = 50
end
end
local function giveItem(p,item)
if p.player.slots[-3]==0 then
p.player.slots[-3]=item
return false
end
if p.player.slots[-1]==0 then
p.player.slots[-1]=item
return false
end
if p.player.slots[-2]==0 then
p.player.slots[-2]=item
return false
end
for i=1, 32 do
if p.player.slots[i]==0 then
p.player.slots[i]=item
return false
end
end
return true //If there is no room, don't pick up the item.
end
local function grantSeeds(p,num)
if p.seeds >= 99 or num < 0 then return true end //If seeds are already maxed or number given is negative, return false.
if p.seeds == 0 then //If the player does not have any seeds, give the Fire Seeds item.
if giveItem(p.mo,22) == true then return true end //If such cannot be done, return true (failure).
end
p.seeds = min(p.seeds+num,99)
end
local function segLength()
local length = 3
for i=0,6 do
if emeralds & 2^i then
length = length + 1
end
end
return length
end
//Status HUD draw function.
local function drawStatus(v,p)
if p.playerstate != PST_LIVE then return nil end //This should not show anything if the player is dead or if the menu is open.
local gdy = 12
local dy = gdy
if opt_barmode == 2 or opt_barmode == 3 or ((p.menuOpen == true or p.conversation != nil) and opt_barmode != 4) then
//Draw Life
/*v.drawString(310,dy,p.temphp.."\135/\128"..(p.maxhpseg*segLength()).." \135HP\128",V_SNAPTOTOP|V_SNAPTORIGHT,"right")
v.drawString(310,dy+10,(p.energy/100).."\132/\128"..(p.maxEnergy*10).." \132EP\128",V_SNAPTOTOP|V_SNAPTORIGHT,"right")
if p.temphp!=p.hp then v.drawString(310,dy+20,(p.temphp-p.hp).." \133Bleed\128",V_SNAPTOTOP|V_SNAPTORIGHT,"right") end*/
v.drawString(310,dy-2,p.temphp.."/"..(p.maxhpseg*segLength()).." HP",V_SNAPTOTOP|V_SNAPTORIGHT,"right")
if p.maxEnergy > 0 then v.drawString(310,dy+8,(p.energy/100).."/"..(p.maxEnergy*10).." EP",V_SNAPTOTOP|V_SNAPTORIGHT,"right") end
if p.temphp!=p.hp then v.drawString(310,dy+18,(p.temphp-p.hp).." Bleed",V_SNAPTOTOP|V_SNAPTORIGHT,"right") end
end
if opt_barmode == 2 then
gdy = 42
end
if !opt_barmode == 3 and ((p.menuOpen == false and p.conversation == nil) or opt_barmode == 4) then
dy = gdy
//Draw Life
v.draw(304,10,v.cachePatch("LIFETOP"),V_SNAPTOTOP|V_SNAPTORIGHT)
for seg=1,p.maxhpseg do
if seg > 1 then
v.draw(304,dy,v.cachePatch("LIFEMID"),V_SNAPTOTOP|V_SNAPTORIGHT)
dy = dy + 1
end
local segL = segLength()
for i=1,segL do
if p.hp > seg*segL+i-segL-1 then v.draw(305,dy,v.cachePatch("LIFEFULL"),V_SNAPTOTOP|V_SNAPTORIGHT)
elseif p.temphp > seg*segL+i-segL-1 then v.draw(305,dy,v.cachePatch("LIFETEMP"),V_SNAPTOTOP|V_SNAPTORIGHT)
else v.draw(305,dy,v.cachePatch("ENERGYEM"),V_SNAPTOTOP|V_SNAPTORIGHT) end
dy = dy + 1
end
end
v.draw(304,dy,v.cachePatch("LIFEBOTT"),V_SNAPTOTOP|V_SNAPTORIGHT)
//Draw Energy
local dy = gdy+2
if p.maxEnergy > 0 then
v.draw(296,10,v.cachePatch("ENERGYTO"),V_SNAPTOTOP|V_SNAPTORIGHT)
for seg=1,p.maxEnergy do
if seg > 1 then
v.draw(296,dy,v.cachePatch("ENERGYMI"),V_SNAPTOTOP|V_SNAPTORIGHT)
dy = dy + 3
end
for i=1,10 do
if p.energy > (seg*10+i-11)*100 then v.draw(297,dy,v.cachePatch("ENERGYFU"),V_SNAPTOTOP|V_SNAPTORIGHT)
else v.draw(297,dy,v.cachePatch("ENERGYEM"),V_SNAPTOTOP|V_SNAPTORIGHT) end
dy = dy + 1
end
end
v.draw(296,dy,v.cachePatch("ENERGYBO"),V_SNAPTOTOP|V_SNAPTORIGHT)
end
end
//Draw status effects.
dy = 10
if p.temp <= -1000 then
v.drawString(10,dy+20,"Cold",V_SNAPTOTOP|V_SNAPTOLEFT|V_BLUEMAP)
v.draw(10,dy+1,v.cachePatch("ICONCOLD"),V_SNAPTOTOP|V_SNAPTOLEFT)
v.drawNum(50,dy+5,-p.temp/1000,V_SNAPTOTOP|V_SNAPTOLEFT)
dy = dy + 30
elseif p.temp >= 1000 then
v.drawString(10,dy+20,"Hot",V_SNAPTOTOP|V_SNAPTOLEFT|V_REDMAP)
v.draw(10,dy+1,v.cachePatch("ICONHEAT"),V_SNAPTOTOP|V_SNAPTOLEFT)
v.drawNum(50,dy+5,p.temp/1000,V_SNAPTOTOP|V_SNAPTOLEFT)
dy = dy + 30
end
if p.wet > 0 then
v.drawString(10,dy+20,"Wet",V_SNAPTOTOP|V_SNAPTOLEFT|V_BLUEMAP)
v.draw(10,dy+1,v.cachePatch("ICONWET"),V_SNAPTOTOP|V_SNAPTOLEFT)
v.drawNum(50,dy+5,(p.wet-1)/500+1,V_SNAPTOTOP|V_SNAPTOLEFT)
dy = dy + 30
end
if p.mo.eflags & MFE_UNDERWATER and 1050-p.powers[pw_underwater] > 350 and p.powers[pw_underwater]!=0 then
v.drawString(10,dy+20,"Drowning",V_SNAPTOTOP|V_SNAPTOLEFT|V_BLUEMAP)
v.draw(10,dy+1,v.cachePatch("ICONDRWN"),V_SNAPTOTOP|V_SNAPTOLEFT)
v.drawNum(50,dy+5,(1050-p.powers[pw_underwater]-386)/70+1,V_SNAPTOTOP|V_SNAPTOLEFT)
dy = dy + 30
end
end
local function cannotAttack(p,slot)
if p.attackTimer > 0 and delayType[p.slots[slot]] == 1 then return true end
return false
end
local function drawInventory(v,p)
if p.playerstate != PST_LIVE then return nil end //This should not show anything if the player is dead or if the menu is open.
v.drawString(100-v.stringWidth("SPIN")/2,10,"SPIN",V_SNAPTOTOP)
if cannotAttack(p,-3) then
v.draw(89,22,v.cachePatch("NOATTACK"),V_SNAPTOTOP|V_50TRANS)
end
if p.slots[-3] > 0 then v.draw(91,24,v.cachePatch(getIcon(p,p.slots,-3)),V_SNAPTOTOP) end
v.drawString(108,34,getItemNum(p,-3),V_SNAPTOTOP,"right")
v.draw(88,21,v.cachePatch("MENUCURS"),V_SNAPTOTOP)
v.drawString(160-v.stringWidth("C1")/2,10,"C1",V_SNAPTOTOP)
if cannotAttack(p,-1) then
v.draw(149,22,v.cachePatch("NOATTACK"),V_SNAPTOTOP|V_50TRANS)
end
if p.slots[-1] > 0 then v.draw(151,24,v.cachePatch(getIcon(p,p.slots,-1)),V_SNAPTOTOP) end
v.drawString(168,34,getItemNum(p,-1),V_SNAPTOTOP,"right")
v.draw(148,21,v.cachePatch("MENUCURS"),V_SNAPTOTOP)
v.drawString(220-v.stringWidth("C2")/2,10,"C2",V_SNAPTOTOP)
if cannotAttack(p,-2) then
v.draw(209,22,v.cachePatch("NOATTACK"),V_SNAPTOTOP|V_50TRANS)
end
if p.slots[-2] > 0 then v.draw(211,24,v.cachePatch(getIcon(p,p.slots,-2)),V_SNAPTOTOP) end
v.drawString(228,34,getItemNum(p,-2),V_SNAPTOTOP,"right")
v.draw(208,21,v.cachePatch("MENUCURS"),V_SNAPTOTOP)
//Draw the names if the screen is large enough and the menu is not open. Otherwise, don't bother. The text is either illegibly small or potentially covered up by the inventory.
if not p.menuOpen and v.width() >= 640 and v.height() >= 400 then
v.drawString(100-v.stringWidth(getName(p,-3),0,"small")/2,48,getName(p,-3),V_SNAPTOTOP,"small")
v.drawString(160-v.stringWidth(getName(p,-1),0,"small")/2,48,getName(p,-1),V_SNAPTOTOP,"small")
v.drawString(220-v.stringWidth(getName(p,-2),0,"small")/2,48,getName(p,-2),V_SNAPTOTOP,"small")
end
if not p.menuOpen then return nil end //This should not show anything else if the menu is closed.
//The global offset for all things on the menu. The menu should be centered on the screen horizontally. The global vertical offset will likely be consistently 50, but this may change.
local offsetX = 7
local offsetY = 50 //This should be AT LEAST 50.
//Heading
if p.shopMode == 0 then
v.drawString(160,offsetY,"Inventory",0,"center") //Draw inventory heading.
else
v.drawString(160,offsetY,shopName[p.currentShop],0,"center") //Draw inventory heading.
//v.drawString(160-v.stringWidth(shopName, 0, "thin")/2,offsetY,shopName,0,"thin")
end
v.drawString(310,offsetY,"Rings: "..p.ringCount,0,"right") //Draw points.
v.draw(offsetX,offsetY+10,v.cachePatch("INVENBOX")) //Draw main box.
v.draw(offsetX+218,offsetY+10,v.cachePatch("SIDEBOX")) //Draw side box.
v.draw(offsetX,offsetY+124,v.cachePatch("DESCBOX")) //Draw description box.
//Draw current inventory.
local invUsed = p.slots //The inventory table to draw.
if p.shopMode == 1 then invUsed = shopInv[p.currentShop]
elseif p.shopMode == 3 then invUsed = buyBack end
for i=1, 32 //Draw inventory items.
if invUsed[i] > 0 then v.draw(offsetX+9+((i-1)%8)*26,offsetY+19+((i-1)/8)*26,v.cachePatch(getIcon(p,invUsed,i))) end
v.drawString(offsetX+26+((i-1)%8)*26,offsetY+29+((i-1)/8)*26,getItemNum(p,i),0,"right")
end
if p.shopMode > 0 then
if p.shopMode == 1 then
v.draw(offsetX+227,offsetY+19,v.cachePatch("ICONBUY"))
else
v.draw(offsetX+227,offsetY+19,v.cachePatch("ICONBUY"),V_TRANSLUCENT)
end
if p.shopMode == 2 then
v.draw(offsetX+253,offsetY+19,v.cachePatch("ICONSELL"))
else
v.draw(offsetX+253,offsetY+19,v.cachePatch("ICONSELL"),V_TRANSLUCENT)
end
if p.shopMode == 3 then
v.draw(offsetX+279,offsetY+19,v.cachePatch("ICONBUYB"))
else
v.draw(offsetX+279,offsetY+19,v.cachePatch("ICONBUYB"),V_TRANSLUCENT)
end
//Draw buy price.
if p.shopMode == 1 and invUsed[8*p.cY+p.cX+1] > 0 then
v.drawString(10,offsetY,"Price: "..(price[invUsed[8*p.cY+p.cX+1]])) //Draw price.
//Draw sell or buy-back price.
elseif invUsed[8*p.cY+p.cX+1] > 0 then
if price[invUsed[8*p.cY+p.cX+1]] == 0 then
v.drawString(10,offsetY,"NO SALE") //Draw points.
else
v.drawString(10,offsetY,"Price: "..(price[invUsed[8*p.cY+p.cX+1]]/2)) //Draw price.
end
end
//Shop option description.
if p.cX == 8 and p.cY == 0 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Buy items for rings",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"Buy items for rings.",0,"small")
end
elseif p.cX == 9 and p.cY == 0 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Sell items for rings",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"Sell items for rings.",0,"small")
end
elseif p.cX == 10 and p.cY == 0 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Buy items back at selling price",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"Buy items back at selling price.",0,"small")
v.drawString(offsetX+5,offsetY+135,"Items not bought back are cleared at the end of each level.",0,"small")
end
end
else
//Draw options icon.
v.draw(offsetX+253,offsetY+19,v.cachePatch("ICONSETT"))
//Draw converter.
if p.convertMode == 1 then v.draw(offsetX+227,offsetY+97,v.cachePatch("ICONCONH")) end
if p.convertMode == 2 then v.draw(offsetX+227,offsetY+97,v.cachePatch("ICONCONE")) end
//Draw batteries owned.
if p.maxEnergy >= 1 then
if leveltime%2 == 1 then
v.draw(offsetX+248,offsetY+93,v.cachePatch("ICONBATT"))
else
v.draw(offsetX+248,offsetY+93,v.cachePatch("ICONBAT2"))
end
if p.maxEnergy < 10 then v.drawString(offsetX+263,offsetY+104,p.maxEnergy)
else v.drawString(offsetX+262,offsetY+104,p.maxEnergy,0,"thin") end
end
//Draw any emeralds currently owned.
if emeralds & EMERALD1 then v.draw(offsetX+284,offsetY+102,v.cachePatch("TEMER1")) end
if emeralds & EMERALD2 then v.draw(offsetX+280,offsetY+96,v.cachePatch("TEMER2")) end
if emeralds & EMERALD3 then v.draw(offsetX+288,offsetY+96,v.cachePatch("TEMER3")) end
if emeralds & EMERALD4 then v.draw(offsetX+292,offsetY+102,v.cachePatch("TEMER4")) end
if emeralds & EMERALD5 then v.draw(offsetX+288,offsetY+108,v.cachePatch("TEMER5")) end
if emeralds & EMERALD6 then v.draw(offsetX+280,offsetY+108,v.cachePatch("TEMER6")) end
if emeralds & EMERALD7 then v.draw(offsetX+276,offsetY+102,v.cachePatch("TEMER7")) end
//Settings description.
if p.cX == 9 and p.cY == 0 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Change various game settings",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"Change various game settings.",0,"small")
end
//Battery description.
elseif p.cX == 9 and p.cY == 3 and p.maxEnergy > 0 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Your batteries. Used to power many devices.",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"Your collected Egg Batteries.",0,"small")
v.drawString(offsetX+5,offsetY+135,"Used to power any of Eggman's devices you have taken.",0,"small")
end
//Emeralds description.
elseif p.cX == 10 and p.cY == 3 and emeralds != 0 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Your chaos emeralds. Can often boost devices built by Eggman.",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"Your collected chaos emeralds.",0,"small")
v.drawString(offsetX+5,offsetY+135,"Devices built by Eggman can often be boosted by these.",0,"small")
end
//Converter description.
elseif p.cX == 8 and p.cY == 3 and p.convertMode != 0 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Converts rings to health or energy Select to toggle preference",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"This converter converts collected rings to health or energy.",0,"small")
v.drawString(offsetX+5,offsetY+135,"Select to toggle which one is prioritized.",0,"small")
end
//Debug room description.
elseif p.cY == 1 and p.cX == 9 and gamemap != 172 then
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,"Nothing to see in this slot",0,"thin")
else
v.drawString(offsetX+5,offsetY+129,"Nothing to see in this slot.",0,"small")
v.drawString(offsetX+5,offsetY+135,"Yup, completely blank.",0,"small")
end
end
end
//Draw item selection cursor.
if (leveltime/10)%2 == 1 then
if p.cX < 8 then v.draw(offsetX+6+(p.cX*26),offsetY+16+(p.cY*26),v.cachePatch("MENUCURS"))
else v.draw(offsetX+16+(p.cX*26),offsetY+16+(p.cY*26),v.cachePatch("MENUCURS")) end
end
//Draw the selected item's name and description.
if p.cX < 8 and invUsed[8*p.cY+p.cX+1] > 0 then
//Draw a single thin line if at low res.
if v.width() < 640 or v.height() < 400 then
v.drawString(offsetX+5,offsetY+129,itemName[invUsed[8*p.cY+p.cX+1]]..":",V_YELLOWMAP,"thin")
v.drawString(offsetX+5+v.stringWidth(itemName[invUsed[8*p.cY+p.cX+1]]..": ",0,"thin"),offsetY+129,itemShortDesc[invUsed[8*p.cY+p.cX+1]],0,"thin")
else
v.drawString(offsetX+5,offsetY+129,itemName[invUsed[8*p.cY+p.cX+1]]..":",V_YELLOWMAP,"small")
v.drawString(offsetX+5+v.stringWidth(itemName[invUsed[8*p.cY+p.cX+1]]..": ",0,"small"),offsetY+129,itemDesc[invUsed[8*p.cY+p.cX+1]],0,"small")
v.drawString(offsetX+5,offsetY+135,itemDesc2[invUsed[8*p.cY+p.cX+1]],0,"small")
end
end
end
local function initp(p)
p.flameHarmful = false //Whether the fire being spewed damages the player.
p.attackTimer = 0 //While this is positive, attacks (such as the sling) will not function.
p.temp = 0 //Temperature. If this reaches positive or negative 10,000, the rat dies.
p.wet = 0 //Wetness. Caps at 5,000.
p.drown = 0 //Drowning timer. This will control the in-game timer. Default cap is 1050 (30 seconds). Egg Rebreather triples this time.
p.menuOpen = false //This tracks whether the menu is open.
end
local function reinit(p)
initp(p.player)
end
//Handles the inventory menu.
local function menuHandler(p)
//Move the cursor, if needed.
if p.cmd.forwardmove > 25 and p.oldForwardMove <= 25 then
p.cY = (p.cY - 1) % 4
S_StartSound(nil,142,p)
end
if p.cmd.forwardmove < -25 and p.oldForwardMove >= -25 then
p.cY = (p.cY + 1) % 4
S_StartSound(nil,142,p)
end
if p.cmd.sidemove > 25 and p.oldSideMove <= 25 then
p.cX = (p.cX + 1) % 11
S_StartSound(nil,142,p)
end
if p.cmd.sidemove < -25 and p.oldSideMove >= -25 then
p.cX = (p.cX - 1) % 11
S_StartSound(nil,142,p)
end
if p.cX < 0 then p.cX = p.cX + 11 end
if p.cY < 0 then p.cY = p.cY + 4 end
//Non-shop
if p.shopMode == 0 then
//Handle converter changing.
if isPress(p,BT_USE) and (p.slots[-3] != 0 or p.slots[8*p.cY+p.cX+1]) != 0 and p.cX < 8 then
local swap = p.slots[-3]
p.slots[-3] = p.slots[8*p.cY+p.cX+1]
p.slots[8*p.cY+p.cX+1] = swap
S_StartSound(nil,244,p)
elseif isPress(p,BT_CUSTOM1) and (p.slots[-1] != 0 or p.slots[8*p.cY+p.cX+1] != 0) and p.cX < 8 then
local swap = p.slots[-1]
p.slots[-1] = p.slots[8*p.cY+p.cX+1]
p.slots[8*p.cY+p.cX+1] = swap
S_StartSound(nil,244,p)
elseif isPress(p,BT_CUSTOM2) and (p.slots[-2] != 0 or p.slots[8*p.cY+p.cX+1] != 0) and p.cX < 8 then
local swap = p.slots[-2]
p.slots[-2] = p.slots[8*p.cY+p.cX+1]
p.slots[8*p.cY+p.cX+1] = swap
S_StartSound(nil,244,p)
elseif (isPress(p,BT_USE) or isPress(p,BT_CUSTOM1) or isPress(p,BT_CUSTOM2)) and p.cY == 3 and p.cX == 8 then
if p.convertMode == 1 then p.convertMode = 2
elseif p.convertMode == 2 then p.convertMode = 1 end
if p.convertMode != 0 then S_StartSound(nil,319,p) end
//Handle settings.
elseif (isPress(p,BT_USE) or isPress(p,BT_CUSTOM1) or isPress(p,BT_CUSTOM2)) and p.cY == 0 and p.cX == 9 then
p.menuOpen = false
converse(p,settingsMain)
//Handle debug room.
elseif (isPress(p,BT_USE) or isPress(p,BT_CUSTOM1) or isPress(p,BT_CUSTOM2)) and gamemap != 172 and p.cY == 1 and p.cX == 9 then
G_ExitLevel(172, true)
end
//Shop
else
//Draw current inventory.
local invUsed = p.slots //The inventory table to draw.
if p.shopMode == 1 then invUsed = shopInv[p.currentShop]
elseif p.shopMode == 3 then invUsed = buyBack end
//Shop right-side options.
if (isPress(p,BT_USE) or isPress(p,BT_CUSTOM1) or isPress(p,BT_CUSTOM2)) and p.cY == 0 and p.cX > 7 and p.shopMode != p.cX - 7 then
S_StartSound(nil,234,p)
p.shopMode = p.cX - 7
elseif (isPress(p,BT_USE) or isPress(p,BT_CUSTOM1) or isPress(p,BT_CUSTOM2)) and p.cX < 8 and p.shopMode > 0 and invUsed[8*p.cY+p.cX+1] > 0 then
if p.shopMode == 2 then
//Seeds
if p.slots[8*p.cY+p.cX+1] == 22 then
p.ringCount = p.ringCount + price[p.slots[8*p.cY+p.cX+1]]/2
shopSeedsBuyback[p.currentShop] = shopSeedsBuyback[p.currentShop] + 1
if shopSeeds[p.currentShop] > 99 then shopSeeds[p.currentShop] = 99 end
p.seeds = p.seeds - 1
if p.seeds == 0 then p.slots[8*p.cY+p.cX+1] = 0 end
S_StartSound(nil,244,p)
if shopSeedsBuyback[p.currentShop] == 1 then
for i=1, 32 do
if buyBack[i]==0 then
buyBack[i]=22
return false
end
end
end
//Misc. Sellables
elseif price[p.slots[8*p.cY+p.cX+1]] > 0 then
p.ringCount = p.ringCount + price[p.slots[8*p.cY+p.cX+1]]/2
local itemSold = p.slots[8*p.cY+p.cX+1]
p.slots[8*p.cY+p.cX+1] = 0
S_StartSound(nil,244,p)
for i=1, 32 do
if buyBack[i]==0 then
buyBack[i]=itemSold
return false
end
end
end
else
local priceFactor = 1
if p.shopMode == 3 then priceFactor = 2 end
//Bullet Box
if invUsed[8*p.cY+p.cX+1] == 28 then
if p.ringCount >= price[invUsed[8*p.cY+p.cX+1]]/priceFactor and p.ammo < 99 then
p.ringCount = p.ringCount - price[invUsed[8*p.cY+p.cX+1]]/priceFactor
p.ammo = p.ammo + 30
if p.ammo > 99 then p.ammo = 99 end //If the player is over max energy, fix that.
invUsed[8*p.cY+p.cX+1] = 0
S_StartSound(nil,244,p)
end
//Bullet Clip
elseif invUsed[8*p.cY+p.cX+1] == 27 then
if p.ringCount >= price[invUsed[8*p.cY+p.cX+1]]/priceFactor and p.ammo < 99 then
p.ringCount = p.ringCount - price[invUsed[8*p.cY+p.cX+1]]/priceFactor
p.ammo = p.ammo + 6
if p.ammo > 99 then p.ammo = 99 end //If the player is over max energy, fix that.
invUsed[8*p.cY+p.cX+1] = 0
S_StartSound(nil,244,p)
end
//Sling Bullets
elseif invUsed[8*p.cY+p.cX+1] == 24 then
if p.ringCount >= price[invUsed[8*p.cY+p.cX+1]]/priceFactor and p.bullets < 99 then
p.ringCount = p.ringCount - price[invUsed[8*p.cY+p.cX+1]]/priceFactor
p.bullets = p.bullets + 30
if p.bullets > 99 then p.bullets = 99 end //If the player is over max energy, fix that.
invUsed[8*p.cY+p.cX+1] = 0
S_StartSound(nil,244,p)
end
//Instant Recharges
elseif invUsed[8*p.cY+p.cX+1] == 23 then
if p.ringCount >= price[invUsed[8*p.cY+p.cX+1]]/priceFactor and p.energy < p.maxEnergy * 1000 then
p.ringCount = p.ringCount - price[invUsed[8*p.cY+p.cX+1]]/priceFactor
p.energy = p.energy + 1000
if p.energy > p.maxEnergy * 1000 then p.energy = p.maxEnergy * 1000 end //If the player is over max energy, fix that.
invUsed[8*p.cY+p.cX+1] = 0
S_StartSound(nil,244,p)
end
//Seeds
elseif invUsed[8*p.cY+p.cX+1] == 22 then
if p.ringCount >= price[invUsed[8*p.cY+p.cX+1]]/priceFactor and not grantSeeds(p,1) then
p.ringCount = p.ringCount - price[invUsed[8*p.cY+p.cX+1]]/priceFactor
if p.shopMode == 1 then shopSeeds[p.currentShop] = shopSeeds[p.currentShop] - 1 else shopSeedsBuyback[p.currentShop] = shopSeedsBuyback[p.currentShop] - 1 end
if (shopSeeds[p.currentShop] < 1 and p.shopMode == 1) or (shopSeedsBuyback[p.currentShop] < 1 and p.shopMode == 3) then invUsed[8*p.cY+p.cX+1] = 0 end
S_StartSound(nil,244,p)
end
//Misc. Buyables
elseif price[invUsed[8*p.cY+p.cX+1]] > 0 and p.ringCount >= price[invUsed[8*p.cY+p.cX+1]]/priceFactor and not giveItem(p.mo,invUsed[8*p.cY+p.cX+1]) then
p.ringCount = p.ringCount - price[invUsed[8*p.cY+p.cX+1]]/priceFactor
invUsed[8*p.cY+p.cX+1] = 0
S_StartSound(nil,244,p)
end
end
end
end
end
addHook("ThinkFrame", do
for p in players.iterate()
if p.wet == nil then
p.energy = 0
p.maxEnergy = 0 //Max energy is in energy segments, not points like energy is.
p.maxhp = 6
p.maxhpseg = 2 //How many health segments there are. I do not yet know how I plan for these to be increased. The max this can go to is 12.
p.hp = 6
p.temphp = 6
p.convertMode = 0 //convertMode is 0 if converter is not present, 1 if refilling health first, 2 if refilling energy first.
p.bullets = 0 //Number of sling bullets owned.
p.seeds = 0 //Number of fire seeds owned.
hud.disable("rings")
hud.disable("time")
hud.disable("score")
hud.disable("lives")
p.ringCount = 0
p.oldButtons = 0
p.oldSideMove = 0
p.oldForwardMove = 0
initp(p)
end
if gameFrozen == false then
//Handle attack timer.
if p.attackTimer > 0 then p.attackTimer = p.attackTimer - 1 end
//Handle infinite lives.
p.lives = 99
//Handle player size. Yes, this does not match vanilla SRB2, but while the large animals are no big deal (no pun intended) in vanilla, the player is supposed to FEEL small, in this mod.
if p.mo.skin == "mouse" then P_SetScale(p.mo,FRACUNIT/2) end
//Handle bleeding and death.
if leveltime%200 == 0 and p.temphp > p.hp then p.temphp = p.temphp - 1 end
if p.temphp <= 0 and p.playerstate == PST_LIVE then P_DamageMobj(p.mo, nil, nil, 10000) end
//Handle converting rings to ring count.
local oldRings = p.ringCount
p.ringCount = p.ringCount + p.mo.health - 1
p.mo.health = 1
p.health = 1
if players[0].playerstate != PST_LIVE then return nil end //This should not do anything further if the player is dead.
//Sector handlers for heat and cold.
if checkSectors(p,801) then
if p.temp > 0 then p.temp = p.temp - 9 end
p.temp = p.temp - 1
if p.wet > 0 then p.temp = p.temp - ((p.wet-1)/1000+1) end //Get cold faster when wet.
p.wet = p.wet - 1 //Dry slower in cold environments.
elseif checkSectors(p,802) then
if p.temp > -995 then p.temp = -995 end
p.temp = p.temp - 5
if p.wet > 0 then p.temp = p.temp - ((p.wet-1)/200+1) end //...I would not recommend going into bitter cold weather while wet.
p.wet = p.wet - 1 //Dry slower in cold environments.
elseif checkSectors(p,803) then
if p.temp < 0 then p.temp = p.temp + 9 end
p.temp = p.temp + 1
p.wet = p.wet - 4 //Dry quicker in hot environments.
elseif checkSectors(p,804) then
if p.temp < 995 then p.temp = 995 end
p.temp = p.temp + 5
p.wet = p.wet - 20 //Dry quicker in hot environments.
else
if abs(p.temp) < 5 then p.temp = 0
elseif p.temp > 0 then p.temp = p.temp - 5
elseif p.temp < 0 then p.temp = p.temp + 5 end
p.wet = p.wet - 2
end
if string.sub(p.mo.subsector.sector.ceilingpic,0,6) == "F_SKY1" and (curWeather == PRECIP_STORM or curWeather == PRECIP_RAIN or curWeather == PRECIP_STORM_NOSTRIKES) then p.wet = p.wet + 7 end
if P_RandomRange(0,4999) < p.wet then p.temp = p.temp - 1 end //Being wet may help cool off and hinder warming up.
if p.wet < 0 then p.wet = 0 end //If negative wetness, fix it.
if p.wet > 5000 or p.mo.eflags & MFE_UNDERWATER then p.wet = 5000 end //If p.wetness over cap, fix it. If the player is underwater, soak them.
if p.wet < 500 and p.mo.eflags & MFE_TOUCHWATER then p.wet = 500 end //If the player is touching water, they should get at least a LITTLE wet.
if emeralds == 127 then
p.energy = p.energy + 10
if p.energy > p.maxEnergy * 1000 then p.energy = p.maxEnergy * 1000 end
end //Regenerate energy if all emeralds have been collected.
//Handle shield environmental immunities.
if p.powers[pw_shield] == 9 or p.powers[pw_shield] == 265 or p.powers[pw_shield] == SH_ELEMENTAL or p.powers[pw_shield] == SH_ELEMENTAL|256 then //Inferno Shield keeps the user dry (though it still disappears if plunged into water) and at just the right temperature.
p.wet = 0
p.temp = 0
elseif p.powers[pw_shield] == 10 or p.powers[pw_shield] == 266 then //Liquid Shield also keeps the user dry (somewhat ironically, depending on how you look at it).
p.wet = 0
end
//Handle being too cold or too hot.
if abs(p.temp) >= 10000 and leveltime % 35 == 0 then
p.hp = p.hp - 1
p.temphp = p.temphp - 1
end
//Handle fire seeds and Flametongue
if p.fireTimer != nil and p.fireTimer>0 then
p.fireTimer = p.fireTimer - 1
if p.fireTimer % 3 == 0 then
local flame = P_SpawnPlayerMissile(p.mo,MT_FLAMEJETFLAME)
flame.momx = flame.momx*5
flame.momy = flame.momy*5
flame.momz = flame.momz*5
S_StartSound(p.mo,sfx_fire,nil)
if p.fireTimer % 15 == 0 and p.flameHarmful == true then
p.hp = p.hp - 1
p.temphp = p.temphp - 1
end
end
end
end
//Set nearest monitor for egg cards.
nearestMonitor = nil
local monitorDist = 50*FRACUNIT //The distance away the nearest monitor is. The starting distance sets the max distance a monitor can be away to be affected by this item.
for spot in mapthings.iterate do
if spot.mobj != nil then
local mon = spot.mobj
if mon.type == MT_SUPERRINGBOX or mon.type == MT_BLACKTV or mon.type == MT_BLUETV or mon.type == MT_EGGMANBOX or mon.type == MT_GREENTV or mon.type == MT_PITYTV or mon.type == MT_INV or mon.type == MT_SNEAKERTV or mon.type == MT_WHITETV or mon.type == MT_YELLOWTV or mon.type == MT_FIRETV or mon.type == MT_WATERTV then //If it's ANY valid monitor type.
if P_CheckSight(p.mo,mon) then
if R_PointToDist2(p.mo.x,p.mo.y,mon.x,mon.y) < monitorDist then
monitorDist = R_PointToDist2(p.mo.x,p.mo.y,mon.x,mon.y)
nearestMonitor = mon
end
end
end
end
end
//Handle if the menu is currently open.
if p.conversation != nil then
//Stop the character from moving.
p.normalspeed = 0
p.jumpfactor = 0
p.charability = 0
p.charability2 = 0
for i=0,22
p.powers[i] = p.oldPowers[i]
end
dialogueHandler(p)
elseif p.menuOpen == true then
//Stop the character from moving.
p.normalspeed = 0
p.jumpfactor = 0
p.charability = 0
p.charability2 = 0
for i=0,22
p.powers[i] = p.oldPowers[i]
end
if isPress(p,BT_CUSTOM3) then
freezeAll(false)
p.menuOpen = false
p.shopMode = 0
else
menuHandler(p)
end
//Handle if dialogue is currently open.
//Handle special input if the menu is not open.
else
//Otherwise, set speed, jump height, and abilities back to normal.
p.normalspeed = skins[p.mo.skin].normalspeed
p.jumpfactor = skins[p.mo.skin].jumpfactor
p.charability = skins[p.mo.skin].ability
p.charability2 = skins[p.mo.skin].ability2
if isPress(p,BT_CUSTOM3) then
p.oldPowers = {}
for i=0,22
p.oldPowers[i] = p.powers[i]
end
freezeAll(true)
p.menuOpen = true
//Reset the cursor coordinates.
p.cX = 0
p.cY = 0
elseif isPress(p,BT_USE) then
useItem(p,-3)
elseif isPress(p,BT_CUSTOM1) then
useItem(p,-1)
elseif isPress(p,BT_CUSTOM2) then
useItem(p,-2)
end
end
p.oldButtons = p.cmd.buttons //Keep the oldbuttons variable up to date.
p.oldSideMove = p.cmd.sidemove
p.oldForwardMove = p.cmd.forwardmove
//Remove any cached images not used during this or the previous frame.
/*for key,val in pairs(cacheUsed) do
if cacheUsed[key] + 1 < leveltime then
cachedIcons[key] = nil
cacheUsed[key] = nil
print("Removed "..key.." from cache")
end
end*/
end
end)
addHook("MobjDeath", reinit, MT_PLAYER)
//Ring
addHook("TouchSpecial", function(o,p)
p = p.player
if p.convertMode == 1 then
if p.hp < p.maxhp then
p.hp = p.hp+1
p.temphp = p.temphp+1
elseif p.energy < p.maxEnergy * 1000 then
p.energy = p.energy + 100
end
elseif p.convertMode == 2 then
if p.energy < p.maxEnergy * 1000 then
p.energy = p.energy + 100
elseif p.hp < p.maxhp then
p.hp = p.hp+1
p.temphp = p.temphp+1
end
end
if p.temphp > p.maxhp then p.temphp = p.maxhp end
end, MT_RING)
//Thermos
addHook("TouchSpecial", function(o,p)
//print("Got a thermos. This should be useful for a long journey.")
return giveItem(p,2)
end, MT_THERMOS) //I'm not sure why MT_THERMOS did not work, but 417 did, so that works.
//Recharge Cell
addHook("TouchSpecial", function(o,p)
if p.player.energy >= p.player.maxEnergy * 1000 then return true end //If the player is already at max energy, don't pick this up.
p.player.energy = p.player.energy + 1000
if p.player.energy > p.player.maxEnergy * 1000 then p.player.energy = p.player.maxEnergy * 1000 end //If the player is over max energy, fix that.
//print("Got an Egg Battery Recharge Cell.")
return false
end, MT_CELL)
//Battery
addHook("TouchSpecial", function(o,p)
if p.player.maxEnergy >= 10 then return true end //If the player is already at capped max energy, don't pick this up.
p.player.maxEnergy = p.player.maxEnergy + 1
p.player.energy = p.player.energy + 1000
//if p.player.maxEnergy == 1 then print("Got a Rechargable Egg Battery! Now you can power various devices!")
//else print("Got a Rechargable Egg Battery! Energy storage increased!") end
return false
end, MT_BATTERY)
//Rebreather
addHook("TouchSpecial", function(o,p)
return giveItem(p,6)
end, MT_REBREATHER)
//Empty Card
addHook("TouchSpecial", function(o,p)
return giveItem(p,7)
end, MT_CARD_EMPTY)
//Medikit
addHook("TouchSpecial", function(o,p)
return giveItem(p,9)
end, MT_MEDIKIT)
//Converter
addHook("TouchSpecial", function(o,p)
p.player.convertMode = 1
end, MT_CONVERTER)
//Sling Bullet
addHook("TouchSpecial", function(o,p)
if p.player.bullets >= 99 then return true end //Enforce the 99 bullet cap.
p.player.bullets = p.player.bullets + 1 //Gain a bullet.
end, MT_BULLET)
//Fire Seed
addHook("TouchSpecial", function(o,p)
if grantSeeds(p.player,1) then return true end
end, MT_FIRESEED)
//Sling Bullet Pile
addHook("TouchSpecial", function(o,p)
if p.player.bullets >= 99 then return true end //Enforce the 99 bullet cap.
p.player.bullets = p.player.bullets + 30 //Gain 30 bullets.
if p.player.bullets > 99 then p.player.bullets = 99 end //Enforce the 99 bullet cap again.
end, MT_BULLETPILE)
//Ammo Clip
addHook("TouchSpecial", function(o,p)
if p.player.ammo >= 99 then return true end //Enforce the 99 gun bullet cap.
p.player.ammo = p.player.ammo + 6 //Gain 6 gun bullets.
if p.player.ammo > 99 then p.player.ammo = 99 end //Enforce the 99 gun bullet cap again.
end, MT_CLIP)
//Ammo Box
addHook("TouchSpecial", function(o,p)
if p.player.ammo >= 99 then return true end //Enforce the 99 gun bullet cap.
p.player.ammo = p.player.ammo + 30 //Gain 6 gun bullets.
if p.player.ammo > 99 then p.player.ammo = 99 end //Enforce the 99 gun bullet cap again.
end, MT_BULLETBOX)
//Recalculate max HP when touching any emerald.
addHook("TouchSpecial", function(o,p)
p = p.player
p.maxhp = p.maxhp + p.maxhpseg
p.hp = p.hp + p.maxhpseg
p.temphp = p.temphp + p.maxhpseg
end, MT_EMERALD1)
addHook("TouchSpecial", function(o,p)
p = p.player
p.maxhp = p.maxhp + p.maxhpseg
p.hp = p.hp + p.maxhpseg
p.temphp = p.temphp + p.maxhpseg
end, MT_EMERALD2)
addHook("TouchSpecial", function(o,p)
p = p.player
p.maxhp = p.maxhp + p.maxhpseg
p.hp = p.hp + p.maxhpseg
p.temphp = p.temphp + p.maxhpseg
end, MT_EMERALD3)
addHook("TouchSpecial", function(o,p)
p = p.player
p.maxhp = p.maxhp + p.maxhpseg
p.hp = p.hp + p.maxhpseg
p.temphp = p.temphp + p.maxhpseg
end, MT_EMERALD4)
addHook("TouchSpecial", function(o,p)
p = p.player
p.maxhp = p.maxhp + p.maxhpseg
p.hp = p.hp + p.maxhpseg
p.temphp = p.temphp + p.maxhpseg
end, MT_EMERALD5)
addHook("TouchSpecial", function(o,p)
p = p.player
p.maxhp = p.maxhp + p.maxhpseg
p.hp = p.hp + p.maxhpseg
p.temphp = p.temphp + p.maxhpseg
end, MT_EMERALD6)
addHook("TouchSpecial", function(o,p)
p = p.player
p.maxhp = p.maxhp + p.maxhpseg
p.hp = p.hp + p.maxhpseg
p.temphp = p.temphp + p.maxhpseg
end, MT_EMERALD7)
//Hook and function to handle damage from various sources.
local function damagePlayer(p,damage,temp)
if p.powers[pw_shield] != 0 and p.powers[pw_shield] != 256 then //I have no intention to give the mouse a fire flower, but for the sake of reusability, I'll account for the possibility anyway.
//Sound 121
if p.powers[pw_shield] % 256 == 4 then
P_BlackOw(p)
elseif p.powers[pw_shield] > 512 and p.powers != 768 then //If the player has a force shield and it has more than 1 health.
p.powers[pw_shield] = p.powers[pw_shield] - 1
P_SpawnShieldOrb(p)
else //Destroy the shield.
P_RemoveShield(p)
end
return //Do not deal any damage if the player is protected by a shield.
end
local ndamage = 0
local ntemp = 0
while damage > 0 do
ndamage = ndamage + P_RandomRange(1,8)
damage = damage - 1
end
while temp > 0 do
ntemp = ntemp + P_RandomRange(1,12)
temp = temp - 1
end
p.hp = p.hp - ndamage - ntemp
p.temphp = p.temphp - ndamage
if p.hp < 0 then
p.temphp = p.temphp + p.hp
p.hp = 0
end
end
//Damage player?
addHook("ShouldDamage", function(p, damager, source, damage)
if damager == nil or damager.type == MT_NULL then
p.player.hp = 0 //p.player.maxhp
p.player.temphp = 0 //p.player.maxhp
return true //This should only happen in the event of something that should instantly kill.
elseif p.player.powers[pw_flashing] > 0 or p.player.powers[pw_invulnerability] > 0 then return false //Only death from loss of HP should bypass invulnerability.
elseif damager.type == MT_BLUECRAWLA then
damagePlayer(p.player,2,1)
elseif damager.type == MT_DEMONFIRE then
if p.player.powers[pw_shield] == SH_ELEMENTAL or p.player.powers[pw_shield] == SH_ELEMENTAL|SH_FIREFLOWER or p.player.powers[pw_shield] == 9 or p.player.powers[pw_shield] == 9|SH_FIREFLOWER then return false end //Elemental and Inferno shields protect against this.
damagePlayer(p.player,3,0)
elseif damager.type == MT_FLAME then
if p.player.powers[pw_shield] == SH_ELEMENTAL or p.player.powers[pw_shield] == SH_ELEMENTAL|SH_FIREFLOWER or p.player.powers[pw_shield] == 9 or p.player.powers[pw_shield] == 9|SH_FIREFLOWER then return false end //Elemental and Inferno shields protect against this.
damagePlayer(p.player,4,0)
elseif damager.type == MT_SPECIALSPIKEBALL then
damagePlayer(p.player,0,3)
else
print("Damager type not recognized by the damage script: "..damager.type)
damagePlayer(p.player,3,2)
end
//print(p.player.hp,p.player.temphp,damager.type)
P_DoPlayerPain(p.player, damager, source)
return false
end, MT_PLAYER)
//Damage blue crawla?
//Note that, for all damage functions, an extra 1 damage will be applied to the enemy unless they are outright immune.
addHook("ShouldDamage", function(e, damager, source, damage)
if damager == nil or damager.type == MT_NULL then
e.health = 0
return true //This should only happen in the event of something that should instantly kill.
elseif damager.type == MT_FLAMEJETFLAME then
e.health = e.health - P_RandomRange(3,5)
elseif damager.type == MT_SLUNGBULLET then //Average bullets per Blue Crawla: 2
e.health = e.health - P_RandomRange(0,1)
//S_StartSound(e,99)
elseif damager.type == MT_PLAYER then //Either an invincible player or armageddon shield blast. Either way, this should insta-kill most normal enemies.
e.health = 0
else
print("Damager type not recognized by the damage script: "..damager.type)
e.health = 0
return true
end
return true
end, MT_BLUECRAWLA)
//Damage FaceStabber?
addHook("ShouldDamage", function(e, damager, source, damage)
if damager == nil or damager.type == MT_NULL then
e.health = 0
return true //This should only happen in the event of something that should instantly kill.
elseif damager.type == MT_FLAMEJETFLAME then
e.health = e.health - P_RandomRange(3,5)
elseif damager.type == MT_SLUNGBULLET then
S_StartSound(e,286)
return false
elseif damager.type == MT_PLAYER then //Either an invincible player or armageddon shield blast. Either way, this should insta-kill most normal enemies.
e.health = 0
else
print("Damager type not recognized by the damage script: "..damager.type)
e.health = 0
return true
end
return true
end, MT_FACESTABBER)
//Here begins the shop initialization scripts.
local function singleShopInit(shopNum,shopItemPool,shopItemNum,itemPoolSize)
shopSeeds[shopNum] = 0
shopSeedsBuyback[shopNum] = 0
//Clear shop inventory and insert new inventory.
local i = 1
while i <= 32 do
if i <= shopItemNum then
local itemNum = shopItemPool[P_RandomRange(1,itemPoolSize)]
if itemNum == 22 and shopSeeds[shopNum] > 0 then
shopItemNum = shopItemNum - 1
i = i - 1
else shopInv[shopNum][i] = itemNum end
if itemNum == 22 then shopSeeds[shopNum] = shopSeeds[shopNum] + 1 end
else shopInv[shopNum][i] = 0 end
buyBack[i] = 0
i = i + 1
end
end
local function shopInit(p)
for i=1,32 do
buyBack[i] = 0
end
//Gilbert Plume
// 10 20
singleShopInit(1,{ 4, 5, 6, 7,22,24,25,26,28,31},P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8),10)
//Martin Milton
// 10 20
singleShopInit(2,{ 3, 3, 3, 4, 4, 5, 5,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,31},P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8),28)
//Glenn
// 10 20
singleShopInit(3,{ 3, 4, 5,23,24},P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8),5)
//Marshall Armond
// 10 20 30 40
singleShopInit(4,{22,23,24,24,24,24,24,25,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,30},P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8),44)
//Copper Wyre
//Though she has a low chance of selling other tools, she primarily deals in technology. Given how rare and valuable her stock is, she tends not to have much inventory at a time.
// 10 20 30 40 50 60
singleShopInit(5,{ 2, 6, 7, 7, 7, 7, 7,10,11,12,13,14,15,16,17,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30},P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8)+P_RandomRange(1,8),60)
end
//Here begins the scripting for maps. I would have put these in separate lumps, but that appears to be impractical.
//A shortcut for spawning objects.
local function intSpawn(x,y,z,obj)
P_SpawnMobj(x*FRACUNIT, y*FRACUNIT, z*FRACUNIT, obj)
end
//Initialize maps.
local function mapInit(map)
shopInit()
//Reset inventory, ring count, and max energy to starting point if the map load is a result of death or retry. Also, deplete energy supply.
for p in players.iterate() do
p.wet = 0
p.temp = 0
p.attackTimer = 0
p.fireTimer = 0
p.maxhp = p.maxhpseg*segLength()
p.hp = p.maxhp
p.temphp = p.maxhp
p.shopMode = 0 //0 means not in a shop, 1 means buying, 2 means selling, 3 means buying back.
if p.lastLev != nil then
if p.lastLev == gamemap then
for i=-3,32 do
p.slots[i] = p.lastInv[i]
p.energy = 0
p.maxEnergy = p.lastMaxEnergy
p.ringCount = p.oldRings
p.convertMode = p.oldConvertMode
p.bullets = p.oldBullets
p.seeds = p.oldSeeds
end
end
else
//Energy is used for various items.
p.energy = 0
p.maxEnergy = 0 //Max energy is in energy segments, not points like energy is.
p.convertMode = 0
p.ammo = 0
p.bullets = 0
p.seeds = 0
//Slots is the array of inventory items. -3 is the item in the Spin slot, -2 is Custom 2, and -1 is Custom 1. Positive numbers are the reserve inventory slots.
p.slots = {}
p.slots[-3] = 1 //Player starts with Interact in their Spin slot.
p.slots[-2] = 19 //DUMMY - For Sling Testing
p.slots[-1] = 0
for i=1, 32 do
p.slots[i] = 0
end
p.lastInv = {}
p.ringCount = 0
end
for i=-3,32 do
p.lastInv[i] = p.slots[i]
end
p.lastLev = gamemap
p.lastMaxEnergy = p.maxEnergy
p.oldRings = p.ringCount
p.oldConvertMode = p.convertMode
p.oldBullets = p.bullets
p.oldSeeds = p.seeds
//Refill health upon ANY map change.
//p.maxhp = 10 + (p.ringCount / 100 * 10)
p.hp = p.maxhp
p.temphp = p.maxhp
end
//Reset the arrays.
interactText = {}
interactDialogue = {}
interactFunc = {}
local r //Variable for random spawns.
local r2 //Variable for random spawns.
local r3 //Variable for random spawns.
//MAPC0
if map == 172 then
//Text
interactText[801] = "A cold test area, for snowy areas and the like. Getting to cold level 10 results in losing a life. Getting wet here is not advised."
interactText[802] = "A freezing test area, for truly unbearably cold areas. Getting wet in this area will result in a fairly quick death."
interactText[803] = "A hot test area, for deserts and the like. Getting to heat level 10 results in losing a life."
interactText[804] = "A scorching test area, for truly unbearably hot areas. Water on characters evaporates quickly here."
//Dialogue
interactDialogue[4] = {"Summon a different enemy?",
{"Cancel", nil}
}
interactDialogue[5] = {"This is a test of the dialogue and shop for Copper the Owl. Who, by the way, is female. Bit of trivia, there.",
{"Shop", function(p)
p.shopMode = 1
p.currentShop = 5
p.menuOpen = true
end},
{"1000 Rings", function(p)
p.ringCount = p.ringCount + 1000
end},
{"10000 Rings", function(p)
p.ringCount = p.ringCount + 10000
end},
{"End Conversation", nil}
}
interactDialogue[6] = {"This is a test of the dialogue and shop for Marshall Armond the arms dealer. Also, fun fact, his gun is called The Spectre.",
{"Shop", function(p)
p.shopMode = 1
p.currentShop = 4
p.menuOpen = true
end},
{"1000 Rings", function(p)
p.ringCount = p.ringCount + 1000
end},
{"10000 Rings", function(p)
p.ringCount = p.ringCount + 10000
end},
{"End Conversation", nil}
}
interactDialogue[7] = {"This is a test of the dialogue and shop for Martin Milton the farmer. He has the most states by far, among the shopkeepers.",
{"Shop", function(p)
p.shopMode = 1
p.currentShop = 2
p.menuOpen = true
end},
{"1000 Rings", function(p)
p.ringCount = p.ringCount + 1000
end},
{"10000 Rings", function(p)
p.ringCount = p.ringCount + 10000
end},
{"End Conversation", nil}
}
interactDialogue[8] = {"This is a test of the dialogue and shop for Plume the eagle.",
{"Shop", function(p)
p.shopMode = 1
p.currentShop = 1
p.menuOpen = true
end},
{"1000 Rings", function(p)
p.ringCount = p.ringCount + 1000
end},
{"10000 Rings", function(p)
p.ringCount = p.ringCount + 10000
end},
{"End Conversation", nil}
}
interactDialogue[9] = {"This is a test of the dialogue and shop for Glenn the poodle. Who is male, though it may not be immediately apparent.",
{"Shop", function(p)
p.shopMode = 1
p.currentShop = 3
p.menuOpen = true
end},
{"1000 Rings", function(p)
p.ringCount = p.ringCount + 1000
end},
{"10000 Rings", function(p)
p.ringCount = p.ringCount + 10000
end},
{"End Conversation", nil}
}
//MAPC1
elseif map == 173 then
//Text
interactText[1] = "The meter on the top right of the screen is your health."
//Dialogue
interactDialogue[3] = {"It's rough out there. Be careful, alright?",
{"End conversation"}
}
interactDialogue[5] = {"Get out there and show that big bully who's boss!",
{"End conversation"}
}
interactDialogue[6] = {"I don't think this is a good idea at all, but you seem set on it. All I can say is, good luck.",
{"End conversation"}
}
interactDialogue[7] = {"I feel sorry for the little guy. Living in the looming shadow of Robotnik's schemes is terrible for us all, but for your own mother to be taken away...",
{"End conversation"}
}
interactDialogue[8] = {"I miss mommy... *sniffle*",
{"End conversation"}
}
interactDialogue[9] = {"I'm sorry, but I don't want to talk right now.",
{"End conversation"}
}
interactDialogue[10] = {"I know, it's pretty dreary weather, but you never know how long it'll be before the badniks get you. I want to get as much time in the open air as I can before that time comes.",
{"End conversation"}
}
interactDialogue[11] = {"I don't think you'll be able to protect yourself with rings like Sonic and his friends can. But if you collect enough of them, maybe something good'll happen.\n\nThey look shiny, and something that shiny just has to be lucky, right?",
{"End conversation"}
}
interactDialogue[17] = {"I beg you to reconsider. Most of Robotnik's badniks can't squeeze in here, and this place appears to be a low-priority target. It'll probably be a while yet before we're in any real danger. We probably have at least a few weeks more left to live free. Please don't throw it all away like this.",
{"End conversation"}
}
//Random dialogue for forest NPCs.
r = P_RandomRange(1,5)
repeat r2 = P_RandomRange(1,5) until(r != r2)
repeat r3 = P_RandomRange(1,5) until(r != r3 and r2 != r3)
print(r,r2)
if r == 1 or r2 == 1 or r3 == 1 then
interactDialogue[3] = {"I really admire you for having the courage to even step out of our hiding place, let alone try to go after Robotnik. I could never do anything like that...\n\nI know it's not much, but here, take this first aid kit. You're bound to get hurt while you're out there and this will help keep you going.",
{"Thanks.", function(p)
giveItem(p.mo,9)
STagChange(3,4)
S_StartSound(nil,244,p)
end}
}
interactDialogue[4] = {"It's rough out there. Be careful, alright?",
{"End conversation"}
}
end
if r == 2 or r2 == 2 or r3 == 2 then
interactDialogue[5] = {"Wow... So you're really gonna go out there, huh? I wish I could help... Oh! I know! Here, take my thermos! You can fill it up near the lake!",
{"Thanks.", function(p)
giveItem(p.mo,2)
STagChange(5,13)
S_StartSound(nil,244,p)
end}
}
interactDialogue[13] = {"Get out there and show that big bully who's boss!",
{"End conversation"}
}
end
if r == 3 or r2 == 3 or r3 == 3 then
interactDialogue[11] = {"Hi! I've been thinking about this big journey you're going on, and I remembered those rings I gathered up on the way here. I think you could use them more than I can. Here you go!",
{"Thanks.", function(p)
STagChange(11,14)
p.mo.health = p.mo.health + 12
S_StartSound(nil,108,p)
end}
}
interactDialogue[14] = {"I don't think you'll be able to protect yourself with rings like Sonic and his friends can. But if you collect enough of them, maybe something good'll happen.\n\nThey look shiny, and something that shiny just has to be lucky, right?",
{"End conversation"}
}
end
if r == 4 or r2 == 4 or r3 == 4 then
interactDialogue[10] = {"I found these on the way here. They're very special seeds, called Fire Seeds. You can eat them and you'll breathe fire! But, well... having fire burst from your stomach isn't exactly the healthiest thing in the world, so only use them if you REALLY need to!",
{"Thanks.", function(p)
//print("Fire seeds have not yet been implemented. Here, have some rings, instead!")
grantSeeds(p,6)
p.mo.health = p.mo.health + 12
//S_StartSound(nil,108,p)
STagChange(10,15)
end}
}
interactDialogue[15] = {"I know, it's pretty dreary weather, but you never know how long it'll be before the badniks get you. I want to get as much time in the open air as I can before that time comes.",
{"End conversation"}
}
end
if r == 5 or r2 == 5 or r3 == 5 then
interactDialogue[9] = {"I brought some sling bullets here with me. They won't work against the more heavily-armored badniks, but they'll definitely work on Robotnik if you manage to corner him.\n\nTake them with you. And please, bring back my beloved.",
{"Thanks.", function(p)
p.bullets = p.bullets + 20
S_StartSound(nil,311,p)
STagChange(9,16)
end}
}
interactDialogue[16] = {"I'm sorry, but I don't want to talk right now.",
{"End conversation"}
}
end
//Random spawn for the lake secret's main chamber.
r = P_RandomRange(1,2)
if r == 1 then
intSpawn(-960, 1040, -40, MT_RING)
intSpawn(-960, 1072, -40, MT_RING)
intSpawn(-960, 1104, -40, MT_RING)
intSpawn(-832, 1040, -40, MT_RING)
intSpawn(-832, 1072, -40, MT_RING)
intSpawn(-832, 1104, -40, MT_RING)
intSpawn(-960, 1040, -40, MT_RING)
intSpawn(-960, 1072, -40, MT_RING)
intSpawn(-928, 1136, -40, MT_RING)
intSpawn(-896, 1136, -40, MT_RING)
intSpawn(-864, 1136, -40, MT_RING)
intSpawn(-928, 1008, -40, MT_RING)
intSpawn(-896, 1008, -40, MT_RING)
intSpawn(-864, 1008, -40, MT_RING)
elseif r == 2 then
for i = 1,25 do
local x = P_RandomRange(-1088,-624)
local y = P_RandomRange(864,1312)
if R_PointInSubsector(x*FRACUNIT, y*FRACUNIT).sector.floorheight == -80*FRACUNIT then intSpawn(x, y, -80, MT_BULLET) end // else
//print(x..","..y.." ("..i..")".." ["..R_PointInSubsector(x, y).sector.floorheight/FRACUNIT.."]") end
end
end
//Random spawn for the lake secret's side chamber.
r = P_RandomRange(1,2)
if r == 1 then
intSpawn(-736, 1280, -88, MT_RING)
intSpawn(-704, 1280, -88, MT_RING)
intSpawn(-736, 1248, -88, MT_RING)
intSpawn(-704, 1248, -88, MT_RING)
intSpawn(-672, 1248, -88, MT_RING)
intSpawn(-704, 1216, -88, MT_RING)
intSpawn(-672, 1216, -88, MT_RING)
intSpawn(-736, 1280, -64, MT_RING)
intSpawn(-704, 1280, -64, MT_RING)
intSpawn(-736, 1248, -64, MT_RING)
intSpawn(-704, 1248, -64, MT_RING)
intSpawn(-672, 1248, -64, MT_RING)
intSpawn(-704, 1216, -64, MT_RING)
intSpawn(-672, 1216, -64, MT_RING)
intSpawn(-736, 1280, -40, MT_RING)
intSpawn(-704, 1280, -40, MT_RING)
intSpawn(-736, 1248, -40, MT_RING)
intSpawn(-704, 1248, -40, MT_RING)
intSpawn(-672, 1248, -40, MT_RING)
intSpawn(-704, 1216, -40, MT_RING)
intSpawn(-672, 1216, -40, MT_RING)
elseif r == 2
intSpawn(-704, 1248, -88, 417)
end
else
print(map)
end
end
addHook("MapLoad", mapInit)
hud.add(drawStatus,"game")
hud.add(drawInventory,"game")
hud.add(drawDialogue,"game")