add game files

This commit is contained in:
VisualGMQ 2022-07-31 22:33:30 +08:00
parent d92247535f
commit b829b49acb
10 changed files with 1220 additions and 1 deletions

4
.gitignore vendored
View File

@ -5,3 +5,7 @@ install
TODO.txt TODO.txt
.cache .cache
compile_flags.txt compile_flags.txt
game/shader
game/*.dll
game/*.dll.a
game/*.exe

4
game/config.lua Normal file
View File

@ -0,0 +1,4 @@
WindowWidth = 1024
WindowHeight = 720
Title = "InfiniteShoot - VisualGMQ For 1MGames v0.1.0"
EntryFile = "main.lua"

50
game/constants.lua Normal file
View File

@ -0,0 +1,50 @@
local _M = {}
_M.TileSize = 32
_M.PlayerInfo = {
velocity = 250,
hp = 100,
}
_M.MonsterInfo = {
velocity = 100,
hp = 50,
damage = 10,
}
_M.RoleColliBox = {
w = 14,
h = 32,
x = 9,
y = 0,
}
_M.Invincible = 1
_M.BulletColliBox = {
x = 10,
y = 0,
w = 12,
h = 12,
}
_M.StonePutProbability = 0.1
_M.PlayerHpBarInfo = {
width = _M.TileSize,
height = 5,
}
_M.MonsetHpBarInfo= {
width = _M.TileSize,
height = 5,
}
_M.GunInfo = {
cooldown = 0.1
}
_M.BulletInfo = {
damage = 10,
velocity = 500,
}
_M.GunInfo = {
cooldown = 0.1
}
_M.BulletInfo = {
damage = 10,
velocity = 500,
}
return _M

19
game/content.lua Normal file
View File

@ -0,0 +1,19 @@
---@class Content
local _M = {}
---@type Texture
_M.Texture = nil
---@type TileSheet
_M.Tilesheet = nil
---@type Entity
_M.PlayerEntity = nil
---@type table<Entity>
_M.BulletList = {}
---@type table<Entity>
_M.MonsterList = {}
return _M

475
game/ecs.lua Normal file
View File

@ -0,0 +1,475 @@
local hazel = require "hazel"
local constants = require "constants"
local math = require "math"
local vmath = require "vmath"
local content = require "content"
---@class ECS
local _M = {}
---@class Entity
---@field name string
---@field component table<Component>
local Entity = { name = "", components = {}}
---@param component Component
function Entity.SetComponent(self, component)
if not component then
return
end
---@type Component
local oldComp = self.components[component:GetName()]
if oldComp then
oldComp.parent = nil
hazel.Log.Logw(string.format("component %s already exists, replace it", oldComp:GetName()))
end
component.parent = self
self.components[component:GetName()] = component
end
---@return Component|nil
---@param type string
function Entity.GetComponent(self, type)
if not type then
return nil
end
return self.components[type]
end
---@param type string
function Entity.RemoveComponent(self, type)
self.components[type] = nil
end
_M.Entity = Entity
---@return Entity
---@param name string
function _M.CreateEntity(name)
local o = {name = name, components = {}}
setmetatable(o, {__index = Entity})
return o
end
---@param self Entity
function Entity.Update(self)
---@param v Component
for _, v in pairs(self.components) do
if v:IsActive() and v.Update then
v:Update()
end
end
end
---@class Component
local Component = {
---@param self Component
---@return boolean
IsActive = function(self)
return self.isActive
end,
---@param self Component
Disable = function(self)
self.isActive = false
end,
---@param self Component
Enable = function(self)
self.isActive = true
end,
---@param self Component
---@return string
GetName = function(self)
return self.name
end,
---@param self Component
---@return Entity
GetParent = function(self)
return self.parent
end,
}
---@return Component
function _M.CreateComponent(o)
local o = o or {}
setmetatable(o , {__index = Component})
return o
end
---@class ComponentType
local ComponentType = {
Transform = "Transform",
Image = "Image",
Controller = "Controller",
RoleProp = "RoleProp",
HpShow = "HpShow",
Gun = "Gun",
Bullet = "Bullet",
Direction = "Direction",
AI = "AI",
ColliBox = "ColliBox",
Invincible = "Invincible",
}
_M.ComponentType = ComponentType
---@class TransformComponent:Component
---@field position Point
---@field size Point
---@field flip number
---@return TransformComponent
---@param pos Point
---@param size Point
---@param flip number|nil
function _M.CreateTransformComponent(pos, size, flip)
local o = { isActive = true, name = ComponentType.Transform, position = pos, size = size, flip = flip or Flip.None, parent = nil }
o.Update = function () end
return _M.CreateComponent(o)
end
---@class ImageComponent:Component
---@field tilesheet TileSheet
---@field row number
---@field col number
---@return ImageComponent
---@param tilesheet TileSheet
---@param col number
---@param row number
function _M.CreateImageComponent(tilesheet, col, row)
local o = { isActive = true, name = ComponentType.Image, tilesheet = tilesheet, col = col, row = row, parent = nil }
---@param self ImageComponent
o.Update = function(self)
---@type TransformComponent
local transform = self:GetParent():GetComponent(ComponentType.Transform)
if tilesheet then
tilesheet:Draw(self.col or 0, self.row or 0,
hazel.CreateRect(transform.position.x, transform.position.y, transform.size.x, transform.size.y),
transform.flip)
end
end
return _M.CreateComponent(o)
end
---@class DirectionComponent:Component
---@field direction Point
function _M.CreateDirectionComponent(startRow)
local o = { isActive = true, name = ComponentType.Direction, direction = hazel.CreatePos(0, 0), startRow = startRow, parent = nil }
---@param self DirectionComponent
---@param dir Point
o.SetDir = function(self, dir)
self.direction = dir
end
---@param self DirectionComponent
o.Update = function(self)
local image = self:GetParent():GetComponent(ComponentType.Image)
if not image then
return
end
local absX = math.abs(self.direction.x)
local absY = math.abs(self.direction.y)
if self.direction.y <= 0 and absX <= absY then
image.col = 0
image.row = 1 + self.startRow
end
if self.direction.y > 0 and absX <= absY then
image.col = 0
image.row = 0 + self.startRow
end
if self.direction.x < 0 and absY < absX then
image.col = 0
image.row = 2 + self.startRow
end
if self.direction.x > 0 and absY < absX then
image.col = 0
image.row = 3 + self.startRow
end
end
return _M.CreateComponent(o)
end
---@class ControllerComponent:Component
---@return ControllerComponent
---@param speed number
function _M.CreateControllerComponent()
local o = { isActive = true, name = ComponentType.Controller, parent = nil }
---@param self ControllerComponent
o.Update = function(self)
---@type RolePropComponent
local roleProp = self:GetParent():GetComponent(ComponentType.RoleProp)
local speed = roleProp.speed
---@type TransformComponent
local transform = self:GetParent():GetComponent(ComponentType.Transform)
local elapseTime = hazel.Time.GetElapseTime()
if hazel.IsKeyPressing(hazel.Key.A) then
transform.position.x = transform.position.x - speed * elapseTime
end
if hazel.IsKeyPressing(hazel.Key.D) then
transform.position.x = transform.position.x + speed * elapseTime
end
if hazel.IsKeyPressing(hazel.Key.W) then
transform.position.y = transform.position.y - speed * elapseTime
end
if hazel.IsKeyPressing(hazel.Key.S) then
transform.position.y = transform.position.y + speed * elapseTime
end
---@type Point
local mousePos = hazel.GetGameMousePos()
local playerCenterX = transform.position.x
local playerCenterY = transform.position.y
playerCenterX = playerCenterX + constants.TileSize / 2
playerCenterY = playerCenterY + constants.TileSize / 2
---@type Point
local mouseDir = hazel.CreatePos(mousePos.x - playerCenterX, mousePos.y - playerCenterY)
self:GetParent():GetComponent(ComponentType.Direction):SetDir(mouseDir)
end
return _M.CreateComponent(o)
end
---@class InvincibleComponent:Component
---@field time number
---@return InvincibleComponent
---@param time number
function _M.CreateInvincibleComponent(time)
local o = { isActive = true, name = ComponentType.Invincible, cooldown = time, timeCountDown = time, parent = nil }
---@return boolean
---@param self InvincibleComponent
o.IsInvincibleState = function(self)
return self.timeCountDown > 0
end
---@param self InvincibleComponent
o.IntoInvincible = function(self)
if self.timeCountDown <= 0 then
self.timeCountDown = self.cooldown
end
end
---@param self InvincibleComponent
o.Update = function(self)
self.timeCountDown = self.timeCountDown - hazel.Time.GetElapseTime()
if self.timeCountDown < 0 then
self.timeCountDown = 0
end
end
return _M.CreateComponent(o)
end
---@class RolePropComponent:Component
---@field hp number
---@field maxHp number
---@field speed number
---@field damage number
---@return RolePropComponent
---@param hp number
---@param speed number
---@param damage number|nil
function _M.CreateRolePropComponent(hp, speed, damage)
local o = { isActive = true, name = ComponentType.RoleProp, maxHp = hp, hp = hp, speed = speed, damage = damage or 0, parent = nil }
---@param self RolePropComponent
o.IsDie = function(self)
return self.hp <= 0
end
o.Update = function(self)
if self.hp < 0 then
self.hp = 0
end
end
return _M.CreateComponent(o)
end
---@class HpShowComponent:Component
---@field size Point
---@return HpShowComponent
---@param size Point
function _M.CreateHpShowComponent(size)
local o = { isActive = true, name = ComponentType.HpShow, size = size, parent = nil }
---@param self HpShowComponent
o.Update = function(self)
---@type Point
local position = self:GetParent():GetComponent(ComponentType.Transform).position
local pos = hazel.CreatePos(position.x, position.y - self.size.y - 2)
---@type RolePropComponent
local roleProp = self:GetParent():GetComponent(ComponentType.RoleProp)
hazel.Renderer.SetDrawColor(1, 1, 1, 1)
hazel.Renderer.FillRect(pos.x, pos.y, size.x, size.y)
hazel.Renderer.SetDrawColor(0, 1, 0, 1)
hazel.Renderer.FillRect(pos.x, pos.y, size.x * roleProp.hp / roleProp.maxHp, size.y)
hazel.Renderer.SetDrawColor(0, 0, 0, 1)
hazel.Renderer.DrawRect(pos.x, pos.y, size.x, size.y)
end
return _M.CreateComponent(o)
end
---@class AIComponent:Component
---@return AIComponent
function _M.CreateAIComponent()
local o = { isActive = true, name = ComponentType.AI, parent = nil }
---@param self AIComponent
o.Update = function(self)
---@type DirectionComponent
local direction = self:GetParent():GetComponent(ComponentType.Direction)
local playerCenterX = content.PlayerEntity:GetComponent(ComponentType.Transform).position.x + constants.TileSize / 2
local playerCenterY = content.PlayerEntity:GetComponent(ComponentType.Transform).position.y + constants.TileSize / 2
local myselfCenterX = self:GetParent():GetComponent(ComponentType.Transform).position.x + constants.TileSize / 2
local myselfCenterY = self:GetParent():GetComponent(ComponentType.Transform).position.y + constants.TileSize / 2
---@type Point
local dir = vmath.Normalize(hazel.CreatePos(playerCenterX - myselfCenterX, playerCenterY - myselfCenterY))
direction:SetDir(dir)
---@type Point
local position = self:GetParent():GetComponent(ComponentType.Transform).position
local velocity = self:GetParent():GetComponent(ComponentType.RoleProp).speed
local elapseTime = hazel.Time.GetElapseTime()
position.x = position.x + velocity * dir.x * elapseTime
position.y = position.y + velocity * dir.y * elapseTime
end
return _M.CreateComponent(o)
end
---@class GunComponent:Component
---@field damage number
---@field speed Point
---@return GunComponent
---@param damage number
---@param velocity number
function _M.CreateGunComponent(damage, velocity)
local o = { isActive = true, name = ComponentType.Gun, damage = damage, timeCounter = 0, parent = nil }
---@param self GunComponent
o.Update = function(self)
if self.timeCounter < constants.GunInfo.cooldown then
self.timeCounter = self.timeCounter + hazel.Time.GetElapseTime()
end
if self.timeCounter >= constants.GunInfo.cooldown then
self.timeCounter = constants.GunInfo.cooldown
end
if hazel.GetMouseButtonState(hazel.MouseButton.Left) == hazel.InputState.Press and self.timeCounter >= constants.GunInfo.cooldown then
self.timeCounter = self.timeCounter - constants.GunInfo.cooldown
---@type Point
local position = self:GetParent():GetComponent(ComponentType.Transform).position
---@type Point
local mousePos = hazel.GetGameMousePos()
local playerCenterX = position.x + constants.TileSize / 2
local playerCenterY = position.y + constants.TileSize / 2
local nMouseDir = vmath.Normalize(hazel.CreatePos(mousePos.x - playerCenterX, mousePos.y - playerCenterY))
local bullet = _M.CreateBullet(hazel.CreatePos(playerCenterX - constants.TileSize / 2, playerCenterY - constants.TileSize / 2), self.damage, hazel.CreatePos(nMouseDir.x * velocity, nMouseDir.y * velocity))
table.insert(content.BulletList, bullet)
end
end
return _M.CreateComponent(o)
end
---@class BulletComponent:Component
---@field damage number
---@field speed Point
---@return BulletComponent
---@param damage number
---@param speed Point
function _M.CreateBulletComponent(damage, speed)
local o = { isActive = true, name = ComponentType.Bullet, damage = damage, speed = speed, parent = nil }
---@param self BulletComponent
o.Update = function(self)
local position = self:GetParent():GetComponent(ComponentType.Transform).position
local elapseTime = hazel.Time.GetElapseTime()
position.x = position.x + self.speed.x * elapseTime
position.y = position.y + self.speed.y * elapseTime
end
return _M.CreateComponent(o)
end
---@class ColliBoxComponent:Component
---@field rect Rect
---@return ColliBoxComponent
---@param rect Rect
function _M.CreateColliBoxComponent(rect)
local o = { isActive = true, name = ComponentType.ColliBox, rect = rect, parent = nil }
o.Update = function(self) end
return _M.CreateComponent(o)
end
---@return Entity
---@param pos Point
---@param damage number
---@param speed Point
function _M.CreateBullet(pos, damage, speed)
---@type Entity
local entity = _M.CreateEntity("Bullet")
entity:SetComponent(_M.CreateTransformComponent(pos, hazel.CreateSize(constants.TileSize, constants.TileSize)))
entity:SetComponent(_M.CreateBulletComponent(damage, speed))
entity:SetComponent(_M.CreateImageComponent(content.Tilesheet, 1, 4))
entity:SetComponent(_M.CreateColliBoxComponent(constants.BulletColliBox))
return entity
end
---@return Entity
---@param pos Point
function _M.CreatePlayer(pos)
local entity = _M.CreateEntity("player")
entity:SetComponent(_M.CreateTransformComponent(hazel.CreatePos(pos.x, pos.y), hazel.CreateSize(constants.TileSize, constants.TileSize), Flip.Vertical))
entity:SetComponent(_M.CreateImageComponent(content.Tilesheet, 0, 0))
entity:SetComponent(_M.CreateControllerComponent())
entity:SetComponent(_M.CreateRolePropComponent(constants.PlayerInfo.hp, constants.PlayerInfo.velocity))
entity:SetComponent(_M.CreateHpShowComponent(hazel.CreateSize(constants.PlayerHpBarInfo.width, constants.PlayerHpBarInfo.height)))
entity:SetComponent(_M.CreateGunComponent(constants.BulletInfo.damage, constants.BulletInfo.velocity))
entity:SetComponent(_M.CreateDirectionComponent(0))
entity:SetComponent(_M.CreateColliBoxComponent(constants.RoleColliBox))
entity:SetComponent(_M.CreateInvincibleComponent(constants.Invincible))
return entity
end
---@return Entity
---@param pos Point
function _M.CreateMonster(pos)
local entity = _M.CreateEntity("monster")
entity:SetComponent(_M.CreateTransformComponent(hazel.CreatePos(pos.x, pos.y), hazel.CreateSize(constants.TileSize, constants.TileSize), Flip.Vertical))
entity:SetComponent(_M.CreateImageComponent(content.Tilesheet, 0, 5))
entity:SetComponent(_M.CreateRolePropComponent(constants.MonsterInfo.hp, constants.MonsterInfo.velocity, constants.MonsterInfo.damage))
entity:SetComponent(_M.CreateHpShowComponent(hazel.CreateSize(constants.MonsetHpBarInfo.width, constants.MonsetHpBarInfo.height)))
entity:SetComponent(_M.CreateDirectionComponent(5))
entity:SetComponent(_M.CreateColliBoxComponent(constants.RoleColliBox))
entity:SetComponent(_M.CreateAIComponent())
return entity
end
return _M

465
game/hazel.lua Normal file
View File

@ -0,0 +1,465 @@
---@class libhazel
local libhazel = require "libhazel"
---@class hazel
local _M = {}
---@class Point
---@field x number
---@field y number
---@class Rect
---@field x number
---@field y number
---@field w number
---@field h number
---@return Point
---@param x number
---@param y number
function _M.CreatePos(x, y)
return {x = x, y = y}
end
---@return Point
---@param w number
---@param h number
function _M.CreateSize(w, h)
return {x = w, y = h}
end
---@return Rect
---@param x number
---@param y number
---@param w number
---@param h number
function _M.CreateRect(x, y, w, h)
return {x = x, y = y, w = w, h = h}
end
---@class InputState
_M.InputState = {
Release = 0,
Press = 1,
Repeat = 2,
}
---@class Key
_M.Key = {
Unkown = -1,
Space = 32,
Apostrophe = 39,
Comma = 44,
Minus = 45,
Period = 46,
Slash = 47,
K_0 = 48,
K_1 = 49,
K_2 = 50,
K_3 = 51,
K_4 = 52,
K_5 = 53,
K_6 = 54,
K_7 = 55,
K_8 = 56,
K_9 = 57,
Semicolon = 59,
Equal = 61,
A = 65,
B = 66,
C = 67,
D = 68,
E = 69,
F = 70,
G = 71,
H = 72,
I = 73,
J = 74,
K = 75,
L = 76,
M = 77,
N = 78,
O = 79,
P = 80,
Q = 81,
R = 82,
S = 83,
T = 84,
U = 85,
V = 86,
W = 87,
X = 88,
Y = 89,
Z = 90,
Left_bracket = 91,
Backslash = 92,
Right_bracket = 93,
Grave_accent = 96,
World_1 = 161,
World_2 = 162,
Escape = 256,
Enter = 257,
Tab = 258,
Backspace = 259,
Insert = 260,
Delete = 261,
Right = 262,
Left = 263,
Down = 264,
Up = 265,
Page_up = 266,
Page_down = 267,
Home = 268,
End = 269,
Caps_lock = 280,
Scroll_lock = 281,
Num_lock = 282,
Print_screen = 283,
Pause = 284,
F1 = 290,
F2 = 291,
F3 = 292,
F4 = 293,
F5 = 294,
F6 = 295,
F7 = 296,
F8 = 297,
F9 = 298,
F10 = 299,
F11 = 300,
F12 = 301,
F13 = 302,
F14 = 303,
F15 = 304,
F16 = 305,
F17 = 306,
F18 = 307,
F19 = 308,
F20 = 309,
F21 = 310,
F22 = 311,
F23 = 312,
F24 = 313,
F25 = 314,
KP_0 = 320,
KP_1 = 321,
KP_2 = 322,
KP_3 = 323,
KP_4 = 324,
KP_5 = 325,
KP_6 = 326,
KP_7 = 327,
KP_8 = 328,
KP_9 = 329,
KP_decimal = 330,
KP_divide = 331,
KP_multiply = 332,
KP_subtract = 333,
KP_add = 334,
KP_enter = 335,
KP_equal = 336,
LeftShift = 340,
LeftControl = 341,
LeftAlt = 342,
LeftSuper = 343,
RightShift = 344,
RightControl = 345,
RightAlt = 346,
RightSuper = 347,
Menu = 348,
}
---@class MouseButton
_M.MouseButton = {
Left = 0,
Right = 1,
Middle = 2,
}
---@return InputState
---@param key Key
function _M.GetKeyState(key)
return libhazel.GetKeyState(key)
end
---@return boolean
---@param key number
function _M.IsKeyPressing(key)
return _M.GetKeyState(key) == _M.InputState.Press
end
---@return boolean
---@param key number
function _M.IsKeyReleasing(key)
return _M.GetKeyState(key) == _M.InputState.Release
end
---@return Point
function _M.GetMousePos()
local x, y = libhazel.GetMousePos()
return _M.CreatePos(x, y)
end
---@return Point
function _M.GetGameMousePos()
local x, y = libhazel.GetMousePos()
local windowSize = _M.GetWindowSize()
local canvaSize = _M.GetCanvaSize()
return _M.CreatePos(x * canvaSize.x / windowSize.x, y * canvaSize.y / windowSize.y)
end
function _M.HideCursor()
libhazel.HideCursor()
end
function _M.ShowCursor()
libhazel.ShowCursor()
end
---@return InputState
---@param btn MouseButton
function _M.GetMouseButtonState(btn)
return libhazel.GetMouseButtonState(btn)
end
---@return number
function _M.GetTime()
return libhazel.GetTime()
end
---@param filename string
function _M.SetWindowIcon(filename)
libhazel.WindowSetIcon(filename)
end
---@return Point
function _M.GetCanvaSize()
local x, y = libhazel.GetCanvaSize()
return _M.CreatePos(x, y)
end
---@return Point
function _M.GetWindowSize()
local x, y = libhazel.GetWindowSize()
return _M.CreatePos(x, y)
end
---@class Texture
---@field w number
---@field h number
---@return Texture
---@param filename string
function _M.LoadTexture(filename)
local texture = libhazel.LoadTexture(filename)
local w, h = libhazel.TextureGetSize(texture)
return {rawPointer = texture, w = w, h = h}
end
---@param texture Texture
function _M.DestroyTexture(texture)
libhazel.DestroyTexture(texture.rawPointer)
end
---@return Point
---@param texture Texture
function _M.GetTextureSize(texture)
local x, y = libhazel.TextureGetSize(texture)
return _M.CreatePos(x, y)
end
function _M.SayHello()
libhazel.SayHello()
end
---@class Flip
Flip = {
None = 0,
Vertical = 1,
Horizontal = 2,
Both = 3,
}
_M.Flip = Flip
---@class Renderer
local Renderer = {
---@param r number
---@param g number
---@param b number
---@param a number
SetClearColor = function(r, g, b, a)
libhazel.RenderSetClearColor(r, g, b, a or 1)
end,
---@param r number
---@param g number
---@param b number
---@param a number
SetDrawColor = function(r, g, b, a)
libhazel.RenderSetDrawColor(r, g, b, a or 1)
end,
---@param x1 number
---@param y1 number
---@param x2 number
---@param y2 number
DrawLine = function(x1, y1, x2, y2)
libhazel.RenderDrawLine(x1, y1, x2, y2)
end,
---@param p1 Point
---@param p2 Point
DrawLineByPoints = function(p1, p2)
libhazel.RenderDrawLine(p1.x, p1.y, p2.x, p2.y)
end,
---@param x number
---@param y number
---@param w number
---@param h number
DrawRect = function(x, y, w, h)
libhazel.RenderDrawRect(x, y, w, h)
end,
---@param rect Rect
DrawRectByRect = function(rect)
libhazel.RenderDrawRect(rect.x, rect.y, rect.w, rect.h)
end,
---@param x number
---@param y number
---@param w number
---@param h number
FillRect = function(x, y, w, h)
libhazel.RenderFillRect(x, y, w, h)
end,
---@param rect Rect
FillRectByRect = function(rect)
libhazel.RenderFillRect(rect.x, rect.y, rect.w, rect.h)
end,
---@param texture Texture
---@param srcrect Rect|nil
---@param dstrect Rect|nil
---@param flip number|nil
DrawTexture = function(texture, srcrect, dstrect, flip)
srcrect = srcrect or _M.CreateRect(0, 0, texture.w, texture.h)
local size = _M.GetCanvaSize();
dstrect = dstrect or _M.CreateRect(0, 0, size.x, size.y)
libhazel.RenderDrawTexture(texture.rawPointer,
srcrect.x, srcrect.y, srcrect.w, srcrect.h,
dstrect.x, dstrect.y, dstrect.w, dstrect.h,
flip or Flip.None)
end,
Clear = function()
libhazel.RenderClear()
end
}
_M.Renderer = Renderer
---@class Log
local Log = {}
function Log.Loge(...)
print("[Lua Error]:", ...)
end
function Log.Logw(...)
print("[Lua Warn]:", ...)
end
function Log.Logi(...)
print("[Lua Info]:", ...)
end
_M.Log = Log
---@class TextureStorage
local TextureStorage = {
storage = {}
}
---@param filename string
---@param name string
---@return Texture
function TextureStorage.LoadTexture(filename, name)
local oldTexture = TextureStorage.storage[name]
if oldTexture then
_M.Log.Logw(string.format("%s already exists, delete it", name))
_M.DestroyTexture(oldTexture)
end
local texture = _M.LoadTexture(filename)
TextureStorage.storage[name] = texture
return texture
end
function TextureStorage.ClearAll()
for _, v in pairs(TextureStorage.storage) do
if v then
_M.DestroyTexture(v)
end
end
end
---@return Texture
---@param name string
function TextureStorage.Get(name)
return TextureStorage.storage[name]
end
_M.TextureStorage = TextureStorage
---@class TileSheet
---@field column number
---@field row number
---@field texture Texture
---@field tileWidth number
---@field tileHeight number
local TileSheet = {}
---@return TileSheet
---@param texture Texture
---@param column number
---@param row number
function _M.CreateTileSheet(texture, column, row)
local o = {texture = texture, column = column, row = row, tileWidth = texture.w / column, tileHeight = texture.h / row}
setmetatable(o, {__index = TileSheet})
return o
end
---@param self TileSheet
---@param column number
---@param row number
---@param dstrect Rect
---@param flip number
function TileSheet.Draw(self, column, row, dstrect, flip)
local srcrect = _M.CreateRect(column * self.tileWidth, row * self.tileHeight, self.tileWidth, self.tileHeight)
_M.Renderer.DrawTexture(self.texture, srcrect, dstrect, flip)
end
_M.TileSheet = TileSheet
local Time = { curTime = 0, elapseTime = 0 }
function Time.RecordElapseTime()
local oldTime = Time.curTime
Time.curTime = libhazel.GetTime()
Time.elapseTime = Time.curTime - oldTime
end
---@return number the passed seconds between two frames
function Time.GetElapseTime()
return Time.elapseTime
end
_M.Time = Time
return _M

174
game/main.lua Normal file
View File

@ -0,0 +1,174 @@
local hazel = require "hazel"
local ECS = require "ecs"
local constants = require "constants"
local math = require "math"
local content = require "content"
local vmath = require "vmath"
local function drawCurosr()
hazel.Renderer.SetDrawColor(1, 0, 0, 1)
local pos = hazel.GetGameMousePos()
local halfW = 10
local halfH = 10
hazel.Renderer.DrawRect(pos.x - halfW, pos.y - halfH, halfW * 2, halfH * 2)
hazel.Renderer.DrawLine(pos.x - halfW + 5, pos.y, pos.x + halfW - 5, pos.y)
hazel.Renderer.DrawLine(pos.x, pos.y - halfH + 5, pos.x, pos.y + halfH - 5)
end
local FloorMap = {}
local function generateFloors()
local xNum = math.ceil(hazel.GetCanvaSize().x / constants.TileSize)
local yNum = math.ceil(hazel.GetCanvaSize().y / constants.TileSize)
for x = 0, xNum do
for y = 0, yNum do
FloorMap[y + x * yNum] = math.random() <= constants.StonePutProbability
end
end
end
local function drawFloors()
local xNum = math.ceil(hazel.GetCanvaSize().x / constants.TileSize)
local yNum = math.ceil(hazel.GetCanvaSize().y / constants.TileSize)
local tileIndex = {row = 9, col = 0}
for x = 0, xNum do
for y = 0, yNum do
if FloorMap[y + x * yNum] then
tileIndex.col = 1
else
tileIndex.col = 0
end
content.Tilesheet:Draw(tileIndex.col, tileIndex.row,
hazel.CreateRect(x * constants.TileSize, y * constants.TileSize,
constants.TileSize, constants.TileSize),
hazel.Flip.None)
end
end
end
local function updateBullet()
for k, _ in pairs(content.BulletList) do
---@type Entity
local bullet = content.BulletList[k]
bullet:Update()
---@type Point
local position = bullet:GetComponent(ECS.ComponentType.Transform).position
if position.x + constants.TileSize <= 0 or
position.x >= hazel.GetCanvaSize().x or
position.y + constants.TileSize <= 0 or
position.y >= hazel.GetCanvaSize().y then
content.BulletList[k] = nil
end
end
end
local function updateMonster()
---@param v Entity
for _, v in pairs(content.MonsterList) do
v:Update()
end
end
local function collisionDeal()
---@type Rect
local playerBox = content.PlayerEntity:GetComponent(ECS.ComponentType.ColliBox).rect
---@type Point
local playerPos = content.PlayerEntity:GetComponent(ECS.ComponentType.Transform).position
---@type Rect
local playerColliBox = hazel.CreateRect(playerPos.x + playerBox.x, playerPos.y + playerBox.y, playerBox.w, playerBox.h)
---@type m Entity
for km, _ in pairs(content.MonsterList) do
---@type Entity
local monster = content.MonsterList[km]
---@type Point
local monsterPos = monster:GetComponent(ECS.ComponentType.Transform).position
---@type ColliBoxComponent
local monsterBoxComponent = monster:GetComponent(ECS.ComponentType.ColliBox)
if monsterBoxComponent then
---@type Rect
local monsterBox = monsterBoxComponent.rect
---@type Rect
local monsterColliBox = hazel.CreateRect(monsterPos.x + monsterBox.x, monsterPos.y + monsterBox.y,
monsterBox.w, monsterBox.h)
---@type RolePropComponent
local monsterRoleProp = monster:GetComponent(ECS.ComponentType.RoleProp)
if vmath.IsRectIntersect(playerColliBox, monsterColliBox) then
---@type InvincibleComponent
local playerInvincible = content.PlayerEntity:GetComponent(ECS.ComponentType.Invincible)
if not playerInvincible:IsInvincibleState() then
---@type RolePropComponent
local playerRoleProp = content.PlayerEntity:GetComponent(ECS.ComponentType.RoleProp)
playerRoleProp.hp = playerRoleProp.hp - monsterRoleProp.damage
if playerRoleProp:IsDie() then
content.PlayerEntity:RemoveComponent(ECS.ComponentType.Controller)
content.PlayerEntity:RemoveComponent(ECS.ComponentType.Gun)
content.PlayerEntity:RemoveComponent(ECS.ComponentType.Direction)
content.PlayerEntity:RemoveComponent(ECS.ComponentType.HpShow)
---@type ImageComponent
local image = content.PlayerEntity:GetComponent(ECS.ComponentType.Image)
image.row = 4
image.col = 0
else
playerInvincible:IntoInvincible()
end
end
end
for kb, _ in pairs(content.BulletList) do
---@type Entity
local bullet = content.BulletList[kb]
---@type Point
local bulletPos = bullet:GetComponent(ECS.ComponentType.Transform).position
---@type Rect
local bulletBox = bullet:GetComponent(ECS.ComponentType.ColliBox).rect
---@type Rect
local bulletColliBox = hazel.CreateRect(bulletPos.x + bulletBox.x, bulletPos.y + bulletBox.y, bulletBox.w, bulletBox.h)
if vmath.IsRectIntersect(bulletColliBox, monsterColliBox) then
content.BulletList[kb] = nil
local damage = bullet:GetComponent(ECS.ComponentType.Bullet).damage
monsterRoleProp.hp = monsterRoleProp.hp - damage
if monsterRoleProp:IsDie() then
monster:RemoveComponent(ECS.ComponentType.Direction)
monster:RemoveComponent(ECS.ComponentType.AI)
monster:RemoveComponent(ECS.ComponentType.HpShow)
monster:RemoveComponent(ECS.ComponentType.ColliBox)
---@type ImageComponent
local image = monster:GetComponent(ECS.ComponentType.Image)
image.row = 4
image.col = 2
end
end
end
end
end
end
function GameStart()
hazel.SetWindowIcon("resources/icon.png")
content.Texture = hazel.LoadTexture("resources/tilesheet.png")
content.Tilesheet = hazel.CreateTileSheet(content.Texture, 3, 10)
content.PlayerEntity = ECS.CreatePlayer(hazel.CreatePos(constants.TileSize * 10, constants.TileSize * 10))
table.insert(content.MonsterList, ECS.CreateMonster(hazel.CreatePos(500, 500)))
hazel.HideCursor()
generateFloors()
end
function GameLoop()
hazel.Time.RecordElapseTime()
hazel.Renderer.SetClearColor(0, 0, 0, 1)
hazel.Renderer.Clear()
drawFloors()
updateMonster()
content.PlayerEntity:Update()
updateBullet()
collisionDeal()
drawCurosr()
end
function GameQuit()
hazel.ShowCursor()
hazel.DestroyTexture(content.Texture)
end

BIN
game/resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

28
game/vmath.lua Normal file
View File

@ -0,0 +1,28 @@
local math = require "math"
local hazel = require "hazel"
local _M = {}
---@return number
---@param p Point
function _M.Len(p)
return math.sqrt(p.x * p.x + p.y * p.y)
end
---@return Point
---@param p
function _M.Normalize(p)
local l = _M.Len(p)
return hazel.CreatePos(p.x / l, p.y / l)
end
---@return boolean
---@param r1 Rect
---@param r2 Rect
function _M.IsRectIntersect(r1, r2)
return not (r1.x + r1.w < r2.x or
r2.x + r2.w < r1.x or
r2.y + r2.h < r1.y or
r1.y + r1.h < r2.y)
end
return _M