Code:
'==================================================================
'
' CITY FIGHTER
' Primitive Shapes
' Your mission is simple : Defeat all red enemies and 1 green Boss
'
' Control Keys
' -LEFT, RIGHT = move
' -SPACE BAR = punch
' -R = continue
' -ESC = quit
'
'==================================================================
include "sfx.n7"
visible sfx = SFX() 'create an sfx instance
#win32
' Set up the screen resolution
set window "CITY FIGHTER", 800, 600
set redraw off
randomize time()
'Note frequency in the 5th Octave
constant DO = 261.63*2
constant RE = 293.66*2
constant MI = 329.63*2
constant FA = 349.23*2
constant SO = 392.00*2
constant LA = 440.00*2
constant TI = 493.88*2
' Constants
constant gravity = 0.8
constant move_step = 10 ' Distance moved per step
constant knockback_step = 20 ' Distance stepped backward during knockback
constant boundary_left = 50 ' Left boundary
constant boundary_right = 750 ' Right boundary
constant ground_level = 400 ' Ground level
constant attack_width = 50 ' Width of attack hitbox
constant attack_height = 20 ' Height of attack hitbox
constant attack_duration = 10 ' Frames an attack lasts
constant max_health = 200 ' Maximum health for players
'Beat
constant b1 = 0.2
constant b2 = 0.4
visible play_sound = true
' SoundFx Definition (duration, pitch, fadeOut, sampleRate)
visible punch1_sfx = CreateNoiseSfx(0.1, 1.00, 0.2, 14200)
visible punch2_sfx = CreateNoiseSfx(0.1, 0.50, 0.2, 14200)
' Initialize Player 1 (Human Player)
visible player1 = []
player1.x = 100
player1.y = ground_level
player1.w = 50 'width
player1.h = 100 'height
player1.vy = 0
player1.ground = true 'on the ground
player1.health = max_health
' Initialize Player 2 (Computer Player)
visible player2 = []
player2.x = 600
player2.y = ground_level
player2.w = 50 'width
player2.h = 100
player2.vy = 0
player2.ground = true 'on the ground
player2.health = max_health
' Attack properties
visible attack = []
attack.timer = 0
attack.x = 0
attack.y = 0
attack.c = [255,255,255]
' computer Control Variables
visible computer = []
computer.moveTimer = 0
computer.attackTimer = 0
computer.direction = 0 ' 1 = right, -1 = left, 0 = no movement
' Resurrection Tracking for Player 2
visible player2_resurrections = 3
' Hit Effect Timers
visible player1_hit_timer = 0
visible player2_hit_timer = 0
' Letters
visible letter = []
letter.T = []
letter.R = []
letter.F = []
letter.G = []
letter.H = []
letter.C = []
letter.Y = []
letter.W = []
letter.N = []
letter.L = []
letter.S = []
letter.E = []
letter.I = []
letter.O = []
letter.U = []
for i = 0 to 4
letter.T[i] = []
letter.R[i] = []
letter.E[i] = []
letter.F[i] = []
letter.I[i] = []
letter.G[i] = []
letter.H[i] = []
letter.C[i] = []
letter.Y[i] = []
letter.O[i] = []
letter.U[i] = []
letter.W[i] = []
letter.N[i] = []
letter.L[i] = []
letter.S[i] = []
next
' Load patterns for letters
letter.S = load_pattern([
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 1],
[0, 0, 0, 0, 1],
[1, 1, 1, 1, 1]
])
letter.T = load_pattern([
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]
])
letter.R = load_pattern([
[1, 1, 1, 1, 0],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 0],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1]
])
letter.E = load_pattern([
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 0],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 1]
])
letter.F = load_pattern([
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0]
])
letter.I = load_pattern([
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[1, 1, 1, 1, 1]
])
letter.G = load_pattern([
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 0, 0, 1, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1]
])
letter.H = load_pattern([
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1]
])
letter.C = load_pattern([
[1, 1, 1, 1, 1],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 1]
])
letter.Y = load_pattern([
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]
])
letter.O = load_pattern([
[0, 1, 1, 1, 0],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[0, 1, 1, 1, 0]
])
letter.U = load_pattern([
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[0, 1, 1, 1, 0]
])
letter.W = load_pattern([
[1, 0, 0, 0, 1],
[1, 0, 0, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 1, 0, 1],
[0, 1, 0, 1, 0]
])
letter.N = load_pattern([
[1, 0, 0, 0, 1],
[1, 1, 0, 0, 1],
[1, 0, 1, 0, 1],
[1, 0, 0, 1, 1],
[1, 0, 0, 0, 1]
])
letter.L = load_pattern([
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 0, 0, 0, 0],
[1, 1, 1, 1, 1]
])
visible car_passes = 0
visible scrolling = true
visible stage = 1
'-----------
' MAIN LOOP
'-----------
restart = false
while not keydown(KEY_ESCAPE, true) ' Exit game if ESC is pressed
' Restart the game
if restart = true then
restart_game()
else
cutscene()
endif
' Handle Player 1 movement
result = handle_player_movement(player1.x, player1.y, player1.vy, player1.ground, 0, false)
player1.x = result[0]
player1.y = result[1]
player1.vy= result[2]
player1.ground = result[3]
' Handle Player 2 (computer) movement
result = handle_player_movement(player2.x, player2.y, player2.vy, player2.ground, computer.direction, true)
player2.x = result[0]
player2.y = result[1]
player2.vy= result[2]
player2.ground = result[3]
computer.direction = result[4]
' Apply gravity and update positions
apply_gravity_and_update_positions()
' Handle attacks
handle_attacks()
' Prevent players from overlapping
if check_collision(player1.x, player1.y, player1.w, player1.h,
player2.x, player2.y, player2.w, player2.h) then
player1.x = player1.x - move_step ' Push Player 1 back
player2.x = player2.x + move_step ' Push Player 2 forward
endif
' Draw background
set color 0, 0, 0
cls
set color 100,100,100
draw_background()
' Draw players and health bars
draw_player(player1.x, player1.y, player1.w, player1.h,
[0, 0, 255], [100, 100, 255], player1.health, 50, 50, player1_hit_timer)
if stage=1 then
draw_player(player2.x, player2.y, player2.w, player2.h,
[255, 0, 0], [255, 100, 100], player2.health, 550, 50, player2_hit_timer)
elseif stage=2 then
draw_player(player2.x, player2.y-40, player2.w+40, player2.h+40,
[0, 255, 0], [100, 255, 100], player2.health, 550, 50, player2_hit_timer)
endif
' Draw attack (if active)
if attack.timer > 0 then
set color attack.c
draw rect attack.x, attack.y, attack_width, attack_height, true
endif
' Check for game over
handle_game_over()
' Decrement hit timers
if player1_hit_timer > 0 then
player1_hit_timer = player1_hit_timer - 1
endif
if player2_hit_timer > 0 then
player2_hit_timer = player2_hit_timer - 1
endif
fwait 30
redraw
wend
'------------
' FUNCTIONS
'------------
' Restart function
function restart_game()
player1.x = 100
player1.y = ground_level
player1.vy = 0
player1.ground = true
player1.health = max_health
player2.x = 600
player2.y = ground_level
player2.vy = 0
player2.ground = true
player2.health = max_health
attack.timer = 0
computer.moveTimer = 0
computer.attackTimer = 0
player2_resurrections = 3 ' Reset resurrection count
player1_hit_timer = 0 ' Reset hit effect timer for Player 1
player2_hit_timer = 0 ' Reset hit effect timer for Player 2
endfunc
' Handle player movement
function handle_player_movement(player_x, player_y, player_vy, player_on_ground, move_direction, is_computer)
if not is_computer then
' Human player input
if keydown(KEY_UP, true) and player_on_ground then
player_vy = -10 ' Jump
player_on_ground = false
endif
if keydown(KEY_LEFT, true) then
move_direction = -1 ' Move left
elseif keydown(KEY_RIGHT, true) then
move_direction = 1 ' Move right
else
move_direction = 0 ' No movement
endif
else
' computer movement logic
if computer.moveTimer <= 0 then
computer.moveTimer = rnd(30, 60) ' Set a random timer for the next movement decision
if abs(player1.x - player2.x) > 200 then
move_direction = choose(1, -1, 0) ' Move toward Player 1 if far away
else
move_direction = choose(-1, 0, 1) ' Random movement if close
endif
else
computer.moveTimer = computer.moveTimer - 1 ' Decrement timer
endif
endif
' Update position based on direction
if move_direction = -1 then
player_x = max(player_x - move_step, boundary_left) ' Move left
elseif move_direction = 1 then
player_x = min(player_x + move_step, boundary_right - player1.w) ' Move right
endif
return [player_x, player_y, player_vy, player_on_ground, move_direction]
endfunc
' Apply gravity and update positions
function apply_gravity_and_update_positions()
player1.vy = player1.vy + gravity
player2.vy = player2.vy + gravity
player1.y = player1.y + player1.vy
player2.y = player2.y + player2.vy
' Check if players are on the ground
if player1.y >= ground_level then
player1.y = ground_level
player1.vy = 0
player1.ground = true
endif
if player2.y >= ground_level then
player2.y = ground_level
player2.vy = 0
player2.ground = true
endif
endfunc
' Collision detection
function check_collision(x1, y1, w1, h1, x2, y2, w2, h2)
' Check if two rectangles overlap
return x1 < x2 + w2 and x1 + w1 > x2 and y1 < y2 + h2 and y1 + h1 > y2
endfunc
' Draw player and health bar
function draw_player(x, y, w, h, color_body, color_head, health, health_x, health_y, hit_timer)
' Apply hit effect if active
if hit_timer > 0 then
y = y + sin(hit_timer * 10) * 5 ' Oscillate vertically
endif
' Draw player body
set color color_body
draw rect x, y, w, h, true
' Draw player head
set color color_head
draw ellipse x + w / 2, y - 20, 20, 20, true
' Draw health bar
set color color_body
draw rect health_x, health_y, health, 20, true ' Health bar
set color 255, 255, 255 ' White border
draw rect health_x, health_y, max_health, 20 ' Border for health bar
endfunc
' Handle attacks
function handle_attacks()
if stage=1 then impact = 10
if stage=2 then impact = 5
' Handle Player 1 Attacks
if keydown(KEY_SPACE, true) and attack.timer <= 0 then
attack.c = [0, 0, 255] ' Blue for Player 1
attack.timer = attack_duration
attack.x = player1.x + player1.w
attack.y = player1.y + player1.h / 2 - attack_height / 2
if check_collision(attack.x, attack.y, attack_width, attack_height,
player2.x, player2.y, player2.w, player2.h) then
play sound punch1_sfx
player2.health = max(player2.health - impact, 0)
player2.x = min(player2.x + knockback_step,boundary_right-player2.w)' Step Player 2 backward
player2_hit_timer = 10 ' Trigger hit effect for Player 2
endif
endif
' Handle Player 2 computer Attack
if computer.attackTimer <= 0 then
if rnd(1, 100) <= 40 then ' chance to attack
if stage=1 then attack.c = [255, 0, 0] ' Red for Player 2 and stage=1
if stage=2 then attack.c = [0,255,0] ' Green for player 2 and stage=2
attack.timer = attack_duration
attack.x = player2.x - attack_width
attack.y = player2.y + player2.h / 2 - attack_height / 2
if check_collision(attack.x, attack.y, attack_width, attack_height,
player1.x, player1.y, player1.w, player1.h) then
play sound punch2_sfx
player1.health = max(player1.health - 10, 0)
player1.x = max(player1.x - knockback_step,boundary_left) ' Step Player 1 backward
player1_hit_timer = 10 ' Trigger hit effect for Player 1
endif
endif
computer.attackTimer = rnd(0, 60) ' Random cooldown for next attack
else
computer.attackTimer = computer.attackTimer - 1
endif
' Decrement attack timer
if attack.timer > 0 then
attack.timer = attack.timer - 1
endif
endfunc
' Handle game over
function handle_game_over()
if player1.health <= 0 then
write_gameover(0)
do; wait 1; redraw; until keydown(KEY_R, true) ' for R to restart
restart_game()
endif
if player2.health <= 0 and stage=1 then
' Check if Player 2 has remaining resurrections only for stage=1
if player2_resurrections > 0 then
' Resurrect Player 2
player2_resurrections = player2_resurrections - 1
player2.health = max_health ' Restore Player 2's health to 100%
player2.x = 600 ' Reset Player 2's position
player2.y = ground_level
player2.vy = 0
player2.ground = true
' Display a red splash screen
set color 255, 0, 0,100
draw rect 0,0,width(),height(),true
set caret width() / 2, height() / 2
set color 255,255,255
center "Ready, GO !"
center "Press R to continue"
redraw
do;wait 1;until keydown(KEY_R,true) ' Pause briefly to show the message
else
' No more red enemies, goto Boss Level
write_gameover(1)
if stage=1 then
play_sound = true
car_passes = 0
cutscene()
endif
stage=2
' Display a green splash screen
set color 0, 255, 0,100
draw rect 0,0,width(),height(),true
set caret width() / 2, height() / 2
set color 255,255,255
center "Fight the Boss"
center "Press R to continue"
redraw
do; wait 1; redraw; until keydown(KEY_R, true) ' R to restart
restart_game()
endif
endif
if player2.health <= 0 and stage=2 then
write_gameover(1)
' Display a blue splash screen
set color 0, 0, 255,100
draw rect 0,0,width(),height(),true
set caret width() / 2, height() / 2
set color 255,255,255
center "Thank you for playing this game"
center "Press R to restart"
redraw
do; wait 1; redraw; until keydown(KEY_R, true) ' R to restart
stage = 1
restart_game()
endif
endfunc
' Play cutscene animation: Scrolling background and car
function cutscene()
background_offset = 0
car_x = 800 ' Start car off-screen
if stage = 1 then
' Draw ground plane
set color 50, 50, 50
draw rect 0, 500, 800, 100, true
draw_title()
redraw
endif
if play_sound then
Song()
play_sound = false
endif
while car_passes < 5+1
' Clear screen
set color 0, 0, 0
cls
' Scroll background
background_offset = background_offset - 5
if background_offset <= -800 then
background_offset = 0
endif
draw_scrolling_background(background_offset)
' Draw car
draw_car(car_x, ground_level+100)
car_x = car_x - 10 ' Move car left
if car_x <= -100 then
car_x = 800 ' Reset car position
car_passes = car_passes + 1
endif
' Stop car on pass
if car_passes = 5 then
car_x = 800 ' Keep car at the right edge
scrolling = false
break
endif
fwait 60
redraw
wend
endfunc
' Draw scrolling background
function draw_scrolling_background(offset)
' Draw buildings twice for seamless scrolling
draw_building(50 + offset, 500, 140, 400)
draw_building(200 + offset, 500, 140, 180)
draw_building(350 + offset, 500, 140, 300)
draw_building(500 + offset, 500, 140, 350)
draw_building(650 + offset, 500, 100, 160)
draw_building(850 + offset, 500, 140, 400)
draw_building(1000 + offset, 500, 140, 180)
draw_building(1150 + offset, 500, 140, 300)
draw_building(1300 + offset, 500, 140, 350)
draw_building(1450 + offset, 500, 100, 160)
' Draw ground plane
set color 50, 50, 50
draw rect 0 + offset, 500, 800, 100, true
draw rect 800 + offset, 500, 800, 100, true
endfunc
' Draw car
function draw_car(x, y)
set color 255, 0, 0
draw rect x, y, 180, 30, true ' Body
draw rect x + 10, y - 20, 160, 20, true ' Roof
set color 0, 0, 0
draw ellipse x + 40, y + 30, 20, 20, true ' Left wheel
draw ellipse x + 140, y + 30, 20, 20, true ' Right wheel
PlayTune(3)
endfunc
' Custom choose function
function choose(option1, option2, option3)
options = []
options[0] = option1
options[1] = option2
options[2] = option3
return options[rnd(0, 2)] ' Randomly select one of the three options
endfunc
' Draw background
function draw_background()
' Clear the screen with a black background
set color 0, 0, 0
cls
set color 255,255,255
set caret 50,30 ; wln "Player 1"
set caret 680,30
if stage = 1 then wln "Player 2"
if stage = 2 then wln "BOSS"
' Draw buildings
draw_building( 50, 500, 140, 400)
draw_building(200, 500, 140, 180)
draw_building(350, 500, 140, 300)
draw_building(500, 500, 140, 350)
draw_building(650, 500, 100, 160)
' Draw ground plane
set color 50, 50, 50
draw rect 0, 500, 800, 100, true
' Draw Title
draw_title()
endfunc
' Helper function to draw a building
function draw_building(x, y, w, h)
' Draw rectangular building
set color 100,100,100
draw rect x, y - h, w, h, true
' Add windows
set color 0, 0, 0
for i = 0 to w step 20
for j = 0 to h step 20
draw rect x + i + 5, y - h + j + 5, 10, 10, true
next
next
endfunc
function load_pattern(pattern_data)
' Converts a 2D array of pattern data into a usable format
pattern = []
for i = 0 to sizeof(pattern_data) - 1
pattern[i] = pattern_data[i]
next
return pattern
endfunc
function draw_title()
' Write "City Fighter" using 5x5 patterns
set color 255, 255, 255
' Parameters for the 5x5 grids
cell_size = 10 ' Size of each cell in the grid
spacing = 10 ' Spacing between letters
word_spacing = 50 ' Additional spacing between "City" and "Fighter"
start_x = 50 ' X-coordinate of the top-left corner of the first letter
start_y = 520 ' Y-coordinate of the top-left corner of the first letter
' Array of patterns for "City Fighter"
patterns = [letter.C, letter.I, letter.T, letter.Y,
letter.F, letter.I, letter.G, letter.H, letter.T, letter.E, letter.R]
' Draw all patterns side by side
current_x = start_x
for i = 0 to sizeof(patterns) - 1
' Get the current pattern
pattern = patterns[i]
' Draw the pattern
draw_letter(pattern, current_x, start_y, cell_size)
' Move to the next letter position
current_x = current_x + (5 * cell_size) + spacing
' Add extra spacing between "City" and "Fighter"
if i = len("City")-1 then
current_x = current_x + word_spacing
endif
next
endfunc
function draw_letter(pattern, x, y, cell_size)
set color 255, 255, 255
' Draws a single letter based on its pattern
for row = 0 to 4
for col = 0 to 4
if pattern[row][col] = 1 then
draw rect x + col * cell_size, y + row * cell_size, cell_size, cell_size, true
endif
next
next
endfunc
function write_gameover(x)
' Write Message
set color 255,255,0
set caret width()/2,30
center "Press R to restart the City Fighter"
' Draw ground plane
set color 50, 50, 50
draw rect 0, 500, 800, 100, true
' Parameters for the 5x5 grids
cell_size = 10 ' Size of each cell in the grid
spacing = 10 ' Spacing between letters
word_spacing = 50 ' Additional spacing between "City" and "Fighter"
start_x = 150 ' X-coordinate of the top-left corner of the first letter
start_y = 520 ' Y-coordinate of the top-left corner of the first letter
' Array of patterns for "You Win" and "You Lose"
if x = 1 then
patterns = [letter.Y, letter.O, letter.U, letter.W, letter.I, letter.N]
else
patterns = [letter.Y, letter.O, letter.U, letter.L, letter.O, letter.S, letter.E]
endif
' Draw all patterns side by side
current_x = start_x
for i = 0 to sizeof(patterns) - 1
' Get the current pattern
pattern = patterns[i]
' Draw the pattern
draw_letter(pattern, current_x, start_y, cell_size)
' Move to the next letter position
current_x = current_x + (5 * cell_size) + spacing
' Add extra spacing after "You"
if i = len("You")-1 then
current_x = current_x + word_spacing
endif
next
endfunc
function CreateNoiseSfx(duration, pitch, fadeOut, sampleRate)
assert sampleRate >= 8000, "CreateBoomSfx: invalid sample rate"
assert pitch > 0, "CreateBoomSfx: invalid pitch"
' Mix four different noise frequencies weighted, in a weird way, by the pitch value.
freqs = [
[v: 0, p: sampleRate/500, d: 0, t: 0, w: pitch],
[v: 0, p: sampleRate/1000, d: 0, t: 0, w: pitch^2],
[v: 0, p: sampleRate/2000, d: 0, t: 0, w: pitch^3],
[v: 0, p: sampleRate/8000, d: 0, t: 0, w: pitch^4]]
s = sizeof(freqs)
data = []
vol = 1
fadeOut = fadeOut*duration*sampleRate
fadeOutDelta = 1/(duration*sampleRate - fadeOut)
for i = 0 to duration*sampleRate - 1
v = 0
w = 0
for j = 0 to s - 1; f = freqs[j]
f.t = f.t - 1
if f.t <= 0
f.t = f.p
f.d = ((rnd()*2 - 1) - f.v)/f.p
endif
f.v = f.v + f.d
v = v + f.v*f.w
w = w + f.w
next
data[i] = vol*v/w
if i > fadeOut vol = vol - fadeOutDelta
next
return createsound(data, data, sampleRate)
endfunc
'This function will play sound frequency x at duration b
function Piano(b,x)
'SquareWave(duration,freq,volume)
mytune = sfx.SquareWave(b,x,0.05)
play sound mytune
Pause()
free sound mytune 'release sound from memeory
endfunc
function Song()
Piano(b2,SO);Piano(b1,SO);Piano(b1,SO)
Piano(b2,SO);Piano(b2,MI)
Piano(b2,DO*2);Piano(b1,DO*2);Piano(b1,DO*2)
Piano(b2,TI);Piano(b2,LA)
endfunc
function Pause()
wait 100
endfunc
function Sound(b,x)
mytune = sfx.SquareWave(b,x,0.2)'(duration,frequency, volume)
play sound mytune ; wait 1
free sound mytune 'release sound from memory
endfunc
function PlayTune(i)
select i
case 1 'starting the game's scene
Sound(0.5,261.63);Sound(0.5,293.66);Sound(0.5,329.63)
case 2 'road's boundary
Sound(0.5,329.63*3);Sound(0.5,261.63*3)
case 3 'car's sound effect
Sound(0.05,261.63/2)
endsel
endfunc