-- title: einhander
-- author: pixelbath
-- desc: one-handed
-- script: lua
-- https://gamefaqs.gamespot.com/ps/197217-einhander/faqs/61724
-- TODO:
-- configurable controls
-- enemy collisions
seed=12345
player={
x=20, y=50,
vx=0, vy=0,
speed=1.5,
inv=0,
show=1,
fire=0,
gun_down=0,
gun_anim=0,
gun_held=0,
is_firing=0,
big_gun_cooldown=0,
}
gunsprites={
292, -- vulcan
294, -- cannon
296, -- spreader
298, -- wasp
300, -- grenade
302, -- hedgehog
}
gunnames={
{324, "V", "ULCAN"},
{325, "C", "ANNON"},
{326, "S", "PREADER"},
{327, "W", "ASP"},
{328, "G", "RENADE"},
{329, "H", "EDGEHOG"},
}
vulcanMuzzleSprites={
264, 266, 280
}
shellParticles={}
-- particles are just white pixels by default
for i=1,15 do
table.insert(shellParticles, {x=0,y=0,vx=0,vy=0,age=0,dieAt=0,color=12,idx=0,isPixel=true})
end
particlePointer=1 -- points at the next particle to be spawned
weaponBgSize=16--12
p_bullets = {}
p_big_bullets = {}
multiplier = 12
multiplier_sub = 0
multiplier_tick = 0
score = 25000
active_enemies={}
t=0
math.randomseed(seed)
-- create a mapping for darkening palette colors
-- note: this is for the default sweetie16 palette
drk={[0]=0,0,1,2,3,6,7,15,0,8,9,10,13,14,15,0}
-- entity interface:
-- props: x, y, vx, vy, speed, t
-- methods: update, draw, setPosition
function createEntity(x, y, speed, t)
local tm = t or 0
local spd = speed or 0
return { x=x, y=y, vx=0, vy=0, speed=spd, t=tm }
end
local gunCounter = createEntity(50, 60)
gunCounter.value = 3000
gunCounter.p_val = 3000
gunCounter.t_def = 5
gunCounter.max_val = 3000
gunCounter.swaps = {}
function gunCounter:setValue(val)
self.value = val
self.p_val = val
self.max_val = val
end
function gunCounter:setPosition(x, y)
self.x = x
self.y = y
end
function gunCounter:dec()
if self.value > 0 then
self.p_val = self.value
self.value = self.value - 1 -- decrement
self.t = self.t_def -- reset timer
-- grab string differences and store indices in .swaps
local prev = string.format("%04d", self.p_val)
local cur = string.format("%04d", self.value)
self.swaps = {}
for i=1,4 do
if prev:sub(i,i) ~= cur:sub(i,i) then
self.swaps[i] = true
end
end
end
end
function gunCounter:update()
end
function gunCounter:draw()
self.t = self.t - 1
if self.t == 0 then
self.swaps = {}
self.p_val = self.value
end
local prev = string.format("%04d", self.p_val)
local cur = string.format("%04d", self.value)
local color = 12
if self.value / self.max_val < 0.1 then color = 4 end
-- clip this area
clip(self.x - 1, self.y - 2, 18, 10)
for i = 1,4 do
local numY = self.y
if self.t > 0 and self.swaps[i] then
numY = self.y - (self.t_def - self.t)
print(prev:sub(i, i), self.x + ((i-1)*4), numY + 6, color, true, 1, true)
end
print(cur:sub(i, i), self.x + ((i-1)*4), numY, color, true, 1, true)
end
clip()
end
function drawParallelogram(x, y, w, h, skewpx, color)
tri(x+skewpx, y, x+w, y+h, x, y+h, color)
tri(x+skewpx, y, x+w, y+h, x+w+skewpx, y, color)
end
function spawnPixelParticle(x, y, vx, vy, age, dieAt, color)
local p = shellParticles[particlePointer]
p.x = x
p.y = y
p.vx = vx
p.vy = vy
p.age = age
p.dieAt = dieAt
p.color = color
p.isPixel = true
particlePointer = particlePointer + 1
if particlePointer > 15 then particlePointer = 1 end
end
function spawnSpriteParticle(x, y, vx, vy, age, dieAt, index)
local p = shellParticles[particlePointer]
p.x = x
p.y = y
p.vx = vx
p.vy = vy
p.age = age
p.dieAt = dieAt
p.idx = index
p.isPixel = false
particlePointer = particlePointer + 1
if particlePointer > 15 then particlePointer = 1 end
end
function spawnEnemySprite(x, y, age, type)
local e = {}
e.hp = 0
e.hp_shield = 0
e.x = x
e.y = y
e.age = age
e.type = type
if type == 1 then
e.hp = 100
e.hp_shield = 50
end
table.insert(active_enemies, e)
end
spawnEnemySprite(150, 50, 0, 1)
function updateEnemies()
for i=1,#active_enemies do
local e = active_enemies[i]
-- car - medium
if e.type == 1 then
end
e.age = e.age + 1
end
end
function drawEnemies()
for i=1,#active_enemies do
local e = active_enemies[i]
-- car - medium
if e.type == 1 then
-- shield
spr(352, e.x, e.y, 0, 1, 0, 0, 2, 1)
-- body
spr(354, e.x + 6, e.y, 0, 1, 0, 0, 3, 1)
end
end
end
function drawPlayer()
if (player.show) then
-- arm
if player.gun_anim > 0 then
--spr(260, player.x+12, player.y+11, 0, 1, 0, 3, 2, 1)
else
if player.gun_down then
spr(260, player.x+12, player.y+11, 0, 1, 0, 0, 2, 1)
else
spr(260, player.x+12, player.y-2, 0, 1, 2, 0, 2, 1)
end
end
-- ship
spr(256, player.x, player.y, 0, 1, 0, 0, 4, 2)
end
-- draw regular bullets
for key,val in ipairs(p_bullets) do
spr(262, val.x, val.y, 0)
end
-- draw big bullets
for key,val in ipairs(p_big_bullets) do
local spritenum = 0
if val.type == 2 then spritenum = 282 end
if val.type == 3 then spritenum = 267 end
spr(spritenum, val.x, val.y, 0)
end
-- custom routines depending on gun held
if player.gun_held > 0 then
local gunsprite = gunsprites[player.gun_held]
local gunpos = player.y - 2
if player.gun_down then
gunsprite = gunsprite + 16
gunpos = gunpos + 14
end
if player.gun_anim > 0 then
spr(gunsprite, player.x+3, gunpos, 0, 1, 0, 3, 2, 1)
else
spr(gunsprite, player.x+12, gunpos, 0, 1, 0, 0, 2, 1)
end
-- vulcan
if player.gun_held == 1 then
local flipfire = 0
local muzzleIdx = math.random(0,3)
if math.random(1, 2) == 2 then
flipfire = 2
end
if player.fire == 0 then
player.fire = 2
else
player.fire = player.fire - 1
end
-- not tracking individual bullets with vulcan
-- begin dakka
if player.is_firing > 0 then
if muzzleIdx > 0 then
if player.gun_down then
spr(vulcanMuzzleSprites[muzzleIdx], player.x + 27, player.y + 14, 0, 1, flipfire, 0, 2, 1)
else
spr(vulcanMuzzleSprites[muzzleIdx], player.x + 28, player.y - 2, 0, 1, flipfire, 0, 2, 1)
end
end
if t % 10 == 0 then
local randFactor = math.random(10,100)
local spawnOffsetY = 1
local lineStart = math.random(player.x//1+20, 260)
if player.gun_down then
spawnOffsetY = 18
-- vulcan tilts downward so we get to do trig! yay!
lineStart = lineStart - player.x
local angle = 8
local radius2 = lineStart+38
local c = math.cos(angle * math.pi / 180)
local s = math.sin(angle * math.pi / 180)
line(c * lineStart + player.x + 18, s * lineStart + player.y + 17, c * radius2 + player.x + 18, s * radius2 + player.y + 17, 14)
else
line(lineStart, player.y+1, lineStart+40, player.y+1, 14)
end
-- shells
spawnPixelParticle(player.x+14, player.y+spawnOffsetY, -0.2, -0.6 + (randFactor / 100), 0, 50, 15)
end
end
-- update shells with light gravity
for i=1,#shellParticles do
local p = shellParticles[i]
p.vy = p.vy + 0.1
end
end
-- cannon
if player.gun_held == 2 then
if player.fire == 0 then
player.fire = 30
else
player.fire = player.fire - 1
end
-- todo: move to update()
for i=1, #p_big_bullets do
if p_big_bullets[i].x < 250 then
p_big_bullets[i].x = p_big_bullets[i].x + 10
end
end
end
end
for i=1,#shellParticles do
local p = shellParticles[i]
if p.age < p.dieAt then
if p.isPixel then
pix(p.x, p.y, p.color)
else
spr(p.idx, p.x, p.y)
end
end
end
if player.gun_anim > 0 then player.gun_anim = player.gun_anim - 1 end
end
function drawUI()
-- weapon bg
-- spr(322, 4, 114, 0, 1, 0, 0, 1, 2)
-- rect(12, 114, 8, 16, 6)
-- spr(323, 20, 114, 0, 1, 0, 0, 1, 2)
drawParallelogram(6, 115, weaponBgSize, 12, 6, 6)
-- selected weapon
if player.gun_held > 0 then
local lettersprite = gunnames[player.gun_held][1]
local guntext = gunnames[player.gun_held][3]
spr(lettersprite, 14, 118, 0, 1)
print(guntext, 24, 122, 12, true, 1, true)
print(gunCounter.value, 12, 129, 12, true, 1, true)
end
gunCounter:draw()
-- multiplier
-- TODO: need to do multipliers over 10, so the X needs to move left when that happens
spr(340, 164, 114, 0)
--spr(340 + multiplier, 172, 113, 0)
spr(330, 180, 114, 0, 1, 0, 0, 4, 1)
spr(331, 212, 114, 0, 1, 0, 0, 3, 1)
spr(334, 236, 114, 0, 1)
if multiplier_sub > 0 then
for i = 1, multiplier_sub do
spr(335, 177 + (i * 6), 114, 0)
end
end
-- score
local scorestring = tostring(score)
for i = 1, #scorestring do
local char = scorestring:sub(i, i)
local numsprite = 340 + tonumber(char)
-- this is a hack because I ordered the numbers weirdly
if char == "0" then numsprite = 350 end
spr(numsprite, (230 - (8 * #scorestring)) + (i * 8), 123, 0)
end
end
function drawWater(waterHeight)
tm=time()/200
for x=0,239 do
for y=waterHeight,135 do
-- introduce some wobble
ox=x+2*math.sin(tm+y)
oy=waterHeight-(y-waterHeight)-1
oc=pix(ox,oy)
-- darken reflected pixel
pix(x,y,drk[oc])
end
end
end
function drawDebug()
-- print("p.gdown: " .. tostring(player.gun_down), 5, 5, 12, true, 1, true)
-- print("spr: " .. tostring(vulcanMuzzleSprites[player.fire + 1]), 5, 13, 12, true, 1, true)
end
function update()
player.vx = 0
player.vy = 0
if btn(0) then
player.vy=-player.speed
end
if btn(1) then
player.vy=player.speed
end
if btn(2) then
player.vx=-player.speed
end
if btn(3) then
player.vx=player.speed
end
-- slow down diagonal movements
if player.vx * player.vy ~= 0 then
player.vx = player.vx * 0.707
player.vy = player.vy * 0.707
end
player.x = player.x + player.vx
player.y = player.y + player.vy
gunCounter:setPosition(player.x + 8, player.y + 22)
if player.x < 5 then player.x = 5 end
if player.x > 220 then player.x = 220 end
if player.y < 5 then player.y = 5 end
if player.y > 128 then player.y = 128 end
-- A: fire main gun
if btn(4) then
if player.fire == 0 then
player.fire = 5
table.insert(p_bullets, {x=player.x+10, y=player.y+2})
else
player.fire = player.fire - 1
end
end
-- B: fire accessory gun
if btn(5) then
if player.gun_held == 1 then
player.is_firing = 1
gunCounter:dec()
end
if player.gun_held == 2 then
-- controlled by cooldown (or semi-auto)
if player.is_firing == 1 then player.is_firing = 0 end
if player.big_gun_cooldown == 0 then
player.big_gun_cooldown = 30
player.is_firing = 1
gunCounter:dec()
else
player.big_gun_cooldown = player.big_gun_cooldown - 1
player.is_firing = 0
end
if player.is_firing == 1 then
table.insert(p_big_bullets, {x=player.x+12, y=player.y+6, type=player.gun_held})
end
end
else
player.big_gun_cooldown = 0
player.is_firing = 0
end
-- X: switch arm
if btnp(6) then
player.gun_down = not player.gun_down
player.gun_anim = 1
end
-- Y: swap weapon (debug)
if btnp(7) then
player.gun_held = player.gun_held + 1
if player.gun_held > 6 then player.gun_held = 0 end
end
for key,val in ipairs(p_bullets) do
p_bullets[key].x = p_bullets[key].x + 8
if val.x > 240 then
table.remove(p_bullets, key)
end
end
for key,val in ipairs(p_big_bullets) do
p_big_bullets[key].x = p_big_bullets[key].x + 8
if val.x > 240 then
table.remove(p_big_bullets, key)
end
end
for i=1,#shellParticles do
local p = shellParticles[i]
p.x = p.x + p.vx
p.y = p.y + p.vy
p.age = p.age + 1
end
t=t+1
end
function TIC()
update()
cls(0)
drawPlayer()
drawEnemies()
drawWater(120)
drawUI()
drawDebug()
end
--
-- 000:00ffffff00ffffff00ffffff00ffffff00ffffff00ffffff00ffffff00ffffff
-- 001:ffffffeeffffffeeffffffeeffffffeeffffffeeffffffeeffffffeeffffffee
-- 004:eee00000e0e00000efe00000f0f0fff0f0f0000000f000000000000000000000
-- 005:eee00000e0e00000efe00000f0f0fff0f0f0f0f000f000000000000000000000
-- 006:eee00000e0e00000efe00000f0f0fff0f0f0000000f000000000000000000000
-- 007:eee00000e0e00000efe00000f0f0fff0f0f0f0f000f000000000000000000000
-- 016:ffffffffffffffffffffffffffbbbffbffbbbffbffffffffffffffffffffffff
-- 017:ffffffffffffffffffffffffbbffffffbbffffffffffffffffffffffffffffff
-- 020:0000000000000000000000000099008800aa0088009a00880088000000000000
-- 021:000000000000000000000000009a00aa00aa00aa009900990088008800000000
-- 022:0000000000000000000000000099009900aa00a900aa00aa0088008800000000
-- 023:000000000000000000000000009a009a00aa009a009900990088008800000000
-- 033:ffffffffffffffffffffffffbbf888ffbbf888fbffffffffffffffffffffffff
--
--
-- 000:04488a844488aaaa4889999a00000d99000000d90000000d0000000000000000
-- 001:48000000889aa000aa889aa09aaa899a99aaa884999aa8430d99a43f00d999ff
-- 002:000000000000000000000000a00000004400000038ddc000f8998000ff88dbc0
-- 004:0d000000d9add0009999addd8122999900128889000890080000000000000000
-- 005:000000000000000000000000d000000080000000800000000000000000000000
-- 006:0000000000000000000000000000440044444cc0000044000000000000000000
-- 007:000000000000000000000000eddc0000fee00000000000000000000000000000
-- 008:00000000000000000cc40000ccccccccccccccc404cc00000000000000000000
-- 009:000000000000000000000000ccc4400000000000000000000000000000000000
-- 010:0000000000000000033000003333300033330000033000000000000000000000
-- 017:0009aaaa00088999000eee080000000000000000000000000000000000000000
-- 018:ffeeeddcaf0fffed880e65fe0000eed000000000000000000000000000000000
-- 019:00000000b0000000d00000000000000000000000000000000000000000000000
-- 020:0d000000d9add0009999addd8122999900128888000890000000000000000000
-- 021:0000000000000000000000009d00000098000000880000000000000000000000
-- 022:0300000034300000030000000000000000000000000000000000000000000000
-- 023:000000000000000000000000eddd0000feed0000000000000000000000000000
-- 024:0000000000000000033300004444333344444433033300000000000000000000
-- 025:0000000000000000000000000000000033000000000000000000000000000000
-- 026:0000000000000000000000000004444000444444000444400000000000000000
-- 032:0000000011101300211023003210340043104c0056705c006770650077707500
-- 036:00000000000000000edce000f999edddfffffeee000099dd0000fff000000000
-- 037:0000000000000000000000d0dddddd9deeeeeefe000000f00000000000000000
-- 038:00000000000000000000dddd0dd0ffe3defddeeed2eeeff9ff00feed00000ff0
-- 039:0000dde00ddee900ee9000003000000000000000d00000000000000000000000
-- 040:000000000000000001eeef00d1e77770d3766667c4d66666000000ee00000000
-- 041:00000000000000000000000077700750f565065077776e500077755000000000
-- 042:0000000000000000000c555c000b646b000b666b0009777900000ff000000000
-- 043:0000000000000000540000006300000063000000720000000000000000000000
-- 044:00000000000000000000edd600006ed600dd7ff600ee777700000fe000000000
-- 046:00000000000dd0dd000ee0ee000ee0ee000444440003fff30003333300000000
-- 048:88808a0098809a00a980ab00ba80bc00cdf0cc00def0dc00ef80ed00f880fd00
-- 052:000000000fedc0000f999e0000fffddd00009eee0000f9de0000fff000000000
-- 053:000000000000000000000000d0000000dddd000deeeeddd9000eeefd000000f0
-- 054:00000000000000000ddd099dd2e9dffefeeeeee90000fff00000feed00000000
-- 055:0000000000000000ddddd000eeeeeeee34000000000000000000000000000000
-- 056:00000000000000005577700e5e677776560565f7570077700000000000000000
-- 057:0000000000000000e00000006666d4c0666673d07777e1d00feee10000000000
-- 058:000000000000000000000ff0000c555c000b666b000b636b0009777900000000
-- 059:0000000000000000000000005400000062000000620000007200000000000000
-- 060:0000000000000000000dd000006665dd006ed6ee007ee600007eff0000000000
-- 062:000000000000000000444440004fff400033333000fe0fe000ed0ed000ee0ee0
-- 068:0c00000c0c00000c0cc000cc00c000c000cc0cc0000c0c00000ccc000000c000
-- 069:00ccccc00cc000c00c0000000c0000000c0000000c0000000cc000c000ccccc0
-- 070:00ccccc00cc000000c0000000ccccc0000000cc0000000c000000cc00ccccc00
-- 071:0c00000c0c00000c0c00c00c0c00c00c0c00c00c0c00c00c0cc0c0cc00ccccc0
-- 072:00ccccc00cc000c00c0000000c0000000c00ccc00c0000c00cc000c000ccccc0
-- 073:0c0000c00c0000c00c0000c00cccccc00c0000c00c0000c00c0000c00c0000c0
-- 074:00dddddd000dd0000000dd00000dd00000dd00000dd0000d0ddddddd00000000
-- 075:dddddddd0dd0000d00dd00000dd0000ddd0000ddd0000dd0dddddddd00000000
-- 076:ddddddddd0000dd0dd0000ddd0000dd00000dd00000dd000dddddddd00000000
-- 077:dddddddd000dd0000000dd00000dd00000dd00000dd0000ddddddddd00000000
-- 078:dd0000000dd0000000d000000dd00000dd000000d00000000000000000000000
-- 079:0000000000333300000333300033330003333000333300000000000000000000
-- 080:0000b000000aabb000a999aa0090ffe90000fffe00000ff00000000000000000
-- 081:000000000000000000000000ab000000e9ab0000fee9ab0000fee9a00000fee0
-- 084:000000000c000c0000c0c000000c000000c0c0000c000c000000000000000000
-- 085:00000000000ccc000000cc000000cc000000cc000000cc000000cc000000cc00
-- 086:00000000000ccc0000c00cc000000cc00000cc00000cc00000cc000000ccccc0
-- 087:00000000000ccc0000c00cc000000cc00000cc0000000cc000c00cc0000ccc00
-- 088:00000000000ccc0000cccc0000c0cc000cc0cc000c00cc000cccccc00000cc00
-- 089:0000000000ccccc000cc000000cccc0000000cc000000cc000c00cc0000ccc00
-- 090:00000000000ccc0000cc00c000cc000000cccc0000cc0cc000cc0cc0000ccc00
-- 091:0000000000ccccc000000cc000000cc00000cc000000cc00000cc000000cc000
-- 092:00000000000ccc0000cc0cc000cc0cc0000ccc0000cc0cc000cc0cc0000ccc00
-- 093:00000000000ccc0000cc0cc000cc0cc0000cccc000000cc000c00cc0000ccc00
-- 094:00000000000ccc0000cc0cc000cc0cc000cc0cc000cc0cc000cc0cc0000ccc00
-- 096:0000000000000000000000aa0000ae9900ae9998ae99998e944cc8889944cc88
-- 097:00000000aadddddaae999eaa9999dddd8889ffeeee9999f08888880088888000
-- 098:000000000000000000000000000aaa9900a9eded0a9ededdd888888800000000
-- 099:00000000000000cc00000bdbddd0bddeeeeddddfee9999998811222200000222
-- 100:0000000000000000000ddb00fffee000bfcfa000999999003333334022222224
--
--
--
-- 000:00000000ffffffff00000000ffffffff
-- 001:012345678acdeeffffeedca876543210
-- 002:0123456789abcdef0123456789abcdef
-- 003:ff000000000000000ff0000000000000
-- 004:01234000000000000000000000000000
--
--
-- 000:01000101010201020101010f010e010e010f0101010201020101010f010e010e010f0101010201020101010f010e010e010f0101010201020101010f6020000000f9
-- 001:9400940094009400940094009400a400a400a400a400b400b400b400b400b400b400b400c400c400c400c400d400d400d400e400e400f400f400f400212000000000
-- 002:030f030e130d330c530b730a9309b309d308f308f308f308f308f308f308f308f308f308f308f308f308f308f308f308f308f308f308f308f308f308105000000000
-- 003:85009500a500b500c500d500e500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500502000000000
-- 004:6500750085009500a500b500c500d500e500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500f500572000000000
--
--
-- 000:40002200000000003000000050003a00000000003000000040002200000000003000000050003a000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
--
--
-- 000:1800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e0000
--
--
-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
--