mirror of
https://github.com/AndorsTrailRelease/andors-trail.git
synced 2026-01-07 03:57:08 +01:00
Compare commits
15 Commits
v0.8.13
...
AT_refacto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4633c0689 | ||
|
|
9b9d8a7c3a | ||
|
|
0cfa6a97c9 | ||
|
|
9729d5a34f | ||
|
|
c024474128 | ||
|
|
3100ffd717 | ||
|
|
e3c8c0ecfb | ||
|
|
cd35ad673f | ||
|
|
5f000ef210 | ||
|
|
2078eedb94 | ||
|
|
6f2ba0fe7a | ||
|
|
06f1a791e3 | ||
|
|
b341d7e6f1 | ||
|
|
31f25a963f | ||
|
|
30030031b2 |
@@ -3,8 +3,8 @@
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gpl.rpg.AndorsTrail"
|
||||
android:versionCode="80"
|
||||
android:versionName="0.8.13"
|
||||
android:versionCode="81"
|
||||
android:versionName="0.8.14dev"
|
||||
android:installLocation="auto"
|
||||
>
|
||||
|
||||
|
||||
@@ -26,13 +26,13 @@ public final class AndorsTrailApplication extends Application {
|
||||
public static final boolean DEVELOPMENT_FORCE_CONTINUEGAME = false;
|
||||
public static final boolean DEVELOPMENT_DEBUGBUTTONS = false;
|
||||
public static final boolean DEVELOPMENT_FASTSPEED = false;
|
||||
public static final boolean DEVELOPMENT_VALIDATEDATA = false;
|
||||
public static final boolean DEVELOPMENT_DEBUGMESSAGES = false;
|
||||
public static final String CURRENT_VERSION_DISPLAY = "0.8.13";
|
||||
public static final boolean DEVELOPMENT_VALIDATEDATA = true;
|
||||
public static final boolean DEVELOPMENT_DEBUGMESSAGES = true;
|
||||
public static final String CURRENT_VERSION_DISPLAY = "0.8.14dev";
|
||||
public static final boolean IS_RELEASE_VERSION = !CURRENT_VERSION_DISPLAY.matches(".*[a-d].*");
|
||||
public static final boolean DEVELOPMENT_INCOMPATIBLE_SAVEGAMES = DEVELOPMENT_DEBUGRESOURCES || DEVELOPMENT_DEBUGBUTTONS || DEVELOPMENT_FASTSPEED || !IS_RELEASE_VERSION;
|
||||
public static final int DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION = 999;
|
||||
public static final int CURRENT_VERSION = DEVELOPMENT_INCOMPATIBLE_SAVEGAMES ? DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION : 80;
|
||||
public static final int CURRENT_VERSION = DEVELOPMENT_INCOMPATIBLE_SAVEGAMES ? DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION : 81;
|
||||
|
||||
private final AndorsTrailPreferences preferences = new AndorsTrailPreferences();
|
||||
private WorldContext world = new WorldContext();
|
||||
|
||||
@@ -67,8 +67,10 @@ public final class ItemInfoActivity extends AndorsTrailBaseActivity {
|
||||
itemType.effects_equip,
|
||||
itemType.effects_use == null ? null : Collections.singletonList(itemType.effects_use),
|
||||
itemType.effects_hit == null ? null : Collections.singletonList(itemType.effects_hit),
|
||||
itemType.effects_miss == null ? null : Collections.singletonList(itemType.effects_miss),
|
||||
itemType.effects_kill == null ? null : Collections.singletonList(itemType.effects_kill),
|
||||
itemType.effects_hitReceived == null ? null : Collections.singletonList(itemType.effects_hitReceived),
|
||||
itemType.effects_missReceived == null ? null : Collections.singletonList(itemType.effects_missReceived),
|
||||
null,
|
||||
itemType.isWeapon()
|
||||
);
|
||||
|
||||
@@ -90,8 +90,10 @@ public final class MonsterInfoActivity extends AndorsTrailBaseActivity {
|
||||
null,
|
||||
null,
|
||||
monster.getOnHitEffectsAsList(),
|
||||
monster.getOnMissEffectsAsList(),
|
||||
null,
|
||||
monster.getOnHitReceivedEffectsAsList(),
|
||||
monster.getOnMissReceivedEffectsAsList(),
|
||||
monster.getOnDeathEffects(),
|
||||
false);
|
||||
hp.update(monster.getMaxHP(), monster.getCurrentHP());
|
||||
|
||||
@@ -183,19 +183,25 @@ public final class HeroinfoActivity_Stats extends Fragment {
|
||||
);
|
||||
|
||||
ArrayList<ItemTraits_OnUse> effects_hit = new ArrayList<ItemTraits_OnUse>();
|
||||
ArrayList<ItemTraits_OnUse> effects_miss = new ArrayList<ItemTraits_OnUse>();
|
||||
ArrayList<ItemTraits_OnUse> effects_kill = new ArrayList<ItemTraits_OnUse>();
|
||||
ArrayList<ItemTraits_OnHitReceived> effects_hitReceived = new ArrayList<ItemTraits_OnHitReceived>();
|
||||
ArrayList<ItemTraits_OnHitReceived> effects_missReceived = new ArrayList<ItemTraits_OnHitReceived>();
|
||||
for (Inventory.WearSlot slot : Inventory.WearSlot.values()) {
|
||||
ItemType type = player.inventory.getItemTypeInWearSlot(slot);
|
||||
if (type == null) continue;
|
||||
if (type.effects_hit != null) effects_hit.add(type.effects_hit);
|
||||
if (type.effects_miss != null) effects_miss.add(type.effects_miss);
|
||||
if (type.effects_kill != null) effects_kill.add(type.effects_kill);
|
||||
if (type.effects_hitReceived != null) effects_hitReceived.add(type.effects_hitReceived);
|
||||
if (type.effects_missReceived != null) effects_missReceived.add(type.effects_missReceived);
|
||||
}
|
||||
if (effects_hit.isEmpty()) effects_hit = null;
|
||||
if (effects_miss.isEmpty()) effects_miss = null;
|
||||
if (effects_kill.isEmpty()) effects_kill = null;
|
||||
if (effects_hitReceived.isEmpty()) effects_hitReceived = null;
|
||||
actorinfo_onhiteffects.update(null, null, effects_hit, effects_kill, effects_hitReceived, null, false);
|
||||
if (effects_missReceived.isEmpty()) effects_missReceived = null;
|
||||
actorinfo_onhiteffects.update(null, null, effects_hit, effects_miss, effects_kill, effects_hitReceived,effects_missReceived, null, false);
|
||||
|
||||
|
||||
updateStatsTableRow(world.model.statistics.getNumberOfCompletedQuests(world), R.id.heroinfo_gamestats_quests, R.id.heroinfo_gamestats_quests_row);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.gpl.rpg.AndorsTrail.context;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.model.ModelContainer;
|
||||
import com.gpl.rpg.AndorsTrail.model.ability.ActorConditionTypeCollection;
|
||||
import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection;
|
||||
@@ -13,6 +14,8 @@ import com.gpl.rpg.AndorsTrail.resource.ConversationLoader;
|
||||
import com.gpl.rpg.AndorsTrail.resource.VisualEffectCollection;
|
||||
import com.gpl.rpg.AndorsTrail.resource.tiles.TileManager;
|
||||
|
||||
import java.security.DigestException;
|
||||
|
||||
public final class WorldContext {
|
||||
//Objectcollections
|
||||
public final ConversationLoader conversationLoader;
|
||||
@@ -62,4 +65,11 @@ public final class WorldContext {
|
||||
public void resetForNewGame() {
|
||||
maps.resetForNewGame();
|
||||
}
|
||||
|
||||
public byte[] getChecksum() throws DigestException {
|
||||
ChecksumBuilder checksumBuilder = new ChecksumBuilder();
|
||||
model.addToChecksum(checksumBuilder);
|
||||
maps.addToChecksum(checksumBuilder, this);
|
||||
return checksumBuilder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +595,10 @@ public final class CombatController implements VisualEffectCompletedCallback {
|
||||
|
||||
private AttackResult attack(final Actor attacker, final Actor target) {
|
||||
int hitChance = getAttackHitChance(attacker, target);
|
||||
if (!Constants.roll100(hitChance)) return AttackResult.MISS;
|
||||
if (!Constants.roll100(hitChance)) {
|
||||
applyAttackMissStatusEffects(attacker, target);
|
||||
return AttackResult.MISS;
|
||||
}
|
||||
|
||||
int damage = Constants.rollValue(attacker.getDamagePotential());
|
||||
boolean isCriticalHit = false;
|
||||
@@ -629,6 +632,21 @@ public final class CombatController implements VisualEffectCompletedCallback {
|
||||
}
|
||||
}
|
||||
|
||||
private void applyAttackMissStatusEffects(Actor attacker, Actor target) {
|
||||
ItemTraits_OnUse[] onMissEffects = attacker.getOnMissEffects();
|
||||
ItemTraits_OnHitReceived[] onMissReceivedEffects = target.getOnMissReceivedEffects();
|
||||
if (onMissEffects != null) {
|
||||
for (ItemTraits_OnUse e : onMissEffects) {
|
||||
controllers.actorStatsController.applyUseEffect(attacker, target, e);
|
||||
}
|
||||
}
|
||||
if (onMissReceivedEffects != null) {
|
||||
for (ItemTraits_OnHitReceived e : onMissReceivedEffects) {
|
||||
controllers.actorStatsController.applyHitReceivedEffect(target, attacker, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void endOfCombatRound() {
|
||||
world.model.worldData.tickWorldTime();
|
||||
controllers.gameRoundController.resetRoundTimers();
|
||||
|
||||
@@ -195,19 +195,27 @@ public final class ItemController {
|
||||
|
||||
public static void recalculateHitEffectsFromWornItems(Player player) {
|
||||
ArrayList<ItemTraits_OnUse> effects_onHit = null;
|
||||
ArrayList<ItemTraits_OnUse> effects_onMiss = null;
|
||||
ArrayList<ItemTraits_OnHitReceived> effects_onHitReceived = null;
|
||||
ArrayList<ItemTraits_OnHitReceived> effects_onMissReceived = null;
|
||||
for (Inventory.WearSlot slot : Inventory.WearSlot.values()) {
|
||||
ItemType type = player.inventory.getItemTypeInWearSlot(slot);
|
||||
if (type == null) continue;
|
||||
ItemTraits_OnUse eh = type.effects_hit;
|
||||
ItemTraits_OnHitReceived ehr = type.effects_hitReceived;
|
||||
if (eh == null && ehr == null) continue;
|
||||
ItemTraits_OnUse em = type.effects_miss;
|
||||
ItemTraits_OnHitReceived emr = type.effects_missReceived;
|
||||
if (eh == null && ehr == null && em == null && emr == null) continue;
|
||||
|
||||
if (effects_onHit == null) effects_onHit = new ArrayList<ItemTraits_OnUse>();
|
||||
if (eh != null) effects_onHit.add(eh);
|
||||
if (effects_onMiss == null) effects_onMiss = new ArrayList<ItemTraits_OnUse>();
|
||||
if (em != null) effects_onMiss.add(em);
|
||||
|
||||
if (effects_onHitReceived == null) effects_onHitReceived = new ArrayList<ItemTraits_OnHitReceived>();
|
||||
if (ehr != null) effects_onHitReceived.add(ehr);
|
||||
if (effects_onMissReceived == null) effects_onMissReceived = new ArrayList<ItemTraits_OnHitReceived>();
|
||||
if (emr != null) effects_onMissReceived.add(emr);
|
||||
}
|
||||
|
||||
if (effects_onHit != null) {
|
||||
@@ -217,6 +225,13 @@ public final class ItemController {
|
||||
} else {
|
||||
player.onHitEffects = null;
|
||||
}
|
||||
if (effects_onMiss != null) {
|
||||
ItemTraits_OnUse[] effects_ = new ItemTraits_OnUse[effects_onMiss.size()];
|
||||
effects_ = effects_onMiss.toArray(effects_);
|
||||
player.onMissEffects = effects_;
|
||||
} else {
|
||||
player.onMissEffects = null;
|
||||
}
|
||||
|
||||
if (effects_onHitReceived != null) {
|
||||
ItemTraits_OnHitReceived[] effects_ = new ItemTraits_OnHitReceived[effects_onHitReceived.size()];
|
||||
@@ -225,6 +240,13 @@ public final class ItemController {
|
||||
} else {
|
||||
player.onHitReceivedEffects = null;
|
||||
}
|
||||
if (effects_onMissReceived != null) {
|
||||
ItemTraits_OnHitReceived[] effects_ = new ItemTraits_OnHitReceived[effects_onMissReceived.size()];
|
||||
effects_ = effects_onMissReceived.toArray(effects_);
|
||||
player.onMissReceivedEffects = effects_;
|
||||
} else {
|
||||
player.onMissReceivedEffects = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void consumeNonItemLoot(Loot loot) {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.gpl.rpg.AndorsTrail.model;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.util.L;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.DigestException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class ChecksumBuilder {
|
||||
|
||||
public static final int CHECKSUM_LENGTH = 32;// 256 bits (depends on the hash algorithm)
|
||||
public static final String CHECKSUM_ALGORITHM = "SHA-256"; //Should be available in all Android versions
|
||||
private ByteBuffer buffer;
|
||||
private final MessageDigest digest;
|
||||
|
||||
private ChecksumBuilder(int initialCapacity, ByteOrder byteOrder) {
|
||||
buffer = ByteBuffer.allocate(initialCapacity);
|
||||
buffer.order(byteOrder);
|
||||
try {
|
||||
digest = MessageDigest.getInstance(CHECKSUM_ALGORITHM); // Or SHA-512 for even stronger hash
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Hash algorithm not found", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ChecksumBuilder(int initialCapacity) {
|
||||
this(initialCapacity, ByteOrder.BIG_ENDIAN); // Default to big-endian
|
||||
}
|
||||
|
||||
public ChecksumBuilder() {
|
||||
this(1024*10); // A reasonable default initial capacity
|
||||
}
|
||||
|
||||
// --- Methods for adding different data types ---
|
||||
|
||||
public ChecksumBuilder add(String value) {
|
||||
if (value != null) {
|
||||
byte[] bytes;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
} else {
|
||||
bytes = value.getBytes();
|
||||
}
|
||||
add(bytes);
|
||||
} else {
|
||||
add(-1); // Use -1 to represent a null string
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChecksumBuilder add(byte[] bytes) {
|
||||
add(bytes.length); // Add length prefix
|
||||
ensureCapacity(bytes.length + 4); // +4 for length prefix (int)
|
||||
buffer.put(bytes);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChecksumBuilder add(boolean value) {
|
||||
ensureCapacity(1);
|
||||
buffer.put(value ? (byte) 1 : (byte) 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChecksumBuilder add(long value) {
|
||||
ensureCapacity(8);
|
||||
buffer.putLong(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChecksumBuilder add(int value) {
|
||||
ensureCapacity(4);
|
||||
buffer.putInt(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChecksumBuilder add(float value) {
|
||||
ensureCapacity(8);
|
||||
buffer.putFloat(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChecksumBuilder add(double value) {
|
||||
ensureCapacity(4);
|
||||
buffer.putDouble(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
// --- Method to finalize and get the checksum ---
|
||||
|
||||
public byte[] build() throws DigestException {
|
||||
buffer.flip(); // Prepare for reading
|
||||
digest.update(buffer);// Only use the actually used part of the buffer
|
||||
buffer.flip(); // Prepare for further writing
|
||||
return digest.digest();
|
||||
}
|
||||
|
||||
|
||||
// --- Utility method to ensure sufficient capacity ---
|
||||
private void ensureCapacity(int required) {
|
||||
if (buffer.remaining() < required) {
|
||||
int newCapacity = Math.max(buffer.capacity() * 2, buffer.capacity() + required);
|
||||
ByteBuffer newBuffer = ByteBuffer.allocate(newCapacity);
|
||||
newBuffer.order(buffer.order());
|
||||
buffer.flip(); // Prepare for reading
|
||||
newBuffer.put(buffer); // Copy existing data
|
||||
buffer = newBuffer; // Assign the new buffer to the field
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.gpl.rpg.AndorsTrail.model;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -22,6 +23,8 @@ import com.gpl.rpg.AndorsTrail.model.quest.Quest;
|
||||
import com.gpl.rpg.AndorsTrail.util.HashMapHelper;
|
||||
|
||||
public final class GameStatistics {
|
||||
private boolean isAlteredSavegame = false;
|
||||
private byte[] checksum = new byte[ChecksumBuilder.CHECKSUM_LENGTH];
|
||||
private int deaths = 0;
|
||||
private final HashMap<String, Integer> killedMonstersByTypeID = new HashMap<String, Integer>();
|
||||
private final HashMap<String, Integer> killedMonstersByName = new HashMap<String, Integer>();
|
||||
@@ -67,6 +70,7 @@ public final class GameStatistics {
|
||||
public boolean hasUnlimitedLives() { return startLives == -1; }
|
||||
|
||||
public int getStartLives() { return startLives; }
|
||||
public boolean getIsAlteredSavegame() { return isAlteredSavegame; }
|
||||
|
||||
public int getLivesLeft() { return hasUnlimitedLives() ? -1 : startLives - deaths; }
|
||||
|
||||
@@ -158,6 +162,18 @@ public final class GameStatistics {
|
||||
}
|
||||
};
|
||||
|
||||
public void setChecksum(byte[] checksum) {
|
||||
if (checksum.length != ChecksumBuilder.CHECKSUM_LENGTH) throw new IllegalArgumentException("Invalid checksum length.");
|
||||
this.checksum = checksum;
|
||||
}
|
||||
|
||||
public boolean compareChecksum(byte[] checksum) {
|
||||
return this.checksum.length == checksum.length && MessageDigest.isEqual(this.checksum, checksum);
|
||||
}
|
||||
|
||||
public void markAsAlteredSavegame() {
|
||||
isAlteredSavegame = true;
|
||||
}
|
||||
|
||||
// ====== PARCELABLE ===================================================================
|
||||
|
||||
@@ -194,6 +210,11 @@ public final class GameStatistics {
|
||||
|
||||
this.startLives = src.readInt();
|
||||
this.unlimitedSaves = src.readBoolean();
|
||||
if (fileversion < 81) return;
|
||||
this.isAlteredSavegame = src.readBoolean();
|
||||
final int checksumLength = src.readInt();
|
||||
this.checksum = new byte[checksumLength];
|
||||
if( src.read(checksum) != checksumLength) throw new IOException("Failed to read full checksum.");
|
||||
}
|
||||
|
||||
public void writeToParcel(DataOutputStream dest) throws IOException {
|
||||
@@ -213,5 +234,29 @@ public final class GameStatistics {
|
||||
dest.writeInt(spentGold);
|
||||
dest.writeInt(startLives);
|
||||
dest.writeBoolean(unlimitedSaves);
|
||||
dest.writeBoolean(isAlteredSavegame);
|
||||
dest.writeInt(checksum.length);
|
||||
dest.write(checksum);
|
||||
}
|
||||
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(deaths);
|
||||
Set<Entry<String, Integer> > set = killedMonstersByTypeID.entrySet();
|
||||
builder.add(set.size());
|
||||
for (Entry<String, Integer> e : set) {
|
||||
builder.add(e.getKey());
|
||||
builder.add(e.getValue());
|
||||
}
|
||||
set = usedItems.entrySet();
|
||||
builder.add(set.size());
|
||||
for (Entry<String, Integer> e : set) {
|
||||
builder.add(e.getKey());
|
||||
builder.add(e.getValue());
|
||||
}
|
||||
builder.add(spentGold);
|
||||
builder.add(startLives);
|
||||
builder.add(unlimitedSaves);
|
||||
builder.add(isAlteredSavegame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,12 @@ public final class InterfaceData {
|
||||
}
|
||||
dest.writeUTF(selectedTabHeroInfo);
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(isMainActivityVisible);
|
||||
builder.add(isInCombat);
|
||||
builder.add(selectedPosition != null);
|
||||
if (selectedPosition != null) selectedPosition.addToChecksum(builder);
|
||||
builder.add(selectedTabHeroInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,4 +49,11 @@ public final class ModelContainer {
|
||||
statistics.writeToParcel(dest);
|
||||
worldData.writeToParcel(dest);
|
||||
}
|
||||
public void addToChecksum(ChecksumBuilder builder){
|
||||
player.addToChecksum(builder);
|
||||
builder.add(currentMaps.map.name);
|
||||
uiSelections.addToChecksum(builder);
|
||||
statistics.addToChecksum(builder);
|
||||
worldData.addToChecksum(builder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,4 +114,13 @@ public final class WorldData {
|
||||
dest.writeLong(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(worldTime);
|
||||
builder.add(timers.size());
|
||||
for(Map.Entry<String, Long> e : timers.entrySet()) {
|
||||
builder.add(e.getKey());
|
||||
builder.add(e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
|
||||
public final class ActorCondition {
|
||||
public static final int MAGNITUDE_REMOVE_ALL = -99;
|
||||
@@ -51,4 +52,11 @@ public final class ActorCondition {
|
||||
dest.writeInt(magnitude);
|
||||
dest.writeInt(duration);
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(conditionType.conditionTypeID);
|
||||
builder.add(magnitude);
|
||||
builder.add(duration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ public class Actor {
|
||||
public int blockChance;
|
||||
public int damageResistance;
|
||||
public ItemTraits_OnUse[] onHitEffects;
|
||||
public ItemTraits_OnUse[] onMissEffects;
|
||||
public ItemTraits_OnHitReceived[] onHitReceivedEffects;
|
||||
public ItemTraits_OnHitReceived[] onMissReceivedEffects;
|
||||
public ItemTraits_OnUse onDeathEffects;
|
||||
public boolean hasVFXRunning = false;
|
||||
public long vfxStartTime = 0;
|
||||
@@ -69,8 +71,12 @@ public class Actor {
|
||||
public int getDamageResistance() { return damageResistance; }
|
||||
public ItemTraits_OnUse[] getOnHitEffects() { return onHitEffects; }
|
||||
public List<ItemTraits_OnUse> getOnHitEffectsAsList() { return onHitEffects == null ? null : Arrays.asList(onHitEffects); }
|
||||
public ItemTraits_OnUse[] getOnMissEffects() { return onMissEffects; }
|
||||
public List<ItemTraits_OnUse> getOnMissEffectsAsList() { return onMissEffects == null ? null : Arrays.asList(onMissEffects); }
|
||||
public ItemTraits_OnHitReceived[] getOnHitReceivedEffects() { return onHitReceivedEffects; }
|
||||
public List<ItemTraits_OnHitReceived> getOnHitReceivedEffectsAsList() { return onHitReceivedEffects == null ? null : Arrays.asList(onHitReceivedEffects); }
|
||||
public ItemTraits_OnHitReceived[] getOnMissReceivedEffects() { return onMissReceivedEffects; }
|
||||
public List<ItemTraits_OnHitReceived> getOnMissReceivedEffectsAsList() { return onMissReceivedEffects == null ? null : Arrays.asList(onMissReceivedEffects); }
|
||||
public ItemTraits_OnUse getOnDeathEffects() { return onDeathEffects; }
|
||||
|
||||
public boolean hasCriticalSkillEffect() { return getCriticalSkill() != 0; }
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.io.IOException;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.controller.Constants;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition;
|
||||
import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection;
|
||||
import com.gpl.rpg.AndorsTrail.model.item.DropList;
|
||||
@@ -193,4 +194,43 @@ public final class Monster extends Actor {
|
||||
dest.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(getMonsterTypeID());
|
||||
if (attackCost == monsterType.attackCost
|
||||
&& attackChance == monsterType.attackChance
|
||||
&& criticalSkill == monsterType.criticalSkill
|
||||
&& criticalMultiplier == monsterType.criticalMultiplier
|
||||
&& damagePotential.equals(monsterType.damagePotential)
|
||||
&& blockChance == monsterType.blockChance
|
||||
&& damageResistance == monsterType.damageResistance
|
||||
) {
|
||||
builder.add(false);
|
||||
} else {
|
||||
builder.add(true);
|
||||
builder.add(attackCost);
|
||||
builder.add(attackChance);
|
||||
builder.add(criticalSkill);
|
||||
builder.add(criticalMultiplier);
|
||||
damagePotential.addToChecksum(builder);
|
||||
builder.add(blockChance);
|
||||
builder.add(damageResistance);
|
||||
}
|
||||
ap.addToChecksum(builder);
|
||||
health.addToChecksum(builder);
|
||||
position.addToChecksum(builder);
|
||||
builder.add(conditions.size());
|
||||
for (ActorCondition c : conditions) {
|
||||
c.addToChecksum(builder);
|
||||
}
|
||||
builder.add(moveCost);
|
||||
|
||||
builder.add(forceAggressive);
|
||||
if (shopItems != null) {
|
||||
builder.add(true);
|
||||
shopItems.addToChecksum(builder);
|
||||
} else {
|
||||
builder.add(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
|
||||
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.controller.Constants;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.model.ability.ActorCondition;
|
||||
import com.gpl.rpg.AndorsTrail.model.ability.SkillCollection;
|
||||
import com.gpl.rpg.AndorsTrail.model.item.DropListCollection;
|
||||
@@ -475,5 +476,62 @@ public final class Player extends Actor {
|
||||
dest.writeUTF(id);
|
||||
dest.writeLong(savedVersion);
|
||||
}
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
//builder.add(baseTraits.iconID);// Do not add to checksum so that it can be changed without invalidating checksums
|
||||
builder.add(baseTraits.maxAP);
|
||||
builder.add(baseTraits.maxHP);
|
||||
//builder.add(name);// Do not add to checksum so that it can be changed without invalidating checksums
|
||||
builder.add(moveCost); // TODO: Should we really write this?
|
||||
builder.add(baseTraits.attackCost);
|
||||
builder.add(baseTraits.attackChance);
|
||||
builder.add(baseTraits.criticalSkill);
|
||||
builder.add(baseTraits.criticalMultiplier);
|
||||
baseTraits.damagePotential.addToChecksum(builder);
|
||||
builder.add(baseTraits.blockChance);
|
||||
builder.add(baseTraits.damageResistance);
|
||||
builder.add(baseTraits.moveCost);
|
||||
|
||||
ap.addToChecksum(builder);
|
||||
health.addToChecksum(builder);
|
||||
position.addToChecksum(builder);
|
||||
builder.add(conditions.size());
|
||||
for (ActorCondition c : conditions) {
|
||||
c.addToChecksum(builder);
|
||||
}
|
||||
builder.add(immunities.size());
|
||||
for (ActorCondition c : immunities) {
|
||||
c.addToChecksum(builder);
|
||||
}
|
||||
lastPosition.addToChecksum(builder);
|
||||
nextPosition.addToChecksum(builder);
|
||||
builder.add(level);
|
||||
builder.add(totalExperience);
|
||||
inventory.addToChecksum(builder);
|
||||
builder.add(baseTraits.useItemCost);
|
||||
builder.add(baseTraits.reequipCost);
|
||||
builder.add(skillLevels.size());
|
||||
for (int i = 0; i < skillLevels.size(); ++i) {
|
||||
builder.add(skillLevels.keyAt(i));
|
||||
builder.add(skillLevels.valueAt(i));
|
||||
}
|
||||
builder.add(spawnMap);
|
||||
builder.add(spawnPlace);
|
||||
builder.add(questProgress.size());
|
||||
for(Entry<String, LinkedHashSet<Integer> > e : questProgress.entrySet()) {
|
||||
builder.add(e.getKey());
|
||||
builder.add(e.getValue().size());
|
||||
for(int progress : e.getValue()) {
|
||||
builder.add(progress);
|
||||
}
|
||||
}
|
||||
builder.add(availableSkillIncreases);
|
||||
builder.add(alignments.size());
|
||||
for(Entry<String, Integer> e : alignments.entrySet()) {
|
||||
builder.add(e.getKey());
|
||||
builder.add(e.getValue());
|
||||
}
|
||||
builder.add(id);
|
||||
builder.add(savedVersion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.savegames.LegacySavegameFormatReaderForItemContainer;
|
||||
|
||||
public final class Inventory extends ItemContainer {
|
||||
@@ -208,4 +209,21 @@ public final class Inventory extends ItemContainer {
|
||||
}
|
||||
}
|
||||
}
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
super.addToChecksum(builder);
|
||||
builder.add(gold);
|
||||
builder.add(NUM_WORN_SLOTS);
|
||||
for(int i = 0; i < NUM_WORN_SLOTS; ++i) {
|
||||
if (wear[i] != null) {
|
||||
builder.add(wear[i].id);
|
||||
}
|
||||
}
|
||||
builder.add(NUM_QUICK_SLOTS);
|
||||
for(int i = 0; i < NUM_QUICK_SLOTS; ++i) {
|
||||
if (quickitem[i] != null) {
|
||||
builder.add(quickitem[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.model.actor.Player;
|
||||
|
||||
public class ItemContainer {
|
||||
@@ -42,6 +43,10 @@ public class ItemContainer {
|
||||
dest.writeUTF(itemType.id);
|
||||
dest.writeInt(quantity);
|
||||
}
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(itemType.id);
|
||||
builder.add(quantity);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(ItemType itemType, int quantity) {
|
||||
@@ -280,4 +285,10 @@ public class ItemContainer {
|
||||
e.writeToParcel(dest);
|
||||
}
|
||||
}
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(items.size());
|
||||
for (ItemEntry e : items) {
|
||||
e.addToChecksum(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,10 @@ public final class ItemType {
|
||||
public final ItemTraits_OnEquip effects_equip;
|
||||
public final ItemTraits_OnUse effects_use;
|
||||
public final ItemTraits_OnUse effects_hit;
|
||||
public final ItemTraits_OnUse effects_miss;
|
||||
public final ItemTraits_OnUse effects_kill;
|
||||
public final ItemTraits_OnHitReceived effects_hitReceived;
|
||||
public final ItemTraits_OnHitReceived effects_missReceived;
|
||||
|
||||
public ItemType(
|
||||
String id
|
||||
@@ -48,8 +50,10 @@ public final class ItemType {
|
||||
, ItemTraits_OnEquip effects_equip
|
||||
, ItemTraits_OnUse effects_use
|
||||
, ItemTraits_OnUse effects_hit
|
||||
, ItemTraits_OnUse effects_miss
|
||||
, ItemTraits_OnUse effects_kill
|
||||
, ItemTraits_OnHitReceived effects_hitReceived
|
||||
, ItemTraits_OnHitReceived effects_missReceived
|
||||
) {
|
||||
this.id = id;
|
||||
this.iconID = iconID;
|
||||
@@ -63,8 +67,10 @@ public final class ItemType {
|
||||
this.effects_equip = effects_equip;
|
||||
this.effects_use = effects_use;
|
||||
this.effects_hit = effects_hit;
|
||||
this.effects_miss = effects_miss;
|
||||
this.effects_kill = effects_kill;
|
||||
this.effects_hitReceived = effects_hitReceived;
|
||||
this.effects_missReceived = effects_missReceived;
|
||||
this.hasPersonalizedName = name.contains(Constants.PLACEHOLDER_PLAYERNAME);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.savegames.LegacySavegameFormatReaderForItemContainer;
|
||||
import com.gpl.rpg.AndorsTrail.util.Coord;
|
||||
|
||||
@@ -88,4 +89,12 @@ public final class Loot {
|
||||
position.writeToParcel(dest);
|
||||
dest.writeBoolean(isVisible);
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(exp);
|
||||
builder.add(gold);
|
||||
items.addToChecksum(builder);
|
||||
position.addToChecksum(builder);
|
||||
builder.add(isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.List;
|
||||
import com.gpl.rpg.AndorsTrail.AndorsTrailApplication;
|
||||
import com.gpl.rpg.AndorsTrail.context.ControllerContext;
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.savegames.LegacySavegameFormatReaderForMap;
|
||||
import com.gpl.rpg.AndorsTrail.util.L;
|
||||
|
||||
@@ -99,4 +100,16 @@ public final class MapCollection {
|
||||
map.writeToParcel(dest, world);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder checksumBuilder, WorldContext world) {
|
||||
List<PredefinedMap> mapsToExport = new ArrayList<PredefinedMap>();
|
||||
for(PredefinedMap map : getAllMaps()) {
|
||||
if (shouldSaveMap(world, map)) mapsToExport.add(map);
|
||||
}
|
||||
checksumBuilder.add(mapsToExport.size());
|
||||
for(PredefinedMap map : mapsToExport) {
|
||||
checksumBuilder.add(map.name);
|
||||
map.addToChecksum(checksumBuilder, world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.controller.Constants;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.model.actor.Monster;
|
||||
import com.gpl.rpg.AndorsTrail.model.actor.MonsterType;
|
||||
import com.gpl.rpg.AndorsTrail.util.Coord;
|
||||
@@ -140,4 +141,12 @@ public final class MonsterSpawnArea {
|
||||
m.writeToParcel(dest);
|
||||
}
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(isSpawning);
|
||||
builder.add(monsters.size());
|
||||
for (Monster m : monsters) {
|
||||
m.addToChecksum(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.gpl.rpg.AndorsTrail.context.ControllerContext;
|
||||
import com.gpl.rpg.AndorsTrail.context.WorldContext;
|
||||
import com.gpl.rpg.AndorsTrail.controller.Constants;
|
||||
import com.gpl.rpg.AndorsTrail.controller.VisualEffectController.BloodSplatter;
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
import com.gpl.rpg.AndorsTrail.model.actor.Monster;
|
||||
import com.gpl.rpg.AndorsTrail.model.item.ItemType;
|
||||
import com.gpl.rpg.AndorsTrail.model.item.Loot;
|
||||
@@ -398,4 +399,30 @@ public final class PredefinedMap {
|
||||
dest.writeBoolean(visited);
|
||||
dest.writeUTF(lastSeenLayoutHash);
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder, WorldContext world) {
|
||||
if (shouldSaveMapData(world)) {
|
||||
builder.add(true);
|
||||
builder.add(spawnAreas.length);
|
||||
for(MonsterSpawnArea a : spawnAreas) {
|
||||
builder.add(a.areaID);
|
||||
a.addToChecksum(builder);
|
||||
}
|
||||
builder.add(activeMapObjectGroups.size());
|
||||
for(String s : activeMapObjectGroups) {
|
||||
builder.add(s);
|
||||
}
|
||||
builder.add(groundBags.size());
|
||||
for(Loot l : groundBags) {
|
||||
l.addToChecksum(builder);
|
||||
}
|
||||
builder.add(currentColorFilter != null);
|
||||
if (currentColorFilter != null) builder.add(currentColorFilter);
|
||||
builder.add(lastVisitTime);
|
||||
} else {
|
||||
builder.add(false);
|
||||
}
|
||||
builder.add(visited);
|
||||
builder.add(lastSeenLayoutHash);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,11 @@ public final class ItemTypeParser extends JsonCollectionParserFor<ItemType> {
|
||||
final ItemTraits_OnEquip equipEffect = itemTraitsParser.parseItemTraits_OnEquip(o.optJSONObject(JsonFieldNames.ItemType.equipEffect));
|
||||
final ItemTraits_OnUse useEffect = itemTraitsParser.parseItemTraits_OnUse(o.optJSONObject(JsonFieldNames.ItemType.useEffect));
|
||||
final ItemTraits_OnUse hitEffect = itemTraitsParser.parseItemTraits_OnUse(o.optJSONObject(JsonFieldNames.ItemType.hitEffect));
|
||||
final ItemTraits_OnUse missEffect = itemTraitsParser.parseItemTraits_OnUse(o.optJSONObject(JsonFieldNames.ItemType.missEffect));
|
||||
final ItemTraits_OnUse killEffect = itemTraitsParser.parseItemTraits_OnUse(o.optJSONObject(JsonFieldNames.ItemType.killEffect));
|
||||
final ItemTraits_OnHitReceived hitReceivedEffect = itemTraitsParser.parseItemTraits_OnHitReceived(o.optJSONObject(JsonFieldNames.ItemType.hitReceivedEffect));
|
||||
|
||||
final ItemTraits_OnHitReceived missReceivedEffect = itemTraitsParser.parseItemTraits_OnHitReceived(o.optJSONObject(JsonFieldNames.ItemType.missReceivedEffect));
|
||||
|
||||
final int baseMarketCost = o.optInt(JsonFieldNames.ItemType.baseMarketCost);
|
||||
final boolean hasManualPrice = o.optInt(JsonFieldNames.ItemType.hasManualPrice, 0) > 0;
|
||||
final ItemType itemType = new ItemType(
|
||||
@@ -58,8 +60,10 @@ public final class ItemTypeParser extends JsonCollectionParserFor<ItemType> {
|
||||
, equipEffect
|
||||
, useEffect
|
||||
, hitEffect
|
||||
, missEffect
|
||||
, killEffect
|
||||
, hitReceivedEffect
|
||||
, missReceivedEffect
|
||||
);
|
||||
return new Pair<String, ItemType>(id, itemType);
|
||||
}
|
||||
|
||||
@@ -167,8 +167,10 @@ public final class JsonFieldNames {
|
||||
public static final String equipEffect = "equipEffect";
|
||||
public static final String useEffect = "useEffect";
|
||||
public static final String hitEffect = "hitEffect";
|
||||
public static final String missEffect = "missEffect";
|
||||
public static final String killEffect = "killEffect";
|
||||
public static final String hitReceivedEffect = "hitReceivedEffect";
|
||||
public static final String missReceivedEffect = "missReceivedEffect";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.security.DigestException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -79,7 +80,7 @@ public final class Savegames {
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | DigestException e) {
|
||||
L.log("Error saving world: " + e.toString());
|
||||
return false;
|
||||
}
|
||||
@@ -87,7 +88,7 @@ public final class Savegames {
|
||||
|
||||
private static void writeBackup(Context androidContext, byte[] savegame, String playerId) throws IOException {
|
||||
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
|
||||
ensureDirExists(cheatDetectionFolder);
|
||||
ensureDirExists(cheatDetectionFolder);
|
||||
File backupFile = new File(cheatDetectionFolder, playerId + "X");
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(backupFile);
|
||||
fileOutputStream.write(savegame);
|
||||
@@ -108,15 +109,15 @@ public final class Savegames {
|
||||
LoadSavegameResult result = loadWorld(androidContext.getResources(), world, controllers, androidContext, fos, fh);
|
||||
fos.close();
|
||||
if (result == LoadSavegameResult.success && slot != SLOT_QUICKSAVE && !world.model.statistics.hasUnlimitedSaves()) {
|
||||
// save to the quicksave slot before deleting the file
|
||||
if (!saveWorld(world, androidContext, SLOT_QUICKSAVE)) {
|
||||
// save to the quicksave slot before deleting the file
|
||||
if (!saveWorld(world, androidContext, SLOT_QUICKSAVE)) {
|
||||
return LoadSavegameResult.unknownError;
|
||||
}
|
||||
getSlotFile(slot, androidContext).delete();
|
||||
writeCheatCheck(androidContext, DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED, fh.playerId);
|
||||
}
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | DigestException e) {
|
||||
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
|
||||
L.log("Error loading world: " + e.toString());
|
||||
StringWriter sw = new StringWriter();
|
||||
@@ -128,33 +129,33 @@ public final class Savegames {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean triedToCheat(Context androidContext, FileHeader fh) throws IOException {
|
||||
long savedVersionToCheck = 0;
|
||||
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
|
||||
ensureDirExists(cheatDetectionFolder);
|
||||
File cheatDetectionFile = new File(cheatDetectionFolder, fh.playerId);
|
||||
if (cheatDetectionFile.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(cheatDetectionFile);
|
||||
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
|
||||
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
|
||||
savedVersionToCheck = cheatDetection.savedVersion;
|
||||
dataInputStream.close();
|
||||
fileInputStream.close();
|
||||
}
|
||||
private static boolean triedToCheat(Context androidContext, FileHeader fh) throws IOException {
|
||||
long savedVersionToCheck = 0;
|
||||
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
|
||||
ensureDirExists(cheatDetectionFolder);
|
||||
File cheatDetectionFile = new File(cheatDetectionFolder, fh.playerId);
|
||||
if (cheatDetectionFile.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(cheatDetectionFile);
|
||||
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
|
||||
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
|
||||
savedVersionToCheck = cheatDetection.savedVersion;
|
||||
dataInputStream.close();
|
||||
fileInputStream.close();
|
||||
}
|
||||
|
||||
if (savedVersionToCheck == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (androidContext.getFileStreamPath(fh.playerId).exists()) {
|
||||
FileInputStream fileInputStream = androidContext.openFileInput(fh.playerId);
|
||||
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
|
||||
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
|
||||
if (cheatDetection.savedVersion == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED) {
|
||||
savedVersionToCheck = DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED;
|
||||
} else if (cheatDetection.savedVersion > savedVersionToCheck) {
|
||||
savedVersionToCheck = cheatDetection.savedVersion;
|
||||
}
|
||||
if (androidContext.getFileStreamPath(fh.playerId).exists()) {
|
||||
FileInputStream fileInputStream = androidContext.openFileInput(fh.playerId);
|
||||
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
|
||||
final CheatDetection cheatDetection = new CheatDetection(dataInputStream);
|
||||
if (cheatDetection.savedVersion == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED) {
|
||||
savedVersionToCheck = DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED;
|
||||
} else if (cheatDetection.savedVersion > savedVersionToCheck) {
|
||||
savedVersionToCheck = cheatDetection.savedVersion;
|
||||
}
|
||||
|
||||
if (AndorsTrailApplication.DEVELOPMENT_DEBUGMESSAGES) {
|
||||
L.log("Internal cheatcheck file savedVersion: " + cheatDetection.savedVersion);
|
||||
@@ -167,83 +168,98 @@ public final class Savegames {
|
||||
return (savedVersionToCheck == DENY_LOADING_BECAUSE_GAME_IS_CURRENTLY_PLAYED || fh.savedVersion < savedVersionToCheck);
|
||||
}
|
||||
|
||||
private static FileOutputStream getOutputFile(Context androidContext, int slot) throws IOException {
|
||||
if (slot == SLOT_QUICKSAVE) {
|
||||
return androidContext.openFileOutput(Constants.FILENAME_SAVEGAME_QUICKSAVE, Context.MODE_PRIVATE);
|
||||
} else {
|
||||
ensureSavegameDirectoryExists(androidContext);
|
||||
return new FileOutputStream(getSlotFile(slot, androidContext));
|
||||
}
|
||||
}
|
||||
private static FileOutputStream getOutputFile(Context androidContext, int slot) throws IOException {
|
||||
if (slot == SLOT_QUICKSAVE) {
|
||||
return androidContext.openFileOutput(Constants.FILENAME_SAVEGAME_QUICKSAVE, Context.MODE_PRIVATE);
|
||||
} else {
|
||||
ensureSavegameDirectoryExists(androidContext);
|
||||
return new FileOutputStream(getSlotFile(slot, androidContext));
|
||||
}
|
||||
}
|
||||
|
||||
private static void ensureSavegameDirectoryExists(Context context) {
|
||||
File dir = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
|
||||
ensureDirExists(dir);
|
||||
}
|
||||
private static void ensureSavegameDirectoryExists(Context context) {
|
||||
File dir = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
|
||||
ensureDirExists(dir);
|
||||
}
|
||||
|
||||
public static boolean ensureDirExists(File dir) {
|
||||
if (!dir.exists()) {
|
||||
boolean worked = dir.mkdir();
|
||||
return worked;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public static boolean ensureDirExists(File dir) {
|
||||
if (!dir.exists()) {
|
||||
boolean worked = dir.mkdir();
|
||||
return worked;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static FileInputStream getInputFile(Context androidContext, int slot) throws IOException {
|
||||
if (slot == SLOT_QUICKSAVE) {
|
||||
return androidContext.openFileInput(Constants.FILENAME_SAVEGAME_QUICKSAVE);
|
||||
} else {
|
||||
return new FileInputStream(getSlotFile(slot, androidContext));
|
||||
}
|
||||
}
|
||||
private static FileInputStream getInputFile(Context androidContext, int slot) throws IOException {
|
||||
if (slot == SLOT_QUICKSAVE) {
|
||||
return androidContext.openFileInput(Constants.FILENAME_SAVEGAME_QUICKSAVE);
|
||||
} else {
|
||||
return new FileInputStream(getSlotFile(slot, androidContext));
|
||||
}
|
||||
}
|
||||
|
||||
public static File getSlotFile(int slot, Context context) {
|
||||
File root = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
|
||||
return getSlotFile(slot, root);
|
||||
}
|
||||
public static File getSlotFile(int slot, Context context) {
|
||||
File root = AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY);
|
||||
return getSlotFile(slot, root);
|
||||
}
|
||||
|
||||
public static File getSlotFile(int slot, File directory) {
|
||||
return new File(directory, getSlotFileName(slot));
|
||||
}
|
||||
public static File getSlotFile(int slot, File directory) {
|
||||
return new File(directory, getSlotFileName(slot));
|
||||
}
|
||||
|
||||
public static String getSlotFileName(int slot) {
|
||||
return Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + slot;
|
||||
}
|
||||
public static String getSlotFileName(int slot) {
|
||||
return Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + slot;
|
||||
}
|
||||
|
||||
|
||||
public static void saveWorld(WorldContext world, OutputStream outStream, String displayInfo) throws IOException {
|
||||
public static void saveWorld(WorldContext world, OutputStream outStream, String displayInfo) throws IOException, DigestException {
|
||||
DataOutputStream dest = new DataOutputStream(outStream);
|
||||
FileHeader.writeToParcel(dest, world.model.player.getName(),
|
||||
displayInfo, world.model.player.iconID,
|
||||
world.model.statistics.isDead(),
|
||||
world.model.statistics.hasUnlimitedSaves(),
|
||||
world.model.player.id,
|
||||
world.model.player.savedVersion);
|
||||
world.model.player.savedVersion,
|
||||
world.model.statistics.getIsAlteredSavegame());
|
||||
|
||||
byte[] checksum = world.getChecksum();
|
||||
world.model.statistics.setChecksum(checksum);
|
||||
|
||||
world.maps.writeToParcel(dest, world);
|
||||
world.model.writeToParcel(dest);
|
||||
dest.close();
|
||||
}
|
||||
|
||||
public static LoadSavegameResult loadWorld(Resources res, WorldContext world, ControllerContext controllers, Context androidContext, InputStream inState, FileHeader fh) throws IOException {
|
||||
DataInputStream src = new DataInputStream(inState);
|
||||
final FileHeader header = new FileHeader(src, fh.skipIcon);
|
||||
if (header.fileversion > AndorsTrailApplication.CURRENT_VERSION)
|
||||
return LoadSavegameResult.savegameIsFromAFutureVersion;
|
||||
public static LoadSavegameResult loadWorld(Resources res, WorldContext world, ControllerContext controllers, Context androidContext, InputStream inState, FileHeader fh) throws IOException, DigestException {
|
||||
DataInputStream src = new DataInputStream(inState);
|
||||
final FileHeader header = new FileHeader(src, fh.skipIcon);
|
||||
if (header.fileversion > AndorsTrailApplication.CURRENT_VERSION)
|
||||
return LoadSavegameResult.savegameIsFromAFutureVersion;
|
||||
|
||||
world.maps.readFromParcel(src, world, controllers, header.fileversion);
|
||||
world.model = new ModelContainer(src, world, controllers, header.fileversion);
|
||||
WorldMapController.populateWorldMap(androidContext, world, controllers.getResources());
|
||||
src.close();
|
||||
|
||||
if (header.fileversion >= 81) {
|
||||
checkChecksum(world);
|
||||
}
|
||||
WorldMapController.populateWorldMap(androidContext, world, controllers.getResources());
|
||||
|
||||
if (header.fileversion < 45) {
|
||||
LegacySavegamesContentAdaptations.adaptToNewContentForVersion45(world, controllers, res);
|
||||
}
|
||||
|
||||
|
||||
onWorldLoaded(res, world, controllers);
|
||||
|
||||
return LoadSavegameResult.success;
|
||||
}
|
||||
|
||||
private static void checkChecksum(WorldContext world) throws DigestException {
|
||||
byte[] checksum = world.getChecksum();
|
||||
if (!world.model.statistics.compareChecksum(checksum)) {
|
||||
world.model.statistics.markAsAlteredSavegame();
|
||||
}
|
||||
}
|
||||
|
||||
private static void onWorldLoaded(Resources res, WorldContext world, ControllerContext controllers) {
|
||||
controllers.actorStatsController.recalculatePlayerStats(world.model.player);
|
||||
controllers.mapController.resetMapsNotRecentlyVisited();
|
||||
@@ -268,15 +284,15 @@ public final class Savegames {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeCheatCheck(Context androidContext, long savedVersion, String playerId) throws IOException {
|
||||
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
|
||||
ensureDirExists(cheatDetectionFolder);
|
||||
File cheatDetectionFile = new File(cheatDetectionFolder, playerId);
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(cheatDetectionFile);
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
|
||||
CheatDetection.writeToParcel(dataOutputStream, savedVersion);
|
||||
dataOutputStream.close();
|
||||
fileOutputStream.close();
|
||||
private static void writeCheatCheck(Context androidContext, long savedVersion, String playerId) throws IOException {
|
||||
File cheatDetectionFolder = AndroidStorage.getStorageDirectory(androidContext, Constants.CHEAT_DETECTION_FOLDER);
|
||||
ensureDirExists(cheatDetectionFolder);
|
||||
File cheatDetectionFile = new File(cheatDetectionFolder, playerId);
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(cheatDetectionFile);
|
||||
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
|
||||
CheatDetection.writeToParcel(dataOutputStream, savedVersion);
|
||||
dataOutputStream.close();
|
||||
fileOutputStream.close();
|
||||
|
||||
fileOutputStream = androidContext.openFileOutput(playerId, Context.MODE_PRIVATE);
|
||||
dataOutputStream = new DataOutputStream(fileOutputStream);
|
||||
@@ -287,26 +303,26 @@ public final class Savegames {
|
||||
|
||||
private static final Pattern savegameFilenamePattern = Pattern.compile(Constants.FILENAME_SAVEGAME_FILENAME_PREFIX + "(\\d+)");
|
||||
|
||||
public static List<Integer> getUsedSavegameSlots(Context context) {
|
||||
try {
|
||||
final List<Integer> result = new ArrayList<Integer>();
|
||||
AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY).listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File f, String filename) {
|
||||
Matcher m = savegameFilenamePattern.matcher(filename);
|
||||
if (m != null && m.matches()) {
|
||||
result.add(Integer.parseInt(m.group(1)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
Collections.sort(result);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
return new ArrayList<Integer>();
|
||||
}
|
||||
}
|
||||
public static List<Integer> getUsedSavegameSlots(Context context) {
|
||||
try {
|
||||
final List<Integer> result = new ArrayList<Integer>();
|
||||
AndroidStorage.getStorageDirectory(context, Constants.FILENAME_SAVEGAME_DIRECTORY).listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File f, String filename) {
|
||||
Matcher m = savegameFilenamePattern.matcher(filename);
|
||||
if (m != null && m.matches()) {
|
||||
result.add(Integer.parseInt(m.group(1)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
Collections.sort(result);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
return new ArrayList<Integer>();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CheatDetection {
|
||||
public final int fileversion;
|
||||
@@ -326,16 +342,17 @@ public final class Savegames {
|
||||
}
|
||||
|
||||
|
||||
public static final class FileHeader {
|
||||
public final int fileversion;
|
||||
public final String playerName;
|
||||
public final String displayInfo;
|
||||
public final int iconID;
|
||||
public boolean skipIcon = false;
|
||||
public final boolean isDead;
|
||||
public final boolean hasUnlimitedSaves;
|
||||
public final String playerId;
|
||||
public final long savedVersion;
|
||||
public static final class FileHeader {
|
||||
public final int fileversion;
|
||||
public final String playerName;
|
||||
public final String displayInfo;
|
||||
public final int iconID;
|
||||
public final boolean isAlteredSavegame;
|
||||
public boolean skipIcon = false;
|
||||
public final boolean isDead;
|
||||
public final boolean hasUnlimitedSaves;
|
||||
public final String playerId;
|
||||
public final long savedVersion;
|
||||
|
||||
public String describe() {
|
||||
return (fileversion == AndorsTrailApplication.DEVELOPMENT_INCOMPATIBLE_SAVEGAME_VERSION ? "(D) " : "") + playerName + ", " + displayInfo;
|
||||
@@ -344,18 +361,18 @@ public final class Savegames {
|
||||
|
||||
// ====== PARCELABLE ===================================================================
|
||||
|
||||
public FileHeader(DataInputStream src, boolean skipIcon) throws IOException {
|
||||
int fileversion = src.readInt();
|
||||
if (fileversion == 11)
|
||||
fileversion = 5; // Fileversion 5 had no version identifier, but the first byte was 11.
|
||||
this.fileversion = fileversion;
|
||||
if (fileversion >= 14) { // Before fileversion 14 (0.6.7), we had no file header.
|
||||
this.playerName = src.readUTF();
|
||||
this.displayInfo = src.readUTF();
|
||||
} else {
|
||||
this.playerName = null;
|
||||
this.displayInfo = null;
|
||||
}
|
||||
public FileHeader(DataInputStream src, boolean skipIcon) throws IOException {
|
||||
int fileversion = src.readInt();
|
||||
if (fileversion == 11)
|
||||
fileversion = 5; // Fileversion 5 had no version identifier, but the first byte was 11.
|
||||
this.fileversion = fileversion;
|
||||
if (fileversion >= 14) { // Before fileversion 14 (0.6.7), we had no file header.
|
||||
this.playerName = src.readUTF();
|
||||
this.displayInfo = src.readUTF();
|
||||
} else {
|
||||
this.playerName = null;
|
||||
this.displayInfo = null;
|
||||
}
|
||||
|
||||
if (fileversion >= 43) {
|
||||
int id = src.readInt();
|
||||
@@ -380,9 +397,14 @@ public final class Savegames {
|
||||
this.playerId = "";
|
||||
this.savedVersion = 0;
|
||||
}
|
||||
if(fileversion >= 81){
|
||||
this.isAlteredSavegame = src.readBoolean();
|
||||
}else{
|
||||
this.isAlteredSavegame = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeToParcel(DataOutputStream dest, String playerName, String displayInfo, int iconID, boolean isDead, boolean hasUnlimitedSaves, String playerId, long savedVersion) throws IOException {
|
||||
public static void writeToParcel(DataOutputStream dest, String playerName, String displayInfo, int iconID, boolean isDead, boolean hasUnlimitedSaves, String playerId, long savedVersion, boolean isAlteredSavegame) throws IOException {
|
||||
dest.writeInt(AndorsTrailApplication.CURRENT_VERSION);
|
||||
dest.writeUTF(playerName);
|
||||
dest.writeUTF(displayInfo);
|
||||
@@ -391,6 +413,7 @@ public final class Savegames {
|
||||
dest.writeBoolean(hasUnlimitedSaves);
|
||||
dest.writeUTF(playerId);
|
||||
dest.writeLong(savedVersion);
|
||||
dest.writeBoolean(isAlteredSavegame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.gpl.rpg.AndorsTrail.util;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -47,4 +49,9 @@ public final class Coord {
|
||||
dest.writeInt(x);
|
||||
dest.writeInt(y);
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(x);
|
||||
builder.add(y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.gpl.rpg.AndorsTrail.util;
|
||||
|
||||
import com.gpl.rpg.AndorsTrail.model.ChecksumBuilder;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -99,4 +101,9 @@ public final class Range {
|
||||
dest.writeInt(max);
|
||||
dest.writeInt(current);
|
||||
}
|
||||
|
||||
public void addToChecksum(ChecksumBuilder builder) {
|
||||
builder.add(max);
|
||||
builder.add(current);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,17 @@ public final class ItemEffectsView extends LinearLayout {
|
||||
private final ActorConditionEffectList itemeffect_onequip_conditions;
|
||||
private final ItemEffectsView_OnUse itemeffect_onuse;
|
||||
private final ItemEffectsView_OnUse itemeffect_onhit;
|
||||
private final ItemEffectsView_OnUse itemeffect_onmiss;
|
||||
private final ItemEffectsView_OnUse itemeffect_onkill;
|
||||
private final ItemEffectsView_OnHitReceived itemeffect_onhitreceived;
|
||||
private final ItemEffectsView_OnHitReceived itemeffect_onmissreceived;
|
||||
private final ItemEffectsView_OnDeath itemeffect_ondeath;
|
||||
private final TextView itemeffect_onuse_title;
|
||||
private final TextView itemeffect_onhit_title;
|
||||
private final TextView itemeffect_onmiss_title;
|
||||
private final TextView itemeffect_onkill_title;
|
||||
private final TextView itemeffect_onhitreceived_title;
|
||||
private final TextView itemeffect_onmissreceived_title;
|
||||
private final TextView itemeffect_ondeath_title;
|
||||
|
||||
public ItemEffectsView(Context context, AttributeSet attr) {
|
||||
@@ -41,13 +45,17 @@ public final class ItemEffectsView extends LinearLayout {
|
||||
|
||||
itemeffect_onuse = (ItemEffectsView_OnUse) findViewById(R.id.itemeffect_onuse);
|
||||
itemeffect_onhit = (ItemEffectsView_OnUse) findViewById(R.id.itemeffect_onhit);
|
||||
itemeffect_onmiss = (ItemEffectsView_OnUse) findViewById(R.id.itemeffect_onmiss);
|
||||
itemeffect_onkill = (ItemEffectsView_OnUse) findViewById(R.id.itemeffect_onkill);
|
||||
itemeffect_onhitreceived = (ItemEffectsView_OnHitReceived) findViewById(R.id.itemeffect_onhitreceived);
|
||||
itemeffect_onmissreceived = (ItemEffectsView_OnHitReceived) findViewById(R.id.itemeffect_onmissreceived);
|
||||
itemeffect_ondeath = (ItemEffectsView_OnDeath) findViewById(R.id.itemeffect_ondeath);
|
||||
itemeffect_onuse_title = (TextView) findViewById(R.id.itemeffect_onuse_title);
|
||||
itemeffect_onhit_title = (TextView) findViewById(R.id.itemeffect_onhit_title);
|
||||
itemeffect_onmiss_title = (TextView) findViewById(R.id.itemeffect_onmiss_title);
|
||||
itemeffect_onkill_title = (TextView) findViewById(R.id.itemeffect_onkill_title);
|
||||
itemeffect_onhitreceived_title = (TextView) findViewById(R.id.itemeffect_onhitreceived_title);
|
||||
itemeffect_onmissreceived_title = (TextView) findViewById(R.id.itemeffect_onmissreceived_title);
|
||||
itemeffect_ondeath_title = (TextView) findViewById(R.id.itemeffect_ondeath_title);
|
||||
}
|
||||
|
||||
@@ -55,8 +63,10 @@ public final class ItemEffectsView extends LinearLayout {
|
||||
ItemTraits_OnEquip effects_equip,
|
||||
Collection<ItemTraits_OnUse> effects_use,
|
||||
Collection<ItemTraits_OnUse> effects_hit,
|
||||
Collection<ItemTraits_OnUse> effects_miss,
|
||||
Collection<ItemTraits_OnUse> effects_kill,
|
||||
Collection<ItemTraits_OnHitReceived> effects_hitreceived,
|
||||
Collection<ItemTraits_OnHitReceived> effects_missreceived,
|
||||
ItemTraits_OnUse effects_death,
|
||||
boolean isWeapon
|
||||
) {
|
||||
@@ -90,6 +100,12 @@ public final class ItemEffectsView extends LinearLayout {
|
||||
} else {
|
||||
itemeffect_onhit_title.setVisibility(View.GONE);
|
||||
}
|
||||
itemeffect_onmiss.update(effects_miss);
|
||||
if (effects_miss != null) {
|
||||
itemeffect_onmiss_title.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
itemeffect_onmiss_title.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
itemeffect_onkill.update(effects_kill);
|
||||
if (effects_kill != null) {
|
||||
@@ -104,6 +120,12 @@ public final class ItemEffectsView extends LinearLayout {
|
||||
} else {
|
||||
itemeffect_onhitreceived_title.setVisibility(View.GONE);
|
||||
}
|
||||
itemeffect_onmissreceived.update(effects_missreceived);
|
||||
if (effects_missreceived != null) {
|
||||
itemeffect_onmissreceived_title.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
itemeffect_onmissreceived_title.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
itemeffect_ondeath.update(effects_death);
|
||||
if (effects_death != null) {
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 74 KiB |
@@ -12,19 +12,19 @@
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
|
||||
<com.gpl.rpg.AndorsTrail.view.AbilityModifierInfoView
|
||||
android:id="@+id/itemeffect_onequip_abilitymodifierinfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.AbilityModifierInfoView
|
||||
android:id="@+id/itemeffect_onequip_abilitymodifierinfo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<com.gpl.rpg.AndorsTrail.view.ActorConditionEffectList
|
||||
android:id="@+id/itemeffect_onequip_conditions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ActorConditionEffectList
|
||||
android:id="@+id/itemeffect_onequip_conditions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/iteminfo_effect_works_when_used"
|
||||
@@ -32,12 +32,12 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnUse
|
||||
android:id="@+id/itemeffect_onuse"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnUse
|
||||
android:id="@+id/itemeffect_onuse"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/iteminfo_effect_works_when_hitting_target"
|
||||
@@ -45,12 +45,25 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnUse
|
||||
android:id="@+id/itemeffect_onhit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnUse
|
||||
android:id="@+id/itemeffect_onhit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/iteminfo_effect_works_when_missing_target"
|
||||
android:id="@+id/itemeffect_onmiss_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnUse
|
||||
android:id="@+id/itemeffect_onmiss"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/iteminfo_effect_works_when_killing_target"
|
||||
@@ -58,12 +71,12 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnUse
|
||||
android:id="@+id/itemeffect_onkill"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnUse
|
||||
android:id="@+id/itemeffect_onkill"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/iteminfo_effect_works_when_hit_by_attacker"
|
||||
@@ -71,23 +84,36 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnHitReceived
|
||||
android:id="@+id/itemeffect_onhitreceived"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnHitReceived
|
||||
android:id="@+id/itemeffect_onhitreceived"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/iteminfo_effect_works_when_missed_by_attacker"
|
||||
android:id="@+id/itemeffect_onmissreceived_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnHitReceived
|
||||
android:id="@+id/itemeffect_onmissreceived"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:text="@string/iteminfo_effect_works_when_killed_by_attacker"
|
||||
android:id="@+id/itemeffect_ondeath_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnDeath
|
||||
android:id="@+id/itemeffect_ondeath"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
<com.gpl.rpg.AndorsTrail.view.ItemEffectsView_OnDeath
|
||||
android:id="@+id/itemeffect_ondeath"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10sp"
|
||||
/>
|
||||
</merge>
|
||||
|
||||
@@ -348,10 +348,12 @@
|
||||
<string name="iteminfo_effect_works_on_target">On target</string>
|
||||
<string name="iteminfo_effect_works_on_attacker">On attacker</string>
|
||||
<string name="iteminfo_effect_works_when_hitting_target">When hitting target</string>
|
||||
<string name="iteminfo_effect_works_when_missing_target">When missing target</string>
|
||||
<string name="iteminfo_effect_works_when_killing_target">On every kill</string>
|
||||
<string name="iteminfo_effect_works_when_used">When used</string>
|
||||
<string name="iteminfo_effect_works_when_equipped">When equipped</string>
|
||||
<string name="iteminfo_effect_works_when_hit_by_attacker">When hit by attacker</string>
|
||||
<string name="iteminfo_effect_works_when_missed_by_attacker">When missed by attacker</string>
|
||||
<string name="iteminfo_effect_works_when_killed_by_attacker">When killed by attacker</string>
|
||||
|
||||
<string name="iteminfo_effect_decrease_current_hp">Drains %1$s HP</string>
|
||||
|
||||
5
useful_links.html
Normal file
5
useful_links.html
Normal file
@@ -0,0 +1,5 @@
|
||||
[b][color=#0080FF]v0.8.13[/color] (Troubling Times) active on Google Play[/b]
|
||||
|
||||
[b]Useful links[/b]
|
||||
[size=80] [url=https://github.com/AndorsTrailRelease/andors-trail][b]Source code of the game[/b][/url] - [url=https://andorstrail.com/viewtopic.php?f=6&t=4560][b]Contribution guide[/b][/url] - [url=https://andorstrail.com/viewtopic.php?f=6&t=4806][b]ATCS Editor[/b][/url] - [url=https://hosted.weblate.org/projects/andors-trail/][b]Translate the game on Weblate[/b][/url] - [url=https://andorstrail.com/viewtopic.php?f=12&t=4583][b]Example walkthrough[/b][/url] - [url=https://andorstrail-directory.github.io/][b]Andor's Trail Directory[/b][/url] - [url=https://discord.gg/FgwXdy6][b]Join the Discord[/b][/url][/size]
|
||||
[size=80][b]Get the game (v0.8.13) from[/b] [url=https://andorstrail.app]Google[/url], [url=https://github.com/AndorsTrailRelease/andors-trail/releases]Github[/url], [url=https://f-droid.org/packages/com.gpl.rpg.AndorsTrail/]F-Droid[/url], [url=https://andorstrail.com/static/AndorsTrail_v0.8.13.apk]our server[/url], [b]or [/b][url=https://andorstrailteam.itch.io/andors-trail] itch.io[/url]
|
||||
Reference in New Issue
Block a user