-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Grzegorz Tańczyk
committed
Feb 10, 2024
1 parent
5322e31
commit 05a1c99
Showing
8 changed files
with
322 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import { useCallback, useState } from 'react'; | ||
import { createWorldState } from '../world/world-state-create'; | ||
import { updateWorldState } from '../world/world-state-updates'; | ||
import { GameState, GameStateComponent } from './types'; | ||
import { City, Explosion, Missile, Sector, State, WorldState } from '../world/world-state-types'; | ||
import styled from 'styled-components'; | ||
|
||
const WorldComponent: GameStateComponent = ({ setGameState }) => { | ||
const [worldState, setWorldState] = useState(() => createWorldState()); | ||
const updateWorld = useCallback( | ||
(worldState: WorldState, deltaTime: number) => setWorldState(updateWorldState(worldState, deltaTime)), | ||
[], | ||
); | ||
|
||
return ( | ||
<StateContainer> | ||
<div className="meta-controls"> | ||
<div>Timestamp: {worldState.timestamp}</div> | ||
<div> | ||
<button onClick={() => updateWorld(worldState, 1)}>+1 Second</button> | ||
<button onClick={() => updateWorld(worldState, 10)}>+10 Seconds</button> | ||
<button onClick={() => updateWorld(worldState, 60)}>+60 seconds</button> | ||
</div> | ||
</div> | ||
<WorldStateRender state={worldState} /> | ||
</StateContainer> | ||
); | ||
}; | ||
|
||
function WorldStateRender({ state }: { state: WorldState }) { | ||
return ( | ||
<div className="world-render"> | ||
{state.sectors.map((sector) => ( | ||
<SectorRender key={sector.id} sector={sector} /> | ||
))} | ||
{state.states.map((state) => ( | ||
<StateRender key={state.id} state={state} /> | ||
))} | ||
{state.cities.map((city) => ( | ||
<CityRender key={city.id} city={city} /> | ||
))} | ||
{state.missiles.map((missile) => ( | ||
<MissileRender key={missile.id} missile={missile} worldTimestamp={state.timestamp} /> | ||
))} | ||
{state.explosions.map((explosion) => ( | ||
<ExplosionRender key={explosion.id} explosion={explosion} worldTimestamp={state.timestamp} /> | ||
))} | ||
</div> | ||
); | ||
} | ||
|
||
function StateRender({ state }: { state: State }) { | ||
return <div className="state-render"></div>; | ||
} | ||
|
||
function CityRender({ city }: { city: City }) { | ||
return <div className="city-render" style={{} as React.CSSProperties}></div>; | ||
} | ||
|
||
function MissileRender({ missile, worldTimestamp }: { missile: Missile; worldTimestamp: number }) { | ||
const progress = Math.min( | ||
Math.max(0, (worldTimestamp - missile.launchTimestamp) / (missile.targetTimestamp - missile.launchTimestamp)), | ||
1, | ||
); | ||
|
||
const x = missile.launch.x + (missile.target.x - missile.launch.x) * progress; | ||
const y = missile.launch.y + (missile.target.y - missile.launch.y) * progress; | ||
|
||
return ( | ||
<div | ||
className="missile-render" | ||
style={ | ||
{ | ||
'--x': x, | ||
'--y': y, | ||
} as React.CSSProperties | ||
} | ||
></div> | ||
); | ||
} | ||
|
||
function ExplosionRender({ explosion, worldTimestamp }: { explosion: Explosion; worldTimestamp: number }) { | ||
if (explosion.startTimestamp > worldTimestamp || explosion.endTimestamp < worldTimestamp) { | ||
return null; | ||
} | ||
|
||
const progress = Math.min( | ||
Math.max(0, (worldTimestamp - explosion.startTimestamp) / (explosion.endTimestamp - explosion.startTimestamp)), | ||
1, | ||
); | ||
|
||
return ( | ||
<div | ||
className="explosion-render" | ||
style={ | ||
{ | ||
'--x': explosion.position.x, | ||
'--y': explosion.position.y, | ||
'--radius': explosion.radius * progress, | ||
} as React.CSSProperties | ||
} | ||
></div> | ||
); | ||
} | ||
|
||
function SectorRender({ sector }: { sector: Sector }) { | ||
return ( | ||
<div | ||
className="sector-render" | ||
data-sector-type={sector.type} | ||
style={ | ||
{ | ||
'--x': sector.rect.left, | ||
'--y': sector.rect.top, | ||
'--width': sector.rect.right - sector.rect.left, | ||
'--height': sector.rect.bottom - sector.rect.top, | ||
} as React.CSSProperties | ||
} | ||
></div> | ||
); | ||
} | ||
|
||
const StateContainer = styled.div` | ||
position: absolute; | ||
left: 0; | ||
top: 0; | ||
width: 100%; | ||
height: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
background: black; | ||
.meta-controls { | ||
display: flex; | ||
flex-grow: 0; | ||
border: 1px solid rgb(0, 255, 0); | ||
padding: 5px 10px; | ||
text-align: left; | ||
color: white; | ||
z-index: 1; | ||
} | ||
.world-render { | ||
display: flex; | ||
flex-grow: 1; | ||
background: black; | ||
.state-render { | ||
} | ||
.sector-render { | ||
transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)); | ||
position: absolute; | ||
width: calc(var(--width) * 1px); | ||
height: calc(var(--height) * 1px); | ||
&[data-sector-type='GROUND'] { | ||
background: rgb(93, 42, 0); | ||
} | ||
&[data-sector-type='WATER'] { | ||
background: rgb(0, 34, 93); | ||
} | ||
} | ||
.missile-render { | ||
transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)); | ||
position: absolute; | ||
width: 1px; | ||
height: 1px; | ||
background: rgb(0, 255, 0); | ||
} | ||
.explosion-render { | ||
transform: translate(calc(var(--x) * 1px), calc(var(--y) * 1px)) translate(-50%, -50%); | ||
position: absolute; | ||
width: calc(var(--radius) * 1px); | ||
height: calc(var(--radius) * 1px); | ||
border-radius: 50%; | ||
background: rgb(255, 255, 255); | ||
} | ||
} | ||
`; | ||
|
||
export const GameStateTechWorld: GameState = { | ||
Component: WorldComponent, | ||
path: '/tech-world', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export function distance(ax: number, ay: number, bx: number, by: number) { | ||
return Math.sqrt(Math.pow(bx - ax, 2) + Math.pow(by - ay, 2)); | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { distance } from '../math/position-utils'; | ||
import { SectorType, WorldState } from './world-state-types'; | ||
|
||
export function createWorldState(): WorldState { | ||
const result: WorldState = { | ||
timestamp: 0, | ||
states: [ | ||
{ | ||
id: 'test-state', | ||
name: 'Test', | ||
}, | ||
], | ||
cities: [], | ||
launchSites: [], | ||
missiles: [ | ||
{ | ||
id: 'test-missile', | ||
launch: { x: 100, y: 100 }, | ||
launchTimestamp: 0, | ||
target: { x: 200, y: 200 }, | ||
targetTimestamp: 10, | ||
}, | ||
], | ||
explosions: [ | ||
{ | ||
id: 'test-explosion', | ||
startTimestamp: 10, | ||
endTimestamp: 15, | ||
position: { x: 200, y: 200 }, | ||
radius: 30, | ||
}, | ||
], | ||
sectors: generateSectors(128, 50), | ||
}; | ||
|
||
return result; | ||
} | ||
|
||
function generateSectors(sectorCount: number, sectorSize: number) { | ||
const cols = Math.floor(Math.sqrt(sectorCount)); | ||
const rows = Math.floor(sectorCount / cols); | ||
|
||
const centerColX = cols / 2; | ||
const centerRowY = rows / 2; | ||
|
||
return Array.from({ length: sectorCount }).map((v, i) => { | ||
const x = i % cols; | ||
const y = Math.floor(i / rows); | ||
|
||
return { | ||
id: 'test-sector-' + i, | ||
type: distance(x, y, centerColX, centerRowY) <= centerColX ? SectorType.GROUND : SectorType.WATER, | ||
rect: { | ||
left: x * sectorSize, | ||
top: y * sectorSize, | ||
right: x * sectorSize + sectorSize, | ||
bottom: y * sectorSize + sectorSize, | ||
}, | ||
}; | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,70 @@ | ||
export type Position = { x: Number; y: Number; z: Number }; | ||
export type Position = { x: number; y: number }; | ||
|
||
export type Rect = { left: number; top: number; right: number; bottom: number }; | ||
|
||
export type StateId = string; | ||
|
||
export type CityId = string; | ||
|
||
export type MissileId = string; | ||
|
||
export type SectorId = string; | ||
|
||
export type ExplosionId = string; | ||
|
||
export type State = { | ||
id: string; | ||
id: StateId; | ||
name: string; | ||
border: Position[]; | ||
}; | ||
|
||
export type City = { | ||
id: string; | ||
id: CityId; | ||
stateId: StateId; | ||
name: string; | ||
position: Position; | ||
size: Number; | ||
}; | ||
|
||
export enum SectorType { | ||
WATER = 'WATER', | ||
GROUND = 'GROUND', | ||
} | ||
|
||
export type Sector = { | ||
id: SectorId; | ||
rect: Rect; | ||
type: SectorType; | ||
}; | ||
|
||
export type LaunchSite = { | ||
position: Position; | ||
stateId?: StateId; | ||
}; | ||
|
||
export type Missile = { | ||
startTimestamp: Number; | ||
start: Position; | ||
position: Position; | ||
id: MissileId; | ||
launch: Position; | ||
launchTimestamp: number; | ||
|
||
target: Position; | ||
targetTimestamp: number; | ||
}; | ||
|
||
export type Explosion = { | ||
startTimestamp: Number; | ||
id: ExplosionId; | ||
startTimestamp: number; | ||
endTimestamp: number; | ||
position: Position; | ||
radius: number; | ||
}; | ||
|
||
export type WorldState = { | ||
timestamp: Number; | ||
timestamp: number; | ||
|
||
states: State[]; | ||
cities: City[]; | ||
launchSites: LaunchSite[]; | ||
|
||
sectors: Sector[]; | ||
|
||
missiles: Missile[]; | ||
explosion: Explosion[]; | ||
explosions: Explosion[]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { WorldState, Missile } from './world-state-types'; | ||
|
||
export function updateWorldState(state: WorldState, deltaTime: number): WorldState { | ||
const worldTimestamp = state.timestamp + deltaTime; | ||
|
||
const result: WorldState = { | ||
timestamp: worldTimestamp, | ||
states: state.states, | ||
cities: state.cities, | ||
launchSites: state.launchSites, | ||
missiles: state.missiles, | ||
explosions: state.explosions, | ||
sectors: state.sectors, | ||
}; | ||
|
||
return result; | ||
} |