blob: bf0ef65780bdfb1b62a74fb861298470ef3b8199 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
namespace LivingVillage.Desktop
open LivingVillage.Kernel
open LivingVillage.Kernel.Sim
module WorldBootstrap =
let private tileCenter (x: int) (y: int) : Vec2 =
{ X = float32 (x * Sim.tilePixels + Sim.tilePixels / 2)
Y = float32 (y * Sim.tilePixels + Sim.tilePixels / 2) }
let private npcSpawnTiles : (int * int) array =
[| for y in [ 14; 18; 28; 32; 36; 40 ] do
for x in [ 24; 28; 32; 36; 40 ] do
yield x, y |]
let private avatarSpawnPosition = tileCenter 31 25
let private daylightStartTick = 6L * Sim.ticksPerDay / 24L
/// 新档职业状态:按世界 seed 与职业种子生成 nowTick 所属日的 Offered 任务(design §3)。
let occupationStateFor (seed: uint64) (kind: Occupation.Kind) (nowTick: int64) : Occupation.State =
let baseState = Occupation.stateOf kind
{ baseState with
Today = Occupation.dailyTaskOf seed (Occupation.occupationSeedOf kind) (Occupation.dayIndexOf nowTick) kind }
/// 跨日刷新当日任务;同日(含 Done/Failed 终态)原样保留。
let refreshOccupationToday (seed: uint64) (state: Occupation.State) (nowTick: int64) : Occupation.State =
{ state with
Today =
Occupation.refreshToday
seed
(Occupation.occupationSeedOf state.Profile.Kind)
state.Profile.Kind
nowTick
state.Today }
/// 以显式站位建世界:avatarTile 为玩家出生瓦片,npcTiles 为 NPC 出生瓦片序列。
/// 64x48 旧档与世界走 `avatarSpawnPosition` + `npcSpawnTiles` 的等价调用,字节口径不变。
let initialWorldWithPlacement
(startAtDaylight: bool)
(seed: uint64)
(count: int)
(avatarTile: int * int)
(npcTiles: (int * int) list)
(occupation: Occupation.State option) : World =
if count < 0 || count > npcTiles.Length then
invalidArg (nameof count) "Desktop bootstrap supports at most 30 NPCs."
let avatarPosition = tileCenter (fst avatarTile) (snd avatarTile)
let startTick = if startAtDaylight then daylightStartTick else 0L
let baseWorld = Sim.initialWorldN seed count
let avatar =
let baseAvatar =
{ baseWorld.Avatar with
Pos = avatarPosition
Mind = { baseWorld.Avatar.Mind with Target = avatarPosition } }
match occupation with
| None -> baseAvatar
| Some state ->
let needs = baseAvatar.Mind.Needs
{ baseAvatar with
Mind =
{ baseAvatar.Mind with
Needs =
{ needs with
Money = float32 state.Profile.InitialMoney
Energy = float32 state.Profile.InitialEnergy } } }
let npcs =
baseWorld.Npcs
|> Array.mapi (fun index npc ->
let x, y = List.item index npcTiles
{ npc with Pos = tileCenter x y })
{ baseWorld with
Tick = startTick
Time = float startTick * Sim.dtSeconds
Avatar = avatar
Npcs = npcs }
let initialWorldWithOccupation (startAtDaylight: bool) (seed: uint64) (count: int) (occupation: Occupation.State option) : World =
initialWorldWithPlacement
startAtDaylight
seed
count
(31, 25)
(List.ofArray npcSpawnTiles)
occupation
/// P20 第三步:默认可玩世界。生成器的核心可达区作为站位,30 个 spawn 全部落核心可达域。
let initialWorldInMap (startAtDaylight: bool) (seed: uint64) (count: int) (map: MapGen.Result) : World =
let avatarTile = ((map.Core.MinX + map.Core.MaxX) / 2, (map.Core.MinY + map.Core.MaxY) / 2)
initialWorldWithPlacement startAtDaylight seed count avatarTile map.Spawns None
let initialWorldForDaylight (startAtDaylight: bool) (seed: uint64) (count: int) : World =
initialWorldWithOccupation startAtDaylight seed count None
let initialWorld (seed: uint64) (count: int) : World =
initialWorldWithOccupation false seed count None
|