/* ==================================================================== CreateAction a unit by passing a Unit struct with the following stuff set: x, y, str, entr, exp, delay, orient, nation, player. This function will use the passed values to create a Unit struct with all values set then. ==================================================================== */ public static Unit CreateUnit(Unit_Lib_Entry prop, Unit_Lib_Entry trsp_prop, Unit unit_base) { Unit unit; if (prop == null) return null; unit = new Unit(); /* shallow copy of properties */ unit.prop = prop; unit.sel_prop = unit.prop; unit.embark = UnitEmbarkTypes.EMBARK_NONE; /* assign the passed transporter without any check */ if (trsp_prop != null && (prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING && (prop.flags & UnitFlags.SWIMMING) != UnitFlags.SWIMMING) { unit.trsp_prop = trsp_prop; /* a sea/air ground transporter is active per default */ if ((trsp_prop.flags & UnitFlags.SWIMMING) == UnitFlags.SWIMMING) { unit.embark = UnitEmbarkTypes.EMBARK_SEA; unit.sel_prop = unit.trsp_prop; } if ((trsp_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING) { unit.embark = UnitEmbarkTypes.EMBARK_AIR; unit.sel_prop = unit.trsp_prop; } } /* copy the base values */ unit.delay = unit_base.delay; unit.x = unit_base.x; unit.y = unit_base.y; unit.str = unit_base.str; unit.entr = unit_base.entr; unit.player = unit_base.player; unit.nation = unit_base.nation; unit.name = unit_base.name; unit.AddExperience(unit_base.exp_level * 100); unit.orient = unit_base.orient; unit.AdjustIcon(); unit.unused = true; unit.supply_level = 100; unit.cur_ammo = unit.prop.ammo; unit.cur_fuel = unit.prop.fuel; if ((unit.cur_fuel == 0) && (unit.trsp_prop != null) && (!string.IsNullOrEmpty(unit.trsp_prop.id)) && unit.trsp_prop.fuel > 0) unit.cur_fuel = unit.trsp_prop.fuel; unit.tag = unit_base.tag; /* update life bar properties */ update_bar(unit); /* allocate backup mem */ unit.backup = new Unit(); return unit; }
public FIGHT_TYPES Attack(Unit target, UNIT_ATTACK type, bool real, bool force_rugged) { int unit_old_str = this.str;//, target_old_str = target.str; int unit_old_ini = this.sel_prop.ini, target_old_ini = target.sel_prop.ini; int unit_dam = 0, unit_suppr = 0, target_dam = 0, target_suppr = 0; int rugged_chance; bool rugged_def = false; int exp_mod; FIGHT_TYPES ret = FIGHT_TYPES.AR_NONE; /* clear flags */ ATTACK_FLAGS strike; /* check if rugged defense occurs */ if (real && type == UNIT_ATTACK.UNIT_ACTIVE_ATTACK) if (this.CheckRuggedDefense(target) || (force_rugged && this.IsClose(target))) { rugged_chance = this.GetRuggedDefenseChance(target); if (Misc.DICE(100) <= rugged_chance || force_rugged) rugged_def = true; } /* PG's formula for initiative is min { base initiative, terrain max initiative } + ( exp_level + 1 ) / 2 + D3 */ /* against aircrafts the initiative is used since terrain does not matter */ /* target's terrain is used for fight */ if ((this.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING && (target.sel_prop.flags & UnitFlags.FLYING) != UnitFlags.FLYING) { this.sel_prop.ini = Math.Min(this.sel_prop.ini, target.terrain.max_ini); target.sel_prop.ini = Math.Min(target.sel_prop.ini, target.terrain.max_ini); } this.sel_prop.ini += (this.exp_level + 1) / 2; target.sel_prop.ini += (target.exp_level + 1) / 2; /* special initiative rules: antitank inits attack tank|recon: atk 0, def 99 tank inits attack against anti-tank: atk 0, def 99 defensive fire: atk 99, def 0 submarine attacks: atk 99, def 0 ranged attack: atk 99, def 0 rugged defense: atk 0 air unit attacks air defense: atk = def non-art vs art: atk 0, def 99 */ if ((this.sel_prop.flags & UnitFlags.ANTI_TANK) == UnitFlags.ANTI_TANK) if ((target.sel_prop.flags & UnitFlags.TANK) == UnitFlags.TANK) { this.sel_prop.ini = 0; target.sel_prop.ini = 99; } if (((this.sel_prop.flags & UnitFlags.DIVING) == UnitFlags.DIVING) || ((this.sel_prop.flags & UnitFlags.ARTILLERY) == UnitFlags.ARTILLERY) || ((this.sel_prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE) || type == UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK ) { this.sel_prop.ini = 99; target.sel_prop.ini = 0; } if ((this.sel_prop.flags & UnitFlags.FLYING) == UnitFlags.FLYING) if ((target.sel_prop.flags & UnitFlags.AIR_DEFENSE) == UnitFlags.AIR_DEFENSE) this.sel_prop.ini = target.sel_prop.ini; if (rugged_def) this.sel_prop.ini = 0; if (force_rugged) target.sel_prop.ini = 99; /* the dice is rolled after these changes */ if (real) { this.sel_prop.ini += Misc.DICE(3); target.sel_prop.ini += Misc.DICE(3); } #if DEBUG if (real) { Console.WriteLine("{0} Initiative: {1}", this.name, this.sel_prop.ini); Console.WriteLine("{0} Initiative: {1}", target.name, target.sel_prop.ini); if (this.CheckRuggedDefense(target)) Console.WriteLine("Rugged Defense: {0} ({1})", rugged_def ? "yes" : "no", this.GetRuggedDefenseChance(target)); } #endif /* in a real combat a submarine may evade */ if (real && type == UNIT_ATTACK.UNIT_ACTIVE_ATTACK && ((target.sel_prop.flags & UnitFlags.DIVING) == UnitFlags.DIVING)) { if (Misc.DICE(10) <= 7 + (target.exp_level - this.exp_level) / 2) { strike = ATTACK_FLAGS.ATK_NO_STRIKE; ret |= FIGHT_TYPES.AR_EVADED; } else strike = ATTACK_FLAGS.ATK_UNIT_FIRST; #if DEBUG Console.WriteLine("\nSubmarine Evasion: {0} ({1}%)\n", (strike == ATTACK_FLAGS.ATK_NO_STRIKE) ? "yes" : "no", 10 * (7 + (target.exp_level - this.exp_level) / 2)); #endif } else /* who is first? */ if (this.sel_prop.ini == target.sel_prop.ini) strike = ATTACK_FLAGS.ATK_BOTH_STRIKE; else if (this.sel_prop.ini > target.sel_prop.ini) strike = ATTACK_FLAGS.ATK_UNIT_FIRST; else strike = ATTACK_FLAGS.ATK_TARGET_FIRST; /* the one with the highest initiative begins first if not defensive fire or artillery */ if (strike == ATTACK_FLAGS.ATK_BOTH_STRIKE) { /* both strike at the same time */ GetDamage(this, this, target, type, real, rugged_def, out target_dam, out target_suppr); if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK)) GetDamage(this, target, this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK, real, rugged_def, out unit_dam, out unit_suppr); target.ApplyDamage(target_dam, target_suppr, this); this.ApplyDamage(unit_dam, unit_suppr, target); } else if (strike == ATTACK_FLAGS.ATK_UNIT_FIRST) { /* unit strikes first */ GetDamage(this, this, target, type, real, rugged_def, out target_dam, out target_suppr); if (target.ApplyDamage(target_dam, target_suppr, this) != 0) if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK) && type != UNIT_ATTACK.UNIT_DEFENSIVE_ATTACK) { GetDamage(this, target, this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK, real, rugged_def, out unit_dam, out unit_suppr); this.ApplyDamage(unit_dam, unit_suppr, target); } } else if (strike == ATTACK_FLAGS.ATK_TARGET_FIRST) { /* target strikes first */ if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK)) { GetDamage(this, target, this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK, real, rugged_def, out unit_dam, out unit_suppr); if (this.ApplyDamage(unit_dam, unit_suppr, target) == 0) ret |= FIGHT_TYPES.AR_UNIT_ATTACK_BROKEN_UP; } if (unit_get_cur_str(this) > 0) { GetDamage(this, this, target, type, real, rugged_def, out target_dam, out target_suppr); target.ApplyDamage(target_dam, target_suppr, this); } } /* check return value */ if (this.str == 0) ret |= FIGHT_TYPES.AR_UNIT_KILLED; else if (unit_get_cur_str(this) == 0) ret |= FIGHT_TYPES.AR_UNIT_SUPPRESSED; if (target.str == 0) ret |= FIGHT_TYPES.AR_TARGET_KILLED; else if (unit_get_cur_str(target) == 0) ret |= FIGHT_TYPES.AR_TARGET_SUPPRESSED; if (rugged_def) ret |= FIGHT_TYPES.AR_RUGGED_DEFENSE; if (real) { /* cost ammo */ if (Config.supply) { //if (DICE(10)<=target_old_str) this.cur_ammo--; if (target.CheckAttack(this, UNIT_ATTACK.UNIT_PASSIVE_ATTACK) && target.cur_ammo > 0) //if (DICE(10)<=unit_old_str) target.cur_ammo--; } /* costs attack */ if (this.cur_atk_count > 0) this.cur_atk_count--; /* target: loose entrenchment if damage was taken or with a unit.str*10% chance */ if (target.entr > 0) if (target_dam > 0 || Misc.DICE(10) <= unit_old_str) target.entr--; /* attacker looses entrenchment if it got hurt */ if (this.entr > 0 && unit_dam > 0) this.entr--; /* gain experience */ exp_mod = target.exp_level - this.exp_level; if (exp_mod < 1) exp_mod = 1; this.AddExperience(exp_mod * target_dam + unit_dam); exp_mod = this.exp_level - target.exp_level; if (exp_mod < 1) exp_mod = 1; target.AddExperience(exp_mod * unit_dam + target_dam); if (this.IsClose(target)) { this.AddExperience(10); target.AddExperience(10); } /* adjust life bars */ update_bar(this); update_bar(target); } this.sel_prop.ini = unit_old_ini; target.sel_prop.ini = target_old_ini; return ret; }
/* ==================================================================== Merge these two units: unit is the new unit and source must be removed from map and memory after this function was called. ==================================================================== */ public void Merge(Unit unit, Unit source) { /* units relative weight */ float weight1, weight2, total; int i; /* compute weight */ weight1 = unit.str; weight2 = source.str; total = unit.str + source.str; /* adjust so weight1 + weigth2 = 1 */ weight1 /= total; weight2 /= total; /* no other actions allowed */ unit.unused = false; unit.cur_mov = 0; unit.cur_atk_count = 0; /* repair damage */ unit.str += source.str; /* reorganization costs some entrenchment: the new units are assumed to have entrenchment 0 since they come. new entr is rounded weighted sum */ unit.entr = (int)Math.Floor((float)unit.entr * weight1 + 0.5); /* + 0 * weight2 */ /* update experience */ i = (int)(weight1 * unit.exp + weight2 * source.exp); unit.exp = 0; unit.AddExperience(i); /* update unit::prop */ /* related initiative */ unit.prop.ini = (int)(weight1 * unit.prop.ini + weight2 * source.prop.ini); /* minimum movement */ if (source.prop.mov < unit.prop.mov) unit.prop.mov = source.prop.mov; /* maximum spotting */ if (source.prop.spt > unit.prop.spt) unit.prop.spt = source.prop.spt; /* maximum range */ if (source.prop.rng > unit.prop.rng) unit.prop.rng = source.prop.rng; /* relative attack count */ unit.prop.atk_count = (int)(weight1 * unit.prop.atk_count + weight2 * source.prop.atk_count); if (unit.prop.atk_count == 0) unit.prop.atk_count = 1; /* relative attacks */ /* if attack is negative simply use absolute value; only restore negative if both units are negative */ for (i = 0; i < DB.UnitLib.trgt_type_count; i++) { bool neg = (unit.prop.atks[i] < 0 && source.prop.atks[i] < 0); unit.prop.atks[i] = (int)(weight1 * Math.Abs(unit.prop.atks[i]) + weight2 * (source.prop.atks[i])); if (neg) unit.prop.atks[i] *= -1; } /* relative defence */ unit.prop.def_grnd = (int)(weight1 * unit.prop.def_grnd + weight2 * source.prop.def_grnd); unit.prop.def_air = (int)(weight1 * unit.prop.def_air + weight2 * source.prop.def_air); unit.prop.def_cls = (int)(weight1 * unit.prop.def_cls + weight2 * source.prop.def_cls); /* relative ammo */ unit.prop.ammo = (int)(weight1 * unit.prop.ammo + weight2 * source.prop.ammo); unit.cur_ammo = (int)(weight1 * unit.cur_ammo + weight2 * source.cur_ammo); /* relative fuel */ unit.prop.fuel = (int)(weight1 * unit.prop.fuel + weight2 * source.prop.fuel); unit.cur_fuel = (int)(weight1 * unit.cur_fuel + weight2 * source.cur_fuel); /* merge flags */ unit.prop.flags |= source.prop.flags; /* sounds, picture are kept */ /* unit::trans_prop isn't updated so far: */ /* transporter of first unit is kept if any else second unit's transporter is used */ if (string.IsNullOrEmpty(unit.trsp_prop.id) && !string.IsNullOrEmpty(source.trsp_prop.id)) { #if TODO memcpy(&unit.trsp_prop, &source.trsp_prop, sizeof(Unit_Lib_Entry)); #endif /* as this must be a ground transporter copy current fuel value */ unit.cur_fuel = source.cur_fuel; throw new NotImplementedException(); } update_bar(unit); }