From 3a5771d96cd86135e4bd4ea1596d9b93a5261ff3 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sat, 25 Apr 2020 12:36:01 +0100 Subject: [PATCH] Part 16 --- Source/Engine/Color.swift | 7 +-- Source/Engine/Texture.swift | 4 ++ Source/Engine/Weapon.swift | 7 ++- .../crosshair.imageset/Contents.json | 21 +++++++ .../crosshair.imageset/crosshair.png | Bin 0 -> 1077 bytes .../font.imageset/Contents.json | 21 +++++++ .../Assets.xcassets/font.imageset/font.png | Bin 0 -> 1043 bytes .../healthIcon.imageset/Contents.json | 21 +++++++ .../healthIcon.imageset/healthIcon.png | Bin 0 -> 144 bytes .../pistolIcon.imageset/Contents.json | 21 +++++++ .../pistolIcon.imageset/pistolIcon.png | Bin 0 -> 997 bytes .../shotgunIcon.imageset/Contents.json | 21 +++++++ .../shotgunIcon.imageset/shotgunIcon.png | Bin 0 -> 165 bytes Source/Rampage/Info.plist | 3 +- Source/Rampage/ViewController.swift | 9 +++ Source/Renderer/Bitmap.swift | 45 +++++++++++--- Source/Renderer/Renderer.swift | 57 ++++++++++++++++++ 17 files changed, 222 insertions(+), 15 deletions(-) create mode 100644 Source/Rampage/Assets.xcassets/crosshair.imageset/Contents.json create mode 100644 Source/Rampage/Assets.xcassets/crosshair.imageset/crosshair.png create mode 100644 Source/Rampage/Assets.xcassets/font.imageset/Contents.json create mode 100644 Source/Rampage/Assets.xcassets/font.imageset/font.png create mode 100644 Source/Rampage/Assets.xcassets/healthIcon.imageset/Contents.json create mode 100644 Source/Rampage/Assets.xcassets/healthIcon.imageset/healthIcon.png create mode 100644 Source/Rampage/Assets.xcassets/pistolIcon.imageset/Contents.json create mode 100644 Source/Rampage/Assets.xcassets/pistolIcon.imageset/pistolIcon.png create mode 100644 Source/Rampage/Assets.xcassets/shotgunIcon.imageset/Contents.json create mode 100644 Source/Rampage/Assets.xcassets/shotgunIcon.imageset/shotgunIcon.png diff --git a/Source/Engine/Color.swift b/Source/Engine/Color.swift index b0b590f..6081eee 100644 --- a/Source/Engine/Color.swift +++ b/Source/Engine/Color.swift @@ -25,8 +25,7 @@ public extension Color { static let clear = Color(r: 0, g: 0, b: 0, a: 0) static let black = Color(r: 0, g: 0, b: 0) static let white = Color(r: 255, g: 255, b: 255) - static let gray = Color(r: 192, g: 192, b: 192) - static let red = Color(r: 255, g: 0, b: 0) - static let green = Color(r: 0, g: 255, b: 0) - static let blue = Color(r: 0, g: 0, b: 255) + static let red = Color(r: 217, g: 87, b: 99) + static let green = Color(r: 153, g: 229, b: 80) + static let yellow = Color(r: 251, g: 242, b: 54) } diff --git a/Source/Engine/Texture.swift b/Source/Engine/Texture.swift index ccb1022..f94dbb8 100644 --- a/Source/Engine/Texture.swift +++ b/Source/Engine/Texture.swift @@ -28,4 +28,8 @@ public enum Texture: String, CaseIterable { case switch1, switch2, switch3, switch4 case elevatorFloor, elevatorCeiling, elevatorSideWall, elevatorBackWall case medkit + case crosshair + case healthIcon + case pistolIcon, shotgunIcon + case font } diff --git a/Source/Engine/Weapon.swift b/Source/Engine/Weapon.swift index a778a50..878e7fe 100644 --- a/Source/Engine/Weapon.swift +++ b/Source/Engine/Weapon.swift @@ -21,6 +21,7 @@ public extension Weapon { let projectiles: Int let spread: Double let defaultAmmo: Double + public let hudIcon: Texture } var attributes: Attributes { @@ -34,7 +35,8 @@ public extension Weapon { cooldown: 0.25, projectiles: 1, spread: 0, - defaultAmmo: .infinity + defaultAmmo: .infinity, + hudIcon: .pistolIcon ) case .shotgun: return Attributes( @@ -45,7 +47,8 @@ public extension Weapon { cooldown: 0.5, projectiles: 5, spread: 0.4, - defaultAmmo: 5 + defaultAmmo: 10, + hudIcon: .shotgunIcon ) } } diff --git a/Source/Rampage/Assets.xcassets/crosshair.imageset/Contents.json b/Source/Rampage/Assets.xcassets/crosshair.imageset/Contents.json new file mode 100644 index 0000000..66e65dc --- /dev/null +++ b/Source/Rampage/Assets.xcassets/crosshair.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "crosshair.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Source/Rampage/Assets.xcassets/crosshair.imageset/crosshair.png b/Source/Rampage/Assets.xcassets/crosshair.imageset/crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..a3e9ce77c81d8eff4b96621707b79459bbd16b5a GIT binary patch literal 1077 zcmbVLJ8aWH7&a=cXd!fBpaWdpg;)HFmyVq!z=pfd1~0Krm~a~_4EDiW6MyZi6|zW;rFdg^p+Xk>_@ zs90fAE0J}Od_#K%$iLQ)pOfVv&R6g(s^O;LLrS$!6@r3e%)=5itk&WhOjA@}-7Z&f zrFd2`k;52~4ih>aK~q$EBJ>P%0b)>v^R}CzzdT!|fo)~z^T{Gt^m4FnPcHj#c6q97 zE-#p}MNgar=};jA4#WlsorW7IVTNw=Dr6stSsJue@Ir>}I8`Z5gB=w1M_wt;i%L>bgmKV)XkyK`YD!7dySB(FL)S6(6qao^n@lsoAb+0a zWm%3m1R+i|;z7&BMi_U4u^xj41Jk!XY$F#$j7AkL;S5bY?S$ZX#o`vR8*~#zQpSdc z$MOuvI!+WDfX}hH^7E#!k%55*zIByJ7Sfb4-Jg`GD3}B2Ta!yMnN5UAU7+4 zBSpis-AHgWDo`h04f^&aXz4z3Ks&>Vy^RO|j=Y1l{wHfJafXfBvqIkH!_IL3^5K-_V;!D?d9y&vk_uj|%`+dLf&zl|^ zJl@{AzZJu<_SArwMr$|vnp>LD|6z9h2wFN}A`6GjF<6pqfW=ia2k?|G=Rq3C>eR#v zh+$aMAuW@IS?Ls~n0i3=d;+d+AvA`?`dmv^iU8s{kk<^J_%^>t;F`)4BVmb>ECCd> zfwB#T%YzxETvVbe(RU1wxf~+U0hDo9A2%G%<%tR}hxVSCB=Cw0Eb_#bsH`-E3#JY5 zaDeeE6iwqS8=ymBmZf{}AVmjB3cakK4sl_Y3r6tjL!fB3I>x2NWHlB#@k9YaizCTW zsT3$N0n^TtbTk_EID$bx^6)!T29#aD;dIp)MBpg4WWnUjzwkkYLbbgNEl zIMp(tf|0Ilk#vCaQmQ#h(*Ip`z3%P6G}zYLkl4vgS%6Ff$DFhk)VQ%OFOh%$2~DS9TDUOG;@y(0XohDvTtP?@mogkE8wyB?Jb^d@nx=A0vX_bUhT@S( zC`!|bcrQzbgoqfVk_mxf;&crwn#!aO3|Pae+t~gcvEDG~7K$taTe|?%q;2YWC23A; zpd}m+g_9IRBR;0FmMyH>KuZ-H+!0G6F{Ibpe_CBLq51K)^}$7l`sf1#&AW{TyY2hR zIJy*9Qer&guH8;gw_l=KdTuV~cYVBw<*948Mt%Eoo1LWgYU#}N;-)&Yd--IPK9T+v zo8|e}cfbC4diME?mkNKLcr^Ru&&*X}^}+g`8=sYXM}L2@7w6urzW3n{HqkNh?A_^& z14VmbypR34XYJF$a&F$gB&ZuBM$_H}r}gbQUmKQr{9&%cm!rLFno10c_xndL{{^m4 BQ`7(e literal 0 HcmV?d00001 diff --git a/Source/Rampage/Assets.xcassets/healthIcon.imageset/Contents.json b/Source/Rampage/Assets.xcassets/healthIcon.imageset/Contents.json new file mode 100644 index 0000000..7416da1 --- /dev/null +++ b/Source/Rampage/Assets.xcassets/healthIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "healthIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Source/Rampage/Assets.xcassets/healthIcon.imageset/healthIcon.png b/Source/Rampage/Assets.xcassets/healthIcon.imageset/healthIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..6590aada8fba6b2669d152fc05a64ada515dd8cd GIT binary patch literal 144 zcmeAS@N?(olHy`uVBq!ia0vp^Y#_`5A|IT2?*XJ3i-X*q7}lMWc?slrdAc};aLkPD z*~oi9L4f&vq?+w!mn4Q~NeoNQICX_|B>H?6d~nJx+HKN23Dyh2|Mq^Ja&+NpF>dve q*8DxvA95J}aTjb)yIrKqsJ4_@a?h{8b<=>RGI+ZBxvXlKi{pw___YR z!9I$j`txIIk*vMs>*?(w|K0BG9kLw7*)pC$)3~mCkV>1V3PIl1XJ8TP=HjI-n4+kz z6IQ8=%i4L#KsKX?7$&e?!ltOyXyEF`EX1G+XDmmizpp>1fo00{WJ2RKSAjKaY{`QY zOXDSDY1R-;dh{$v1riakA=W`)&pEyn$aF(jBKy$H(x3ssvogIGR9Pzk1$hu8m{`Q% zcpeA>!^abXz@Gt8j*qe&d4&icmlA>$O@ih_6K|e5Efv*V(-%3(bPZ!yV%d7V&eUTJ z@@80G6va>@8jTP{#9wr<9z+~}sHLDn-|#FKTgU;SqFzPwSf)v&dnwp%Th{TLWg-P* z1KnkLh6___0X6OaP}^>!eO!e5@%~fTFD<%|EkYm7dj@IT^ib%^l@t%^75Wq=IH!LSKoN6#MP0Bky)*S=pRhcFljAfY;FJ{zuj8l^%ioo;PbS9eP zm2{R9MIk+sNyJ-R6&dq3ba0Dn?sKypx#2L_F7d2F&sun z{PM=#L-#(dZLGeC58OPk3GNJj+xod%7~cG0y*-k?oHH-puT-YK94s1H|;y<53(@KJd2^4W3qNoMN$ACB2U761SM literal 0 HcmV?d00001 diff --git a/Source/Rampage/Assets.xcassets/shotgunIcon.imageset/Contents.json b/Source/Rampage/Assets.xcassets/shotgunIcon.imageset/Contents.json new file mode 100644 index 0000000..cd1baff --- /dev/null +++ b/Source/Rampage/Assets.xcassets/shotgunIcon.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "shotgunIcon.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/Source/Rampage/Assets.xcassets/shotgunIcon.imageset/shotgunIcon.png b/Source/Rampage/Assets.xcassets/shotgunIcon.imageset/shotgunIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..5e7531ba6a41e75676f8e3574e5f01920d07103a GIT binary patch literal 165 zcmeAS@N?(olHy`uVBq!ia0vp^{6Ngc!3HE{ZXRg@QjEnx?oJHr&dIz4a^gK*9780g zCMPUlFgO>t`>(yC>&k=kdLm5I(^3~^gjE*{I$z8`xnASXrcQAQNeKxF10SEwCN0v( zaxzXPJb81vVAt}lMj&tup2nRQaPtUvPsH42-UB?Xk_?;Y<}fgD%#vn}owgk05C%_I KKbLh*2~7a~&NprV literal 0 HcmV?d00001 diff --git a/Source/Rampage/Info.plist b/Source/Rampage/Info.plist index 16be3b6..1729de5 100644 --- a/Source/Rampage/Info.plist +++ b/Source/Rampage/Info.plist @@ -28,9 +28,10 @@ armv7 + UIStatusBarHidden + UISupportedInterfaceOrientations - UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight diff --git a/Source/Rampage/ViewController.swift b/Source/Rampage/ViewController.swift index dc369b7..6063751 100644 --- a/Source/Rampage/ViewController.swift +++ b/Source/Rampage/ViewController.swift @@ -72,6 +72,10 @@ class ViewController: UIViewController { tapGesture.delegate = self } + override var prefersStatusBarHidden: Bool { + return true + } + private var inputVector: Vector { switch panGesture.state { case .began, .changed: @@ -129,6 +133,11 @@ class ViewController: UIViewController { let width = Int(imageView.bounds.width), height = Int(imageView.bounds.height) var renderer = Renderer(width: width, height: height, textures: textures) + let insets = self.view.safeAreaInsets + renderer.safeArea = Rect( + min: Vector(x: Double(insets.left), y: Double(insets.top)), + max: renderer.bitmap.size - Vector(x: Double(insets.left), y: Double(insets.bottom)) + ) renderer.draw(world) imageView.image = UIImage(bitmap: renderer.bitmap) diff --git a/Source/Renderer/Bitmap.swift b/Source/Renderer/Bitmap.swift index 5022192..8286100 100644 --- a/Source/Renderer/Bitmap.swift +++ b/Source/Renderer/Bitmap.swift @@ -22,6 +22,10 @@ public struct Bitmap { } public extension Bitmap { + var size: Vector { + return Vector(x: Double(width), y: Double(height)) + } + subscript(x: Int, y: Int) -> Color { get { return pixels[x * height + y] } set { @@ -69,32 +73,57 @@ public extension Bitmap { } } - mutating func drawColumn(_ sourceX: Int, of source: Bitmap, at point: Vector, height: Double) { + mutating func drawColumn( + _ sourceX: Int, + of source: Bitmap, + at point: Vector, + height: Double, + tint: Color? = nil + ) { let start = Int(point.y), end = Int((point.y + height).rounded(.up)) let stepY = Double(source.height) / height let offset = Int(point.x) * self.height if source.isOpaque { for y in max(0, start) ..< min(self.height, end) { let sourceY = max(0, Double(y) - point.y) * stepY - let sourceColor = source[sourceX, Int(sourceY)] + var sourceColor = source[sourceX, Int(sourceY)] + if let tint = tint { + sourceColor.r = UInt8(UInt16(sourceColor.r) * UInt16(tint.r) / 255) + sourceColor.g = UInt8(UInt16(sourceColor.g) * UInt16(tint.g) / 255) + sourceColor.b = UInt8(UInt16(sourceColor.b) * UInt16(tint.b) / 255) + sourceColor.a = UInt8(UInt16(sourceColor.a) * UInt16(tint.a) / 255) + } pixels[offset + y] = sourceColor } } else { for y in max(0, start) ..< min(self.height, end) { let sourceY = max(0, Double(y) - point.y) * stepY - let sourceColor = source[sourceX, Int(sourceY)] + var sourceColor = source[sourceX, Int(sourceY)] + if let tint = tint { + sourceColor.r = UInt8(UInt16(sourceColor.r) * UInt16(tint.r) / 255) + sourceColor.g = UInt8(UInt16(sourceColor.g) * UInt16(tint.g) / 255) + sourceColor.b = UInt8(UInt16(sourceColor.b) * UInt16(tint.b) / 255) + sourceColor.a = UInt8(UInt16(sourceColor.a) * UInt16(tint.a) / 255) + } blendPixel(at: offset + y, with: sourceColor) } } } - mutating func drawImage(_ source: Bitmap, at point: Vector, size: Vector) { + mutating func drawImage( + _ source: Bitmap, + xRange: Range? = nil, + at point: Vector, + size: Vector, + tint: Color? = nil + ) { + let xRange = xRange ?? 0 ..< source.width let start = Int(point.x), end = Int(point.x + size.x) - let stepX = Double(source.width) / size.x - for x in max(0, start) ..< min(width, end) { - let sourceX = (Double(x) - point.x) * stepX + let stepX = Double(xRange.count) / size.x + for x in max(0, start) ..< max(0, start, min(width, end)) { + let sourceX = Int(max(0, Double(x) - point.x) * stepX) + xRange.lowerBound let outputPosition = Vector(x: Double(x), y: point.y) - drawColumn(Int(sourceX), of: source, at: outputPosition, height: size.y) + drawColumn(sourceX, of: source, at: outputPosition, height: size.y, tint: tint) } } diff --git a/Source/Renderer/Renderer.swift b/Source/Renderer/Renderer.swift index a1fa8e8..12d1fb0 100644 --- a/Source/Renderer/Renderer.swift +++ b/Source/Renderer/Renderer.swift @@ -13,10 +13,12 @@ private let fizzle = (0 ..< 10000).shuffled() public struct Renderer { public private(set) var bitmap: Bitmap private let textures: Textures + public var safeArea: Rect public init(width: Int, height: Int, textures: Textures) { self.bitmap = Bitmap(width: width, height: height, color: .black) self.textures = textures + self.safeArea = Rect(min: Vector(x: 0, y: 0), max: bitmap.size) } } @@ -134,6 +136,61 @@ public extension Renderer { size: Vector(x: weaponWidth, y: screenHeight) ) + // Crosshair + let crosshair = textures[.crosshair] + let hudScale = bitmap.size.y / 64 + let crosshairSize = crosshair.size * hudScale + bitmap.drawImage(crosshair, at: (bitmap.size - crosshairSize) / 2, size: crosshairSize) + + // Health icon + let healthIcon = textures[.healthIcon] + var offset = safeArea.min + Vector(x: 1, y: 1) * hudScale + bitmap.drawImage(healthIcon, at: offset, size: healthIcon.size * hudScale) + offset.x += healthIcon.size.x * hudScale + + // Health + let font = textures[.font] + let charSize = Vector(x: font.size.x / 10, y: font.size.y) + let health = Int(max(0, world.player.health)) + let healthTint: Color + switch health { + case ...10: + healthTint = .red + case 10 ... 30: + healthTint = .yellow + default: + healthTint = .green + } + for char in String(health) { + let index = Int(char.asciiValue!) - 48 + let step = Int(charSize.x) + let xRange = index * step ..< (index + 1) * step + bitmap.drawImage( + font, + xRange: xRange, + at: offset, + size: charSize * hudScale, + tint: healthTint + ) + offset.x += charSize.x * hudScale + } + + // Ammunition + offset.x = safeArea.max.x + let ammo = Int(max(0, min(99, world.player.ammo))) + for char in String(ammo).reversed() { + let index = Int(char.asciiValue!) - 48 + let step = Int(charSize.x) + let xRange = index * step ..< (index + 1) * step + offset.x -= charSize.x * hudScale + bitmap.drawImage(font, xRange: xRange, at: offset, size: charSize * hudScale) + } + + // Weapon icon + let weaponIcon = textures[world.player.weapon.attributes.hudIcon] + offset.x -= weaponIcon.size.x * hudScale + bitmap.drawImage(weaponIcon, at: offset, size: weaponIcon.size * hudScale) + // Effects for effect in world.effects { switch effect.type {