-
Notifications
You must be signed in to change notification settings - Fork 0
/
Deck.adept
317 lines (262 loc) · 15.1 KB
/
Deck.adept
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
import 'main.adept'
import 'Card.adept'
import 'parse.adept'
DECK_FILENAME == "deck.txt"
struct Deck (
blueprints <CardBlueprint> List,
num_creature_cards usize
) {
func load successful {
this.blueprints.clear()
filename String = where() + "assets/" + DECK_FILENAME
filename_cstr *ubyte = filename.cstr()
defer delete filename_cstr
f *FILE = fopen(filename_cstr, 'r')
unless f, return false
defer fclose(f)
buffer_size usize = 1024
buffer *ubyte = new ubyte * 1024
defer delete buffer
entry String
details <String> List
while fgets(buffer, buffer_size, f) != null {
// Strip off newline if it's at the end of the buffer
if buffer[strlen(buffer) - 1] == '\n'ub, buffer[strlen(buffer) - 1] = 0x00
if buffer[0] == ' 'ub {
details.add(stringConstant(buffer).trimmed())
} else {
unless entry == "", this.loadCard(entry, details)
entry = string(buffer)
entry.forceIdentifierLike()
details.clear()
}
}
if !entry.empty() && !this.loadCard(entry, details), return false
return true
}
func loadCard(name String, details <String> List) successful {
title String = name
description String = ""
kind CardKind = CardKind::CREATURE
cost int = 0
hp int = 0
attack int = 0
offense_type Offense
defense_type Defense
ct CardTextures
traits CardTraits
traits.defaults()
key StringView
each String in details {
if isMarkerDetail(it, &key) {
if key == "creature", kind = CardKind::CREATURE
else if key == "spell", kind = CardKind::SPELL
else if key == "stat", kind = CardKind::STAT
else if key == "trap", kind = CardKind::TRAP; traits.activation.defaultUnlessSet()
else if key == "developer", traits.developer = true
else if key == "on-fire", traits.on_fire = true
else if key == "fire-resistant", traits.fire_resistant = true
else if key == "invulnerable-to-blunt", traits.invulnerable_to_blunt = true
else if key == "must-be-attacked-last", traits.must_be_attacked_last = true
else if key == "must-be-attacked-first", traits.must_be_attacked_first = true
else if key == "random-victim", traits.random_victim = true
else if key == "rage", traits.rage = true
else if key == "can-heal", traits.can_heal = true
else if key == "attack-equals-hp", traits.attack_equals_hp = true
else if key == "counter-attack", traits.counter_attack = true
else if key == "ignores-counter-attack", traits.ignores_counter_attack = true
else if key == "cutting", traits.cutting = true
else if key == "passive-cutting", traits.passive_cutting = true
else if key == "bleeding", traits.bleeding = true
else if key == "unhealable", traits.unhealable = true
else if key == "biting", traits.biting = true
else if key == "eternal-fire", traits.eternal_fire = true
else if key == "ignores-attack-order", traits.ignores_attack_order = true
else if key == "only-heal", traits.only_heal = true
else if key == "requires-target", traits.requires_target = true
else if key == "insta-kill", traits.insta_kill = true
else if key == "applies-on-fire", traits.applies_on_fire = true
else if key == "applies-rage", traits.applies_rage = true
else if key == "urgent", traits.urgent = true
else if key == "transform", traits.transform = true
else if key == "gives-eternal-fire", traits.gives_eternal_fire = true
else if key == "ignore-effect-damage", traits.ignore_effect_damage = true
else if key == "reinforced-shields", traits.reinforced_shields = true
else if key == "reflect", traits.reflect = true
else if key == "trip", traits.trip = true
else if key == "ai-none", traits.ai_usage = aiUsage(AIUsageKind::NONE)
else if key == "ai-offensive", traits.ai_usage = aiUsage(AIUsageKind::OFFENSIVE)
else if key == "ai-defensive", traits.ai_usage = aiUsage(AIUsageKind::DEFENSIVE)
else {
print("WARNING: Unrecognized card marker trait '%'" % key)
}
} else if isStringDetail(it, "title", def new_title StringView) {
// Cut off anything after the second line
if new_title.length > 26, new_title = new_title.span(0, 26)
// Attempt to seperate into two lines if needed
if new_title.length > 13 {
middle long = cast long (new_title.length / 2)
space_closest_to_middle long = -1
each ubyte in static new_title, if it == ' 'ub && (space_closest_to_middle == -1 || labs(idx as long - middle) < labs(space_closest_to_middle - middle)),
space_closest_to_middle = idx as long
if space_closest_to_middle != -1 {
new_title.make()
new_title.array[space_closest_to_middle as usize] = '\n'ub
} else {
new_title = new_title.span(0, 13)
}
}
title = new_title.clone()
} else if isStringDetail(it, "description", def new_description StringView) {
// Wrap description into multiple lines (16 characters max per line) (6 lines max)
max_characters usize = 16 * 6
if new_description.length > max_characters, new_description = new_description.span(0, max_characters)
new_description.make()
since_last_newline long = 0
each ubyte in static new_description {
since_last_newline++
if since_last_newline > 16 {
range_since String = new_description.range(idx - (since_last_newline - 1), idx + 1)
last_space long = range_since.last(' 'ub)
// Overwrite best space character if available
if last_space != -1 {
new_description.array[idx as long - (since_last_newline - 1) + last_space] = '\n'ub
since_last_newline = (range_since.length as long - 1) - last_space
} else {
// Overwrite current character with newline if going over
new_description.array[idx] = '\n'ub
since_last_newline = 0
}
}
}
description = new_description.commit()
} else if isStringDetail(it, "texture", def new_texture StringView) {
ct = cardTextures(gamedata.assets_folder + "cards/" + new_texture + "/")
} else if isStringDetail(it, "offense", def new_offense StringView) {
offense_type = offense(new_offense)
} else if isStringDetail(it, "defense", def new_defense StringView) {
defense_type = defense(new_defense)
} else if isStringDetail(it, "usage", def new_usage StringView) {
traits.usage = cardUsage(new_usage)
} else if isStringDetail(it, "activation", def new_activation StringView) {
traits.activation = cardActivation(new_activation)
} else if isIntDetail(it, &key, undef int_value int) {
dest *int = null
if key == "cost", dest = &cost
else if key == "hp", dest = &hp
else if key == "attack", dest = &attack
else if key == "battlecry-adjacent", dest = &traits.battlecry_adjacent
else if key == "shield", dest = &traits.shield
else if key == "spreads-fire", dest = &traits.spreads_fire
else if key == "resistant-over", dest = &traits.resistant_over
else if key == "gains-hp", dest = &traits.gains_hp
else if key == "gains-attack", dest = &traits.gains_attack
else if key == "endure", dest = &traits.endure
else if key == "recycle", dest = &traits.recycle
else if key == "burning", dest = &traits.burning
else if key == "flimsy", dest = &traits.flimsy
else if key == "resistance", dest = &traits.resistance
else if key == "attacks", dest = &traits.attacks
else if key == "refund", dest = &traits.refund
else if key == "adjacent-heal", dest = &traits.adjacent_heal
else if key == "revive", dest = &traits.revive
else if key == "passive-burning", dest = &traits.passive_burning
else if key == "deathcry-adjacent", dest = &traits.deathcry_adjacent
else if key == "raise-attack", dest = &traits.raise_attack
else if key == "applies-shield", dest = &traits.applies_shield
else if key == "draw-immediately", dest = &traits.draw_immediately
else if key == "everybody-dance-now", dest = &traits.everybody_dance_now
else if key == "applies-healing", dest = &traits.applies_healing
else if key == "applies-attack", dest = &traits.applies_attack
else if key == "everybody-burn-now", dest = &traits.everybody_burn_now
else if key == "steal-random", dest = &traits.steal_random
else if key == "super", dest = &traits.super
else if key == "thrift", dest = &traits.thrift
else if key == "draw-cost-reduction", dest = &traits.draw_cost_reduction
else if key == "applies-resistance", dest = &traits.applies_resistance
else if key == "cause-burning", dest = &traits.cause_burning
else if key == "gives-mana", dest = &traits.gives_mana
else if key == "expires", dest = &traits.expires
if dest {
*dest = int_value
} else {
print("WARNING: Unrecognized card integer trait '%'" % key)
}
} else {
print("WARNING: Unrecognized card trait '%'" % it)
}
}
// Default to texture named <entry> except with dashes replaced with underscores
if ct.portrait == null, ct = cardTextures(gamedata.assets_folder + "cards/" + name + "/")
// Force valid texture if something went wrong
ct.forceValid()
// Warn of undefined activation for traps
if kind == CardKind::TRAP && traits.activation.kind == CardActivationKind::NONE,
print("WARNING: trap '%' has no activation event" % name)
// Warn of undefined ai usage for spells
if kind == CardKind::SPELL && traits.ai_usage.kind == AIUsageKind::UNDEFINED,
print("WARNING: spell '%' has undefined AI usage" % name)
unless this.blueprints.addBlueprint(name, title, description, kind, cost, hp, attack, offense_type, defense_type, &ct, &traits),
return false
if kind == CardKind::CREATURE,
this.num_creature_cards++
return true
}
func randomCard CardInstance {
if this.blueprints.length == 0 {
null_card_instance POD CardInstance
printf('ERROR: Deck.randomCard() has no blueprints to choose from\n')
return null_card_instance
}
return cardInstance(this.blueprints.getPointer(random(this.blueprints.length)))
}
func randomCreatureBlueprintExcluding(excluded_name String, randomness ulong) *CardBlueprint {
if this.num_creature_cards < 2,
print("ERROR: Deck.randomCreatureBlueprintExcluding() failed, not enough creature cards exist\n")
creature_index usize = randomness % (this.num_creature_cards - 1)
each CardBlueprint in static this.blueprints {
if it.kind != CardKind::CREATURE, continue
if it.name == excluded_name, continue
if creature_index-- == 0, return this.blueprints.getPointer(idx)
}
print("ERROR: Deck.randomCreatureBlueprintExcluding() failed\n")
return null
}
func getCardByName(name String) CardInstance {
blueprint *CardBlueprint = this.getCardBlueprint(name)
unless blueprint, print("Deck.getCardByName() failed to find card '%'" % name); return this.randomCard()
return cardInstance(blueprint)
}
func getCardBlueprint(name String) *CardBlueprint {
each CardBlueprint in this.blueprints, if it.name == name, return &it
return null
}
func print {
each CardBlueprint in this.blueprints, it.print()
}
}
func isMarkerDetail(detail String, out key *StringView) bool {
if detail.count(' 'ub) != 0, return false
unless detail.isNameLike(), return false
*key = detail.reference()
return true
}
func isIntDetail(detail String, out key *StringView, out value *int) bool {
if detail.count(' 'ub) != 1, return false
space long = detail.first(' 'ub)
before StringView = detail.range(0, space)
after StringView = detail.range(space + 1, detail.length)
unless after.isIntegerLike(), return false
*key = before
*value = after.toInt()
return true
}
func isStringDetail(detail String, key String, out value *StringView) bool {
if detail.count(' 'ub) == 0, return false
space long = detail.first(' 'ub)
before StringView = detail.range(0, space)
after StringView = detail.range(space + 1, detail.length)
unless before == key, return false
*value = after
return true
}